From 91f8ff21d8f80a9520f73a84e32bed0c1d0b2bed Mon Sep 17 00:00:00 2001 From: David Galloway Date: Fri, 14 Oct 2016 13:05:27 -0400 Subject: [PATCH] firmware: New role. Adding BIOS and BMC support for mira Signed-off-by: David Galloway --- firmware.yml | 12 ++++ roles/firmware/defaults/main.yml | 9 +++ roles/firmware/tasks/main.yml | 19 ++++++ roles/firmware/tasks/mira/bios-update.yml | 75 +++++++++++++++++++++++ roles/firmware/tasks/mira/bios.yml | 14 +++++ roles/firmware/tasks/mira/bmc-update.yml | 30 +++++++++ roles/firmware/tasks/mira/bmc.yml | 27 ++++++++ 7 files changed, 186 insertions(+) create mode 100644 firmware.yml create mode 100644 roles/firmware/defaults/main.yml create mode 100644 roles/firmware/tasks/main.yml create mode 100644 roles/firmware/tasks/mira/bios-update.yml create mode 100644 roles/firmware/tasks/mira/bios.yml create mode 100644 roles/firmware/tasks/mira/bmc-update.yml create mode 100644 roles/firmware/tasks/mira/bmc.yml diff --git a/firmware.yml b/firmware.yml new file mode 100644 index 0000000..a1e9124 --- /dev/null +++ b/firmware.yml @@ -0,0 +1,12 @@ +--- +# "any_errors_fatal: true" makes sure the run stops if any problems happen. +# This gives you the ability to flash backed up firmwares or diagnose +# problems without the playbook cleaning up after itself or causing more damage. + +- hosts: all + any_errors_fatal: true + strategy: free + roles: + - secrets + - firmware + become: true diff --git a/roles/firmware/defaults/main.yml b/roles/firmware/defaults/main.yml new file mode 100644 index 0000000..5897fbf --- /dev/null +++ b/roles/firmware/defaults/main.yml @@ -0,0 +1,9 @@ +--- +# Defaults should be overridden in the secrets repo in each machine type's +# group_vars file +latest_bios_version: null +latest_bmc_version: null + +flashrom_location: "http://download.flashrom.org/releases/flashrom-0.9.9.tar.bz2" + +firmware_update_path: "/home/{{ ansible_user }}/firmware-update" diff --git a/roles/firmware/tasks/main.yml b/roles/firmware/tasks/main.yml new file mode 100644 index 0000000..a3bb2e8 --- /dev/null +++ b/roles/firmware/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- include: mira/bios.yml + tags: + - bios + when: '"mira" in ansible_hostname' + +- include: mira/bmc.yml + tags: + - bmc + when: '"mira" in ansible_hostname' + +# This won't get run if a previous playbook fails. So if a backup of a BIOS is +# needed to restore, it'll still be there +- name: Clean up firmware update directory + file: + path: "{{ firmware_update_path }}" + state: absent + tags: + - always diff --git a/roles/firmware/tasks/mira/bios-update.yml b/roles/firmware/tasks/mira/bios-update.yml new file mode 100644 index 0000000..bcee809 --- /dev/null +++ b/roles/firmware/tasks/mira/bios-update.yml @@ -0,0 +1,75 @@ +--- +# This file is only called when current_bios_version +# and latest_bios_version do not match + +- name: Install packages for CentOS/RHEL + yum: + name: "{{ item }}" + state: latest + with_items: + - pciutils-devel + - zlib-devel + - libftdi-devel + - libusb-devel + - make + - gcc + when: ansible_pkg_mgr == "yum" + +- name: Install packages for Ubuntu + apt: + name: "{{ item }}" + state: latest + with_items: + - flashrom + when: ansible_pkg_mgr == "apt" + +# Flashrom has to be built on CentOS so we add an extra dir for it +# This is equivalent to 'mkdir -p' +- name: Create BIOS update working directory structure + file: + path: "{{ firmware_update_path }}/bios-update/flashrom" + state: directory + +# This file must be the already-extracted binary blob from the Supermicro +# firmware archive. Naming scheme is PPPPPY.MDD +# PPPPP = Project name; Y = Year; M = Month; DD = Day +# We rename it to 'new-bios' here so the playbook can consume a universal name +- name: Download BIOS binary + get_url: + url: "{{ bios_location }}" + dest: "{{ firmware_update_path }}/bios-update/new-bios" + +# There is flashrom RPM in any trusted repositories so we have to compile it +- name: Download flashrom archive (CentOS) + get_url: + url: "{{ flashrom_location }}" + dest: "{{ firmware_update_path }}/bios-update/flashrom.tar.bz2" + validate_certs: no + when: ansible_pkg_mgr == "yum" + +# The flashrom tarballs extract to a directory with its version number by default +# '--strip-components 1' gets rid of that dir so the playbook can run with any +# flashrom version +- name: Extract flashrom (CentOS) + shell: "tar -xjf {{ firmware_update_path }}/bios-update/flashrom.tar.bz2 --directory {{ firmware_update_path }}/bios-update/flashrom --strip-components 1" + when: ansible_pkg_mgr == "yum" + +- name: Compile flashrom (CentOS) + shell: "cd {{ firmware_update_path }}/bios-update/flashrom && make" + when: ansible_pkg_mgr == "yum" + +- name: Back up existing BIOS (CentOS) + shell: "cd {{ firmware_update_path }}/bios-update && flashrom/flashrom --programmer internal --read BIOS.bak" + when: ansible_pkg_mgr == "yum" + +- name: Flash new BIOS (CentOS) + shell: "cd {{ firmware_update_path }}/bios-update && flashrom/flashrom --programmer internal --write new-bios" + when: ansible_pkg_mgr == "yum" + +- name: Back up existing BIOS (Ubuntu) + shell: "flashrom -p internal:Supermicro:X8SIL --read {{ firmware_update_path }}/bios-update/BIOS.bak" + when: ansible_pkg_mgr == "apt" + +- name: Flash new BIOS (Ubuntu) + shell: "flashrom -p internal:Supermicro:X8SIL --write {{ firmware_update_path }}/bios-update/new-bios" + when: ansible_pkg_mgr == "apt" diff --git a/roles/firmware/tasks/mira/bios.yml b/roles/firmware/tasks/mira/bios.yml new file mode 100644 index 0000000..8a197bc --- /dev/null +++ b/roles/firmware/tasks/mira/bios.yml @@ -0,0 +1,14 @@ +--- +- name: Determine current BIOS firmware version + shell: dmidecode --type bios | grep Version | awk '{ print $2 }' + register: current_bios_version + changed_when: False + +- name: Determine if BIOS update is needed + set_fact: + need_bios_update: true + when: current_bios_version.stdout != latest_bios_version + +- name: Include BIOS update logic + include: roles/firmware/tasks/mira/bios-update.yml + when: need_bios_update is defined and need_bios_update == true diff --git a/roles/firmware/tasks/mira/bmc-update.yml b/roles/firmware/tasks/mira/bmc-update.yml new file mode 100644 index 0000000..cac2590 --- /dev/null +++ b/roles/firmware/tasks/mira/bmc-update.yml @@ -0,0 +1,30 @@ +--- +# This file is only called when current_bmc_version +# and latest_bmc_version do not match + +- name: Install unzip + package: + name: unzip + state: latest + +- name: Create BMC update working directory structure + file: + path: "{{ firmware_update_path }}/bmc-update" + state: directory + +# Download the archive and rename to something the playbook can consume +- name: Download BMC archive + get_url: + url: "{{ bmc_location }}" + dest: "{{ firmware_update_path }}/bmc-update/bmc.zip" + force: yes + +- name: Extract IPMI archive + shell: "cd {{ firmware_update_path }}/bmc-update && unzip bmc.zip" + +- name: Flash new BMC (Takes around 5 minutes) + shell: "cd {{ firmware_update_path }}/bmc-update/Linux* && chmod +x lUpdate && ./lUpdate -f ../*.bin -i kcs -r y" + register: bmc_flash_output + +# Print output of flash script +- debug: var=bmc_flash_output.stdout_lines|last diff --git a/roles/firmware/tasks/mira/bmc.yml b/roles/firmware/tasks/mira/bmc.yml new file mode 100644 index 0000000..179d964 --- /dev/null +++ b/roles/firmware/tasks/mira/bmc.yml @@ -0,0 +1,27 @@ +--- +- name: Install ipmitool + package: + name: ipmitool + state: latest + +- name: Enable IPMI kernel modules + modprobe: + name: "{{ item }}" + state: present + with_items: + - ipmi_devintf + - ipmi_si + +- name: Determine current BMC firmware version + shell: ipmitool mc info | grep "Firmware Revision" | awk '{ print $4 }' + register: current_bmc_version + changed_when: False + +- name: Determine if BMC update is needed + set_fact: + need_bmc_update: true + when: current_bmc_version.stdout != latest_bmc_version + +- name: Include BMC update logic + include: roles/firmware/tasks/mira/bmc-update.yml + when: need_bmc_update is defined and need_bmc_update == true -- 2.39.5