Overview

aioRunbook (asyncio runbook) is a Python package providing an orchestration framework for automated network tests and network migrations using supporting libraries like asyncssh, netconf and snmp for interaction with the netwok elements.

_images/overview.jpg

aioRunbook is designed to be controlled by either shell execution (aioRunbookScheduler) respectively aihttp web api (aioRunbookHttpServer), which utilizes the procedures from aioRunbookScheduler.

Use cases for aioRunbook are:

  • automated lab tests for recurring test scenarios
  • automated network migrations based on sequencial test steps with the option of a rollback functionality

aioRunbook is explicitly designed for customizable inspection of the results of scheduled test/migration steps in a sequence of test steps. Each test step interacts with just one single device. aioRunbook is designed to document the test/migration results in customizable PDF/HTML outputs.

All characteristics of the test sequence and the behaviour of the io-adapters to the network devices is controlled by a single config file.

The results of the aioRunbook steps are stored intermediately in an internal data structures, which can be exported as JSON file, for further processing like:

  • rendering to PDF documents
  • dispatching the results in a web app

YAML runbook as common abstraction layer

The aioRunbook configuration is defined in a main YAML file. This YAML file defines the order and characteristic of all test steps. The YAML structure is loaded in the beginning and during the execution of the test the data structure is enriched with the output of each test step. In the current design the YAML structure is open to open to add additional attributes. This concept has the flexibility that new additional attributes can be added on demand. Adapters, analyzers, or postprocessing (PDF/HTML) can utilize those attributes.

config:
  <opt. variable definitions/file reference>
  <opt. host file reference>
  steps:
    - <step-type #1>
        <step-type 1 attributes>
       commands:
          - <step-type 1 commands>
    - <step-type #n>
        <step-type n attributes>
        commands:
          - <step-type n commands>
  pdfOutput:
      <attributes for PDF output>

Device Access Config

The are two ways to define the connection parameters for device access:

  • inline for each step.
  • Using a host dictionary file. The first word of the step name string is used as reference. YAML host file example:
config:
  hostfiles:
  - ./testHost.yml
  steps:
    - <step-type #1>
       name: lo0_SSH headline1
       commands:
         - <step-type 1 commands>
    - <step-type #n>
       name: lo0_SSH headline2
       commands:
         - <step-type n commands>
  • testHost.yml hostfile definitions:
lo0_SSH:
  device: 127.0.0.1
  method: ssh
  vendor: ubuntu
  password: testPassword
  user: testUser

Macros and Variables

aioRunbook supports a two stage concept for parameters.

  • Macros, which are replaced before the steps are executed. Macros can contain Variables as placeholder.
  • Variables, which are accessed during step execution. Check/Await stes can set variables.
_images/variablesAndMacros.jpg

Macros

Macro substitution mechanism is using Jinja2 library with specific tags:

block_start_string = '#-MACRO-BLOCK',
block_end_string = '-#',
variable_start_string = '#-MACRO-',
variable_end_string = '-#',

example of substitution for a long list of commands

config:
  macroFiles:
    - 'testMacroFile.yml'
  steps:
    - record:
        name: a flock of echo commands
        method: local-shell
        commands: #-MACRO-ECHO5-#

with the contributing macrodefinition file in testMacroFile.yml

ECHO5:
  - echo cmdString line1
  - echo cmdString line2
  - echo cmdString line3
  - echo cmdString line4
  - echo cmdString line5

the resulting-intermediate yml file does look like:

config:
  macroFiles:
    - 'testMacroFile.yml'
  steps:
    - record:
        name: a flock of echo commands
        method: local-shell
        commands:
          - echo cmdString line1
          - echo cmdString line2
          - echo cmdString line3
          - echo cmdString line4
          - echo cmdString line5

Variables

Variable substitution mechanism is using Jinja2 library with specific tags:

variable_start_string = '.VAR.',
variable_end_string = '.',

Variables can be used in a subset for following step attributes:

  • name
  • command
  • attributes used in check/await steps.

Jinja2 can process dict- and list-structures. aioRunbookScheduler provides two parameters ( loopIndex and stepIndex ) to iterate over variable lists, when executing steps/loops.

Var Defs Inline

config:
  vars:
    text1: "test for substituion in the name attribute"
    text2: freeze
  steps:
    - record:
        name: '.VAR.text1.'
        method: local-shell
        commands:
          - 'pip3 .VAR.text2'

Var Defs via Files

config:
  varFiles:
    - 'testParamaterFile.yml'
  steps:
    - record:
        name: '.VAR.text1.'
        method: local-shell
        commands:
          - 'pip3 .VAR.text2.'

with the contributing variable/parameter file testParamaterFile.yml:

vars:
  text1: "test for substituion in the name attribute"
  text2: freeze

Concept of Test Steps

A test step comprehends a set of interactions with a single device. During a test step one or more commands be executed on the specific device. The commands are provided with a list of strings, respectively with a list of objects for specific adaptors. In general the device parameters are referenced by the first word of the step attribute name. Optionally the the output (text, json, xml or API data) can be validated with customizable verification criteria. Based on experience is a good habit to verify the test step output if possible.

_images/steps.jpg

Following Step Types are implemented:

  • record: executes a list (with one ore more list-elements) of commands and records the output of CLI commands
  • config: similar to record, executes a list CLI commands. Was used to differentiate in PDF post processing, might be deprecated in the future.
  • check: executes a list commands and checks the result of one command by an appropriate analyzer. By default the output of the last command is checked.
  • await: performs periodical checks and performs an output check until the result is ‘’pass’‘, respectively the give-up-timer has expired.
  • copy: sFTP file transfer to/from the network elements
  • sleep: waits a configurable period of time before continuing with the next step.
  • break: waits for a user input (return) before continuing with the next step
  • comment: includes text-comments, text-segments, pre-recorded screenshots and file attachments. The later is a handy tool to document
    complete router configurations in the PDF output.

This an example of the aioRunbook steps:

config:
  steps:
    - record:
        name: "<DUT> - <test step summary line>"
        commands:
          - <command #1>
          - <command #n>
    - check:
        name: "<DUT> - <test step summary line>"
        commands:
          - <command #1>
          - <command #n>
        # option for CLI output verification
        textFSMOneLine: '(.*Hostname\: MX1.*) 1'
        # option for JSON output verification
        jsonOneLine: '[some] == "data"'
    - await:
        name: "<DUT> - <test step summary line>"
        give-up-timer: 10
        commands:
          - <command #1>
          - <command #n>
        # option for CLI output verification
        textFSMOneLine: '(.*Hostname\: MX1.*) 1'
        # option for JSON output verification
        jsonOneLine: '[some] == "data"'

Step Scheduler

Two modes of scheduling for test steps are supported:

  • foreground / blocking mode: the scheduler waits until the test-step is finshed, before the next step is started.
  • background / non blocking mode: the scheduler starts the test-step in the background and continues to the nest test-step.
    Once the test step is finished in the background, then the results are collected.

It is possible to loop the list of test steps by configuring a loop counter.:

_images/loopssteps.jpg

This example lists the config options for aioRunbook step concurrency:

config:
  loops: <n>  #optional default := 1
  steps:
    - record:
        name: "<DUT> - <foreground test step summary line>"
        commands:
          - <command #1>
    - record:
        name: "<DUT> - <background test step summary line>"
        startInBackground: true  #optional default := false
        randomStartDelay: 1  #optional default := 0 (seconds)
        commands:
          - <command #1>

step Types

sleep

Useful to pause for a pause-period the execution before moving to the next step

config:
  steps:
    - sleep:
        name: sleep seconds specified in the seconds attribute
        seconds: 5
    - sleep:
        name: sleep of 5 seconds specified in the name attribute

Attributes:

  • name(str): Arbitrary identifier for this step. can include the pause period
    as integer value. Mandatory
  • seconds(int/float): Specifies the value in seconds to be paused. Optionally in case
    that the pause period is provided with the name attribute, otherwise mandatory.

break

Useful to pause the execution and wait for operator confirmation before moving to the next step

config:
  steps:
    - break:
        name: hit return to proceed
    - break:
        name: break for status analyses
        display: hit return to proceed

Attributes:

  • name(str): Arbitrary identifier for this step. Mandatory
  • display(str): Specifies the value in seconds to be paused. Optionally in case
    that the pause period is provided with the name attribute, otherwise mandatory.
  • optionally any arbitrary attribute which is processed by output rendering

comment

Useful to attach local config files respective local images to PDF output. Include additional information to the output, which then can be post-processed by the rendering engine. Remote config files must be copied first via copy to the local machine.

config:
  steps:
  - comment:
      name: comment test - inlude log file as text
      description : |
        description for comment, the box below is the recorded log.
      inludeTextFileInListingBox: wiki-static-logs/uname.log
                                  # parent search directory is TA/results_IPLS
      result: pass    #pass/fail/skip
  - comment:
      name: comment test - inlude log file as attachment
      description : |
        description for comment, attached the recorded log.
      attachFile: ../../wiki-static-logs/uname.log
      result: pass    #pass/fail/skip
  - comment:
      name: comment test
      description : |
        description for comment, smiley as pdf
      inludeImageFromFile: wiki-static-logs/smiley
                           # parent search directory is ../../ which is TA/results_IPLS
                           # image must be present in .pdf or .eps format
      result: pass    #pass/fail/skip

Warning

the relative directory to the PDF output directory. please specify absolute path.

record

executes a list (with one ore more list-elements) of CLI commands and records the output of CLI commands

config:
  steps:
    - record:
        name: unixDUT - print a test message
        commands:
          - echo "Hello World"

Attributes:

  • name(str): Arbitrary identifier for this step. Mandatory
  • startInBackground(boolean): Defines whether the test-step is scheduled in the foreground or background. Optional, Default is false
  • randomStartDelay(int;float): Provides the functionality to delay the start of the step with a variable timer. the theoretical
    minimal delay is 0, the theoretical maximum delay is value provided with this attribute. Only applicable for background steps.
  • Optionally any arbitrary attribute which is processed by adaptors, analyzers or rendering. Please refer to the sections below

config

function is similar to record, only that the PDF template engine processed the out of the config stepType differently from the record stepType. Might be deprecated in the future.

Note

not advised to be used - please use instead:

  • check step if the result of config change can be verified. (e.g. commit result, REST response code)
  • record step (with attributes to control the insertion in the PDF document)

check

Command interaction with the DUT is similar to record, in addition the output from the DUT is analyzed with an analyzer function.

config:
  steps:
    - check:
        name: unixDUT - print a test message
        commands:
          - echo "Hello World"
        textFSMOneLine: '(.*Hello World.*) 1'

Attributes:

  • name(str): Arbitrary identifier for this step. Mandatory
  • startInBackground(boolean): Defines whether the test-step is scheduled in the foreground or background. Optional, Default is false
  • randomStartDelay(int;float): Provides the functionality to delay the start of the step with a variable timer. the theoretical
    minimal delay is 0, the theoretical maximum delay is value provided with this attribute. Only applicable for background steps.
  • checkMethod(str): Specifies the analyzer Optional. Supported options are:
    • textfsm (default)
    • exact
    • xpath
    • json
    • regex (not recommended to use)
  • checkCommandOffsetFromLastCommand(int) Used for checking intermediate commands for a list of multiple commands. Optional. Default is the last command
  • Optionally any arbitrary attribute which is processed by adaptors, analyzers or rendering. Please refer to the sections below

await

similar to check, however in cases of negative analyzer responses the command series is repetitive executed until either the analyzer response is positive respectively the give-up-timer period is exceeded.

config:
  steps:
    - await:
        name: "DUT - await that BGP neighbor is established"
        give-up-timer: 40
        command-repetition-timer: 5
        commands:
          - "show bgp neighbor 10.1.1.1"
        textFSM: |
          Value Required P0 ( *Type\: External *State\: Established.*)

          Start
            ^${P0} -> Record

          End
        checkResultCount: 1

Attributes:

  • name(str): Arbitrary identifier for this step. Mandatory
  • startInBackground(boolean): Defines whether the test-step is scheduled in the foreground or background. Optional, Default is false
  • randomStartDelay(int;float): Provides the functionality to delay the start of the step with a variable timer. the theoretical
    minimal delay is 0, the theoretical maximum delay is value provided with this attribute. Only applicable for background steps.
  • checkCommandOffsetFromLastCommand(int) Used for checking intermediate commands for a list of multiple commands. Optional. Default is the last command
  • give-up-timer (int): Specifies the period in which a positive response is tried.
  • command-repetition-timer (int): Sets the frequency interval in seconds for command repetition. Optional. Default is 1 second.
  • Optionally any arbitrary attribute which is processed by adaptors, analyzers or rendering. Please refer to the sections below

copy

currently sftp is supported.

config:
  steps:
    - copy:
        name: unixDUT - copy a file from the device
        direction: get
        remote: /tmp/tmpCopyFile
    - copy:
        name: unixDUT - copy a file to the device
        direction: put
        local: tmpCopyFile

Attributes:

  • please refer for sftp login commands to the ssh adapter.
  • direction (str): Direction of the filetransfer. Mandatory, “put” or “get” are the options.
  • local (str): defines the local filename. Mandatory for get direction.
  • remote (str): defines the remote filename. Mandatory for put direction.

Warning

The target file name is currently identical with the source filename. There is the option in asyncssh to store the file under a different name. Might be added in the future.

Warning

currently only sFTP is supported. ftp might be added in the future.

Adapters

following adapters are supported to connect to network devices:

  • aioLocalShellConnect - provides access to the local shell
  • aioSshConnect - an SSH adapter for router interactions
  • aioTelnetConnect - a telnet adapter for router interactions
  • aioRtbRestConnect - a REST adaptor for rtbrick devices
  • aioSnmpConnect - a SNMPv2c and SMPv3 adaptor, based on pysnmp

Note

following adapters will be added in the future:

  • netconfConnect
  • snmpTrapReceiver

aioLocalShellConnect

adapter used for executing commands in a local shell. Please note that each command is executed in its own shell. Please use the concatenation with semicolon to execute all sub-commands in one single shell.

example

  • YAML config file step definitions:
config:
  hostfiles:
    - ./testHosts.yml
  steps:
    - record:
        name: mySystem - ls of . and ls of ..
        commands:
          - "ls"
          - "cd ..; ls"
  • hostfile definitions:
mySystem: {'device': 'local-shell','vendor':'local-shell', 'method':'local-shell' }
  • YAML dump of an enriched stepDict output dict (last step after execution)
    - commandCount: 1
      commandOrig: ls
      device: local-shell
      elapsed: '0:00:00.018422'
      elapsedRaw: 0.018422
      endTS: '2018-01-21 22:53:19.512726'
      loopCounter: 1
      output: 'host.yml
        myLog.log
        test.yml
        test_aioRtBRickRest_restDummy.py
        test_aioRunbook.py
        test_aioSftp_Juniper.py
        test_aioSshConnect.py
        test_aioSshConnect_Cisco.py
        test_aioSshConnect_Juniper.py
        test_aioSshConnect_Ubuntu.py
        test_aioTelnetConnect_Cisco.py
        test_aioTelnetConnect_Juniper.py'
      pass: true
      skip: false
      startTS: '2018-01-21 22:53:19.494304'
      stepCount: 0
      stepType: record
    - commandCount: 2
      commandOrig: cd ..; ls
      device: local-shell
      elapsed: '0:00:00.017580'
      elapsedRaw: 0.01758
      endTS: '2018-01-21 22:53:19.530385'
      loopCounter: 1
      output: '__pycache__
        adaptors
        aioRunbook.py
        analyzers
        archive
        devTests
        docs
        fileTransfer.py
        myLog.log
        runbook.py
        runbooks
        testHosts.yml
        tests
        tools'
      pass: true
      skip: false
      startTS: '2018-01-21 22:53:19.512805'
      stepCount: 0
      stepType: record
    stepCounter: 0
    vendor: local-shell
valueMatrix:

aioSshConnect

adapter used for ssh device access:

example Junos

  • YAML config file step definitions:
config:
  hostfiles:
    - ./testHosts.yml
  steps:
    - check:
        name: "myMX - show version"
        commands:
          - config
          - set system host-name myMX
          - commit and-quit
        textFSMOneLine: '(.*commit complete.*) 1'
    - check:
        name: "myMX - show version"
        commands:
          - show version | match Hostname
        textFSMOneLine: '(.*Hostname\: myMX.*) 1'
  • hostfile definitions:
myMX:  {'device': '192.168.56.11','method':'ssh','vendor':'juniper','password': 'admin1', 'user': 'admin'}
  • YAML dump of an enriched stepDict output dict (last step after execution)
checkResultCount: 1
commands:
- show version | match Hostname
device: 192.168.56.11
hostname: myMX
method: ssh
name: myMX - show version
output:
- checkResult:
  - - 'Hostname: myMX'
  commandCount: 1
  commandOrig: show version | match Hostname
  device: 192.168.56.11
  elapsed: '0:00:00.012352'
  elapsedRaw: 0.012352
  endTS: '2017-12-31 08:04:24.343667'
  loopCounter: 1
  output: '

    Hostname: myMX

    '
  pass: true
  skip: false
  startTS: '2017-12-31 08:04:24.331315'
  stepCount: 1
  stepType: check
password: admin1
textFSM: "Value Required P0 (.*Hostname\\: myMX.*)\n\nStart\n  ^${P0} -> Record\n\n\
  End"
textFSMOneLine: '(.*Hostname\: myMX.*) 1'
user: admin
vendor: juniper

example IOS-XR

  • YAML config file step definitions:
- check:
    name: "myXR - set hostname"
    commands:
      - conf
      - hostname myXR
      - commit
      - exit
    textFSMOneLine: '(.*error.*) 0'
- check:
    name: "myXR - verify hostname setting"
    commands:
      - show running | incl hostname
    textFSMOneLine: '(.*myXR.*) 1'
  • hostfile definitions:
myXR:  {'device': '192.168.56.31','method':'ssh','vendor':'cisco','password': 'cisco', 'user': 'cisco'}
  • YAML dump of an enriched stepDict output dict (last step after execution)
checkResultCount: 1
commands:
- show running | incl hostname
device: 192.168.56.31
hostname: myXR
method: ssh
name: myXR - verify hostname setting
output:
- checkResult:
  - - hostname myXR
  commandCount: 1
  commandOrig: show running | incl hostname
  device: 192.168.56.31
  elapsed: '0:00:00.160972'
  elapsedRaw: 0.160972
  endTS: '2017-12-31 07:40:23.680625'
  loopCounter: 1
  output: "\rSun Dec 31 07:40:22.514 UTC\nBuilding configuration...\nhostname myXR"
  pass: true
  skip: false
  startTS: '2017-12-31 07:40:23.519653'
  stepCount: 1
  stepType: check
password: cisco
textFSM: "Value Required P0 (.*myXR.*)\n\nStart\n  ^${P0} -> Record\n\nEnd"
textFSMOneLine: (.*myXR.*) 1
user: cisco
vendor: cisco

aioTelnetConnect

adapter used for telnet device access. ported from telnetlib to asyncio

shall behave similar to the ssh adapter

aioRtbRestConnect

This adapter controls the interactions with rtbrick routers.

example rtbrick

  • YAML config file step definitions:
config:
  hostfiles:
    - ./testHosts.yml
  steps:
    - record:
        name: "myRtBrickDevice - just a request"
        commands:
          - 'set global.interface.logical.config interface_name lo-0/0/0/0/10 interface_description VRF1-lo-10 instance VRF1'
        includeRequestInOutput: true
myRtBrickDevice:  {'device': '127.0.0.1','port' : 8080, 'method':'rest','vendor':'rtbrick'}
  • YAML dump of an enriched stepDict output dict (last step after execution). Please note that the output string is itself an YaML formatted string, which is cluttered in this dump. However this string can be parsed as YAML string in the post processing.
commands:
- set global.interface.logical.config interface_name lo-0/0/0/0/10 interface_description
  VRF1-lo-10 instance VRF1
device: 127.0.0.1
hostname: restDummy
includeRequestInOutput: true
jsonOneLine: '["returnCode"] == 200'
method: rest
name: restDummy - set logical loopback for VRF1
output:
- commandCount: 1
  commandOrig: set global.interface.logical.config interface_name lo-0/0/0/0/10 interface_description
    VRF1-lo-10 instance VRF1
  device: 127.0.0.1
  elapsed: '0:00:00.008215'
  elapsedRaw: 0.008215
  endTS: '2017-12-31 08:30:13.775593'
  loopCounter: 1
  output: "header:\n  BDS-Objects-added: '1'\n  Content-Length: '2'\n  Content-Type:\
    \ application/json; charset=utf-8\n  Date: Sun, 31 Dec 2017 07:30:13 GMT\n  Server:\
    \ Python/3.5 aiohttp/2.3.6\njson: {}\nrequest:\n  command: POST\n  json:\n   \
    \ objects:\n    - attribute:\n        instance: VRF1\n        interface_description:\
    \ VRF1-lo-10\n        interface_name: lo-0/0/0/0/10\n    table:\n      table_name:\
    \ global.interface.logical.config\n  url: http://127.0.0.1:8080/bds/object/add\n\
    returnCode: 200\n"
  pass: true
  skip: false
  startTS: '2017-12-31 08:30:13.767378'
  stepCount: 0
  stepType: record
port: 8080
vendor: rtbrick

aioSnmpConnect

an adAdapter to send SNMPv2c and SNMPv3 requests. based on underlying pysnmp framework.

example snmp v2

  • YAML config file step definitions:
config:
  hostfiles:
    - ./host.yml
  steps:
    - record:
        name: "DUT - get SNMP OID "
        startInBackground: true
        blockingAdapter: true
        commands:
          - get iso.3.6.1.2.1.1.1.0
"DUT:  {'device': '192.168.56.11','port':'161','method':'snmp','community': 'COMMUNITY1', 'version': 'v2c'}"

aioNetconfConnect

an adAdapter to send netconf requests via the ncclient framework

example Junos

  • YAML config file step definitions:
config:
  hostfiles:
    - ./host.yml
  steps:
    - record:
        name: "DUT - this is a test for get_netconf config"
        commands:
          - get_config running
          - get_chassis_inventory
"DUT:  {'device': '192.168.56.11','port':'830','method':'netconf', 'ncclientVendor': 'junos', 'password': 'admin1', 'user': 'admin'}"

Analyzers

following analyzers are supported to connect to network devices:

  • textFsmCheck for checking CLI output in text format.
  • jsonCheck for checking data in YAML or JSON notion.

Warning

following analyzers need to be ported:

  • xpathCheck

textFsmCheck

textFsmCheck is a wrapper for TextFSM. TextFSM is a Python module which implements a template based state machine for parsing semi-formatted text. Originally developed to allow programmatic access to information returned from the command line interface (CLI) of networking devices. The TextFSM engine takes two inputs - a template file (which provided with the stepDict), and text input (which is provided from command responses from CLI-commands from of a adaptor towards a device) and returns a list of records that contains the data parsed from the text. TextFSM was developed internally at Google and released on github under the Apache 2.0 licence for the benefit of the wider community.

Once the textFSM returns the list of records (matrix), this matrix can be analysed with following algorithms:

  • checkResultCount <int>: Just compares the provided number with the number of textFSM records
  • evalResultCount <string>: Evaluates the provided python formula with the number of
    textFSM records, which is represented as list [] in the python formula. Useful if the exact number of matches can vary: e.g “[] < 0”
  • evalListElement <string>: Used for textFSM, json and xpath. Evaluates the actually
    value of matched field in with python expression. The field is represented as list element in the python formula. Useful for numeric range evaluations: e.g. “500 <= [0]”

textFSMOneLine ex.

The textFSMOneLine provides the means to specify a textFSM template in a single line. during execution of the verification, the contents of a single line are exploded to the textFSM template. textFSMOneLine has the restriction that is supports only on parameter.

  • example YAML config file step definitions:
config:
  steps:
    - check:
            name: "DUT1 - check the ISIS Metric of 100 is applied on interface to DUT2"
            commands:
              - show isis interface xe-1/0/0.0 extensive
            textFSMOneLine: "(.*Metric\\: 100) 1"
  • extracted code segment, which does the exploding.
if "textFSMOneLine" in stepDict.keys():
    parameterString = " ".join(stepDict["textFSMOneLine"].split(" ")[:-1])
    stepDict["textFSM"] = "Value Required P0 {}\n\nStart\n".format(parameterString) + "  ^${P0} -> Record\n\nEnd"
    stepDict["checkResultCount"] = int((stepDict["textFSMOneLine"].split(" ")[-1]))

Note

it might be useful to modify the the code, so that also evalResultCount and evalListElement are suported

simple textFSM ex.

The textFSM multiline string attribute represents the complete textFSM template.

  • example YAML config file definition of a simple textFSM template:
config:
  steps:
    - check:
        name: "DUT1 - check that route 10.19.36.0 with RT 65535:1136 is sent to DUT2"
        commands:
          - "show route advertising-protocol bgp 10.20.2.1 community target:65535:1136 rd-prefix 10.20.2.2:1136:10.19.36.0/24"
        textFSM: |
          Value Required P0 (.*10.20.2.2:1136\:10.19.36.0/24.*)

          Start
            ^${P0} -> Record

          End
        checkResultCount: 1

complex textFSM ex.

The textFSM multiline string attribute represents the complete textFSM template.

  • example YAML config file definition of a more complex textFSM template:
config:
  steps:
    - check:
        name: "DUT1 - check that show-system-switch shows correct state"
        description: |
          The command show-status-switch must return following lines:\\
          Graceful switchover: On\\
          Configuration database: Ready\\
          Kernel database: Ready\\
          Switchover Status: Ready\\
        commands:
          - show system switch
        textFSM: |
          Value P0 (.*Graceful switchover\: On.*)
          Value P1 (.*Configuration database\: Ready.*)
          Value P2 (.*Kernel database\: (Ready|Synchronizing).*)
          Value P3 (.*(Switchover Status|Peer state)\: (Ready|Steady State).*)

          Start
            ^${P0} -> Record
            ^${P1} -> Record
            ^${P2} -> Record
            ^${P3} -> Record

          End
        checkResultCount: 4

jsonCheck

verification method for returned data structures (e.g. REST response, Juniper JSON formatted CLI response)

  • jsonOneLine string: provides a comprehensive method to validate a single data element
    in the json datatructure. Currently the == operator is supported. Additional operators could be added on demand.
  • jsonMultiLine list of strings provides a comprehensive method to validate multiple
    data elements in the json datatructure. All valiations are must succeed. If a single validations fails, the check test is marked as failed.
  • checkMethod json: provides a disaggregated method.
    • jsonString provides the json validation string in python syntax: e.g. [“software-information”][0][“junos-version”][0][“data”]
    • evalValue & evaluates the value of the jsonmatch in an python expression. Useful for string verifications: “{} == ‘16.1R4-S4.3’”

jsonOneLine ex.

  • example YAML config file definition of a jsonOneLine statement:
config:
  steps:
    - check:
        name: "restDummy - set logical loopback for VRF1"
        commands:
          - 'set global.interface.logical.config interface_name lo-0/0/0/0/10 interface_description VRF1-lo-10 instance VRF1'
        jsonOneLine: '["returnCode"] == 200'

jsonMultiLine ex.

  • example YAML config file definition of a jsonMultiLine statement:
config:
  steps:
    - check:
        name: "restDummy - set logical loopback for VRF1"
        commands:
          - 'get VRF1.bgp.rib-in.ipv6.unicast.2003:4:f022:ff00::1.2003:4:f022:ff00::3 prefix6 2049:10:6::/48 recv_path_id 0'
        jsonMultiLine:
          -  '["returnCode"] == 200'
          -  '['json']['objects'][0]['attribute']['community'] == ["3320:1276", "3320:2010", "3320:9010", "65535:65284"]'
          -  '['json']['objects'][0]['attribute']["as_path"] == [3320, 4910, 4910, 4910, 64512, 4910, 65535]'

jsonString ex.

  • older specification for json checks, might be deprecated in the future:
config:
  steps:
    - check:
        name: "restDummy - set logical loopback for VRF1"
        commands:
          - 'set global.interface.logical.config interface_name lo-0/0/0/0/10 interface_description VRF1-lo-10 instance VRF1'
        checkMethod: json
        jsonString: '["returnCode"]'
        evalListElement: '[0] == 200'

diffCheck

This analyzer compares the current output with previously recorded outputs(snapshots), respectively with corresponding outputs of previous steps or loops. Recorded snapshots can be stored either in clear-text or zipped format.

_images/diffCheck.jpg

Note

relative diffChecks to steps and loops will be added in future.

diffSnapshot

setting the diffSnapshot requires a flag in the execSteps function and a subsequent function call:

myRunbook = aioRunbookScheduler("test.yml")
loop = asyncio.get_event_loop()
loop.run_until_complete(myRunbook.execSteps(loop,setDiffSnapshot = True))
myRunbook.writeDiffSnapshotToFile()

diffSnapshot is the default diff source, diffSource attribute can be omitted in this case

config:
  steps:
    - check:
        name: record test local-shell
        method: local-shell
        device: local-shell
        vendor: local-shell
        commands:
          - 'pip3 freeze | tail -n 3'
        checkMethod: diff
diffSnapshot:
  created: '2018-02-18 11:45:48.086652'
  loop_1_step_1:
  - pysnmp==4.4.4
  - PyYAML==3.12
  - yarl==1.1.0

outputFromStep

config:
  steps:
    - record:
        name: record test local-shell
        method: local-shell
        commands:
          - 'pip3 freeze'
    - check:
        name: comapre againt previous step
        method: local-shell
        commands:
          - 'pip3 freeze'
        checkMethod: diff
        diffSource: outputFromStep 1

previousLoop

config:
  loops: 2
  steps:
    - check:
        name: check previous step
        method: local-shell
        commands:
          - 'pip3 freeze'
        checkMethod: diff
        diffSource: previousLoop

diffTextFSMFilter

To filter important lines and exclude dynamic lines (timers, counters … ) the text output can be filtered by use of an textFSM filter. This is a white-list mechanism.

The diffTextFSMFilter can be applied to all three diffSources: diffSnapshot, outputFromStep and previousLoop

the example below filters pip3 packages which have the character “y” in the name.

config:
  steps:
    - check:
        name: record test local-shell
        method: local-shell
        device: local-shell
        vendor: local-shell
        commands:
          - 'pip3 freeze | tail -n 3'
        checkMethod: diff
        diffTextFSMFilter: |
          Value P0 (.*y.*)

          Start
            ^${P0} -> Record

          End
diffSnapshot:
  created: '2018-02-18 11:45:48.086652'
  loop_1_step_1:
  - PyYAML==3.12
  - yarl==1.1.0

postProcessing

aioPdfRender

This class provides the functionality to write the test results to PDF, by using Latex/Jinja2 templates. It is the responsibility of the users to create customized templates and the to use the the Jinja2 Variables to gather the results of the runbook execution. An example is found in the tests folder.

myRunbook = aioRunbookScheduler("test.yml")
loop = asyncio.get_event_loop()
loop.run_until_complete(myRunbook.execSteps(loop))

threadExecutor = concurrent.futures.ThreadPoolExecutor(max_workers=3,)
myAioPdfRender = aioPdfRender(myRunbook.configDict,{},True)
loop.run_until_complete( myAioPdfRender.writePdfFile(threadExecutor))