- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-20-2024 02:20 AM - edited 04-19-2024 04:43 AM
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
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:
Hope that both simple script will help Cisco and Juniper community
Regards,
Jerems
Solved! Go to Solution.
- Labels:
-
Python Network Automation
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-20-2024 02:33 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-20-2024 11:11 AM
Would certainly add this to DevNet Code exchange, as this is a great use case!
Connect with me https://bigevilbeard.github.io
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-20-2024 02:33 AM
Thanks @bigevilbeard !
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-20-2024 11:11 AM
Would certainly add this to DevNet Code exchange, as this is a great use case!
Connect with me https://bigevilbeard.github.io

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-30-2024 06:59 PM
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:
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!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-01-2024 12:16 AM
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-31-2024 10:56 PM
thanks a lot for sharing
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-01-2024 12:47 AM
