Installation & Setup
Everything needed to run the AI/DC solution locally is included in the repository. The environment runs in Docker; you interact with it through the infrahubctl CLI and the Infrahub web UI.
Requirements​
- Python 3.11+ (3.12 recommended)
- Docker and Docker Compose (v2)
- uv — Python package manager
- Git — for cloning the repository
The infrahubctl CLI is installed automatically as part of the infrahub-sdk dependency. No separate installation is needed.
Getting started​
Cloning the repository​
First we have to clone the repository.
git clone https://github.com/opsmill/infrahub-solution-ai-dc.git
Install dependencies​
uv sync --all-packages
This installs all Python dependencies including infrahub-sdk (which provides infrahubctl), invoke (task runner), and the solution's own infrahub-solution-ai-dc package.
Configure environment variables​
export INFRAHUB_USERNAME="admin"
export INFRAHUB_PASSWORD="infrahub"
INFRAHUB_USERNAME and INFRAHUB_PASSWORD are used by infrahubctl to authenticate with the Infrahub API. VERSION sets the Infrahub image tag. If not using direnv, source the file manually or export these variables.
One optional variable belongs to the MCP server rather than to infrahubctl, so keep it out of the block above: INFRAHUB_API_TOKEN sets the token an MCP client presents to the Infrahub MCP server. Setting it takes precedence over INFRAHUB_USERNAME / INFRAHUB_PASSWORD for infrahubctl too, so export it only once you have a token to use:
export INFRAHUB_API_TOKEN="<token>"
Alternatively, you can use direnv and define the environment variables in a .envrc file at the root of the project.
Build container image​
uv run inv build
This will build a custom Infrahub container image, this is needed because this solution is using a custom Python package src/infrahub_solution_ai_dc that we want to use in our Generators. This makes importing classes and functions from this package easier and more reliable.
Start Infrahub​
uv run inv start
This downloads the base docker-compose.yml from https://infrahub.opsmill.io (if not already present), builds a custom Docker image, then runs docker compose up -d. The custom image extends the standard Infrahub image with the infrahub-solution-ai-dc Python package — a shared library (in src/infrahub_solution_ai_dc/) that the Generators and Transforms use for common logic such as IP addressing, cabling, and interface sorting. Without it, Generators running inside the task workers would not have access to that shared code. The override file (docker-compose.override.yml) ensures the custom image is used in place of the standard one.
Services started:
| Service | Description | Port |
|---|---|---|
infrahub-server | Infrahub API and web UI | 8000 |
task-manager | Prefect API server for workflow orchestration | 4200 |
task-worker | Infrahub task workers (2 replicas) | — |
database | Neo4j graph database | 7474, 7687 |
cache | Redis | — |
message-queue | RabbitMQ | — |
task-manager-db | PostgreSQL for Prefect | — |
infrahub-mcp | Infrahub MCP server for AI agents | 8001 |
Open http://localhost:8000 in a browser and log in with username admin, password infrahub.
Infrahub MCP server​
inv start also brings up infrahub-mcp, a sidecar container running the Infrahub MCP server. It exposes Infrahub as a set of MCP tools so an AI agent can read the graph and make changes to it, reaching infrahub-server over the internal Docker network. Once the stack is up, check it responds:
curl http://localhost:8001/health
Connecting an agent. The repository ships a .mcp.json at its root, so Claude Code opened in the repository detects the server with no configuration. Detection is not approval: a server that a project defines stays at pending approval until you accept it in an interactive session, and a fresh clone cannot approve its own servers. Approve infrahub at that prompt on first run and the mcp__infrahub__* tools become available. You do this once per clone.
Authentication. The container is configured with no Infrahub credential. Every client authenticates for itself by sending its own token in an Authorization: Bearer header, and a request that carries no such header is rejected. Like Infrahub's own port 8000, the MCP port is published on all interfaces.
Version. The image is pinned in docker-compose.override.yml to registry.opsmill.io/opsmill/infrahub-mcp:v1.1.7. Bump it to another published version with INFRAHUB_MCP_VERSION:
export INFRAHUB_MCP_VERSION="v1.1.8"
uv run inv start
Other settings. INFRAHUB_MCP_LOG_LEVEL (default info) sets how much the container logs, readable with docker compose logs infrahub-mcp. INFRAHUB_MCP_READ_ONLY=true disables the write tools, so agents can query the graph but not change it.
Other MCP clients. Any client that speaks the streamable-http transport can use the same endpoint:
{
"mcpServers": {
"infrahub": {
"type": "streamable-http",
"url": "http://localhost:8001/mcp",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
Agent changes. Writes made through the MCP server do not land on main. Each session gets its own Infrahub branch named mcp/session-<date>-<hex>, and the changes on it reach main through a proposed change that you review — nothing is merged automatically. These are Infrahub branches carrying data changes only; no git branch is created.
Load data​
uv run inv load
This runs four steps in sequence:
infrahubctl schema load schemas— loads the 7 schema files defining the data modelinfrahubctl menu load menus/— loads UI menu configurationinfrahubctl object load objects/— loads all design objects (groups, manufacturers, device types, IPAM, overlay resource pools, profiles, device templates, fabrics, pods, racks, and the seedBlueoverlay tenant)infrahubctl object load repository.yml— registers the Git repository with Infrahub
After inv load, Infrahub imports the repository and reads .infrahub.yml to register Generators, Transforms, queries, and artifact definitions. Verify with infrahubctl repository list. Generators cannot run until this completes.
Load trigger rules​
uv run infrahubctl object load triggers.yml
This creates the CoreGeneratorAction and CoreNodeTriggerRule objects that drive automatic modular Generator execution — including the tenant/overlay trigger (run-tenant-generator). triggers.yml is a committed top-level file that is not part of inv load: it must be loaded after the repository has synced, because the trigger rules reference Generator definitions that need to exist first.
If loaded before Generator definitions are imported, the trigger rules will fail to resolve their action references.
Load supplementary data (optional)​
# NOTE: the data/ directory is NOT loaded by `inv load`.
uv run infrahubctl object load data/permissions.yml # sample operator account `john`
uv run infrahubctl object load data/tenant-red.yml # second, day-two overlay tenant `Red`
The data/ directory holds material that is intentionally kept out of the default load: a sample operator persona (account john, role and group definitions) and a second overlay tenant (Red on Fabric-A) used to demonstrate scoped, non-clobbering day-two multi-tenant generation. Load these manually when you want them.
Repository structure​
The repository is self-contained — everything below is included. You do not need to build any of these from scratch.
Schemas​
| File | Contents |
|---|---|
schemas/logical_design.yml | NetworkFabric and NetworkPod — the design hierarchy with Generator signaling attributes (checksum, amount_of_spines) and the fabric overlay ASN |
schemas/physical_location.yml | LocationHall and LocationRack — physical locations with Generator target support |
schemas/device.yml | NetworkDevice, NetworkInterface, and NetworkLink — devices with computed attributes, ASN, route-reflector role, and VTEP loopback |
schemas/ipam.yml | IpamIPPrefix with role-based allocation and IpamIPAddress |
schemas/overlay.yml | NetworkTenant, NetworkVrf, and NetworkSegment — the overlay intent model |
schemas/routing.yml | NetworkBGPSession — the stored iBGP EVPN control-plane sessions |
schemas/generator.yml | GeneratorTarget generic enabling trigger-based modular Generator execution via checksum attribute |
Generators​
| File | Responsibility |
|---|---|
generators/generate_fabric.py | FabricGenerator — IP pool and overlay ASN allocation, super spine creation, writes checksums to child Pods |
generators/generate_pod.py | PodGenerator — validates fabric complete, spine creation, spine-to-super-spine cabling, iBGP EVPN sessions, writes checksums to child Racks |
generators/generate_rack.py | RackGenerator — validates pod complete, leaf creation, leaf-to-spine cabling, VTEP loopbacks |
generators/generate_tenant.py | OverlayGenerator (registered as generate-tenant, targets the tenants group) — allocates overlay identifiers and materializes tenant/VRF/segment state |
Each Generator has a paired .gql query file in the same directory. The .infrahub.yml file in the repository root wires Generator definitions to their queries and target groups. Note the naming split for the overlay Generator: the definition, file, and query are named generate-tenant/generate_tenant, while the Python class is OverlayGenerator.
Transforms and artifacts​
| File | Purpose |
|---|---|
transforms/startup_config.gql + templates/startup_config_{cisco,arista,dell,juniper}.j2 | Four per-vendor Jinja2 Transforms, each producing a startup configuration artifact for the devices in its vendor group ({manufacturer}_devices). See Multivendor configuration. |
transforms/cabling_plan.py / .gql | Python Transform producing a CSV cabling plan artifact per fabric |
transforms/computed_interface_description.py / .gql | Python Transform applied as a computed attribute on each interface |
Demo data (object files)​
Loaded in numbered order by infrahubctl object load objects/:
| File | Contents |
|---|---|
objects/01_groups.yml | CoreStandardGroup objects: halls, racks, fabrics, pods, devices, tenants, and the per-vendor device groups cisco_devices / arista_devices / dell_devices / juniper_devices |
objects/02_manufacturer.yml | Equipment manufacturers (Cisco, Arista, Dell, Juniper) |
objects/03_device_type.yml | Per-vendor device types for leaf, spine, and super spine switches |
objects/04_ipam.yml | IP supernet (10.0.0.0/8) and FabricSupernetPool |
objects/05_profiles.yml | Interface role profiles (MTU, role assignments) |
objects/06_device_template.yml | Per-vendor device templates defining interface layouts per device role |
objects/07_pools.yml | Overlay resource pools: ASN, L2VNI, L3VNI, and VLAN number pools |
objects/10_fabric.yml | Fabric and Pod objects: Fabric-A (Cisco), Fabric-B (Arista), Fabric-C (Dell), Fabric-D (Juniper) — each single-vendor |
objects/11_rack.yml | Rack objects across all four fabrics with leaf counts and per-vendor template assignments |
objects/12_overlay.yml | Seed overlay tenant Blue on Fabric-A, with a VRF and segments (routed, rack-scoped, and L2-only examples) |
Trigger rules live in the top-level triggers.yml (not under objects/) and are loaded separately after repository sync — see Load trigger rules. Supplementary data (data/permissions.yml, data/tenant-red.yml) is loaded manually — see Load supplementary data.
Infrastructure​
| File | Purpose |
|---|---|
Dockerfile | Builds a custom Infrahub image with the infrahub-solution-ai-dc Python package installed |
docker-compose.override.yml | Replaces standard Infrahub image with the custom build; mounts src/ for live code changes; defines the infrahub-mcp sidecar |
.mcp.json | MCP client configuration pointing at the infrahub-mcp sidecar |
repository.yml | CoreRepository object pointing to /upstream (the mounted repository) |
tasks.py | Invoke task definitions for environment management |
.infrahub.yml | Central configuration wiring Generators, Transforms, queries, and artifacts |
Invoke tasks​
| Task | Command | Description |
|---|---|---|
| Start | inv start | Download compose file (if needed) and start all services |
| Stop | inv stop | Stop containers, remove networks (data preserved) |
| Destroy | inv destroy | Stop containers, remove networks and volumes (full reset) |
| Restart | inv restart | Restart all services (or inv restart --component=<name> for one) |
| Load | inv load | Load schemas, menus, objects, and register repository |
| Load schema | inv load-schema | Load schema files only |
| Load menu | inv load-menu | Load menu definitions only |
| Build | inv build | Build the custom Docker image locally |
| Test | inv test | Run pytest test suite |
| Format | inv format | Run Ruff formatter and auto-fix linting issues |
Troubleshooting​
Services fail to start​
- Check Docker is running:
docker info - Check port conflicts: Infrahub uses ports 8000, 4200, 7474, 7687, and 8001 for the MCP server
- View logs:
docker compose logs infrahub-serverordocker compose logs task-worker - Full reset:
uv run inv destroy && uv run inv start
Repository not syncing​
- Verify with
infrahubctl repository list - The repository mounts from
/upstreaminside the container — checkdocker compose logs task-workerfor import errors - Schema mismatches between what
.infrahub.ymlreferences and what is loaded can block sync
MCP tool calls fail authentication​
- Check what your shell is presenting:
echo $INFRAHUB_API_TOKEN - A token exported for a different Infrahub instance fails closed here rather than falling back — unset it or export one for this stack
- Confirm the server is up:
curl http://localhost:8001/health
MCP port already in use​
- Move both the published port and the client address with one variable:
export INFRAHUB_MCP_PORT=8011, thenuv run inv start - Export it in the shell your agent runs in as well, and restart the agent — it reads the port when it loads
.mcp.json - Confirm what the container publishes:
docker compose ps infrahub-mcp
MCP image fails to pull​
- Confirm the pinned tag exists:
docker manifest inspect registry.opsmill.io/opsmill/infrahub-mcp:v1.1.7 - Select a different published version:
export INFRAHUB_MCP_VERSION=<version> - Point at a different registry or repository:
export INFRAHUB_MCP_DOCKER_IMAGE=<image>