Discussion:
Python SSHv2 to Cisco routers for multiple commands
Sam Crooks
2009-03-10 16:24:42 UTC
Permalink
Does anyone have any suggestions for SSHv2 to multiple Cisco devices and
(this is the catch) running multiple commands?

I've tried with Paramiko using the SSHClient(), then connect()'ing and
issuing exec_command(), and it seems that the router closes the channel
after the .exec_command('command here'). Subsequent write()'s on the stdin
file object returned do not work, as shown on the examples I have seen:
http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/#more-465


I've tried various examples with twisted.conch.ssh and it seems Twisted is a
bit more low-level than paramiko's SSHClient class.



I'm trying to be able to issue a string of commands to routers which
require a particular sequence;

configure terminal
hostname blahrouter1
ip domain-name x.i.z

ip tftp source-interface lo0
end


copy tftp:/x.y.z.w/my/path/file.txt flash:
Andreux Fort
2009-03-10 16:34:45 UTC
Permalink
Post by Sam Crooks
Does anyone have any suggestions for SSHv2 to multiple Cisco devices and
(this is the catch) running multiple commands?
I've tried with Paramiko using the SSHClient(), then connect()'ing and
issuing exec_command(), and it seems that the router closes the channel
after the .exec_command('command here').  Subsequent write()'s on the stdin
http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/#more-465
Yes. The SSH session you're opening has a lifetime of one command.
If you want to do more than that, you have a couple of options:

1. Don't use exec_command(). Create the connection yourself, like
SSHClient does, and then run in SSH1.5 mode (details escape me now in
this airport, but I've done this).

2. On Juniper, I can do "show foo; show this; show that", which works
just fine with exec_command(). Does that work on a Crisco? (I'm
guessing no, since that's a single command that the juniper splits).
Post by Sam Crooks
I've tried various examples with twisted.conch.ssh and it seems Twisted is a
bit more low-level than paramiko's SSHClient class.
Yes; however, the SSHClient class wraps up a few other things that you
could just do yourself, too. The code isn't that ugly, although it
will raise EOFError on most operations (or socket.timeout in paramiko
1.7), and the docstrings in SSHClient's methods don't say that, so be
aware.
Post by Sam Crooks
I'm trying to  be able to issue a string of commands to routers which
require a particular sequence;
configure terminal
hostname blahrouter1
ip domain-name x.i.z
ip tftp source-interface lo0
end
So you're trying to bootstrap a new router?
You could use the Cisco TFTP/BOOTP/DHCP method of bootstrapping. I
don't think many people really use this, but you may be in luck (i.e.,
it may work on your routers).

For ongoing mainteance:
Put the actual commands in a file, and then just send "copy source
dest" on the router? This is how I do config pushes (it allows you to
copy to a local file on the router first and perform an MD5 checksum,
too. Which I'd recommend so your operators don't want to kill you
:-).
--
Andreux Fort (***@choqolat.org)
Sam Crooks
2009-03-10 17:58:00 UTC
Permalink
I'm not necessary trying to bootstrap a router... that was just a string of
config commands off the top of my head... the point is that they must be
sequential, and must be done ins a single session to make sense...

I thought about the output a file and SCP / TFTP transfer it, but that is a
bit of a kludge/workaround... would like to avoid if possible. it requires
TFTP, yet another dependency, so I'd prefer to do it directly with SSH if
possible.


The ultimate goal is to build a provisioning system for a DMVPN solution we
have


We use DMVPN in combination with GETVPN (aka GDOI) over Internet links to
provide branch and customer (extranet) connectivity. The DMVPN solution for
branch connectivity uses dynamic spoke-to-spoke tunneling, and GETVPN is
used so that there is no lag in the dynamic tunnel setup, as there is then
no IKE and IPSec negotiation between spoke routers when they build direct
spok-to-spoke tunnels. We're also using Front-Door VRF on the routers, so
the Internet interface is in an FVRF and the tunnel(s) are configured to use
the FVRF for NHRP resolution (tunnel vrf <vrf-name>). We have an ACL
applied inbound and another ACL applied outbound.

In order for each spoke router to build dynamic tunnel to another spoke, ESP
must be permitted inbound in the inbound ACL applied to the Internet
interface. Due to the dictates of our internal infosec people, the ACL,
must explicitly permit EACH spoke router's public IP, rather than a nice,
easy "permit esp any any "


the effect of this is that each time we add a new branch office router, we
must update the ACL and deploy it to all the branch routers. In addition,
we must add IKE key entries (using PSK IKE keys on the GETVPN keyservers),
and add routing neighbors to the headends routing (using eBGP for routing
exchange through the tunnel).

The net effect is that the addition of 1 router to the group triggers a few
different small config changes to the keyservers, a few routing neighbor
additions, and an ACL deployment to all existing branch routers.


My goal is to script this whole process:

- create a DB-like structure that is persistent on disk of all the routers
and their attributes (name, location, loopbak0 ip, tun1 ip , tun 2 ip,
internet interface IPs, IKE keys, etc) - so far, using shelve for this, and
using a class to represent the routers, serializing to disk using shelve...
working OK at the moment

- to add a new router
- add a new object to the DB

- to deploy the new router
- from the DB structure, pull out the public IPs and generate the ACL.
- ssh to GETVPN keyserver
- add the IKE keys for the new router's public IPs,
- issue commands to SCP or TFTP a new ACL which controls access to
the key server to the keyserver's flash
- checksum the ACL file on flash:
- copy the ACL to running-config
- wr mem
- ssh to the DMVPN headends
- add BGP neighbors for the new router's tunnel IPs (the tunnel
IPs are the eBGP peering addresses)
- wr mem
- ssh to each router's Lo0 ip in the DB
- issue commands to SCP or TFTP the new ACL to the router's
flash:
- checksum the ACL file on flash:
- copy the ACL to running config
- wr mem
- (optional) archive a copy of each router's config before and after to
a Subversion repository for disaster recovery and troubleshooting purposes
- write a copy of the ACLs into a Subversion repository
- write a copy of the shelve structure/DB structure elsewhere (ie:
Subversion repository)
- send a status report about what happened


The SSH aspect for multiple commands is currently tripping me up.

:-|


I've look at various sources, but all are somewhat convoluted.. .I'm new to
using Twisted, so I am sure that is a part of my issue... also the examples
I've seen are a bit trivial. Planning on looking closely at the tkconch.py
example script that comes with Twisted, as well as the code in ModiPy, which
also uses Twisted... ModiPy itself doesn't seem to work.


-
Post by Sam Crooks
Post by Sam Crooks
Does anyone have any suggestions for SSHv2 to multiple Cisco devices and
(this is the catch) running multiple commands?
I've tried with Paramiko using the SSHClient(), then connect()'ing and
issuing exec_command(), and it seems that the router closes the channel
after the .exec_command('command here'). Subsequent write()'s on the
stdin
http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/#more-465
Yes. The SSH session you're opening has a lifetime of one command.
1. Don't use exec_command(). Create the connection yourself, like
SSHClient does, and then run in SSH1.5 mode (details escape me now in
this airport, but I've done this).
2. On Juniper, I can do "show foo; show this; show that", which works
just fine with exec_command(). Does that work on a Crisco? (I'm
guessing no, since that's a single command that the juniper splits).
Post by Sam Crooks
I've tried various examples with twisted.conch.ssh and it seems Twisted
is a
Post by Sam Crooks
bit more low-level than paramiko's SSHClient class.
Yes; however, the SSHClient class wraps up a few other things that you
could just do yourself, too. The code isn't that ugly, although it
will raise EOFError on most operations (or socket.timeout in paramiko
1.7), and the docstrings in SSHClient's methods don't say that, so be
aware.
Post by Sam Crooks
I'm trying to be able to issue a string of commands to routers which
require a particular sequence;
configure terminal
hostname blahrouter1
ip domain-name x.i.z
ip tftp source-interface lo0
end
So you're trying to bootstrap a new router?
You could use the Cisco TFTP/BOOTP/DHCP method of bootstrapping. I
don't think many people really use this, but you may be in luck (i.e.,
it may work on your routers).
Put the actual commands in a file, and then just send "copy source
dest" on the router? This is how I do config pushes (it allows you to
copy to a local file on the router first and perform an MD5 checksum,
too. Which I'd recommend so your operators don't want to kill you
:-).
--
Corey Smith
2009-03-11 13:57:00 UTC
Permalink
Have you looked at pxssh in pexpect?

s = pxssh.pxssh(timeout=5, logfile=logfile)
s.login(host, username, password, original_prompt=r'(?i)%s#' % host,
auto_prompt_reset=False)
s.PROMPT = re.compile(r'(?i)^%s' % host, re.MULTILINE)
s.sendline('term length 0')
prompt(s)
s.sendline('term width 512')
prompt(s)
s.sendline('show version')
prompt(s)

-Corey Smith
Post by Sam Crooks
The SSH aspect for multiple commands is currently tripping me up.
Darrell Newcomb
2009-03-11 15:12:42 UTC
Permalink
It's good to see the list get a little activity. To share ideas:

The first part of the original post, sending multiple commands, can
be done easily with clogin (rancid) and a flat file of commands. As
well as use the config archive to drive the target device list in a
few seconds. For example: clogin -x <file> `grep "pattern in config
for devices you'd like to change" * | grep -v "devices/things to
exclude" | grep -v ".new" | awk -F: '{print $1}' | sort | uniq`
Where the * is any filename/path pattern that sucks up config files
into grep, this tends to vary most across different environments.


The 2nd part being a copy takes a bit of expect(clogin -s <expect
file>) to handle the confirmation prompting that IOS sends. That
prompt doesn't (at least historically) match what clogin's internal
expect is looking for after sending the last line so it stops sending
other lines.


What can be time consuming is doing the front-end logic to handle all
kinds of edge cases if you're working with larger device sets:
--verification of available space on device
--verification of target path/disk being on the device
--clean-up of manually placed or old files (gets better after more of
the changes are driven by automation)
--target path/disk variations across device types
--devices/drives that weren't available during the update run

Detecting those ahead of time is IMHO the easiest way to handle most
cases, but then again this comes from someone who use "old" stuff
like awk too much. ;-)
Post by Sam Crooks
I'm trying to be able to issue a string of commands to routers
which require a particular sequence;
configure terminal
hostname blahrouter1
ip domain-name x.i.z
ip tftp source-interface lo0
end
Have you looked at pxssh in pexpect?
s = pxssh.pxssh(timeout=5, logfile=logfile)
s.login(host, username, password, original_prompt=r'(?i)%s#' % host,
auto_prompt_reset=False)
s.PROMPT = re.compile(r'(?i)^%s' % host, re.MULTILINE)
s.sendline('term length 0')
prompt(s)
s.sendline('term width 512')
prompt(s)
s.sendline('show version')
prompt(s)
-Corey Smith
Post by Sam Crooks
The SSH aspect for multiple commands is currently tripping me up.
_______________________________________________
Toolmakers mailing list
https://lists.isc.org/mailman/listinfo/toolmakers
Matthew Walster
2009-03-10 16:37:35 UTC
Permalink
Post by Sam Crooks
Does anyone have any suggestions for SSHv2 to multiple Cisco devices and
(this is the catch) running multiple commands?
Expect scripts? For an easy way of using it, just use clogin from RANCID.

Matthew Walster
Loading...