📜 ⬆️ ⬇️

Programming for network engineers: the first case

The use of programming in the network business has already become a trend, so in the continuation of the article Why the network engineers need programming, I begin a series of small notes about automating the solution of various practical problems. To dispel the aura of complexity around this topic, some examples and case studies will be published, mainly using Python, and links to deeper material and technical documentation will be given. The introductory article of this series is below.

First, a couple of words for antagonists, do not rush to say "this is not for me." Trends in the data networking industry push the question “how to do” to the background, moving the issue to the forefront, i.e. error-free and costly operation, these same trends are pushing us, network engineers, to study various automation tools. For those whose minds are not “infected” with a virus called a pointer to a function pointer, I suggest starting this journey with Python, the book Automate the Boring Stuff with Python can be a good help in this matter. The book is written in a very friendly form for beginners, and is perfect for obtaining the necessary minimum of knowledge and practical experience. The material of the chapters, examples and assignments correspond to the spirit and philosophy of python:


After reading less than half of the chapters, you will understand python data structures and will be able to write the first code, and after reading the second half you will get an idea of ​​the capabilities of python in solving application problems, like sending HTTP requests, working with data in CSV format or parsing JSON documents. I am a supporter of learning from low-level to high-level programming, but after reading the book, I noticed that the story itself pushes to understand the basics of programming with the same simplicity and naturalness with which children begin to communicate in their native language. If you are not a professional programmer, but simply look for literature to learn how to formulate your thoughts in the form of code, try to make python your native language.

So, the first example is dedicated to checking the operation of the states of the Juniper Networks router. As a reference, I chose the presence verification task on the remote side of the response arm for the configured RSVP LSP. The presence of two-way MPLS transport is a prerequisite for the transfer of traffic of various kinds of VPN. Service signaling may well work on IP, and data-plane traffic requires seamless MPLS paths between PE routers. To test LSP paths from the CLI, we usually use the command
')
display mpls lsp 

In this case, we need to make sure that for each LSP from the Ingress section there is such an LSP from the Egress section whose destination address is equal to the source address of the first LSP.
We will solve this problem with the help of Pyez, this mini framework contains a set of classes and data structures for interacting with routers from Python code. Here is a more detailed description of the capabilities of Understanding Junos PyEZ , and the installation procedure for Junos PyEZ

Pyez leverages Junos's ability to convert output to XML. Add an option to any team

 | display xml 

and you get a ready-to-use machine-to-machine interface. More details about this feature can be found in XML and Junos OS Overview

It is in this form that Pyez receives data from the router, and since it doesn’t give me much pleasure to work with raw XML data, I’ll tell you how views (View) and tables (Table) allow you to abstract from the subtleties of this format.

Each Junos command corresponds to a certain Pyez method. To find out the name of this method, use the option | display xml rpc | display xml rpc . The method name is right inside the tags, in this case, get-mpls-lsp-information

show mpls lsp | display xml rpc
 user@host> show mpls lsp | display xml rpc <rpc-reply xmlns:junos="http://xml.juniper.net/junos/15.1R1/j...">; <rpc> <get-mpls-lsp-information> </get-mpls-lsp-information> </rpc> </rpc-reply> 


You can view the output format of the methods and their corresponding commands by receiving the response in XML format, or examine it on the XML API Explorer page - Operational Tags The router will respond to the get-mpls-lsp-information as follows:

show mpls lsp | display xml
  <rsvp-session-information> <rsvp-session-data>....</rsvp-session-data> </rsvp-session-information> <rsvp-session-data> <session-type><i>session-type</i></session-type> <count><i>count</i></count> <display-count><i>display-count</i></display-count> <up-count><i>up-count</i></up-count> <down-count><i>down-count</i></down-count> <detours><i>detours</i></detours> <rsvp-session>....</rsvp-session> <mpls-p2mp-lsp>....</mpls-p2mp-lsp> </rsvp-session-data> <rsvp-session> <destination-address><i>destination-address</i></destination-address> <is-detour><i>is-detour</i></is-detour> <source-address><i>source-address</i></source-address> <lsp-state><i>lsp-state</i></lsp-state> <lsp-pktbytes><i>lsp-pktbytes</i></lsp-pktbytes> <bypass-name><i>bypass-name</i></bypass-name> <no-statistics><i>no-statistics</i></no-statistics> <route-count><i>route-count</i></route-count> <rsb-count><i>rsb-count</i></rsb-count> <resv-style><i>resv-style</i></resv-style> <label-in><i>label-in</i></label-in> <label-out><i>label-out</i></label-out> <name><i>name</i></name> <mpls-p2mp-lsp-name><i>mpls-p2mp-lsp-name</i></mpls-p2mp-lsp-name> <p2mp-remerge-state><i>p2mp-remerge-state</i></p2mp-remerge-state> <lsp-description><i>lsp-description</i></lsp-description> <lsp-path-type><i>lsp-path-type</i></lsp-path-type> <mpls-lsp-type><i>mpls-lsp-type</i></mpls-lsp-type> <lsp-aggregation><i>lsp-aggregation</i></lsp-aggregation> <graceful-deletion-triggered><i>graceful-deletion-triggered</i></graceful-deletion-triggered> <source-tna-address><i>source-tna-address</i></source-tna-address> <destination-tna-address><i>destination-tna-address</i></destination-tna-address> <bidirectional><i>bidirectional</i></bidirectional> <associated-bidirectional><i>associated-bidirectional</i></associated-bidirectional> <lsp-associated-lspname><i>lsp-associated-lspname</i></lsp-associated-lspname> <lsp-associated-lspsrc><i>lsp-associated-lspsrc</i></lsp-associated-lspsrc> <upstream-label-in><i>upstream-label-in</i></upstream-label-in> <upstream-label-out><i>upstream-label-out</i></upstream-label-out> <suggested-label-in><i>suggested-label-in</i></suggested-label-in> <suggested-label-out><i>suggested-label-out</i></suggested-label-out> <recovery-label-in><i>recovery-label-in</i></recovery-label-in> <recovery-label-out><i>recovery-label-out</i></recovery-label-out> <psb-lifetime><i>psb-lifetime</i></psb-lifetime> <psb-creation-time><i>psb-creation-time</i></psb-creation-time> <path-mtu><i>path-mtu</i></path-mtu> <path-mtu-in-kernel><i>path-mtu-in-kernel</i></path-mtu-in-kernel> <sender-tspec><i>sender-tspec</i></sender-tspec> <layer2-tspec>....</layer2-tspec> <adspec><i>adspec</i></adspec> <ct-bw><i>ct-bw</i></ct-bw> <lsp-diffserv-info><i>lsp-diffserv-info</i></lsp-diffserv-info> <lsp-id><i>lsp-id</i></lsp-id> <tunnel-id><i>tunnel-id</i></tunnel-id> <proto-id><i>proto-id</i></proto-id> <p2mp-branch-id><i>p2mp-branch-id</i></p2mp-branch-id> <p2mp-subgroup-orig><i>p2mp-subgroup-orig</i></p2mp-subgroup-orig> <self-id><i>self-id</i></self-id> <p2mp-self-id><i>p2mp-self-id</i></p2mp-self-id> <session-id><i>session-id</i></session-id> <is-fastreroute><i>is-fastreroute</i></is-fastreroute> <is-linkprotection><i>is-linkprotection</i></is-linkprotection> <is-nodeprotection><i>is-nodeprotection</i></is-nodeprotection> <is-soft-preemption><i>is-soft-preemption</i></is-soft-preemption> <rsvp-path-status><i>rsvp-path-status</i></rsvp-path-status> <rsvp-lp-backup-route-cnt><i>rsvp-lp-backup-route-cnt</i></rsvp-lp-backup-route-cnt> <rsvp-lp-backup-lsp-cnt><i>rsvp-lp-backup-lsp-cnt</i></rsvp-lp-backup-lsp-cnt> <packet-information>....</packet-information> <explicit-route>....</explicit-route> <record-route>....</record-route> <lsp-attribute-flags>....</lsp-attribute-flags> <lp-history>....</lp-history> <rsvp-telink>....</rsvp-telink> <protection-attribute>....</protection-attribute> <association-attribute>....</association-attribute> <detour>....</detour> <detour-branch>....</detour-branch> <mpls-lsp>....</mpls-lsp> </rsvp-session> <mpls-lsp> <destination-address><i>destination-address</i></destination-address> <source-address><i>source-address</i></source-address> <lsp-state><i>lsp-state</i></lsp-state> <route-count><i>route-count</i></route-count> <active-path><i>active-path</i></active-path> <is-primary><i>is-primary</i></is-primary> <name><i>name</i></name> <bidirectional><i>bidirectional</i></bidirectional> <associated-bidirectional><i>associated-bidirectional</i></associated-bidirectional> <lsp-associated-lspname><i>lsp-associated-lspname</i></lsp-associated-lspname> <lsp-associated-lspsrc><i>lsp-associated-lspsrc</i></lsp-associated-lspsrc> <lsp-description><i>lsp-description</i></lsp-description> <lsp-pktbytes><i>lsp-pktbytes</i></lsp-pktbytes> <lsp-packets><i>lsp-packets</i></lsp-packets> <lsp-bytes><i>lsp-bytes</i></lsp-bytes> <aggregate-lsp-pktbytes><i>aggregate-lsp-pktbytes</i></aggregate-lsp-pktbytes> <no-statistics><i>no-statistics</i></no-statistics> <mpls-p2mp-name><i>mpls-p2mp-name</i></mpls-p2mp-name> <lsp-type><i>lsp-type</i></lsp-type> <lsp-control-status><i>lsp-control-status</i></lsp-control-status> <egress-label-operation><i>egress-label-operation</i></egress-label-operation> <is-fastreroute><i>is-fastreroute</i></is-fastreroute> <is-linkprotection><i>is-linkprotection</i></is-linkprotection> <is-nodeprotection><i>is-nodeprotection</i></is-nodeprotection> <is-inter-domain-path><i>is-inter-domain-path</i></is-inter-domain-path> <load-balance><i>load-balance</i></load-balance> <lsp-diffserv-te-info><i>lsp-diffserv-te-info</i></lsp-diffserv-te-info> <metric><i>metric</i></metric> <revert-timer><i>revert-timer</i></revert-timer> <revert-timer-remain><i>revert-timer-remain</i></revert-timer-remain> <optimize-protection-timer><i>optimize-protection-timer</i></optimize-protection-timer> <admin-groups>....</admin-groups> <admin-groups-extended>....</admin-groups-extended> <mpls-srlg>....</mpls-srlg> <lsp-creation-time><i>lsp-creation-time</i></lsp-creation-time> <lsp-soft-preemption-counter><i>lsp-soft-preemption-counter</i></lsp-soft-preemption-counter> <lsp-soft-preemption-time><i>lsp-soft-preemption-time</i></lsp-soft-preemption-time> <retry-timer><i>retry-timer</i></retry-timer> <retry-limit><i>retry-limit</i></retry-limit> <mpls-lsp-autobandwidth>....</mpls-lsp-autobandwidth> <mpls-lsp-path>....</mpls-lsp-path> <mpls-lsp-attributes>....</mpls-lsp-attributes> </mpls-lsp> 


An XML document consists of elements that can contain child elements or atomic values. For example, the <rsvp-session-information> </rsvp-session-information> element contains <rsvp-session-data></rsvp-session-data> child elements, which in turn contain <rsvp-session> elements, inside which is an <mpls-lsp> element with atomic values ​​of type name .

To loop through nested structures of this kind, I use views and tables. This approach relies on Python's dynamic typing to create arrays or lists of dictionaries at run time. If an element, for example <rsvp-session-data> , contains a number of sub-elements <rsvp-session> , you get a list of them, and if a sub-element, for example <destination-address> is unique, you will get its value as an object . For more information about tables and views, see Defining Junos PyEZ Operational Tables and Defining Junos PyEZ Views for Operational Tables .

The format of views and tables in the sample code is yml string variable, the key elements of this format are below:

The rpc value is used in the table, and contains the name of the method from the display xml rpc output.

The item value is used in the table, and contains the name of the XML element that interests us. Names are set according to the path hierarchy in the XML document

The view value is used in the table, and contains a description of the view. In the case of the presence of nested elements, you must make nested descriptions.

The value of fields is used in the view and contains the names of the atomic elements.

Here, perhaps, everything you need to know about Pyez to organize an iteration on a flat and nested operational data of the router, complete the example below.

 import sys import yaml from jnpr.junos.factory.factory_loader import FactoryLoader from jnpr.junos import Device yml = ''' --- MplsSession: rpc: get-mpls-lsp-information item: rsvp-session-data view: MplsSessionView MplsSessionView: fields: type: session-type count: count lsp: _MplsLsp rsvp: _RsvpLsp _RsvpLsp: item: rsvp-session view: _MplsLspView _MplsLsp: item: rsvp-session/mpls-lsp view: _MplsLspView _MplsLspView: fields: dst_addr: destination-address src_addr: source-address state: lsp-state route_count: route-count active_path: active-path name: name ''' globals().update(FactoryLoader().load(yaml.load(yml))) if (len(sys.argv) < 4): print 'Call this script as ' + sys.argv[0] + ' host user password ' sys.exit() try: host = sys.argv[1] user = sys.argv[2] password = sys.argv[3] dev = Device(host=host, user=user, password=password, mode='telnet', port='23') dev.open() except Exception: print 'Cannot connect to ' + host sys.exit() bt = MplsSession(dev).get() dev.close() out='' for s in bt: if (s.type == 'Ingress'): for l in s.lsp: bidir = 0 for ss in bt: if (ss.type == 'Egress'): for r in ss.rsvp: if ( (r.dst_addr == l.src_addr) and (r.src_addr == l.dst_addr) ): bidir = 1 out = 'Remote LSP named ' + r.name if (bidir == 0): print 'Unidirectional LSP named ' + l.name + ' to: ' + l.dst_addr + ' is in ' + l.state + ' state' if (bidir == 1): print 'Bidirectional LSP named ' + l.name + ' to: ' + l.dst_addr + ' is in ' + l.state + ' state' print out print '' 

Source: https://habr.com/ru/post/350908/


All Articles