2 # -------------------------------------------------------------------------
3 # Copyright (c) 2015-2017 AT&T Intellectual Property
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # -------------------------------------------------------------------------
20 from oslo_config import cfg
21 from oslo_log import log
26 from conductor import service
27 # from conductor.solver.optimizer import decision_path as dpath
28 # from conductor.solver.optimizer import best_first
29 # from conductor.solver.optimizer import greedy
30 from conductor.solver.optimizer import fit_first
31 from conductor.solver.optimizer import random_pick
32 from conductor.solver.request import demand
33 from conductor.solver.triage_tool.triage_data import TriageData
35 LOG = log.getLogger(__name__)
43 CONF.register_opts(SOLVER_OPTS, group='solver')
46 class Optimizer(object):
48 # FIXME(gjung): _requests should be request (no underscore, one item)
49 def __init__(self, conf, _requests=None, _begin_time=None):
52 # start time of solving the plan
53 if _begin_time is not None:
54 self._begin_time = _begin_time
56 # self.search = greedy.Greedy(self.conf)
58 # self.search = best_first.BestFirst(self.conf)
60 if _requests is not None:
61 self.requests = _requests
63 # Were the 'simulators' ever used? It doesn't look like this.
64 # Since solver/simulator code needs cleansing before being moved to ONAP,
65 # I see no value for having this piece of code which is not letting us do
66 # that cleanup. Also, Shankar has confirmed solver/simulators folder needs
67 # to go away. Commenting out for now - may be should be removed permanently.
70 # ''' for simulation '''
71 # req_sim = request_simulator.RequestSimulator(self.conf)
72 # req_sim.generate_requests()
73 # self.requests = req_sim.requests
75 def get_solution(self, num_solutions):
77 LOG.debug("search start")
78 for rk in self.requests:
79 request = self.requests[rk]
80 LOG.debug("--- request = {}".format(rk))
82 decision_list = list()
84 LOG.debug("1. sort demands")
85 demand_list = self._sort_demands(request)
87 LOG.debug(" demand = {}".format(d.name))
89 LOG.debug("2. search")
91 while (num_solutions == 'all' or num_solutions > 0):
93 LOG.debug("searching for the solution {}".format(len(decision_list) + 1))
96 _copy_demand_list = copy.deepcopy(demand_list)
98 if not request.objective.goal:
99 LOG.debug("No objective function is provided. "
100 "Random pick algorithm is used")
101 self.search = random_pick.RandomPick(self.conf)
102 best_path = self.search.search(demand_list, request)
104 LOG.debug("Fit first algorithm is used")
105 self.search = fit_first.FitFirst(self.conf)
106 best_path = self.search.search(demand_list,
107 request.objective, request)
109 if best_path is not None:
110 self.search.print_decisions(best_path)
112 LOG.debug("no solution found")
115 LOG.debug("search delay = {} sec".format(time.time() - st))
117 # add the current solution to decision_list
118 decision_list.append(best_path.decisions)
120 #remove the candidate with "uniqueness = true"
121 demand_list = copy.deepcopy(_copy_demand_list)
122 self._remove_unique_candidate(request, best_path, demand_list)
124 if num_solutions != 'all':
126 self.search.triageSolver.getSolution(decision_list)
129 def _remove_unique_candidate(self, _request, current_decision, demand_list):
131 # This method is to remove previous solved/used candidate from consideration
132 # when Conductor needs to provide multiple solutions to the user/client
134 for demand_name, candidate_attr in current_decision.decisions.items():
135 candidate_uniqueness = candidate_attr.get('uniqueness')
136 if candidate_uniqueness and candidate_uniqueness == 'true':
137 # if the candidate uniqueness is 'false', then remove
138 # that solved candidate from the translated candidates list
139 _request.demands[demand_name].resources.pop(candidate_attr.get('candidate_id'))
140 # update the demand_list
141 for demand in demand_list:
142 if(getattr(demand, 'name') == demand_name):
143 demand.resources = _request.demands[demand_name].resources
145 def _sort_demands(self, _request):
146 LOG.debug(" _sort_demands")
149 # first, find loc-demand dependencies
150 # using constraints and objective functions
151 open_demand_list = []
152 for key in _request.constraints:
153 c = _request.constraints[key]
154 if c.constraint_type == "access_distance":
155 for dk in c.demand_list:
156 if _request.demands[dk].sort_base != 1:
157 _request.demands[dk].sort_base = 1
158 open_demand_list.append(_request.demands[dk])
159 for op in _request.objective.operand_list:
160 if op.function.func_type == "latency_between": #TODO do i need to include the region_group here?
161 if isinstance(op.function.loc_a, demand.Location):
162 if _request.demands[op.function.loc_z.name].sort_base != 1:
163 _request.demands[op.function.loc_z.name].sort_base = 1
164 open_demand_list.append(op.function.loc_z)
165 elif isinstance(op.function.loc_z, demand.Location):
166 if _request.demands[op.function.loc_a.name].sort_base != 1:
167 _request.demands[op.function.loc_a.name].sort_base = 1
168 open_demand_list.append(op.function.loc_a)
169 elif op.function.func_type == "distance_between":
170 if isinstance(op.function.loc_a, demand.Location):
171 if _request.demands[op.function.loc_z.name].sort_base != 1:
172 _request.demands[op.function.loc_z.name].sort_base = 1
173 open_demand_list.append(op.function.loc_z)
174 elif isinstance(op.function.loc_z, demand.Location):
175 if _request.demands[op.function.loc_a.name].sort_base != 1:
176 _request.demands[op.function.loc_a.name].sort_base = 1
177 open_demand_list.append(op.function.loc_a)
179 if len(open_demand_list) == 0:
180 init_demand = self._exist_not_sorted_demand(_request.demands)
181 open_demand_list.append(init_demand)
183 # second, find demand-demand dependencies
185 d_list = self._get_depended_demands(open_demand_list, _request)
187 demand_list.append(d)
189 init_demand = self._exist_not_sorted_demand(_request.demands)
190 if init_demand is None:
192 open_demand_list.append(init_demand)
196 def _get_depended_demands(self, _open_demand_list, _request):
200 if len(_open_demand_list) == 0:
203 d = _open_demand_list.pop(0)
206 demand_list.append(d)
208 for key in _request.constraints:
209 c = _request.constraints[key]
210 # FIXME(snarayanan): "aic" only to be known by conductor-data
211 if c.constraint_type == "aic_distance":
212 if d.name in c.demand_list:
213 for dk in c.demand_list:
214 if dk != d.name and \
215 _request.demands[dk].sort_base != 1:
216 _request.demands[dk].sort_base = 1
217 _open_demand_list.append(
218 _request.demands[dk])
220 for op in _request.objective.operand_list:
221 if op.function.func_type == "latency_between": #TODO
222 if op.function.loc_a.name == d.name:
223 if op.function.loc_z.name in \
224 _request.demands.keys():
226 op.function.loc_z.name].sort_base != 1:
228 op.function.loc_z.name].sort_base = 1
229 _open_demand_list.append(op.function.loc_z)
230 elif op.function.loc_z.name == d.name:
231 if op.function.loc_a.name in \
232 _request.demands.keys():
234 op.function.loc_a.name].sort_base != 1:
236 op.function.loc_a.name].sort_base = 1
237 _open_demand_list.append(op.function.loc_a)
239 elif op.function.func_type == "distance_between":
240 if op.function.loc_a.name == d.name:
241 if op.function.loc_z.name in \
242 _request.demands.keys():
244 op.function.loc_z.name].sort_base != 1:
246 op.function.loc_z.name].sort_base = 1
247 _open_demand_list.append(op.function.loc_z)
248 elif op.function.loc_z.name == d.name:
249 if op.function.loc_a.name in \
250 _request.demands.keys():
252 op.function.loc_a.name].sort_base != 1:
254 op.function.loc_a.name].sort_base = 1
255 _open_demand_list.append(op.function.loc_a)
259 def _exist_not_sorted_demand(self, _demands):
260 not_sorted_demand = None
262 demand = _demands[key]
263 if demand.sort_base != 1:
264 not_sorted_demand = demand
266 return not_sorted_demand