3 # -------------------------------------------------------------------------
4 # Copyright (c) 2015-2017 AT&T 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 # -------------------------------------------------------------------------
24 from oslo_log import log
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
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
52 LOG = log.getLogger(__name__)
55 # FIXME(snarayanan): This is really a SolverRequest (or Request) object
58 def __init__(self, _region_gen=None):
61 self.region_gen = _region_gen
65 self.request_id = None
67 # def get_data_engine_interface(self):
68 # self.cei = cei.ConstraintEngineInterface()
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")
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
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
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)
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")
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")
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,
193 _properties=c_property)
194 self.constraints[my_attribute_constraint.name] = \
195 my_attribute_constraint
197 LOG.error("unknown constraint type {}".format(constraint_type))
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()
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
232 self.objective.operand_list.append(operand)
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)