Skip to main content

What this demo shows

Two readers land on this page. One is evaluating Infrahub and has no interest in optics. The other works in optics every day and already owns a planner and a controller. The first half below is written for the first reader and the second half for the second. The last two sections are for both.

If you want the result before the reading, the quick start takes you from a fresh clone to a merge Infrahub refuses.

For the network automation engineer​

Each row below names a capability first, then the scenario in this demo that exercises it, then a link to the page that walks it. The optical detail is the example rather than the subject. A wavelength here plays the part a prefix, a VLAN or a circuit would play in your own domain, and the mechanism is the same one you would use.

The capability map​

Infrahub capabilityWhat this demo does with itScenario, and where it is walked
BranchesEvery service is provisioned on a branch cut from the default one. The plant sits on the default branch and no service is ever written there.Scenario one, create the branch and the request
Proposed changesOpening a proposed change from the branch runs the whole pipeline: fourteen checks, one generator and three artifact definitions, each reported on the change. The branch has to be created with --sync-with-git for that, or the change runs the two built-in validators and stops.Scenario one, review and merge
Checks that gate a merge.infrahub.yml registers fourteen check definitions. provisionable reads status and fails the change for a service the generator left at rejected that nobody signed for. The reason code and the detail are what make the message readable.Madrid to Warsaw at DP-16QAM, the regenerator pair and the quick start
GeneratorsOne generator definition turns a service request that names no route into a carrier, an optical path, 25 ordered hops and two containers.Scenario one, run the generator
ArtifactsThree artifact definitions: a JSON service trace on every service, and two SVG maps rendered once per PoP, each from its own stored query.Scenario five and the two maps every PoP carries
Python transformsTen transforms, each bound to its own stored query, render every report in the demo from live data.Scenario eight, the AI services against their budgets
Stored GraphQL queries25 queries, one per report or check, so a query two consumers share is never a query neither can change.Adding a report
Object files in Gitobjects/ holds the plant and loads on every init. demo/ holds scenario input and is loaded by hand onto a branch.objects/ against demo/
Schema as codeEight schema files loaded as one payload, with flat generics composed on the concrete nodes.The files
Computed attributes32 of them across the eight files, every one Jinja2, most turning a scaled integer into a value a person can read.Display attributes
Schema constraintsEnums, min_value and max_value, optional: false with a default_value, and uniqueness constraints in the ["relationship", "attribute__value"] form.The enum and the default_value at the six reason codes, the bounds at the rules that constrain a change, the uniqueness constraint at a regenerated circuit
on_delete: cascadeUsed on the two relationships that own their children: a device's ports, and an optical path's hops. No page walks it.Not walked
Custom menusmenus/otn.yml builds the sidebar as five top-level entries and fifteen leaves, and every kind sets include_in_menu: false so nothing appears twice.Menu and navigation
Groups as targetsA CoreGeneratorGroup fires the generator. A CoreStandardGroup scoped to the PoPs is what the two map artifacts render against.Scenario one, create the branch and the request
The repository as a Git integrationThe schema, the object files, the checks, the generator and the transforms all come from this repository. invoke init registers it, and that is what turns the pipeline on.Registering the repository

How a check decides a merge​

Three facts carry most of the weight, and they are worth stating on their own because they are the part that translates directly to your domain.

Any check that logs an error blocks the merge. log_error blocks and log_info annotates. There is no log_warning in between, so a check has no way to soften a finding into advice.

A check can be global rather than targeted. provisionable is registered with no targets, so it judges every service on the branch instead of the ones your change touched. Adding a span, retiring a mode or filling a corridor can make a service nobody edited impossible to build, and a targeted check bound to the edited objects would report green over it.

A refusal can be signed for. refusal_accepted is a Boolean on the service with default_value: false. Set it and the branch merges with the refusal on the record, which is why scenario two merges: the 400G on the saturated Frankfurt to Milan corridor is refused for slots, and demo/90_fra_mil_saturated.yml signs for that refusal. The scenario that actually holds a merge shut is demo/06_mad_waw_16qam.yml, where nothing signs for anything. Concepts has the decision tree.

For the optical engineer​

An optical team does three jobs with a corridor. Design it, turn it up, and then answer questions about it for as long as it carries traffic: which services a duct cut takes down, whether two circuits are diverse, what is committed on a corridor nobody has built yet.

The first two jobs have tools. A planning tool designs the corridor and needs no live equipment to do it. A domain controller turns the service up, and it holds planned objects too. Both are scoped, the planning tool to one project and one vendor, the controller to one domain. The third job crosses every vendor, every layer and every project, which is why it usually ends up in a spreadsheet.

This demo holds that third record: versioned, held in one graph, and reviewed before it becomes true. It runs no propagation simulation, writes to no device, and reconciles against no external system. What this demo does not exercise is the full list.

Where this sits next to your planner and your NMS​

QuestionOffline plannerDomain controllerThis demo
Design a corridor: amplifier siting, budget, bill of materialsYes. This is the tool for it.NoNo. It holds the result of that work, not the work
Receive power and error rate on a channel right nowNoYes, with historyThe last reading only, carried as a fact on a monitor
Turn the service up on the hardwareNoYesNo. Nothing here writes to a device
What is committed but not yet built, across vendorsInside one project fileAs planned objects, inside one domainOn a branch, across the whole plant
Why this route and not the other oneWith whoever ran the toolNoThe path, its ordered hops, the running budget, and the proposed change where a person signed
What drops if one duct is cutWithin its own projectWithin its own domainScenario four
Where a coloured pluggable in a router lands on the line systemIt is not that tool's transponderSplit across two controllersOne chain of objects, the one below. Three OpenZR+ wavelengths run router to router here. A 400ZR reaches none of these sections, and the model says so and says why
Which IP link rides which wavelength rides which ductNoNoOne query, and scenario seven reads it
What the plan looked like last quarterAn older file, if anyone kept itRarelyThe branch and the commit history

The first three rows are jobs the planning tool and the controller already do, and this demo does not attempt any of them. The remaining six fall between the two tools.

What varies by vendor, and where the model puts it​

What variesWhere the model puts itWhat a second vendor costs
The mode catalog: reach, required OSNR, baud, forward error correctionOtnOpticalMode objects in objects/03_optical_modes.ymlMore objects. No schema change and no code change
How much spectrum a mode occupiesA width on the mode, fitted against OtnFrequencyGridNothing. channel_collision reads the width and never asks who built the card
Insertion loss and noise figurevendor and model on OtnOpticalElement, with the loss fields beside themObjects per model
An alien wavelength on a line system another operator builtA transponder whose carrier crosses sections built from other equipmentNothing. No relationship in the hop chain is scoped to a vendor
Framing and correction latencyfec_latency_ns on the mode, framing_latency_ns on the ODU switchValues per model, added up by scenario eight
Which optic supplies a modeOtnTransceiverType, with the modes each part number supportsMore catalog entries. transceiver_mode_support refuses a mode the fitted part cannot run, whoever made it
Naming and slot conventionsThe human_friendly_id on each kindA different string

Every racked element in this plant loads as vendor: Generic. The field exists and the dataset leaves it unnamed, so the table above says where a vendor difference goes rather than which vendor. A second vendor is more objects in objects/, reviewed on a branch like any other change.

One model, every layer it touches​

LayerWhat the demo holdsKinds
Duct and shared riskWhich routes share a duct, which is what makes two services not diverseOtnConduit
FiberAttenuation, dispersion, group index, Raman gain where it is pumped, and loss added on purposeOtnFiberSpan, OtnFiberType, OtnRamanPump, OtnFixedAttenuator, OtnVariableAttenuator
SpectrumThe 50 GHz grid, the coarse grid, what each carrier occupies on both, and which fiber a channel lands onOtnFrequencyGrid, OtnCwdmChannel, OtnMuxClientPort, OtnMuxLinePort
Optical carrierOne provisioned wavelength, the mode it runs, and every section it crossesOtnOpticalCarrier, OtnOpticalMode
Digital wrapperThe container hierarchy, tributary slots, and where a wavelength terminates and the next one startsOtnContainer, OtnOduSwitch
Client handoverEthernet, SDH, PDH, Fibre Channel and InfiniBand, each with the container it maps intoOtnClientSignal
Router edgeThe router, its grey port as the endpoint a service names, and the coloured line port where a pluggable puts a wavelength straight on the line systemOtnRouter, OtnRouterPort, OtnLinePort, OtnTransceiver, OtnTransceiverType
Above thatNothing. No VLAN, no prefix, no routing protocolNot exercised

The last row counts as much as the others. This model stops at the router port. A layer boundary is a relationship like any other, so reaching one layer further up is the same work as the layers already modelled here.

Other repositories have done that work for the layers above. infrahub-demo-dc models a data center down to cables, VLANs and IP space. infrahub-demo-sp models a service provider core with MPLS, BGP and L3VPN services. infrahub-solution-ai-dc models an AI data center from the physical location up through the routing overlay to the workload. This repository is the layer below all three, and none of them shares code with it. Each defines its layer in schema files, which is what makes a layer a modelling decision rather than a product feature.

From a router port to a duct​

Eight steps, each one a relationship that already exists in the schema.

StepKindReached throughWhat it fixes
1OtnRouterPortthe service endpoint's portsthe interface the IP side sees
2OtnClientPortconnected_tothe grey handover
3OtnTransponderdevicethe card doing the adaptation
4OtnLinePortportsthe coloured line side
5OtnOpticalCarriercarrierthe wavelength and its mode
6OtnOpticalMultiplexSectionsectionsROADM to ROADM
7OtnFiberSpanspansthe physical section and its loss
8OtnConduitconduitthe shared risk

Two IP links are diverse only when step eight differs for both. diversity.py checks that on every proposed change, and scenario seven walks it. The IP team asks whether two links share a duct. The optical team asks which services a duct cut takes down. Both questions read this chain, so both teams need it in the same place.

What this demo does not exercise​

Absence read from silence is a bad inference, so the list below is explicit. Each row was checked against the repository rather than recalled.

CapabilityStatus here
Physical-layer propagation modellingNot done. The budget is a running total against a mode's required OSNR, walked at the link budget. Nothing here solves for a nonlinear penalty or fits a spectrum plan the way a planning tool does.
Live performance monitoringNot held. A monitor carries its last reading and the timestamp it was taken at, and invoke demo-drift compares that against what the model says it should be. There is no time series and nothing polls anything.
Resource pools, CoreNumberPool, prefix and address poolsNot used. No pool of any kind is declared. A channel is chosen by fitting a mode's occupied width into free spectrum, which is a bin-packing decision rather than an allocation from a range.
Profiles, generate_profileNot used. Shared default values are written into the object files.
Object templates, generate_templateNot used. The generator writes structure instead, because what it writes depends on which route closed.
IPAM, prefixes and addressesNot used. There is no IpamPrefix and no address anywhere in the schema. The vendored location generic drops LocationHosting deliberately, and that is the relationship that would have brought IPAM in.
Transform-backed computed attributesNot used. All 32 computed attributes are Jinja2 over the node's own fields. Nothing here aggregates over a cardinality-many relationship inside the schema.
Jinja2 transforms and device configuration artifactsNot used. All ten transforms are Python, and no artifact in this demo is a router configuration.
Hierarchical locationsDeclared and then switched off. The vendored generic carries hierarchical: true, but OtnSite sets parent: "" and children: "", so the fifteen sites are one flat set with no region or country above them.
Webhooks and event-driven integrationNot used. Every run here is a command you type or a proposed change you open.
Accounts, roles and permissionsNot used. The repository ships no account, no account group and no permission object, and every command runs as the admin token in .env.
Synchronisation with an external systemNot used. Nothing in this repository reads from or writes to anything outside Infrahub, a management system or a planning tool included. The monitor readings in objects/ are loaded from files, not polled from equipment.

Two capabilities look absent from that list and are not. on_delete: cascade is used, and so are uniqueness constraints of the ["relationship", "attribute__value"] form. Both are in the capability map above.

Where to go from here​

  • Quick start is the shortest path to a blocked merge.
  • Provisioning scenarios walk the two scenarios that write: one service that provisions, and one corridor where a 400G is refused and a 100G is not.
  • Reporting scenarios walk the six that read, including what a fiber cut takes down and which services are not diverse.
  • The link budget is where the optical arithmetic is set out, corridor by corridor, including the two that do not close.
  • Developer guide is where to look if you want to add a report, a check or an element of your own.