When you setup to learn new things you are bound to get a bit sideways once and awhile. With writing code I think that is even more the case. You start out with an idea and if your lucky like I have been so far it starts to flow and things just work. But in the spirit of learning in manageable chunks and sharing the experience with you I started targeting a single host. When I tried to make the leap to multiple hosts things got interesting. Along with that it became clear that these posts were going to get messy quick. So lets clean all that up and move on to the cool stuff.
Up to this point I have been posting the entire code for what we are talking about as part of the article. I am going to stop doing that from here on out. Code gets big quickly and it makes for messy hard to digest blogposts. As before I will have some dialog, what the goals are, maybe a code snippet then a link to the full code in My GitHub Repository.
In order to get true validation of the code I am writing I have also had to extend my testing base from two lab switches. That is where GNS3 comes in. I have built out a simple 10 node network of 3725 routers that let me validate many IOS commands across a sufficiently large sample of devices to make sure the code is working right. You can find my post on setting up that network here.
Once I had the test network I found myself manually pinging each device to make sure it was reachable prior to running a config script against it. It was just simpler than having to second guess my connectivity every time something went wrong with my code. But I thought to myself “Hey stupid your learning Python to automate things write a program to do it!” So I did, you can my post on that here along with the code.
The final piece of cleaning up, came when I tried to implement a flat file to populate the hosts for the script. Calling a file is not a big challenge. But when setup a for loop is when things got interesting. In Part 1 I used this line:
child.logfile = os.fdopen(sys.stdout.fileno(), 'w', 0)
In order to print the running expect code so we could watch what was going on. If you look at the code here and look at line 28 you will see it. You will see that is comes immediately after we setup child = pexpect.spawn('ssh %s@%s' % (switch_un, switch_ip))
, which is a variable for the SSH session we setup to deliver the configuration. By placing that line after I setup the child variable I am able to use the child variable as part of the output to STDOUT. When I originally got it setup and going I was receiving errors that I was able to eliminate by killing the session on the IOS device. You can see it in line 59 here where it says child.sendline('quit').
What I did not know at the time was that all I had done is mask a real issue.
Thanks to Tyler Christiansen I was able to debug the issue and more importantly find a better way to implement printing my output. So this is where I tell you that you need to have friends who have been doing this for awhile. If you don’t then your going to be left dealing with forums, IRC and blogposts of people with lots of opinions and not always the best answers. The long and short is “File descriptors are tricksy.” from Tyler. I would love to be able to explain why it didn’t work but its beyond me. I was lucky to have Tyler who had already figured out a good way to deal with the issue I I needed to solve so here it is.
# Imports Needed Libraries to run Code
import pexpect
import sys
import os
#
# Creates Variables to be referenced by code
#
switch_un = "pytest"
switch_pw = "pytest"
enable_pw = "password"
vlan = "300"
vlan_name = "TESTVLAN"
switch_port = "GigabitEthernet0/10"
#
#
# Create a logfile
stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# Create Import for Text file that contains Switch IP's
# Read the list to variable switches
# Create a for loop in which switch is the variable derived
# from a line of swtiches
with open('device_file1.txt') as fname:
switches = fname.read().splitlines()
for switch in switches:
#
# This section shows the setup of the SSH session and
# authentication through config T
#
print switch
child = pexpect.spawn('ssh %s@%s' % (switch_un, switch))
# Drop logfile to Standard Out.
child.logfile = stdout
The important bits are in red. With those changes to the overall code for Post 4 works. I am not going to change the code for Post 1 or Post 2 because I want to be able to keep referencing things as I learn more. If I go all 1984 and erase my mistakes then it will be hard for others to come through and learn from them or for me to re-live this if I have to in the future.
Well thats it. Hopefully I have cleaned up enough of my initial mess to get moving again. That said learning to code is one rabbit hole after another. Blogging about it is even worse. But what I am seeing very clearly is that learning to code and all the infrastructure that comes along with it leads to learning lots of other amazing things like Git, Vagrant, Docker and much more. The reason why is if you constantly do all this by hand you will never move forward. I look forward to this journey and dragging you all along.