Coverage for watcher/common/metal_helper/maas.py: 65%
64 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# Copyright 2023 Cloudbase Solutions
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from oslo_config import cfg
17from oslo_log import log
19from watcher.common import exception
20from watcher.common.metal_helper import base
21from watcher.common.metal_helper import constants as metal_constants
22from watcher.common import utils
24CONF = cfg.CONF
25LOG = log.getLogger(__name__)
27try:
28 from maas.client import enum as maas_enum
29except ImportError:
30 maas_enum = None
33class MaasNode(base.BaseMetalNode):
34 hv_up_when_powered_off = False
36 def __init__(self, maas_node, nova_node, maas_client):
37 super().__init__(nova_node)
39 self._maas_client = maas_client
40 self._maas_node = maas_node
42 def get_power_state(self):
43 maas_state = utils.async_compat_call(
44 self._maas_node.query_power_state,
45 timeout=CONF.maas_client.timeout)
47 # python-libmaas may not be available, so we'll avoid a global
48 # variable.
49 power_states_map = {
50 maas_enum.PowerState.ON: metal_constants.PowerState.ON,
51 maas_enum.PowerState.OFF: metal_constants.PowerState.OFF,
52 maas_enum.PowerState.ERROR: metal_constants.PowerState.ERROR,
53 maas_enum.PowerState.UNKNOWN: metal_constants.PowerState.UNKNOWN,
54 }
55 return power_states_map.get(maas_state,
56 metal_constants.PowerState.UNKNOWN)
58 def get_id(self):
59 return self._maas_node.system_id
61 def power_on(self):
62 LOG.info("Powering on MAAS node: %s %s",
63 self._maas_node.fqdn,
64 self._maas_node.system_id)
65 utils.async_compat_call(
66 self._maas_node.power_on,
67 timeout=CONF.maas_client.timeout)
69 def power_off(self):
70 LOG.info("Powering off MAAS node: %s %s",
71 self._maas_node.fqdn,
72 self._maas_node.system_id)
73 utils.async_compat_call(
74 self._maas_node.power_off,
75 timeout=CONF.maas_client.timeout)
78class MaasHelper(base.BaseMetalHelper):
79 def __init__(self, *args, **kwargs):
80 super().__init__(*args, **kwargs)
81 if not maas_enum: 81 ↛ 82line 81 didn't jump to line 82 because the condition on line 81 was never true
82 raise exception.UnsupportedError(
83 "MAAS client unavailable. Please install python-libmaas.")
85 @property
86 def _client(self):
87 if not getattr(self, "_cached_client", None):
88 self._cached_client = self._osc.maas()
89 return self._cached_client
91 def list_compute_nodes(self):
92 out_list = []
93 node_list = utils.async_compat_call(
94 self._client.machines.list,
95 timeout=CONF.maas_client.timeout)
97 compute_nodes = self.nova_client.hypervisors.list()
98 compute_node_map = dict()
99 for compute_node in compute_nodes:
100 compute_node_map[compute_node.hypervisor_hostname] = compute_node
102 for node in node_list:
103 hypervisor_node = compute_node_map.get(node.fqdn)
104 if not hypervisor_node:
105 LOG.info('Cannot find hypervisor %s', node.fqdn)
106 continue
108 out_node = MaasNode(node, hypervisor_node, self._client)
109 out_list.append(out_node)
111 return out_list
113 def _get_compute_node_by_hostname(self, hostname):
114 compute_nodes = self.nova_client.hypervisors.search(
115 hostname, detailed=True)
116 for compute_node in compute_nodes: 116 ↛ exitline 116 didn't return from function '_get_compute_node_by_hostname' because the loop on line 116 didn't complete
117 if compute_node.hypervisor_hostname == hostname:
118 return compute_node
120 def get_node(self, node_id):
121 maas_node = utils.async_compat_call(
122 self._client.machines.get, node_id,
123 timeout=CONF.maas_client.timeout)
124 compute_node = self._get_compute_node_by_hostname(maas_node.fqdn)
125 return MaasNode(maas_node, compute_node, self._client)