Spaces:
Paused
Paused
| from __future__ import annotations | |
| import re | |
| from pathlib import Path | |
| from sysadmin_env.models import DiagnosticTrigger | |
| from sysadmin_env.models import DifficultyTier | |
| from sysadmin_env.models import TaskMetadata | |
| from sysadmin_env.models import TaskScenarioDefinition | |
| from sysadmin_env.models import TaskScenarioState | |
| TASK_ID = "network_broken" | |
| COMPLETION_HEALTH = 0.99 | |
| ROUTE_PATH = Path("etc/network/routes/default") | |
| ADDR_PATH = Path("etc/network/interfaces/eth0.addr") | |
| LINK_PATH = Path("etc/network/interfaces/eth0.link") | |
| RESOLV_PATH = Path("etc/resolv.conf") | |
| PING_FLAG_PATH = Path("run/network.ping") | |
| BROKEN_ROUTE = "default via 192.0.2.1 dev eth9\n" | |
| FIXED_ROUTE = "default via 10.0.2.2 dev eth0\n" | |
| BROKEN_RESOLV = "nameserver 0.0.0.0\n" | |
| FIXED_RESOLV = "nameserver 1.1.1.1\n" | |
| def build_definition(base_filesystem_path: str) -> TaskScenarioDefinition: | |
| metadata = TaskMetadata( | |
| task_id=TASK_ID, | |
| difficulty=DifficultyTier.hard, | |
| description="broken network namespace with corrupted routing and dns", | |
| max_steps=70, | |
| time_limit=480.0, | |
| base_filesystem_path=base_filesystem_path, | |
| ) | |
| return TaskScenarioDefinition( | |
| metadata=metadata, | |
| requires_network_isolation=True, | |
| diagnostic_triggers=diagnostic_triggers(), | |
| ) | |
| def diagnostic_triggers() -> list[DiagnosticTrigger]: | |
| return [ | |
| DiagnosticTrigger( | |
| fact_id="routes_checked", | |
| command_patterns=[r"ip\s+route\s+show", r"route\s+-n"], | |
| reward=0.07, | |
| ), | |
| DiagnosticTrigger( | |
| fact_id="addresses_checked", | |
| command_patterns=[r"ip\s+addr", r"ifconfig\b"], | |
| reward=0.05, | |
| ), | |
| DiagnosticTrigger( | |
| fact_id="links_checked", | |
| command_patterns=[r"ip\s+link", r"ethtool\b"], | |
| reward=0.05, | |
| ), | |
| DiagnosticTrigger( | |
| fact_id="connectivity_checked", | |
| command_patterns=[r"ping\b", r"curl\b"], | |
| reward=0.06, | |
| ), | |
| DiagnosticTrigger( | |
| fact_id="dns_checked", | |
| command_patterns=[r"cat\s+.+resolv\.conf", r"grep\s+.+resolv\.conf"], | |
| reward=0.05, | |
| ), | |
| ] | |
| def prepare_filesystem(root: str | Path) -> None: | |
| root_path = Path(root) | |
| for relative in [ | |
| Path("etc/network/routes"), | |
| Path("etc/network/interfaces"), | |
| Path("run"), | |
| Path("etc"), | |
| Path("usr/local/bin"), | |
| Path("root"), | |
| ]: | |
| (root_path / relative).mkdir(parents=True, exist_ok=True) | |
| (root_path / ROUTE_PATH).write_text(BROKEN_ROUTE) | |
| (root_path / ADDR_PATH).write_text("10.0.2.15/24\n") | |
| (root_path / LINK_PATH).write_text("up\n") | |
| (root_path / RESOLV_PATH).write_text(BROKEN_RESOLV) | |
| (root_path / PING_FLAG_PATH).write_text("broken\n") | |
| _write_executable(root_path / "usr/local/bin/ip", _ip_stub()) | |
| _write_executable(root_path / "usr/local/bin/route", _route_stub()) | |
| _write_executable(root_path / "usr/local/bin/ping", _ping_stub()) | |
| def inject_fault(root: str | Path) -> None: | |
| prepare_filesystem(root) | |
| def observe_command(root: str | Path, command: str, _result) -> None: | |
| root_path = Path(root) | |
| if re.search(r"\b(ip\s+route|ip\s+addr|ip\s+link|ping|resolv\.conf)\b", command, flags=re.IGNORECASE): | |
| (root_path / PING_FLAG_PATH).write_text("diagnosed\n") | |
| def synchronize(root: str | Path) -> None: | |
| root_path = Path(root) | |
| if not (root_path / PING_FLAG_PATH).exists(): | |
| (root_path / PING_FLAG_PATH).write_text("broken\n") | |
| def grade(root: str | Path) -> TaskScenarioState: | |
| root_path = Path(root) | |
| route_fixed = (root_path / ROUTE_PATH).read_text() == FIXED_ROUTE | |
| dns_fixed = (root_path / RESOLV_PATH).read_text() == FIXED_RESOLV | |
| connectivity_restored = _connectivity_restored(root_path, route_fixed, dns_fixed) | |
| routing_diagnosed = route_fixed or (root_path / PING_FLAG_PATH).read_text().strip() == "diagnosed" | |
| health = 0.0 | |
| if routing_diagnosed: | |
| health += 0.2 | |
| if route_fixed: | |
| health += 0.3 | |
| if dns_fixed: | |
| health += 0.2 | |
| if connectivity_restored: | |
| health += 0.29 | |
| if connectivity_restored: | |
| health = COMPLETION_HEALTH | |
| return TaskScenarioState( | |
| health=health, | |
| done=connectivity_restored, | |
| details={ | |
| "routing_issue_diagnosed": routing_diagnosed, | |
| "default_route_restored": route_fixed, | |
| "dns_resolution_restored": dns_fixed, | |
| "outbound_connectivity_restored": connectivity_restored, | |
| }, | |
| ) | |
| def command_reveals_fact(command: str, trigger: DiagnosticTrigger) -> bool: | |
| return any(re.search(pattern, command, flags=re.IGNORECASE) for pattern in trigger.command_patterns) | |
| def _connectivity_restored(root_path: Path, route_fixed: bool, dns_fixed: bool) -> bool: | |
| if not route_fixed or not dns_fixed: | |
| return False | |
| return (root_path / LINK_PATH).read_text().strip() == "up" | |
| def _write_executable(path: Path, content: str) -> None: | |
| path.write_text(content) | |
| path.chmod(0o755) | |
| def _ip_stub() -> str: | |
| return """#!/bin/sh | |
| routefile="/etc/network/routes/default" | |
| addrfile="/etc/network/interfaces/eth0.addr" | |
| linkfile="/etc/network/interfaces/eth0.link" | |
| if [ "$1" = "route" ] && [ -z "$2" ]; then | |
| cat "$routefile" | |
| exit 0 | |
| fi | |
| if [ "$1" = "route" ] && [ "$2" = "show" ]; then | |
| cat "$routefile" | |
| exit 0 | |
| fi | |
| if [ "$1" = "route" ] && [ "$2" = "add" ] && [ "$3" = "default" ] && [ "$4" = "via" ] && [ -n "$5" ]; then | |
| printf 'default via %s dev eth0\n' "$5" > "$routefile" | |
| exit 0 | |
| fi | |
| if [ "$1" = "route" ] && [ "$2" = "del" ] && [ "$3" = "default" ]; then | |
| : > "$routefile" | |
| exit 0 | |
| fi | |
| if [ "$1" = "addr" ]; then | |
| addr="$(cat "$addrfile")" | |
| printf '%s\n' "2: eth0 inet $addr" | |
| exit 0 | |
| fi | |
| if [ "$1" = "link" ]; then | |
| state="$(cat "$linkfile")" | |
| printf '%s\n' "2: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 state $state" | |
| exit 0 | |
| fi | |
| printf '%s\n' "unsupported ip command" >&2 | |
| exit 1 | |
| """ | |
| def _route_stub() -> str: | |
| return """#!/bin/sh | |
| exec ip route show | |
| """ | |
| def _ping_stub() -> str: | |
| return """#!/bin/sh | |
| target="" | |
| for arg in "$@"; do | |
| case "$arg" in | |
| -*) ;; | |
| [0-9]) ;; | |
| *) target="$arg" ;; | |
| esac | |
| done | |
| route="$(cat /etc/network/routes/default 2>/dev/null)" | |
| dns="$(cat /etc/resolv.conf 2>/dev/null)" | |
| link="$(cat /etc/network/interfaces/eth0.link 2>/dev/null)" | |
| if [ "$link" != "up" ]; then | |
| printf '%s\n' "link down" >&2 | |
| exit 1 | |
| fi | |
| case "$target" in | |
| *[!0-9.]* ) | |
| if [ "$route" = "default via 10.0.2.2 dev eth0" ] && [ "$dns" = "nameserver 1.1.1.1" ]; then | |
| printf '%s\n' "1 packets transmitted 1 received" | |
| exit 0 | |
| fi | |
| printf '%s\n' "temporary failure in name resolution" >&2 | |
| exit 1 | |
| ;; | |
| * ) | |
| if [ "$route" = "default via 10.0.2.2 dev eth0" ]; then | |
| printf '%s\n' "1 packets transmitted 1 received" | |
| exit 0 | |
| fi | |
| printf '%s\n' "network is unreachable" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| """ | |