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 }}"
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("============================================================")