Coverage for watcher/applier/actions/change_node_power_state.py: 85%
57 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# Copyright (c) 2017 ZTE
3#
4# Authors: Li Canwei
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
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
20import time
22from oslo_log import log
24from watcher._i18n import _
25from watcher.applier.actions import base
26from watcher.common import exception
27from watcher.common.metal_helper import constants as metal_constants
28from watcher.common.metal_helper import factory as metal_helper_factory
30LOG = log.getLogger(__name__)
33class ChangeNodePowerState(base.BaseAction):
34 """Compute node power on/off
36 By using this action, you will be able to on/off the power of a
37 compute node.
39 The action schema is::
41 schema = Schema({
42 'resource_id': str,
43 'state': str,
44 })
46 The `resource_id` references a baremetal node id (list of available
47 ironic nodes is returned by this command: ``ironic node-list``).
48 The `state` value should either be `on` or `off`.
49 """
51 STATE = 'state'
53 @property
54 def schema(self):
55 return {
56 'type': 'object',
57 'properties': {
58 'resource_id': {
59 'type': 'string',
60 "minlength": 1
61 },
62 'resource_name': {
63 'type': 'string',
64 "minlength": 1
65 },
66 'state': {
67 'type': 'string',
68 'enum': [metal_constants.PowerState.ON.value,
69 metal_constants.PowerState.OFF.value]
70 }
71 },
72 'required': ['resource_id', 'state'],
73 'additionalProperties': False,
74 }
76 @property
77 def node_uuid(self):
78 return self.resource_id
80 @property
81 def state(self):
82 return self.input_parameters.get(self.STATE)
84 def execute(self):
85 target_state = self.state
86 return self._node_manage_power(target_state)
88 def revert(self):
89 if self.state == metal_constants.PowerState.ON.value:
90 target_state = metal_constants.PowerState.OFF.value
91 elif self.state == metal_constants.PowerState.OFF.value: 91 ↛ 93line 91 didn't jump to line 93 because the condition on line 91 was always true
92 target_state = metal_constants.PowerState.ON.value
93 return self._node_manage_power(target_state)
95 def _node_manage_power(self, state, retry=60):
96 if state is None: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true
97 raise exception.IllegalArgumentException(
98 message=_("The target state is not defined"))
100 metal_helper = metal_helper_factory.get_helper(self.osc)
101 node = metal_helper.get_node(self.node_uuid)
102 current_state = node.get_power_state()
104 if state == current_state.value: 104 ↛ 105line 104 didn't jump to line 105 because the condition on line 104 was never true
105 return True
107 if state == metal_constants.PowerState.OFF.value:
108 compute_node = node.get_hypervisor_node().to_dict()
109 if (compute_node['running_vms'] == 0): 109 ↛ 112line 109 didn't jump to line 112 because the condition on line 109 was always true
110 node.set_power_state(state)
111 else:
112 LOG.warning(
113 "Compute node %s has %s running vms and will "
114 "NOT be shut off.",
115 compute_node["hypervisor_hostname"],
116 compute_node['running_vms'])
117 return False
118 else:
119 node.set_power_state(state)
121 node = metal_helper.get_node(self.node_uuid)
122 while node.get_power_state() == current_state and retry:
123 time.sleep(10)
124 retry -= 1
125 node = metal_helper.get_node(self.node_uuid)
126 if retry > 0: 126 ↛ 129line 126 didn't jump to line 129 because the condition on line 126 was always true
127 return True
128 else:
129 return False
131 def pre_condition(self):
132 pass
134 def post_condition(self):
135 pass
137 def get_description(self):
138 """Description of the action"""
139 return ("Compute node power on/off through Ironic or MaaS.")