Skip to content

Improvements from user feedback #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
test:
mkdir -p .pyATS
docker run -it --rm -v $(PWD):/tests ciscotestautomation/pyats bash /tests/run_tests.sh
docker run -it -e PYATS_USERNAME -e PYATS_PASSWORD -e PYATS_AUTH_PASS --rm -v $(PWD):/tests ciscotestautomation/pyats bash /tests/run_tests.sh
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

# Introduction

This repo provides a demonstration of Cisco pyATS with a NetDevOps style workflow.
This repo contains a full test script in pyATS with local libraries that connects to a
testbed of IOS devices, and runs various test cases that parses command outputs,
collects router information, and report them in log.

# Requirements

* Python 2.7
* Python 3.x
* Docker

# Workflow
Expand All @@ -20,7 +22,7 @@ export VIRL_HOST=myvirlserver.change.me
### Clone this repo / create virtualenv / install requirements

```
git clone https://github.com/kecorbin/pyats-ios-sample
git clone https://github.com/CiscoDevNet/pyats-ios-sample
cd pyats-ios-sample
virtualenv venv
source venv/bin/activate
Expand Down Expand Up @@ -76,7 +78,31 @@ virl generate pyats
```


### Verify
#### Testbed Credentials

The generated testbed will look to the execution environment to provide credentials, make
sure these are set in either your local environment, or docker container.

```
tacacs:
username: "%ENV{PYATS_USERNAME}"
passwords:
tacacs: "%ENV{PYATS_PASSWORD}"
enable: "%ENV{PYATS_AUTH_PASS}"
line: "%ENV{PYATS_PASSWORD}"

```

You can set these like such


```
export PYATS_USERNAME=cisco
export PYATS_PASSWORD=cisco
export PYATS_AUTH_PASS=cisco
```

### Test Cases

Launch pyATS test suite

Expand Down Expand Up @@ -128,11 +154,7 @@ make test

```

# pyATS details

This repo contains a full test script in pyATS with local libraries that connects to a
testbed of IOS devices, and runs various test cases that parses command outputs,
collects router information, and report them in log.

## General Information

Expand Down Expand Up @@ -164,6 +186,8 @@ demonstration purposes.
| | Gig0/1 <-> Gig0/1 | |
+-------------+ +-------------+

```

## Testing

This script performs the following tests for demonstration purposes.
Expand Down Expand Up @@ -192,4 +216,4 @@ $ easypy pyats_ios_example_job.py -testbed_file pyats_ios_example.yaml

References:
For the complete and up-to-date user guide on pyATS, visit:
https://developer.cisco.com/site/pyats/docs/
https://developer.cisco.com/site/pyats/docs/
79 changes: 79 additions & 0 deletions pyats-intro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import sys
from IPython import embed
import logging
import unicon

from genie.conf import Genie
from ats.topology import loader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is intended for pyats v5.0+ -> all the imports should reflect

from pyats.topology instead... but this is a minor comment

from genie.abstract import Lookup
from genie.libs import ops # noqa





if __name__ == '__main__':

# local imports
import argparse

parser = argparse.ArgumentParser(description="standalone parser")
parser.add_argument('--testbed', default='./default_testbed.yaml',
dest='testbed', type=loader.load)
parser.add_argument('--device', dest='device_name')

args, unknown = parser.parse_known_args()

device_name = args.device_name

# pyats testbed != genie testbed
genie_testbed = Genie.init(args.testbed)

# this gives us device_name as Device Object e.g dist1
vars()[device_name] = genie_testbed.devices[device_name]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the more appropriate way would be to do locals()

but then again - device_name could have a dash, then it's an inappropriate variable name.

i would suggest to not show this part. it will definitely confuse novice users

# or we can also just use `device`
device = vars()[device_name]

# logger = logging.getLogger("UNICON")
# unicon.logs.remove_stream_handler(logging)

# connect to the device (quietly)
import time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports on top or on top of if name == 'main' is good practise imo

print("pyATS and Genie are about to profile the device and create structured data for "
"you to use")
time.sleep(10)
device.connect()


# work with an abstracted device model
abstract = Lookup.from_device(device)

# interface info is always a good place to start..
interfaces = abstract.ops.interface.interface.Interface(device)
interfaces.learn()

print("""

Welcome to the device object tutorial, as you may have noticed, Genie was
busy profiling all of the interfaces of your device. you can access them via

interfaces.info

you can interact with your device using either `device` or the device name
you specified with --device

You can start by exploring some of the common operations available for the
device by typing

dir(device)

you can also explore the rest of the genie models at:

https://pubhub.devnetcloud.com/media/pyats-packages/docs/genie/genie_libs/#/models

Enjoy!

""")


embed()
34 changes: 20 additions & 14 deletions pyats_ios_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

Topology:

+-------------+ Eth0/0 <-> Eth0/0 +-------------+
+-------------+ GigabitEthernet0/1 +-------------+
| | ------------------------------------ | |
| ios1 | | ios2 |
| | ------------------------------------ | |
+-------------+ Eth0/1 <-> Eth0/1 +-------------+
| | | |
+-------------+ +-------------+

Testing:
This script performs the following tests for demonstration purposes.
Expand All @@ -34,10 +34,7 @@
counts.

- execute `show ip interface brief` command: basic command execution and
data parsing; extract all
ethernet and serial
interfaces; logs number of
interface counts.
data parsing;

- verify ethernet and serial interface counts from above commands.

Expand Down Expand Up @@ -114,12 +111,21 @@ def check_topology(self,
# add them to testscript parameters
self.parent.parameters.update(ios1 = ios1, ios2 = ios2)

# get corresponding links
# verify at least one interface between two devices
links = ios1.find_links(ios2)
assert len(links) >= 1, 'require one link between ios1 and ios2'

# save link as uut link parameter
self.parent.parameters['uut_link'] = links.pop()
# Select an Interface by Name, for use cases like new link turnup,
# this could also be provided as an argument to a script
link = ios1.interfaces['GigabitEthernet0/1']

# the parameters here are fairly arbitary, but are useful for
# passing some data between tests, so that additional things
# can be done.g IP lookup / ping / counters
self.parent.parameters['uut_link'] = link

# let someone know
logger.info("Using UUT Link: {}".format(link))


@aetest.subsection
Expand Down Expand Up @@ -166,12 +172,12 @@ class PingTestcase(aetest.Testcase):
@aetest.setup
def setup(self, uut_link):
destination = []
for intf in uut_link.interfaces:
destination.append(str(intf.ipv4.ip))

# apply loop to next section
aetest.loop.mark(self.ping, destination = destination)
# get the IP address of the link being tested
destination.append(str(uut_link.ipv4.ip))

# apply loop to next section `ping destination from these/all devices`
aetest.loop.mark(self.ping, destination=destination)

@aetest.test
def ping(self, device, destination):
Expand Down
47 changes: 25 additions & 22 deletions pyats_ios_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,59 @@ testbed:
name: pyATS_IOS_Example_Testbed
devices:
ios1:
os: iosxe
connections:
defaults:
class: 'unicon.Unicon'
a:
a:
protocol: telnet
ip: localhost
port: 11023
passwords:
enable: lab
line: lab
tacacs: lab
tacacs:
username: lab
ip: 10.94.241.240
tacacs:
username: cisco
passwords:
tacacs: cisco
type: ios
custom:
abstraction:
order: [os, type]

ios2:
os: iosxe
connections:
defaults:
class: 'unicon.Unicon'
a:
a:
protocol: telnet
ip: localhost
port: 11024
passwords:
enable: lab
line: lab
tacacs: lab
tacacs:
username: lab
ip: 10.94.241.239
tacacs:
username: cisco
passwords:
tacacs: cisco
type: ios
custom:
abstraction:
order: [os, type]
topology:
ios1:
interfaces:
FastEthernet0/0:
GigabitEthernet0/1:
ipv4: 10.10.10.1/24
ipv6: '10:10:10::1/64'
link: n1
type: ethernet
Loopback0:
Loopback0:
ipv4: 192.168.0.1/32
ipv6: '192::1/128'
link: ios1_Loopback0
type: loopback
ios2:
interfaces:
FastEthernet0/0:
GigabitEthernet0/1:
ipv4: 10.10.10.2/24
ipv6: '10:10:10::2/64'
link: n1
type: ethernet
Loopback0:
Loopback0:
ipv4: 192.168.0.2/32
ipv6: '192::2/128'
link: ios2_Loopback0
Expand Down
Loading