From da49b3e4626dc2048ac47484c9b62f97ab3a2e1a Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 15 May 2026 14:23:30 -0700 Subject: [PATCH] Add CML integration: XRd and ExaBGP node/image definitions and build scripts CML 2.9 node definitions for XRd Control-Plane (third RR) and ExaBGP route injector as Docker-based CML nodes. Includes build scripts to export Docker images as tars for CML import, with IOS-XR startup configs for IS-IS, BGP, and BMP. Co-Authored-By: Claude Opus 4.6 (1M context) --- cml/build-cml-image.sh | 53 +++++++++ cml/build-xrd-image.sh | 62 +++++++++++ cml/exabgp-image-definition.yaml | 10 ++ cml/exabgp-node-definition.yaml | 112 +++++++++++++++++++ cml/xrd-image-definition.yaml | 10 ++ cml/xrd-node-definition.yaml | 179 +++++++++++++++++++++++++++++++ 6 files changed, 426 insertions(+) create mode 100755 cml/build-cml-image.sh create mode 100755 cml/build-xrd-image.sh create mode 100644 cml/exabgp-image-definition.yaml create mode 100644 cml/exabgp-node-definition.yaml create mode 100644 cml/xrd-image-definition.yaml create mode 100644 cml/xrd-node-definition.yaml diff --git a/cml/build-cml-image.sh b/cml/build-cml-image.sh new file mode 100755 index 0000000..cb17585 --- /dev/null +++ b/cml/build-cml-image.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Build the ExaBGP Docker image and export it for CML 2.9 import. +# +# Usage: +# ./cml/build-cml-image.sh +# +# Output: +# /tmp/obmp-exabgp.tar — upload this to CML via: +# Tools > Node and Image Definitions > Image Definitions > Manage Image Uploads +# +# After upload, also import the node + image definition YAMLs: +# Tools > Node and Image Definitions > Import > cml/exabgp-node-definition.yaml +# Tools > Node and Image Definitions > Import > cml/exabgp-image-definition.yaml + +set -e +cd "$(dirname "$0")/.." + +echo "=== Building ExaBGP Docker image ===" +docker build -t obmp-exabgp:latest ./exabgp/ + +echo "" +echo "=== Exporting image to /tmp/obmp-exabgp.tar ===" +docker save -o /tmp/obmp-exabgp.tar obmp-exabgp:latest + +echo "" +echo "=== Image details ===" +SIZE=$(du -h /tmp/obmp-exabgp.tar | cut -f1) +echo " File: /tmp/obmp-exabgp.tar ($SIZE)" + +SHA=$(sha256sum /tmp/obmp-exabgp.tar | awk '{print $1}') +echo " SHA256: $SHA" + +IMAGE_ID=$(docker image inspect obmp-exabgp:latest --format='{{.Id}}') +echo " Image ID: $IMAGE_ID" + +echo "" +echo "=== Next steps ===" +echo "1. Update cml/exabgp-image-definition.yaml with:" +echo " sha256: $SHA" +echo "" +echo "2. Upload to CML:" +echo " a. Tools > Node and Image Definitions > Import" +echo " Upload: cml/exabgp-node-definition.yaml" +echo " b. Tools > Node and Image Definitions > Import" +echo " Upload: cml/exabgp-image-definition.yaml" +echo " c. Tools > Node and Image Definitions > Image Definitions > Manage Image Uploads" +echo " Upload: /tmp/obmp-exabgp.tar" +echo "" +echo "3. In your CML lab topology:" +echo " a. Drag 'ExaBGP Route Injector' from the node palette" +echo " b. Draw links to CORE-01 and CORE-02" +echo " c. Edit the boot.sh in the node config to set correct IPs" +echo " d. Start the node" diff --git a/cml/build-xrd-image.sh b/cml/build-xrd-image.sh new file mode 100755 index 0000000..c126b44 --- /dev/null +++ b/cml/build-xrd-image.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Export the XRd control-plane Docker image for CML 2.9 import. +# +# Usage: +# ./cml/build-xrd-image.sh +# +# The XRd image already exists locally (ios-xr/xrd-control-plane:25.1.1). +# This script just exports it to a .tar file for CML upload. + +set -e + +IMAGE="ios-xr/xrd-control-plane:25.1.1" +OUTPUT="/tmp/xrd-control-plane.tar" + +echo "=== Verifying XRd image exists ===" +if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then + echo "ERROR: Image $IMAGE not found locally." + echo "Check with: docker images | grep xrd" + exit 1 +fi + +echo " Image: $IMAGE" +SIZE=$(docker image inspect "$IMAGE" --format='{{.Size}}' | numfmt --to=iec 2>/dev/null || echo "unknown") +echo " Size: $SIZE" + +echo "" +echo "=== Exporting image to $OUTPUT ===" +echo " (This may take a minute for ~1.3GB image...)" +docker save -o "$OUTPUT" "$IMAGE" + +echo "" +echo "=== Export complete ===" +TAR_SIZE=$(du -h "$OUTPUT" | cut -f1) +echo " File: $OUTPUT ($TAR_SIZE)" + +SHA=$(sha256sum "$OUTPUT" | awk '{print $1}') +echo " SHA256: $SHA" + +echo "" +echo "=== Next steps ===" +echo "1. Update cml/xrd-image-definition.yaml with:" +echo " sha256: $SHA" +echo "" +echo "2. Upload to CML:" +echo " a. Tools > Node and Image Definitions > Import" +echo " Upload: cml/xrd-node-definition.yaml" +echo " b. Tools > Node and Image Definitions > Import" +echo " Upload: cml/xrd-image-definition.yaml" +echo " c. Tools > Node and Image Definitions > Image Definitions > Manage Image Uploads" +echo " Upload: $OUTPUT" +echo " (For large files, consider SCP to CML server instead)" +echo "" +echo "3. In your CML lab topology:" +echo " a. Drag 'XRd Control-Plane (IOS-XR)' from the node palette" +echo " b. Draw links to CORE-01 (→Gi0/0/0/0) and CORE-02 (→Gi0/0/0/1)" +echo " c. Edit xrd-startup.cfg if needed (IPs, BMP target, etc.)" +echo " d. Start the node (allow ~3-5 min for XRd boot)" +echo "" +echo "4. After boot, verify via XRd console:" +echo " show isis adjacency" +echo " show bgp summary" +echo " show bmp server 1" diff --git a/cml/exabgp-image-definition.yaml b/cml/exabgp-image-definition.yaml new file mode 100644 index 0000000..63532fc --- /dev/null +++ b/cml/exabgp-image-definition.yaml @@ -0,0 +1,10 @@ +id: obmp-exabgp.latest +node_definition_id: obmp-exabgp +description: |- + OpenBMP ExaBGP Route Injector + Python 3.11 + ExaBGP + Flask API for BGP route injection testing. +label: ExaBGP Route Injector +disk_image: obmp-exabgp.tar +read_only: false +schema_version: 0.0.1 +# sha256: diff --git a/cml/exabgp-node-definition.yaml b/cml/exabgp-node-definition.yaml new file mode 100644 index 0000000..2705679 --- /dev/null +++ b/cml/exabgp-node-definition.yaml @@ -0,0 +1,112 @@ +id: obmp-exabgp +boot: + timeout: 60 + completed: + - "ExaBGP Route Injector" + uses_regex: false +sim: + linux_native: + libvirt_domain_driver: docker + driver: ubuntu + ram: 512 + cpus: 1 + cpu_limit: 100 + video: + memory: 1 +general: + nature: server + description: OpenBMP ExaBGP Route Injector (Docker container) + read_only: false +configuration: + generator: + driver: null + provisioning: + files: + - editable: false + name: config.json + content: |- + { + "docker": { + "image": "obmp-exabgp:latest", + "mounts": [ + "type=bind,source=cfg/boot.sh,target=/cml-boot.sh" + ], + "misc_args": [], + "env": [ + "EXABGP_LOCAL_AS=65100", + "EXABGP_PEER_AS=65020", + "EXABGP_API_PORT=5050" + ] + }, + "shell": "/bin/bash", + "day0cmd": [ "/bin/bash", "/cml-boot.sh" ], + "busybox": false + } + - editable: true + name: boot.sh + content: |- + #!/bin/bash + # CML boot script for ExaBGP container + # Configures data-plane interfaces before starting ExaBGP + # + # Interface mapping (assigned by CML topology links): + # eth0 = first connected interface (data-plane link 1) + # eth1 = second connected interface (data-plane link 2) + # ...additional interfaces as connected in topology + # + # Edit the IPs below to match your topology addressing. + # These are examples using 10.120.x.x/30 point-to-point links. + + # --- Data-plane interface configuration --- + # Link to CORE-01: ExaBGP=10.120.1.2/30, CORE-01=10.120.1.1/30 + ip address add 10.120.1.2/30 dev eth0 + ip link set dev eth0 up + + # Link to CORE-02: ExaBGP=10.120.2.2/30, CORE-02=10.120.2.1/30 + ip address add 10.120.2.2/30 dev eth1 + ip link set dev eth1 up + + # --- Set environment for ExaBGP peering --- + export EXABGP_LOCAL_IP=10.120.1.2 + export EXABGP_PEER_1=10.120.1.1 + export EXABGP_PEER_2=10.120.2.1 + + # --- Start ExaBGP --- + exec /bin/bash /exabgp/startup.sh + media_type: raw + volume_name: cfg +device: + interfaces: + serial_ports: 1 + physical: + - eth0 + - eth1 + - eth2 + - eth3 + has_loopback_zero: false + default_count: 2 +ui: + label_prefix: exabgp- + icon: server + label: ExaBGP Route Injector + visible: true + group: Others + description: |- + OpenBMP ExaBGP Route Injector + BGP route injection for OpenBMP testing. + AS 65100 (eBGP) peering with IOS-XR routers (AS 65020). + Flask API on port 5050 for route management. +inherited: + image: + ram: true + cpus: false + data_volume: false + boot_disk_size: false + cpu_limit: false + node: + ram: true + cpus: false + data_volume: false + boot_disk_size: false + cpu_limit: false +schema_version: 0.0.1 diff --git a/cml/xrd-image-definition.yaml b/cml/xrd-image-definition.yaml new file mode 100644 index 0000000..7603731 --- /dev/null +++ b/cml/xrd-image-definition.yaml @@ -0,0 +1,10 @@ +id: xrd-control-plane.25.1.1 +node_definition_id: xrd-control-plane-rr +description: |- + Cisco XRd Control-Plane 25.1.1 + IOS-XR containerized routing daemon for BGP/IS-IS/BMP workloads. +label: XRd Control-Plane 25.1.1 +disk_image: xrd-control-plane.tar +read_only: false +schema_version: 0.0.1 +# sha256: diff --git a/cml/xrd-node-definition.yaml b/cml/xrd-node-definition.yaml new file mode 100644 index 0000000..2fcefbd --- /dev/null +++ b/cml/xrd-node-definition.yaml @@ -0,0 +1,179 @@ +id: xrd-control-plane-rr +boot: + timeout: 300 + completed: + - "IOS XR RUN" + uses_regex: false +sim: + linux_native: + libvirt_domain_driver: docker + driver: ubuntu + ram: 2048 + cpus: 2 + cpu_limit: 100 + video: + memory: 1 +general: + nature: router + description: Cisco XRd Control-Plane - IOS-XR containerized routing daemon + read_only: false +configuration: + generator: + driver: null + provisioning: + files: + - editable: false + name: config.json + content: |- + { + "docker": { + "image": "ios-xr/xrd-control-plane:25.1.1", + "mounts": [ + "type=bind,source=cfg/boot.sh,target=/cml-boot.sh", + "type=bind,source=cfg/xrd-startup.cfg,target=/etc/xrd/startup.cfg" + ], + "misc_args": [ + "--privileged" + ], + "env": [ + "XR_STARTUP_CFG=/etc/xrd/startup.cfg", + "XR_MGMT_INTERFACES=linux:eth0,chksum", + "XR_INTERFACES=linux:eth1,xr_name=Gi0/0/0/0;linux:eth2,xr_name=Gi0/0/0/1;linux:eth3,xr_name=Gi0/0/0/2;linux:eth4,xr_name=Gi0/0/0/3" + ] + }, + "shell": "/bin/bash", + "day0cmd": [ "/bin/bash", "/cml-boot.sh" ], + "busybox": false + } + - editable: true + name: boot.sh + content: |- + #!/bin/bash + # CML boot wrapper for XRd control-plane. + # XRd handles its own init — this script configures + # data-plane interfaces before XRd starts. + # + # Interface mapping (set via XR_INTERFACES env var): + # eth0 = MgmtEth0/RP0/CPU0/0 (CML mgmt) + # eth1 = Gi0/0/0/0 (data-plane link 1, e.g. to CORE-01) + # eth2 = Gi0/0/0/1 (data-plane link 2, e.g. to CORE-02) + # eth3+ = Gi0/0/0/2+ (additional links) + # + # Linux-level IP config is handled by XRd via startup.cfg. + # Just ensure interfaces are up. + for iface in eth0 eth1 eth2 eth3 eth4; do + [ -d /sys/class/net/$iface ] && ip link set dev $iface up + done + + # XRd entrypoint + exec /usr/sbin/xrd + - editable: true + name: xrd-startup.cfg + content: |- + !! XRd Control-Plane - Third Route Reflector (RR3) + !! Peers with CORE-01 and CORE-02 as RR mesh (non-client iBGP) + !! Sends BMP to OpenBMP collector at 10.40.40.202:5000 + !! + hostname XRd-RR3 + ! + interface Loopback0 + ipv4 address 10.10.255.30 255.255.255.255 + ! + interface Gi0/0/0/0 + description to-CORE-01 + ipv4 address 10.120.3.2 255.255.255.252 + no shutdown + ! + interface Gi0/0/0/1 + description to-CORE-02 + ipv4 address 10.120.4.2 255.255.255.252 + no shutdown + ! + router isis 1 + is-type level-2-only + net 49.0001.0100.1000.0030.00 + address-family ipv4 unicast + metric-style wide + ! + interface Loopback0 + passive + address-family ipv4 unicast + ! + ! + interface Gi0/0/0/0 + point-to-point + address-family ipv4 unicast + ! + ! + interface Gi0/0/0/1 + point-to-point + address-family ipv4 unicast + ! + ! + ! + router bgp 65020 + bgp router-id 10.10.255.30 + address-family ipv4 unicast + ! + neighbor 10.10.255.0 + remote-as 65020 + update-source Loopback0 + address-family ipv4 unicast + ! + ! + neighbor 10.10.255.20 + remote-as 65020 + update-source Loopback0 + address-family ipv4 unicast + ! + ! + ! + bmp server 1 + host 10.40.40.202 port 5000 + description OpenBMP + update-source Gi0/0/0/0 + flapping-delay 60 + initial-delay 5 + stats-reporting-period 300 + initial-refresh delay 30 spread 2 + ! + ssh server v2 + end + media_type: raw + volume_name: cfg +device: + interfaces: + serial_ports: 1 + physical: + - eth0 + - eth1 + - eth2 + - eth3 + - eth4 + has_loopback_zero: false + default_count: 3 +ui: + label_prefix: xrd- + icon: router + label: XRd Control-Plane (IOS-XR) + visible: true + group: Cisco + description: |- + Cisco XRd Control-Plane (IOS-XR 25.1.1) + Containerized IOS-XR routing daemon for control-plane workloads. + Full BGP, IS-IS, BMP, NETCONF support. + Configured as third Route Reflector (RR3) with BMP to OpenBMP. +inherited: + image: + ram: true + cpus: true + data_volume: false + boot_disk_size: false + cpu_limit: false + node: + ram: true + cpus: true + data_volume: false + boot_disk_size: false + cpu_limit: false +schema_version: 0.0.1