# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# ### GENIE Parsergen
#
#
# In addition to using the Ops package to retrieve and parse operational state of a device, the Genie Parsergen Class provides 
# a one-step parsing mechanism that is capable of parsing dynamic tabular and non-tabular device outputs in a “noticeably” 
# less lines of code compared to standard parsing mechanisms.  
#
#
# The Parsergen Class is particularly useful where Genie Ops does not have a model for the particular state you are 
# looking to parse.  
# As an example there is currently no Genie Ops Model for NVE/VXLAN.  This gap can be overcome by creating the parser that can 
#  then be leveraged by pyATS/GENIE.  
#  
# The object of the remaining exercises is to 
# * Parse VXLAN relevant state
# * Create an Ops library
# * Run a pyATS easypy script to test condition of VXLAN state
#
#
# ###Tabular Parsing
#
# The Genie Parsergen Class can deal with both Tabular and Non Tabular device output from a networking device. We 
# shall initially explore Tabular parsing
#
# Consider the output from the show command 'show nve vni'
#
# ```
# Interface  VNI        Multicast-group VNI state  Mode  BD    cfg vrf                      
# nve1       6001       N/A             Up         L2DP  1     CLI N/A 
# ```
#
# As can been seen above this is a column based/tabular output.  In order to parse this output we need to instruct
# parsergen as to the titles of the columns.  Follow the commands below to parse the command 'show nve vni'

# As in previous sections initiate the testbed topology and import the relevant libraries for this exercise

# +
import pprint
from genie.conf import Genie
from genie import parsergen

from genie.libs.ops.interface.iosxe.interface import Interface

testbed = Genie.init('../scripts/vagrant_single_ios.yaml')
uut = testbed.devices.iosxe1
uut.connect()
# -

# The testbed object 'uut.device' has a method of execute.  Execute will run the command on the device and return
# a string as the result of the command

output = uut.device.execute('show nve vni')

# A list identifying the headers of the expected column output is created

header = ['Interface', 'VNI', 'Multicast-group', 'VNI state', 'Mode', 'BD', 'cfg', 'vrf']

# We will now use the parsergen oper_fill_tabular method to parse the string and store as structured data

result = parsergen.oper_fill_tabular(device_output=output, device_os='iosxe', header_fields=header, index=[0])

# Now print the structured data returned

pprint.pprint(result.entries)

# Determine the type of the result object entries attribute

type(result.entries)

# As you will see the returned data is now structured data in the form of a dictionary

# ### GENIE Non Tabular Parsing
#
# Not all output from the device will be in tabular form.  Parsergen can deal with non tabular
# returned data.  
# Parsergen tries to match a given set of data using regular expressions that describe the values found
# in the show command output.
#
# Consider the following output from the _show nve interface nve 1_ .  
# We shall parse the data to retrieve Source_Interface and Primary address based upon an encapsulation of Vxlan
#
# ```bash
#
# Interface: nve1, State: Admin Up, Oper Up, Encapsulation: Vxlan,
# BGP host reachability: Disable, VxLAN dport: 4789
# VNI number: L3CP 0 L2CP 0 L2DP 1
# source-interface: Loopback10 (primary:172.16.10.1 vrf:0)
# ```
#
# There are two methods by which we can retrieve this data - Manual regular expressions and Markup
#

# ### Using Regular Expressions manually
#
#
# To start make sure that your Python Virtual Environment is still running from step 4 and that you are in 
# the scripts directory.
# Initiate an iPython interactive session and intialise the testbed

# +
from pprint import pprint
from genie.conf import Genie
from genie import parsergen

testbed = Genie.init('../scripts/vagrant_single_ios.yaml')
uut = testbed.devices.iosxe1
uut.connect()
# -

# Create a dictionary of show commands. Only one show command for IOSXE in this instance

show_cmds = {
     'iosxe': {
        'show_int' : "show nve interface {}",
     }
}

# Create a dictionary of regular expressions to capture the elements required in the output. The 
# example has regular expressions that will capture the encapsulation type, the source interface and the primary address.  
# As useful tool for creating and validing python _re_ based regular expressions can be found here: [Pythex](https://pythex.org/)

# +
regex = {

    'iosxe': {
        'nve.intf.if_encap': r'[a-zA-Z0-9\:\,\s]+Encapsulation:\s+(\w+),',
        'nve.intf.source_intf': r'^source-interface:\s+(\w+)',
        'nve.intf.primary': r'[a-zA-Z0-9\:\,a-zA-Z0-9\s]+\(primary:([A-Fa-f0-9:\.]+)'
     }
}

regex_tags = {
    'iosxe': ['nve.intf.if_encap',  'nve.intf.source_intf', 'nve.intf.primary']
    }

# -

# 'Extend' the Parsergen Class to include the show commands and the regular expressions

parsergen.extend(show_cmds=show_cmds, regex_ext=regex, regex_tags=regex_tags)

# Now determine the parameters you wish to start the regex search on. The first item in the 
# tuple is the key name of the regex value, the second item is the value being searched in this
# case all interfaces with Vxlan encapsulation

attrValPairsToParse = [('nve.intf.if_encap', 'Vxlan')]

# Finally we create the object pgfill by calling the _parsergen.oper\_fill_ method is called.  The arguments in this method will
# * determine the device to be called (uut)
# * determine which show command to call from the key show_int and use nve1 as the interface name for the show command
# * Provide the attribute value pairs to search on
# * And use the defined regular expressions that begin with _nve.intf_

pgfill = parsergen.oper_fill (
    uut,
    ('show_int', ['nve1']),
    attrValPairsToParse,
    refresh_cache=True,
    regex_tag_fill_pattern='nve\.intf')

# Now enter the parse method for pgfill to populate parsergen ext_dictio attribute with the parsed items

pgfill.parse()

# Display the completed parse with

pprint(parsergen.ext_dictio)

# Disconnect from the device

uut.disconnect()

# ---

# ### Using Markup Text to parse Non Tabular Output
#
# Rather than explicitly defining regular expressions for each item to retrieve, as an alternative
# we can use a special CLI command markup format that will automatically generate the regular
# expressions.
#
# If you have an iPython session running. Close and restart iPython
#
# Initiate an iPython interactive session and intialise the testbed

# +
import pprint
from genie.conf import Genie
from genie import parsergen


testbed = Genie.init('../scripts/vagrant_single_ios.yaml')
uut = testbed.devices.iosxe1
uut.connect()
# -

# Enter the following to assign the _marked up_ string to the variable markedupIOSX

markedupIOSX = '''
OS: iosxe
CMD: show_nve_interface
SHOWCMD: show nve interface {ifname}
PREFIX: nve.intf
ACTUAL:

Interface: nve1, State: Admin Up, Oper Up, Encapsulation: Vxlan,
BGP host reachability: Disable, VxLAN dport: 10000
VNI number: L3CP 0 L2CP 0 L2DP 1
source-interface: Loopback10 (primary:1.1.1.1 vrf:22)

MARKUP:
Interface: XW<ifname>Xnve1, State: Admin XW<state>XUp, Oper Up, Encapsulation: XW<encap>XVxlan,
BGP host reachability: Disable, VxLAN dport: XN<udp_port>X1000
VNI number: L3CP 0 L2CP 0 L2DP 1
source-interface: XW<source_interface>XLoopback0 (primary:XA<primary_address>X1.1.1.1 vrf:XN<VRF>X22)'''

# You will notice in the string that there are some key components
#
# **OS:** Define the operating system being used  
# **CMD:** Used by parsergen as the dict key for the _SHOWCMD_  
# **SHOWCMD:** The actual show command to be issued  
# **PREFIX** Will be used to prefix the keys for each item parsed  
# **ACTUAL** Output expected from the device (optional)  
# **MARKUP** The Output with markup added. Will be used to identify items to parse
#
# The Markup itself begins and ends with **X** with the key name inbetween.  For example
# **XW\<ifname>X**  will assign a value to the key nve.intf.**ifname**
#
# Full list of Markup tags are included at the bottom of this file.
#
# The remaining commands are similar to those used for parsing with regular expressions
#
# 'Extend' the Parsergen Class to include the show commands and the regular expressions

parsergen.extend_markup(markedupIOSX)

# Now determine the parameters you wish to start the regex search on. The first item in the 
# tuple is the key name of the regex value, the second item is the value being searched. In this instance
# only nve interfaces that have a Vxlan encapsulation are being considered

attrValPairsToCheck = [('nve.intf.encap', 'Vxlan'),]

# Create an object called pgfill from the parsergen.oper_fill method in order to create a dictionary of the parsed output. 

pgfill = parsergen.oper_fill(device=uut,
                             show_command=('show_nve_interface', [], {'ifname':'nve1'}),
                             attrvalpairs=attrValPairsToCheck,
                             refresh_cache=True, 
                             regex_tag_fill_pattern='nve\.intf')

# Now call the parse method for the object pgfill

pgfill.parse()

# Print the parsed output

print(parsergen.ext_dictio)

# Disconnect from the device

uut.disconnect()

# **Mark Up Reference**
#
# The following are the available values for x in the XxX notation:
#
# * A - IPv4 or IPv6 address.  
# * B - Value terminated with a close brace, bracket, or parenthesis.
# * C - Value terminated with a comma.
# * F - Floating point number.
# * H - Hexidecimal number.
# * I - Interface name.
# * M - Mac address.
# * N - Decimal number.
# * R - everything else to the newline.
# * P - IPv4 or IPv6 prefix.
# * Q - Value terminated by a double quote.
# * S - Non-space value.
# * T - Time (00:00:00)
# * W - A word.

# ---

# ### GENIE Creating an OPS object
#
# We are now going to create a VxLAN OPS object that will collate the output of the two parsers we created earlier.
#
# For the sake of brevity these two parsers have been defined within Classes in the file [iosxevxlan.py](../scripts/iosxevxlan.py).  
# The parsers are also inheriting from Genie Metaparser.  The configuration of Metaparser is outside the scope of this workshop
# but further details can be found at - [Metaparser](https://pubhub.devnetcloud.com/media/pyats-packages/docs/metaparser/index.html)
#

import pprint
from genie.conf import Genie
testbed = Genie.init('../scripts/vagrant_single_ios.yaml')
uut = testbed.devices.iosxe1
uut.connect()

# First we shall import from Genie ops the Base class.  We will create a class that will inherit from 'Base' to leverage the
# 'Maker' functionality.  
# 'Maker' simplifies the process of mapping parsers output to the ops object attributes. 
#
# Further information on the Maker class can be found at [Maker](https://pubhub.devnetcloud.com/media/pyats-packages/docs/genie/Ops/developer/maker.html) 
#
# In addition we will import the parsers that were created earlier.
#
# Enter the code below into your ipython session

from genie.ops.base import Base
from iosxevxlan import ShowNveVni,ShowNvePeers


# We now create a class that will be our Ops object, named Vxlan.  This class inherits from the Base class of Genie Ops.  
# A method which referred to as _learn_ is created.  The remaining code performs the following functions  
#
# * Runs a for loop issuing the commands for the parsers and then adds data (add_leaf) to the new Ops object structure.
# * src is the dictionary item from the parsed output. For example '['(?P<interf>.*)][VNI]' will equate to the value of VNI (6001)
# * dest is where the data will be placed in the new object structure referenced as *info*.  In this case the src and dest keys are the same
# but this does not have to be the case
# * Finally the make() is invoked to finalise the new object structure.

class Vxlan(Base):

    def learn(self, custom=None):


        # Capture output from ShowNveVni parser
        src = '[(?P<interf>.*)]'
        dest = 'info[(?P<interf>.*)]'
        req_keys = ['[VNI]','[Multicast-group]','[VNIstate]','[Mode]']
        for key in req_keys:
            self.add_leaf(cmd=ShowNveVni,
                          src=src + '[{}]'.format(key),
                          dest=dest + '[{}]'.format(key))


        # Capture ouptut from ShowNveVni parser
        src = '[(?P<nvename>.*)]'
        dest = 'info[(?P<nvename>.*)]'
        req_keys = ['[Peer-IP]','[Router-RMAC]','[Type]','[state]']
        for key in req_keys:
            self.add_leaf(cmd=ShowNvePeers,
                          src=src + '[{}]'.format(key),
                          dest=dest + '[{}]'.format(key))

        #Add ops data to the Vxlan ojbect
        self.make()

# Finally create a new ops object called myvxlan and learn from the device

myvxlan = Vxlan(device=uut)

myvxlan.learn()

myvxlan.info

# Disconnect from the device

uut.disconnect()

# ### You have successfully created a VxLAN Ops Model.

StarcoderdataJupyterScriptsDedupFiltered