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"
                            }
                        }
                    ]
                }
            }
        ]
    }
}