Update baremetal_inspect to be compatible with new sdk

Refactors the module to be based off OpenstackModule.

Changes sdk calls to use the proxy layer where appropriate. The
inspection itself stays at the cloud layer to support waiting.

Make sure we convert returned resource objects to dict

Adds a barebones role to test the module. This won't run in CI, since
we don't have the ironic plugin configured in devstack.

Changes the return value of the module to be the entire node, instead of
just the properties that resulted from inspection. Return docs were
updated to reflect this.

Update module params to use `name` as the identifier for the node,
aliasing it to the previous supported values of `id` and `uuid`.

Use module kwargs to specify mutually exclusive params.

Stop catching exceptions and instead let them bubble up so ansible
handles them.

Change-Id: I2b07b58c8b068d7f18db9862fcecb4088328ac74
This commit is contained in:
Rafael Castillo 2022-05-25 11:44:31 -07:00 committed by Jakob Meng
parent 1bf22feb2d
commit d2eb98d048
3 changed files with 365 additions and 80 deletions

View File

@ -0,0 +1,56 @@
expected_fields:
- allocation_id
- bios_interface
- boot_interface
- boot_mode
- chassis_id
- clean_step
- conductor
- conductor_group
- console_interface
- created_at
- deploy_interface
- deploy_step
- driver
- driver_info
- driver_internal_info
- extra
- fault
- id
- inspect_interface
- instance_id
- instance_info
- is_automated_clean_enabled
- is_console_enabled
- is_maintenance
- is_protected
- is_retired
- is_secure_boot
- last_error
- links
- maintenance_reason
- management_interface
- name
- network_interface
- owner
- port_groups
- ports
- power_interface
- power_state
- properties
- protected_reason
- provision_state
- raid_config
- raid_interface
- rescue_interface
- reservation
- resource_class
- retired_reason
- states
- storage_interface
- target_power_state
- target_provision_state
- target_raid_config
- traits
- updated_at
- vendor_interface

View File

@ -0,0 +1,15 @@
---
# TODO: Actually run this role in CI. Atm we do not have DevStack's ironic plugin enabled.
- name: Introspect node
openstack.cloud.baremetal_inspect:
cloud: "{{ cloud }}"
name: node-1
register: inspect
- debug: var=inspect
- name: assert return values of baremetal_inspect module
assert:
that:
# allow new fields to be introduced but prevent fields from being removed
- expected_fields|difference(inspect.node.keys())|length == 0

View File

@ -10,32 +10,21 @@ module: baremetal_inspect
short_description: Explicitly triggers baremetal node introspection in ironic.
author: OpenStack Ansible SIG
description:
- Requests Ironic to set a node into inspect state in order to collect metadata regarding the node.
This command may be out of band or in-band depending on the ironic driver configuration.
This is only possible on nodes in 'manageable' and 'available' state.
- Requests Ironic to set a node into inspect state in order to collect
metadata regarding the node. This command may be out of band or in-band
depending on the ironic driver configuration. This is only possible on
nodes in 'manageable' and 'available' state.
options:
mac:
description:
- unique mac address that is used to attempt to identify the host.
type: str
uuid:
description:
- globally unique identifier (UUID) to identify the host.
type: str
name:
description:
- unique name identifier to identify the host in Ironic.
- Name or id of the node to inspect.
- Mutually exclusive with I(mac)
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
timeout:
description:
- A timeout in seconds to tell the role to wait for the node to complete introspection if wait is set to True.
default: 1200
type: int
aliases: [id, uuid]
requirements:
- "python >= 3.6"
@ -46,27 +35,260 @@ extends_documentation_fragment:
'''
RETURN = '''
ansible_facts:
description: Dictionary of new facts representing discovered properties of the node..
returned: changed
type: complex
contains:
node:
description: A dictionary describing the node after inspection
returned: changed
type: dict
contains:
allocation_id:
description: The UUID of the allocation associated with the node.
type: str
bios_interface:
description: The bios interface to be used for this node.
type: str
boot_interface:
description: The boot interface for a Node, e.g. "pxe".
type: str
boot_mode:
description: The current boot mode state (uefi/bios).
type: str
chassis_id:
description: UUID of the chassis associated with this Node.
type: str
clean_step:
description: |
The current clean step. Introduced with the cleaning feature.
type: str
conductor:
description: The conductor currently servicing a node.
type: str
conductor_group:
description: The conductor group for a node.
type: str
console_interface:
description: Console interface to use when working with serial console.
type: str
sample: no-console
created_at:
description: Timestamp at which the node was last updated.
type: str
deploy_interface:
description: The deploy interface for a node
type: str
sample: iscsi
deploy_step:
description: The current deploy step.
type: str
driver:
description: The name of the driver.
type: str
driver_info:
description: |
All the metadata required by the driver to manage this Node. List
of fields varies between drivers.
type: dict
driver_internal_info:
description: Internal metadata set and stored by the Node's driver.
type: dict
extra:
description: A set of one or more arbitrary metadata key and value pairs.
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".
type: str
id:
description: The UUID for the resource.
type: str
inspect_interface:
description: The interface used for node inspection.
type: str
sample: no-inspect
instance_id:
description: UUID of the Nova instance associated with this Node.
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).
type: dict
is_automated_clean_enabled:
description: Override enabling of automated cleaning.
type: bool
is_console_enabled:
description: |
Indicates whether console access is enabled or disabled on this node.
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.
type: bool
is_protected:
description: |
Whether the node is protected from undeploying, rebuilding and
deletion.
type: bool
is_retired:
description: Whether the node is marked for retirement.
type: bool
is_secure_boot:
description: |
Whether the node is currently booted with secure boot turned on.
type: bool
last_error:
description: |
Any error from the most recent (last) transaction that started but
failed to finish.
type: str
links:
description: |
A list of relative links. Includes the self and bookmark links.
type: list
maintenance_reason:
description: |
User-settable description of the reason why this Node was placed into
maintenance mode.
type: str
management_interface:
description: Interface for out-of-band node management.
type: str
sample: ipmitool
name:
description: |
Human-readable identifier for the Node resource. Certain words are
reserved.
type: str
network_interface:
description: |
Which Network Interface provider to use when plumbing the network
connections for this Node.
type: str
owner:
description: A string or UUID of the tenant who owns the object.
type: str
port_groups:
description: Links to the collection of portgroups on this node.
type: list
ports:
description: Links to the collection of ports on this node
type: list
power_interface:
description: Interface used for performing power actions on the node.
type: str
sample: ipmitool
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).
type: str
properties:
description: Properties of the node as found by inspection
type: dict
contains:
memory_mb:
description: Amount of node memory as updated in the node properties
type: str
sample: "1024"
description: Amount of node memory as updated in the node properties
type: str
sample: "1024"
cpu_arch:
description: Detected CPU architecture type
type: str
sample: "x86_64"
description: Detected CPU architecture type
type: str
sample: "x86_64"
local_gb:
description: Total size of local disk storage as updated in node properties.
type: str
sample: "10"
description: |
Total size of local disk storage as updated in node properties.
type: str
sample: "10"
cpus:
description: Count of cpu cores defined in the updated node properties.
type: str
sample: "1"
description: |
Count of cpu cores defined in the updated node properties.
type: str
sample: "1"
protected_reason:
description: The reason the node is marked as protected.
type: str
provision_state:
description: The current provisioning state of this Node.
type: str
raid_config:
description: |
Represents the current RAID configuration of the node. Introduced with
the cleaning feature.
type: dict
raid_interface:
description: Interface used for configuring RAID on this node.
type: str
sample: no-raid
rescue_interface:
description: The interface used for node rescue.
type: str
sample: no-rescue
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.
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.
type: str
retired_reason:
description: TODO
type: str
states:
description: |
Links to the collection of states. Note that this resource is also
used to request state transitions.
type: list
storage_interface:
description: |
Interface used for attaching and detaching volumes on this node, e.g.
"cinder".
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".
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.
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.
type: dict
traits:
description: List of traits for this node.
type: list
updated_at:
description: TODO
type: str
vendor_interface:
description: |
Interface for vendor-specific functionality on this node, e.g.
"no-vendor".
type: str
'''
EXAMPLES = '''
@ -75,58 +297,50 @@ EXAMPLES = '''
name: "testnode1"
'''
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
OpenStackModule
)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
class BaremetalInspectModule(OpenStackModule):
argument_spec = dict(
name=dict(aliases=['uuid', 'id']),
mac=dict(),
)
module_kwargs = dict(
mutually_exclusive=[
('name', 'mac'),
],
required_one_of=[
('name', 'mac'),
],
)
def run(self):
node_name_or_id = self.params['name']
node = None
if node_name_or_id is not None:
node = self.conn.baremetal.find_node(node_name_or_id)
else:
node = self.conn.get_machine_by_mac(self.params['mac'])
if node is None:
self.fail_json(msg="node not found.")
node = self.conn.inspect_machine(node['id'],
wait=self.params['wait'],
timeout=self.params['timeout'])
node = node.to_dict(computed=False)
# TODO(TheJulia): diff properties, ?and ports? and determine
# if a change occurred. In theory, the node is always changed
# if introspection is able to update the record.
self.exit_json(changed=True, node=node)
def main():
argument_spec = ironic_argument_spec(
uuid=dict(),
name=dict(),
mac=dict(),
timeout=dict(default=1200, type='int'),
)
module_kwargs = openstack_module_kwargs()
module = IronicModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['name'] or module.params['uuid']:
server = cloud.get_machine(_choose_id_value(module))
elif module.params['mac']:
server = cloud.get_machine_by_mac(module.params['mac'])
else:
module.fail_json(msg="The worlds did not align, "
"the host was not found as "
"no name, uuid, or mac was "
"defined.")
if server:
cloud.inspect_machine(server['uuid'], module.params['wait'])
# TODO(TheJulia): diff properties, ?and ports? and determine
# if a change occurred. In theory, the node is always changed
# if introspection is able to update the record.
module.exit_json(changed=True,
ansible_facts=server['properties'])
else:
module.fail_json(msg="node not found.")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
module = BaremetalInspectModule()
module()
if __name__ == "__main__":