ansible-collections-openstack/plugins/modules/baremetal_node_action.py
Dmitry Tantsur 8b98452cbb Refactor ironic authentication into a new module_utils module
This change merely moves the code to one location. The next logical
step would be to make IronicModule inherit the common ansible module.

Change-Id: Iec0ca1e33de6ebc36d7664941eafe1d77203d8f2
2020-10-26 11:05:31 +00:00

363 lines
13 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 = '''
---
module: baremetal_node_action
short_description: Activate/Deactivate Bare Metal Resources from OpenStack
author: OpenStack Ansible SIG
description:
- Deploy to nodes controlled by Ironic.
options:
name:
description:
- Name of the node to create.
type: str
state:
description:
- Indicates desired state of the resource.
- I(state) can be C('present'), C('absent'), C('maintenance') or C('off').
default: present
type: str
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: str
default: 'yes'
uuid:
description:
- globally unique identifier (UUID) to be given to the resource.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
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 an instance is
set to present.
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.
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') or C('off').
default: present
type: str
maintenance:
description:
- A setting to allow the direct control if a node is in
maintenance mode.
- I(maintenance) can be C('yes'), C('no'), C('True'), or C('False').
type: str
maintenance_reason:
description:
- A string expression regarding the reason a node is in a
maintenance mode.
type: str
wait:
description:
- A boolean value instructing the module to wait for node
activation or deactivation to complete before returning.
type: bool
default: 'no'
timeout:
description:
- An integer value representing the number of seconds to
wait for the node activation or deactivation to complete.
default: 1800
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Activate a node by booting an image with a configdrive attached
- openstack.cloud.baremetal_node_action:
cloud: "openstack"
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
state: present
power: present
deploy: True
maintenance: False
config_drive: "http://192.168.1.1/host-configdrive.iso"
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
# Activate a node by booting an image with a configdrive json object
- openstack.cloud.baremetal_node_action:
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
auth_type: None
ironic_url: "http://192.168.1.1:6385/"
config_drive:
meta_data:
hostname: node1
public_keys:
default: ssh-rsa AAA...BBB==
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
'''
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def _is_true(value):
true_values = [True, 'yes', 'Yes', 'True', 'true', 'present', 'on']
if value in true_values:
return True
return False
def _is_false(value):
false_values = [False, None, 'no', 'No', 'False', 'false', 'absent', 'off']
if value in false_values:
return True
return False
def _check_set_maintenance(module, cloud, node):
if _is_true(module.params['maintenance']):
if _is_false(node['maintenance']):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node has been set into "
"maintenance mode")
else:
# User has requested maintenance state, node is already in the
# desired state, checking to see if the reason has changed.
if (str(node['maintenance_reason']) not in
str(module.params['maintenance_reason'])):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node maintenance reason "
"updated, cannot take any "
"additional action.")
elif _is_false(module.params['maintenance']):
if node['maintenance'] is True:
cloud.remove_machine_from_maintenance(node['uuid'])
return True
else:
module.fail_json(msg="maintenance parameter was set but a valid "
"the value was not recognized.")
return False
def _check_set_power_state(module, cloud, node):
if 'power on' in str(node['power_state']):
if _is_false(module.params['power']):
# User has requested the node be powered off.
cloud.set_machine_power_off(node['uuid'])
module.exit_json(changed=True, msg="Power requested off")
if 'power off' in str(node['power_state']):
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
return False
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
module.exit_json(
changed=False,
msg="Power for node is %s, node must be reactivated "
"OR set to state absent"
)
# In the event the power has been toggled on and
# deployment has been requested, we need to skip this
# step.
if (
_is_true(module.params['power'])
and _is_false(module.params['deploy'])
):
# Node is powered down when it is not awaiting to be provisioned
cloud.set_machine_power_on(node['uuid'])
return True
# Default False if no action has been taken.
return False
def main():
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
instance_info=dict(type='dict', required=False),
config_drive=dict(type='raw', required=False),
state=dict(required=False, default='present'),
maintenance=dict(required=False),
maintenance_reason=dict(required=False),
power=dict(required=False, default='present'),
deploy=dict(required=False, default='yes'),
wait=dict(type='bool', required=False, default=False),
timeout=dict(required=False, type='int', default=1800),
)
module_kwargs = openstack_module_kwargs()
module = IronicModule(argument_spec, **module_kwargs)
if (
module.params['config_drive']
and not isinstance(module.params['config_drive'], (str, dict))
):
config_drive_type = type(module.params['config_drive'])
msg = ('argument config_drive is of type %s and we expected'
' str or dict') % config_drive_type
module.fail_json(msg=msg)
node_id = _choose_id_value(module)
if not node_id:
module.fail_json(msg="A uuid or name value must be defined "
"to use this module.")
sdk, cloud = openstack_cloud_from_module(module)
try:
node = cloud.get_machine(node_id)
if node is None:
module.fail_json(msg="node not found")
uuid = node['uuid']
instance_info = module.params['instance_info']
changed = False
wait = module.params['wait']
timeout = module.params['timeout']
# User has requested desired state to be in maintenance state.
if module.params['state'] == 'maintenance':
module.params['maintenance'] = True
if node['provision_state'] in [
'cleaning',
'deleting',
'wait call-back']:
module.fail_json(msg="Node is in %s state, cannot act upon the "
"request as the node is in a transition "
"state" % node['provision_state'])
# TODO(TheJulia) This is in-development code, that requires
# code in the shade library that is still in development.
if _check_set_maintenance(module, cloud, node):
if node['provision_state'] in 'active':
module.exit_json(changed=True,
result="Maintenance state changed")
changed = True
node = cloud.get_machine(node_id)
if _check_set_power_state(module, cloud, node):
changed = True
node = cloud.get_machine(node_id)
if _is_true(module.params['state']):
if _is_false(module.params['deploy']):
module.exit_json(
changed=changed,
result="User request has explicitly disabled "
"deployment logic"
)
if 'active' in node['provision_state']:
module.exit_json(
changed=changed,
result="Node already in an active state."
)
if instance_info is None:
module.fail_json(
changed=changed,
msg="When setting an instance to present, "
"instance_info is a required variable.")
# 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.
cloud.update_machine(uuid, instance_info=instance_info)
cloud.validate_node(uuid)
if not wait:
cloud.activate_node(uuid, module.params['config_drive'])
else:
cloud.activate_node(
uuid,
configdrive=module.params['config_drive'],
wait=wait,
timeout=timeout)
# TODO(TheJulia): Add more error checking..
module.exit_json(changed=changed, result="node activated")
elif _is_false(module.params['state']):
if node['provision_state'] not in "deleted":
cloud.update_machine(uuid, instance_info={})
if not wait:
cloud.deactivate_node(uuid)
else:
cloud.deactivate_node(
uuid,
wait=wait,
timeout=timeout)
module.exit_json(changed=True, result="deleted")
else:
module.exit_json(changed=False, result="node not found")
else:
module.fail_json(msg="State must be present, absent, "
"maintenance, off")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()