tobiko/tobiko/openstack/ironic/_node.py

148 lines
5.3 KiB
Python

# Copyright 2021 Red Hat
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import typing
import ironicclient.v1.client
from oslo_log import log
import tobiko
from tobiko.openstack.ironic import _client
LOG = log.getLogger(__name__)
IronicNode = typing.Union[ironicclient.v1.node.Node]
IronicNodeType = typing.Union[str, IronicNode]
def get_node_id(node: typing.Optional[IronicNodeType] = None,
node_id: typing.Optional[str] = None) -> str:
if node_id is None:
if isinstance(node, str):
node_id = node
else:
assert node is not None
node_id = node.uuid
return node_id
def get_node(node: typing.Optional[IronicNodeType] = None,
node_id: typing.Optional[str] = None,
client: _client.IronicClientType = None,
**params) -> IronicNode:
node_id = get_node_id(node=node, node_id=node_id)
return _client.ironic_client(client).node.get(node_id, **params)
class WaitForNodePowerStateError(tobiko.TobikoException):
message = ("Node {node_id} not changing power state from "
"{node_power_state} to {power_state}")
class WaitForNodePowerStateTimeout(WaitForNodePowerStateError):
message = ("Node {node_id} didn't change its state from "
"{node_power_state} to {power_state} state after "
"{timeout} seconds")
IRONIC_NODE_TRANSIENT_POWER_STATES: typing.Dict[str, typing.List[str]] = {
'power on': ['power off'],
'power off': ['power on'],
}
def wait_for_node_power_state(
node: IronicNodeType,
power_state: str,
client: _client.IronicClientType = None,
timeout: tobiko.Seconds = None,
sleep_time: tobiko.Seconds = None,
transient_status: typing.Optional[typing.List[str]] = None) -> \
IronicNode:
if transient_status is None:
transient_status = IRONIC_NODE_TRANSIENT_POWER_STATES.get(
power_state) or []
node_id = get_node_id(node)
for attempt in tobiko.retry(timeout=timeout,
interval=sleep_time,
default_timeout=300.,
default_interval=5.):
_node = get_node(node_id=node_id, client=client)
if _node.power_state == power_state:
break
if _node.power_state not in transient_status:
raise WaitForNodePowerStateError(
node_id=node_id,
node_power_state=_node.power_state,
power_state=power_state)
if attempt.is_last:
raise WaitForNodePowerStateTimeout(
node_id=node_id,
node_power_state=_node.power_state,
power_state=power_state,
timeout=timeout)
LOG.debug(f"Waiting for Ironic node '{node_id}' power state to get "
f"from {_node.power_state} to {power_state}...")
else:
raise RuntimeError("Retry look break before timing out")
return _node
def power_off_node(node: IronicNodeType,
soft=False,
client: _client.IronicClientType = None,
timeout: tobiko.Seconds = None,
sleep_time: tobiko.Seconds = None) \
-> IronicNode:
client = _client.ironic_client(client)
node = get_node(node=node, client=client)
if node.power_state == 'power off':
return node
LOG.info(f"Power off baremetal node '{node.uuid}' "
f"(power state = '{node.power_state}').")
client.node.set_power_state(node.uuid,
state='off',
soft=soft)
return wait_for_node_power_state(node=node.uuid,
power_state='power off',
client=client,
timeout=timeout,
sleep_time=sleep_time)
def power_on_node(node: IronicNodeType,
client: _client.IronicClientType = None,
timeout: tobiko.Seconds = None,
sleep_time: tobiko.Seconds = None) -> \
IronicNode:
client = _client.ironic_client(client)
node = get_node(node=node, client=client)
if node.power_state == 'power on':
return node
LOG.info(f"Power on baremetal node '{node.uuid}' "
f"(power_state='{node.power_state}').")
client.node.set_power_state(node_id=node.uuid,
state='on')
return wait_for_node_power_state(node=node.uuid,
power_state='power on',
client=client,
timeout=timeout,
sleep_time=sleep_time)