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

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# 

16 

17from oslo_log import log 

18 

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 

24 

25LOG = log.getLogger(__name__) 

26 

27 

28class NodeResourceConsolidationPlanner(base.BasePlanner): 

29 """Node Resource Consolidation planner implementation 

30 

31 This implementation preserves the original order of actions in the 

32 solution and try to parallelize actions which have the same action type. 

33 

34 *Limitations* 

35 

36 - This is a proof of concept that is not meant to be used in production 

37 """ 

38 

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 } 

52 

53 return action 

54 

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) 

58 

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 

65 

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")) 

92 

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) 

98 

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) 

107 

108 for action in node_enabled_actions: 

109 action['parents'] = enabled_parents 

110 self._create_action(context, action) 

111 

112 self._create_efficacy_indicators( 

113 context, action_plan.id, solution.efficacy_indicators) 

114 

115 return action_plan 

116 

117 def _create_action_plan(self, context, audit_id, solution): 

118 strategy = objects.Strategy.get_by_name( 

119 context, solution.strategy.name) 

120 

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 } 

128 

129 new_action_plan = objects.ActionPlan(context, **action_plan_dict) 

130 new_action_plan.create() 

131 

132 return new_action_plan 

133 

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() 

148 

149 efficacy_indicators.append(new_efficacy_indicator) 

150 return efficacy_indicators 

151 

152 def _create_action(self, context, _action): 

153 try: 

154 LOG.debug("Creating the %s in the Watcher database", 

155 _action.get("action_type")) 

156 

157 new_action = objects.Action(context, **_action) 

158 new_action.create() 

159 

160 return new_action 

161 except Exception as exc: 

162 LOG.exception(exc) 

163 raise