1# Copyright (c) 2011 OpenStack Foundation. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16from oslo_log import log as logging 17 18from cinder.scheduler import filters 19from cinder.scheduler.filters import extra_specs_ops 20 21LOG = logging.getLogger(__name__) 22 23 24class CapabilitiesFilter(filters.BaseBackendFilter): 25 """BackendFilter to work with resource (instance & volume) type records.""" 26 27 def _satisfies_extra_specs(self, capabilities, resource_type): 28 """Check if capabilities satisfy resource type requirements. 29 30 Check that the capabilities provided by the services satisfy 31 the extra specs associated with the resource type. 32 """ 33 34 if not resource_type: 35 return True 36 37 extra_specs = resource_type.get('extra_specs', []) 38 if not extra_specs: 39 return True 40 41 for key, req in extra_specs.items(): 42 43 # Either not scoped format, or in capabilities scope 44 scope = key.split(':') 45 46 # Ignore scoped (such as vendor-specific) capabilities 47 if len(scope) > 1 and scope[0] != "capabilities": 48 continue 49 # Strip off prefix if spec started with 'capabilities:' 50 elif scope[0] == "capabilities": 51 del scope[0] 52 53 cap = capabilities 54 for index in range(len(scope)): 55 try: 56 cap = cap[scope[index]] 57 except (TypeError, KeyError): 58 LOG.debug("Backend doesn't provide capability '%(cap)s' ", 59 {'cap': scope[index]}) 60 return False 61 62 # Make all capability values a list so we can handle lists 63 cap_list = [cap] if not isinstance(cap, list) else cap 64 65 # Loop through capability values looking for any match 66 for cap_value in cap_list: 67 if extra_specs_ops.match(cap_value, req): 68 break 69 else: 70 # Nothing matched, so bail out 71 LOG.debug('Volume type extra spec requirement ' 72 '"%(key)s=%(req)s" does not match reported ' 73 'capability "%(cap)s"', 74 {'key': key, 'req': req, 'cap': cap}) 75 return False 76 return True 77 78 def backend_passes(self, backend_state, filter_properties): 79 """Return a list of backends that can create resource_type.""" 80 # Note(zhiteng) Currently only Cinder and Nova are using 81 # this filter, so the resource type is either instance or 82 # volume. 83 resource_type = filter_properties.get('resource_type') 84 if not self._satisfies_extra_specs(backend_state.capabilities, 85 resource_type): 86 LOG.debug("%(backend_state)s fails resource_type extra_specs " 87 "requirements", {'backend_state': backend_state}) 88 return False 89 return True 90