I’ve implemented some new changes to pyMultiChange and netlib. The biggest change affects both netlib and pyMultiChange. In netlib, I ripped out both the ‘simple_creds’ and ‘simple_yaml’ methods, as both stored user credentials in plain text on the computer that you used them on.

Instead, utilizing the ‘keyring’ python module, I created a new class called ‘KeyRing’. Using ‘KeyRing’ is simple. It implements three methods. These methods are ‘get_creds’, ‘set_creds’, and ‘del_creds’.

‘get_creds’ will attempt to retrieve the credentials for a username, if the credentials exist. If they do not exist, ‘get_creds’ will automatically call the ‘set_creds’ method.

1

2

3

4

5

6

7

8

9

10

11

&gt;&gt;&gt;from netlib.user_keyring import KeyRing

&gt;&gt;&gt;creds=KeyRing(username='testuser')

&gt;&gt;&gt;creds.get_creds()

No credentials keyring exist.Creating newcredentials.

Enter your user password:

Confirm your user password:

Enter your enable password:

Confirm your enable password:

&gt;&gt;&gt;print creds.get_creds()

{'username':'testuser','enable':u'test','password':u'testpass'}

&gt;&gt;&gt;

‘set_creds’ does exactly what it sounds like. It allows you to set your credentials. If no user credentials exist, it creates a new keyring. However, if user credentials do exist, it over-writes the credentials.

1

2

3

4

5

6

7

&gt;&gt;&gt;creds.set_creds()

Enter your user password:

Confirm your user password:

Enter your enable password:

Confirm your enable password:

&gt;&gt;&gt;print creds.get_creds()

{'username':'testuser','enable':u'enable123','password':u'test123'}

Finally, ‘del_creds’ deletes a user’s credentials from an existing keyring.

1

2

3

&gt;&gt;&gt;creds.del_creds()

Enter your user password:

Deleting keyring credentials fortestuser

The ‘keyring‘ python library utilizes your operating systems native methods for storing passwords. For Example, in Mac OS X, it will utilize the KeyChain functionality, In Linux, it will use dbus, and in Microsoft Windows, it utilizes the Credential Vault. There are also methods available for you to create your own backend. Much more secure than the ~/.tacacslogin or ~/.tacacs.yml files that used to be created from the old methods.

As expected, this change was implemented into pyMultiChange. Doing so, required that new command line arguments needed to be implemented. There are actually several new command line arguments, since the last time that I wrote about pyMultiChange. I’ll go over them here.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

usage:multi_change.py[-h]-uUSERNAME[--delete-creds[DELETE_CREDS]]

[--set-creds[SET_CREDS]][-dDEVICES][-cCOMMANDS]

[-s[SSH]][-t[TELNET]][-o[OUTPUT]][-v[VERBOSE]]

[--delay DELAY][--buffer BUFFER]

[--threaded[THREADED]][-mMAXTHREADS]

Managing network devices with python

optional arguments:

-h,--help show thishelp message andexit

-uUSERNAME,--username USERNAME

Specify your username.

--delete-creds[DELETE_CREDS]

Delete credentials from keyring.

--set-creds[SET_CREDS]

set keyring credentials.

-dDEVICES,--devices DEVICES

Specifiesahost file

-cCOMMANDS,--commands COMMANDS

Specifiesacommands file

-s[SSH],--ssh[SSH]

Default:Usethe SSH protocol

-t[TELNET],--telnet[TELNET]

Usethe Telnet protocol

-o[OUTPUT],--output[OUTPUT]

Verbose command output

-v[VERBOSE],--verbose[VERBOSE]

Debug script output

--delay DELAY Change the defaultdelay exec between commands

--buffer BUFFER Change the defaultSSH output buffer

--threaded[THREADED]

Enable process threading

-mMAXTHREADS,--maxthreads MAXTHREADS

Define the maximum number of threads

The first, is that multi_change.py now requires that you specify a username for all actions. This allows multi_change.py to interact with the keyring to set, extract, and delete credentials.

1

2

3

4

5

6

usage:multi_change.py[-h]-uUSERNAME[--delete-creds[DELETE_CREDS]]

[--set-creds[SET_CREDS]][-dDEVICES][-cCOMMANDS]

[-s[SSH]][-t[TELNET]][-o[OUTPUT]][-v[VERBOSE]]

[--delay DELAY][--buffer BUFFER]

[--threaded[THREADED]][-mMAXTHREADS]

multi_change.py:error:argument-u/--username isrequired

When you specify a username, multi_change.py immediately attempts to extract the credentials for the user. If they don’t exist, multi_change.py will prompt you to set the credentials.

1

2

3

4

5

6

$multi_change.py-utestuser

No credentials keyring exist.Creating newcredentials.

Enter your user password:

Confirm your user password:

Enter your enable password:

Confirm your enable password:

You can also utilize the ‘–set-creds’ command line argument to either set credentials for a new user or over-write the credentials for an existing user.

1

2

3

4

5

$multi_change.py-utestuser--set-creds

Enter your user password:

Confirm your user password:

Enter your enable password:

Confirm your enable password:

Like wise, you can use the ‘–delete-creds’ to delete existing creds.

1

2

3

$multi_change.py-utestuser--delete-creds

Enter your user password:

Deleting keyring credentials fortestuser

Beyond that, the option to utilize threading was created. With the threading ability, you also get the ability to specify the number of threads and the delay factor between command execution.

For example, the below series of command line arguments enable threading, utilizing 50 threads, create a delay factor of 5 seconds, and will display the command output.

This is very handy for running a common command set across a large number of devices very quickly.

Beyond that, there are a few under the hood enhancements.

Protocol failover will no longer happen. Meaning that if a device fails to login via SSH, it will no longer failover to attempt to login via telnet and vise versa.

Login failures are logged in a file called ‘failure.log’. This file is created in the local folder that you’re running ‘multi_change.py’ in.

multi_change.py will now only read the commands file once, rather than reading it for every device that it attempts to make changes on.

pyMultiChange and netlib are now python installable packages. Meaning that you can run their ‘setup.py’ files and they will be installed as native python packages, allowing them to be called from anywhere on the OS.

In the previous blog, I kicked the tires on the ios_command and ios_config Ansible modules. I still had my development environment set up from then, so I decided that I wanted to kick the tires on the ios_template module.

The online documentation currently has several errors, with the module documentation in the same state, which is undesirable. However, after some experimentation, I feel that I can adequately describe what the module does.

You feed the module a candidate configuration for a device, the module will then reach out to the device, pull its current running configuration, compare the running configuration to the candidate configuration, determine what configuration needs to be added to the device based upon the comparison, then add the configuration to the device.

I’ve noted two caveats with this module. First, it will not negate any commands, so if you update your configuration template to remove a swath of configuration, the module will not make those changes. Second, the module isn’t intelligent enough to determine the risk associated with a command, thus it’s unable to take preemptive actions, such as bleeding traffic from a link or device.

With that in mind, let’s get to the playbook.

Here is what my playbook looks like:

YAML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

---

- hosts: ios

gather_facts: no

connection: local

tasks:

- name: OBTAIN LOGIN CREDENTIALS

include_vars: secrets.yaml

- name: DEFINE PROVIDER

set_fact:

provider:

host: "{{inventory_hostname}}"

username: "{{creds['username']}}"

password: "{{creds['password']}}"

auth_pass: "{{creds['auth_pass']}}"

- name: TEST IOS_TEMPLATE

ios_template:

provider: "{{provider}}"

authorize: true

backup: true

src: "{{inventory_hostname}}.candidate_config.txt"

register: template

- debug: var=template

In this playbook, the src file – “{{ inventory_hostname }}.candidate_config.txt“, which are referenced, contain the running configuration of each of my devices in inventory, with the exception that I’ve added an access-list called TEST. Given what we know of the module, it will compare the running config to the candidate config, determine that the access-list called TEST is missing from the running config, and attempt to add the access-list to the device.

You can see below that there is an access-list at the tail end of both of the configurations.

That is definitely cool! Though, it’s also definitely lacking from a configuration intent perspective. I’m sure that it will improve with time.

Some colleagues and I have been attempting to solve this configuration intent problem. It’s a difficult problem to solve for, but we have a working code base. Soon, I’ll try to write about configuration intent and how we are approaching the problem.