Skip to content

Feat(eos_designs): Preview: Containerlab Digital Twin - #6846

Open
ankudinov wants to merge 58 commits into
aristanetworks:develfrom
ankudinov:clab-digital-twin
Open

Feat(eos_designs): Preview: Containerlab Digital Twin#6846
ankudinov wants to merge 58 commits into
aristanetworks:develfrom
ankudinov:clab-digital-twin

Conversation

@ankudinov

@ankudinov ankudinov commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Change Summary

Preview: Containerlab Digital Twin

Related Issue(s)

Fixes #3379

Component(s) name

arista.avd.eos_designs

How to test

use AVD playground to generate Containerlab digital twing

Checklist

Repository Checklist

  • My code has been rebased from devel before I start
  • I have read the CONTRIBUTING document.
  • My change requires a change to the documentation and documentation have been updated accordingly.
  • I have updated molecule CI testing accordingly. (check the box if not applicable)

Summary by CodeRabbit

  • New Features
    • Added Containerlab Digital Twin support generating lab topology, per-device startup-config artifacts, and an interface mapping file.
    • Introduced new Digital Twin Containerlab Molecule scenarios (positive and negative) with added inventory/group variables for multi-node labs.
  • Bug Fixes
    • Improved change detection/changed reporting for Containerlab startup-config output generation.
  • Documentation
    • Expanded Digital Twin documentation and schemas to include the Containerlab environment and its constraints.
  • Tests
    • Added/extended Molecule and unit test coverage plus CI scenario matrix updates, including validation of expected failure cases.

@github-actions

Copy link
Copy Markdown

Review docs on Read the Docs

To test this pull request:

# Create virtual environment for this testing below the current directory
python -m venv test-avd-pr-6846
# Activate the virtual environment
source test-avd-pr-6846/bin/activate
# Install all requirements including PyAVD
pip install "pyavd[ansible] @ git+https://github.com/ankudinov/avd.git@clab-digital-twin#subdirectory=python-avd" --force
# Point Ansible collections path to the Python virtual environment
export ANSIBLE_COLLECTIONS_PATH=$VIRTUAL_ENV/ansible_collections
# Install Ansible collection
python -m ansible.cli.galaxy collection install git+https://github.com/ankudinov/avd.git#/ansible_collections/arista/avd/,clab-digital-twin --force
cd test-avd-pr-6846
# Run your playbook using `python -m ansible.cli.playbook path/to/playbook.yml ...`

You can also test this PR using AVD playground:

  • Rebase your branch to makes sure it is up-to-date and has latest lab topologies for example inventories
  • Authenticate on labs.arista.com. Use your email! Not login.
  • Start the playground
  • In the lab selector UI pick "I want to use a specific AVD fork"
  • Enter following parameters:
GitHub org: ankudinov
Repository name: avd
Branch: clab-digital-twin
  • Select an example inventory to test the PR
  • Once the AVD Playground setup will be finished, type make start and test anything once the lab is up

@github-actions github-actions Bot added role: eos_cli_config_gen issue related to eos_cli_config_gen role state: CI Updated CI scenario have been updated in the PR state: Documentation role Updated role: eos_designs issue related to eos_designs role labels Apr 15, 2026
@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.65%. Comparing base (3a149ad) to head (0b7ec75).
⚠️ Report is 33 commits behind head on devel.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##            devel    #6846      +/-   ##
==========================================
+ Coverage   92.59%   92.65%   +0.05%     
==========================================
  Files         781      781              
  Lines       42574    42662      +88     
  Branches    10300    10314      +14     
==========================================
+ Hits        39422    39529     +107     
+ Misses       1927     1913      -14     
+ Partials     1225     1220       -5     
Components Coverage Δ
pyavd 94.39% <100.00%> (+0.01%) ⬆️
schema_tools 68.67% <ø> (ø)
Ansible plugins 79.39% <100.00%> (+0.85%) ⬆️
Files with missing lines Coverage Δ
...ta/avd/plugins/action/eos_designs_documentation.py 91.11% <100.00%> (+11.11%) ⬆️
...designs/structured_config/metadata/digital_twin.py 93.33% <100.00%> (+4.04%) ⬆️
...hon-avd/pyavd/api/fabric_documentation/__init__.py 100.00% <100.00%> (ø)
python-avd/pyavd/get_fabric_documentation.py 97.82% <100.00%> (+4.96%) ⬆️

... and 16 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the state: conflict PR with conflict label Apr 30, 2026
@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions Bot removed the state: conflict PR with conflict label Jun 15, 2026
@github-actions

Copy link
Copy Markdown

Conflicts have been resolved. A maintainer will review the pull request shortly.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Containerlab Digital Twin support is added across schemas, topology generation, documentation serialization, startup-config output, Molecule scenarios, and validation fixtures. Existing Digital Twin topology artifacts and generated device documentation are also updated.

Changes

Containerlab Digital Twin

Layer / File(s) Summary
Data models and topology generation
python-avd/pyavd/api/fabric_documentation/..., python-avd/pyavd/get_fabric_documentation.py, python-avd/pyavd/_eos_designs/...
Adds Containerlab topology models, accepts the containerlab environment, builds nodes and links, validates management addressing, and updates ACT link filtering.
Documentation and startup-config output
ansible_collections/arista/avd/plugins/..., ansible_collections/arista/avd/roles/..., docs/plugins/...
Serializes Containerlab topology YAML, writes interface mappings, selects .clab.yml output, and optionally copies rendered EOS configuration to startup-config paths.
Positive Containerlab Molecule scenario
ansible_collections/arista/avd/extensions/molecule/digital_twin_containerlab/...
Adds inventory, device intent, configurations, topology artifacts, lifecycle playbooks, documentation, and verification for a four-node fabric.
Negative validation scenarios
ansible_collections/arista/avd/extensions/molecule/digital_twin_containerlab_negative_tests/...
Adds expected-failure coverage for DHCP, empty, null, and multi-subnet management addressing.
Existing topology artifacts and CI
ansible_collections/arista/avd/extensions/molecule/digital_twin/..., ansible_collections/arista/avd/extensions/molecule/eos_designs-twodc-5stage-clos/..., .github/workflows/...
Updates existing topology document starts and adds both Containerlab Molecule scenarios to the CI matrix.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • aristanetworks/avd#7198: Both changes modify eos_designs_documentation.py and its Digital Twin serialization path.

Suggested labels: rn: Feat(eos_cli_config_gen|eos_designs)

Suggested reviewers: joelbreton2, juliopdx

Poem

I’m a rabbit with configs to deploy,
Through topology tunnels I hop with joy.
Static IPs line up in a neat subnet lane,
Startup files bloom like carrots after rain.
Containerlab spins—what a wonderful sight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: preview Containerlab Digital Twin support in eos_designs.
Description check ✅ Passed The description covers the required template sections and key details, though the proposed-changes narrative and test steps are brief.
Linked Issues check ✅ Passed The changes implement Containerlab topology generation and related artifacts requested by issue #3379.
Out of Scope Changes check ✅ Passed The added docs, tests, schemas, and Molecule scenarios all support Containerlab Digital Twin generation and stay on scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread ansible_collections/arista/avd/roles/eos_designs/docs/data-models.md Outdated
@gmuloc

gmuloc commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

need to the new scenarios to

@github-actions github-actions Bot added the type: code quality CI and development toolset label Jul 17, 2026
@ankudinov

Copy link
Copy Markdown
Contributor Author

need to the new scenarios to

done

Comment thread python-avd/pyavd/get_fabric_documentation.py Outdated
kinds={
default_kind: ContainerlabKind(
enforce_startup_config=True,
image="arista/ceos:latest",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We hardcode this assuming that all users will import ceos image naming it arista/ceos. Should we provide a flexibility to override this if needed (maybe they want a different version or they named it differently)?

For ACT we provide the following key which we later take into account during topology generaiton:

digital_twin.fabric.act_os_version
<node_type_keys.key>.defaults.digital_twin.act_os_version

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this. Especially when users want to implement this functionality internally, we are now relying on the end user to add an additional workflow to replace arista/ceos:latest, with whatever is used internally.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree in principle. However let's move this to another PR. I'd prefer to have a discussion around joint ACT/cLab schema first before blindly adding containerlab_image on top of act_os_version as yet another knob. To avoid cases like fabric: {}
But absolutely has to be configurable in the future.


sorted_avd_facts = sorted(fabric_documentation_facts.avd_facts.items())

unsupported_devices = [device for device, facts in sorted_avd_facts if not facts.mgmt_ip or facts.mgmt_ip == "dhcp"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devices may have inband mgmt or use DHCP on OOB MGMT interface. This may bee very restrictive to raise in this case.

I would suggest to use digital_twin.mgmt_ip (if defined) for Clab and fallback to regular mgmt_ip.

These override keys are currently exposed at the following levels:

"<node_type_keys.key>.defaults.digital_twin.mgmt_ip"
"<node_type_keys.key>.node_groups.[].nodes.[].digital_twin.mgmt_ip"
"<node_type_keys.key>.node_groups.[].digital_twin.mgmt_ip"
"<node_type_keys.key>.nodes.[].digital_twin.mgmt_ip"
"device_profiles.[].digital_twin.mgmt_ip"
"devices.[].digital_twin.mgmt_ip"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic handling calculation of the oob_mgmt_ip should be extended to include containerlab DT case (right now it only reacts on ACT mode)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should solve the problem with regular mgmt_ips sitting in the different subnets (currently this would always lead to raising)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not explode the scope of this PR. Overriding mgmt IP is certainly important, however I'd love to add basics first. It will also take a bit more than address override. Currently cLabs only supports single subnet for management. srl-labs/containerlab#3237 can help. But I have to craft and test a working environment before adding any code. If you already have one - share.


return ContainerlabDigitalTwin(
name=f"{fabric_documentation_facts.fabric_name}, Containerlab Digital Twin",
prefix="",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose this to the user as well? Name overlapping for prod switches is probably not possible (so running Clab DT for multiple prod fabrics on the same host should be ok) but if user tries to generate containerlab topos for our example fabrics and run them on the same host then there will be container name collision

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The reason is - the prefix will change lab hostnames and we don't want to do that for the current implementation. This may change though once we have the full story with mgmt ip override, etc. Let's keep it clean and simple at the start - same mgmt IP, same hostname.

return ContainerlabDigitalTwin(
name=f"{fabric_documentation_facts.fabric_name}, Containerlab Digital Twin",
prefix="",
mgmt=ContainerlabMgmt(network="custom_mgmt", ipv4_subnet=str(next(iter(unique_mgmt_networks)))),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network="custom_mgmt" - wouldn't this lead to the deployment failure if we try to run multiple clab topologies on the same host? If definition of the bridge matches - we risk to have ceos mgmt IP collisions. If ipv4-subnets differ for different topologies - there we'll probably face deployment error.

We should probably prepend fabric name to the bridge name, or even make this configurable by the user?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. we should not aim to support all possible deployment cases and expect clean environment every time. This can be discussed later, but will not be implemented in this PR

Comment on lines +83 to +88
data = ContainerlabDigitalTwin(
mgmt=ContainerlabMgmt(network="clab-mgmt", ipv4_subnet="172.16.1.0/24"),
topology=ContainerlabTopology(
nodes={1: ContainerlabNode(mgmt_ipv4="172.16.1.101")},
kinds=(ContainerlabKind(enforce_startup_config=True, image="ceos:latest"),),
endpoint_lists=[("leaf1:eth1", "spine1:eth1")],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pylance is not happy about reusing the same class names that we import from pyavd

Argument of type "ContainerlabMgmt" cannot be assigned to parameter "mgmt" of type "ContainerlabMgmt" in function "__init__"
  "avd.ansible_collections.arista.avd.tests.unit.action.test_eos_designs_documentation.ContainerlabMgmt" is not assignable to "pyavd.api.fabric_documentation.ContainerlabMgmt"

Maybe we should either use actual production clab classes for this test or rename locally-redefined so that the don't overlap with imported ones?

content["topology"]["nodes"] = {
node_name: {
"mgmt-ipv4": node_settings["mgmt-ipv4"],
"startup-config": f"intended/configs/{node_name}.cfg",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are using hardcoded path to the startup config, why not to set this when we instantiate dataclasses (meaning ContainerlabNode would just have another field startup_config that we'll set together with mgmt_ipv4). Not sure what is the benefit of manipulating this here in plugin instead of setting it in the beginning

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this path hardcodes to intended/configs/{node_name}.cfg assuming that output_dir_name is always intended. But if it is not the case - path under starup-config in topology file may become unreachable

description OOB_MANAGEMENT
no shutdown
vrf MGMT
ip address 172.16.1.101/24

@alexeygorbunov alexeygorbunov Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Digital twin will likely be running on-prem (meaning original OOB MGMT subnet would likely be routable from the clab runner) and nodes will be reusing prod hostnames, mgmt IPs, credentials, etc. This may create a risk of pushing something unexpected to the prod.

links=tuple(links),
),
interface_mapping={
"ManagementIntf": {"eth0": "Management1"},

@alexeygorbunov alexeygorbunov Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If node_config.mgmt_interface is set to Ma0 (or anything other than Ma1) then generated EOS config will have Ma0 (and not Ma1 expected by Clab and mapped to Linux eth0) which i guess may lead to mgmt connectivity issues. We probably need a special section under shared_utils.mgmt_interface to enforce OOB MA to be Ma1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I though about it and taking management from node settings. However what if every node will have it's own custom mgmt interface? In that case we have to build int map for every node. This is not a big problem, but adds complexity and int map was added only to merge the PR a bit quicker and support "older" cEOSlab versions. Long term we want to use INTF_MAP_ETH0 env var which is coming very soon and set it to node mgmt interface.

@@ -0,0 +1,41 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would pointing language server to the schema definition be helpful with making sure all generated key are schema-compliant?

# yaml-language-server: $schema=https://raw.githubusercontent.com/srl-labs/containerlab/main/schemas/clab.schema.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have a digital_twin folder inside a digital_twin folder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outer digital_twin references the name of the scenario/fabric that we picked (it could have been anything else overall). Inner digital_twin is a dedicated folder containing digital_twin artifacts (at lest for ACT mode) but is also tunable (via output_dir_name and documentation_dir_name vars)

@JulioPDX

Copy link
Copy Markdown
Contributor

Any thought to create generic names that could be used across DT environments. For example instead of act_username, we would have digital_twin_usename, or have a heirarchy of ansible_user, first defined local_users, digital_twin_username. this would prevent the creation of multiple keys to change values in the different topology files.

@ankudinov

Copy link
Copy Markdown
Contributor Author

hought to create generic names that could be used across DT environments. For example instead of act_username, we would have digital_twin_usename, or have a heirarchy of ansible_user, first def

We certainly have to brainstorm a unified DT schema. Different PR though.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

role: eos_cli_config_gen issue related to eos_cli_config_gen role role: eos_designs issue related to eos_designs role state: CI Updated CI scenario have been updated in the PR state: conflict PR with conflict state: Documentation role Updated type: code quality CI and development toolset

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eos_designs output containerlab topology

6 participants