{ "cells": [ { "cell_type": "markdown", "id": "e58bd80a", "metadata": {}, "source": [ "# Module 2 — Lab 3: Data Structures and Raw Log Analysis\n", "\n", "**Topics:** list, tuple, dictionary, set, parsing\n", "\n", "\n", "> **Instructions:** Run the cells in order. Complete every `TODO`, then run the **Self-check** cell. Do not open the Solution notebook before attempting the tasks yourself.\n" ] }, { "cell_type": "markdown", "id": "53230dd3", "metadata": {}, "source": [ "## Input data\n", "\n", "Each log line contains four comma-separated fields: `timestamp,source_ip,username,status`.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ad6600dc", "metadata": {}, "outputs": [], "source": [ "raw_logs = [\n", " \"2026-09-01T08:00:00Z,203.0.113.55,alice,SUCCESS\",\n", " \"2026-09-01T08:01:00Z,198.51.100.14,admin,FAILED\",\n", " \"2026-09-01T08:01:20Z,198.51.100.14,root,FAILED\",\n", " \"2026-09-01T08:02:00Z,192.0.2.88,bob,SUCCESS\",\n", " \"2026-09-01T08:02:30Z,198.51.100.14,guest,FAILED\",\n", " \"2026-09-01T08:03:00Z,192.0.2.99,carol,FAILED\",\n", "]\n", "\n", "critical_fields = (\"timestamp\", \"source_ip\", \"username\", \"status\")\n", "blocked_ips = {\"198.51.100.14\", \"203.0.113.200\"}\n", "\n", "print(f\"Rows: {len(raw_logs)}\")\n", "print(\"Critical fields:\", critical_fields)\n" ] }, { "cell_type": "markdown", "id": "b99fcf3b", "metadata": { "tags": [ "exercise" ] }, "source": [ "## 1. Convert one log line into a dictionary\n" ] }, { "cell_type": "code", "execution_count": null, "id": "a7f7c548", "metadata": { "tags": [ "exercise" ] }, "outputs": [], "source": [ "def parse_log_line(line):\n", " # TODO 1: split the line into four variables\n", " # TODO 2: return a dictionary with the four keys in critical_fields\n", " # Normalize username to lowercase and status to uppercase.\n", " pass\n", "\n", "\n", "sample_event = parse_log_line(raw_logs[0])\n", "print(sample_event)\n" ] }, { "cell_type": "markdown", "id": "0d95749d", "metadata": { "tags": [ "exercise" ] }, "source": [ "## 2. Create an event list and a set of unique IP addresses\n" ] }, { "cell_type": "code", "execution_count": null, "id": "809d56f4", "metadata": { "tags": [ "exercise" ] }, "outputs": [], "source": [ "# TODO 3: create the events list by parsing each raw log line\n", "events = []\n", "\n", "# TODO 4: create the unique_ips set from events\n", "unique_ips = set()\n", "\n", "print(\"Unique IPs:\", sorted(unique_ips))\n", "print(\"Number of unique IPs:\", len(unique_ips))\n" ] }, { "cell_type": "markdown", "id": "f97d6524", "metadata": { "tags": [ "exercise" ] }, "source": [ "## 3. Count failed logins with a dictionary\n" ] }, { "cell_type": "code", "execution_count": null, "id": "7bb93fd8", "metadata": { "tags": [ "exercise" ] }, "outputs": [], "source": [ "failed_by_ip = {}\n", "\n", "# TODO 5: count FAILED events for each source_ip.\n", "# Hint: dictionary.get(ip, 0)\n", "\n", "print(failed_by_ip)\n" ] }, { "cell_type": "markdown", "id": "1b67dd02", "metadata": { "tags": [ "exercise" ] }, "source": [ "## 4. Combine dictionaries and sets to create alerts\n" ] }, { "cell_type": "code", "execution_count": null, "id": "7a77c94f", "metadata": { "tags": [ "exercise" ] }, "outputs": [], "source": [ "alerts = []\n", "\n", "# TODO 6: create an alert when count >= 3 OR the IP is in blocked_ips.\n", "# Each alert is a dictionary containing source_ip, failed_attempts, and blocked.\n", "\n", "print(alerts)\n" ] }, { "cell_type": "markdown", "id": "9f495105", "metadata": {}, "source": [ "## Self-check\n" ] }, { "cell_type": "code", "execution_count": null, "id": "487170fc", "metadata": {}, "outputs": [], "source": [ "assert sample_event[\"username\"] == \"alice\"\n", "assert len(events) == 6\n", "assert unique_ips == {\"203.0.113.55\", \"198.51.100.14\", \"192.0.2.88\", \"192.0.2.99\"}\n", "assert failed_by_ip[\"198.51.100.14\"] == 3\n", "assert alerts == [{\"source_ip\": \"198.51.100.14\", \"failed_attempts\": 3, \"blocked\": True}]\n", "print(\"✓ Lab 3 completed\")\n" ] }, { "cell_type": "markdown", "id": "f634dc9c", "metadata": {}, "source": [ "## Reflection questions\n", "\n", "- Why is a `set` suitable for storing unique IP addresses or a blocklist?\n", "- Why is a `tuple` suitable for field names that should not change?\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.6" } }, "nbformat": 4, "nbformat_minor": 5 }