Coverage for watcher/decision_engine/planner/node_resource_consolidation.py: 92%
78 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
1# -*- encoding: utf-8 -*-
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
17from oslo_log import log
19from watcher.common import exception
20from watcher.common import utils
21from watcher.decision_engine.model import element
22from watcher.decision_engine.planner import base
23from watcher import objects
25LOG = log.getLogger(__name__)
28class NodeResourceConsolidationPlanner(base.BasePlanner):
29 """Node Resource Consolidation planner implementation
31 This implementation preserves the original order of actions in the
32 solution and try to parallelize actions which have the same action type.
34 *Limitations*
36 - This is a proof of concept that is not meant to be used in production
37 """
39 def create_action(self,
40 action_plan_id,
41 action_type,
42 input_parameters=None):
43 uuid = utils.generate_uuid()
44 action = {
45 'uuid': uuid,
46 'action_plan_id': int(action_plan_id),
47 'action_type': action_type,
48 'input_parameters': input_parameters,
49 'state': objects.action.State.PENDING,
50 'parents': None
51 }
53 return action
55 def schedule(self, context, audit_id, solution):
56 LOG.debug('Creating an action plan for the audit uuid: %s', audit_id)
57 action_plan = self._create_action_plan(context, audit_id, solution)
59 actions = list(solution.actions)
60 if len(actions) == 0:
61 LOG.warning("The action plan is empty")
62 action_plan.state = objects.action_plan.State.SUCCEEDED
63 action_plan.save()
64 return action_plan
66 node_disabled_actions = []
67 node_enabled_actions = []
68 node_migrate_actions = {}
69 for action in actions:
70 action_type = action.get('action_type')
71 parameters = action.get('input_parameters')
72 json_action = self.create_action(
73 action_plan_id=action_plan.id,
74 action_type=action_type,
75 input_parameters=parameters)
76 # classing actions
77 if action_type == 'change_nova_service_state':
78 if parameters.get('state') == (
79 element.ServiceState.DISABLED.value):
80 node_disabled_actions.append(json_action)
81 else:
82 node_enabled_actions.append(json_action)
83 elif action_type == 'migrate':
84 source_node = parameters.get('source_node')
85 if source_node in node_migrate_actions:
86 node_migrate_actions[source_node].append(json_action)
87 else:
88 node_migrate_actions[source_node] = [json_action]
89 else:
90 raise exception.UnsupportedActionType(
91 action_type=action.get("action_type"))
93 # creating actions
94 mig_parents = []
95 for action in node_disabled_actions:
96 mig_parents.append(action['uuid'])
97 self._create_action(context, action)
99 enabled_parents = []
100 for actions in node_migrate_actions.values():
101 enabled_parents.append(actions[-1].get('uuid'))
102 pre_action_uuid = []
103 for action in actions:
104 action['parents'] = mig_parents + pre_action_uuid
105 pre_action_uuid = [action['uuid']]
106 self._create_action(context, action)
108 for action in node_enabled_actions:
109 action['parents'] = enabled_parents
110 self._create_action(context, action)
112 self._create_efficacy_indicators(
113 context, action_plan.id, solution.efficacy_indicators)
115 return action_plan
117 def _create_action_plan(self, context, audit_id, solution):
118 strategy = objects.Strategy.get_by_name(
119 context, solution.strategy.name)
121 action_plan_dict = {
122 'uuid': utils.generate_uuid(),
123 'audit_id': audit_id,
124 'strategy_id': strategy.id,
125 'state': objects.action_plan.State.RECOMMENDED,
126 'global_efficacy': solution.global_efficacy,
127 }
129 new_action_plan = objects.ActionPlan(context, **action_plan_dict)
130 new_action_plan.create()
132 return new_action_plan
134 def _create_efficacy_indicators(self, context, action_plan_id, indicators):
135 efficacy_indicators = []
136 for indicator in indicators: 136 ↛ 137line 136 didn't jump to line 137 because the loop on line 136 never started
137 efficacy_indicator_dict = {
138 'uuid': utils.generate_uuid(),
139 'name': indicator.name,
140 'description': indicator.description,
141 'unit': indicator.unit,
142 'value': indicator.value,
143 'action_plan_id': action_plan_id,
144 }
145 new_efficacy_indicator = objects.EfficacyIndicator(
146 context, **efficacy_indicator_dict)
147 new_efficacy_indicator.create()
149 efficacy_indicators.append(new_efficacy_indicator)
150 return efficacy_indicators
152 def _create_action(self, context, _action):
153 try:
154 LOG.debug("Creating the %s in the Watcher database",
155 _action.get("action_type"))
157 new_action = objects.Action(context, **_action)
158 new_action.create()
160 return new_action
161 except Exception as exc:
162 LOG.exception(exc)
163 raise