Hello! Welcome to the final lesson of the "Python for DevOps Engineers" module.
In our last session, we took the first step in making our Python scripts reusable by learning the basics of argparse. We saw how to define positional and optional arguments to pass information to our script from the command line, transforming a static script into an interactive one.
Today, we're going to build on that foundation to create a polished, practical Command-Line Interface (CLI) tool. We won't just be adding arguments to a script; we'll be structuring our code to behave like professional DevOps tools (git, docker, aws-cli) that perform multiple actions.
Today's Learning Outcome: By the end of this lesson, you will be able to build a simple CLI tool in Python that wraps a previously learned automation task.
This is a capstone lesson for our Python module, bringing together file structure, functions, and command-line parsing to create a complete utility.
1. From a Simple Script to a Versatile Tool
In the last lesson, we adapted our GitHub user script to take a username from the command line. This is a form of "wrapping" a task—we took the core logic (making an API call) and wrapped it in a argparse interface.
Let's look at another clear example of this process. The following video demonstrates taking an existing Python script for a Rock, Paper, Scissors game and integrating argparse to add a simple feature: personalizing the game with the player's name.
Python Command Line Arguments tutorial for Beginners
This video from Dave Gray's channel provides an excellent, practical example of 'wrapping' an existing script with argparse.
Watch from 13:53 to 18:00. Focus on these key steps: Copying the argparse boilerplate code into the script. Passing the parsed argument (args.name) into the main game function. Modifying the game's internal functions to actually use the name that was passed in.
This approach is great for scripts that do one primary thing. However, most powerful DevOps tools need to handle multiple, distinct actions. For instance:
githascommit,push,pull,branch...dockerhasrun,build,ps,images...awshass3,ec2,lambda...
To build a tool like this, we need to use a more advanced feature of argparse: subcommands.
2. Building Multi-Action Tools with Subcommands
Subcommands allow you to create a hierarchy of commands. You have your main script, which then calls different sub-programs based on the first argument.
argparse handles this through a sub-parser system. The setup looks like this:
- Create the main parser:
parser = argparse.ArgumentParser() - Add a subparser object:
subparsers = parser.add_subparsers(dest='command')- This special object will manage all your subcommands.
dest='command'is useful because it stores which subcommand was used inargs.command.
- This special object will manage all your subcommands.
- Create a parser for each subcommand:
cpu_parser = subparsers.add_parser('cpu')- Each subcommand gets its own
ArgumentParserobject.
- Each subcommand gets its own
- Add arguments to the subcommand parser:
cpu_parser.add_argument('--threshold', ...)- Arguments added here only apply when that specific subcommand is used.
- Link the subcommand to a function: A common and clean pattern is to use
set_defaults(func=my_function). This associates the subcommand with the function that should be executed.
This might seem complex in text, but it's a very powerful and organized pattern.
Build Command-Line Interfaces With Python's argparse
The Real Python article 'Build Command-Line Interfaces With Python's argparse' has an excellent section on creating subcommands. It walks through building a calculator CLI with add, sub, mul, and div subcommands.
Read the section 'Adding Subcommands to Your CLIs'. Pay close attention to how add_subparsers() is used, how each subcommand gets its own parser (add_parser('add')), and the clever use of set_defaults(func=add) to link the command to its logic. This is the exact pattern we will use in our project.
3. Project: Build a System Monitoring CLI
Remember in Module 9, you wrote Bash scripts to monitor system resources. Let's now create a more robust Python-based CLI tool to do the same.
Our goal is to create a tool named monitor.py that can be used as follows:
python monitor.py cpu: Shows current CPU usage.python monitor.py memory: Shows current memory usage.python monitor.py disk: Shows current disk usage.
This is a perfect use case for subcommands!
Step 1: Install psutil
To get system information like CPU and memory usage in Python, the psutil library is the standard choice. It's a cross-platform library that makes these tasks simple.
Open your terminal and install it:
pip install psutil
Step 2: Write the Core Logic Functions
First, let's write the functions that do the actual work. Create a new file called monitor.py and add the following code. This separates our logic from our command-line interface.
import psutil
import argparse
def check_cpu_usage():
"""Checks and prints the current CPU usage."""
usage = psutil.cpu_percent(interval=1)
print(f"CPU Usage: {usage}%")
def check_memory_usage():
"""Checks and prints the current memory usage."""
memory = psutil.virtual_memory()
# Convert bytes to GB for readability
total_gb = memory.total / (1024**3)
available_gb = memory.available / (1024**3)
print(f"Memory Usage: {memory.percent}%")
print(f"Total: {total_gb:.2f} GB | Available: {available_gb:.2f} GB")
def check_disk_usage():
"""Checks and prints the disk usage for the root partition."""
disk = psutil.disk_usage('/')
# Convert bytes to GB for readability
total_gb = disk.total / (1024**3)
used_gb = disk.used / (1024**3)
print(f"Disk Usage: {disk.percent}%")
print(f"Total: {total_gb:.2f} GB | Used: {used_gb:.2f} GB")
Step 3: Build the argparse Wrapper with Subcommands
Now, let's add the CLI structure. We will create a main() function to contain this logic. Add this code to the bottom of your monitor.py file:
def main():
# 1. Create the main parser
parser = argparse.ArgumentParser(description="A simple system monitoring CLI.")
# 2. Create the subparser object
subparsers = parser.add_subparsers(dest='command', required=True, help='Available commands')
# 3. Create the parser for the "cpu" command
cpu_parser = subparsers.add_parser('cpu', help='Check CPU usage.')
cpu_parser.set_defaults(func=check_cpu_usage) # Link to the function
# 4. Create the parser for the "memory" command
mem_parser = subparsers.add_parser('memory', help='Check memory usage.')
mem_parser.set_defaults(func=check_memory_usage) # Link to the function
# 5. Create the parser for the "disk" command
disk_parser = subparsers.add_parser('disk', help='Check disk usage.')
disk_parser.set_defaults(func=check_disk_usage) # Link to the function
# Parse the arguments
args = parser.parse_args()
# Call the function associated with the chosen subcommand
args.func()
if __name__ == "__main__":
main()
Test your understanding!
Based on the code above, if you run python monitor.py cpu --help, which parser is responsible for generating the help message?
- The main
parserobject. - The
subparsersobject. - The
cpu_parserobject. - The
psutillibrary.
Show answer
The correct answer is 3. The cpu_parser object. Each subcommand has its own dedicated parser (cpu_parser, mem_parser, etc.) that handles its own arguments and help messages. The main parser handles the top-level help message.
Step 4: Run Your New Tool!
Save the file and run it from your terminal. Try all the commands:
# Check the main help message
$ python monitor.py -h
usage: monitor.py [-h] {cpu,memory,disk} ...
A simple system monitoring CLI.
positional arguments:
{cpu,memory,disk} Available commands
cpu Check CPU usage.
memory Check memory usage.
disk Check disk usage.
options:
-h, --help show this help message and exit
# Run each subcommand
$ python monitor.py cpu
CPU Usage: 12.8%
$ python monitor.py memory
Memory Usage: 45.1%
Total: 15.89 GB | Available: 8.72 GB
$ python monitor.py disk
Disk Usage: 60.3%
Total: 233.22 GB | Used: 140.65 GB
You have successfully built a multi-action CLI tool that wraps an automation task!
4. Seeing the Pattern in a Real-World DevOps Tool
The subcommand pattern you just implemented is not just a toy example; it's the foundation for complex, real-world DevOps tools. To see this in action, let's look at a project that builds a custom CLI for managing AWS resources.
Build Your Own AWS DevOps CLI with Python & Boto3
This article, 'Build Your Own AWS DevOps CLI with Python & Boto3', demonstrates how to build a tool for managing EC2 instances, S3 buckets, and Lambda functions. It uses the exact same argparse subcommand pattern you just learned.
Scan through the section '4. Connecting Everything With main.py (CLI)'. You don't need to understand the AWS-specific code (Boto3). Instead, focus on the argparse setup. Notice how the author creates subparsers for 'ec2', 's3', and 'lambda', and then nested subparsers for actions like 'create' and 'list'. This shows you how this pattern scales to build powerful tools.
Seeing this pattern applied to a large, practical project should solidify your understanding and show you the real power of what you've just learned.
Conclusion
Congratulations on completing the Python for DevOps module! You've gone from Python basics to building a functional, professional CLI tool. This is a huge step in your journey to DevOps mastery.
Key Takeaways:
- CLI tools can be structured to handle multiple actions using subcommands in
argparse. - The
add_subparsers()method is the key to creating subcommand handlers. - A clean design pattern is to separate core logic (the functions that do the work) from the CLI wrapper (the
argparsecode). - Using
set_defaults(func=...)is an elegant way to link a subcommand to the function it should execute. - The
if __name__ == "__main__":block is the standard way to make a Python file both importable as a module and runnable as a script.
Next Up: Containerization with Docker!
You've built scripts and tools. Now, how do you run them reliably on any machine, be it your laptop, a colleague's computer, or a cloud server? The answer is containerization.
In our next module, we'll dive into Docker. You'll learn how to package your applications—like the CLI tool you just built—into lightweight, portable containers. This is a cornerstone of modern DevOps, and the skills you've gained in Linux, networking, and scripting will all come together here.