Upgrade IOS with ansible

Motivation

A reoccuring task of a network administrator is the upgrade of all kinds of devices in the network. A boring task that takes its time. A worthy task for automation. In this artice I want to present a collection of playbooks that will do the upgrade.

My intention was to make the playbooks modular based on the switch models. For every switch or router model Cisco offers different upgrade paths. So I have an umbrella playbook that determins if an upgrade is nescessary and than includes the correct task according to the device model.

My work was inspired by the work of Gerald Dykeman: Upgrading Cisco IOS Devices with Ansible

Inventory

In my inventory I define the devices and group them by the vendor and the device model. The vendor group defines the access to the device and in the model group I define the IOS version, I would like to have on my devices:

all:
  hosts:
    switch01:
      ansible_host: 192.0.2.1
    switch02:
      ansbile_host: 192.0.2.2

  children:
    cisco:
      hosts:
        switch01:
        switch02:
      vars:
        ansible_connection: network_cli
        ansible_network_os: ios
    c2960l:
      hosts:
        switch01:
      vars:
        compliant_ios_version: 15.2(7)E1
        ios_hash: b8bebb153c0fe95104f18785be2759e9
        ios_file: c2960l-universalk9-mz.152-7.E1.bin
    c9200l:
      hosts:
        switch02:
      vars:
        compliant_ios_version: 16.12.02
        ios_hash: 9ba9f898d529f5132b78bca636e95576
        ios_file: cat9k_lite_iosxe.16.12.02.SPA.bin

Of course, you also can define the devices, groups, and variables in a suitable backend assset management tool like netbox. In a productive enviromment with lots of devices you will use such an inventory backend anyway.

Meta-Playbook

The umbrella playbook checks if the upgrade is nescessary and delegates the work to the playbooks that take care of the specific switch models. The need for an upgrade resuls from a comparison of the running IOS version and the desired version defined in the inventory.

ios-upgrade.yaml
- hosts: cisco

  # Define the device model by parsing the ios fact
  vars:
    model: "{{ ansible_net_model | regex_replace('^WS-C(\\d+.).*$', '\\1') }}"

  tasks:

    - name: Include model series specific upgrade
      block:

        - name: do include
          include: "ios-upgrade-{{ model }}.yaml"

      # only if the running IOS version is not the desired version
      when: (ansible_net_version != compliant_ios_version)

If the running verison differs from the desired version defined in the inventory, the playbook passed the control to an other playbook specific for the device model.

Playbook for 2960L

The low-cost (ha, ha! yes, I know) access models from Cisco are the 2960L. The playbook for the upgrade of this model is:

ios-upgrade-2960L.yaml
  - name: Copy IOS to device
    ios_command:
      commands:
        - command: 'copy tftp://192.0.2.16/{{ ios_file }} flash:'
          prompt: 'Destination filename \[{{ ios_file }}\]?'
          answer: "\r"
    vars:
      ansible_command_timeout: 600

  - name: Check md5sum of IOS on device
    ios_command:
      commands:
        - command: 'verify /md5 flash:{{ ios_file }} {{ ios_hash }}'
    register: md5_output

  - name: Continue if hash matches
    block:

      - name: Set new boot variable
        ios_config:
          lines: boot system flash:{{ ios_file }}
          save_when: always

      - name: Reboot device
        ios_command:
          commands:
            - command: reload
              prompt: '\[confirm\]'
              answer: "\r"

      - name: Wait for switch to reboot
        wait_for:
          host: "{{ ansible_host }}"
          port: 22
          delay: 60
          timeout: 600
        delegate_to: localhost

      - name: Gather facts of the switch again
        ios_facts:

      - name: Assert that the running IOS version is correct
        assert:
          that:
            - compliant_ios_version == ansible_net_version
          msg: "IOS version does not match compliant version. Upgrade unsuccessful."

    when: '"Verified" in md5_output.stdout[0]'

Playbook for 9200L

Enterprise grade access switches are the various 9200 models. The upgrade of its operating system is different:

ios-upgrade-9200L.yaml
  - name: remove old OS files
    ios_command:
      commands:
        - command: install remove inactive
          prompt: Do you want to remove the above files
          answer: 'y'
    vars:
      ansible_command_timeout: 300

  - name: Copy IOS to device
    ios_command:
      commands:
        - command: 'copy tftp://192.0.2.16/srv/tftp/{{ ios_file }} flash: vrf Mgmt-vrf'
          prompt: 'Destination filename \[{{ ios_file }}\]?'
          answer: "\r"
    vars:
      ansible_command_timeout: 1800

  - name: Check md5sum of IOS on device
    ios_command:
      commands:
        - command: 'verify /md5 flash:{{ ios_file }} {{ ios_hash }}'
    register: md5_output

  - name: Continue if hash matches
    block:

      - name: Set new boot variable
        ios_config:
          lines: boot system flash:packages.conf
        save_when: always

      - name: Write config
        ios_command:
          commands:
            - command: wr mem

      - name: Install new IOS
        ios_command:
          commands:
            - command: 'install add file flash:{{ ios_file }} activate commit prompt-level none'
        vars:
          ansible_command_timeout: 1800

      - name: Wait for switch to reboot
        wait_for:
          host: "{{ ansible_host }}"
          port: 22
          delay: 60
          timeout: 600
        delegate_to: localhost

      - name: Gather facts of the switch again
        ios_facts:

      - name: Assert that the running IOS version is correct
        assert:
          that:
            - compliant_ios_version == ansible_net_version
          msg: "IOS version does not match compliant version. Upgrade unsuccessful."

    when: '"Verified" in md5_output.stdout[0]'

Please note the huge timeouts for the copy and the install operation. Feedback, improvements and other comments to ms@sys4.de.

Michael Schwartzkopff, 14 Feb 2020

   ansible    IOS    upgrade