Parsing (IOS)¶
One of the features ntc_rosetta supports is parsing native configuration and turning into data modelled after YANG models. For that purpose ntc_rosetta leverages yangify and builds on top of it to make it more consumable.
ntc_rosetta introduces the concept of “drivers”. Drivers are objects that implements the parsing and translation of a given YANG model for a particular NOS. For instance, if you wanted to parse IOS configuration and convert it into data that follows the openconfig model you would load the corresponding driver like this:
[1]:
from ntc_rosetta import get_driver
ios = get_driver("ios", "openconfig")
ios_driver = ios()
The same processor can also translate the given model to native configuration.
Now, let’s see how we can use this driver to parse IOS configuration and turn it into an Openconfig model. First, let’s load some IOS configuration:
[2]:
with open("data/ios/config.txt", "r") as f:
config = f.read()
[3]:
print(config)
interface FastEthernet1
description This is Fa1
shutdown
exit
!
interface FastEthernet1.1
description This is Fa1.1
exit
!
interface FastEthernet1.2
description This is Fa1.2
exit
!
interface FastEthernet3
description This is Fa3
no shutdown
switchport mode access
switchport access vlan 10
exit
!
interface FastEthernet4
shutdown
switchport mode trunk
switchport trunk allowed vlan 10,20
exit
!
vlan 10
name prod
no shutdown
exit
!
vlan 20
name dev
shutdown
exit
!
Once the configuration is loaded, you need to parse it. The parser has some conventions you have to be aware of, for instance, when parsing configuration, it’s going to expect you pass a native
argument with a dictionary where the key dev_conf
is the native configuration:
[4]:
parsed = ios_driver.parse(native={"dev_conf": config})
That’s literally all you have to do parse the native configuration and turn it into structured data. We can check the result by dumping the parsed.raw_value()
:
[5]:
import json
print(json.dumps(parsed.raw_value(), indent=4))
{
"openconfig-interfaces:interfaces": {
"interface": [
{
"name": "FastEthernet1",
"config": {
"name": "FastEthernet1",
"type": "iana-if-type:ethernetCsmacd",
"description": "This is Fa1",
"enabled": false
},
"subinterfaces": {
"subinterface": [
{
"index": 1,
"config": {
"index": 1,
"description": "This is Fa1.1"
}
},
{
"index": 2,
"config": {
"index": 2,
"description": "This is Fa1.2"
}
}
]
}
},
{
"name": "FastEthernet3",
"config": {
"name": "FastEthernet3",
"type": "iana-if-type:ethernetCsmacd",
"description": "This is Fa3",
"enabled": true
},
"openconfig-if-ethernet:ethernet": {
"openconfig-vlan:switched-vlan": {
"config": {
"interface-mode": "ACCESS",
"access-vlan": 10
}
}
}
},
{
"name": "FastEthernet4",
"config": {
"name": "FastEthernet4",
"type": "iana-if-type:ethernetCsmacd",
"enabled": false
},
"openconfig-if-ethernet:ethernet": {
"openconfig-vlan:switched-vlan": {
"config": {
"interface-mode": "TRUNK",
"trunk-vlans": [
10,
20
]
}
}
}
}
]
},
"openconfig-network-instance:network-instances": {
"network-instance": [
{
"name": "default",
"config": {
"name": "default"
},
"vlans": {
"vlan": [
{
"vlan-id": 10,
"config": {
"vlan-id": 10,
"name": "prod",
"status": "ACTIVE"
}
},
{
"vlan-id": 20,
"config": {
"vlan-id": 20,
"name": "dev",
"status": "SUSPENDED"
}
}
]
}
}
]
}
}
ntc_rosetta, also let’s you parse some parts of the model, however, you need to be aware that might break the validation of the object:
[6]:
from yangson.exceptions import SemanticError
try:
parsed_vlans = ios_driver.parse(
native={"dev_conf": config},
include=[
"/openconfig-network-instance:network-instances/network-instance/vlans",
]
)
except SemanticError as e:
print(f"error: {e}")
error: [/openconfig-network-instance:network-instances/network-instance/0/name] instance-required
You can workaround this in two ways: 1. By disabling the validation of the object 2. By parsing all the necessary elements to make the object compliant.
You can disable the validation of the object by passing validate=False
:
[7]:
parsed_vlans = ios_driver.parse(
native={"dev_conf": config},
validate=False,
include=[
"/openconfig-network-instance:network-instances/network-instance/vlans",
]
)
print(json.dumps(parsed_vlans.raw_value(), indent=4))
{
"openconfig-network-instance:network-instances": {
"network-instance": [
{
"name": "default",
"vlans": {
"vlan": [
{
"vlan-id": 10,
"config": {
"vlan-id": 10,
"name": "prod",
"status": "ACTIVE"
}
},
{
"vlan-id": 20,
"config": {
"vlan-id": 20,
"name": "dev",
"status": "SUSPENDED"
}
}
]
}
}
]
}
}
And you can make sure your object is valid by passing the list of elements that are needed to make the object compliant:
[8]:
parsed_vlans = ios_driver.parse(
native={"dev_conf": config},
include=[
"/openconfig-network-instance:network-instances/network-instance/name",
"/openconfig-network-instance:network-instances/network-instance/config",
"/openconfig-network-instance:network-instances/network-instance/vlans",
]
)
print(json.dumps(parsed_vlans.raw_value(), indent=4))
{
"openconfig-network-instance:network-instances": {
"network-instance": [
{
"name": "default",
"config": {
"name": "default"
},
"vlans": {
"vlan": [
{
"vlan-id": 10,
"config": {
"vlan-id": 10,
"name": "prod",
"status": "ACTIVE"
}
},
{
"vlan-id": 20,
"config": {
"vlan-id": 20,
"name": "dev",
"status": "SUSPENDED"
}
}
]
}
}
]
}
}