3 # -------------------------------------------------------------------------
4 # Copyright (c) 2018 Intel Corporation Intellectual Property
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # -------------------------------------------------------------------------
21 '''Utility functions for
22 Hardware Platform Awareness (HPA) constraint plugin'''
28 from conductor.i18n import _LE, _LI
30 # Third-party library imports
31 from oslo_log import log
33 LOG = log.getLogger(__name__)
36 def match_all_operator(big_list, small_list):
38 Match ALL operator for HPA
39 Check if smaller list is a subset of bigger list
40 :param big_list: bigger list
41 :param small_list: smaller list
42 :return: True or False
44 if not big_list or not small_list:
47 big_set = set(big_list)
48 small_set = set(small_list)
50 return small_set.issubset(big_set)
53 class HpaMatchProvider(object):
55 def __init__(self, candidate, req_cap_list):
56 self.flavors_list = candidate['flavors']['flavor']
57 self.req_cap_list = req_cap_list
59 # Find the flavor which has all the required capabilities
60 def match_flavor(self):
61 # Keys to find capability match
62 hpa_keys = ['hpa-feature', 'architecture', 'hpa-version']
64 for capability in CapabilityDataParser.get_item(self.req_cap_list,
66 if capability.item['mandatory'] == 'True':
67 hpa_list = {k: capability.item[k] \
68 for k in hpa_keys if k in capability.item}
69 req_filter_list.append(hpa_list)
72 for flavor in self.flavors_list:
73 flavor_filter_list = []
75 flavor_cap_list = flavor['hpa-capabilities']
77 LOG.info(_LI("hpa-capabilities not found in flavor "))
79 for capability in CapabilityDataParser.get_item(flavor_cap_list,
81 hpa_list = {k: capability.item[k] \
82 for k in hpa_keys if k in capability.item}
83 flavor_filter_list.append(hpa_list)
84 # if flavor has the matching capability compare attributes
85 if self._is_cap_supported(flavor_filter_list, req_filter_list):
86 match_found, score = self._compare_feature_attributes(flavor_cap_list)
88 LOG.info(_LI("Matching Flavor found '{}' for request - {}").
89 format(flavor['flavor-name'], self.req_cap_list))
92 flavor_map = {"flavor-id": flavor['flavor-id'],
93 "flavor-name": flavor['flavor-name']}
97 def _is_cap_supported(self, flavor, cap):
103 # Found all capabilities in Flavor
106 # Convert to bytes value using unit
107 def _get_normalized_value(self, unit, value):
109 if not value.isdigit():
115 value = value * 1024 * 1024
117 value = value * 1024 * 1024 * 1024
120 def _get_req_attribute(self, req_attr):
122 c_op = req_attr['operator']
123 c_value = req_attr['hpa-attribute-value']
125 if 'unit' in req_attr:
126 c_unit = req_attr['unit']
128 LOG.info(_LI("invalid JSON "))
132 c_value = self._get_normalized_value(c_unit, c_value)
135 def _get_flavor_attribute(self, flavor_attr):
137 attrib_value = yaml.load(flavor_attr['hpa-attribute-value'])
143 for key, value in attrib_value.iteritems():
149 f_value = self._get_normalized_value(f_unit, f_value)
152 def _get_operator(self, req_op):
154 operator_list = ['=', '<', '>', '<=', '>=', 'ALL']
156 if req_op not in operator_list:
169 elif req_op == 'ALL':
170 op = match_all_operator
175 def _compare_attribute(self, flavor_attr, req_attr):
177 req_value, req_op = self._get_req_attribute(req_attr)
178 flavor_value = self._get_flavor_attribute(flavor_attr)
180 if req_value is None or flavor_value is None:
183 # Compare operators only valid for Integers
184 if req_op in ['<', '>', '<=', '>=']:
185 if not req_value.isdigit() or not flavor_value.isdigit():
188 op = self._get_operator(req_op)
193 # All is valid only for lists
194 if isinstance(req_value, list) and isinstance(flavor_value, list):
195 return op(flavor_value, req_value)
197 # if values are string compare them as strings
199 if not req_value.isdigit() or not flavor_value.isdigit():
200 return op(req_value, flavor_value)
202 # Only integers left to compare
203 if req_op in ['<', '>', '<=', '>=', '=']:
204 return op(int(flavor_value), int(req_value))
208 # for the feature get the capabilty feature attribute list
209 def _get_flavor_cfa_list(self, feature, flavor_cap_list):
210 for capability in CapabilityDataParser.get_item(flavor_cap_list,
212 flavor_feature, feature_attributes = capability.get_fields()
213 # One feature will match this condition as we have pre-filtered
214 if feature == flavor_feature:
215 return feature_attributes
217 # flavor has all the required capabilties
218 # For each required capability find capability in flavor
219 # and compare each attribute
220 def _compare_feature_attributes(self, flavor_cap_list):
222 for capability in CapabilityDataParser.get_item(self.req_cap_list, None):
223 hpa_feature, req_cfa_list = capability.get_fields()
224 flavor_cfa_list = self._get_flavor_cfa_list(hpa_feature, flavor_cap_list)
225 if flavor_cfa_list is not None:
226 for req_feature_attr in req_cfa_list:
227 req_attr_key = req_feature_attr['hpa-attribute-key']
228 # filter to get the attribute being compared
229 flavor_feature_attr = \
230 filter(lambda ele: ele['hpa-attribute-key'] == \
231 req_attr_key, flavor_cfa_list)
232 if not flavor_feature_attr:
234 if not self._compare_attribute(flavor_feature_attr[0],
237 if flavor_cfa_list is not None and capability.item['mandatory'] == 'False':
238 score = score + int(capability.item['score'])
242 class CapabilityDataParser(object):
243 """Helper class to parse data"""
245 def __init__(self, item):
249 def get_item(cls, payload, key):
254 features = (payload[key])
259 LOG.info(_LI("invalid JSON "))
261 def get_fields(self):
262 return (self.get_feature(),
263 self.get_feature_attributes())
265 def get_feature_attributes(self):
266 return self.item.get('hpa-feature-attributes')
268 def get_feature(self):
269 return self.item.get('hpa-feature')