17fe7a5322b832809b72f0204b83d9657ed175cf
[optf/has.git] / conductor / conductor / data / plugins / inventory_provider / generator.py
1 #
2 # -------------------------------------------------------------------------
3 #   Copyright (C) 2020 Wipro Limited.
4 #
5 #   Licensed under the Apache License, Version 2.0 (the "License");
6 #   you may not use this file except in compliance with the License.
7 #   You may obtain a copy of the License at
8 #
9 #       http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #   Unless required by applicable law or agreed to in writing, software
12 #   distributed under the License is distributed on an "AS IS" BASIS,
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 #   See the License for the specific language governing permissions and
15 #   limitations under the License.
16 #
17 # -------------------------------------------------------------------------
18 #
19
20 import itertools
21 from operator import eq
22 from operator import ge
23 from operator import le
24 import uuid
25
26 from oslo_log import log
27
28 from conductor.data.plugins.inventory_provider import base
29 from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
30 from conductor.data.plugins.inventory_provider.candidates.slice_profiles_candidate import get_slice_requirements
31 from conductor.data.plugins.inventory_provider.candidates.slice_profiles_candidate import SliceProfilesCandidate
32
33 LOG = log.getLogger(__name__)
34
35
36 OPERATORS = {'gte': ge,
37              'lte': le,
38              'eq': eq}
39
40
41 class Generator(base.InventoryProviderBase):
42
43     def __init__(self):
44         """Initialize variables"""
45         pass
46
47     def initialize(self):
48         pass
49
50     def name(self):
51         """Return human-readable name."""
52         return "generator"
53
54     def resolve_demands(self, demands, plan_info, triage_translator_data):
55         """Resolve demands into candidate list"""
56         resolved_demands = {}
57         for name, requirements in demands.items():
58             resolved_demands[name] = []
59             for requirement in requirements:
60                 inventory_type = requirement.get('inventory_type').lower()
61                 candidate_uniqueness = requirement.get('unique', 'true')
62                 filtering_attributes = requirement.get('filtering_attributes')
63                 default_fields = requirement.get('default_attributes')
64                 resolved_demands[name].extend(self.generate_candidates(inventory_type,
65                                                                        filtering_attributes,
66                                                                        candidate_uniqueness,
67                                                                        default_fields))
68
69         return resolved_demands
70
71     def generate_candidates(self, inventory_type, filtering_attributes, candidate_uniqueness, default_fields):
72
73         if inventory_type == "slice_profiles":
74             return self.generate_slice_profile_candidates(filtering_attributes, inventory_type,
75                                                           candidate_uniqueness, default_fields)
76         else:
77             LOG.debug("No functionality implemented for \
78                       generating candidates for inventory_type {}".format(inventory_type))
79             return []
80
81     def generate_slice_profile_candidates(self, filtering_attributes, inventory_type,
82                                           candidate_uniqueness, default_fields):
83         """Generates a list of slice profile candidate based on the filtering attributes,
84
85            A sample filtering attribute is given below
86            filtering_attributes = {'core': {'latency': {'min': 15, 'max': 20, 'steps': 1},
87                                             'reliability': {'values': [99.999]}},
88                                    'ran': {'latency': {'min': 10, 'max': 20, 'steps': 1},
89                                            'reliability': {'values': [99.99]},
90                                            'coverage_area_ta_list': {'values': ['City: Chennai']}}}
91             It will generate slice profile combination from the attributes for each subnet and
92             generates combination of slice profile tuples from the each subnet.
93         """
94         subnet_combinations = {}
95         for subnet, attributes in filtering_attributes['subnets'].items():
96             attribute_names, attribute_combinations = generate_combinations(attributes)
97             subnet_combinations[subnet] = organize_combinations(attribute_names, attribute_combinations)
98
99         subnet_names, slice_profile_combinations = get_combinations_from_dict(subnet_combinations)
100         organized_combinations = organize_combinations(subnet_names, slice_profile_combinations)
101         candidates = []
102         for combination in organized_combinations:
103             if is_valid(get_slice_requirements(combination), filtering_attributes['service_profile']):
104                 info = Candidate.build_candidate_info(self.name(), inventory_type, 1.0, candidate_uniqueness,
105                                                       str(uuid.uuid4()))
106                 candidate = SliceProfilesCandidate(info=info, subnet_requirements=combination,
107                                                    default_fields=default_fields)
108                 converted_candidate = candidate.convert_nested_dict_to_dict()
109                 candidates.append(converted_candidate)
110
111         return candidates
112
113
114 def is_valid(converted_candidate, service_profile):
115     for attr, attr_value in service_profile.items():
116         if not OPERATORS[attr_value['operator']](converted_candidate[attr], attr_value['value']):
117             return False
118     return True
119
120
121 def generate_combinations(attributes):
122     """Generates all combination of the given attribute values.
123
124        The params can have a values list or range(min, max)
125        from which the combinations are generated.
126     """
127     attr = dict()
128     for attribute, attr_params in attributes.items():
129         values = attr_params.get('values')
130         if not values:
131             values = range(attr_params.get('min', 1), attr_params.get('max'),
132                            attr_params.get('steps', 1))
133         attr[attribute] = values
134
135     return get_combinations_from_dict(attr)
136
137
138 def get_combinations_from_dict(attr):
139     """Generates combinations from a dictionary containing lists
140
141        Input:
142        attr = {"latency": [1,2,3],
143                "reliability": [99.99, 99.9]
144               }
145        Output:
146        attribute_name: ["latency", "reliability"]
147        attribute_combinations: [[1,99.99], [2,99.99], [3,99.99], [1,99.9], [2,99.9], [3,99.9]]
148     """
149     attribute_names = list(attr.keys())
150     attribute_combinations = list(itertools.product(*attr.values()))
151     return attribute_names, attribute_combinations
152
153
154 def organize_combinations(attribute_names, attribute_combinations):
155     """Organise the generated combinations into list of dicts.
156
157        Input:
158        attribute_name: ["latency", "reliability"]
159        attribute_combinations: [[1,99.99], [2,99.99], [3,99.99], [1,99.9], [2,99.9], [3,99.9]]
160        Output:
161        combinations = [{'latency': 1, 'reliability': 99.99},
162                        {'latency': 2, 'reliability': 99.99},
163                        {'latency': 3, 'reliability': 99.99},
164                        {'latency': 1, 'reliability': 99.9},
165                        {'latency': 2, 'reliability': 99.9},
166                        {'latency': 3, 'reliability': 99.9}
167                       ]
168     """
169     combinations = []
170     for combination in attribute_combinations:
171         comb = {}
172         for (name, value) in zip(attribute_names, combination):
173             comb[name] = value
174         combinations.append(comb)
175     return combinations