Python Session 06: Infrastructure Patterns

Production patterns. This session covers HTTP requests, CLI argument parsing, configuration files, and logging.

Pre-Session State

  • Can run subprocess commands

  • Understand environment variables

  • Know functions and classes

Lesson 1: HTTP Requests (urllib)

Concept: Use urllib.request for HTTP without external deps.

Exercise 1.1: Basic GET

from urllib.request import urlopen
import json

# Note: In production, use 'requests' library
url = "https://api.github.com/users/octocat"

with urlopen(url) as response:
    data = json.loads(response.read())
    print(f"Name: {data['name']}")
    print(f"Repos: {data['public_repos']}")

Exercise 1.2: With headers

from urllib.request import Request, urlopen

url = "https://api.github.com/user"
token = "ghp_xxxx"  # Your token

req = Request(url)
req.add_header("Authorization", f"token {token}")
req.add_header("Accept", "application/json")

try:
    with urlopen(req) as response:
        print(response.read().decode())
except Exception as e:
    print(f"Error: {e}")

Exercise 1.3: POST request

from urllib.request import Request, urlopen
import json

url = "https://httpbin.org/post"
data = {"name": "kvm-01", "ip": "10.50.1.110"}

req = Request(url, data=json.dumps(data).encode(), method='POST')
req.add_header("Content-Type", "application/json")

with urlopen(req) as response:
    result = json.loads(response.read())
    print(result)

Lesson 2: Argument Parsing

Concept: Use argparse for CLI tools.

Exercise 2.1: Basic arguments

import argparse

parser = argparse.ArgumentParser(description='Host management tool')
parser.add_argument('hostname', help='Target hostname')
parser.add_argument('-p', '--port', type=int, default=22, help='SSH port')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')

args = parser.parse_args()

print(f"Connecting to {args.hostname}:{args.port}")
if args.verbose:
    print("Verbose mode enabled")

Exercise 2.2: Subcommands

import argparse

parser = argparse.ArgumentParser(description='Server tool')
subparsers = parser.add_subparsers(dest='command')

# 'list' subcommand
list_parser = subparsers.add_parser('list', help='List servers')
list_parser.add_argument('--format', choices=['table', 'json'], default='table')

# 'ping' subcommand
ping_parser = subparsers.add_parser('ping', help='Ping server')
ping_parser.add_argument('host', help='Host to ping')

args = parser.parse_args()

if args.command == 'list':
    print(f"Listing servers in {args.format} format")
elif args.command == 'ping':
    print(f"Pinging {args.host}")

Exercise 2.3: Required vs optional

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--config', required=True, help='Config file (required)')
parser.add_argument('--dry-run', action='store_true', help='Don\'t make changes')
parser.add_argument('--count', type=int, default=1, help='Number of iterations')

args = parser.parse_args()

Lesson 3: Configuration Files

Concept: Use configparser for INI-style configs.

Exercise 3.1: Read config

import configparser

# Create test config
config_content = """
[server]
hostname = kvm-01
ip = 10.50.1.110
port = 22

[credentials]
username = admin
"""

with open('/tmp/server.ini', 'w') as f:
    f.write(config_content)

# Read config
config = configparser.ConfigParser()
config.read('/tmp/server.ini')

print(config['server']['hostname'])
print(config.get('server', 'port'))
print(config.getint('server', 'port'))

Exercise 3.2: Write config

import configparser

config = configparser.ConfigParser()
config['server'] = {
    'hostname': 'kvm-01',
    'ip': '10.50.1.110',
    'port': '22'
}
config['logging'] = {
    'level': 'INFO',
    'file': '/var/log/app.log'
}

with open('/tmp/app.ini', 'w') as f:
    config.write(f)

Lesson 4: Logging

Concept: Use logging for structured output.

Exercise 4.1: Basic logging

import logging

logging.basicConfig(level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s')

logging.debug("Debug message (won't show)")
logging.info("Info message")
logging.warning("Warning message")
logging.error("Error message")

Exercise 4.2: Logger with file output

import logging

# Create logger
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)

# Console handler
console = logging.StreamHandler()
console.setLevel(logging.INFO)

# File handler
file_handler = logging.FileHandler('/tmp/myapp.log')
file_handler.setLevel(logging.DEBUG)

# Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console.setFormatter(formatter)
file_handler.setFormatter(formatter)

logger.addHandler(console)
logger.addHandler(file_handler)

logger.info("Application started")
logger.debug("Debug info (file only)")
logger.error("Something went wrong")

Exercise 4.3: Structured logging context

import logging

logging.basicConfig(level=logging.INFO,
    format='%(asctime)s - %(levelname)s - [%(host)s] %(message)s')

logger = logging.getLogger()

# Add context with extra
logger.info("Connection established", extra={'host': 'kvm-01'})
logger.warning("High CPU usage", extra={'host': 'kvm-02'})

Summary: What You Learned

Concept Syntax Example

HTTP GET

urlopen(url)

Returns response object

HTTP headers

Request.add_header()

Add auth, content-type

argparse

ArgumentParser()

parser.add_argument('--flag')

Subcommands

add_subparsers()

list, show, delete commands

ConfigParser

configparser.ConfigParser()

INI file handling

Logging

logging.basicConfig()

Configure level, format

Logger

logging.getLogger(name)

Named logger instance

Exercises to Complete

  1. [ ] Build a CLI tool with list/show/ping subcommands

  2. [ ] Read server config from INI file and ping each server

  3. [ ] Fetch GitHub user info and log the results

  4. [ ] Create a tool with --verbose flag that adjusts log level

  • Drill 06 - Practice these patterns

  • Python docs: argparse, logging, configparser

Session Log

Timestamp Notes

Start

<Record when you started>

End

<Record when you finished>