Automation
Automation

How-To: Use tables for automation tasks

by Cordelia on ‎08-06-2015 05:39 AM - edited on ‎09-18-2017 02:59 PM by Administrator Administrator

Overview

Explains using tables for automation tasks.

 

Discussion

 

Table widgets can be either loaded from a Python module or using the loadyaml() function provided by the jnpr.junos.factory module.  For the purpose of this discussion to illustrate the use of tables, we will use the former.  The use of loadyaml() is explained here.

 

Tables are associated to Device variables using one of two methods:

As a standalone variable:

 

1 from jnpr.junos.op.ethport import EthPortTable  
2 eths = EthPortTable(dev)eths.get()

 

As a bound attribute of the Device:

 

1 from jnpr.junos.op.ethport import EthPortTable  
2 dev.bind(eths=EthPortTable)dev.eths.get()

 

In either case, using the table once it is bound to the Device is the same.

 

Table widgets are presented to have a "look and feel" similar to Python dictionary types (dict).  This enables you to access information bykeys(), values(), and items() and select a specific item by key name or index location.

 

The underlying Junos OS XML command and response data extraction is handled by Junos PyEZ so the user does not need to know these details.  The Table or View widgets are abstractions defined using YAML files, so that the  widgets creator does not need to code these in Python.  The topic of creating these YAML files is discussed on other topic pages (see sidebar links).

 

Retrieving All Table Items

 

Retrieving table items is done using the get() method.  If you do not pass any arguments to get(), then all of the table items will be extracted from the Junos OS device.

 

For example:

 

1 >>> eths.get()
2 EthPortTable:jnpr-dc-fw: 3 items

 

From the Python shell we can see that there are three Ethernet ports in this table.  Programmatically, we could also use the len() function to obtain this information:

 

1 >>> len(eths)
2 3

 

Retrieving Specific Table Items

 

You can use get() to be more specific about the items to retrieve.  You can do this by knowing the available Junos OS command options and passing them directly to get().  Let's take a look at an example using the equivalent of the Junos OS CLI show route command.  This Table widget is provided by Junos PyEZ.  We can use it to retrieve all the routes:

 

1 >>> from jnpr.junos.op.routes import RouteTable
2 >>>
3 >>> routes = RouteTable(dev)
4 >>> routes.get()
5 RouteTable:jnpr-dc-fw: 148 items

 

But what if we want only a specific destination, for example, the same as doing show route 192.168.56.0/24?  You would pass the specific destination to the get() method like so:

 

1 >>> routes.get('192.168.56.0/24')
2 RouteTable:jnpr-dc-fw: 2 items

 

You can be even more specific if you know the Junos OS CLI command parameter.  For example, let's say that you only wanted to retrieve "local" routes for a given destination, the same as show route 192.168.56.0/24 protocol local.

 

1 >>> routes.get('192.168.56.0/24', protocol='local')
2 RouteTable:jnpr-dc-fw: 1 items

 

Ok, so how did I know what to do?  This is where actually knowing the Junos OS CLI (as a network engineer) comes into play.

 

If you  were to go to the Junos OS CLI and enter the command, you can display the Junos OS XML API using the "pipe display" filter like so:

 

01 jeremy@junos> show route 192.168.56.0/24 protocol local | display xml rpc
02 <rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.1I0/junos">
03     <rpc>
04         <get-route-information>
05                 <destination>192.168.56.0/24</destination>                <protocol>local</protocol>        </get-route-information>
06     </rpc>
07     <cli>
08         <banner></banner>
09     </cli>
10 </rpc-reply>

 

Here you can see that the command is taking two arguments: destination and protocol.  So to use any Junos OS CLI parameter, you simply provide the key=value pair when you make a call with get().  You can pass multiple key value pairs.  For example, let's say you wanted to do the above, but for just a specific routing table.  I'll use "inet.0" just for an example.  From the Junos OS CLI, it would look like this:

 

01 jeremy@junos> show route 192.168.56.0/24 protocol local table inet.0 | display xml rpc            
02 <rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.1I0/junos">
03     <rpc>
04         <get-route-information>
05                 <destination>192.168.56.0/24</destination>
06                 <table>inet.0</table>                <protocol>local</protocol>
07         </get-route-information>
08     </rpc>
09     <cli>
10         <banner></banner>
11     </cli>
12 </rpc-reply>

 

And the corresponding use of get() would look like this:

 

1 >>> routes.get('192.168.56.0/24', protocol='local', table='inet.0')
2 RouteTable:jnpr-dc-fw: 1 items

 

But hold on, why didn't we explicitly use destination in the call to get()?  This is because when we defined the YAML widget for the table, we identified that the "default argument key" was the field called "destination".  Here is the corresponding YAML file for theRouteTable:

 

1 RouteTable:
2   rpc: get-route-information
3   args_key: destination  item: route-table/rt
4   key: rt-destination
5   view: RouteTableView

 

The choice to define the YAML in this way was to mimic the behavior of the Junos OS CLI; you can simply provide the destination value as the first parameter to the show command.  On the Junos OS CLI you can see this identified:

 

1 jeremy@junos> show route ?
2 Possible completions:
3   <[Enter]>            Execute this command
4   <destination>        IP address and optional prefix length of destination
5   active-path          Show active paths

 

It should be noted that you can explicitly use the destination key=value in the call to get() should you choose to do so.  Also, if the YAML forRouteTable did not have the args_key property defined, you would be required to do so.

 

Accessing Table Items

 

Once you've retrieved table items, you can treat the table like a Python dictionary:

 

  • keys() - get a list of table item names
  • values() - get a list of table View-tuple (name/value) for each field in each record
  • items() - a tuple composite of keys() and values()

 

Keys:

 

1 >>> routes.get('192.168.56.0/24')
2 RouteTable:jnpr-dc-fw: 2 items
3 >>> routes.keys()
4 ['192.168.56.0/24', '192.168.56.10/32']

 

Values:

 

1 >>> pp( routes.values() )
2 [[('age', '167293'), ('via', 'ge-0/0/0.0'), ('protocol', 'Direct')],
3  [('age', '167296'), ('via', 'ge-0/0/0.0'), ('protocol', 'Local')]]

 

Items:

 

1 >>> pp( routes.items() )
2 [('192.168.56.0/24',
3   [('age', '167518'), ('via', 'ge-0/0/0.0'), ('protocol', 'Direct')]),
4  ('192.168.56.10/32',
5   [('age', '167521'), ('via', 'ge-0/0/0.0'), ('protocol', 'Local')])]

 

Selecting a Specific Table Item

 

You can select a specific table item either by key (name) or by index.  When you select a table item, you get a View.  The View lets you then access the specific fields in a native Pythonic way (i.e., you don't need to know the Junos OS or XML).

By specific key or name:

 

1 >>> this = routes['192.168.56.10/32']
2 >>> this
3 RouteTableView:192.168.56.10/32

 

By index, get the first entry in the table:

 

1 >>> this = routes[0]
2 >>> this
3 RouteTableView:192.168.56.0/24

 

Once you have a View to the table item, you can access the properties, like so:

 

1 >>> this.name
2 '192.168.56.0/24'
3 >>> this.via
4 'ge-0/0/0.0'
5 >>> this.protocol
6 'Direct'

 

You can apply a different View to a table item, which gives you the effective capability similar to "brief, extensive, detailed", except in the case of Junos PyEZ, you get to decide what is in the view, rather than Junos OS.  Details on this process are described in the View topic pages.

 

Iterating Through a Table

 

Tables support iteration, so you can loop through each item in the same way you would loop through a list or dictionary.  Each iteration item is the table-item view.  So you can do things like:

 

01 >>> from jnpr.junos.op.ethport import *
02 >>> eths = EthPortTable(dev)
03 >>> eths.get()
04 EthPortTable:jnpr-dc-fw: 32 items
05 >>> for port in eths:
06 ...   print "{}: {}".format(port.name, port.oper)
07 ...
08 ge-0/0/0: up
09 ge-0/0/1: up
10 ge-0/0/2: up
11 ge-0/0/3: down
12 ge-0/0/4: down
13 #<snip>

 

Slicing a Table

 

You can take "slices" of a table the same way you would with a list.  For example, if you wanted to look at the first ten items in a table, you could do this:

 

01 >>> for port in eths[0:10]:
02 ...   print "{}:{}".format(port.name, port.oper)
03 ...
04 ge-0/0/0:up
05 ge-0/0/1:up
06 ge-0/0/2:up
07 ge-0/0/3:down
08 ge-0/0/4:down
09 ge-0/0/5:up
10 ge-0/0/6:up
11 ge-0/0/7:down
12 ge-0/0/8:down
13 ge-0/0/9:down

 

Key in a Table

 

You can also us the "in" operator to determine if a table contains a key entry.  For example:

 

1 >>> 'ge-0/0/0' in eths
2 True
3 >>> 'ge-5/0/20' in eths
4 False

 

Saving the Table XML to a Local File

 

You can save the XML data to your local file system (not the Junos OS device) using the savexml() method.  You can optionally include the Device hostname and timestamp as part of the created filename:

 

1 >>> from jnpr.junos.op.ethport import *
2 >>> eths = EthPortTable(dev)
3 >>> eths.get()
4 EthPortTable:jnpr-dc-fw: 3 items
5 >>>
6 >>> eths.savexml('/var/tmp/eths.xml',hostname=True,timestamp=True)

 

Looking at the local file system:

 

[jeremy@centos /var/tmp]$ ls -al *.xml
-rw-rw-r--. 1 jeremy jeremy 7930 Dec 23 15:27 eths_jnpr-dc-fw_20131223152707.xml

Loading Table XML from a Local File

 

You can load the locally stored XML data directly into a Table widget using the path parameter to the table creation, as illustrated:

 

1 >>> eths = EthPortTable(path='/var/tmp/eths_jnpr-dc-fw_20131223152707.xml')
2 >>> eths.get()
3 EthPortTable:/var/tmp/eths_jnpr-dc-fw_20131223152707.xml: 3 items

 

Defining Your Own Tables

 

The Junos PyEZ library will contain a growing number of table definitions. The Junos PyEZ library was designed so that you can quickly define you own tables; it is assumed that what you want will not be in the library, and you do not need to wait. Generally speaking, it should take you less than 10 minutes to define a new table and associated view, once you get the hang of it. Take a look at the existing tables in the source directory: jnpr/junos/op for examples.  See the sidebar links to jump to topic pages discussions for the specific process.