diff --git a/ansible/dell-compute-node-boot-mode.yml b/ansible/dell-compute-node-boot-mode.yml new file mode 100644 index 000000000..37564df2e --- /dev/null +++ b/ansible/dell-compute-node-boot-mode.yml @@ -0,0 +1,44 @@ +--- +# Set the boot mode (BIOS, UEFI) of Dell compute nodes. + +- name: Ensure compute nodes are present in the Ansible inventory + hosts: config-mgmt + gather_facts: no + vars: + # Set this to a colon-separated list of compute node hostnames on which to + # set the boot mode. If unset, all compute nodes will be set. + compute_node_limit: "" + compute_node_limit_list: "{{ compute_node_limit.split(':') }}" + tasks: + - name: Add hosts for the compute nodes + add_host: + name: "{{ item.key }}" + groups: compute + with_dict: "{{ idrac_network_ips }}" + # Don't add hosts that already exist. + when: + - "{{ item.key not in groups['all'] }}" + - "{{ item.key | replace('-idrac', '') not in groups['all'] }}" + - "{{ not compute_node_limit or item.key | replace('-idrac', '') in compute_node_limit_list }}" + run_once: True + +- name: Ensure compute nodes boot mode is set + hosts: compute + gather_facts: no + vars: + # Set this to the required boot mode. One of 'bios' or 'uefi'. + drac_boot_mode: "bios" + tasks: + - name: Set a fact containing the compute node IPMI address + set_fact: + ansible_host: "{{ idrac_network_ips[inventory_hostname] }}" + ansible_user: "{{ ipmi_username }}" + ansible_ssh_pass: "{{ ipmi_password }}" + + - name: Ensure the compute node's BMC is added as an SSH known host + include_role: + role: ssh-known-host + + - name: Ensure the compute node's boot mode is set + include_role: + role: drac-boot-mode diff --git a/ansible/roles/drac-boot-mode/README.md b/ansible/roles/drac-boot-mode/README.md new file mode 100644 index 000000000..5a3ca4b3b --- /dev/null +++ b/ansible/roles/drac-boot-mode/README.md @@ -0,0 +1,35 @@ +DRAC Boot Mode +============== + +Ansible role to set the BIOS boot mode for a Dell server with a DRAC. + +Requirements +------------ + +None + +Role Variables +-------------- + +`drac_boot_mode`: Set this to the required boot mode. + +`drac_retries`: Number of times to attempt to perform write operations. + +`drac_delay`: Delay between successive write operations. + +Example Playbook +---------------- + +The following playbook sets the boot mode of a host to 'BIOS'. + + --- + - name: Ensure DRAC boot mode is BIOS + hosts: dracs + roles: + - role: drac-boot-mode + drac_boot_mode: bios + +Author Information +------------------ + +- Mark Goddard () diff --git a/ansible/roles/drac-boot-mode/defaults/main.yml b/ansible/roles/drac-boot-mode/defaults/main.yml new file mode 100644 index 000000000..24efaa767 --- /dev/null +++ b/ansible/roles/drac-boot-mode/defaults/main.yml @@ -0,0 +1,12 @@ +--- +# Set this to the required boot mode. +drac_boot_mode: "bios" + +# Timeout when waiting for boot mode configuration to be applied. +drac_boot_mode_timeout: 600 + +# Number of times to attempt to perform write operations. +drac_boot_mode_retries: 5 + +# Interval between successive write operations. +drac_boot_mode_interval: 5 diff --git a/ansible/roles/drac-boot-mode/tasks/main.yml b/ansible/roles/drac-boot-mode/tasks/main.yml new file mode 100644 index 000000000..a2c2ce8e2 --- /dev/null +++ b/ansible/roles/drac-boot-mode/tasks/main.yml @@ -0,0 +1,64 @@ +--- +- name: Fail if the requested boot mode is unexpected + fail: + msg: > + Unexpected requested boot mode {{ drac_boot_mode }}. Expected one of + {{ drac_boot_mode_valid_modes | join(', ') }}. + when: "{{ drac_boot_mode | lower not in drac_boot_mode_valid_modes }}" + +- name: Check the boot mode + raw: "racadm get BIOS.BiosBootSettings.BootMode" + register: result + failed_when: "'ERROR' in result.stdout" + changed_when: False + +- name: Set a fact containing the current boot mode + set_fact: + # Format of the last line is: + # BootMode=[ (Pending Value=)] + current_boot_mode: "{{ result.stdout_lines[-1].partition('=')[2] | lower }}" + +- name: Fail if the current boot mode is unexpected + fail: + msg: > + Unexpected current boot mode {{ current_boot_mode }}. Expected one of + {{ drac_boot_mode_valid_modes | join(', ') }}. + when: "{{ current_boot_mode not in drac_boot_mode_valid_modes }}" + +- block: + - name: Set the boot mode + raw: "racadm set BIOS.BiosBootSettings.BootMode {{ drac_boot_mode | lower | capitalize }}" + register: result + failed_when: "'ERROR' in result.stdout" + until: "{{ drac_boot_mode_busy_message not in result.stdout }}" + retries: "{{ drac_boot_mode_retries }}" + delay: "{{ drac_boot_mode_interval }}" + + - name: Ensure BIOS configuration job is created + raw: "racadm jobqueue create BIOS.Setup.1-1 -s TIME_NOW" + register: result + failed_when: "'ERROR' in result.stdout" + until: "{{ drac_boot_mode_busy_message not in result.stdout }}" + retries: "{{ drac_boot_mode_retries }}" + delay: "{{ drac_boot_mode_interval }}" + + - name: Set a fact containing the BIOS configuration job ID + set_fact: + # Format of the last line is: + # JOB_ID = + drac_boot_mode_bios_job_id: "{{ result.stdout_lines[-1].split()[-1] }}" + + - name: Ensure server is rebooted + raw: "racadm serveraction powercycle" + register: result + failed_when: "'ERROR' in result.stdout" + + - name: Wait for the BIOS configuration job to complete + raw: "racadm jobqueue view -i {{ drac_boot_mode_bios_job_id }}" + register: result + failed_when: "'ERROR' in result.stdout" + until: "{{ 'Status=Completed' in result.stdout }}" + retries: "{{ drac_boot_mode_timeout // drac_boot_mode_interval }}" + delay: "{{ drac_boot_mode_interval }}" + + when: "{{ current_boot_mode != drac_boot_mode }}" diff --git a/ansible/roles/drac-boot-mode/vars/main.yml b/ansible/roles/drac-boot-mode/vars/main.yml new file mode 100644 index 000000000..488055adf --- /dev/null +++ b/ansible/roles/drac-boot-mode/vars/main.yml @@ -0,0 +1,8 @@ +--- +# List of valid DRAC boot modes. +drac_boot_mode_valid_modes: + - "bios" + - "uefi" + +# Message emitted by the DRAC which can be retried. +drac_boot_mode_busy_message: "ERROR: Lifecycle Controller is currently in use."