--- # Use bifrost to provision the overcloud nodes with a base OS. - name: Ensure the overcloud nodes are provisioned hosts: overcloud max_fail_percentage: >- {{ overcloud_provision_max_fail_percentage | default(kayobe_max_fail_percentage) | default(100) }} tags: - provision vars: wait_manageable_timeout: 60 wait_available_timeout: 60 # Set to False to avoid waiting for the nodes to become active. wait_active: True wait_active_timeout: 600 wait_active_interval: 10 # Set to False to avoid waiting for the nodes to be accessible via # SSH. wait_ssh: True wait_ssh_timeout: 600 # List of states from which we can get to active. provisionable_states: - enroll - manageable - available - active # List of valid states while a node is being provisioned. deploying_states: # The API is asynchronous, so allow the initial state. - available - deploying - wait call-back # List of hosts to limit Bifrost deploy-dynamic.yaml playbook to. bifrost_limit: ['localhost'] # Retries to use when using Ironic API and hitting node locked errors. ironic_retries: 6 ironic_retry_interval: 5 seed_host: "{{ groups['seed'][0] }}" gather_facts: no tasks: - name: Check the ironic node's initial provision state command: > docker exec bifrost_deploy bash -c ' export OS_CLOUD=bifrost && export OS_BAREMETAL_API_VERSION=1.34 && export BIFROST_INVENTORY_SOURCE=ironic && export BIFROST_NODE_NAMES="{{ inventory_hostname }}" && ansible baremetal --connection local --inventory /etc/bifrost/inventory/ -e @/etc/bifrost/bifrost.yml -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m command -a "baremetal node show {% raw %}{{ inventory_hostname }}{% endraw %} -f value -c provision_state"' register: show_result changed_when: False delegate_to: "{{ seed_host }}" vars: # NOTE: Without this, the seed's ansible_host variable will not be # respected when using delegate_to. ansible_host: "{{ hostvars[seed_host].ansible_host | default(seed_host) }}" - name: Set a fact containing the ironic node's initial provision state set_fact: initial_provision_state: "{{ show_result.stdout_lines[1] }}" - name: Fail if the ironic node is in an unexpected provision state fail: msg: > Ironic node for {{ inventory_hostname }} is in an unexpected initial provision state: {{ initial_provision_state }}. Expected states are: {{ provisionable_states | join(',') }}. when: initial_provision_state not in provisionable_states - name: Ensure the ironic node is manageable command: > docker exec bifrost_deploy bash -c ' export OS_CLOUD=bifrost && export BIFROST_INVENTORY_SOURCE=ironic && export BIFROST_NODE_NAMES="{{ inventory_hostname }}" && ansible baremetal -vvvv --connection local --inventory /etc/bifrost/inventory/ -e @/etc/bifrost/bifrost.yml -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m command -a "baremetal node manage --wait {{ wait_manageable_timeout }} {% raw %}{{ inventory_hostname }}{% endraw %}"' register: manage_result until: manage_result is successful or 'is locked by host' in manage_result.stdout retries: "{{ ironic_retries }}" delay: "{{ ironic_retry_interval }}" when: initial_provision_state == 'enroll' delegate_to: "{{ seed_host }}" vars: # NOTE: Without this, the seed's ansible_host variable will not be # respected when using delegate_to. ansible_host: "{{ hostvars[seed_host].ansible_host | default(seed_host) }}" - name: Ensure the ironic node is available command: > docker exec bifrost_deploy bash -c ' export OS_CLOUD=bifrost && export BIFROST_INVENTORY_SOURCE=ironic && export BIFROST_NODE_NAMES="{{ inventory_hostname }}" && ansible baremetal -vvvv --connection local --inventory /etc/bifrost/inventory/ -e @/etc/bifrost/bifrost.yml -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m command -a "baremetal node provide --wait {{ wait_available_timeout }} {% raw %}{{ inventory_hostname }}{% endraw %}"' register: provide_result until: >- provide_result is successful or 'is locked by host' in provide_result.stdout or 'while it is in state "available"' in provide_result.stdout failed_when: - provide_result is not successful # If fast-track deployment is enabled, it's possible that ironic will # move the node to available, even though the initial state was # manageable. - "'while it is in state \"available\"' not in provide_result.stdout" retries: "{{ ironic_retries }}" delay: "{{ ironic_retry_interval }}" when: initial_provision_state in ['enroll', 'manageable'] delegate_to: "{{ seed_host }}" vars: # NOTE: Without this, the seed's ansible_host variable will not be # respected when using delegate_to. ansible_host: "{{ hostvars[seed_host].ansible_host | default(seed_host) }}" - name: Set a fact containing the bifrost host list set_fact: bifrost_limit: "{{ bifrost_limit + [item] }}" with_items: "{{ play_hosts }}" when: hostvars[item].initial_provision_state != 'active' run_once: True - name: Ensure the ironic nodes are provisioned command: > docker exec bifrost_deploy bash -c ' export OS_CLOUD=bifrost && export BIFROST_INVENTORY_SOURCE=ironic && ansible-playbook -vvvv /bifrost/playbooks/deploy-dynamic.yaml --inventory /etc/bifrost/inventory/ -e @/etc/bifrost/bifrost.yml -e @/etc/bifrost/dib.yml --limit {{ bifrost_limit | join(':') }}' when: bifrost_limit delegate_to: "{{ seed_host }}" vars: # NOTE: Without this, the seed's ansible_host variable will not be # respected when using delegate_to. ansible_host: "{{ hostvars[seed_host].ansible_host | default(seed_host) }}" # We execute this only once, allowing the Bifrost Ansible to handle # multiple nodes. run_once: True - name: Wait for the ironic node to become active command: > docker exec bifrost_deploy bash -c ' export OS_CLOUD=bifrost && export OS_BAREMETAL_API_VERSION=1.34 && export BIFROST_INVENTORY_SOURCE=ironic && export BIFROST_NODE_NAMES="{{ inventory_hostname }}" && ansible baremetal --connection local --inventory /etc/bifrost/inventory/ -e @/etc/bifrost/bifrost.yml -e @/etc/bifrost/dib.yml --limit {{ inventory_hostname }} -m command -a "baremetal node show {% raw %}{{ inventory_hostname }}{% endraw %} -f value -c provision_state"' register: show_result # Wait until the node is no longer in one of the deploying states. until: not show_result.stdout_lines[1:] | intersect(deploying_states) retries: "{{ wait_active_timeout // wait_active_interval }}" delay: "{{ wait_active_interval }}" when: - wait_active | bool - initial_provision_state != 'active' changed_when: False delegate_to: "{{ seed_host }}" vars: # NOTE: Without this, the seed's ansible_host variable will not be # respected when using delegate_to. ansible_host: "{{ hostvars[seed_host].ansible_host | default(seed_host) }}" - name: Set a fact containing the final provision state set_fact: final_provision_state: "{{ show_result.stdout_lines[1] }}" when: - wait_active | bool - initial_provision_state != 'active' - name: Fail if any of the nodes are not available fail: msg: > Ironic node for {{ inventory_hostname }} is in an unexpected provision state after provisioning. Ironic provision state: {{ final_provision_state }}. Expected: active. when: - wait_active | bool - initial_provision_state != 'active' - final_provision_state != 'active' - name: Wait for SSH access to the nodes local_action: module: wait_for host: "{{ ansible_host }}" port: 22 state: started timeout: "{{ wait_ssh_timeout }}" when: wait_ssh | bool