ansible-collections-openstack/plugins/modules/baremetal_node_action.py
Jakob Meng 65a7e74b2b Refactored baremetal_node_action module
Sorted argument specs and documentation of the module.

Refactored baremetal_node_action module to be a subclass of the
OpenStackModule class.

Redefined baremetal_node_info's module attributes 'id' and 'uuid'
as aliases of the 'name' attribute because modules in this collection
do not differentiate between ids and names. The previous revision of
baremetal_node_info module had the same behaviour implemented but did
not make the relationship between 'id'/'uuid' and 'name' explicit.

Changed types and/or choices of module attributes 'deploy',
'maintenance', 'power' and 'state' to match what has been described
in DOCUMENTATION string and to get rid of the non-Ansible'ish and
inconsistent parsing of input values in _is_true() and _is_false()
functions. Ansible can handle argument types for us, no need to
implement it ourselfs.

Dropped deprecated ironic_url attribute from DOCUMENTATION docstring.
Dropped wait and timeout attributes from DOCUMENTATION because their
docstrings will be added via documentation fragment.

Dropped attribute 'result' from module results because in our modules
we consistently do not explain what we do in modules.

Updated DOCUMENTATION, EXAMPLES and added RETURN docstrings.

Refactored the change logic for maintenance, power state and state,
eliminating unreachable or broken code.

Dropped wait attribute from DOCUMENTATION because its docstring will
be added via documentation fragment.

Kept timeout attribute in DOCUMENTATION and argument_spec because
it has a high(er) default value, to account for long node
(de)activiation times, than what e.g. the generic doc fragment
specifies.

Change-Id: I991f23c16583da106105677d75b3651959280d98
2022-10-25 11:30:46 +02:00

273 lines
9.8 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2015, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = r'''
---
module: baremetal_node_action
short_description: Activate/Deactivate Bare Metal nodes from OpenStack
author: OpenStack Ansible SIG
description:
- Deploy to Bare Metal nodes controlled by Ironic.
options:
deploy:
description:
- Indicates if the resource should be deployed. Allows for deployment
logic to be disengaged and control of the node power or maintenance
state to be changed.
type: bool
default: true
config_drive:
description:
- A configdrive file or HTTP(S) URL that will be passed along to the
node.
type: raw
instance_info:
description:
- Definition of the instance information which is used to deploy
the node. This information is only required when I(state) is
set to C(present) or C(on).
type: dict
suboptions:
image_source:
description:
- An HTTP(S) URL where the image can be retrieved from.
image_checksum:
description:
- The checksum of image_source.
image_disk_format:
description:
- The type of image that has been requested to be deployed.
maintenance:
description:
- Set node into maintenance mode.
- The power state as controlled with I(power) will not be changed
when maintenance mode of a node is changed.
type: bool
maintenance_reason:
description:
- A string expression regarding the reason a node is in a
maintenance mode.
type: str
name:
description:
- Name or ID of the Bare Metal node.
type: str
required: true
aliases: ['id', 'uuid']
power:
description:
- A setting to allow power state to be asserted allowing nodes
that are not yet deployed to be powered on, and nodes that
are deployed to be powered off.
- I(power) can be C(present), C(absent), C(maintenance), C(on) or
C(off).
choices: ['present', 'absent', 'maintenance', 'on', 'off']
default: present
type: str
state:
description:
- Indicates desired state of the resource.
- I(state) can be C(present), C(absent), C(maintenance), C(on) or
C(off).
choices: ['present', 'absent', 'maintenance', 'on', 'off']
default: present
type: str
timeout:
description:
- Number of seconds to wait for the node activation or deactivation
to complete.
type: int
default: 1800
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = r'''
# Activate a node by booting an image with a configdrive attached
- openstack.cloud.baremetal_node_action:
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
deploy: yes
cloud: "openstack"
config_drive: "http://192.168.1.1/host-configdrive.iso"
maintenance: no
power: present
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
state: present
# Activate a node by booting an image with a configdrive json object
- openstack.cloud.baremetal_node_action:
auth_type: None
auth:
endpoint: "http://192.168.1.1:6385/"
id: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
config_drive:
meta_data:
hostname: node1
public_keys:
default: ssh-rsa AAA...BBB==
delegate_to: localhost
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
'''
RETURN = r'''
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
OpenStackModule
)
class BaremetalNodeActionModule(OpenStackModule):
argument_spec = dict(
config_drive=dict(type='raw'),
deploy=dict(type='bool', default=True),
instance_info=dict(type='dict'),
maintenance=dict(type='bool'),
maintenance_reason=dict(),
name=dict(required=True, aliases=['id', 'uuid']),
power=dict(default='present',
choices=['present', 'absent', 'maintenance', 'on', 'off']),
state=dict(default='present',
choices=['present', 'absent', 'maintenance', 'on', 'off']),
timeout=dict(type='int', default=1800), # increased default value
)
module_kwargs = dict(
required_if=[
('state', 'present', ('instance_info',)),
],
)
def run(self):
# Fail early on invalid arguments
config_drive = self.params['config_drive']
if config_drive and not isinstance(config_drive, (str, dict)):
self.fail_json(msg='config_drive must be of type str or dict,'
' not {0}'.format(type(config_drive)))
# User has requested desired state to be in maintenance state.
if self.params['state'] == 'maintenance':
if self.params['maintenance'] is False:
self.fail_json(
msg='state=maintenance contradicts with maintenance=false')
self.params['maintenance'] = True
name_or_id = self.params['name']
node = self.conn.baremetal.find_node(name_or_id, ignore_missing=False)
if node['provision_state'] in ['cleaning',
'deleting',
'wait call-back']:
self.fail_json(msg='Node is in {0} state, cannot act upon the'
' request as the node is in a transition'
' state'.format(node['provision_state']))
changed = False
# Update maintenance state
if self.params['maintenance']:
maintenance_reason = self.params['maintenance_reason']
if not node['maintenance'] \
or node['maintenance_reason'] != maintenance_reason:
self.conn.baremetal.set_node_maintenance(
node['id'], reason=maintenance_reason)
self.exit_json(changed=True)
else: # self.params['maintenance'] is False
if node['maintenance']:
self.conn.baremetal.unset_node_maintenance(node['id'])
if node['provision_state'] in 'active':
# Maintenance state changed
self.exit_json(changed=True)
changed = True
node = self.conn.baremetal.get_node(node['id'])
# Update power state
if node['power_state'] == 'power on':
if self.params['power'] in ['absent', 'off']:
# User has requested the node be powered off.
self.conn.baremetal.set_node_power_state(node['id'],
'power off')
self.exit_json(changed=True)
elif node['power_state'] == 'power off':
if self.params['power'] not in ['absent', 'off'] \
or self.params['state'] not in ['absent', 'off']:
# In the event the power has been toggled on and
# deployment has been requested, we need to skip this
# step.
if self.params['power'] == 'present' \
and not self.params['deploy']:
# Node is powered down when it is not awaiting to be
# provisioned
self.conn.baremetal.set_node_power_state(node['id'],
'power on')
changed = True
node = self.conn.baremetal.get_node(node['id'])
else:
self.fail_json(msg='Node has unknown power state {0}'
.format(node['power_state']))
if self.params['state'] in ['present', 'on']:
if not self.params['deploy']:
# User request has explicitly disabled deployment logic
self.exit_json(changed=changed)
if 'active' in node['provision_state']:
# Node already in an active state
self.exit_json(changed=changed)
# TODO(TheJulia): Update instance info, however info is
# deployment specific. Perhaps consider adding rebuild
# support, although there is a known desire to remove
# rebuild support from Ironic at some point in the future.
self.conn.baremetal.update_node(
node['id'],
instance_info=self.params['instance_info'])
self.conn.baremetal.validate_node(node['id'])
self.conn.baremetal.set_node_provision_state(
node['id'],
target='active',
config_drive=self.params['config_drive'],
wait=self.params['wait'],
timeout=self.params['timeout'])
# TODO(TheJulia): Add more error checking..
self.exit_json(changed=True)
elif node['provision_state'] not in 'deleted':
self.conn.baremetal.update_node(node['id'], instance_info={})
self.conn.baremetal.set_node_provision_state(
node['id'],
target='deleted',
wait=self.params['wait'],
timeout=self.params['timeout'])
self.exit_json(changed=True)
else:
# self.params['state'] in ['absent', 'off']
# and node['provision_state'] in 'deleted'
self.exit_json(changed=changed)
def main():
module = BaremetalNodeActionModule()
module()
if __name__ == "__main__":
main()