Skip to content

Python Client

Example Python client library for hier-config-api.

Simple Client Implementation

import requests
from typing import List, Dict, Any, Optional
from dataclasses import dataclass

@dataclass
class RemediationResult:
    """Remediation result container."""
    remediation_id: str
    platform: str
    remediation_config: str
    rollback_config: str
    summary: Dict[str, int]
    tags: Dict[str, List[str]]

class HierConfigAPIClient:
    """Client for hier-config-api."""

    def __init__(self, base_url: str = "http://localhost:8000"):
        """Initialize client.

        Args:
            base_url: Base URL of the API server
        """
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()

    def _post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        """Make POST request."""
        url = f"{self.base_url}{endpoint}"
        response = self.session.post(url, json=data)
        response.raise_for_status()
        return response.json()

    def _get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Make GET request."""
        url = f"{self.base_url}{endpoint}"
        response = self.session.get(url, params=params)
        response.raise_for_status()
        return response.json()

    def compare_configs(
        self,
        platform: str,
        running_config: str,
        intended_config: str
    ) -> Dict[str, Any]:
        """Compare two configurations.

        Args:
            platform: Platform type
            running_config: Current configuration
            intended_config: Desired configuration

        Returns:
            Comparison result with diff
        """
        return self._post("/api/v1/configs/compare", {
            "platform": platform,
            "running_config": running_config,
            "intended_config": intended_config
        })

    def generate_remediation(
        self,
        platform: str,
        running_config: str,
        intended_config: str,
        tag_rules: Optional[List[Dict[str, Any]]] = None,
        include_tags: Optional[List[str]] = None,
        exclude_tags: Optional[List[str]] = None
    ) -> RemediationResult:
        """Generate remediation configuration.

        Args:
            platform: Platform type
            running_config: Current configuration
            intended_config: Desired configuration
            tag_rules: Optional tag rules
            include_tags: Tags to include
            exclude_tags: Tags to exclude

        Returns:
            Remediation result
        """
        data = {
            "platform": platform,
            "running_config": running_config,
            "intended_config": intended_config
        }

        if tag_rules:
            data["tag_rules"] = tag_rules
        if include_tags:
            data["include_tags"] = include_tags
        if exclude_tags:
            data["exclude_tags"] = exclude_tags

        result = self._post("/api/v1/remediation/generate", data)

        return RemediationResult(
            remediation_id=result["remediation_id"],
            platform=result["platform"],
            remediation_config=result["remediation_config"],
            rollback_config=result["rollback_config"],
            summary=result["summary"],
            tags=result["tags"]
        )

    def create_report(
        self,
        devices: List[Dict[str, str]]
    ) -> str:
        """Create multi-device report.

        Args:
            devices: List of device configs

        Returns:
            Report ID
        """
        result = self._post("/api/v1/reports", {
            "remediations": devices
        })
        return result["report_id"]

    def get_report_summary(self, report_id: str) -> Dict[str, Any]:
        """Get report summary.

        Args:
            report_id: Report identifier

        Returns:
            Report summary
        """
        return self._get(f"/api/v1/reports/{report_id}/summary")

    def get_report_changes(
        self,
        report_id: str,
        tag: Optional[str] = None,
        min_devices: int = 1
    ) -> Dict[str, Any]:
        """Get detailed changes from report.

        Args:
            report_id: Report identifier
            tag: Filter by tag
            min_devices: Minimum device count

        Returns:
            Change details
        """
        params = {"min_devices": min_devices}
        if tag:
            params["tag"] = tag

        return self._get(f"/api/v1/reports/{report_id}/changes", params)

    def export_report(
        self,
        report_id: str,
        format: str = "json"
    ) -> str:
        """Export report in specified format.

        Args:
            report_id: Report identifier
            format: Export format (json, csv, yaml)

        Returns:
            Report content
        """
        url = f"{self.base_url}/api/v1/reports/{report_id}/export"
        response = self.session.get(url, params={"format": format})
        response.raise_for_status()
        return response.text

    def list_platforms(self) -> List[Dict[str, Any]]:
        """List supported platforms.

        Returns:
            List of platform information
        """
        return self._get("/api/v1/platforms")

    def validate_config(
        self,
        platform: str,
        config_text: str
    ) -> Dict[str, Any]:
        """Validate configuration.

        Args:
            platform: Platform type
            config_text: Configuration to validate

        Returns:
            Validation result
        """
        return self._post(f"/api/v1/platforms/{platform}/validate", {
            "config_text": config_text
        })

    def create_batch_job(
        self,
        devices: List[Dict[str, str]]
    ) -> str:
        """Create batch remediation job.

        Args:
            devices: List of device configs

        Returns:
            Job ID
        """
        result = self._post("/api/v1/batch/remediation", {
            "device_configs": devices
        })
        return result["job_id"]

    def get_batch_status(self, job_id: str) -> Dict[str, Any]:
        """Get batch job status.

        Args:
            job_id: Job identifier

        Returns:
            Job status
        """
        return self._get(f"/api/v1/batch/jobs/{job_id}")

    def get_batch_results(self, job_id: str) -> Dict[str, Any]:
        """Get batch job results.

        Args:
            job_id: Job identifier

        Returns:
            Job results
        """
        return self._get(f"/api/v1/batch/jobs/{job_id}/results")

Usage Examples

Basic Usage

from hier_config_client import HierConfigAPIClient

# Initialize client
client = HierConfigAPIClient("http://localhost:8000")

# Compare configs
result = client.compare_configs(
    platform="cisco_ios",
    running_config="hostname router1",
    intended_config="hostname router2"
)

print(result["unified_diff"])

Generate Remediation

# Generate remediation
remediation = client.generate_remediation(
    platform="cisco_ios",
    running_config=open("current.cfg").read(),
    intended_config=open("desired.cfg").read()
)

print("Remediation:")
print(remediation.remediation_config)

print("\nRollback:")
print(remediation.rollback_config)

print(f"\nSummary: {remediation.summary}")

Multi-Device Report

# Create report
devices = [
    {
        "device_id": "router1",
        "platform": "cisco_ios",
        "running_config": "...",
        "intended_config": "..."
    },
    {
        "device_id": "router2",
        "platform": "cisco_ios",
        "running_config": "...",
        "intended_config": "..."
    }
]

report_id = client.create_report(devices)

# Get summary
summary = client.get_report_summary(report_id)
print(f"Devices with changes: {summary['devices_with_changes']}")

# Export to CSV
csv_data = client.export_report(report_id, format="csv")
with open("report.csv", "w") as f:
    f.write(csv_data)

Batch Processing

import time

# Create batch job
job_id = client.create_batch_job(devices)
print(f"Job ID: {job_id}")

# Monitor progress
while True:
    status = client.get_batch_status(job_id)
    print(f"Progress: {status['progress']:.1f}%")

    if status['status'] in ['completed', 'failed']:
        break

    time.sleep(2)

# Get results
results = client.get_batch_results(job_id)
for result in results['results']:
    print(f"{result['device_id']}: {result['status']}")

Context Manager

class HierConfigAPIClient:
    # ... previous code ...

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.session.close()

# Usage with context manager
with HierConfigAPIClient("http://localhost:8000") as client:
    result = client.compare_configs(...)

Async Client

import httpx
from typing import List, Dict, Any

class AsyncHierConfigAPIClient:
    """Async client for hier-config-api."""

    def __init__(self, base_url: str = "http://localhost:8000"):
        self.base_url = base_url.rstrip("/")
        self.client = httpx.AsyncClient()

    async def compare_configs(
        self,
        platform: str,
        running_config: str,
        intended_config: str
    ) -> Dict[str, Any]:
        """Compare configurations asynchronously."""
        url = f"{self.base_url}/api/v1/configs/compare"
        response = await self.client.post(url, json={
            "platform": platform,
            "running_config": running_config,
            "intended_config": intended_config
        })
        response.raise_for_status()
        return response.json()

    async def close(self):
        """Close client."""
        await self.client.aclose()

# Usage
import asyncio

async def main():
    client = AsyncHierConfigAPIClient()
    try:
        result = await client.compare_configs(...)
        print(result)
    finally:
        await client.close()

asyncio.run(main())