Skip to main content

Command Palette

Search for a command to run...

Advanced Python for DevOps

60 Days DevOps Challenge: Day 6

Updated
โ€ข6 min readโ€ขView as Markdown

Initial Tasks:

โœ… Task 1: Set up a virtual environment (venv) and install the required packages (pip install flask django requests logging).

python3 -m venv venv
source venv/bin/activate
pip install flask django requests logging

๐Ÿ”น Challenge 1: Create a Python script that connects to a remote server via SSH using paramiko.

Solution:

import paramiko

def ssh_connect(host, username, password, command):
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(host, username=username, password=password)
    stdin, stdout, stderr = client.exec_command(command)
    print(stdout.read().decode())
    client.close()

ssh_connect("your_server_ip", "your_user", "your_password", "ls -l")

Explanation:

  • Uses paramiko to connect to a remote server.

  • Executes a command (ls -l) and prints the output.


๐Ÿ”น Challenge 2: Build a simple Flask API with an endpoint that returns system health (CPU/memory usage).

Solution:

from flask import Flask, jsonify
import psutil

app = Flask(__name__)

@app.route('/health')
def system_health():
    return jsonify({
        "cpu_usage": psutil.cpu_percent(),
        "memory_usage": psutil.virtual_memory().percent
    })

if __name__ == '__main__':
    app.run(debug=True)

Explanation:

  • A Flask API that returns CPU and memory usage using psutil.

๐Ÿ”น Challenge 4: Use Python subprocess to execute system commands and capture output.

Solution:

import subprocess

def run_command(command):
    """Executes a system command and captures the output."""
    try:
        result = subprocess.run(command, shell=True, text=True, capture_output=True, check=True)
        print("Output:\n", result.stdout)
    except subprocess.CalledProcessError as e:
        print("Error:\n", e.stderr)

# Example: List files in the current directory
run_command("ls -l")  # Use "dir" instead of "ls -l" on Windows

Explanation:

  • Uses subprocess.run() to execute system commands.

  • shell=True allows running shell commands directly.

  • capture_output=True stores both stdout and stderr.

  • check=True raises an error if the command fails.


๐Ÿ”น Challenge 5: Build a Flask API that fetches live weather data from an external API and returns it in JSON format.

Solution:

import requests
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/weather/<city>')
def get_weather(city):
    api_key = "your_api_key"
    url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
    response = requests.get(url).json()
    return jsonify(response)

if __name__ == '__main__':
    app.run(debug=True)

Explanation:

  • Calls OpenWeatherMap API and returns weather data for a given city.

๐Ÿ”น Challenge 6: Deploy a Django application on AWS EC2 with Nginx & Gunicorn.

Solution (Steps):

- Launch EC2 instance.
- Install dependencies:

sudo apt update && sudo apt install python3-pip python3-venv nginx

- Setup Django project:

python3 -m venv venv
source venv/bin/activate
pip install django gunicorn
django-admin startproject myproject

- Run Gunicorn:

gunicorn --bind 0.0.0.0:8000 myproject.wsgi

- Configure Nginx (/etc/nginx/sites-available/myproject):

server {
    location / {
        proxy_pass http://127.0.0.1:8000;
    }
}

- Restart Nginx:

sudo systemctl restart nginx

Explanation:

  • Deploys Django with Gunicorn and Nginx on AWS EC2.

๐Ÿ”น Challenge 7: Write a Python program to parse log files and extract failed SSH login attempts.

Solution:

def parse_failed_logins(log_file):
    pattern = r"Failed password for (invalid user )?(\S+) from (\d+\.\d+\.\d+\.\d+)"

    with open(log_file, 'r') as file:
        for line in file:
            match = re.search(pattern, line)
            if match:
                user = match.group(2)
                ip = match.group(3)
                print(f"Failed login attempt - User: {user}, IP: {ip}")

# Run with:
parse_failed_logins("/var/log/auth.log")

Explanation:

  • Reads logs and extracts failed SSH login attempts.

๐Ÿ”น Challenge 8: Use fabric or paramiko to automate SSH login and run commands on multiple servers.

Solution:

import paramiko

servers = [
    {"host": "192.168.1.10", "user": "ubuntu", "key": "/path/to/private-key.pem"},
    {"host": "192.168.1.20", "user": "ec2-user", "key": "/path/to/private-key.pem"}
]

def run_command(command):
    for server in servers:
        print(f"Connecting to {server['host']}...")
        try:
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(hostname=server["host"], username=server["user"], key_filename=server["key"])

            stdin, stdout, stderr = client.exec_command(command)
            print(f"Output from {server['host']}:\n{stdout.read().decode()}")

            client.close()        except Exception as e:
            print(f"Error connecting to {server['host']}: {e}")

# Example usage
run_command("uptime")

Explanation:

  • Uses Paramiko to run commands on remote servers.

๐Ÿ”น Challenge 9: Implement a Python script that monitors Docker containers and sends alerts if a container crashes.

Solution:

import docker
import time
import smtplib
from email.mime.text import MIMEText

# Docker client
client = docker.from_env()

# Email alert function
def send_email_alert(container_name):
    sender_email = "your-email@gmail.com"
    receiver_email = "alert-receiver@gmail.com"
    password = "your-email-password"

    msg = MIMEText(f"Alert: Container {container_name} has crashed!")
    msg["Subject"] = "Docker Container Crash Alert"
    msg["From"] = sender_email
    msg["To"] = receiver_email

    try:
        server = smtplib.SMTP("smtp.gmail.com", 587)
        server.starttls()
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, msg.as_string())
        server.quit()
        print(f"Email alert sent for {container_name}!")
    except Exception as e:
        print(f"Failed to send email: {e}")

# Monitor Docker containers
def monitor_containers():
    running_containers = {c.id: c.name for c in client.containers.list()}

    while True:
        time.sleep(5)  # Check every 5 seconds
        for container_id, container_name in running_containers.items():
            try:
                container = client.containers.get(container_id)
                if container.status != "running":
                    print(f"ALERT: {container_name} has crashed!")
                    send_email_alert(container_name)
            except docker.errors.NotFound:
                print(f"ALERT: {container_name} has been removed or crashed!")
                send_email_alert(container_name)

if __name__ == "__main__":
    print("Starting Docker container monitoring...")
    monitor_containers()

Explanation:

  • Monitors all running containers

  • Detects crashes in real-time

  • Sends alerts via Email

  • Runs in a continuous loop


๐Ÿ”น Challenge 10: Check GitHub Repo for New Commits & Trigger Build

Solution:

import requests
import time

# GitHub Repo Details
GITHUB_REPO = "owner/repository"  # Replace with your repo
GITHUB_BRANCH = "main"  # Change based on your workflow
GITHUB_TOKEN = "your_github_token"  # Required for private repos

# Jenkins Build Details
JENKINS_URL = "http://your-jenkins-server/job/your-job/build"
JENKINS_USER = "your_username"
JENKINS_API_TOKEN = "your_jenkins_api_token"

# Headers for GitHub API
HEADERS = {"Authorization": f"token {GITHUB_TOKEN}"} if GITHUB_TOKEN else {}

def get_latest_commit():
    """Fetches the latest commit SHA from GitHub."""
    url = f"https://api.github.com/repos/{GITHUB_REPO}/commits/{GITHUB_BRANCH}"
    response = requests.get(url, headers=HEADERS)

    if response.status_code == 200:
        return response.json()["sha"]
    else:
        print(f"Error fetching commits: {response.json()}")
        return None

def trigger_jenkins_build():
    """Triggers a Jenkins build job."""
    response = requests.post(JENKINS_URL, auth=(JENKINS_USER, JENKINS_API_TOKEN))

    if response.status_code == 201:
        print("โœ… Jenkins build triggered successfully!")
    else:
        print(f"โš ๏ธ Failed to trigger build: {response.text}")

def monitor_commits():
    """Continuously checks for new commits and triggers a build."""
    last_commit = get_latest_commit()

    if not last_commit:
        print("Failed to fetch initial commit. Exiting...")
        return

    print(f"Monitoring repository: {GITHUB_REPO} on branch: {GITHUB_BRANCH}")

    while True:
        time.sleep(60)  # Check every 60 seconds
        new_commit = get_latest_commit()

        if new_commit and new_commit != last_commit:
            print(f"๐Ÿ†• New commit detected: {new_commit}")
            trigger_jenkins_build()
            last_commit = new_commit

if __name__ == "__main__":
    monitor_commits()

Explanation:

  • Uses the GitHub API to fetch the latest commit hash from a specified branch.

  • Continuously monitors the repository for new commits.

  • If a new commit is detected, it triggers a Jenkins build using the Jenkins API.

  • Runs in a loop checking every 60 seconds for updates.


๐Ÿ”น Challenge 11: Package Python Script as CLI Tool

Solution (script.py):

import argparse

parser = argparse.ArgumentParser(description="Simple CLI tool")
parser.add_argument("--name", required=True, help="Enter your name")
args = parser.parse_args()

print(f"Hello, {args.name}!")

Run:

python script.py --name Pratik

Explanation:

  • Uses argparse to create a simple CLI tool.

๐Ÿ”น Challenge 12: Convert Python Script to Binary (PyInstaller)

Solution:

pip install pyinstaller
pyinstaller --onefile --noconsole script.py

Explanation:

  • PyInstaller converts the Python script into a standalone binary executable.

  • The --onefile flag creates a single executable instead of multiple files.

  • The --noconsole flag (optional) hides the console when running GUI applications.

  • The generated binary does not require Python on the target system.

More from this blog

Devops

24 posts