
With "extends_documentation_fragment: ['openstack.cloud.openstack']" it is not necessary to list required Python libraries in section 'requirements' of DOCUMENTATION docstring in modules. Ansible will merge requirements from doc fragments and DOCUMENTATION docstring which previously resulted in duplicates such as in server module [0]: * openstacksdk * openstacksdk >= 0.36, < 0.99.0 * python >= 3.6 When removing the 'requirements' section from server module, then Ansible will list openstacksdk once only: * openstacksdk >= 0.36, < 0.99.0 * python >= 3.6 To see what documentation Ansible will produce for server module run: ansible-doc --type module openstack.cloud.server [0] https://docs.ansible.com/ansible/latest/collections/openstack/\ cloud/server_module.html Change-Id: I727ed95ee480bb644b5a533f6a9526973677064c
689 lines
24 KiB
Python
689 lines
24 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# (c) 2014, 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
|
|
short_description: Create/Delete Bare Metal Resources from OpenStack
|
|
author: OpenStack Ansible SIG
|
|
description:
|
|
- Create or Remove Ironic nodes from OpenStack.
|
|
options:
|
|
bios_interface:
|
|
description:
|
|
- The bios interface for this node, e.g. C(no-bios).
|
|
type: str
|
|
boot_interface:
|
|
description:
|
|
- The boot interface for this node, e.g. C(pxe).
|
|
type: str
|
|
chassis_id:
|
|
description:
|
|
- Associate the node with a pre-defined chassis.
|
|
type: str
|
|
aliases: ['chassis_uuid']
|
|
console_interface:
|
|
description:
|
|
- The console interface for this node, e.g. C(no-console).
|
|
type: str
|
|
deploy_interface:
|
|
description:
|
|
- The deploy interface for this node, e.g. C(iscsi).
|
|
type: str
|
|
driver:
|
|
description:
|
|
- The name of the Ironic Driver to use with this node.
|
|
- Required when I(state) is C(present)
|
|
type: str
|
|
driver_info:
|
|
description:
|
|
- Information for this node's driver. Will vary based on which
|
|
driver is in use. Any sub-field which is populated will be validated
|
|
during creation. For compatibility reasons sub-fields `power`,
|
|
`deploy`, `management` and `console` are flattened.
|
|
required: true
|
|
type: dict
|
|
id:
|
|
description:
|
|
- ID to be given to the baremetal node. Will be auto-generated on
|
|
creation if not specified, and I(name) is specified.
|
|
- Definition of I(id) will always take precedence over I(name).
|
|
type: str
|
|
aliases: ['uuid']
|
|
inspect_interface:
|
|
description:
|
|
- The interface used for node inspection, e.g. C(no-inspect).
|
|
type: str
|
|
management_interface:
|
|
description:
|
|
- The interface for out-of-band management of this node, e.g.
|
|
"ipmitool".
|
|
type: str
|
|
name:
|
|
description:
|
|
- unique name identifier to be given to the resource.
|
|
type: str
|
|
network_interface:
|
|
description:
|
|
- The network interface provider to use when describing
|
|
connections for this node.
|
|
type: str
|
|
nics:
|
|
description:
|
|
- 'A list of network interface cards, eg, C( - mac: aa:bb:cc:aa:bb:cc)'
|
|
- This node attribute cannot be updated.
|
|
required: true
|
|
type: list
|
|
elements: dict
|
|
suboptions:
|
|
mac:
|
|
description: The MAC address of the network interface card.
|
|
type: str
|
|
required: true
|
|
power_interface:
|
|
description:
|
|
- The interface used to manage power actions on this node, e.g.
|
|
C(ipmitool).
|
|
type: str
|
|
properties:
|
|
description:
|
|
- Definition of the physical characteristics of this node
|
|
- Used for scheduling purposes
|
|
type: dict
|
|
suboptions:
|
|
cpu_arch:
|
|
description:
|
|
- CPU architecture (x86_64, i686, ...)
|
|
type: str
|
|
cpus:
|
|
description:
|
|
- Number of CPU cores this machine has
|
|
type: str
|
|
memory_mb:
|
|
description:
|
|
- Amount of RAM in MB this machine has
|
|
aliases: ['ram']
|
|
type: str
|
|
local_gb:
|
|
description:
|
|
- Size in GB of first storage device in this machine (typically
|
|
/dev/sda)
|
|
aliases: ['disk_size']
|
|
type: str
|
|
capabilities:
|
|
description:
|
|
- Special capabilities for this node such as boot_option etc.
|
|
- For more information refer to
|
|
U(https://docs.openstack.org/ironic/latest/install/advanced.html).
|
|
type: str
|
|
root_device:
|
|
description:
|
|
- Root disk device hints for deployment.
|
|
- For allowed hints refer to
|
|
U(https://docs.openstack.org/ironic/latest/install/advanced.html).
|
|
type: dict
|
|
raid_interface:
|
|
description:
|
|
- Interface used for configuring raid on this node.
|
|
type: str
|
|
rescue_interface:
|
|
description:
|
|
- Interface used for node rescue, e.g. C(no-rescue).
|
|
type: str
|
|
resource_class:
|
|
description:
|
|
- The specific resource type to which this node belongs.
|
|
type: str
|
|
skip_update_of_masked_password:
|
|
description:
|
|
- Deprecated, no longer used.
|
|
- Updating or specifing a password has not been supported for a while.
|
|
type: bool
|
|
state:
|
|
description:
|
|
- Indicates desired state of the resource
|
|
choices: ['present', 'absent']
|
|
default: present
|
|
type: str
|
|
storage_interface:
|
|
description:
|
|
- Interface used for attaching and detaching volumes on this node, e.g.
|
|
C(cinder).
|
|
type: str
|
|
timeout:
|
|
description:
|
|
- Number of seconds to wait for the newly created node to reach the
|
|
available state.
|
|
type: int
|
|
default: 1800
|
|
vendor_interface:
|
|
description:
|
|
- Interface for all vendor-specific actions on this node, e.g.
|
|
C(no-vendor).
|
|
type: str
|
|
extends_documentation_fragment:
|
|
- openstack.cloud.openstack
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
- name: Enroll a node with some basic properties and driver info
|
|
openstack.cloud.baremetal_node:
|
|
chassis_id: "00000000-0000-0000-0000-000000000001"
|
|
cloud: "devstack"
|
|
driver: "pxe_ipmitool"
|
|
driver_info:
|
|
ipmi_address: "1.2.3.4"
|
|
ipmi_username: "admin"
|
|
ipmi_password: "adminpass"
|
|
id: "00000000-0000-0000-0000-000000000002"
|
|
nics:
|
|
- mac: "aa:bb:cc:aa:bb:cc"
|
|
- mac: "dd:ee:ff:dd:ee:ff"
|
|
properties:
|
|
capabilities: "boot_option:local"
|
|
cpu_arch: "x86_64"
|
|
cpus: 2
|
|
local_gb: 64
|
|
memory_mb: 8192
|
|
root_device:
|
|
wwn: "0x4000cca77fc4dba1"
|
|
'''
|
|
|
|
RETURN = r'''
|
|
node:
|
|
description: Dictionary describing the Bare Metal node.
|
|
type: dict
|
|
returned: On success when I(state) is 'present'.
|
|
contains:
|
|
allocation_id:
|
|
description: The UUID of the allocation associated with the node.
|
|
If not null, will be the same as instance_id (the
|
|
opposite is not always true). Unlike instance_id,
|
|
this field is read-only. Please use the Allocation API
|
|
to remove allocations.
|
|
returned: success
|
|
type: str
|
|
bios_interface:
|
|
description: The bios interface to be used for this node.
|
|
returned: success
|
|
type: str
|
|
boot_interface:
|
|
description: The boot interface for a node, e.g. "pxe".
|
|
returned: success
|
|
type: str
|
|
boot_mode:
|
|
description: The boot mode for a node, either "uefi" or "bios"
|
|
returned: success
|
|
type: str
|
|
chassis_id:
|
|
description: UUID of the chassis associated with this node. May be
|
|
empty or None.
|
|
returned: success
|
|
type: str
|
|
clean_step:
|
|
description: The current clean step.
|
|
returned: success
|
|
type: str
|
|
conductor:
|
|
description: |
|
|
The conductor currently servicing a node.
|
|
returned: success
|
|
type: str
|
|
conductor_group:
|
|
description: The conductor group for a node.
|
|
returned: success
|
|
type: str
|
|
console_interface:
|
|
description: The console interface for a node, e.g. "no-console".
|
|
returned: success
|
|
type: str
|
|
created_at:
|
|
description: Bare Metal node created at timestamp.
|
|
returned: success
|
|
type: str
|
|
deploy_interface:
|
|
description: The deploy interface for a node, e.g. "direct".
|
|
returned: success
|
|
type: str
|
|
deploy_step:
|
|
description: The current deploy step.
|
|
returned: success
|
|
type: str
|
|
driver:
|
|
description: The name of the driver.
|
|
returned: success
|
|
type: str
|
|
driver_info:
|
|
description: All the metadata required by the driver to manage this
|
|
node. List of fields varies between drivers, and can
|
|
be retrieved from the
|
|
/v1/drivers/<DRIVER_NAME>/properties resource.
|
|
returned: success
|
|
type: dict
|
|
driver_internal_info:
|
|
description: Internal metadata set and stored by the node's driver.
|
|
returned: success
|
|
type: dict
|
|
extra:
|
|
description: A set of one or more arbitrary metadata key and value
|
|
pairs.
|
|
returned: success
|
|
type: dict
|
|
fault:
|
|
description: The fault indicates the active fault detected by
|
|
ironic, typically the node is in "maintenance mode".
|
|
None means no fault has been detected by ironic.
|
|
"power failure" indicates ironic failed to retrieve
|
|
power state from this node. There are other possible
|
|
types, e.g., "clean failure" and "rescue abort
|
|
failure".
|
|
returned: success
|
|
type: str
|
|
id:
|
|
description: The UUID for the resource.
|
|
returned: success
|
|
type: str
|
|
inspect_interface:
|
|
description: The interface used for node inspection.
|
|
returned: success
|
|
type: str
|
|
instance_id:
|
|
description: UUID of the Nova instance associated with this node.
|
|
returned: success
|
|
type: str
|
|
instance_info:
|
|
description: Information used to customize the deployed image. May
|
|
include root partition size, a base 64 encoded config
|
|
drive, and other metadata. Note that this field is
|
|
erased automatically when the instance is deleted
|
|
(this is done by requesting the node provision state
|
|
be changed to DELETED).
|
|
returned: success
|
|
type: dict
|
|
is_automated_clean_enabled:
|
|
description: Indicates whether the node will perform automated
|
|
clean or not.
|
|
returned: success
|
|
type: bool
|
|
is_console_enabled:
|
|
description: Indicates whether console access is enabled or
|
|
disabled on this node.
|
|
returned: success
|
|
type: bool
|
|
is_maintenance:
|
|
description: Whether or not this node is currently in "maintenance
|
|
mode". Setting a node into maintenance mode removes it
|
|
from the available resource pool and halts some
|
|
internal automation. This can happen manually (eg, via
|
|
an API request) or automatically when Ironic detects a
|
|
hardware fault that prevents communication with the
|
|
machine.
|
|
returned: success
|
|
type: bool
|
|
is_protected:
|
|
description: Whether the node is protected from undeploying,
|
|
rebuilding and deletion.
|
|
returned: success
|
|
type: bool
|
|
is_retired:
|
|
description: Whether the node is retired and can hence no longer be
|
|
provided, i.e. move from manageable to available, and
|
|
will end up in manageable after cleaning (rather than
|
|
available).
|
|
returned: success
|
|
type: bool
|
|
is_secure_boot:
|
|
description: Indicates whether node is currently booted with
|
|
secure_boot turned on.
|
|
returned: success
|
|
type: bool
|
|
last_error:
|
|
description: Any error from the most recent (last) transaction that
|
|
started but failed to finish.
|
|
returned: success
|
|
type: str
|
|
links:
|
|
description: A list of relative links, including self and bookmark
|
|
links.
|
|
returned: success
|
|
type: list
|
|
maintenance_reason:
|
|
description: User-settable description of the reason why this node
|
|
was placed into maintenance mode
|
|
returned: success
|
|
type: str
|
|
management_interface:
|
|
description: Interface for out-of-band node management.
|
|
returned: success
|
|
type: str
|
|
name:
|
|
description: Human-readable identifier for the node resource. May
|
|
be undefined. Certain words are reserved.
|
|
returned: success
|
|
type: str
|
|
network_interface:
|
|
description: Which Network Interface provider to use when plumbing
|
|
the network connections for this node.
|
|
returned: success
|
|
type: str
|
|
owner:
|
|
description: A string or UUID of the tenant who owns the object.
|
|
returned: success
|
|
type: str
|
|
ports:
|
|
description: List of ironic ports on this node.
|
|
returned: success
|
|
type: list
|
|
port_groups:
|
|
description: List of ironic port groups on this node.
|
|
returned: success
|
|
type: list
|
|
power_interface:
|
|
description: Interface used for performing power actions on the
|
|
node, e.g. "ipmitool".
|
|
returned: success
|
|
type: str
|
|
power_state:
|
|
description: The current power state of this node. Usually, "power
|
|
on" or "power off", but may be "None" if Ironic is
|
|
unable to determine the power state (eg, due to
|
|
hardware failure).
|
|
returned: success
|
|
type: str
|
|
properties:
|
|
description: Physical characteristics of this node. Populated by
|
|
ironic-inspector during inspection. May be edited via
|
|
the REST API at any time.
|
|
returned: success
|
|
type: dict
|
|
protected_reason:
|
|
description: The reason the node is marked as protected.
|
|
returned: success
|
|
type: str
|
|
provision_state:
|
|
description: The current provisioning state of this node.
|
|
returned: success
|
|
type: str
|
|
raid_config:
|
|
description: Represents the current RAID configuration of the node.
|
|
Introduced with the cleaning feature.
|
|
returned: success
|
|
type: dict
|
|
raid_interface:
|
|
description: Interface used for configuring RAID on this node.
|
|
returned: success
|
|
type: str
|
|
rescue_interface:
|
|
description: The interface used for node rescue, e.g. "no-rescue".
|
|
returned: success
|
|
type: str
|
|
reservation:
|
|
description: The name of an Ironic Conductor host which is holding
|
|
a lock on this node, if a lock is held. Usually
|
|
"null", but this field can be useful for debugging.
|
|
returned: success
|
|
type: str
|
|
resource_class:
|
|
description: A string which can be used by external schedulers to
|
|
identify this node as a unit of a specific type of
|
|
resource. For more details, see
|
|
https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html
|
|
returned: success
|
|
type: str
|
|
retired_reason:
|
|
description: The reason the node is marked as retired.
|
|
returned: success
|
|
type: str
|
|
states:
|
|
description: Links to the collection of states.
|
|
returned: success
|
|
type: list
|
|
storage_interface:
|
|
description: Interface used for attaching and detaching volumes on
|
|
this node, e.g. "cinder".
|
|
returned: success
|
|
type: str
|
|
target_power_state:
|
|
description: If a power state transition has been requested, this
|
|
field represents the requested (ie, "target") state,
|
|
either "power on" or "power off".
|
|
returned: success
|
|
type: str
|
|
target_provision_state:
|
|
description: If a provisioning action has been requested, this
|
|
field represents the requested (ie, "target") state.
|
|
Note that a node may go through several states during
|
|
its transition to this target state. For instance,
|
|
when requesting an instance be deployed to an
|
|
AVAILABLE node, the node may go through the following
|
|
state change progression, AVAILABLE -> DEPLOYING ->
|
|
DEPLOYWAIT -> DEPLOYING -> ACTIVE
|
|
returned: success
|
|
type: str
|
|
target_raid_config:
|
|
description: Represents the requested RAID configuration of the
|
|
node, which will be applied when the node next
|
|
transitions through the CLEANING state. Introduced
|
|
with the cleaning feature.
|
|
returned: success
|
|
type: dict
|
|
traits:
|
|
description: List of traits for this node.
|
|
returned: success
|
|
type: list
|
|
updated_at:
|
|
description: Bare Metal node updated at timestamp.
|
|
returned: success
|
|
type: str
|
|
vendor_interface:
|
|
description: Interface for vendor-specific functionality on this
|
|
node, e.g. "no-vendor".
|
|
returned: success
|
|
type: str
|
|
'''
|
|
|
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
|
OpenStackModule
|
|
)
|
|
|
|
|
|
class BaremetalNodeModule(OpenStackModule):
|
|
argument_spec = dict(
|
|
bios_interface=dict(),
|
|
boot_interface=dict(),
|
|
chassis_id=dict(aliases=['chassis_uuid']),
|
|
console_interface=dict(),
|
|
deploy_interface=dict(),
|
|
driver=dict(),
|
|
driver_info=dict(type='dict', required=True),
|
|
id=dict(aliases=['uuid']),
|
|
inspect_interface=dict(),
|
|
management_interface=dict(),
|
|
name=dict(),
|
|
network_interface=dict(),
|
|
nics=dict(type='list', required=True, elements='dict'),
|
|
power_interface=dict(),
|
|
properties=dict(
|
|
type='dict',
|
|
options=dict(
|
|
cpu_arch=dict(),
|
|
cpus=dict(),
|
|
memory_mb=dict(aliases=['ram']),
|
|
local_gb=dict(aliases=['disk_size']),
|
|
capabilities=dict(),
|
|
root_device=dict(type='dict'),
|
|
),
|
|
),
|
|
raid_interface=dict(),
|
|
rescue_interface=dict(),
|
|
resource_class=dict(),
|
|
skip_update_of_masked_password=dict(
|
|
type='bool',
|
|
removed_in_version='3.0.0',
|
|
removed_from_collection='openstack.cloud',
|
|
),
|
|
state=dict(default='present', choices=['present', 'absent']),
|
|
storage_interface=dict(),
|
|
timeout=dict(default=1800, type='int'), # increased default value
|
|
vendor_interface=dict(),
|
|
)
|
|
|
|
module_kwargs = dict(
|
|
required_if=[
|
|
('state', 'present', ('driver',)),
|
|
],
|
|
required_one_of=[
|
|
('id', 'name'),
|
|
],
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
def run(self):
|
|
name_or_id = \
|
|
self.params['id'] if self.params['id'] else self.params['name']
|
|
node = self.conn.baremetal.find_node(name_or_id)
|
|
state = self.params['state']
|
|
|
|
if self.ansible.check_mode:
|
|
self.exit_json(changed=self._will_change(state, node))
|
|
|
|
if state == 'present' and not node:
|
|
node = self._create()
|
|
self.exit_json(changed=True,
|
|
node=node.to_dict(computed=False))
|
|
|
|
elif state == 'present' and node:
|
|
update = self._build_update(node)
|
|
if update:
|
|
node = self._update(node, update)
|
|
self.exit_json(changed=bool(update),
|
|
node=node.to_dict(computed=False))
|
|
|
|
elif state == 'absent' and node:
|
|
self._delete(node)
|
|
self.exit_json(changed=True)
|
|
|
|
elif state == 'absent' and not node:
|
|
self.exit_json(changed=False)
|
|
|
|
def _build_update(self, node):
|
|
update = {}
|
|
# TODO(TheJulia): Presently this does not support updating nics.
|
|
# Support needs to be added.
|
|
|
|
# Update all known updateable attributes
|
|
node_attributes = dict(
|
|
(k, self.params[k])
|
|
for k in [
|
|
'bios_interface',
|
|
'boot_interface',
|
|
'chassis_id',
|
|
'console_interface',
|
|
'deploy_interface',
|
|
'driver',
|
|
'driver_info',
|
|
'inspect_interface',
|
|
'management_interface',
|
|
'name',
|
|
'network_interface',
|
|
'power_interface',
|
|
'raid_interface',
|
|
'rescue_interface',
|
|
'resource_class',
|
|
'storage_interface',
|
|
'vendor_interface',
|
|
]
|
|
if k in self.params and self.params[k] is not None
|
|
and self.params[k] != node[k])
|
|
|
|
properties = self.params['properties']
|
|
if properties is not None:
|
|
properties = dict(
|
|
(k, v) for k, v in properties.items() if v is not None)
|
|
if properties and properties != node['properties']:
|
|
node_attributes['properties'] = properties
|
|
|
|
# name can only be updated if id is given
|
|
if self.params['id'] is None and 'name' in node_attributes:
|
|
self.fail_json(msg='The name of a node cannot be updated without'
|
|
' specifying an id')
|
|
|
|
if node_attributes:
|
|
update['node_attributes'] = node_attributes
|
|
|
|
return update
|
|
|
|
def _create(self):
|
|
kwargs = {}
|
|
|
|
for k in ('bios_interface',
|
|
'boot_interface',
|
|
'chassis_id',
|
|
'console_interface',
|
|
'deploy_interface',
|
|
'driver',
|
|
'driver_info',
|
|
'id',
|
|
'inspect_interface',
|
|
'management_interface',
|
|
'name',
|
|
'network_interface',
|
|
'power_interface',
|
|
'raid_interface',
|
|
'rescue_interface',
|
|
'resource_class',
|
|
'storage_interface',
|
|
'vendor_interface'):
|
|
if self.params[k] is not None:
|
|
kwargs[k] = self.params[k]
|
|
|
|
properties = self.params['properties']
|
|
if properties is not None:
|
|
properties = dict(
|
|
(k, v) for k, v in properties.items() if v is not None)
|
|
if properties:
|
|
kwargs['properties'] = properties
|
|
|
|
node = self.conn.register_machine(
|
|
nics=self.params['nics'],
|
|
wait=self.params['wait'],
|
|
timeout=self.params['timeout'],
|
|
**kwargs)
|
|
|
|
self.exit_json(changed=True, node=node.to_dict(computed=False))
|
|
|
|
def _delete(self, node):
|
|
self.conn.unregister_machine(
|
|
nics=self.params['nics'], uuid=node['id'])
|
|
|
|
def _update(self, node, update):
|
|
node_attributes = update.get('node_attributes')
|
|
if node_attributes:
|
|
node = self.conn.baremetal.update_node(
|
|
node['id'], **node_attributes)
|
|
|
|
return node
|
|
|
|
def _will_change(self, state, node):
|
|
if state == 'present' and not node:
|
|
return True
|
|
elif state == 'present' and node:
|
|
return bool(self._build_update(node))
|
|
elif state == 'absent' and node:
|
|
return True
|
|
else:
|
|
# state == 'absent' and not node:
|
|
return False
|
|
|
|
|
|
def main():
|
|
module = BaremetalNodeModule()
|
|
module()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|