Cisco configuration backup with Netbox and Gitlab

Table of contents

  1. Introduction
  2. Building a list (dict) with Device data from Netbox
    1. Importing libraries
    2. Define variables
    3. Query Netbox API
    4. Building device list
    5. Cleanup
  3. Control backup script with device status

Related posts

Introduction

I’m planning a small blog post series about Cisco configuration backup with Netbox and Gitlab.

My idea is to “control” which devices are backed up by Netbox with Device Type, Site, IPv4 address and Status (“Active”).

Based on above information the script should then login with SSH to the devices, send a couple of commands like banner, wr mem, copy run scp to start a file transfer to save the configuration on a SCP host temporary directory.

The configuration files are then checked in into a GitLab repository so it’s easy to track and compare configuration files.

To outline the idea, I made the drawing shown below.

Concept Cisco configuration backup with Netbox and Gitlab

Building a list (dict) with Device data from Netbox

Importing libraries

This script uses Python3 and some libraries. For example pynetbox is used to get information from Netbox, pexpect is used for SSH and CLI for the devices, gitlab is used to communicate with the Gitlab server.


    #!/usr/bin/python3
    import pynetbox
    import requests
    import urllib3
    import pexpect
    import sys
    import json
    import gitlab
    from datetime import date
    today = str(date.today())

Define variables

Some variables need to be defined, for example:

  • Netbox and Gitlab URL and API Token

  • SCP for the file transfer

  • Backup User to log in into Cisco devices

Some notes: I am not using TFTP therefore the Script needs some Login credentials for SCP. The Backup User is created on a RADIUS server and valid for all devices.


    ###############################################################################
    #
    # Set global variables
    #
    ###############################################################################
    # Netbox URL and Token
    netbox_url = "https://HOST_OR_IP"
    netbox_token = "TOKEN"
    # Gitlab URL and Token
    gitlab_url = "https://HOST_OR_IP"
    gitlab_token = "TOKEN"
    # SCP User, Password and Host (SCP host can be same like Netbox host)
    scp_user = "USERNAME"
    scp_pass = "PASSWORD"
    scp_host = "HOST_OR_IP"
    # Set User, Password and Enable to connect with SSH to devices
    backup_user = "USERNAME"
    backup_pass = "PASSWORD"
    backup_enable = "ENABLE_PASSWORD"
    # Set Gitlab project ID
    project_id = PROJECT_ID

Query Netbox API

In the next step the script is connecting to the Netbox API to get out a list (dict) with the Device Type, Site, Hostname, IPv4 and Status.


    def getDevicesFromNetbox():
      urllib3.disable_warnings()
      requests.packages.urllib3.disable_warnings()
      netbox = pynetbox.api(
        url = netbox_url,
        token = netbox_token,
        threading = True
      )
      netbox.http_session.verify = False

      # init empty device array
      devices_netbox = []

In my Netbox setup, I defined the Device Type in a specific scheme, for example network device types start with “net-“, UPS (Uninterruptable Power Supply) start with “pwr-” and so on.

Building device list

To build the list (dict), the script is using the pynetbox filter and then the script is appending the devices to the initialized list. This is repeating for my core-switches, access-switches, wireless-controllers and wan-firewalls.


    #
    # Build a list with Core-Switches
    # Note: The filtering by role depends on the roles defined in netbox
    #
    # the list needs at minimum the following information:
    # device / site / hostname / status / ipv4 address
    # device: used to determine the device and what CLI commands are required to start a backup
    # site: used to build a directory structure in gitlab project
    # hostname: the hostname of the device to build .cfg
    # status: to determine if device can be backed up or is out of it
    # ipv4: IP address to connect to the device

    core_switch = netbox.dcim.devices.filter(role='net-core-switch')
    for i in range(0, len(core_switch)):
      data = netbox.dcim.devices.get(name = core_switch[i])
      hostname = str(data).split(":")
      hostname = hostname[0]
      device_details = ["switch", str(data.site), hostname, str(data.primary_ip), str(data.status)]
      devices_netbox.append(device_details)

    access_switch = netbox.dcim.devices.filter(role='net-access-switch')
    for i in range(0, len(access_switch)):
      data = netbox.dcim.devices.get(name = access_switch[i])
      hostname = str(data).split(":")
      hostname = hostname[0]
      device_details = ["switch", str(data.site), hostname, str(data.primary_ip), str(data.status)]
      devices_netbox.append(device_details)

    wifi_controller = netbox.dcim.devices.filter(role='net-wireless-lan-controller')
    for i in range(0, len(wifi_controller)):
      data = netbox.dcim.devices.get(name = wifi_controller[i])
      hostname = str(data).split(":")
      hostname = hostname[0]
      device_details = ["wificontroller", str(data.site), hostname, str(data.primary_ip), str(data.status)]
      devices_netbox.append(device_details)

    wan_firewall = netbox.dcim.devices.filter(role='net-wan-firewall')
    for i in range(0, len(wan_firewall)):
      data = netbox.dcim.devices.get(name = wan_firewall[i])
      hostname = str(data).split(":")
      hostname = hostname[0]
      device_details = ["wanfirewall", str(data.site), hostname, str(data.primary_ip), str(data.status)]
      devices_netbox.append(device_details)

    return devices_netbox

The result is a list (dict) which contains the data to control the SSH session. With this, the script can then use a function for a Cisco switch or another function for a Cisco Wireless Controller. This separation is required because each device type uses a different CLI syntax. Also the Site can get important if the script should handle more than one location.

Cleanup

For Cisco Switch Stacks, some “cleanup” is required. In my case, the first switch in a stack has the extension “:1”, the second switch in a stack “:2” and so on.

Also Netbox returns the subnet bits in the IPv4 Address. This means, a “cleanup” for the IPv4 Address is required as well.

Note: For security reasons, hostnames and IP addresses are obfuscated.

Screenshot: List with network devices from Netbox

Control backup script with device status

To verify if I can control the planned backup scriopt from Netbox, I am setting for example one of my ASA Firewalls of “Offline”.

And as expected, the script output shows the status of this ASA Firewall as “Offline”.

Screenshot: Device offline in Netbox and script output