Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions 01-python-foundations/my_system_health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import psutil

try:
threshold = input("Enter the CPU threshold (%): ")
threshold = float(threshold)
except ValueError:
print("That's not a valid number. Please enter something like 75 or 80.5")
exit()

cpu_usage = psutil.cpu_percent(interval=1)
memory_usage = psutil.virtual_memory().percent
disk_usage = psutil.disk_usage("/").percent

print("You entered:", threshold)
print("Current CPU usage:", cpu_usage)
print("Current Memory usage:", memory_usage)
print("Current Disk usage:", disk_usage)

if cpu_usage > threshold:
print("CPU status: WARNING - usage is above threshold")
else:
print("CPU status: Healthy")

if memory_usage > threshold:
print("Memory status: WARNING - usage is above threshold")
else:
print("Memory status: Healthy")

if disk_usage > threshold:
print("Disk status: WARNING - usage is above threshold")
else:
print("Disk status: Healthy")
6 changes: 3 additions & 3 deletions 02-apis-and-json/call_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import requests

API_URL = "https://jsonplaceholder.typicode.com/todos/1"
API_URL = "https://jsonplaceholder.typicode.com/todos/2"


def fetch_todo(url):
Expand All @@ -17,8 +17,8 @@ def main():
for key, value in todo.items():
print(f"{key:10}: {value}")

if todo.get("userId") == 1:
print("\n>> This todo belongs to user 1")
if todo.get("userId") == 2:
print("\n>> This todo belongs to user 2")


if __name__ == "__main__":
Expand Down
24 changes: 24 additions & 0 deletions 02-apis-and-json/github_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import json
import requests

username = input("Enter a GitHub username: ")
url = f"https://api.github.com/users/{username}"

try:
response = requests.get(url, timeout=10)
response.raise_for_status()
except requests.exceptions.HTTPError:
print(f"Could not find a GitHub user called '{username}'.")
exit()

data = response.json()

print("Name :", data.get("name"))
print("Public repos:", data.get("public_repos"))
print("Followers :", data.get("followers"))
print("Location :", data.get("location"))

with open("github_user.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)

print("\nSaved full response to github_user.json")
2 changes: 1 addition & 1 deletion 02-apis-and-json/stock_market_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def get_daily_series(symbol, api_key):
def main():
api_key = os.environ.get("ALPHAVANTAGE_API_KEY")
if not api_key:
print("Set ALPHAVANTAGE_API_KEY first: export ALPHAVANTAGE_API_KEY=...")
print("Set ALPHAVANTAGE_API_KEY first: export ALPHAVANTAGE_API_KEY=MCHYRHM0ME21PCKH")
sys.exit(1)

symbol = input("Enter a stock symbol (e.g. IBM, AMZN, GOOGL): ").strip().upper()
Expand Down
40 changes: 40 additions & 0 deletions 03-file-handling-and-logs/my_log_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import json

LOG_FILE = "app.log"

try:
with open(LOG_FILE, "r", encoding="utf-8") as f:
lines = f.readlines()
except FileNotFoundError:
print(f"Log file not found: {LOG_FILE}")
exit()

print("Total lines:", len(lines))

info_count = 0
warning_count = 0
error_count = 0

for line in lines:
words = line.split()
if "INFO" in words:
info_count += 1
if "WARNING" in words:
warning_count += 1
if "ERROR" in words:
error_count += 1

print("INFO :", info_count)
print("WARNING:", warning_count)
print("ERROR :", error_count)

summary = {
"INFO": info_count,
"WARNING": warning_count,
"ERROR": error_count,
}

with open("log_summary.json", "w", encoding="utf-8") as f:
json.dump(summary, f, indent=2)

print("\nSaved summary to log_summary.json")
13 changes: 11 additions & 2 deletions 04-object-oriented-python/log_analyzer_oop.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
class LogAnalyzer:
def __init__(self, log_file):
self.log_file = log_file
self.counts = {level: 0 for level in LEVELS}
self.counts = {
"INFO": 0,
"WARNING": 0,
"ERROR": 0,
"UNKNOWN": 0,
}

def read_logs(self):
try:
Expand All @@ -32,7 +37,9 @@ def analyze(self, lines):
self.counts["UNKNOWN"] += 1
return self.counts

def write_summary(self, path="log_counts.json"):
def write_summary(self, path=None):
if path is None:
path = Path(self.log_file).with_suffix(".json")
with open(path, "w", encoding="utf-8") as f:
json.dump(self.counts, f, indent=2)

Expand All @@ -47,6 +54,8 @@ def main():
return

result = analyzer.analyze(lines)
analyzer.write_summary("custom_log_counts.json")

print("Log Analysis Summary:")
for level, count in result.items():
print(f" {level:7}: {count}")
Expand Down
52 changes: 52 additions & 0 deletions 05-cli-tools-argparse/my_log_analyzer_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import argparse
import json
import sys
from pathlib import Path

LEVELS = ("INFO", "WARNING", "ERROR")

parser = argparse.ArgumentParser(description="Analyze a log file for INFO/WARNING/ERROR counts.")
parser.add_argument("--file", required=True, help="path to the log file")
parser.add_argument("--out", help="write the summary to this JSON file")
parser.add_argument("--level", choices=LEVELS, help="show the count for only this level")

args = parser.parse_args()

log_path = Path(args.file)
if not log_path.is_file():
print(f"Error: log file not found: {args.file}", file=sys.stderr)
sys.exit(2)

with open(log_path, "r", encoding="utf-8") as f:
lines = f.readlines()

info_count = 0
warning_count = 0
error_count = 0

for line in lines:
words = line.split()
if "INFO" in words:
info_count += 1
if "WARNING" in words:
warning_count += 1
if "ERROR" in words:
error_count += 1

counts = {
"INFO": info_count,
"WARNING": warning_count,
"ERROR": error_count,
}

if args.level:
print(f"{args.level}: {counts[args.level]}")
else:
print("INFO :", info_count)
print("WARNING:", warning_count)
print("ERROR :", error_count)

if args.out:
with open(args.out, "w", encoding="utf-8") as f:
json.dump(counts, f, indent=2)
print(f"Wrote summary to {args.out}")
5 changes: 5 additions & 0 deletions 05-cli-tools-argparse/my_summary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"INFO": 10,
"WARNING": 2,
"ERROR": 3
}
5 changes: 5 additions & 0 deletions 05-cli-tools-argparse/summary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"INFO": 10,
"WARNING": 2,
"ERROR": 3
}
3 changes: 3 additions & 0 deletions 06-aws-automation-boto3/cdk_demo/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "python app.py"
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Python for DevOps + Agentic AI

<p align="center">
<img src="https://github-readme-activity-graph.vercel.app/graph?username=elviDev&theme=github-light&cache_seconds=1800&v=3" alt="Elvis's Contribution Graph" width="100%" />
</p>

Learn to use Python for real DevOps work — automation, cloud operations,
log analysis, internal tooling, and local AI agents.

Expand Down
55 changes: 55 additions & 0 deletions capstone/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,61 @@ interview-ready.

---

## My implementation

A FastAPI service — "DevOps Intelligence API" — covering the full "Impressive"
tier: log analysis, system metrics, AWS resource visibility, and an
AI-assisted log investigation endpoint backed by a local Ollama model.

### Endpoints

| Method | Path | What it does |
| ------ | -------------- | -------------------------------------------------- |
| GET | `/health` | Liveness check |
| GET | `/logs` | INFO/WARNING/ERROR counts for a log file |
| GET | `/metrics` | Current CPU, memory, and disk usage |
| GET | `/aws/s3` | S3 buckets grouped by age |
| GET | `/aws/ec2` | EC2 instances and their state |
| GET | `/aws/report` | Combined S3 + EC2 inventory |
| POST | `/ai/analyze` | Plain-English log summary from a local LLM agent |

### Setup

```bash
cd capstone
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux

pip install -r requirements.txt
```

### Run

```bash
uvicorn app.main:app --reload
```

The API is then available at `http://127.0.0.1:8000` (interactive docs at
`/docs`). `/logs` and `/ai/analyze` default to `sample_logs/app.log` if no
`file` is given.

The `/aws/*` endpoints need AWS credentials available to `boto3` (e.g. via
`aws configure` or environment variables). `/ai/analyze` needs a local
[Ollama](https://ollama.com) instance running with the model set in
`OLLAMA_MODEL` (defaults to `qwen3.6:latest`) pulled and available.

### Tests

```bash
pytest
```

All 16 tests run without live AWS credentials or a running Ollama instance —
AWS and AI calls are mocked.

---

## The story these modules tell

Every module in this course builds one capability. The capstone assembles them:
Expand Down
63 changes: 37 additions & 26 deletions capstone/STAR.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
# My Capstone — S.T.A.R Explanation

Fill this in with your own words. A lot of people struggle in interviews not for
lack of skills, but because they can't explain their work clearly. Bullet points
are fine.

## Situation
<!-- What was the problem or context? -->
-
- Our team's application logs and infrastructure health were only checked
manually — someone had to SSH in, tail log files, and separately check AWS
and system metrics whenever something seemed off. That was slow, easy to
forget, and gave no consistent way to check status on demand.

## Task
<!-- What were you responsible for? -->
-
- I was responsible for automating this with Python: parse and summarize
application logs, expose system health metrics, surface AWS resource
inventory, and make all of it available through a single, reusable service
instead of one-off scripts.

## Action
<!-- What did you actually build/do? Be specific about the tech. -->
-
- Wrote a log parser that reads a log file and counts INFO/WARNING/ERROR
occurrences using whole-word matching, then wrapped it in an
`analyze_log_file` function returning structured counts.
- Built a `psutil`-based metrics service reporting CPU, memory, and disk
usage, with a configurable CPU threshold that flags the system as healthy
or under high load.
- Used `boto3` to build an AWS inventory service: S3 buckets grouped by age
(new vs. old, based on a configurable day threshold) and EC2 instances with
their current state.
- Exposed all of it through a FastAPI service (`/health`, `/logs`, `/metrics`,
`/aws/s3`, `/aws/ec2`, `/aws/report`) with routers, services, and Pydantic
schemas kept in separate layers, and consistent HTTP error mapping
(404/400/500/502) for missing files, bad input, and AWS/API failures.
- Added a local AI layer: a LangChain/LangGraph agent running against a local
Ollama model, given a tool that returns the exact deterministic log counts
so it can't invent statistics, exposed via `POST /ai/analyze`. Added
path-traversal protection so the endpoint can only read `.log` files inside
the approved log directory.
- Wrote 16 unit tests covering the log parser, metrics service, AWS service
(mocking `boto3` clients so no real AWS calls are made in tests), and the
AI router's path-resolution and security checks.

## Result
<!-- What changed? Quantify if you can (time saved, errors caught). -->
-

---

### Example (for reference — replace with your own)

- Situation: Application logs grew daily and manually scanning them for errors
was slow and error-prone.
- Task: Automate log analysis with Python so issues surface quickly.
- Action: Wrote a log parser that counts INFO/WARNING/ERROR, refactored it into
a class, added an `argparse` CLI, then exposed it via a FastAPI `/logs`
endpoint alongside `/metrics` and `/health`. Added a local LangGraph + Ollama
agent to summarize logs in plain English.
- Result: Reduced manual log review effort and gave the team quick, on-demand
visibility into application health through a single API.
- Replaced manual log/metrics/AWS checks with a single running API that
answers each question in one request instead of several manual steps.
- The AI endpoint gives a plain-English read of what a log file shows,
grounded in the same deterministic counts the API reports elsewhere, which
makes it trustworthy enough to actually rely on rather than just a demo.
- All 16 tests pass and the AWS/AI tests run without needing live AWS
credentials or a running Ollama instance, so the suite is safe to run in
CI.
Empty file added capstone/app/__init__.py
Empty file.
Loading