Home > Servers > Systems Management > White Papers > Dell PowerEdge: Getting Started with Redfish Ansible Modules > Upgrading the firmware of server components
As shown by vulnerabilities such as Spectre and Meltdown, being able to apply firmware upgrades in a timely fashion is critical to any IT infrastructure. Redfish and Ansible enable the automation of firmware upgrades of any components, such as network cards, PERC RAID cards, the server BIOS, or even the iDRAC itself. However, only a single firmware can be updated at a time, so to upgrade multiple firmware, you must duplicate the upgrade tasks for each of them.
In this example, we update the firmware of the PERC RAID controller by using the following playbook. The playbook assumes that each host in the inventory defines the baseuri variable, as previously described.
---
- hosts: testhosts
name: Update firmware of PERC Card
connection: local
gather_facts: False
vars:
ansible_python_interpreter: "/usr/bin/env python"
retries_count: 180
polling_interval: 5
username: <iDRAC user>
password: <iDRAC user password>
reboot_uri: "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
perc_firmware: "/home/msanders/Downloads/SAS-RAID_Firmware_6MTTK_WN64_52.16.1-4158_A05_01.EXE"
collections:
- dellemc.openmanage
tasks:
- name: "Upload new PERC firmware"
redfish_firmware:
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
image_uri: "{{ perc_firmware }}"
validate_certs: no
register: result
- name: "Track PERC upload job to completion"
uri:
url: "https://{{ baseuri }}{{ result.task.uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "GET"
use_proxy: yes
status_code: 200, 202
return_content: yes
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: job_result
until: job_result.json.TaskState == 'Completed' or job_result.json.TaskState == 'Starting'
retries: "{{ retries_count }}"
delay: "{{ polling_interval }}"
- name: "Reboot the server to update PERC firmware"
uri:
url: "https://{{ baseuri }}{{ reboot_uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "POST"
body_format: raw
body: '{"ResetType": "ForceRestart"}'
use_proxy: yes
status_code: 204
return_content: no
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: reboot_result
changed_when: reboot_result.status == 204
when: job_result.json.TaskState == 'Starting' and job_result.json.Messages.0.Message == 'Task successfully scheduled.'
- name: "Wait 5mins for PERC firmware to be applied"
wait_for:
timeout: 300
when: job_result.json.TaskState == 'Starting' and job_result.json.Messages.0.Message == 'Task successfully scheduled.'
- name: "Track PERC firmware update job"
uri:
url: "https://{{ baseuri }}{{ result.task.uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "GET"
use_proxy: yes
status_code: 200, 202
return_content: yes
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: final_result
until: final_result.json.TaskState == 'Completed'
retries: "{{ retries_count }}"
delay: "{{ polling_interval }}"
- name: "Fact from PERC firmware upgrade"
set_fact:
job_details: "{{ final_result.json }}"
failed_when: final_result.json.TaskState == "Completed" and final_result.json.TaskStatus != "OK"
changed_when: final_result.json.TaskState == "Completed" and final_result.json.TaskStatus == "OK"
A dissection of the playbook, starting with the vars section, follows:
vars:
ansible_python_interpreter: "/usr/bin/env python"
retries_count: 180
polling_interval: 5
username: <iDRAC user>
password: <iDRAC user password>
reboot_uri: "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
perc_firmware: "/home/user1/Downloads/SAS-RAID_Firmware_6MTTK_WN64_52.16.1-4158_A05_01.EXE"
The retries_count and polling_interval variables are used when we have to wait for a task to finish. The variable retries_count specifies how many times we want to retry, while polling_interval specifies how often we should check to see if the task has finished.
Next, we define the username and password to be used to log in to the iDRAC.
After each firmware update, the server must be rebooted. If you are performing multiple updates, defining a variable for the reboot URI can avoid significant typing and potential errors, which is why we have defined the reboot_uri variable here.
The final variable is perc_firmware, which points to the firmware package on the host from which the Ansible playbook will run. Firmware for PowerEdge servers is offered in either a .BIN package or a .EXE package. If you perform updates through the iDRAC web interface, the .BIN package must be used, but for updates performed using Redfish, the .EXE is required.
The required tasks to update the firmware are as follows:
- name: "Upload new PERC firmware"
redfish_firmware:
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
image_uri: "{{ perc_firmware }}"
validate_certs: no
register: result
This task starts a job to upload the firmware to the iDRAC. Depending on the size of the package and the speed of the network, this task can take a few minutes; however, it starts the job and returns as soon the job is started.
- name: "Track PERC upload job to completion"
uri:
url: "https://{{ baseuri }}{{ result.task.uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "GET"
use_proxy: yes
status_code: 200, 202
return_content: yes
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: job_result
until: job_result.json.TaskState == 'Completed' or job_result.json.TaskState == 'Starting'
retries: "{{ retries_count }}"
delay: "{{ polling_interval }}"
Because the previous task starts the upload job but does not wait for the job to be finished before returning, this task performs the necessary work of checking the status of the job. It polls the specified URI at every polling_interval until the number of retries equals retries_count or until the state of the job running on the iDRAC is either ‘Completed’ or ‘Starting’. If the number of retries is reached and the state of the job is neither ‘Completed’ nor ‘Starting’, the task fails.
- name: "Reboot the server to update PERC firmware"
uri:
url: "https://{{ baseuri }}{{ reboot_uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "POST"
body_format: raw
body: '{"ResetType": "ForceRestart"}'
use_proxy: yes
status_code: 204
return_content: no
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: reboot_result
changed_when: reboot_result.status == 204
when: job_result.json.TaskState == 'Starting' and job_result.json.Messages.0.Message == 'Task successfully scheduled.'
After the firmware update job is started on the iDRAC, the server must reboot before the Lifecycle Controller performs the update. The next task is to reboot the server.
- name: "Wait 5mins for PERC firmware to be applied"
wait_for:
timeout: 300
when: job_result.json.TaskState == 'Starting' and job_result.json.Messages.0.Message == 'Task successfully scheduled.'
Task #4 and task #5 are similar in that they are both waiting for the new firmware to be applied. We could have removed task #4 and started polling the server immediately, but instead we wait for 5 minutes before starting to poll the server. We take this approach because we know that the rebooting of the server and applying the new firmware will take some time. Depending on the server, this wait time could be adjusted or even removed.
- name: "Track PERC firmware update job"
uri:
url: "https://{{ baseuri }}{{ result.task.uri }}"
user: "{{ username }}"
password: "{{ password }}"
method: "GET"
use_proxy: yes
status_code: 200, 202
return_content: yes
validate_certs: no
force_basic_auth: yes
headers:
Content-Type: "application/json"
Accept: "application/json"
register: final_result
until: final_result.json.TaskState == 'Completed'
retries: "{{ retries_count }}"
delay: "{{ polling_interval }}"
As with task #2, this task polls the server to get the status of the iDRAC job and ensure that it is completed. It polls the specified URI at every polling_interval until the number of retries equals retries_count or until the state of the job running on the iDRAC is either ‘Completed’ or ‘Starting’. If the number of retries is reached and the state of the job is neither ‘Completed’ nor ‘Starting’, the task fails.
- name: "Fact from PERC firmware upgrade"
set_fact:
job_details: "{{ final_result.json }}"
failed_when: final_result.json.TaskState == "Completed" and final_result.json.TaskStatus != "OK"
changed_when: final_result.json.TaskState == "Completed" and final_result.json.TaskStatus == "OK"
This task gathers the facts of the previous tasks and show whether the update finished successfully or not.