From 75d93c8da05484e23ffa83ba26fc1b8c10be5ec8 Mon Sep 17 00:00:00 2001 From: Will Szumski Date: Mon, 8 Sep 2025 12:22:12 +0000 Subject: [PATCH] Support built-in inspector Switches to using the builtin inspector rather than the standalone ironic-inspector service since this has now been dropped from kolla-ansible. Where possible, the new service has been configured to maintain the same behaviour of the old standalone service. When using the standalone inspector, Kayobe has a few opinionated defaults for the set of processing hooks. These defaults have been translated over to the built-in inspector equivalents for a more consistent experience. Inspection rules have rewritten to use the new format. CLI invocations have been updated to use the commands for the new inspection service. Change-Id: I09bd59d085c7ec3fa0ccd6abb84bd2d0c8b9825d Depends-On: https://review.opendev.org/c/openstack/kolla-ansible/+/961266 Signed-off-by: Will Szumski --- ...emetal-compute-introspection-data-save.yml | 4 +- ansible/inventory/group_vars/all/bifrost | 4 +- ansible/inventory/group_vars/all/inspector | 361 ++++++++++-------- ansible/inventory/group_vars/all/kolla | 8 +- ansible/inventory/group_vars/all/openstack | 33 ++ ansible/kolla-bifrost-hostvars.yml | 2 +- ansible/kolla-openstack.yml | 2 +- ansible/overcloud-introspection-data-save.yml | 2 +- ansible/overcloud-introspection-rules.yml | 20 + .../ironic-inspector-rules/defaults/main.yml | 2 +- .../library/os_ironic_inspector_rule.py | 21 +- .../ironic-inspector-rules/tasks/main.yml | 13 +- .../kolla-ansible/templates/kolla/globals.yml | 2 +- ansible/roles/kolla-bifrost/defaults/main.yml | 2 +- .../kolla/config/bifrost/bifrost.yml | 4 +- .../roles/kolla-openstack/defaults/main.yml | 4 +- .../templates/kolla/config/ironic.conf | 32 ++ ansible/seed-introspection-rules.yml | 2 +- dev/functions | 4 +- .../configuration/reference/bifrost.rst | 4 +- etc/kayobe/bifrost.yml | 2 +- etc/kayobe/inspector.yml | 36 +- playbooks/kayobe-overcloud-base/run.yml | 1 - .../kayobe-overcloud-upgrade-base/run.yml | 1 - ...ng-builtin-inspector-04ab4ea4f1a1c3c8.yaml | 26 ++ requirements.yml | 2 +- 26 files changed, 375 insertions(+), 219 deletions(-) create mode 100644 releasenotes/notes/adds-support-for-configuring-builtin-inspector-04ab4ea4f1a1c3c8.yaml diff --git a/ansible/baremetal-compute-introspection-data-save.yml b/ansible/baremetal-compute-introspection-data-save.yml index 505349e5a..28ae86c96 100644 --- a/ansible/baremetal-compute-introspection-data-save.yml +++ b/ansible/baremetal-compute-introspection-data-save.yml @@ -10,7 +10,7 @@ virtualenv: "{{ venv }}" name: - python-openstackclient - - python-ironic-inspector-client + - python-ironicclient state: latest virtualenv_command: python3.{{ ansible_facts.python.version.minor }} -m venv extra_args: "{% if pip_upper_constraints_file %}-c {{ pip_upper_constraints_file }}{% endif %}" @@ -31,7 +31,7 @@ tasks: - name: Query baremetal compute nodes' hardware introspection data command: > - {{ venv }}/bin/openstack baremetal introspection data save {{ inventory_hostname }} + {{ venv }}/bin/openstack baremetal node inventory save {{ inventory_hostname }} register: save_result changed_when: False # Ignore errors, log a message later. diff --git a/ansible/inventory/group_vars/all/bifrost b/ansible/inventory/group_vars/all/bifrost index 76042ff6b..fc02cf5a0 100644 --- a/ansible/inventory/group_vars/all/bifrost +++ b/ansible/inventory/group_vars/all/bifrost @@ -89,8 +89,8 @@ kolla_bifrost_extra_kernel_options: [] ############################################################################### # Ironic Inspector configuration. -# List of of inspector processing plugins. -kolla_bifrost_inspector_processing_hooks: "{{ inspector_processing_hooks }}" +# List of of inspector plugins. +kolla_bifrost_inspector_hooks: "{{ inspector_hooks }}" # Which MAC addresses to add as ports during introspection. One of 'all', # 'active' or 'pxe'. diff --git a/ansible/inventory/group_vars/all/inspector b/ansible/inventory/group_vars/all/inspector index 4497d39eb..f74f39a09 100644 --- a/ansible/inventory/group_vars/all/inspector +++ b/ansible/inventory/group_vars/all/inspector @@ -31,40 +31,47 @@ inspector_ipa_ramdisk_checksum_url: "{{ ipa_ramdisk_checksum_url }}" inspector_ipa_ramdisk_checksum_algorithm: "{{ ipa_ramdisk_checksum_algorithm }}" ############################################################################### -# Ironic inspector processing configuration. +# Ironic inspector processing configuration for the inspector implementation +# built-in to Ironic. -# List of of default inspector processing plugins. -inspector_processing_hooks_default: - - ramdisk_error - - scheduler - - validate_interfaces - - capabilities - - pci_devices - - lldp_basic - - local_link_connection +# List of hooks to enable for inspection. Default is [$default_hooks, memory, +# boot-mode, cpu-capabilities, pci-devices, parse-lldp]. +inspector_hooks_default: + - $default_hooks + - memory + - boot-mode + - cpu-capabilities + - pci-devices + - parse-lldp -# List of of additional inspector processing plugins. -inspector_processing_hooks_extra: [] +# List of extra inspection hooks to enable. Default is an empty list. +inspector_hooks_extra: [] -# List of of additional inspector processing plugins. -inspector_processing_hooks: > - {{ inspector_processing_hooks_default + inspector_processing_hooks_extra }} +# List of of additional inspector hooks to enable. Default is +# {{ inspector_hooks_default + inspector_hooks_extra }}. +inspector_hooks: > + {{ inspector_hooks_default + inspector_hooks_extra }} + +############################################################################### +# Common Ironic Inspector processing configuration. # Which MAC addresses to add as ports during introspection. One of 'all', -# 'active' or 'pxe'. +# 'active' or 'pxe'. Default is 'pxe'. inspector_add_ports: "pxe" # Which ports to keep after introspection. One of 'all', 'present', or 'added'. +# Default is 'added'. inspector_keep_ports: "added" -# Whether to enable discovery of nodes not managed by Ironic. +# Whether to enable discovery of nodes not managed by Ironic. Default is true. inspector_enable_discovery: True -# The Ironic driver with which to register newly discovered nodes. +# The Ironic driver with which to register newly discovered nodes. Default is +# 'ipmi'. inspector_discovery_enroll_node_driver: "ipmi" ############################################################################### -# Ironic inspector configuration. +# Ironic inspector introspection rules configuration. # Ironic inspector IPMI username to set. inspector_ipmi_username: "{{ ipmi_username }}" @@ -86,9 +93,6 @@ inspector_lldp_switch_port_interface_default: eth0 # check for an LLDP switch port description to use as the node's name. inspector_lldp_switch_port_interface_map: {} -############################################################################### -# Ironic inspector introspection rules configuration. - # Enable IPMI rules: inspector_rules_ipmi_enabled: True @@ -114,17 +118,24 @@ inspector_rule_var_redfish_verify_ca: True inspector_rule_ipmi_credentials: description: "Set IPMI driver_info if no credentials" conditions: - - field: "node://driver_info.ipmi_username" - op: "is-empty" - - field: "node://driver_info.ipmi_password" - op: "is-empty" + - args: + value: "{node.driver_info.ipmi_username}" + regex: '\\{node\\.driver_info\\.ipmi_username\\}' + op: "matches" + - args: + value: "{node.driver_info.ipmi_password}" + regex: '\\{node\\.driver_info\\.ipmi_password\\}' + op: "matches" + sensitive: "true" actions: - - action: "set-attribute" - path: "driver_info/ipmi_username" - value: "{{ inspector_rule_var_ipmi_username }}" - - action: "set-attribute" - path: "driver_info/ipmi_password" - value: "{{ inspector_rule_var_ipmi_password }}" + - op: "set-attribute" + args: + path: "driver_info/ipmi_username" + value: "{{ inspector_rule_var_ipmi_username }}" + - op: "set-attribute" + args: + path: "driver_info/ipmi_password" + value: "{{ inspector_rule_var_ipmi_password }}" # Deployment kernel referenced by inspector rule. inspector_rule_var_deploy_kernel: @@ -133,12 +144,15 @@ inspector_rule_var_deploy_kernel: inspector_rule_deploy_kernel: description: "Set deploy kernel" conditions: - - field: "node://driver_info.deploy_kernel" - op: "is-empty" + - args: + value: "{node.driver_info.deploy_kernel}" + regex: '\\{node\\.driver_info\\.deploy_kernel\\}' + op: "matches" actions: - - action: "set-attribute" - path: "driver_info/deploy_kernel" - value: "{{ inspector_rule_var_deploy_kernel }}" + - op: "set-attribute" + args: + path: "driver_info/deploy_kernel" + value: "{{ inspector_rule_var_deploy_kernel }}" # Deployment ramdisk referenced by inspector rule. inspector_rule_var_deploy_ramdisk: @@ -147,220 +161,233 @@ inspector_rule_var_deploy_ramdisk: inspector_rule_deploy_ramdisk: description: "Set deploy ramdisk" conditions: - - field: "node://driver_info.deploy_ramdisk" - op: "is-empty" + - args: + value: "{node.driver_info.deploy_ramdisk}" + regex: '\\{node\\.driver_info\\.deploy_ramdisk\\}' + op: "matches" actions: - - action: "set-attribute" - path: "driver_info/deploy_ramdisk" - value: "{{ inspector_rule_var_deploy_ramdisk }}" - -# Ironic inspector rule to initialise root device hints. -inspector_rule_root_hint_init: - description: "Initialise root device hint" - conditions: - - field: "node://properties.root_device" - op: "is-empty" - actions: - # Inspector can't combine references to introspection data with non-string - # types, see https://bugs.launchpad.net/ironic-inspector/+bug/1670768. We - # must therefore first set the root_device property to an empty dict, then - # update the fields within it. - - action: "set-attribute" - path: "properties/root_device" - value: {} + - op: "set-attribute" + args: + path: "driver_info/deploy_ramdisk" + value: "{{ inspector_rule_var_deploy_ramdisk }}" # Ironic inspector rule to set serial root device hint. inspector_rule_root_hint_serial: description: "Set serial root device hint" conditions: - - field: "data://root_disk.serial" - op: "is-empty" - invert: True + - args: + value: "{node.properties[root_device]}" + regex: "\\{node\\.properties\\[root_device\\]\\}" + op: "matches" actions: - - action: "set-attribute" - path: "properties/root_device/serial" - value: "{data[root_disk][serial]}" + - op: "set-attribute" + args: + path: "properties/root_device/name" + value: "{plugin_data[root_disk][by_path]}" + # Ironic inspector rule to set the interface on which the node PXE booted. inspector_rule_set_pxe_interface_mac: description: "Set node PXE interface MAC address" conditions: - - field: "data://boot_interface" - op: "is-empty" - invert: True + - args: + value: "{plugin_data[boot_interface]}" + regex: "'\\{plugin_data\\[boot_interface\\]\\}'" + op: "!matches" actions: - - action: "set-attribute" - path: "extra/pxe_interface_mac" - value: "{data[boot_interface]}" + - op: "set-attribute" + args: + path: "extra/pxe_interface_mac" + value: "{plugin_data[boot_interface]}" # Name of network interface to use for LLDP referenced by switch port # description rule. inspector_rule_var_lldp_switch_port_interface: +# Internal variables. Not meant for use externally. +_inspector_rule_interface_path: "{all_interfaces.{{ inspector_rule_var_lldp_switch_port_interface }}}" +_inspector_rule_lldp_processed_path: "{all_interfaces.{{ inspector_rule_var_lldp_switch_port_interface }}.lldp_processed}" +_inspector_rule_switch_port_description_path: "{all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description}" + # Ironic inspector rule to set the node's name from an interface's LLDP switch # port description. inspector_rule_lldp_switch_port_desc_to_name: description: "Set node name from {{ inspector_rule_var_lldp_switch_port_interface }} LLDP switch port description" conditions: # Check for the existence of the switch_port_description field. - - field: "data://all_interfaces.{{ inspector_rule_var_lldp_switch_port_interface }}" - op: "is-empty" - invert: True - - field: "data://all_interfaces.{{ inspector_rule_var_lldp_switch_port_interface }}.lldp_processed" - op: "is-empty" - invert: True - - field: "data://all_interfaces.{{ inspector_rule_var_lldp_switch_port_interface }}.lldp_processed.switch_port_description" - op: "is-empty" - invert: True + - args: + value: "{{ _inspector_rule_interface_path }}" + regex: "{{ _inspector_rule_interface_path | regex_escape }}" + op: "!matches" + - args: + value: "{{ _inspector_rule_lldp_processed_path }}" + regex: "{{ _inspector_rule_lldp_processed_path | regex_escape }}" + op: "!matches" + - args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "{{ _inspector_rule_switch_port_description_path | regex_escape }}" + op: "!matches" # Filter out switch port descriptions using the switch's interface names. # On some switches (including Dell Network OS 9.10(0.1) and some Ruckus # switches), the port description TLV is sent but contains the interface # name rather than the interface's description. Dell switches use a space # character between port type and port number, while Ruckus switches don't. - - field: "data://all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description" - op: "matches" - value: "^GigabitEthernet ?([0-9/]*)$" - invert: True - - field: "data://all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description" - op: "matches" - value: "^TenGigabitEthernet ?([0-9/]*)$" - invert: True - - field: "data://all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description" - op: "matches" - value: "^twentyFiveGigE ?([0-9/]*)$" - invert: True - - field: "data://all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description" - op: "matches" - value: "^fortyGigE ?([0-9/]*)$" - invert: True - - field: "data://all_interfaces.{{inspector_rule_var_lldp_switch_port_interface}}.lldp_processed.switch_port_description" - op: "matches" - value: "^Port-channel ?([0-9/]*)$" - invert: True + - op: "!matches" + args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "^GigabitEthernet ?([0-9/]*)$" + - op: "!matches" + args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "^TenGigabitEthernet ?([0-9/]*)$" + - op: "!matches" + args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "^twentyFiveGigE ?([0-9/]*)$" + - op: "!matches" + args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "^fortyGigE ?([0-9/]*)$" + - op: "!matches" + args: + value: "{{ _inspector_rule_switch_port_description_path }}" + regex: "^Port-channel ?([0-9/]*)$" actions: - - action: "set-attribute" - path: "name" - value: "{data[all_interfaces][{{ inspector_rule_var_lldp_switch_port_interface }}][lldp_processed][switch_port_description]}" - -# Ironic inspector rule to initialise system vendor data in the node's metadata. -inspector_rule_save_system_vendor_init: - description: "Initialise system vendor data in Ironic node metadata" - conditions: - - field: "data://inventory.system_vendor" - op: "is-empty" - invert: True - - field: "node://extra.system_vendor" - op: "is-empty" - actions: - - action: "set-attribute" - path: "extra/system_vendor" - value: {} + - op: "set-attribute" + args: + path: "name" + value: "{{ _inspector_rule_switch_port_description_path }}" # Ironic inspector rule to save system vendor manufacturer data in the node's # metadata. inspector_rule_save_system_vendor_manufacturer: description: "Save system vendor manufacturer data in Ironic node metadata" conditions: - - field: "data://inventory.system_vendor" - op: "is-empty" - invert: True - - field: "data://inventory.system_vendor.manufacturer" - op: "is-empty" - invert: True + - args: + value: "{inventory.system_vendor}" + regex: "\\{inventory\\.system_vendor\\}" + op: "!matches" + - args: + value: "{inventory.system_vendor.manufacturer}" + regex: "\\{inventory\\.system_vendor\\.manufacturer\\}" + op: "!matches" actions: - - action: "set-attribute" - path: "extra/system_vendor/manufacturer" - value: "{data[inventory][system_vendor][manufacturer]}" + - op: "set-attribute" + args: + path: "extra/system_vendor/manufacturer" + value: "{inventory[system_vendor][manufacturer]}" # Ironic inspector rule to save system vendor serial number in the node's # metadata. inspector_rule_save_system_vendor_serial_number: description: "Save system vendor serial number in Ironic node metadata" conditions: - - field: "data://inventory.system_vendor" - op: "is-empty" - invert: True - - field: "data://inventory.system_vendor.serial_number" - op: "is-empty" - invert: True + - args: + value: "{inventory.system_vendor}" + regex: "\\{inventory\\.system_vendor\\}" + op: "!matches" + - args: + value: "{inventory.system_vendor.serial_number}" + regex: "\\{inventory\\.system_vendor\\.serial_number\\}" + op: "!matches" actions: - - action: "set-attribute" - path: "extra/system_vendor/serial_number" - value: "{data[inventory][system_vendor][serial_number]}" + - op: "set-attribute" + args: + path: "extra/system_vendor/serial_number" + value: "{inventory[system_vendor][serial_number]}" # Ironic inspector rule to save system vendor product name in the node's # metadata. inspector_rule_save_system_vendor_product_name: description: "Save system vendor product name in Ironic node metadata" conditions: - - field: "data://inventory.system_vendor" - op: "is-empty" - invert: True - - field: "data://inventory.system_vendor.product_name" - op: "is-empty" - invert: True + - args: + value: "{inventory.system_vendor}" + regex: "\\{inventory\\.system_vendor\\}" + op: "!matches" + - args: + value: "{inventory.system_vendor.product_name}" + regex: "\\{inventory\\.system_vendor\\.product_name\\}" + op: "!matches" actions: - - action: "set-attribute" - path: "extra/system_vendor/product_name" - value: "{data[inventory][system_vendor][product_name]}" + - op: "set-attribute" + args: + path: "extra/system_vendor/product_name" + value: "{inventory[system_vendor][product_name]}" # Ironic inspector rule to save introspection data to the node. inspector_rule_save_data: description: "Save introspection data to Ironic node" conditions: [] actions: - - action: "set-attribute" - path: "extra/introspection_data" - value: "{data}" + - op: "set-attribute" + args: + path: "extra/introspection_data/inventory" + value: "{inventory}" + - op: "set-attribute" + args: + path: "extra/introspection_data/plugin_data" + value: "{plugin_data}" # Redfish rules # Ironic inspector rule to set Redfish credentials. inspector_rule_redfish_credentials: description: "Set Redfish driver_info if no credentials" conditions: - - field: "node://driver_info.redfish_username" - op: "is-empty" - - field: "node://driver_info.redfish_password" - op: "is-empty" + - args: + value: "{node.driver_info.redfish_username}" + regex: "\\{node\\.driver_info\\.redfish_username\\}" + op: "matches" + - args: + value: "{node.driver_info.redfish_password}" + regex: "\\{node\\.driver_info\\.redfish_password\\}" + op: "matches" + sensitive: true actions: - - action: "set-attribute" - path: "driver_info/redfish_username" - value: "{{ inspector_rule_var_redfish_username }}" - - action: "set-attribute" - path: "driver_info/redfish_password" - value: "{{ inspector_rule_var_redfish_password }}" + - op: "set-attribute" + args: + path: "driver_info/redfish_username" + value: "{{ inspector_rule_var_redfish_username }}" + - op: "set-attribute" + args: + path: "driver_info/redfish_password" + value: "{{ inspector_rule_var_redfish_password }}" # Ironic inspector rule to set Redfish address. inspector_rule_redfish_address: description: "Set Redfish address" conditions: - - field: "node://driver_info.redfish_address" - op: "is-empty" + - args: + value: "{node.driver_info.redfish_address}" + regex: "\\{node\\.driver_info\\.redfish_address\\}" + op: "matches" actions: - - action: "set-attribute" - path: "driver_info/redfish_address" - value: "{data[inventory][bmc_address]}" + - op: "set-attribute" + args: + path: "driver_info/redfish_address" + value: "{inventory[bmc_address]}" # Ironic inspector rule to set Redfish certificate authority. inspector_rule_redfish_verify_ca: description: "Set Redfish Verify CA" conditions: - - field: "node://driver_info.redfish_verify_ca" - op: "is-empty" + - args: + value: "{node.driver_info.redfish_verify_ca}" + regex: "\\{node\\.driver_info\\.redfish_verify_ca\\}" + op: "matches" actions: - - action: "set-attribute" - path: "driver_info/redfish_verify_ca" - value: "{{ inspector_rule_var_redfish_verify_ca }}" + - op: "set-attribute" + args: + path: "driver_info/redfish_verify_ca" + value: "{{ inspector_rule_var_redfish_verify_ca }}" # List of default ironic inspector rules. inspector_rules_default: - "{{ inspector_rule_deploy_kernel }}" - "{{ inspector_rule_deploy_ramdisk }}" - - "{{ inspector_rule_root_hint_init }}" - "{{ inspector_rule_root_hint_serial }}" - "{{ inspector_rule_set_pxe_interface_mac }}" - "{{ inspector_rule_lldp_switch_port_desc_to_name }}" - - "{{ inspector_rule_save_system_vendor_init }}" - "{{ inspector_rule_save_system_vendor_manufacturer }}" - "{{ inspector_rule_save_system_vendor_serial_number }}" - "{{ inspector_rule_save_system_vendor_product_name }}" diff --git a/ansible/inventory/group_vars/all/kolla b/ansible/inventory/group_vars/all/kolla index ec74668ff..411b116e6 100644 --- a/ansible/inventory/group_vars/all/kolla +++ b/ansible/inventory/group_vars/all/kolla @@ -153,7 +153,7 @@ overcloud_container_image_regex_map: - regex: ^designate enabled: "{{ kolla_enable_designate | bool }}" - regex: ^dnsmasq - enabled: "{{ kolla_enable_ironic | bool }}" + enabled: "{{ kolla_enable_ironic_dnsmasq | bool }}" - regex: ^etcd enabled: "{{ kolla_enable_etcd | bool }}" - regex: ^fluentd @@ -548,6 +548,8 @@ kolla_enable_heat: "{{ kolla_enable_openstack_core | bool }}" kolla_enable_horizon: "{{ kolla_enable_openstack_core | bool }}" kolla_enable_influxdb: "{{ kolla_enable_cloudkitty | bool }}" kolla_enable_ironic: "no" +kolla_enable_ironic_dnsmasq: "{{ kolla_enable_ironic | bool and kolla_inspector_enable_discovery | bool }}" +kolla_enable_ironic_pxe_filter: "{{ kolla_enable_ironic | bool and kolla_inspector_enable_discovery | bool }}" kolla_enable_ironic_neutron_agent: "{{ kolla_enable_neutron | bool and kolla_enable_ironic | bool }}" kolla_enable_iscsid: "{{ kolla_enable_cinder | bool and kolla_enable_cinder_backend_iscsi | bool }}" kolla_enable_kuryr: "no" @@ -695,3 +697,7 @@ kolla_https_proxy: "{{ https_proxy }}" # List of domains, hostnames, IP addresses and networks for which no proxy is # used. Default value is "{{ no_proxy }}". kolla_no_proxy: "{{ no_proxy }}" + +############################################################################## +# Inspector configuration +kolla_inspector_enable_discovery: "{{ inspector_enable_discovery | bool }}" \ No newline at end of file diff --git a/ansible/inventory/group_vars/all/openstack b/ansible/inventory/group_vars/all/openstack index e2525ac78..430293d3f 100644 --- a/ansible/inventory/group_vars/all/openstack +++ b/ansible/inventory/group_vars/all/openstack @@ -9,6 +9,11 @@ openstack_release: "master" openstack_branch: >- {% if openstack_release != 'master' %}stable/{% endif %}{{ openstack_release | lower }} +############################################################################### +# OpenStack virtualenv configuration. + +os_virtualenv_python: "{{ '/usr/bin/python3.12' if ansible_facts.os_family == 'RedHat' else '/usr/bin/python3' }}" + ############################################################################### # OpenStack authentication configuration. @@ -28,6 +33,19 @@ openstack_auth: auth_url: "{{ lookup('env', 'OS_AUTH_URL') }}" system_scope: "{{ lookup('env', 'OS_SYSTEM_SCOPE') }}" +# Internal variable to set the system scope authentication. +openstack_auth_system_scope_value: 'all' + +# Overcloud authentication parameters for system scope. By default this will +# use the user defined in openstack_auth. +# NOTE(wszumski): Not all projects support system scope yet and we sometimes need +# to use system scope and project scope in the same ansible run. +openstack_auth_system_scope: >- + {{ openstack_auth | combine( + {'system_scope': openstack_auth_system_scope_value, + 'project_domain_name': '', + 'project_name': ''})}} + # Overcloud CA certificate path. openstack_cacert: "{{ lookup('env', 'OS_CACERT') }}" @@ -49,6 +67,14 @@ openstack_auth_env: OS_CACERT: "{{ lookup('env', 'OS_CACERT') }}" OS_SYSTEM_SCOPE: "{{ lookup('env', 'OS_SYSTEM_SCOPE') }}" +# Overcloud authentication environment variables for system scope. By default +# this will use the user defined in openstack_auth_env. +openstack_auth_env_system_scope: >- + {{ openstack_auth_env | combine( + {'OS_SYSTEM_SCOPE': openstack_auth_system_scope_value, + 'OS_PROJECT_DOMAIN_NAME': '', + 'OS_PROJECT_NAME': ''})}} + # List of parameters required in openstack_auth when openstack_auth_type is # password. openstack_auth_password_required_params: @@ -56,3 +82,10 @@ openstack_auth_password_required_params: - "username" - "password" - "auth_url" + +# List of parameters required in openstack_auth when openstack_auth_type is +# password and using system scope +openstack_auth_password_required_params_system: + - "username" + - "password" + - "auth_url" diff --git a/ansible/kolla-bifrost-hostvars.yml b/ansible/kolla-bifrost-hostvars.yml index d560e4d0e..ebe13ceb0 100644 --- a/ansible/kolla-bifrost-hostvars.yml +++ b/ansible/kolla-bifrost-hostvars.yml @@ -61,7 +61,7 @@ -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m shell - -a "env OS_CLOUD=bifrost baremetal introspection data save {% raw %}{{ inventory_hostname }}{% endraw %}"' + -a "env OS_CLOUD=bifrost baremetal inventory save {% raw %}{{ inventory_hostname }}{% endraw %}"' register: save_result changed_when: False # Ignore errors, log a message later. diff --git a/ansible/kolla-openstack.yml b/ansible/kolla-openstack.yml index bb116330d..ea2aec967 100644 --- a/ansible/kolla-openstack.yml +++ b/ansible/kolla-openstack.yml @@ -131,7 +131,7 @@ roles: - role: kolla-openstack # Ironic inspector configuration. - kolla_inspector_processing_hooks: "{{ inspector_processing_hooks }}" + kolla_inspector_hooks: "{{ inspector_hooks }}" kolla_inspector_add_ports: "{{ inspector_add_ports }}" kolla_inspector_keep_ports: "{{ inspector_keep_ports }}" kolla_inspector_enable_discovery: "{{ inspector_enable_discovery }}" diff --git a/ansible/overcloud-introspection-data-save.yml b/ansible/overcloud-introspection-data-save.yml index 284a899f2..0e2b16111 100644 --- a/ansible/overcloud-introspection-data-save.yml +++ b/ansible/overcloud-introspection-data-save.yml @@ -25,7 +25,7 @@ -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m shell - -a "env OS_CLOUD=bifrost baremetal introspection data save {% raw %}{{ inventory_hostname }}{% endraw %}"' + -a "env OS_CLOUD=bifrost baremetal node inventory save {% raw %}{{ inventory_hostname }}{% endraw %}"' register: save_result changed_when: False # Ignore errors, log a message later. diff --git a/ansible/overcloud-introspection-rules.yml b/ansible/overcloud-introspection-rules.yml index 0782800d2..4c4f1126c 100644 --- a/ansible/overcloud-introspection-rules.yml +++ b/ansible/overcloud-introspection-rules.yml @@ -55,3 +55,23 @@ changed_when: False register: ipa_ramdisk_id environment: "{{ openstack_auth_env }}" + + roles: + - role: ironic-inspector-rules + os_openstacksdk_install_epel: "{{ dnf_install_epel }}" + os_openstacksdk_state: "latest" + ironic_inspector_venv: "{{ venv }}" + ironic_inspector_upper_constraints_file: "{{ openstacksdk_upper_constraints_file }}" + ironic_inspector_auth_type: "{{ openstack_auth_type }}" + ironic_inspector_auth: "{{ openstack_auth_system_scope }}" + ironic_inspector_cacert: "{{ openstack_cacert }}" + ironic_inspector_interface: "{{ openstack_interface }}" + ironic_inspector_rules: "{{ inspector_rules }}" + # These variables may be referenced in the introspection rules. + inspector_rule_var_ipmi_username: "{{ inspector_ipmi_username }}" + inspector_rule_var_ipmi_password: "{{ inspector_ipmi_password }}" + inspector_rule_var_redfish_username: "{{ inspector_redfish_username }}" + inspector_rule_var_redfish_password: "{{ inspector_redfish_password }}" + inspector_rule_var_lldp_switch_port_interface: "{{ inspector_lldp_switch_port_interface_default }}" + inspector_rule_var_deploy_kernel: "{{ ipa_kernel_id.stdout }}" + inspector_rule_var_deploy_ramdisk: "{{ ipa_ramdisk_id.stdout }}" diff --git a/ansible/roles/ironic-inspector-rules/defaults/main.yml b/ansible/roles/ironic-inspector-rules/defaults/main.yml index ee38abae1..fd36cc0cf 100644 --- a/ansible/roles/ironic-inspector-rules/defaults/main.yml +++ b/ansible/roles/ironic-inspector-rules/defaults/main.yml @@ -2,7 +2,7 @@ # Path to a directory in which to create a virtualenv. ironic_inspector_venv: -# Upper constraints file for installation of python-ironic-inspector-client. +# Upper constraints file for installation of python-ironicclient. ironic_inspector_upper_constraints_file: # Authentication type. diff --git a/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py b/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py index 5db8f8088..c84743a51 100644 --- a/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py +++ b/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py @@ -53,6 +53,10 @@ options: description: - List of actions to be taken when the conditions are met. required: true + sensitive: + description: + - Whether to mark the rule as sensitive in Ironic + required: false """ EXAMPLES = """ @@ -74,13 +78,13 @@ os_ironic_inspector_rule: def _get_client(module, cloud): """Return an Ironic inspector client.""" - return cloud.baremetal_introspection + return cloud.baremetal def _ensure_rule_present(module, client): """Ensure that an inspector rule is present.""" if module.params['uuid']: - response = client.get('/rules/{}'.format(module.params['uuid'])) + response = client.get('/inspection_rules/{}'.format(module.params['uuid']), headers={'X-OpenStack-Ironic-API-Version': '1.96'}) if not response.ok: if response.status_code != 404: module.fail_json(msg="Failed retrieving Inspector rule %s: %s" @@ -88,7 +92,7 @@ def _ensure_rule_present(module, client): else: rule = response.json() # Check whether the rule differs from the request. - keys = ('conditions', 'actions', 'description') + keys = ('conditions', 'actions', 'description', 'sensitive') for key in keys: expected = module.params[key] if key == 'conditions': @@ -96,9 +100,10 @@ def _ensure_rule_present(module, client): # conditions that may not be in the requested rule. Apply # defaults to allow the comparison to succeed. expected = copy.deepcopy(expected) + if key == 'actions': + expected = copy.deepcopy(expected) for condition in expected: - condition.setdefault('invert', False) - condition.setdefault('multiple', 'any') + condition.setdefault('loop', []) if rule[key] != expected: break else: @@ -111,9 +116,10 @@ def _ensure_rule_present(module, client): "conditions": module.params['conditions'], "actions": module.params['actions'], "description": module.params['description'], + "sensitive": module.params['sensitive'], "uuid": module.params['uuid'], } - response = client.post("/rules", json=rule) + response = client.post("/inspection_rules", json=rule, headers={'X-OpenStack-Ironic-API-Version': '1.96'}) if not response.ok: module.fail_json(msg="Failed creating Inspector rule %s: %s" % (module.params['uuid'], response.text)) @@ -124,7 +130,7 @@ def _ensure_rule_absent(module, client): """Ensure that an inspector rule is absent.""" if not module.params['uuid']: module.fail_json(msg="UUID is required to ensure rules are absent") - response = client.delete("/rules/{}".format(module.params['uuid'])) + response = client.delete("/inspection_rules/{}".format(module.params['uuid']), headers={'X-OpenStack-Ironic-API-Version': '1.96'}) if not response.ok: # If the rule does not exist, no problem and no change. if response.status_code == 404: @@ -140,6 +146,7 @@ def main(): actions=dict(type='list', required=True), description=dict(required=False), uuid=dict(required=False), + sensitive=dict(type='bool', required=False, default=False), state=dict(required=False, default='present', choices=['present', 'absent']), ) diff --git a/ansible/roles/ironic-inspector-rules/tasks/main.yml b/ansible/roles/ironic-inspector-rules/tasks/main.yml index 93fbe7fcb..41a1aab7e 100644 --- a/ansible/roles/ironic-inspector-rules/tasks/main.yml +++ b/ansible/roles/ironic-inspector-rules/tasks/main.yml @@ -8,9 +8,12 @@ cacert: "{{ ironic_inspector_cacert | default(omit, true) }}" cloud: "{{ ironic_inspector_cloud | default(omit, true) }}" interface: "{{ ironic_inspector_interface | default(omit, true) }}" - conditions: "{{ item.conditions }}" - actions: "{{ item.actions }}" - description: "{{ item.description | default(omit) }}" - uuid: "{{ item.uuid | default(item.description | to_uuid) | default(omit) }}" + conditions: "{{ ironic_inspector_rules[item].conditions }}" + actions: "{{ ironic_inspector_rules[item].actions }}" + description: "{{ ironic_inspector_rules[item].description | default(omit) }}" + uuid: "{{ ironic_inspector_rules[item].uuid | default(ironic_inspector_rules[item].description | to_uuid) | default(omit) }}" + sensitive: "{{ ironic_inspector_rules[item].sensitive | default(omit) }}" state: present - with_items: "{{ ironic_inspector_rules }}" + loop_control: + label: "{{ ironic_inspector_rules[item].description }}" + with_items: "{{ range(0, ironic_inspector_rules | length) | list }}" diff --git a/ansible/roles/kolla-ansible/templates/kolla/globals.yml b/ansible/roles/kolla-ansible/templates/kolla/globals.yml index a1a01451d..ccc37cddf 100644 --- a/ansible/roles/kolla-ansible/templates/kolla/globals.yml +++ b/ansible/roles/kolla-ansible/templates/kolla/globals.yml @@ -403,7 +403,7 @@ ironic_dnsmasq_dhcp_ranges: {% endif %} {% endif %} {% if kolla_inspector_extra_kernel_options %} -ironic_inspector_kernel_cmdline_extras: +ironic_kernel_cmdline_extras: {{ kolla_inspector_extra_kernel_options | to_nice_yaml }} {% endif %} # PXE bootloader file for Ironic Inspector, relative to /var/lib/ironic/tftpboot. diff --git a/ansible/roles/kolla-bifrost/defaults/main.yml b/ansible/roles/kolla-bifrost/defaults/main.yml index ae2e5fa47..eff98ea8f 100644 --- a/ansible/roles/kolla-bifrost/defaults/main.yml +++ b/ansible/roles/kolla-bifrost/defaults/main.yml @@ -42,7 +42,7 @@ kolla_bifrost_dnsmasq_dns_servers: [] kolla_bifrost_domain: # List of of inspector processing plugins. -kolla_bifrost_inspector_processing_hooks: +kolla_bifrost_inspector_hooks: # Which MAC addresses to add as ports during introspection. One of 'all', # 'active' or 'pxe'. diff --git a/ansible/roles/kolla-bifrost/templates/kolla/config/bifrost/bifrost.yml b/ansible/roles/kolla-bifrost/templates/kolla/config/bifrost/bifrost.yml index 5269328fe..e1e671b67 100644 --- a/ansible/roles/kolla-bifrost/templates/kolla/config/bifrost/bifrost.yml +++ b/ansible/roles/kolla-bifrost/templates/kolla/config/bifrost/bifrost.yml @@ -28,9 +28,9 @@ dnsmasq_dns_servers: "{{ kolla_bifrost_dnsmasq_dns_servers | join(',') }}" domain: "{{ kolla_bifrost_domain }}" {% endif %} -{% if kolla_bifrost_inspector_processing_hooks %} +{% if kolla_bifrost_inspector_hooks %} # Comma-separated list of inspector processing plugins. -inspector_processing_hooks: "{{ kolla_bifrost_inspector_processing_hooks | join(',') }}" +inspector_hooks: "{{ kolla_bifrost_inspector_hooks | join(',') }}" {% endif %} {% if kolla_bifrost_inspector_port_addition %} diff --git a/ansible/roles/kolla-openstack/defaults/main.yml b/ansible/roles/kolla-openstack/defaults/main.yml index c326bd838..0fabde950 100644 --- a/ansible/roles/kolla-openstack/defaults/main.yml +++ b/ansible/roles/kolla-openstack/defaults/main.yml @@ -567,7 +567,9 @@ kolla_extra_ironic: # Ironic inspector configuration. # Comma-separated list of inspector processing plugins. -kolla_inspector_processing_hooks: + +# Comma-separated list of inspector processing plugins for built-in inspector +kolla_inspector_hooks: # Which MAC addresses to add as ports during introspection. One of 'all', # 'active' or 'pxe'. diff --git a/ansible/roles/kolla-openstack/templates/kolla/config/ironic.conf b/ansible/roles/kolla-openstack/templates/kolla/config/ironic.conf index cbb8ca683..3961eb631 100644 --- a/ansible/roles/kolla-openstack/templates/kolla/config/ironic.conf +++ b/ansible/roles/kolla-openstack/templates/kolla/config/ironic.conf @@ -1,4 +1,11 @@ [DEFAULT] +enabled_inspect_interfaces = redfish,no-inspect,agent +{% if kolla_inspector_enable_discovery | bool %} +# Setting default_inspect_interface is required for the inspection flow to +# continue correctly after the node creation. See: +# https://docs.openstack.org/ironic/latest/admin/inspection/discovery.html +default_inspect_interface = agent +{% endif %} {% if kolla_ironic_enabled_hardware_types %} enabled_hardware_types: {{ kolla_ironic_enabled_hardware_types | join(',') }} {% endif %} @@ -37,6 +44,31 @@ kernel_append_params = {{ kolla_ironic_pxe_append_params | join(' ') }} tftp_server = {{ hostvars[inventory_hostname].ansible_facts[api_interface | replace('-', '_')]['ipv4']['address'] }} {% endraw %} +[auto_discovery] +enabled = {{ kolla_inspector_enable_discovery }} +driver = {{ kolla_inspector_discovery_enroll_node_driver }} + +[inspector] +{% if kolla_inspector_enable_discovery | bool %} +# Under unmanaged inspection we understand in-band inspection where the boot +# configuration (iPXE scripts, DHCP options, etc) is not provided by the Bare +# Metal service. In this case, the node is simply set to boot from network and +# powered on. See: +# https://docs.openstack.org/ironic/latest/admin/inspection/managed.html#unmanaged-inspection +require_managed_boot = False +{% endif %} +{% if kolla_inspector_add_ports %} +add_ports = {{ kolla_inspector_add_ports }} +{% endif %} + +{% if kolla_inspector_keep_ports %} +keep_ports = {{ kolla_inspector_keep_ports }} +{% endif %} + +{% if kolla_inspector_hooks %} +hooks = {{ kolla_inspector_hooks | join(',') }} +{% endif %} + {% if kolla_extra_ironic %} ####################### # Extra configuration diff --git a/ansible/seed-introspection-rules.yml b/ansible/seed-introspection-rules.yml index ca92bdd13..edcb031bf 100644 --- a/ansible/seed-introspection-rules.yml +++ b/ansible/seed-introspection-rules.yml @@ -19,4 +19,4 @@ inspector_rule_var_lldp_switch_port_interface: "{{ kolla_bifrost_inspector_lldp_switch_port_interface }}" inspector_rule_var_deploy_kernel: "{{ kolla_bifrost_inspector_deploy_kernel }}" inspector_rule_var_deploy_ramdisk: "{{ kolla_bifrost_inspector_deploy_ramdisk }}" - when: kolla_enable_bifrost | bool and false # TODO(priteau): Re-enable with built-in inspection + when: kolla_enable_bifrost | bool diff --git a/dev/functions b/dev/functions index 4a627e2a9..907151f10 100644 --- a/dev/functions +++ b/dev/functions @@ -563,9 +563,9 @@ function overcloud_test_init { environment_setup if [[ ! -z "$UPPER_CONSTRAINTS_FILE" ]]; then - pip install python-openstackclient -c "$UPPER_CONSTRAINTS_FILE" + pip install python-openstackclient python-ironicclient -c "$UPPER_CONSTRAINTS_FILE" else - pip install python-openstackclient + pip install python-openstackclient python-ironicclient fi source "${KOLLA_CONFIG_PATH:-/etc/kolla}/admin-openrc.sh" diff --git a/doc/source/configuration/reference/bifrost.rst b/doc/source/configuration/reference/bifrost.rst index 4180dd3da..cbd3e738e 100644 --- a/doc/source/configuration/reference/bifrost.rst +++ b/doc/source/configuration/reference/bifrost.rst @@ -268,9 +268,9 @@ Ironic Inspector configuration The following options configure the Ironic Inspector service in the ``bifrost-deploy`` container. -``kolla_bifrost_inspector_processing_hooks`` +``kolla_bifrost_inspector_hooks`` List of of inspector processing plugins. Default is ``{{ - inspector_processing_hooks }}``, defined in + inspector_hooks }}``, defined in ``${KAYOBE_CONFIG_PATH}/inspector.yml``. ``kolla_bifrost_inspector_port_addition`` Which MAC addresses to add as ports during introspection. One of ``all``, diff --git a/etc/kayobe/bifrost.yml b/etc/kayobe/bifrost.yml index 8c5e9a501..4150e6600 100644 --- a/etc/kayobe/bifrost.yml +++ b/etc/kayobe/bifrost.yml @@ -90,7 +90,7 @@ # Ironic Inspector configuration. # List of of inspector processing plugins. -#kolla_bifrost_inspector_processing_hooks: +#kolla_bifrost_inspector_hooks: # Which MAC addresses to add as ports during introspection. One of 'all', # 'active' or 'pxe'. diff --git a/etc/kayobe/inspector.yml b/etc/kayobe/inspector.yml index 713751dfc..926316b71 100644 --- a/etc/kayobe/inspector.yml +++ b/etc/kayobe/inspector.yml @@ -31,32 +31,40 @@ #inspector_ipa_ramdisk_checksum_algorithm: ############################################################################### -# Ironic inspector processing configuration. +# Ironic inspector processing configuration for the inspector implementation +# built-in to Ironic. -# List of of default inspector processing plugins. -#inspector_processing_hooks_default: +# List of hooks to enable for inspection. Default is [$default_hooks, memory, +# boot-mode, cpu-capabilities, pci-devices, parse-lldp]. +#inspector_hooks_default: -# List of of additional inspector processing plugins. -#inspector_processing_hooks_extra: +# List of extra inspection hooks to enable. Default is an empty list. +#inspector_hooks_extra: -# List of of additional inspector processing plugins. -#inspector_processing_hooks: +# List of of additional inspector hooks to enable. Default is +# {{ inspector_hooks_default + inspector_hooks_extra }}. +#inspector_hooks: + +############################################################################### +# Common Ironic Inspector processing configuration. # Which MAC addresses to add as ports during introspection. One of 'all', -# 'active' or 'pxe'. +# 'active' or 'pxe'. Default is 'pxe'. #inspector_add_ports: # Which ports to keep after introspection. One of 'all', 'present', or 'added'. +# Default is 'added'. #inspector_keep_ports: -# Whether to enable discovery of nodes not managed by Ironic. +# Whether to enable discovery of nodes not managed by Ironic. Default is true. #inspector_enable_discovery: -# The Ironic driver with which to register newly discovered nodes. +# The Ironic driver with which to register newly discovered nodes. Default is +# 'ipmi'. #inspector_discovery_enroll_node_driver: ############################################################################### -# Ironic inspector configuration. +# Ironic inspector introspection rules configuration. # Ironic inspector option to enable IPMI rules. Set to 'True' by default. #inspector_rules_ipmi_enabled: @@ -90,9 +98,6 @@ # Redfish CA setting. Set to 'True' by default #inspector_rule_var_redfish_verify_ca: -############################################################################### -# Ironic inspector introspection rules configuration. - # Ironic inspector rule to set IPMI credentials. #inspector_rule_ipmi_credentials: @@ -102,9 +107,6 @@ # Ironic inspector rule to set deployment ramdisk. #inspector_rule_deploy_ramdisk: -# Ironic inspector rule to initialise root device hints. -#inspector_rule_root_hint_init: - # Ironic inspector rule to set serial root device hint. #inspector_rule_root_hint_serial: diff --git a/playbooks/kayobe-overcloud-base/run.yml b/playbooks/kayobe-overcloud-base/run.yml index cdc3828e2..76ecd8af6 100644 --- a/playbooks/kayobe-overcloud-base/run.yml +++ b/playbooks/kayobe-overcloud-base/run.yml @@ -41,7 +41,6 @@ cmd: dev/overcloud-test-baremetal.sh &> {{ logs_dir }}/ansible/overcloud-test-baremetal chdir: "{{ kayobe_src_dir }}" executable: /bin/bash - when: false # TODO(priteau): Re-enable with built-in inspection - name: Register dummy baremetal compute nodes shell: diff --git a/playbooks/kayobe-overcloud-upgrade-base/run.yml b/playbooks/kayobe-overcloud-upgrade-base/run.yml index 03d5e3e72..820536979 100644 --- a/playbooks/kayobe-overcloud-upgrade-base/run.yml +++ b/playbooks/kayobe-overcloud-upgrade-base/run.yml @@ -109,7 +109,6 @@ cmd: dev/overcloud-test-baremetal.sh &> {{ logs_dir }}/ansible/overcloud-test-bm-post-upgrade chdir: "{{ kayobe_src_dir }}" executable: /bin/bash - when: false # TODO(priteau): Re-enable with built-in inspection environment: KAYOBE_CONFIG_SOURCE_PATH: "{{ kayobe_config_src_dir }}" diff --git a/releasenotes/notes/adds-support-for-configuring-builtin-inspector-04ab4ea4f1a1c3c8.yaml b/releasenotes/notes/adds-support-for-configuring-builtin-inspector-04ab4ea4f1a1c3c8.yaml new file mode 100644 index 000000000..a721182c8 --- /dev/null +++ b/releasenotes/notes/adds-support-for-configuring-builtin-inspector-04ab4ea4f1a1c3c8.yaml @@ -0,0 +1,26 @@ +--- +upgrade: + - | + ``inspector_processing_hooks`` has been removed. A new variable named + ``inspector_hooks`` has been introduced to to replace it since the names of + the hooks differ between standalone and built-in implementations. See + :ironic-doc:`Ironic documentation ` for + more details. + - | + Support for standalone inspector has been removed. All Ironic nodes will + need to be migrated from the ``inspector`` inspect-interface to ``agent``. + It is recommended that you do this after upgrading, but you will need to + ensure that you add ``inspector`` and ``agent`` to + ``kolla_ironic_enabled_inspect_interfaces`` for the upgrade. Check that + ``kolla_ironic_default_inspect_interface`` is not set to ``inspector``. + See :ironic-doc:`Ironic documentation ` for + more details. + - | + The format of inspection rules has changed. Any custom rules will need + to be updated to the new format. See + :ironic-doc:`Ironic documentation ` + for more details. + - | + The format of the data output from ``kayobe baremetal compute introspection + data save`` and ``kayobe overcloud introspection data save`` has changed. + You may need to update any tooling that is using this data. diff --git a/requirements.yml b/requirements.yml index 2490a5bcb..68c92d3a2 100644 --- a/requirements.yml +++ b/requirements.yml @@ -22,7 +22,7 @@ collections: - name: stackhpc.network version: 1.0.0 - name: stackhpc.openstack - version: 0.2.2 + version: 0.6.0 roles: - src: ahuffman.resolv