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

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# 

19 

20import time 

21 

22from oslo_log import log 

23 

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 

29 

30LOG = log.getLogger(__name__) 

31 

32 

33class ChangeNodePowerState(base.BaseAction): 

34 """Compute node power on/off 

35 

36 By using this action, you will be able to on/off the power of a 

37 compute node. 

38 

39 The action schema is:: 

40 

41 schema = Schema({ 

42 'resource_id': str, 

43 'state': str, 

44 }) 

45 

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

50 

51 STATE = 'state' 

52 

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 } 

75 

76 @property 

77 def node_uuid(self): 

78 return self.resource_id 

79 

80 @property 

81 def state(self): 

82 return self.input_parameters.get(self.STATE) 

83 

84 def execute(self): 

85 target_state = self.state 

86 return self._node_manage_power(target_state) 

87 

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) 

94 

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

99 

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

103 

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 

106 

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) 

120 

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 

130 

131 def pre_condition(self): 

132 pass 

133 

134 def post_condition(self): 

135 pass 

136 

137 def get_description(self): 

138 """Description of the action""" 

139 return ("Compute node power on/off through Ironic or MaaS.")