Skip to content

Instantly share code, notes, and snippets.

@afaqk9394
Created November 3, 2019 07:05
Show Gist options
  • Select an option

  • Save afaqk9394/f593d6c6d09dbd07d6d43d7a02c53269 to your computer and use it in GitHub Desktop.

Select an option

Save afaqk9394/f593d6c6d09dbd07d6d43d7a02c53269 to your computer and use it in GitHub Desktop.
display router NETCONF capabilities
from ncclient import manager
HOST = 'ios-xe-mgmt-latest.cisco.com'
PORT = '10000'
USER = 'developer'
PASS = 'C1sco12345'
with manager.connect(host=HOST, port=PORT, username=USER,
password=PASS, hostkey_verify=False,
device_params={'name': 'default'},
look_for_keys=False, allow_agent=False) as m:
# print all NETCONF capabilities
print('***Full List of Capabilities***')
for capability in m.server_capabilities:
print(capability.split('?')[0])
Code Output (truncated)
Now, we try to download the running configuration using NETCONF API call. While the output is natively in XML form, but we convert it to JSON and display it too.
Python Code (XML)
import xmltodict
from ncclient import manager
import json
import xml.dom.minidom
HOST = 'ios-xe-mgmt-latest.cisco.com'
PORT = '10000'
USER = 'developer'
PASS = 'C1sco12345'
m = manager.connect(host=HOST, port=PORT, username=USER,
password=PASS, hostkey_verify=False,
device_params={'name': 'default'},
look_for_keys=False, allow_agent=False)
running_config_xml = m.get_config('running')
running_config_json = xmltodict.parse(str(running_config_xml))
#printing running-config in XML
print(xml.dom.minidom.parseString(str(running_config_xml)).toprettyxml())
Code Output (truncated)
Learn, Build, Fork, and Share with Our Instant IDE.
Hit the Green Play Button to Execute.
Python Code (JSON)
import xmltodict
from ncclient import manager
import json
HOST = 'ios-xe-mgmt-latest.cisco.com'
PORT = '10000'
USER = 'developer'
PASS = 'C1sco12345'
m = manager.connect(host=HOST, port=PORT, username=USER,
password=PASS, hostkey_verify=False,
device_params={'name': 'default'},
look_for_keys=False, allow_agent=False)
running_config_xml = m.get_config('running')
running_config_json = xmltodict.parse(str(running_config_xml))
#printing running-config in JSON
print(json.dumps(running_config_json, indent=4, sort_keys=True))
Code Output (Truncated)
Learn, Build, Fork, and Share with Our Instant IDE.
Hit the Green Play Button to Execute.
Before, we move on from NETCONF examples, let us go over the device handlers that ncclient supports. If you noticed, in all our code, we used ‘default’ device handler which is the same as “csr” for Cisco CSR1000v device.
Juniper: device_params={‘name’:’junos’}
Cisco CSR: device_params={‘name’:’csr’}
Cisco Nexus: device_params={‘name’:’nexus’}
Huawei: device_params={‘name’:’huawei’}
Alcatel Lucent: device_params={‘name’:’alu’}
H3C: device_params={‘name’:’h3c’}
HP Comware: device_params={‘name’:’hpcomware’}
Let me note down some code choices that we have made when using ncclient and why.
Manager object is used to lookup operations, e.g. get_config etc.
Mostly, we’re using the following template to connect into the device via NETCONF
m = manager.connect(
host=env_lab.IOS_XE_1["host"],
port=env_lab.IOS_XE_1["netconf_port"],
username=env_lab.IOS_XE_1["username"],
password=env_lab.IOS_XE_1["password"],
hostkey_verify=False
)
close_session() method is used to close the NETCONF session, i.e. m.close_session(). However, when you use “with manage.connect <> as blah:”, it automatically closes the connection once the transaction is done.
xmltodict comes handy to parse XML to dictionary and thus making it compatible with JSON encoding for further processing. Remember, JSON is a serialization construct thus making it easier to sort and transmit between systems, whereas dictionary is a data structure to use inside your code. However, they both represent objects as name/value pairs.
We’ve already discussed xml.dom.minidom library, which is one of the two popular methods to process XML format.
XML to dictionary can be done with a single line of code. “xml_data” below represents raw xml data.
Netconf_info = xmltodict.parse(str(xml_data))[“rpc-reply”][“data”]
You can also add or delete IOS XE configuration using Manager object, however in both cases, you need to first construct an XML configuration template.
YANG and RESTCONF Example
RESTCONF uses REST APIs so it uses HTTPS port 443 for transport. In order to configure RESTCONF, besides priv 15 user, you also need to enable HTTPS server and restconf.
Enabling HTTPS server and RESTCONF
In order to connect and execute our RESTCONF API calls, we can various python libraries.
netmiko
requests (REST get/post call etc.)
json (encoding support)
We will simply use requests and json libraries in our example below.
Python Code
import json
import requests
import sys
from argparse import ArgumentParser
from collections import OrderedDict
import urllib3
# Disable SSL Warnings, optional.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# These variables target the RESTCONF Always-On Sandbox hosted by Cisco DevNet
HOST = 'ios-xe-mgmt-latest.cisco.com'
PORT = '9443'
USER = 'developer'
PASS = 'C1sco12345'
# Identifies the interface on the device used for management access
# Used to ensure the script isn't used to update the IP leveraged to manage device
MANAGEMENT_INTERFACE = "GigabitEthernet1"
# Create the base URL for RESTCONF calls
url_base = "https://{h}:{p}/restconf".format(h=HOST, p=PORT)
# Identify yang+json as the data formats
headers = {'Content-Type': 'application/yang-data+json',
'Accept': 'application/yang-data+json'}
#Define our 5 functions
# Function to retrieve the list of interfaces on a device
def get_configured_interfaces():
url = url_base + "/data/ietf-interfaces:interfaces"
# this statement performs a GET on the specified url
response = requests.get(url,
auth=(USER, PASS),
headers=headers,
verify=False
)
# return the json as text
return response.json()["ietf-interfaces:interfaces"]["interface"]
# Used to configure the IP address on an interface
def configure_ip_address(interface, ip):
# RESTCONF URL for specific interface
url = url_base + "/data/ietf-interfaces:interfaces/interface={i}".format(i=interface)
# Create the data payload to reconfigure IP address
# Need to use OrderedDicts to maintain the order of elements
data = OrderedDict([('ietf-interfaces:interface',
OrderedDict([
('name', interface),
('type', 'iana-if-type:ethernetCsmacd'),
('ietf-ip:ipv4',
OrderedDict([
('address', [OrderedDict([
('ip', ip["address"]),
('netmask', ip["mask"])
])]
)
])
),
])
)])
# Use PUT request to update data
response = requests.put(url,
auth=(USER, PASS),
headers=headers,
verify=False,
json=data
)
print(response.text)
# Retrieve and print the current configuration of an interface
def print_interface_details(interface):
url = url_base + "/data/ietf-interfaces:interfaces/interface={i}".format(i=interface)
# this statement performs a GET on the specified url
response = requests.get(url,
auth=(USER, PASS),
headers=headers,
verify=False
)
intf = response.json()["ietf-interfaces:interface"]
# return the json as text
print("Name: ", intf["name"])
try:
print("IP Address: ", intf["ietf-ip:ipv4"]["address"][0]["ip"], "/",
intf["ietf-ip:ipv4"]["address"][0]["netmask"])
except KeyError:
print("IP Address: UNCONFIGURED")
print()
return(intf)
# Ask the user to select an interface to configure. Ensures input is valid
def interface_selection(interfaces):
# Ask User which interface to configure
sel = input("Which Interface do you want to configure? ")
# Validate interface input
# Must be an interface on the device AND NOT be the Management Interface
while sel == MANAGEMENT_INTERFACE or not sel in [intf["name"] for intf in interfaces]:
print("INVALID: Select an available interface.")
print(" " + MANAGEMENT_INTERFACE + " is used for management.")
print(" Choose another Interface")
sel = input("Which Interface do you want to configure? ")
return(sel)
# Asks the user to provide an IP address and Mask. Data is NOT validated.
def get_ip_info():
# Ask User for IP and Mask
ip = {}
ip["address"] = input("What IP address do you want to set? ")
ip["mask"] = input("What Subnet Mask do you want to set? ")
return(ip)
#try out functions with user input
# Get a List of Interfaces
interfaces = get_configured_interfaces()
print("The router has the following interfaces: \n")
for interface in interfaces:
print(" * {name:25}".format(name=interface["name"]))
print("")
# Ask User which interface to configure
selected_interface = interface_selection(interfaces)
print(selected_interface)
# Print Starting Interface Details
print("Starting Interface Configuration")
print_interface_details(selected_interface)
# As User for IP Address to set
ip = get_ip_info()
# Configure interface
configure_ip_address(selected_interface, ip)
# Print Ending Interface Details
print("Ending Interface Configuration")
print_interface_details(selected_interface)
# Get a List of Interfaces
interfaces = get_configured_interfaces()
print("The router has the following interfaces: \n")
for interface in interfaces:
print(" * {name:25}".format(name=interface["name"]))
print("")
# Ask User which interface to configure
selected_interface = interface_selection(interfaces)
print(selected_interface)
# Print Starting Interface Details
print("Starting Interface Configuration")
print_interface_details(selected_interface)
# As User for IP Address to set
ip = get_ip_info()
# Configure interface
configure_ip_address(selected_interface, ip)
# Print Ending Interface Details
print("Ending Interface Configuration")
print_interface_details(selected_interface)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment