Because we think CLI is here to stay, at least for troubleshooting purposes
Because we use it on a daily basis
Because we were tired of reinventing the wheel...
We decided to build this small project using Python pexpect, argparse and pycrypto modules.
user@m32e:~/cli-wrapper$ cli.py -u username -w password -r csr1000v-1 -x telnet
csr1000v-1>
csr1000v-1>q
Connection closed by foreign host.
<<< gracefully exited from: csr1000v-1
user@m32e:~/cli-wrapper$
user@m32e:~/cli-wrapper$ cli.py -u username -w password enablepassword -r csr1000v-1 -x telnet
csr1000v-1#
csr1000v-1#q
Connection closed by foreign host.
<<< gracefully exited from: csr1000v-1
user@m32e:~/cli-wrapper$
user@m32e:~/cli-wrapper$ cli.py -u username -w password -r csr1000v-1 -x telnet -c 'sho ip int brief | inc Gi'
terminal length 0
csr1000v-1> >>> now executing commands from ['sho ip int brief | inc Gi'] on csr1000v-1
sho ip int brief | inc Gi
GigabitEthernet1 10.255.1.90 YES TFTP up up
GigabitEthernet2 10.0.0.62 YES TFTP up up
GigabitEthernet3 10.0.0.50 YES TFTP up up
GigabitEthernet4 10.0.0.2 YES TFTP up up
GigabitEthernet5 10.0.0.6 YES TFTP up up
csr1000v-1>
<<< gracefully exited from: csr1000v-1
user@m32e:~/cli-wrapper$ cli.py -u username -w password -r csr1000v-1 -x telnet -c 'sho ip int brief | inc Gi'
'show ip route' -l
>>> now logging output from csr1000v-1 in logs/csr1000v-1_D30A74ADA26C.log
>>> now executing commands from ['sho ip int brief | inc Gi', 'show ip route'] on csr1000v-1
<<< gracefully exited from: csr1000v-1
user@m32e:~/cli-wrapper$ cat > commands/sample_2
show ip route
show ip int brief
sho logging
user@m32e:~/cli-wrapper$
user@m32e:~/cli-wrapper$ cli.py -u username -w password -r csr1000v-1 -x telnet -cf commands/sample_2 -l
>>> now logging output from csr1000v-1 in logs/csr1000v-1_7C4C722E185B.log
>>> now executing commands from ['show ip route', 'show ip int brief', 'sho logging'] on csr1000v-1
<<< gracefully exited from: csr1000v-1
user@m32e:~/cli-wrapper$ cli.py -u username -w password -f hosts/liste_csr1000v -x telnet -cf commands/sample_
2 -l
>>> now logging output from csr1000v-1 in logs/csr1000v-1_F69181CBB187.log
>>> now executing commands from ['show ip route', 'show ip int brief', 'sho logging'] on csr1000v-1
<<< gracefully exited from: csr1000v-1
>>> now logging output from csr1000v-2 in logs/csr1000v-2_196E7D86E698.log
>>> now executing commands from ['show ip route', 'show ip int brief', 'sho logging'] on csr1000v-2
<<< gracefully exited from: csr1000v-2
>>> now logging output from csr1000v-3 in logs/csr1000v-3_91F885603B1D.log
>>> now executing commands from ['show ip route', 'show ip int brief', 'sho logging'] on csr1000v-3
<<< gracefully exited from: csr1000v-3
>>> now logging output from csr1000v-4 in logs/csr1000v-4_E2C17A24952E.log
>>> now executing commands from ['show ip route', 'show ip int brief', 'sho logging'] on csr1000v-4
<<< gracefully exited from: csr1000v-4
user@m32e:~/cli-wrapper$ cat profiles/sample
#
--username-credentials
myLoginAccount
--password
mySecretPassword
#
user@m32e:~/cli-wrapper$
user@m32e:~/cli-wrapper$ ./cipher.py
What do you wanna do ? decrypt/encrypt - [D/e]: e
What is the filename you want to encrypt ? profiles/sample
Please enter your password: *********
Encrypt "profiles/sample" and save it in "profiles/sample.enc"
user@m32e:~/cli-wrapper$ ./cli.py -f hosts/xrvs -o profiles/sample.enc -cf commands/ipRoutesXr -l
Please enter your password:
>>> now logging output from xrv1.xyz in logs/xrv1.xyz_6792508A8CDC.log
>>> now executing commands from ['show route ipv4', 'show route ipv6'] on xrv1.xyz
<<< gracefully exited from: xrv1.xyz
>>> now logging output from xrv2.xyz in logs/xrv2.xyz_C109B3CDD1B3.log
>>> now executing commands from ['show route ipv4', 'show route ipv6'] on xrv2.xyz
<<< gracefully exited from: xrv2.xyz
>>> now logging output from xrv3.xyz in logs/xrv3.xyz_CD502442E225.log
>>> now executing commands from ['show route ipv4', 'show route ipv6'] on xrv3.yz
<<< gracefully exited from: xrv3.xyz
>>> now logging output from xrv4.xyz in logs/xrv4.xyz_F48C73F48520.log
>>> now executing commands from ['show route ipv4', 'show route ipv6'] on xrv4.xyz
<<< gracefully exited from: xrv4.xyz
Use a sub procedure to perform custom actions within a connection (look at "skeleton.py" example in subs directory)
user@m32e:~/cli-wrapper$ ./cli.py -f hosts/xrvs -o profiles/sample.enc -s subs.skeleton send_one_comment -v -i
Please enter your password:
csr1000v-1#
csr1000v-1#!
csr1000v-1#!!
csr1000v-1#!!! kinda useless comment...
csr1000v-1#!!
csr1000v-1#!
csr1000v-1#
###Use a sub procedure to perform custom actions before the establishment of a connection (look at "second_proxy.py" example in subs directory) note : if we don't have a 'jumphost' parameter set, the connection does not exist when this procedure is executed.
user@m32e:~/cli-wrapper$ ./cli.py -r host -ss subs.second_proxy transparent_connection_to_2nd_proxy
user@m32e:~/cli-wrapper$ cli.py -h
usage:
./cli.py -r "remote host"| -f "list of hosts" [options]
./cli.py -h #get help on numerous options
A small tool to log into any piece of equipment with a decent CLI interface...
You can pass a lot of options to comply with most of devices behavior. Note
that if you ask ./cli.py to look for options in a file, that is "-o" option,
this file obviously contains your credentials, passwords, and thus MUST be
encrypted using the "cipher.py" tool. Enjoy !
optional arguments:
-h, --help show this help message and exit
-f FILE, --hosts-file FILE
file containing a list of hosts
-r REMOTE_HOSTNAME, --remote-host REMOTE_HOSTNAME
remote host name or IP
-c COMMAND [COMMAND ...], --command COMMAND [COMMAND ...]
command(s) to be executed on remote host
-cf COMMAND_FILE, --command-file COMMAND_FILE
file containing a list of commands
-d, --debug debug mode will out in clear all arguments passed to
the script
-i, --interact do not interact after connection
-j LIST [LIST ...], --jumphost-credentials LIST [LIST ...]
an ordered list: (protocol,host,port,username,password
,prompt,timeout,verbose) you can omit latest elements
-l, --logfile create a logging file in "logs" subdirectory
-m MORE, --no-more MORE
command to pass to remote host to avoid --more-- in
execution, defaults to "terminal length 0"
-o OVERRIDE, --options-override OVERRIDE
ciphered file containing arguments that override
command line options
-p PROMPT, --prompt PROMPT
expected prompt from remote host
-po TCP_PORT_NUMBER, --port-number TCP_PORT_NUMBER
TCP port number for connection
-s MODULE METHOD, --sub-proc MODULE METHOD
module and function to execute
-ss MODULE METHOD, --presub-proc MODULE METHOD
module and function to execute before connection to
remote device
-t TIMEOUT, --timeout TIMEOUT
max seconds to wait for an answer from remote host
-u USERNAME, --username-credentials USERNAME
username to log into remote host
-v, --verbose unhide connection process, usefull for debugging
-w PASSWORD [PASSWORD ...], --password PASSWORD [PASSWORD ...]
password to log into remote host and optionally an
enable password
-x {telnet,ssh}, --protocol {telnet,ssh}
protocol to be used for connection, defaults to ssh
user@m32e:~/cli-wrapper$
- grab the package from Github using
git clone
for example - install it anywhere on your machine
- read
requirements.txt
for a list of Python modules required by the application you can usepip install -r requirements.txt
(see known problems below if pycrypto install fails) - modify environment variables stored in
libs/constants.py
if needed - keep your setup up to date :
git pull
- simply execute
cli.py --help
from command line, and follow the guidelines - if you want to store your passwords securely, you should :
- put them in a plain text file (see "profiles/sample" or "plaintext" files)
- cipher the file using
cipher.py
utility with a single password - delete the original plain file
- from now on, you can use
--override
option to inject your credentials into the program
- automation of connection and authentication phases in a secure way
- provide a list of hosts from which we need the same pieces of infos
- provide a list of commands to be executed on a remote host
- comprehensive logging capabilities
- possibility to use a jump server (a server that sits in between us and the targeted host)
- storage of common parameters in local ciphered files
- provide a sub procedure to be customized, so it can execute special actions, depending on interaction with remote host
- a comprehensive documentation set :-)
- let the user provide their own parameters that could be reused in sub procedure
- maybe a testing unit...
- Linux Ubuntu, Debian and others for sure but we haven't tested it yet.
- Cygwin on Windows
- Mac OS
Any piece of equipment offering a decent command line interface.
Note that the expected prompt from the remote device is a regular expression that you can change.
Then the way you interact with the remote device, either manually or automatically, is all yours.
- pycrypto install through pip usually works well but there used to be a bug, the workaround is to install pycrypto from source : https://www.dlitz.net/software/pycrypto/
- sometimes the encrypted profile provided to the "-o" option requires an empty line at the end, you can figure that out with debug option "-d", and check if the last parameter is not missing.
- Pexpect and Pcrypto authors and developers