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) <noreply@anthropic.com>
This commit is contained in:
sam 2026-05-15 14:23:30 -07:00
parent 541f018bc5
commit da49b3e462
6 changed files with 426 additions and 0 deletions

53
cml/build-cml-image.sh Executable file
View File

@ -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"

62
cml/build-xrd-image.sh Executable file
View File

@ -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"

View File

@ -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: <UPDATE after running: sha256sum /tmp/obmp-exabgp.tar>

View File

@ -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

View File

@ -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: <UPDATE after running: sha256sum /tmp/xrd-control-plane.tar>

View File

@ -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