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'''
26 import conductor.common.prometheus_metrics as PC
29 from conductor.i18n import _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
58 self.m_vim_id = candidate.get('vim-id')
60 # Find the flavor which has all the required capabilities
61 def match_flavor(self):
62 # Keys to find capability match
63 hpa_keys = ['hpa-feature', 'architecture', 'hpa-version']
65 for capability in CapabilityDataParser.get_item(self.req_cap_list,
67 if capability.item['mandatory'] == 'True':
68 hpa_list = {k: capability.item[k] \
69 for k in hpa_keys if k in capability.item}
70 if hpa_list not in req_filter_list:
71 req_filter_list.append(hpa_list)
74 for flavor in self.flavors_list:
75 flavor_filter_list = []
77 flavor_cap_list = flavor['hpa-capabilities']
79 LOG.info(_LI("hpa-capabilities not found in flavor "))
80 # Metrics to Prometheus
81 m_flavor_name = flavor['flavor-name']
82 PC.HPA_FLAVOR_MATCH_UNSUCCESSFUL.labels('ONAP', 'N/A', 'N/A',
86 for capability in CapabilityDataParser.get_item(flavor_cap_list,
88 hpa_list = {k: capability.item[k] \
89 for k in hpa_keys if k in capability.item}
90 flavor_filter_list.append(hpa_list)
91 # if flavor has the matching capability compare attributes
92 if self._is_cap_supported(flavor_filter_list, req_filter_list):
93 match_found, score, req_directives = self._compare_feature_attributes(flavor_cap_list)
95 LOG.info(_LI("Matching Flavor found '{}' for request - {}").
96 format(flavor['flavor-name'], self.req_cap_list))
97 # Metrics to Prometheus
98 m_flavor_name = flavor['flavor-name']
99 PC.HPA_FLAVOR_MATCH_SUCCESSFUL.labels('ONAP', 'N/A', 'N/A',
100 'N/A', self.m_vim_id,
102 if score > max_score:
104 flavor_map = {"flavor-id": flavor['flavor-id'],
105 "flavor-name": flavor['flavor-name'],
107 directives = {"flavor_map": flavor_map,
108 "directives": req_directives}
110 # Metrics to Prometheus
111 m_flavor_name = flavor['flavor-name']
112 PC.HPA_FLAVOR_MATCH_UNSUCCESSFUL.labels('ONAP', 'N/A',
117 # Metrics to Prometheus
118 m_flavor_name = flavor['flavor-name']
119 PC.HPA_FLAVOR_MATCH_UNSUCCESSFUL.labels('ONAP', 'N/A',
126 def _is_cap_supported(self, flavor, cap):
132 # Found all capabilities in Flavor
135 # Convert to bytes value using unit
136 def _get_normalized_value(self, unit, value):
138 if not value.isdigit():
144 value = value * 1024 * 1024
146 value = value * 1024 * 1024 * 1024
149 def _get_req_attribute(self, req_attr):
151 c_op = req_attr['operator']
152 c_value = req_attr['hpa-attribute-value']
154 if 'unit' in req_attr:
155 c_unit = req_attr['unit']
157 LOG.info(_LI("invalid JSON "))
161 c_value = self._get_normalized_value(c_unit, c_value)
164 def _get_flavor_attribute(self, flavor_attr):
166 attrib_value = yaml.load(flavor_attr['hpa-attribute-value'])
172 for key, value in attrib_value.iteritems():
178 f_value = self._get_normalized_value(f_unit, f_value)
181 def _get_operator(self, req_op):
183 operator_list = ['=', '<', '>', '<=', '>=', 'ALL']
185 if req_op not in operator_list:
198 elif req_op == 'ALL':
199 op = match_all_operator
204 def _compare_attribute(self, flavor_attr, req_attr):
206 req_value, req_op = self._get_req_attribute(req_attr)
207 flavor_value = self._get_flavor_attribute(flavor_attr)
209 if req_value is None or flavor_value is None:
212 # Compare operators only valid for Integers
213 if req_op in ['<', '>', '<=', '>=']:
214 if not req_value.isdigit() or not flavor_value.isdigit():
217 op = self._get_operator(req_op)
222 # All is valid only for lists
223 if isinstance(req_value, list) and isinstance(flavor_value, list):
224 return op(flavor_value, req_value)
226 # if values are string compare them as strings
228 if not req_value.isdigit() or not flavor_value.isdigit():
229 return op(req_value, flavor_value)
231 # Only integers left to compare
232 if req_op in ['<', '>', '<=', '>=', '=']:
233 return op(int(flavor_value), int(req_value))
237 # for the feature get the capabilty feature attribute list
238 def _get_flavor_cfa_list(self, feature, flavor_cap_list):
239 feature_attr_list = []
240 for capability in CapabilityDataParser.get_item(flavor_cap_list,
242 flavor_feature, feature_attributes = capability.get_fields()
243 # Multiple features that match this condition will be filtered
244 if feature == flavor_feature:
245 feature_attr_list.append(feature_attributes)
246 return feature_attr_list
248 # flavor has all the required capabilties
249 # For each required capability find capability in flavor
250 # and compare each attribute
251 def _compare_feature_attributes(self, flavor_cap_list):
254 for capability in CapabilityDataParser.get_item(self.req_cap_list, None):
255 hpa_feature, req_cfa_list = capability.get_fields()
256 feature_directive = capability.get_directives()
257 if feature_directive:
258 feature_directive[:] = [d for d in feature_directive
259 if d.get("type") != ""]
260 for item in feature_directive:
261 directives.append(item)
262 flavor_cfa_list = self._get_flavor_cfa_list(hpa_feature, flavor_cap_list)
264 if flavor_cfa_list is not None:
265 for flavor_cfa in flavor_cfa_list:
267 for req_feature_attr in req_cfa_list:
268 req_attr_key = req_feature_attr['hpa-attribute-key']
269 # filter to get the attribute being compared
270 flavor_feature_attr = \
271 filter(lambda ele: ele['hpa-attribute-key'] ==
272 req_attr_key, flavor_cfa)
273 if not flavor_feature_attr:
275 elif not self._compare_attribute(flavor_feature_attr[0],
283 if not req_flag and capability.item['mandatory'] == 'True':
284 return False, 0, None
285 if req_flag and capability.item['mandatory'] == 'False':
286 score = score + int(capability.item['score'])
287 return True, score, directives
290 class CapabilityDataParser(object):
291 """Helper class to parse data"""
293 def __init__(self, item):
297 def get_item(cls, payload, key):
302 features = (payload[key])
307 LOG.info(_LI("invalid JSON "))
309 def get_fields(self):
310 return (self.get_feature(),
311 self.get_feature_attributes())
313 def get_feature_attributes(self):
314 return self.item.get('hpa-feature-attributes')
316 def get_feature(self):
317 return self.item.get('hpa-feature')
319 def get_directives(self):
320 return self.item.get('directives')