Develop Ops
The section will discuss how users can develop a Genie
ops
object. Please
review the user guide before beginning this section.
Note
All code in this section is pseudo-code only.
Overview
Ops
object is a snapshot of a particular feature
on a particular device
at
a specific time.
Many commands like, Cli, Yang, and XML gather a `feature`s’ state and store it into one object. Each command is parsed and then regrouped into one common structure. This structure is compatible with various operating systems.
We created a wiki to help users write a common feature
operational structure which
can be used across various OS
. Many teams part of XR/XE and Nxos monitor the
wiki to review all of the structures posted. An Ops
structure must mention
the attributes of the object, and any level of a dictionary. For more
guidelines and examples, please visit the wiki.
An Ops
object is made by sending multiple commands, and merging the output
into 1 common structure stored in the object. Let’s see how we merge the multiple
output into 1 common structure.
Let’s imagine that the output of a few parsers all related to the same feature
looks like this:
process_id:
1
vrf
blue
id:2
age:3
orange
id:5
age:6
2
vrf
default
id:12
age:13
orange
id:15
age:16
intf:
eth3/1:
vrf:
blue:
process_id:
1:
state:Up
ip_address:1.1.1.1
eth3/4:
vrf:
orange:
process_id:
1:
state:Up
ip_address:1.1.1.2
eth3/5:
vrf:
orange:
process_id:
2:
state:Up
ip_address:1.1.1.4
After looking at the feature
, we develop the following structure:
process_id:
<id>
vrf
<vrf>
router_id:<value>
age:<value>
interface_attr
<interface>
state:<value>
ip_address:<value>
We see that both structures can be merged into the same common structure; we have modified
id
to router_id
. However, converting both outputs into this structure
would require a lot of loops. To save time and reduce complexity while
creating these classes, we have created a new object called, Maker. Maker
converts parser outputs into one common structure.
Before continuing with this section, please review Maker documentation.
Creating the object
Let’s now use Maker to create our object. Our Ops
object begins as a
normal python class, which inherits from Base
.
from genie.ops.base import Base
class Ospf(Base):
'''Ospf Ops Object'''
pass
This class inherits all of the following arguments and functions for free:
+--------------------------------------------------------------------------+
| Base Class |
+==========================================================================+
| Arguments | Description |
|-----------------------+--------------------------------------------------|
| device | Device object |
| attributes | Limit which field to learn |
| maker | Maker object |
| ignored | Ignore specific attributes when comparing |
| | two snapshots |
| callables | Map callables strings to callable for all leafs |
|-----------------------+--------------------------------------------------|
| Functions | Description |
|--------------------------------------------------------------------------|
| add_leaf | Wrapper to self.maker.add_leaf |
| maker | Wrapper to self.maker.make |
| learn | Learn all the leafs |
| diff | Compare two objects and show the differences |
| __eq__ | Allows equality , ==, and return True/False |
+==========================================================================+
Now let’s create our first Ops
object with Maker and Base
object. We will
use Ospf
for this example:
from genie.ops.base import Base
class Ospf(Base):
'''Cli Ops Ops object'''
def learn(self):
'''Learn Ospf Object'''
# Step one, create our structure
# Step two, place holder to make it more readable
src_vrf = '[process_id][(?P<process_id>.*)][vrf][(?P<vrf>.*)]'
src_int = '[intf][(?P<intf>.*)][vrf][(?P<vrf>.*)][process_id][(?P<process_id>.*)]
dest_vrf = 'name[(?P<process_id>.*)][vrf_attr][(?P<vrf>.*)]'
dest_int = 'name[(?P<process_id>.*)][vrf_attr][(?P<vrf>.*)][interface][(?P<intf>.*)]'
# Step three, create our leafs (The structure could be placed directly
# in the leaf too, but it is more readable if it is defined
# at the top).
self.add_leaf(cmd=<parser for ospf process>,
src=src_vrf+'[id]',
dest=dest_vrf+'[router_id]')
self.add_leaf(cmd=<parser for ospf process>,
src=src_vrf+'[age]',
dest=dest_vrf+'[age]')
self.add_leaf(cmd=<parser ospf interface>,
src=src_int,
dest=dest_int)
self.make()
The above Ospf ops
class creates the Code Structure by merging two different
Clis
together.
# Assuming we have already a connected device
ospf = Ospf(device=device)
ospf.learn()
import pprint
pprint.pprint(ospf.name)
{'process_id': {'1': {'vrf': {'blue': {'age': 3,
'interface': {'eth3/1': {'ip_address': '1.1.1.1',
'state': 'up'}},
'router_id': 2},
'orange': {'age':6,
'interface': {'eth3/2': {'ip_address': '1.1.1.2',
'state': 'up'}},
'router_id': 5}}},
'2':{'vrf': {'default': {'age': 13,
'router_id': 12},
'orange': {'age':6,
'interface': {'eth3/5': {'ip_address': '1.1.1.4',
'state': 'up'}},
'router_id': 15}}}}}
We took two different parser outputs and then transposed them into the same structure in just a few lines of code.
Hybrid
If from some reason Maker does not respond to your needs, you may use the hybrid method.
from genie.ops.base import Base
class Ospf(Base):
'''Ops Ops object'''
def learn(self):
'''Learn Ospf Object'''
self.add_leaf(cmd=<parser for ospf process>,
src=[process_id]',
dest=process_id')
self.make()
# Do whatever action is needed with process_id
for pid in self.process_id:
...