Parsing (JUNOS)

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

junos = get_driver("junos", "openconfig")
junos_driver = junos()

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:

[3]:
with open("data/junos/dev_conf.xml", "r") as f:
    config = f.read()
[4]:
print(config)
<configuration>
        <interfaces>
            <interface>
                <name>xe-0/0/1</name>
                <unit>
                    <name>0</name>
                    <family>
                        <ethernet-switching>
                            <interface-mode>access</interface-mode>
                            <vlan>
                                <members>10</members>
                            </vlan>
                        </ethernet-switching>
                    </family>
                </unit>
            </interface>
            <interface>
                <name>xe-0/0/3</name>
                <unit>
                    <name>0</name>
                    <family>
                        <ethernet-switching>
                            <interface-mode>trunk</interface-mode>
                            <vlan>
                                <members>10</members>
                                <members>20</members>
                            </vlan>
                        </ethernet-switching>
                    </family>
                </unit>
            </interface>
            <interface>
                <name>xe-0/0/4</name>
                <unit>
                    <name>0</name>
                    <family>
                        <ethernet-switching>
                            <interface-mode>trunk</interface-mode>
                            <vlan>
                                <members>VLAN-100</members>
                                <members>VLAN-200</members>
                            </vlan>
                        </ethernet-switching>
                    </family>
                </unit>
            </interface>
            <interface>
                <name>xe-0/0/5</name>
                <unit>
                    <name>0</name>
                    <family>
                        <ethernet-switching>
                            <interface-mode>access</interface-mode>
                            <vlan>
                                <members>VLAN-100</members>
                            </vlan>
                        </ethernet-switching>
                    </family>
                </unit>
            </interface>
        </interfaces>
    <vlans>
            <vlan>
                <name>default</name>
                <vlan-id>1</vlan-id>
            </vlan>
            <vlan>
                <name>prod</name>
                <vlan-id>20</vlan-id>
            </vlan>
            <vlan inactive="inactive">
                <vlan-id>10</vlan-id>
            </vlan>
            <vlan>
                <name>VLAN-100</name>
                <vlan-id>100</vlan-id>
            </vlan>
            <vlan>
                <name>VLAN-200</name>
                <vlan-id>200</vlan-id>
            </vlan>
    </vlans>
</configuration>

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:

[7]:
parsed = junos_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():

[9]:
import json
print(json.dumps(parsed.raw_value(), indent=4))
{
    "openconfig-interfaces:interfaces": {
        "interface": [
            {
                "name": "xe-0/0/1",
                "config": {
                    "name": "xe-0/0/1",
                    "type": "iana-if-type:ethernetCsmacd",
                    "enabled": true
                },
                "subinterfaces": {
                    "subinterface": [
                        {
                            "index": 0,
                            "config": {
                                "index": 0
                            }
                        }
                    ]
                },
                "openconfig-if-ethernet:ethernet": {
                    "openconfig-vlan:switched-vlan": {
                        "config": {
                            "interface-mode": "ACCESS",
                            "access-vlan": 10
                        }
                    }
                }
            },
            {
                "name": "xe-0/0/3",
                "config": {
                    "name": "xe-0/0/3",
                    "type": "iana-if-type:ethernetCsmacd",
                    "enabled": true
                },
                "subinterfaces": {
                    "subinterface": [
                        {
                            "index": 0,
                            "config": {
                                "index": 0
                            }
                        }
                    ]
                },
                "openconfig-if-ethernet:ethernet": {
                    "openconfig-vlan:switched-vlan": {
                        "config": {
                            "interface-mode": "TRUNK",
                            "trunk-vlans": [
                                10,
                                20
                            ]
                        }
                    }
                }
            },
            {
                "name": "xe-0/0/4",
                "config": {
                    "name": "xe-0/0/4",
                    "type": "iana-if-type:ethernetCsmacd",
                    "enabled": true
                },
                "subinterfaces": {
                    "subinterface": [
                        {
                            "index": 0,
                            "config": {
                                "index": 0
                            }
                        }
                    ]
                },
                "openconfig-if-ethernet:ethernet": {
                    "openconfig-vlan:switched-vlan": {
                        "config": {
                            "interface-mode": "TRUNK",
                            "trunk-vlans": [
                                100,
                                200
                            ]
                        }
                    }
                }
            },
            {
                "name": "xe-0/0/5",
                "config": {
                    "name": "xe-0/0/5",
                    "type": "iana-if-type:ethernetCsmacd",
                    "enabled": true
                },
                "subinterfaces": {
                    "subinterface": [
                        {
                            "index": 0,
                            "config": {
                                "index": 0
                            }
                        }
                    ]
                },
                "openconfig-if-ethernet:ethernet": {
                    "openconfig-vlan:switched-vlan": {
                        "config": {
                            "interface-mode": "ACCESS",
                            "access-vlan": 100
                        }
                    }
                }
            }
        ]
    },
    "openconfig-network-instance:network-instances": {
        "network-instance": [
            {
                "name": "default",
                "config": {
                    "name": "default"
                },
                "vlans": {
                    "vlan": [
                        {
                            "vlan-id": 1,
                            "config": {
                                "vlan-id": 1,
                                "name": "default",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 20,
                            "config": {
                                "vlan-id": 20,
                                "name": "prod",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 10,
                            "config": {
                                "vlan-id": 10,
                                "status": "SUSPENDED"
                            }
                        },
                        {
                            "vlan-id": 100,
                            "config": {
                                "vlan-id": 100,
                                "name": "VLAN-100",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 200,
                            "config": {
                                "vlan-id": 200,
                                "name": "VLAN-200",
                                "status": "ACTIVE"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

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:

[11]:
from yangson.exceptions import SemanticError
try:
    parsed_vlans = junos_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:

[13]:
parsed_vlans = junos_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": 1,
                            "config": {
                                "vlan-id": 1,
                                "name": "default",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 20,
                            "config": {
                                "vlan-id": 20,
                                "name": "prod",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 10,
                            "config": {
                                "vlan-id": 10,
                                "status": "SUSPENDED"
                            }
                        },
                        {
                            "vlan-id": 100,
                            "config": {
                                "vlan-id": 100,
                                "name": "VLAN-100",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 200,
                            "config": {
                                "vlan-id": 200,
                                "name": "VLAN-200",
                                "status": "ACTIVE"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

And you can make sure your object is valid by passing the list of elements that are needed to make the object compliant:

[15]:
parsed_vlans = junos_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": 1,
                            "config": {
                                "vlan-id": 1,
                                "name": "default",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 20,
                            "config": {
                                "vlan-id": 20,
                                "name": "prod",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 10,
                            "config": {
                                "vlan-id": 10,
                                "status": "SUSPENDED"
                            }
                        },
                        {
                            "vlan-id": 100,
                            "config": {
                                "vlan-id": 100,
                                "name": "VLAN-100",
                                "status": "ACTIVE"
                            }
                        },
                        {
                            "vlan-id": 200,
                            "config": {
                                "vlan-id": 200,
                                "name": "VLAN-200",
                                "status": "ACTIVE"
                            }
                        }
                    ]
                }
            }
        ]
    }
}
[ ]: