I do not like to write a python cli that accepts argument as I am an advocate of using wizard style to guide user on how to configure things easily. But it seems the time has come for me to write a CLI script in python, and in order to provide usage guide and data parsing i need to use the python’s argparse
module.
stdin arguments
You can write a script without the need to parse, you need to import sys
then use its argv
method to get the arguments.
argv
is a list, index 0 is argv[0]
is the script name, argv[1]
is the first argument, then argv[2]
is the next and so on.
This is a sample code to send commands to Cisco asa with netmiko:
from netmiko import ConnectHandler import sys asa_config = dict( username=sys.argv[1], password=sys.argv[2], ip=sys.argv[3], device_type="cisco_asa" ) with ConnectHandler(**asa_config) as asa: result = asa.send_command(sys.argv[4]) print(result)
The usage will be this:
python asacmd2.py admin P@ssw0rd 192.168.100.40 "sh int ip brief"
What bad about this is there is no “option” to remind user what are the positional arguments, and yes it is positional arguments, means I need to remember the first argument is username, second is password third is ip address and last is the command i need to send with netmiko.
And if there is no argument sent, python complains:
Traceback (most recent call last): File "asacmd2.py", line 5, in username=sys.argv[1], IndexError: list index out of range
And there is no help message on what i need to put in as arguments.
Parse the arguments with keyword options
So an improvement over the previous code will be using argparse
, where i can add the argument and pass the data into a dest
, and refer to the data like I am using a method.
So here’s the code:
#!/usr/bin/python3 from netmiko import ConnectHandler from argparse import ArgumentParser import sys # arg parser configuration parser = ArgumentParser(prog="asa command line tool", description="asa command line tool", usage="python3 asacmd.py -u admin -p password -d 192.168.1.1 -c \"show int ip brief\"") parser.add_argument("-u", "--user", help="Username of device.", type=str, dest="user", required=True) parser.add_argument("-p", "--pass", help="password", type=str, dest="password", required=True) parser.add_argument("-d", "--device", help="IP address of cisco asa", type=str, dest="device", required=True) parser.add_argument("-c", "--cmd", help="cisco asa command", type=str, dest="cmd", required=True) parser.add_argument("--port", help="ssh port if not define it is 22 by default.", type=int, dest="port") # https://stackoverflow.com/questions/4042452/display-help-message-with-python-argparse-when-script-is-called-without-any-argu # if no argument is supplied print help message. if len(sys.argv) == 1: parser.print_help(sys.stderr) sys.exit(1) args = parser.parse_args() # netmiko configuration asa_config = dict( username=args.user, password=args.password, ip=args.device, device_type="cisco_asa", port=args.port if args.port else 22 ) with ConnectHandler(**asa_config) as asa: result = asa.send_command(args.cmd) # print out the result print(result)
With argparse I do not need to remember the position as the switch in the cli is my keyword for me to supply the value, an example:
python asacmd.py -p P@ssw0rd -d 192.168.100.40 -u admin -c "show int ip brief"
Notice the position does not matter, the switch itself provides a key for the value to be put in by user.
If I cannot remember the switch, I will do python asacmd.py -h
usage: python3 asacmd.py -u admin -p password -d 192.168.1.1 -c "show int ip brief" asa command line tool optional arguments: -h, --help show this help message and exit -u USER, --user USER Username of device. -p PASSWORD, --pass PASSWORD password -d DEVICE, --device DEVICE IP address of cisco asa -c CMD, --cmd CMD cisco asa command --port PORT ssh port if not define it is 22 by default.
And if I do not supply any switch, the script will complain in a more helpful way:
If I supplied the wrong switch, the usage example can give user an idea on how to use like this: