Skip to content

Repository files navigation

arista-lifecycle-avd

The AVD project for the NTC + Arista "Modern Network Lifecycle" demo. It follows the layout of Arista's AVD 0-to-hero reference repo with one site, DC1, and one difference that matters:

  • sites/ is generated by Nautobot. The DC1 Fabric design job renders inventory, group_vars, tenants, connected endpoints, and the clab topology, then opens a pull request here. Do not edit those files by hand; change Nautobot and rerun the design.
  • config_contexts/ is hand-edited. Nautobot syncs these two files as config contexts (AVD Fabric Settings, AVD Management Settings) and the design writes them back out as sites/_global_vars/fabric_settings.yml and management.yml. Edit here, run invoke sync-avd-repo in the workshop repo, rerun the design, review the PR. Nautobot stores the context as JSON, so the rendered sites/_global_vars/*.yml files come back with keys in Nautobot's order, not the order in these files; the content is identical.

Build

Open the repo in the devcontainer (AVD universal image) or run:

docker run --rm -it -v "$PWD":/workspace -w /workspace \
  ghcr.io/aristanetworks/avd/universal:python3.13-avd-v6.3.0 make dc1-build

make help lists the targets. deploy and validate targets read CVAAS_SERVER, CVAAS_TOKEN, ANTA_USERNAME, and ANTA_PASSWORD from the environment; none of those are committed. The cvpadmin sha512 hash and the BGP peer password are lab values copied from Arista's public reference repo, and they are committed here in the clear: the hash appears in config_contexts/dc1_management.yml and in digital_twin/clab/init-configs/basic.cfg, and the BGP peer password in config_contexts/dc1_fabric_settings.yml. Both are also carried into the generated files under sites/. Replace them before pointing this repo at anything real.

Pipeline

Four GitHub workflows in .github/workflows/ carry a change from a branch to production. All four run on the self-hosted runner labelled self-hosted, clab, in the AVD universal container (ghcr.io/aristanetworks/avd/universal:python3.13-avd-v6.3.0, --user=root --network host). The three event-driven workflows (feature_branches.yml, PR_test_digital_twin.yml, main_branch.yml) are gated on a dorny/paths-filter check of sites/DC1/group_vars/** and sites/_global_vars/** so unrelated commits do not touch the fabric. cvp_post_CC_validation.yml has no such filter: a change control dispatch is an explicit request to validate now.

Workflow Trigger What it does
feature_branches.yml push to any branch but main make dc1-build, then commits the regenerated intended configs, documentation, twin configs, and amplification report back to the branch.
PR_test_digital_twin.yml pull_request (opened, synchronize, reopened) Job deploy-digital-twin builds, finds the running twin, and pushes the twin configs over eAPI. Job Network-validation-digital-twin runs ANTA against the twin, commits the report, posts it as a sticky pull request comment, and fails the check if validation failed. The second job runs only if the deploy succeeded.
main_branch.yml push to main Job deploy-prod deploys DC1 through CloudVision as a Service with cv_submit_workspace=true cv_run_change_control=true. Job Network-validation then runs ANTA against production and commits the report. The second job runs only if the deploy succeeded.
cvp_post_CC_validation.yml repository_dispatch, type validation_trigger Runs the production ANTA validation on demand and commits the report. Post this dispatch when a CloudVision change control finishes.

Workflow-generated commits are authored as github-actions[bot] and carry [skip ci], so a commit back never retriggers the pipeline.

Secrets and variables

Four Actions secrets and one Actions variable:

Secret Used by
CLAB_JWT_SECRET PR_test_digital_twin.yml, to mint a token for the containerlab API server
CVAAS_SERVER, CVAAS_TOKEN main_branch.yml, read by playbooks/deploy.yml
ANTA_PASSWORD main_branch.yml and cvp_post_CC_validation.yml, read by playbooks/validate.yml
Variable Used by
ANTA_USERNAME the same two workflows, as `${{ vars.ANTA_USERNAME

ANTA_USERNAME is a variable and not a secret on purpose. It is a username, and its value is the four-letter string anta, which as a secret GitHub would mask everywhere it appears in any log in this repository. The workflows fall back to anta when the variable is unset, so the pipeline runs with the variable missing.

The digital twin needs no credentials of its own: containerlab boots cEOS with admin/admin and twin_inventory.py writes those into the generated inventory.

Deploying to the twin

digital_twin/clab/build_twin_lab.py does not push configuration at the running twin: cEOS-lab gates most routing configuration behind platform capabilities that never settle reliably for runtime sessions, while startup configuration is applied ungated at boot. So the deploy REDEPLOYS the running twin lab in place through the containerlab API server, with each node's built configuration embedded as an inline startup-config: same lab name, same node names, static twin management addresses that reuse each node's production last octet, and a boot wait until every node's eAPI answers.

Each configuration is first rendered through digital_twin/clab/twin_config_filter.py. The filter drops the management-plane stanzas, plus daemon TerminAttr (the twin never has the CloudVision onboarding token the agent needs, so on the twin it could only restart in a loop) and sflow (cEOS-lab rejects it), and appends a tail that restores admin/admin, interface Management1 with the node's own twin address in the default VRF, and eAPI over https in the default VRF. vrf instance MGMT is deliberately kept: mlag configuration's heartbeat peer address, the name servers, and the NTP servers all reference it, and an MGMT VRF with no interface in it cannot carry a session. Everything the pull request is validating - BGP, VLANs, VRFs, Ethernet interfaces, MLAG, route maps - passes through byte for byte, which the tests in digital_twin/clab/tests/test_twin_config_filter.py assert. Filtering is idempotent: every stanza the tail adds is a stanza the filter removes.

One consequence for reading twin ANTA results: the catalogs are generated from the production intended configs, so any check that asserts production's management addressing (Management1's address, the MGMT VRF default route) is an expected delta on the twin, not a regression. Read those rows against production, not against the twin.

The digital twin prerequisite

PR_test_digital_twin.yml does not create the twin, it finds one. A twin lab for DC1 must already be running on the containerlab host before the pull request is opened or updated; in the demo, Nautobot's "Create & Deploy Digital Twin" job for DC1 starts it. digital_twin/clab/twin_inventory.py asks the containerlab API server (http://localhost:8080, reachable because the job container uses host networking on the runner's droplet) which labs are up, ignores the production lab dc1, and writes twin_inventory.yml for the rest. If production is the only lab running, the script exits 2 with a message saying which Nautobot job to launch, and the pull request validation fails immediately rather than testing nothing.

Forks

Pull requests from forks are not validated. A pull_request run from a fork gets no secrets and a read-only token, so it could neither reach the twin nor commit the report; both jobs in PR_test_digital_twin.yml skip unless the head branch lives in this repository. pull_request_target is deliberately not used anywhere.

Reset

main is the demo's starting point and is tagged baseline. invoke reset-demo in the workshop repo closes every nautobot/* pull request, deletes those branches, moves main back to baseline, and resets Nautobot to match. To change the starting point, commit to main and move the tag: git tag -f baseline && git push -f origin baseline.

About

AVD project for the NTC + Arista Modern Network Lifecycle demo. The sites/ inputs are generated by Nautobot Design Builder.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages