"Derive Coverage Area TA list from coverage area"
[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 from conductor.data.plugins.inventory_provider.cps import CPS
33
34 LOG = log.getLogger(__name__)
35
36
37 OPERATORS = {'gte': ge,
38              'lte': le,
39              'eq': eq}
40
41
42 class Generator(base.InventoryProviderBase):
43
44     def __init__(self):
45         """Initialize variables"""
46         pass
47
48     def initialize(self):
49         pass
50
51     def name(self):
52         """Return human-readable name."""
53         return "generator"
54
55     def resolve_demands(self, demands, plan_info, triage_translator_data):
56         """Resolve demands into candidate list"""
57         resolved_demands = {}
58         for name, requirements in demands.items():
59             resolved_demands[name] = []
60             for requirement in requirements:
61                 inventory_type = requirement.get('inventory_type').lower()
62                 candidate_uniqueness = requirement.get('unique', 'true')
63                 filtering_attributes = requirement.get('filtering_attributes')
64                 default_fields = requirement.get('default_attributes')
65                 resolved_demands[name].extend(self.generate_candidates(inventory_type,
66                                                                        filtering_attributes,
67                                                                        candidate_uniqueness,
68                                                                        default_fields))
69
70         return resolved_demands
71
72     def generate_candidates(self, inventory_type, filtering_attributes, candidate_uniqueness, default_fields):
73
74         if inventory_type == "slice_profiles":
75             return self.generate_slice_profile_candidates(filtering_attributes, inventory_type,
76                                                           candidate_uniqueness, default_fields)
77         else:
78             LOG.debug("No functionality implemented for \
79                       generating candidates for inventory_type {}".format(inventory_type))
80             return []
81
82     def generate_slice_profile_candidates(self, filtering_attributes, inventory_type,
83                                           candidate_uniqueness, default_fields):
84         """Generates a list of slice profile candidate based on the filtering attributes,
85
86            A sample filtering attribute is given below
87            filtering_attributes = {'core': {'latency': {'min': 15, 'max': 20, 'steps': 1},
88                                             'reliability': {'values': [99.999]}},
89                                    'ran': {'latency': {'min': 10, 'max': 20, 'steps': 1},
90                                            'reliability': {'values': [99.99]},
91                                            'coverage_area_ta_list': {"derive_from":{"method":"get_tracking_area",
92                                                                                 "args": {
93                                                                          "coverage_area": {"get_param": "coverageArea"}
94                                                                                           }}}}}
95
96             It will generate slice profile combination from the attributes for each subnet and
97             generates combination of slice profile tuples from the each subnet.
98         """
99         subnet_combinations = {}
100         for subnet, attributes in filtering_attributes['subnets'].items():
101             attribute_names, attribute_combinations = generate_combinations(attributes)
102             subnet_combinations[subnet] = organize_combinations(attribute_names, attribute_combinations)
103
104         subnet_names, slice_profile_combinations = get_combinations_from_dict(subnet_combinations)
105         organized_combinations = organize_combinations(subnet_names, slice_profile_combinations)
106         candidates = []
107         for combination in organized_combinations:
108             if is_valid(get_slice_requirements(combination), filtering_attributes['service_profile']):
109                 info = Candidate.build_candidate_info(self.name(), inventory_type, 1.0, candidate_uniqueness,
110                                                       str(uuid.uuid4()))
111                 coverage_area = filtering_attributes['service_profile'].get("coverage_area").get("value")
112                 candidate = SliceProfilesCandidate(info=info, subnet_requirements=combination,
113                                                    default_fields=default_fields, coverage_area=coverage_area)
114                 converted_candidate = candidate.convert_nested_dict_to_dict()
115                 candidates.append(converted_candidate)
116
117         return candidates
118
119
120 def is_valid(converted_candidate, service_profile):
121     for attr, attr_value in service_profile.items():
122         if attr == "coverage_area":
123             pass
124         else:
125             if not OPERATORS[attr_value['operator']](converted_candidate[attr], attr_value['value']):
126                 return False
127     return True
128
129
130 def get_tracking_area(args):
131     coverage_list = []
132     coverage_area_zones_list = args.split("-")
133     zone_id_list = coverage_area_zones_list[1].split(",")
134     for zone_id in zone_id_list:
135         values = CPS().get_coveragearea_ta(zone_id)
136         for x in values:
137             if not x.get("nRTAC") in coverage_list:
138                 coverage_list.append(x.get("nRTAC"))
139     return coverage_list
140
141
142 def generate_combinations(attributes):
143     """Generates all combination of the given attribute values.
144
145        The params can have a values list or range(min, max)
146        from which the combinations are generated.
147     """
148     attr = dict()
149     ta_list = []
150
151     for attribute, attr_params in attributes.items():
152         if attr_params.get('values'):
153             values = attr_params.get('values')
154         elif attr_params.get('derive_from'):
155             derive_from = attr_params.get("derive_from")
156             method_name = derive_from.get("method")
157             args = derive_from.get("args").get("coverage_area")
158             ta_list = (eval(method_name)(args))
159             values = [ta_list]
160         else:
161             values = range(attr_params.get('min', 1), attr_params.get('max'),
162                            attr_params.get('steps', 1))
163         attr[attribute] = values
164     return get_combinations_from_dict(attr)
165
166
167 def get_combinations_from_dict(attr):
168     """Generates combinations from a dictionary containing lists
169
170        Input:
171        attr = {"latency": [1,2,3],
172                "reliability": [99.99, 99.9]
173               }
174        Output:
175        attribute_name: ["latency", "reliability"]
176        attribute_combinations: [[1,99.99], [2,99.99], [3,99.99], [1,99.9], [2,99.9], [3,99.9]]
177     """
178     attribute_names = list(attr.keys())
179     attribute_combinations = list(itertools.product(*attr.values()))
180     return attribute_names, attribute_combinations
181
182
183 def organize_combinations(attribute_names, attribute_combinations):
184     """Organise the generated combinations into list of dicts.
185
186        Input:
187        attribute_name: ["latency", "reliability"]
188        attribute_combinations: [[1,99.99], [2,99.99], [3,99.99], [1,99.9], [2,99.9], [3,99.9]]
189        Output:
190        combinations = [{'latency': 1, 'reliability': 99.99},
191                        {'latency': 2, 'reliability': 99.99},
192                        {'latency': 3, 'reliability': 99.99},
193                        {'latency': 1, 'reliability': 99.9},
194                        {'latency': 2, 'reliability': 99.9},
195                        {'latency': 3, 'reliability': 99.9}
196                       ]
197     """
198     combinations = []
199     for combination in attribute_combinations:
200         comb = {}
201         for (name, value) in zip(attribute_names, combination):
202             comb[name] = value
203         combinations.append(comb)
204     return combinations