148 lines
5.3 KiB
Python
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)
|