Package: umnet-ncs-instances
Problem StatementUMnet's goal is to represent our entire network configuration in NSO.
As of Nov 2021, our current network configuration lives in rancid backups,
TopologyWalker files, Netinfo, Equipdb, and a few other places.
We need some way to get this data from all these sources and use it to
populate NSO.After NSO is populated, we will still need ways to create data objects in python
that are structured appropriately for NSO to ingest (for example, in Netsplash).Solution DetailsThis package contains a set of classes that represent NSO objects designed by UMnet
(services, actions, etc). When you import these classes into your python code, you
can work with them similar to how you can work with NSO's high level python API.
When you're done you can generate XML using the gen_xml() method.types.pyhas the basic classes that emulate NSO'smaagic python api.instances.pyhas instance classes that represent specific UMnet NCS instances.When anNCSInstanceobject is initialized in code, its structure is set via the
initializer.__setattr__has been overloaded for all of the object types such
that you can't change the structure of the object or the types post-initialization.
What you can do is:Set aLeafvalue (value must match the type specificed during initialization)Append a value to aLeafList(same type restriction applies)Set the choice for aChoiceobject (via the "choose" method)Add an entry to aListas you would to a dict.You can also read these values after setting them in your code for validation purposes. Just
like with the maagic API you can read aLeafdirectly. ALeafListbehaves like a python list,
and aListbehaves like a dict.An ExampleHere is how a device instance is represented ininstances.py.class Device(NCSInstance):

    _path = "/config/devices/device"

    _nsmap = {
            "config":"http://tail-f.com/ns/config/1.0",
            "devices":"http://tail-f.com/ns/ncs",
            }

    _xml_munge = {
        r'<ned-id>(.+)</ned-id>':'<ned-id xmlns:\g<1>="http://tail-f.com/ns/ned-id/\g<1>">\g<1></ned-id>'
    }

    def __init__(self):

        self.name = Leaf(str)
        self.address = Leaf(str)
        self.authgroup = Leaf(str, value="default")
        self.state = Container()
        self.state.admin_state = Leaf(str, value="unlocked")
        self.device_type = Choice(['cli','netconf'])
        self.device_type.cli = Container()
        self.device_type.netconf = Container()
        self.device_type.cli.ned_id = Leaf(str)
        self.device_type.netconf.ned_id = Leaf(str)
        self.in_band_mgmt = Container()
        self.in_band_mgmt.set_ns("http://umnet.umich.edu/umnetcommon")
        self.in_band_mgmt.ip_address = Leaf(str)
        self.in_band_mgmt.interface = Leaf(str)Here is some basic python code that creates a new device.from umnet_ncs_instances import Device

device = Device()
device.name = "d-ARBL-1"
device.addresss = "198.108.10.4"
device.in_band_mgmt.ip_address = "198.108.10.4"
device.in_band_mgmt.interface = "Loopback0"
device.device_type.choose("cli")
device.device_type.cli.ned_id = "cisco-ios-cli-6.73"
print(device.gen_xml())When you run the code above, here is what you'll get:(venv) bash-3.2$ python test.py
<config xmlns="http://tail-f.com/ns/config/1.0">
  <devices xmlns="http://tail-f.com/ns/ncs">
    <device>
      <name>d-ARBL-1</name>
      <address>198.108.10.4</address>
      <authgroup>default</authgroup>
      <state>
        <admin-state>unlocked</admin-state>
      </state>
      <cli>
        <ned-id xmlns:cisco-ios-cli-6.73="http://tail-f.com/ns/ned-id/cisco-ios-cli-6.73">cisco-ios-cli-6.73</ned-id>
      </cli>
      <in-band-mgmt xmlns="http://umnet.umich.edu/umnetcommon">
        <ip-address>198.108.10.4</ip-address>
        <interface>Loopback0</interface>
      </in-band-mgmt>
    </device>
  </devices>
</config>Why do it this way?There are many ways to accomplish this task, almost all of which wouldn't require you to
manually re-define yang objects as python classes as we're doing here. Our goal with this repo is to
create objects representing NSO instances that:Can be used in code anywhere, without requiring access to an instance of NSO running UMnet packages.Are easy to understand/manipulate and provide a similar experience as working with service objects
generated by NSO's high level python API.Don't require compiling yang files.Generate xml that can be used in an RPC call or manually ingested by NSO.