cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1068
Views
6
Helpful
6
Replies

Junos & Ios-XE Netbox Rest API Python Script Network Automation

Jerems
Spotlight
Spotlight

Hi Dear Community,

Just wanted to share my experience with Python and Netbox Api to add automatically to my Netbox inventory on one hand a Cisco device running  IOS-XE and on the other hand a Juniper SRX device running Junos.

We generate two kinds of payload  to add a device-type and the device itself.

This script is part of a collection of post related to ZTP (Zero Touch Provisioning) with Cisco and juniper devices:

https://community.cisco.com/t5/controllers/matrix-for-ztp-process-on-ios-xe-devices/td-p/4763275

https://community.cisco.com/t5/controllers/you-plan-to-play-with-python-on-ios-xe-device/td-p/4756765

https://community.cisco.com/t5/controllers/can-quot-cli-quot-python-librairy-in-ios-xe-help-to-upgrade/td-p/4566969

https://community.cisco.com/t5/controllers/ios-xe-guestshell-and-python-librairies-list-all-python-packages/td-p/4565331

https://community.cisco.com/t5/controllers/question-about-guestshell-amp-ztp-on-isr1111-or-isr4321/td-p/4560791

https://community.cisco.com/t5/controllers/alternative-to-unavailable-module-requests-in-ios-xe-guestshell/td-p/4566130

In this LAB, the device acts as a DHCP client which is instructed to download and execute the following python script at the end of the boot process. Thanks to this, the device will be automatically added to my SSOT, Netbox.

Script for Cisco :

 

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
########################################################################################################################
# This file is a part of Jeyriku.net.
#
# Created: 05.05.2023 11:52:05
# Author: Jeremie Rouzet
#
# Last Modified: 19.01.2024 09:48:15
# Modified By: Jeremie Rouzet
#
# Copyright (c) 2023 Jeyriku.net
########################################################################################################################

# Import the Required Librairies
import time
import cli
import requests
import json

# The librairy "CLI" is used to extract and store in variables some datas coming from router/switch config

split_ip = cli.execute("sh ip int brief | in 192.168.100.")
temp_ip = split_ip.split()[1]
print(temp_ip)
temp_ip.split(".")
last_digit_ip = temp_ip.split(".")[3]
print(last_digit_ip)

# The librairy "CLI" is now used to configure global items on the router/switch.

print("\n\n *** Configuring Hostname *** \n\n")
config_hostname = cli.configure(["hostname jeyrouter" + "_" + last_digit_ip, "end"])
print(config_hostname)

print("\n\n *** Configuring Domain name + name server *** \n\n")
cli.configure(["ip domain name int.jeyriku.net", "ip name-server 192.168.0.252 192.168.0.250", "end"])

print("\n\n *** Configuring Username *** \n\n")
cli.configure(["username ****** privilege 15 secret 0 *******", "end"])

print("\n\n *** Configuring Line VTY *** \n\n")
cli.configure(
    ["line vty 0 15", "exec-timeout 0 0", "login local", "transport input telnet ssh", "escape-character 17", "end"]
)

# The librairy "CLI" is now used to configure IOX & Guestshell on the router/switch.

print("\n\n *** Configuring Iox *** \n\n")
cli.configure(["iox", "end"])

time.sleep(30)

print("\n\n *** Configuring Interface for App-Hosting *** \n\n")
cli.configure(
    [
        "interface VirtualPortGroup0",
        "ip address 192.168.1.1 255.255.255.0",
        "ip nat inside",
        # "no mop enabled",
        # "no mop sysid",
        "end",
    ]
)

print("\n\n *** Configuring Interface for App-Hosting *** \n\n")
cli.configure(["interface GigabitEthernet0/0/0", "ip address dhcp", "ip nat outside", "no shut", "end"])

print("\n\n *** Configuring ACL for App-Hosting *** \n\n")
cli.configure(["ip access-list extended GUESTSHELL-NAT-ACL", "10 permit ip 192.168.1.0 0.0.0.255 any", "end"])

print("\n\n *** Configuring NAT for App-Hosting *** \n\n")
cli.configure(["ip nat inside source list GUESTSHELL-NAT-ACL interface GigabitEthernet0/0/0 overload", "end"])


print("\n\n *** Configuring App-Hosting *** \n\n")
cli.configure(
    [
        "app-hosting appid guestshell",
        "app-vnic gateway0 virtualportgroup 0 guest-interface 0",
        "guest-ipaddress 192.168.1.2 netmask 255.255.255.0",
        "app-default-gateway 192.168.1.1 guest-interface 0",
        "name-server0 192.168.0.252",
        "name-server1 192.168.0.250",
        "end",
    ]
)

time.sleep(30)

# Guestshell is executed as a container which embbends a python interpreter to execute some scripts

print("\n\n *** Launch Guestshell *** \n\n")
output = cli.execute("guestshell enable")
print(output)

time.sleep(60)

# Below we build variables which will be part of the payload in the following Post request to Netbox

find_hostname = cli.execute("sh run | i hostname")
print(find_hostname)
init_hostname = find_hostname.split()[1]
print("The device name is: " + init_hostname)
find_ip = cli.execute("sh ip int brief | in 192.168.100.")
init_ip = find_ip.split()[1]
print("The device primary ip is: " + init_ip)

find_device_type = cli.execute("sh platform | in type")
print(find_device_type)
init_device_type = find_device_type.split()[2]
print("The device type is: " + init_device_type)
print(init_device_type)

# From here we build the Post request integrating Payload, Data, Header & URL to create a new device-type

headers = {
    "Content-Type": "application/json",
    "Authorization": "Token bf8261deae36fc89952f6eb3e5e2398a1360f1e7",
    "Cookie": "csrftoken=Gt6iIfIlQTZZHY7kvJFVMEXRQSsH7rqCQ3bKjop4Gbc1VfVx7YHRGXKTgCWlXmVe"
}
#URL EndPoint to add the device type. Be careful with the synthax and don't forget the slash at the end of the URL
url = "https://192.168.0.251/api/dcim/device-types/"

payload = json.dumps({
    "manufacturer": {
        "name": "Cisco Systems",
        "slug": "cisco-systems"
    },
    "model": init_device_type,
    "slug": init_device_type
})

# Here we add a new device-type to Netbox thanks to the previous steps

response = requests.request("POST", url, headers=headers, data=payload, verify=False, timeout=10)
print(response.text)

# New Payload for new item to be added to Netbox

payload1 = json.dumps({
    "name": init_hostname,
    "device_type": {"model": init_device_type},
    "device_role": {"name": "ToBeDefined"},
    "site": {"name": "JeyHome2"},
    "custom_fields": {
        "dns_server_pri": "192.168.0.252",
        "dns_server_sec": "192.168.0.250",
        "domain_name": "int.jeyriku.net",
        "dev_lpbk": "null",
        "snmp_community": "jeyricorp",
        "snmp_location": "int.jeyriku.net, 2 allee des campagnols, Seynod, 74600, Annecy, France [45.85995, 6.0793]",
        "snmp_server1": "192.168.0.248",
        "snmp_server2": "192.168.0.249"
    }
    })

url1 = "https://192.168.0.251/api/dcim/devices/"

# Add a new device to Netbox
response1 = requests.request("POST", url1, headers=headers, data=payload1, verify=False, timeout=10)
print(response1.text)

 

 Script for Juniper :

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
########################################################################################################################
# This file is a part of Jeyriku.net.
#
# Created: 05.05.2023 11:52:05
# Author: Jeremie Rouzet
#
# Last Modified: 19.04.2024 13:04:44
# Modified By: Jeremie Rouzet
#
# Copyright (c) 2023 Jeyriku.net
########################################################################################################################
import json
import requests
from jnpr.junos import Device


with Device(host="localhost") as dev:
    init_hostname = dev.facts["hostname"]
    init_device_type = dev.facts["model"]
    init_serial_number = dev.facts["serialnumber"]
    print(init_hostname)
    print(init_device_type)
    print(init_serial_number)

    inti_ip = dev.rpc.get_interface_information(interface_name="ge-0/0/0.0", terse=True, normalize=True)
    ip = inti_ip.xpath(
        './/address-family[address-family-name="inet"]/ \
      interface-address/ifa-local'
    )[0].text
    ip.split("/")
    host_ip = ip.split("/")[0]
    print(host_ip)

headers = {
    "Content-Type": "application/json",
    "Authorization": "Token bf8261deae36fc89952f6eb3e5e2398a1360f1e7",
    "Cookie": "csrftoken=dkjQd8zfqO3x5WWN956IlEgQHaZ5AAJwwRV9D9hbMzCsVECiLUtLH1qYWlZ4ft5u",
}

url = "https://192.168.0.251/api/dcim/device-types"

payload = json.dumps(
    {
        "manufacturer": {"name": "Juniper Networks", "slug": "juniper-networks"},
        "model": init_device_type,
        "slug": init_device_type,
    }
)

# Add a new device to CDB
response = requests.request("POST", url, headers=headers, data=payload, verify=False, timeout=10)
print(response.text)

payload1 = json.dumps(
    {
        "name": init_hostname,
        "device_type": {"model": init_device_type},
        "device_role": {"name": "ToBeDefined"},
        "site": {"name": "JeyHome2"},
        "serial": init_serial_number,
        "custom_fields": {
            "dns_server_pri": "192.168.0.252",
            "dns_server_sec": "192.168.0.250",
            "domain_name": "int.jeyriku.net",
            "dev_lpbk": "null",
            "snmp_community": "jeyricorp",
            "snmp_location": "int.jeyriku.net, 2 allee des campagnols, Seynod, 74600, Annecy, France [45.85995, 6.0793]",
            "snmp_server1": "192.168.0.248",
            "snmp_server2": "192.168.0.249",
        },
    }
)

url1 = "https://192.168.0.251/api/dcim/devices/"

# Add a new device to CDB
response1 = requests.request("POST", url1, headers=headers, data=payload1, verify=False, timeout=10)
print(response1.text)

And the result in Netbox:

Jerems_0-1713526911571.png

Hope that both simple script will help Cisco and Juniper community

Regards,

Jerems

2 Accepted Solutions

Accepted Solutions

Jerems
Spotlight
Spotlight

Would certainly add this to DevNet Code exchange, as this is a great use case!

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

View solution in original post

6 Replies 6

Jerems
Spotlight
Spotlight

Thanks @bigevilbeard !

Would certainly add this to DevNet Code exchange, as this is a great use case!

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

michaelcoffey
Level 1
Level 1

Hi Jerems - I wonder if my question is relevant on this post.  I have been looking for a way to use ZTP to add my device(s) (CAT9300) to Gluware.  I would like to do this without touching the device and simply use the ZTP process to put a base config along with an api put to the server.  I can do everything but without the "requests" module I cannot find a way to make this happen.  I noticed in your configuration that you have imported requests but you also indicate that this is a python script that runs automatically within Guestshell.  (Also saw another post where you seem to have had the same issue of not being able to upgrade with PIP from within the python script.)  Can you tell me if you have solved this issue?

NOTE: I am experimenting in CML using a CSR1000v

Here is what I have tried and the error:

michaelcoffey_0-1706669509571.png
The console after guestshell launches:
michaelcoffey_1-1706669814169.png

I have followed all of the troubleshooting I can find, including updating my dns server to point to google.  Additionally, when I activate guestshell through the command line I can install requests and import it without any issue.

Any insight you can offer would be helpful!

Thanks!

 

Hi @michaelcoffey,

I would have liked to know when the python librairy "requests" was integrated within the list of builtin librairies in the guestshell container. By the way, the script below worked with ISR4331 and ISR1111-8P and ISR1111-4P.

If you are missing the request librairy you can still use the other one "http.client" which is present in the early version of Guesshell.

 

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
########################################################################################################################
# This file is a part of Jeyriku.net.
#
# Created: 05.05.2023 11:52:05
# Author: Jeremie Rouzet
#
# Last Modified: 01.02.2024 09:07:38
# Modified By: Jeremie Rouzet
#
# Copyright (c) 2023 Jeyriku.net
########################################################################################################################
import time
import cli
import http.client
import json
import ssl

# Check if Pynetbox library is installed

split_ip = cli.execute("sh ip int brief | in 192.168.100.")
temp_ip = split_ip.split()[1]
print(temp_ip)
temp_ip.split(".")
last_digit_ip = temp_ip.split(".")[3]
print(last_digit_ip)

print("\n\n *** Configuring Hostname *** \n\n")
config_hostname = cli.configure(["hostname jeyrouter" + "_" + last_digit_ip, "end"])
print(config_hostname)

print("\n\n *** Configuring Domain name + name server *** \n\n")
cli.configure(["ip domain name int.jeyriku.net", "ip name-server 192.168.0.252 192.168.0.250", "end"])

print("\n\n *** Configuring Username *** \n\n")
cli.configure(
    ["username ****** privilege 15 secret 0 MySecretPassword", "end"]
)

print("\n\n *** Configuring Line VTY *** \n\n")
cli.configure(
    ["line vty 0 15", "exec-timeout 0 0", "login local", "transport input telnet ssh", "escape-character 17", "end"]
)

print("\n\n *** Configuring Iox *** \n\n")
cli.configure(["iox", "end"])

time.sleep(30)

print("\n\n *** Configuring Interface for App-Hosting *** \n\n")
cli.configure(
    [
        "interface VirtualPortGroup0",
        "ip address 192.168.1.1 255.255.255.0",
        "ip nat inside",
        # "no mop enabled",
        # "no mop sysid",
        "end",
    ]
)

print("\n\n *** Configuring Interface for App-Hosting *** \n\n")
cli.configure(["interface GigabitEthernet0/0/0", "ip address dhcp", "ip nat outside", "no shut", "end"])

print("\n\n *** Configuring ACL for App-Hosting *** \n\n")
cli.configure(["ip access-list extended GUESTSHELL-NAT-ACL", "10 permit ip 192.168.1.0 0.0.0.255 any", "end"])

print("\n\n *** Configuring NAT for App-Hosting *** \n\n")
cli.configure(["ip nat inside source list GUESTSHELL-NAT-ACL interface GigabitEthernet0/0/0 overload", "end"])


print("\n\n *** Configuring App-Hosting *** \n\n")
cli.configure(
    [
        "app-hosting appid guestshell",
        "app-vnic gateway0 virtualportgroup 0 guest-interface 0",
        "guest-ipaddress 192.168.1.2 netmask 255.255.255.0",
        "app-default-gateway 192.168.1.1 guest-interface 0",
        "name-server0 192.168.0.252",
        "name-server1 192.168.0.250",
        "end",
    ]
)

time.sleep(30)

print("\n\n *** Launch Guestshell *** \n\n")
output = cli.execute("guestshell enable")
print(output)

time.sleep(60)

find_hostname = cli.execute("sh run | i hostname")
print(find_hostname)
init_hostname = find_hostname.split()[1]
print("The device name is: " + init_hostname)
find_ip = cli.execute("sh ip int brief | in 192.168.100.")
init_ip = find_ip.split()[1]
print("The device primary ip is: " + init_ip)

find_device_type = cli.execute("sh platform | in type")
print(find_device_type)
init_device_type = find_device_type.split()[2]
print("The device type is: " + init_device_type)


conn = http.client.HTTPSConnection("192.168.0.251", context=ssl._create_unverified_context())
payload = json.dumps(
    {
        "name": init_hostname,
        "device_type": {"model": init_device_type},
        "device_role": {"name": "Router_CE_Cisco"},
        "site": {"name": "JeyHome2"},
        "custom_fields": {
            "dns_server_pri": "192.168.0.252",
            "dns_server_sec": "192.168.0.250",
            "domain_name": "int.jeyriku.net",
            "dev_lpbk": "null",
            "snmp_community": "jeyricorp",
            "snmp_location": "int.jeyriku.net, 2 allee des campagnols, Seynod, 74600, Annecy, France [45.85995, 6.0793]",
            "snmp_server1": "192.168.0.248",
            "snmp_server2": "192.168.0.249",
        },
    }
)
headers = {
    "Content-Type": "application/json",
    "Authorization": "Token bf8261deae36fc89952f6eb3e5e2398a1360f1e7",
    "Cookie": "csrftoken=dkjQd8zfqO3x5WWN956IlEgQHaZ5AAJwwRV9D9hbMzCsVECiLUtLH1qYWlZ4ft5u",
}
conn.request("POST", "/api/dcim/devices/", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

print("\n\n *** Execute Guestshell command - create persistent guestshell *** \n\n")
output1 = cli.execute("guestshell create image persistflash:/guestshell.pie")
print(output1)

print("\n\n *** Execute Guestshell command - execute persistent guestshell *** \n\n")
output2 = cli.execute("guestshell run image persistflash:/guestshell.pie")
print(output2)

print("\n\n *** Execute Guestshell command - install requests *** \n\n")
output3 = cli.execute("guestshell run sudo python3 -m pip install requests")
print(output3)

time.sleep(30)

print("\n\n *** Execute Guestshell command - install pynetbox *** \n\n")
output4 = cli.execute("guestshell run sudo python3 -m pip install pynetbox")
print(output4)

print("\n\n *** launch automatically Guestshell at bootup *** \n\n")
cli.configure(["guestshell enable", "end"])

print("\n\n *** Execute Guestshell command - execute dohost command *** \n\n")
output5 = cli.execute(["guestshell run bash dohost sh ip int brief"])
print(output5)

You may find errors in this script but it should not be a blocker at all. You can adapt it to your environment for sure.

Try it and let me know !

Regards,

Jerems

thanks a lot for sharing

Your Welcome !