My Ansible playbook to update VyOS system

I just want to share my playbook to upgrade VyOS image with Ansible.
This playbook copies local iso file, (version 1.2.8 in my playbook).
Installs system with add system image
Schedules a reboot at ~4:30 in the morning.
Clean up old installation file.

Tested with ansible 2.9.14

---
- name: VyOS - install new image and schedule machine reboot.
  gather_facts: False
  vars:
    vyos_version: 1.2.8
    vyos_dir: "files/"
    vyos_file: "vyos-{{vyos_version}}-amd64.iso"
  hosts:
    - vyosrouter

  tasks:
    - name: "Copying {{vyos_file}} to system"
      net_put:
        src: "{{ vyos_dir }}{{ vyos_file }}"
        dest: "{{ vyos_file }}"

    - name: "Installing {{vyos_version}} on system"
      cli_command:
        command: add system image "{{ vyos_file }}"
        newline: True
        check_all: True
        prompt:
        - 'What would you like to name this image'
        - 'directory and config file'
        - 'current configuration'
        answer:
        - "{{ vyos_version }}"
        - 'Yes'
        - 'Yes'

    - name: Schedule reboot around 04:30 tonight
      cli_command:
        command: "reboot at 04:{{ range(25,35) | random }}"

    - name: Cleanup installation file
      cli_command:
        command: "rm {{ vyos_file }}"
5 Likes

Bonus python script to cleanup old images.
This will keep atleast two images on the router, and will not ask to delete running or default boot.
tested with netmiko 3.3.0. (pip3 install netmiko)
Just run it with python3 thisscript.py vyos.router.lan router2.ext

from netmiko import ConnectHandler
from re import match
from sys import argv

if (len(argv) < 2):
    print(f"Give atleast one router as argument... python3 ./{argv[0]} vyosrouter.lan")
    quit()

device = {
    'device_type': 'vyos',
    'username': 'user',
    'use_keys': True,
    'ssh_config_file': '~/.ssh/config',
}
# Loop through given arguments
for router in argv[1:]:
    device['host'] = router
    print(f"Connecting to {device['host']}...")
    connection = ConnectHandler(**device)
    print("Connected... finding images...")
    output = connection.send_command("show system image").splitlines()

    images = []
    print("==========================")
    for line in output:
        if v := match(r"^\s+[0-9]+\:\s(\S+.*)", line):
            images.append(line)
            print(line)
    print("==========================")

    # Ask to delete images in reverse order.
    # Dont ask to delete if there are two or less.
    for i, line in reversed(list(enumerate(images))):
        if(i < 2):
            print("====================")
            print("Must keep atleast two images!!!....")
            break
        # Assumes no whitespace in version name.
        elif m := match(r"^\s+[0-9]+\:\s(\S+)$", line):
            a = input(f"Delete: {m.group(1)} ? [Yes]/No ")
            if a in {"Yes", "yes", "y", ""}:
                print(f"Deleting system image {m.group(1)}...")
                delete  = connection.send_command_timing("delete system image " + m.group(1))
                delete += connection.send_command_timing("Yes")
                print(f"Successfully deleted {m.group(1)} ")
        # Skipping lines that contains text after version, it indicates its running/default boot
        elif m := match(r"^\s+[0-9]+\:\s(\S+).(.*)$", line):
            print(f"Skipping: {m.group(1)} {m.group(2)} ...")

    print(connection.send_command("show system image"))
    print(f"Disconnecting from {device['host']}...")
    connection.disconnect()
    print("============================================================")

1 Like