143 lines
5.3 KiB
Python
143 lines
5.3 KiB
Python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from cinder.scheduler.evaluator import evaluator
|
|
from cinder.scheduler import filters
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class DriverFilter(filters.BaseBackendFilter):
|
|
"""DriverFilter filters backend based on a 'filter function' and metrics.
|
|
|
|
DriverFilter filters based on volume backend's provided 'filter function'
|
|
and metrics.
|
|
"""
|
|
|
|
def backend_passes(self, backend_state, filter_properties):
|
|
"""Determines if a backend has a passing filter_function or not."""
|
|
stats = self._generate_stats(backend_state, filter_properties)
|
|
|
|
LOG.debug("Checking backend '%s'",
|
|
stats['backend_stats']['backend_id'])
|
|
result = self._check_filter_function(stats)
|
|
LOG.debug("Result: %s", result)
|
|
LOG.debug("Done checking backend '%s'",
|
|
stats['backend_stats']['backend_id'])
|
|
|
|
return result
|
|
|
|
def _check_filter_function(self, stats):
|
|
"""Checks if a volume passes a backend's filter function.
|
|
|
|
Returns a tuple in the format (filter_passing, filter_invalid).
|
|
Both values are booleans.
|
|
"""
|
|
if stats['filter_function'] is None:
|
|
LOG.debug("Filter function not set :: passing backend")
|
|
return True
|
|
|
|
try:
|
|
filter_result = self._run_evaluator(stats['filter_function'],
|
|
stats)
|
|
except Exception as ex:
|
|
# Warn the admin for now that there is an error in the
|
|
# filter function.
|
|
LOG.warning("Error in filtering function "
|
|
"'%(function)s' : '%(error)s' :: failing backend",
|
|
{'function': stats['filter_function'],
|
|
'error': ex, })
|
|
return False
|
|
|
|
return filter_result
|
|
|
|
def _run_evaluator(self, func, stats):
|
|
"""Evaluates a given function using the provided available stats."""
|
|
backend_stats = stats['backend_stats']
|
|
backend_caps = stats['backend_caps']
|
|
extra_specs = stats['extra_specs']
|
|
qos_specs = stats['qos_specs']
|
|
volume_stats = stats['volume_stats']
|
|
|
|
LOG.debug('Running evaluator: extra_specs: %(extra)s\n'
|
|
'stats: %(stats)s\n'
|
|
'capabilities: %(capabilities)s\n'
|
|
'volume: %(volume)s\n'
|
|
'qos: %(qos)s', {'extra': extra_specs,
|
|
'stats': backend_stats,
|
|
'capabilities': backend_caps,
|
|
'volume': volume_stats,
|
|
'qos': qos_specs})
|
|
|
|
result = evaluator.evaluate(
|
|
func,
|
|
extra=extra_specs,
|
|
stats=backend_stats,
|
|
capabilities=backend_caps,
|
|
volume=volume_stats,
|
|
qos=qos_specs)
|
|
|
|
return result
|
|
|
|
def _generate_stats(self, backend_state, filter_properties):
|
|
"""Generates statistics from backend and volume data."""
|
|
|
|
backend_stats = {
|
|
'host': backend_state.host,
|
|
'cluster_name': backend_state.cluster_name,
|
|
'backend_id': backend_state.backend_id,
|
|
'volume_backend_name': backend_state.volume_backend_name,
|
|
'vendor_name': backend_state.vendor_name,
|
|
'driver_version': backend_state.driver_version,
|
|
'storage_protocol': backend_state.storage_protocol,
|
|
'QoS_support': backend_state.QoS_support,
|
|
'total_capacity_gb': backend_state.total_capacity_gb,
|
|
'allocated_capacity_gb': backend_state.allocated_capacity_gb,
|
|
'free_capacity_gb': backend_state.free_capacity_gb,
|
|
'reserved_percentage': backend_state.reserved_percentage,
|
|
'updated': backend_state.updated,
|
|
}
|
|
|
|
backend_caps = backend_state.capabilities
|
|
|
|
filter_function = None
|
|
|
|
if ('filter_function' in backend_caps and
|
|
backend_caps['filter_function'] is not None):
|
|
filter_function = str(backend_caps['filter_function'])
|
|
|
|
qos_specs = filter_properties.get('qos_specs', {})
|
|
|
|
volume_type = filter_properties.get('volume_type', {})
|
|
extra_specs = volume_type.get('extra_specs', {})
|
|
|
|
request_spec = filter_properties.get('request_spec', {})
|
|
volume_stats = request_spec.get('volume_properties', {})
|
|
|
|
stats = {
|
|
'backend_stats': backend_stats,
|
|
'backend_caps': backend_caps,
|
|
'extra_specs': extra_specs,
|
|
'qos_specs': qos_specs,
|
|
'volume_stats': volume_stats,
|
|
'volume_type': volume_type,
|
|
'filter_function': filter_function,
|
|
}
|
|
|
|
return stats
|