diff --git a/playbooks/openstack.yml b/playbooks/openstack.yml index 871875e302..309f3ebcfa 100644 --- a/playbooks/openstack.yml +++ b/playbooks/openstack.yml @@ -126,6 +126,11 @@ tags: - neutron + - role: ironic + when: atmosphere_ironic_enabled | default(false) | bool + tags: + - ironic + # NOTE(mnaser): This is disabled out of the box until we have a native way # of configuring it with a pre-configured backend out of the # box. diff --git a/releasenotes/notes/add-opt-in-ironic-openstack-role-8ad2a10d2f06bb02.yaml b/releasenotes/notes/add-opt-in-ironic-openstack-role-8ad2a10d2f06bb02.yaml new file mode 100644 index 0000000000..19aa00440a --- /dev/null +++ b/releasenotes/notes/add-opt-in-ironic-openstack-role-8ad2a10d2f06bb02.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The standard OpenStack deployment playbook can now deploy the Ironic role + when ``atmosphere_ironic_enabled`` is set to ``true``. The option defaults + to ``false`` so existing deployments remain unchanged. diff --git a/roles/ironic/README.md b/roles/ironic/README.md index 03dfba09bb..c061228f1a 100644 --- a/roles/ironic/README.md +++ b/roles/ironic/README.md @@ -1 +1,14 @@ # `ironic` + +The Ironic role deploys the OpenStack Bare Metal service. It is available from +the standard `playbooks/openstack.yml` workflow but remains disabled by +default. + +Enable it in inventory variables: + +```yaml +atmosphere_ironic_enabled: true +``` + +The playbook condition uses `default(false)`, so inventories that do not define +the variable retain the existing deployment behavior. diff --git a/roles/openstack_helm_endpoints/tasks/main.yml b/roles/openstack_helm_endpoints/tasks/main.yml index 9170fd0e8a..0264d4c1e6 100644 --- a/roles/openstack_helm_endpoints/tasks/main.yml +++ b/roles/openstack_helm_endpoints/tasks/main.yml @@ -76,7 +76,7 @@ ansible.builtin.set_fact: openstack_helm_endpoints: | {{ openstack_helm_endpoints | combine(lookup('vars', '_openstack_helm_endpoints_' + service), recursive=True) }} - loop: "{{ openstack_helm_endpoints_list }}" + loop: "{{ openstack_helm_endpoints_list | default([], true) }}" loop_control: loop_var: service diff --git a/tests/unit/test_ironic_playbook.py b/tests/unit/test_ironic_playbook.py new file mode 100644 index 0000000000..9d6d0a76e4 --- /dev/null +++ b/tests/unit/test_ironic_playbook.py @@ -0,0 +1,47 @@ +# Copyright (c) 2026 VEXXHOST, Inc. +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +import yaml + + +def test_ironic_role_is_opt_in_and_follows_neutron(): + repository_root = Path(__file__).parents[2] + plays = yaml.safe_load( + (repository_root / "playbooks" / "openstack.yml").read_text() + ) + + openstack_play = next( + play + for play in plays + if play.get("hosts") == "controllers[0]" + and any(role.get("role") == "neutron" for role in play.get("roles", [])) + ) + roles = openstack_play["roles"] + role_names = [role["role"] for role in roles] + ironic = roles[role_names.index("ironic")] + + assert role_names.index("ironic") > role_names.index("neutron") + assert ironic["when"] == ("atmosphere_ironic_enabled | default(false) | bool") + assert ironic["tags"] == ["ironic"] + + +def test_disabled_ironic_role_cannot_expand_an_empty_endpoint_loop(): + repository_root = Path(__file__).parents[2] + tasks = yaml.safe_load( + ( + repository_root + / "roles" + / "openstack_helm_endpoints" + / "tasks" + / "main.yml" + ).read_text() + ) + task = next( + task + for task in tasks + if task.get("name") == "Generate OpenStack-Helm endpoints" + ) + + assert task["loop"] == ("{{ openstack_helm_endpoints_list | default([], true) }}")