Merge "Adding .gitignore to the repository"
[optf/has.git] / conductor / conductor / solver / request / parser.py
1 #!/usr/bin/env python
2 #
3 # -------------------------------------------------------------------------
4 #   Copyright (c) 2015-2017 AT&T Intellectual Property
5 #
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
9 #
10 #       http://www.apache.org/licenses/LICENSE-2.0
11 #
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.
17 #
18 # -------------------------------------------------------------------------
19 #
20
21
22 # import json
23 import operator
24 from oslo_log import log
25 import random
26 # import sys
27
28 from conductor.solver.optimizer.constraints \
29     import access_distance as access_dist
30 from conductor.solver.optimizer.constraints \
31     import cloud_distance as cloud_dist
32 from conductor.solver.optimizer.constraints \
33     import attribute as attribute_constraint
34 # from conductor.solver.optimizer.constraints import constraint
35 from conductor.solver.optimizer.constraints \
36     import inventory_group
37 from conductor.solver.optimizer.constraints \
38     import service as service_constraint
39 from conductor.solver.optimizer.constraints import zone
40 from conductor.solver.request import demand
41 from conductor.solver.request.functions import cloud_version
42 from conductor.solver.request.functions import distance_between
43 from conductor.solver.request import objective
44
45 # from conductor.solver.request.functions import distance_between
46 # from conductor.solver.request import objective
47 # from conductor.solver.resource import region
48 # from conductor.solver.resource import service
49 # from conductor.solver.utils import constraint_engine_interface as cei
50 # from conductor.solver.utils import utils
51
52 LOG = log.getLogger(__name__)
53
54
55 # FIXME(snarayanan): This is really a SolverRequest (or Request) object
56 class Parser(object):
57
58     def __init__(self, _region_gen=None):
59         self.demands = {}
60         self.locations = {}
61         self.region_gen = _region_gen
62         self.constraints = {}
63         self.objective = None
64         self.cei = None
65         self.request_id = None
66
67     # def get_data_engine_interface(self):
68     #    self.cei = cei.ConstraintEngineInterface()
69
70     # FIXME(snarayanan): This should just be parse_template
71     def parse_template(self, json_template=None):
72         if json_template is None:
73             LOG.error("No template specified")
74             return "Error"
75
76         # get demands
77         demand_list = json_template["conductor_solver"]["demands"]
78         for demand_id, candidate_list in demand_list.items():
79             current_demand = demand.Demand(demand_id)
80             # candidate should only have minimal information like location_id
81             for candidate in candidate_list["candidates"]:
82                 candidate_id = candidate["candidate_id"]
83                 current_demand.resources[candidate_id] = candidate
84             current_demand.sort_base = 0  # this is only for testing
85             self.demands[demand_id] = current_demand
86
87         # get locations
88         location_list = json_template["conductor_solver"]["locations"]
89         for location_id, location_info in location_list.items():
90             loc = demand.Location(location_id)
91             loc.loc_type = "coordinates"
92             loc.value = (float(location_info["latitude"]),
93                          float(location_info["longitude"]))
94             self.locations[location_id] = loc
95
96         # get constraints
97         input_constraints = json_template["conductor_solver"]["constraints"]
98         for constraint_id, constraint_info in input_constraints.items():
99             constraint_type = constraint_info["type"]
100             constraint_demands = list()
101             parsed_demands = constraint_info["demands"]
102             if isinstance(parsed_demands, list):
103                 for d in parsed_demands:
104                     constraint_demands.append(d)
105             else:
106                 constraint_demands.append(parsed_demands)
107             if constraint_type == "distance_to_location":
108                 c_property = constraint_info.get("properties")
109                 location_id = c_property.get("location")
110                 op = operator.le  # default operator
111                 c_op = c_property.get("distance").get("operator")
112                 if c_op == ">":
113                     op = operator.gt
114                 elif c_op == ">=":
115                     op = operator.ge
116                 elif c_op == "<":
117                     op = operator.lt
118                 elif c_op == "<=":
119                     op = operator.le
120                 elif c_op == "=":
121                     op = operator.eq
122                 dist_value = c_property.get("distance").get("value")
123                 my_access_distance_constraint = access_dist.AccessDistance(
124                     constraint_id, constraint_type, constraint_demands,
125                     _comparison_operator=op, _threshold=dist_value,
126                     _location=self.locations[location_id])
127                 self.constraints[my_access_distance_constraint.name] = \
128                     my_access_distance_constraint
129             elif constraint_type == "distance_between_demands":
130                 c_property = constraint_info.get("properties")
131                 op = operator.le  # default operator
132                 c_op = c_property.get("distance").get("operator")
133                 if c_op == ">":
134                     op = operator.gt
135                 elif c_op == ">=":
136                     op = operator.ge
137                 elif c_op == "<":
138                     op = operator.lt
139                 elif c_op == "<=":
140                     op = operator.le
141                 elif c_op == "=":
142                     op = operator.eq
143                 dist_value = c_property.get("distance").get("value")
144                 my_cloud_distance_constraint = cloud_dist.CloudDistance(
145                     constraint_id, constraint_type, constraint_demands,
146                     _comparison_operator=op, _threshold=dist_value)
147                 self.constraints[my_cloud_distance_constraint.name] = \
148                     my_cloud_distance_constraint
149             elif constraint_type == "inventory_group":
150                 my_inventory_group_constraint = \
151                     inventory_group.InventoryGroup(
152                         constraint_id, constraint_type, constraint_demands)
153                 self.constraints[my_inventory_group_constraint.name] = \
154                     my_inventory_group_constraint
155             elif constraint_type == "region_fit":
156                 c_property = constraint_info.get("properties")
157                 controller = c_property.get("controller")
158                 request = c_property.get("request")
159                 # inventory type is cloud for region_fit
160                 inventory_type = "cloud"
161                 my_service_constraint = service_constraint.Service(
162                     constraint_id, constraint_type, constraint_demands,
163                     _controller=controller, _request=request, _cost=None,
164                     _inventory_type=inventory_type)
165                 self.constraints[my_service_constraint.name] = \
166                     my_service_constraint
167             elif constraint_type == "instance_fit":
168                 c_property = constraint_info.get("properties")
169                 controller = c_property.get("controller")
170                 request = c_property.get("request")
171                 # inventory type is service for instance_fit
172                 inventory_type = "service"
173                 my_service_constraint = service_constraint.Service(
174                     constraint_id, constraint_type, constraint_demands,
175                     _controller=controller, _request=request, _cost=None,
176                     _inventory_type=inventory_type)
177                 self.constraints[my_service_constraint.name] = \
178                     my_service_constraint
179             elif constraint_type == "zone":
180                 c_property = constraint_info.get("properties")
181                 qualifier = c_property.get("qualifier")
182                 category = c_property.get("category")
183                 my_zone_constraint = zone.Zone(
184                     constraint_id, constraint_type, constraint_demands,
185                     _qualifier=qualifier, _category=category)
186                 self.constraints[my_zone_constraint.name] = my_zone_constraint
187             elif constraint_type == "attribute":
188                 c_property = constraint_info.get("properties")
189                 my_attribute_constraint = \
190                     attribute_constraint.Attribute(constraint_id,
191                                                    constraint_type,
192                                                    constraint_demands,
193                                                    _properties=c_property)
194                 self.constraints[my_attribute_constraint.name] = \
195                     my_attribute_constraint
196             else:
197                 LOG.error("unknown constraint type {}".format(constraint_type))
198                 return
199
200         # get objective function
201         if "objective" not in json_template["conductor_solver"]\
202            or not json_template["conductor_solver"]["objective"]:
203             self.objective = objective.Objective()
204         else:
205             input_objective = json_template["conductor_solver"]["objective"]
206             self.objective = objective.Objective()
207             self.objective.goal = input_objective["goal"]
208             self.objective.operation = input_objective["operation"]
209             for operand_data in input_objective["operands"]:
210                 operand = objective.Operand()
211                 operand.operation = operand_data["operation"]
212                 operand.weight = float(operand_data["weight"])
213                 if operand_data["function"] == "distance_between":
214                     func = distance_between.DistanceBetween("distance_between")
215                     param = operand_data["function_param"][0]
216                     if param in self.locations:
217                         func.loc_a = self.locations[param]
218                     elif param in self.demands:
219                         func.loc_a = self.demands[param]
220                     param = operand_data["function_param"][1]
221                     if param in self.locations:
222                         func.loc_z = self.locations[param]
223                     elif param in self.demands:
224                         func.loc_z = self.demands[param]
225                     operand.function = func
226                 elif operand_data["function"] == "cloud_version":
227                     self.objective.goal = "min_cloud_version"
228                     func = cloud_version.CloudVersion("cloud_version")
229                     func.loc = operand_data["function_param"]
230                     operand.function = func
231
232                 self.objective.operand_list.append(operand)
233
234     def map_constraints_to_demands(self):
235         # spread the constraints over the demands
236         for constraint_name, constraint in self.constraints.items():
237             for d in constraint.demand_list:
238                 if d in self.demands.keys():
239                     self.demands[d].constraint_list.append(constraint)
240