A Modern NetDevOps learning plan

Now that I have a load of free time I have set up a sort of a learning plan to plug some gaps in my knowledge that I always wanted to plug, but didn’t have the time to do properly.

Mindful that I have an opportunity for learning without external influences on my time, but also that my Daughter only has Kindergarten until lunchtime every day, I really only get the morning before the family time kicks in.

[Read more]

End of an Era: The Solera Years

Two weeks ago I was made redundant. COVID and “shifting business priorities” meant a re-org of my department and for whatever reason, that is that. Over the last year or so I had assumed this day might come and the question I had internally was if I would jump or if I would be pushed. Since I was slap bang in the middle of a key project, I had assumed I might still have 9-12 months or so left to close that out. Turns out I was wrong. Thankfully my documentation is average enough for them to cope without me.

[Read more]

Doing YANG Wrong: Part 5 - Manufacturer/Model deviations

Part 5: Deviations

So we can talk to the device, and we can use candidate configs to stage and then apply configs in aggregate, but we still can’t make a CSR1000v take a simple openconfig IP address. At the beginning I deliberately called out I wanted to use generic models, and avoid the deviations. This is because the python model binding i then generate only works on that vendor’s box now. This isn’t terrible, but it’s not really what we want. Lets see if it works tho.

[Read more]

Doing YANG Wrong: Part 4 - Config stores

Part 4: Config stores

As we head down this rabbit hole, we start to get ever closer to something useful, but ever more deep into the weeds of NETCONF/YANG. For the uninitiated, config stores are a place where you can put config chunks either singularly, or in aggregate over a series of netconf pushes, to generate a new config that you will apply in one hit. In the SP world you might want to setup some interfaces, some BGP and then some overlay like an MPLS service. You may not want to do all this in one script since you might not need all of those things in one script. better to make a script per thing, and then call the ones your CMDB thinks it needs, place them all in a candidate config, validate that as a whole and then push that in one go. If anything fails, you can then throw it all away and not touch the operational running config. At this point juniper folks are shrugging their shoulders - this is very old hat to them, but for enterprise people, particularly the type who lived on the CLI for years, this is unimaginable.

[Read more]

Doing YANG Wrong: Part 3 - Using the python bindings

Part 3: Using the python bindings to push a config

Given we generated that python file locally in a machine, we assume here that you are still in that subdirectory.

The below code was stolen fully from the YANG Book. Much love and all credit to them for their work.

from interface_setup import openconfig_interfaces
from pyangbind.lib.serialise import pybindIETFXMLEncoder
from ncclient import manager

# device settings
username = 'yangconf'
password = 'my_good_password.'
device_ip = '192.168.70.21'

# config settings 
inside_interface = 'GigabitEthernet4'
inside_ip_addr = '109.109.109.2'
inside_ip_prefix = 24


def send_to_device(**kwargs):
    rpc_body = '<config>' + pybindIETFXMLEncoder.serialise(kwargs['py_obj']) + '</config>'
    with manager.connect_ssh(host-kwargs['dev'], port=830, username=kwargs['user'], password=kwargs['password'], hostkey_verify=False) as m:
        try:
            m.edit_config(target='running', config=rpc_body)
            print('Successfully configured IP on {}'.format(kwargs['dev']))
        except Exception as e:
            print('Failed to configure interface: {}'.format(e))


if __name__ == '__main__':
    # instanciate the openconfig model
    ocintmodel = openconfig_interfaces()

    # create an instance of the interfaces
    ocinterfaces = ocintmodel.interfaces

    # create a new interface instance in that parent object
    inside_if = ocinterfaces.interface.add(inside_interface)

    # even a routed interface required a subinterface, its just at index 0
    inside_if.subinterfaces.subinterface.add(0)

    # create an instance of that subinterface object to edit
    inside_sub_if = inside_if.subinterfaces.subinterface[0]

    # apply an IP to that object
    inside_sub_if.ipv4.addresses.address.add(inside_ip_addr)
    
    # read that ip object into an ip object
    ip = inside_sub_if.ipv4.addresses.address[inside_ip_addr]

    # set the IP and the subnet mask properly

    ip.config.ip = inside_ip_addr
    ip.config.prefix_length = inside_ip_prefix
    send_to_device(dev=device_ip, user=username, password=password, py_obj=ocinterfaces)

When I run this, it fails.

[Read more]