Spaces:
Sleeping
Sleeping
Download server/scenario_engine.py from SERG4NT/incidentops: direct link, hf CLI and curl.
- Browser
- Download file 6.65 kB
-
https://huggingface.co/spaces/SERG4NT/incidentops/resolve/main/server/scenario_engine.py
- Command line
-
hf download hf://spaces/SERG4NT/incidentops/server/scenario_engine.py
-
curl -L -o scenario_engine.py https://huggingface.co/spaces/SERG4NT/incidentops/resolve/main/server/scenario_engine.py
6.65 kB
| import json | |
| import random | |
| from typing import Dict, List, Any | |
| from pathlib import Path | |
| from server.models import Alert, LogEntry, MetricSeries, DeployEvent | |
| class ScenarioEngine: | |
| def __init__(self): | |
| self.templates_dir = Path(__file__).parent.parent / 'scenarios' | |
| self.templates = self._load_templates() | |
| def _load_templates(self) -> Dict[str, Dict]: | |
| templates = {} | |
| for f in self.templates_dir.glob('*.json'): | |
| with open(f) as file: | |
| templates[f.stem] = json.load(file) | |
| return templates | |
| def generate(self, template_id: str, seed: int) -> Dict[str, Any]: | |
| rng = random.Random(seed) | |
| if template_id not in self.templates: | |
| raise ValueError(f"Scenario template {template_id} not found.") | |
| template = self.templates[template_id] | |
| # Generate base timeline (60 minutes of logs/metrics) | |
| timeline = self._generate_timeline(rng) | |
| # Inject root cause anomaly | |
| if 'root_anomaly' in template: | |
| root_anomaly = template['root_anomaly'] | |
| self._inject_root_anomaly(timeline, root_anomaly, rng) | |
| # Cascade to dependents | |
| for dep in template.get('dependents', []): | |
| lag = rng.randint(2, 8) | |
| self._cascade_to_dependent(timeline, dep, lag, rng) | |
| # Generate logs from anomalies | |
| logs = self._generate_logs(timeline, template, rng) | |
| # Build final realistic alert list with noise | |
| base_alerts = template.get('real_alerts', []) | |
| noise_alerts = self._generate_noise_alerts(rng.randint(4, 7), rng) | |
| all_alerts = base_alerts + noise_alerts | |
| rng.shuffle(all_alerts) | |
| # Deploy history | |
| deploys = self._generate_deploy_history(template, rng) | |
| return { | |
| 'id': template_id, | |
| 'seed': seed, | |
| 'alerts': all_alerts, | |
| 'logs': logs, | |
| 'metrics': timeline['metrics'], | |
| 'deploy_history': deploys, | |
| 'root_cause_keywords': template.get('root_cause_keywords', {}), | |
| 'valid_fixes': template.get('valid_fixes', []), | |
| 'relevant_services': template.get('relevant_services', []), | |
| } | |
| def _generate_timeline(self, rng: random.Random) -> Dict: | |
| timestamps = [f"2023-10-01T00:{m:02d}:00Z" for m in range(60)] | |
| metrics = {} | |
| for service in ['payment-api', 'order-service', 'db-cluster']: | |
| metrics[f"{service}.cpu_percent"] = MetricSeries( | |
| name=f"{service}.cpu_percent", unit="%", timestamps=timestamps, | |
| values=[rng.uniform(20, 40) for _ in timestamps] | |
| ) | |
| metrics[f"{service}.memory_percent"] = MetricSeries( | |
| name=f"{service}.memory_percent", unit="%", timestamps=timestamps, | |
| values=[rng.uniform(40, 60) for _ in timestamps] | |
| ) | |
| metrics[f"{service}.requests_5xx"] = MetricSeries( | |
| name=f"{service}.requests_5xx", unit="ops", timestamps=timestamps, | |
| values=[rng.uniform(0, 0.5) for _ in timestamps] | |
| ) | |
| return {'timestamps': timestamps, 'metrics': metrics} | |
| def _inject_root_anomaly(self, timeline: Dict, anomaly: Dict, rng: random.Random): | |
| start_min = anomaly.get('start_min', 30) | |
| end_min = anomaly.get('end_min', 60) | |
| metric_name = anomaly.get('metric') | |
| if metric_name in timeline['metrics']: | |
| for i in range(start_min, end_min): | |
| timeline['metrics'][metric_name].values[i] += rng.uniform(40, 60) | |
| def _cascade_to_dependent(self, timeline: Dict, dep: Dict, lag: int, rng: random.Random): | |
| metric_name = dep.get('metric') | |
| # Simple cascade logic at start+lag | |
| if metric_name in timeline['metrics']: | |
| for i in range(30 + lag, 60): | |
| timeline['metrics'][metric_name].values[i] += rng.uniform(5, 15) * dep.get('cascade_factor', 1) | |
| def _generate_logs(self, timeline: Dict, template: Dict, rng: random.Random) -> List[Dict]: | |
| logs = [] | |
| for i, ts in enumerate(timeline['timestamps']): | |
| for srv in ['payment-api', 'order-service', 'db-cluster']: | |
| # Background info logs | |
| if rng.random() > 0.5: | |
| logs.append({"ts": ts, "level": "INFO", "service": srv, "message": f"Processed request successfully for {srv}"}) | |
| # If anomaly is happening, inject errors | |
| if i >= 30: # Hardcoded anomaly window for simplicity | |
| if srv in template.get('relevant_services', []): | |
| if rng.random() > 0.7: | |
| logs.append({"ts": ts, "level": "ERROR", "service": srv, "message": f"Connection pool exhausted or memory high in {srv}"}) | |
| return logs | |
| def _generate_noise_alerts(self, count: int, rng: random.Random) -> List[Dict]: | |
| noise = [] | |
| for i in range(count): | |
| noise.append({ | |
| "alert_id": f"noise_{i}", | |
| "title": f"Intermittent latency in {rng.choice(['frontend', 'worker-queue'])}", | |
| "service": rng.choice(['frontend', 'worker-queue']), | |
| "timestamp": f"2023-10-01T00:{rng.randint(0, 59):02d}:00Z", | |
| "raw_value": rng.uniform(500, 800), | |
| "threshold": 500, | |
| # Intentionally missing true_severity and true_team or set to false flags for grading | |
| }) | |
| return noise | |
| def _generate_deploy_history(self, template: Dict, rng: random.Random) -> List[Dict]: | |
| deploys = [] | |
| services = ['payment-api', 'order-service', 'frontend'] | |
| for i in range(10): | |
| srv = rng.choice(services) | |
| deploys.append({ | |
| "deploy_id": f"dep_{i}", | |
| "service": srv, | |
| "sha": f"{rng.randint(100000, 999999)}", | |
| "timestamp": f"2023-10-01T00:{rng.randint(0, 59):02d}:00Z", | |
| "deployer": f"user_{rng.randint(1, 5)}", | |
| "diff_summary": "Minor fixes and updates" | |
| }) | |
| # Inject bad deploy if missing | |
| if 'root_cause_keywords' in template and 'v2.4.1' in template['root_cause_keywords'].get('required', []): | |
| deploys.append({ | |
| "deploy_id": "dep_bad", | |
| "service": "payment-api", | |
| "sha": "bad123", | |
| "timestamp": "2023-10-01T00:25:00Z", | |
| "deployer": "user_1", | |
| "diff_summary": "Update payment-api to v2.4.1" | |
| }) | |
| return sorted(deploys, key=lambda x: x['timestamp']) | |