Of course! Using Python with Fabric to manage yum is a very common and powerful task for automating system administration on Red Hat-based Linux distributions (like CentOS, RHEL, Fedora, etc.).

Fabric is a high-level Python library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks. It allows you to execute commands or upload files to remote systems in parallel or series.
Here’s a comprehensive guide on how to use Fabric to manage yum.
Prerequisites
First, you need to have Fabric installed. You'll also want the invoke library, which Fabric depends on.
pip install fabric invoke
For this example, you'll also need a remote server (or a local VM) running a Red Hat-based OS with SSH access configured (passwordless SSH keys are highly recommended).

Basic Fabric Setup
A Fabric project is typically managed by a fabfile.py in your project's root directory. This file contains your tasks.
Let's start with a basic fabfile.py that connects to a single server.
fabfile.py
from invoke import task
from fabric import Connection
# Define your host. You can use a shorthand like 'user@host'
# or define a dictionary for more options.
HOST = "centos_user@your_server_ip"
@task
def c():
"""Establish a connection to the server."""
# This task doesn't do much by itself, but it's useful for testing
# the connection. You can run `fab c` to see if you can connect.
print(f"Connecting to {HOST}...")
# The actual connection is established when you run other tasks
# that use the 'Connection' object.
You can test this connection with:

fab -H centos_user@your_server_ip c
You'll be prompted for your SSH password (or it will use your key).
Task 1: Listing Installed Packages
Let's create a task to list all installed packages. This demonstrates the basic pattern of using Connection.run().
fabfile.py
from invoke import task
from fabric import Connection
HOST = "centos_user@your_server_ip"
@task
def list_installed(conn: Connection):
"""Lists all packages installed via yum."""
print("Fetching list of installed packages...")
# The 'run' method executes a command on the remote host.
# capture=True captures the output of the command.
# warn=True will not cause the task to fail if the command returns
# a non-zero exit code (e.g., if a package isn't found).
result = conn.run("yum list installed", hide=True)
# The result object contains stdout, stderr, exit_code, etc.
print(result.stdout)
print(f"Command exited with code: {result.exit_code}")
# You can run this task with:
# fab -H centos_user@your_server_ip list_installed
Task 2: Installing a Package
This is where Fabric becomes truly useful. We'll create a task to install a package. We'll also add a "clean" task to remove it.
fabfile.py
from invoke import task
from fabric import Connection
HOST = "centos_user@your_server_ip"
# A helper function to create a connection
def get_connection():
return Connection(HOST)
@task
def install_htop(conn: Connection):
"""Installs the 'htop' package."""
print("Attempting to install 'htop'...")
# sudo=True will run the command with 'sudo'.
# pty=True is often needed for sudo prompts to work correctly.
conn.run("sudo yum install -y htop", pty=True)
print("htop installation command sent.")
@task
def remove_htop(conn: Connection):
"""Removes the 'htop' package."""
print("Attempting to remove 'htop'...")
conn.run("sudo yum remove -y htop", pty=True)
print("htop removal command sent.")
# Run with:
# fab -H centos_user@your_server_ip install_htop
# fab -H centos_user@your_server_ip remove_htop
Note: The -y flag in yum is crucial for automation, as it automatically answers "yes" to any prompts, preventing the script from hanging.
Task 3: Checking for & Installing Updates
A common administrative task is to check for and apply all available updates.
fabfile.py
from invoke import task
from fabric import Connection
HOST = "centos_user@your_server_ip"
@task
def update_system(conn: Connection):
"""Checks for and applies all available yum updates."""
print("Checking for system updates...")
conn.run("sudo yum check-update", pty=True)
print("\nApplying all updates...")
# 'sudo yum update -y' is the command to update all packages.
conn.run("sudo yum update -y", pty=True)
print("System update complete.")
# Run with:
# fab -H centos_user@your_server_ip update_system
Advanced: Handling Multiple Hosts & Configuration
Real-world automation involves managing multiple servers. Fabric makes this easy.
A. Defining Multiple Hosts
You can define a list of hosts and pass them to your tasks.
fabfile.py
from invoke import task
from fabric import Connection
# Define a list of your servers
HOSTS = [
"web1_user@web1.example.com",
"web2_user@web2.example.com",
"db_user@db.example.com"
]
@task
def update_all_servers(hosts, conn: Connection):
"""Updates the system on a list of servers."""
# The 'hosts' argument is special; it gets populated by the -H flag.
print(f"--- Updating {conn.host} ---")
conn.run("sudo yum update -y", pty=True)
print(f"--- Finished updating {conn.host} ---")
# To run this on multiple servers, provide them with the -H flag:
# fab -H web1_user@web1.example.com,web2_user@web2.example.com,db_user@db.example.com update_all_servers
B. Using a Configuration File (Recommended)
For complex setups, managing hosts and configuration in a Python file can get messy. Fabric supports loading configurations from a YAML file.
-
Create
fabconfig.yml:hosts: web_servers: - user1@web1.prod - user2@web2.prod db_servers: - user3@db.prod # You can also define default settings for connections connection: connect_timeout: 10 # ... other fabric.Connection options -
Update
fabfile.pyto use the config:from invoke import task from fabric import Config from fabric.connection import Connection import yaml # Load the configuration file with open("fabconfig.yml", "r") as f: config_data = yaml.safe_load(f) @task def update_webs(hosts, conn: Connection): """Updates all web servers defined in the config.""" print(f"--- Updating web server: {conn.host} ---") conn.run("sudo yum update -y", pty=True) print(f"--- Finished updating {conn.host} ---") # To run this, you specify the group name from the config: # fab -l # (list all tasks) # fab -c fabconfig.yml -H web_servers update_webs # The -H flag can now take a group name from your YAML file!
Best Practices & Error Handling
- Idempotency: Write your tasks so they can be run multiple times without causing errors.
yum install -y package_nameis idempotent. If the package is already installed, it will do nothing. - Check Exit Codes: Always check the
result.exit_codeafter aconn.run()call. A non-zero code usually indicates an error. - Use
hide='warnings'orhide=True: For tasks running in production, you don't want to spam the console with every line of output fromyum. Usehide=Trueto suppress it and only print the final result or errors. - Specificity: Instead of
yum update, preferyum install package_namefor specific packages. It's more predictable and safer for automation.
Example with robust error handling:
from invoke import task
from fabric import Connection
HOST = "centos_user@your_server_ip"
@task
def safe_install(conn: Connection, package_name: str):
"""
Safely installs a package if it's not already installed.
Usage: fab -H user@host safe_install:package_name=vim
"""
print(f"Checking if '{package_name}' is installed...")
# Use 'warn=True' so the command doesn't fail if grep finds nothing
check_result = conn.run(f"yum list installed | grep -q '{package_name}'", warn=True, hide=True)
if check_result.ok:
print(f"'{package_name}' is already installed. Nothing to do.")
else:
print(f"'{package_name}' is not installed. Installing now...")
install_result = conn.run(f"sudo yum install -y {package_name}", pty=True, hide='warnings')
if install_result.ok:
print(f"Successfully installed '{package_name}'.")
else:
print(f"Error installing '{package_name}'. Exit code: {install_result.exit_code}")
# You could re-raise the exception to fail the task
# raise Exception("Package installation failed.")
Summary
| Goal | yum Command |
Fabric Code |
|---|---|---|
| List Installed | yum list installed |
conn.run("yum list installed", hide=True) |
| Install Package | sudo yum install -y <pkg> |
conn.run("sudo yum install -y htop", pty=True) |
| Remove Package | sudo yum remove -y <pkg> |
conn.run("sudo yum remove -y htop", pty=True) |
| Update System | sudo yum update -y |
conn.run("sudo yum update -y", pty=True) |
| Check Updates | sudo yum check-update |
conn.run("sudo yum check-update", hide=True) |
| Multiple Hosts | (same commands) | Use -H host1,host2 or a YAML config file. |
