From e1bf3dff4172baff4f3049888c71cbe42e2cbec5 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Thu, 7 Nov 2024 18:08:25 +0000 Subject: [PATCH 01/34] Replace telnetlib with scrapli - Add python3-pip, git and scrapli install via pip to all node - Dockerfiles. - Replace all nodes telnetlib functions with new adapated scrapli functions. (wait_write, expect, read_until) - Move Cisco and Juniper nodes to their own vendor subdirectories, fix makefiles to ensure functionality works. - Other things, which should be outlined in the PR. --- aoscx/docker/Dockerfile | 8 +- aoscx/docker/launch.py | 6 +- asav/Makefile | 12 - cisco/asav/Makefile | 21 ++ {asav => cisco/asav}/README.md | 0 cisco/asav/cidfile | 1 + {asav => cisco/asav}/docker/Dockerfile | 8 +- {asav => cisco/asav}/docker/launch.py | 34 ++- {c8000v => cisco/c8000v}/Makefile | 10 +- {c8000v => cisco/c8000v}/README.md | 0 cisco/c8000v/cidfile | 1 + {c8000v => cisco/c8000v}/docker/Dockerfile | 7 +- {c8000v => cisco/c8000v}/docker/launch.py | 18 +- {cat9kv => cisco/cat9kv}/Makefile | 7 +- {cat9kv => cisco/cat9kv}/README.md | 0 {cat9kv => cisco/cat9kv}/docker/Dockerfile | 4 + {cat9kv => cisco/cat9kv}/docker/launch.py | 20 +- {csr => cisco/csr}/Makefile | 6 +- {csr => cisco/csr}/README.md | 0 cisco/csr/cidfile | 1 + {csr => cisco/csr}/docker/Dockerfile | 6 +- {csr => cisco/csr}/docker/launch.py | 25 +- cisco/ftdv/Makefile | 21 ++ {ftdv => cisco/ftdv}/README.md | 0 cisco/ftdv/cidfile | 1 + {ftdv => cisco/ftdv}/docker/Dockerfile | 4 + {ftdv => cisco/ftdv}/docker/launch.py | 28 +- {n9kv => cisco/n9kv}/Makefile | 9 +- {n9kv => cisco/n9kv}/README.md | 0 {n9kv => cisco/n9kv}/docker/Dockerfile | 6 +- {n9kv => cisco/n9kv}/docker/OVMF.fd | Bin {n9kv => cisco/n9kv}/docker/launch.py | 18 +- {nxos => cisco/nxos}/Makefile | 0 {nxos => cisco/nxos}/README.md | 0 {nxos => cisco/nxos}/docker/Dockerfile | 6 +- {nxos => cisco/nxos}/docker/launch.py | 6 +- {vios => cisco/vios}/Makefile | 9 +- {vios => cisco/vios}/README.md | 0 {vios => cisco/vios}/docker/Dockerfile | 4 + {vios => cisco/vios}/docker/launch.py | 10 +- {xrv => cisco/xrv}/Makefile | 9 +- {xrv => cisco/xrv}/README.md | 0 {xrv => cisco/xrv}/docker/Dockerfile | 6 +- {xrv => cisco/xrv}/docker/launch.py | 49 +--- {xrv9k => cisco/xrv9k}/LICENSE | 0 cisco/xrv9k/Makefile | 31 +++ {xrv9k => cisco/xrv9k}/README.md | 0 cisco/xrv9k/cidfile | 1 + {xrv9k => cisco/xrv9k}/docker/Dockerfile | 6 +- {xrv9k => cisco/xrv9k}/docker/launch.py | 255 +++++++----------- cmglinux/docker/Dockerfile | 4 + cmglinux/docker/launch.py | 6 +- common/vrnetlab.py | 218 ++++++++++----- dell_sonic/docker/Dockerfile | 4 + dell_sonic/docker/launch.py | 6 +- fortigate/docker/Dockerfile | 6 +- fortigate/docker/launch.py | 10 +- freebsd/docker/Dockerfile | 4 + freebsd/docker/launch.py | 6 +- ftdv/Makefile | 12 - ftosv/docker/Dockerfile | 6 +- ftosv/docker/launch.py | 6 +- huawei_vrp/docker/Dockerfile | 4 + huawei_vrp/docker/launch.py | 8 +- .../vjunosevolved}/Makefile | 5 +- .../vjunosevolved}/README.md | 0 .../vjunosevolved}/docker/Dockerfile | 6 +- .../vjunosevolved}/docker/init.conf | 0 .../vjunosevolved}/docker/launch.py | 6 +- .../vjunosevolved}/docker/make-config.sh | 0 .../vjunosrouter}/Makefile | 5 +- .../vjunosrouter}/README.md | 0 .../vjunosrouter}/docker/Dockerfile | 6 +- .../vjunosrouter}/docker/init.conf | 0 .../vjunosrouter}/docker/launch.py | 6 +- .../vjunosrouter}/docker/make-config.sh | 0 .../vjunosswitch}/Makefile | 5 +- .../vjunosswitch}/README.md | 0 .../vjunosswitch}/docker/Dockerfile | 6 +- .../vjunosswitch}/docker/init.conf | 0 .../vjunosswitch}/docker/launch.py | 6 +- .../vjunosswitch}/docker/make-config.sh | 0 {vmx => juniper/vmx}/Makefile | 7 +- {vmx => juniper/vmx}/README.md | 0 {vmx => juniper/vmx}/docker/Dockerfile | 6 +- {vmx => juniper/vmx}/docker/launch.py | 18 +- {vmx => juniper/vmx}/vmx-extract.sh | 0 {vqfx => juniper/vqfx}/Makefile | 7 +- {vqfx => juniper/vqfx}/README.md | 0 {vqfx => juniper/vqfx}/docker/Dockerfile | 6 +- {vqfx => juniper/vqfx}/docker/launch.py | 10 +- {vsrx => juniper/vsrx}/Makefile | 5 +- {vsrx => juniper/vsrx}/README.md | 0 {vsrx => juniper/vsrx}/docker/Dockerfile | 4 + {vsrx => juniper/vsrx}/docker/init.conf | 0 {vsrx => juniper/vsrx}/docker/launch.py | 6 +- .../vsrx}/docker/make-config-iso.sh | 0 makefile.include | 3 + ocnos/docker/Dockerfile | 6 +- ocnos/docker/launch.py | 6 +- openbsd/docker/Dockerfile | 6 +- openbsd/docker/launch.py | 6 +- openwrt/docker/Dockerfile | 5 + openwrt/docker/launch.py | 6 +- pan/docker/Dockerfile | 6 +- pan/docker/launch.py | 8 +- routeros/docker/Dockerfile | 6 +- routeros/docker/launch.py | 8 +- sonic/docker/Dockerfile | 4 + sonic/docker/launch.py | 6 +- sros/docker/Dockerfile | 4 + sros/docker/launch.py | 6 +- ubuntu/docker/Dockerfile | 6 +- ubuntu/docker/launch.py | 6 +- veos/docker/Dockerfile | 6 +- veos/docker/launch.py | 6 +- vrp/docker/Dockerfile | 8 +- vrp/docker/launch.py | 8 +- vsr1000/docker/Dockerfile | 8 +- vsr1000/docker/launch.py | 6 +- xrv9k/Makefile | 23 -- 121 files changed, 724 insertions(+), 533 deletions(-) delete mode 100644 asav/Makefile create mode 100644 cisco/asav/Makefile rename {asav => cisco/asav}/README.md (100%) create mode 100644 cisco/asav/cidfile rename {asav => cisco/asav}/docker/Dockerfile (71%) mode change 100644 => 100755 rename {asav => cisco/asav}/docker/launch.py (82%) mode change 100644 => 100755 rename {c8000v => cisco/c8000v}/Makefile (74%) rename {c8000v => cisco/c8000v}/README.md (100%) create mode 100644 cisco/c8000v/cidfile rename {c8000v => cisco/c8000v}/docker/Dockerfile (72%) rename {c8000v => cisco/c8000v}/docker/launch.py (93%) rename {cat9kv => cisco/cat9kv}/Makefile (72%) rename {cat9kv => cisco/cat9kv}/README.md (100%) rename {cat9kv => cisco/cat9kv}/docker/Dockerfile (86%) rename {cat9kv => cisco/cat9kv}/docker/launch.py (92%) rename {csr => cisco/csr}/Makefile (87%) rename {csr => cisco/csr}/README.md (100%) create mode 100644 cisco/csr/cidfile rename {csr => cisco/csr}/docker/Dockerfile (77%) rename {csr => cisco/csr}/docker/launch.py (93%) create mode 100644 cisco/ftdv/Makefile rename {ftdv => cisco/ftdv}/README.md (100%) create mode 100644 cisco/ftdv/cidfile rename {ftdv => cisco/ftdv}/docker/Dockerfile (82%) rename {ftdv => cisco/ftdv}/docker/launch.py (93%) rename {n9kv => cisco/n9kv}/Makefile (78%) rename {n9kv => cisco/n9kv}/README.md (100%) rename {n9kv => cisco/n9kv}/docker/Dockerfile (79%) rename {n9kv => cisco/n9kv}/docker/OVMF.fd (100%) rename {n9kv => cisco/n9kv}/docker/launch.py (91%) rename {nxos => cisco/nxos}/Makefile (100%) rename {nxos => cisco/nxos}/README.md (100%) rename {nxos => cisco/nxos}/docker/Dockerfile (73%) rename {nxos => cisco/nxos}/docker/launch.py (97%) rename {vios => cisco/vios}/Makefile (67%) rename {vios => cisco/vios}/README.md (100%) rename {vios => cisco/vios}/docker/Dockerfile (81%) rename {vios => cisco/vios}/docker/launch.py (95%) rename {xrv => cisco/xrv}/Makefile (76%) rename {xrv => cisco/xrv}/README.md (100%) rename {xrv => cisco/xrv}/docker/Dockerfile (73%) rename {xrv => cisco/xrv}/docker/launch.py (82%) rename {xrv9k => cisco/xrv9k}/LICENSE (100%) create mode 100644 cisco/xrv9k/Makefile rename {xrv9k => cisco/xrv9k}/README.md (100%) create mode 100644 cisco/xrv9k/cidfile rename {xrv9k => cisco/xrv9k}/docker/Dockerfile (75%) rename {xrv9k => cisco/xrv9k}/docker/launch.py (54%) delete mode 100644 ftdv/Makefile rename {vjunosevolved => juniper/vjunosevolved}/Makefile (76%) rename {vjunosevolved => juniper/vjunosevolved}/README.md (100%) rename {vjunosevolved => juniper/vjunosevolved}/docker/Dockerfile (82%) rename {vjunosevolved => juniper/vjunosevolved}/docker/init.conf (100%) rename {vjunosevolved => juniper/vjunosevolved}/docker/launch.py (98%) rename {vjunosevolved => juniper/vjunosevolved}/docker/make-config.sh (100%) rename {vjunosrouter => juniper/vjunosrouter}/Makefile (71%) rename {vjunosrouter => juniper/vjunosrouter}/README.md (100%) rename {vjunosswitch => juniper/vjunosrouter}/docker/Dockerfile (81%) rename {vjunosrouter => juniper/vjunosrouter}/docker/init.conf (100%) rename {vjunosrouter => juniper/vjunosrouter}/docker/launch.py (97%) rename {vjunosrouter => juniper/vjunosrouter}/docker/make-config.sh (100%) rename {vjunosswitch => juniper/vjunosswitch}/Makefile (76%) rename {vjunosswitch => juniper/vjunosswitch}/README.md (100%) rename {vjunosrouter => juniper/vjunosswitch}/docker/Dockerfile (81%) rename {vjunosswitch => juniper/vjunosswitch}/docker/init.conf (100%) rename {vjunosswitch => juniper/vjunosswitch}/docker/launch.py (97%) rename {vjunosswitch => juniper/vjunosswitch}/docker/make-config.sh (100%) rename {vmx => juniper/vmx}/Makefile (79%) rename {vmx => juniper/vmx}/README.md (100%) rename {vmx => juniper/vmx}/docker/Dockerfile (73%) rename {vmx => juniper/vmx}/docker/launch.py (97%) rename {vmx => juniper/vmx}/vmx-extract.sh (100%) rename {vqfx => juniper/vqfx}/Makefile (94%) rename {vqfx => juniper/vqfx}/README.md (100%) rename {vqfx => juniper/vqfx}/docker/Dockerfile (73%) rename {vqfx => juniper/vqfx}/docker/launch.py (97%) rename {vsrx => juniper/vsrx}/Makefile (72%) rename {vsrx => juniper/vsrx}/README.md (100%) rename {vsrx => juniper/vsrx}/docker/Dockerfile (83%) rename {vsrx => juniper/vsrx}/docker/init.conf (100%) rename {vsrx => juniper/vsrx}/docker/launch.py (97%) rename {vsrx => juniper/vsrx}/docker/make-config-iso.sh (100%) delete mode 100644 xrv9k/Makefile diff --git a/aoscx/docker/Dockerfile b/aoscx/docker/Dockerfile index 751b31ea..b12bb5ed 100644 --- a/aoscx/docker/Dockerfile +++ b/aoscx/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye +FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Stefano Sasso ENV DEBIAN_FRONTEND=noninteractive @@ -19,7 +19,11 @@ RUN apt-get update -qy \ ftp \ qemu-system-x86=1:5.2+dfsg-11+deb11u2 \ qemu-utils=1:5.2+dfsg-11+deb11u2 \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli ARG IMAGE COPY $IMAGE* / diff --git a/aoscx/docker/launch.py b/aoscx/docker/launch.py index 04f9a0ef..77dc60ee 100755 --- a/aoscx/docker/launch.py +++ b/aoscx/docker/launch.py @@ -64,7 +64,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"switch login:"], 1) + (ridx, match, res) = self.expect([b"switch login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("trying to log in with 'admin'") @@ -92,8 +92,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/asav/Makefile b/asav/Makefile deleted file mode 100644 index ec25d49e..00000000 --- a/asav/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -VENDOR=Cisco -NAME=ASAv -IMAGE_FORMAT=qcow2 -IMAGE_GLOB=*.qcow2 - -# match versions like: -# asav9-18-2.qcow2 -VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\-[0-9]\+\-[0-9]\+[a-z]\?\)\([^0-9].*\|$$\)/\1/') - --include ../makefile-sanity.include --include ../makefile.include --include ../makefile-install.include \ No newline at end of file diff --git a/cisco/asav/Makefile b/cisco/asav/Makefile new file mode 100644 index 00000000..721ab989 --- /dev/null +++ b/cisco/asav/Makefile @@ -0,0 +1,21 @@ +VENDOR=cisco +NAME=asav +IMAGE_FORMAT=qcow2 +IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 + +# match versions like: +# asav9-18-2.qcow2 +VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\-[0-9]\+\-[0-9]\+[a-z]\?\)\([^0-9].*\|$$\)/\1/') + +-include ../../makefile-sanity.include +-include ../../makefile.include + +docker-pre-build: + -cat cidfile | xargs --no-run-if-empty docker rm -f + -rm cidfile + +docker-build: docker-build-common + docker run --cidfile cidfile --privileged $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) --trace --install + docker commit --change='ENTRYPOINT ["/launch.py"]' $$(cat cidfile) $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) + docker rm -f $$(cat cidfile) \ No newline at end of file diff --git a/asav/README.md b/cisco/asav/README.md similarity index 100% rename from asav/README.md rename to cisco/asav/README.md diff --git a/cisco/asav/cidfile b/cisco/asav/cidfile new file mode 100644 index 00000000..d7be3343 --- /dev/null +++ b/cisco/asav/cidfile @@ -0,0 +1 @@ +4ea02de7db6f6d3d77e1c19085eb0d18cbf994a094cb91a5c214de8086db8b24 \ No newline at end of file diff --git a/asav/docker/Dockerfile b/cisco/asav/docker/Dockerfile old mode 100644 new mode 100755 similarity index 71% rename from asav/docker/Dockerfile rename to cisco/asav/docker/Dockerfile index 720080c4..31fe70c6 --- a/asav/docker/Dockerfile +++ b/cisco/asav/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Kristian Larsson ENV DEBIAN_FRONTEND=noninteractive @@ -17,7 +17,11 @@ RUN apt-get update -qy \ dnsutils \ telnet \ genisoimage \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG VERSION ENV VERSION=${VERSION} diff --git a/asav/docker/launch.py b/cisco/asav/docker/launch.py old mode 100644 new mode 100755 similarity index 82% rename from asav/docker/launch.py rename to cisco/asav/docker/launch.py index 309becfc..ca75ea85 --- a/asav/docker/launch.py +++ b/cisco/asav/docker/launch.py @@ -37,7 +37,7 @@ def trace(self, message, *args, **kws): class ASAv_vm(vrnetlab.VM): - def __init__(self, username, password, install_mode=False): + def __init__(self, username, password, conn_mode, install_mode=False): for e in os.listdir("/"): if re.search(".qcow2$", e): disk_image = "/" + e @@ -47,6 +47,7 @@ def __init__(self, username, password, install_mode=False): ) self.nic_type = "e1000" self.install_mode = install_mode + self.conn_mode = conn_mode self.num_nics = 8 def bootstrap_spin(self): @@ -58,18 +59,18 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"ciscoasa>"], 1) + (ridx, match, res) = self.expect([b"ciscoasa>"], 1) if match: # got a match! if ridx == 0: # login if self.install_mode: - self.logger.debug("matched, ciscoasa>") + self.logger.info("matched, ciscoasa>") self.wait_write("", wait=None) self.wait_write("", None) self.wait_write("", wait="ciscoasa>") self.running = True return - self.logger.debug("matched, ciscoasa>") + self.logger.info("matched, ciscoasa>") self.wait_write("", wait=None) # run main config! @@ -85,8 +86,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -103,8 +104,7 @@ def bootstrap_config(self): self.wait_write("VR-netlab9", wait="Repeat Password:") self.wait_write("", wait="ciscoasa#") self.wait_write("configure terminal", wait="#") - self.wait_write("N", wait="[Y]es, [N]o, [A]sk later:") - self.wait_write("", wait="(config)#") + self.wait_write("N", wait="[Y]es, [N]o, [A]sk later:", timeout=15) self.wait_write("aaa authentication ssh console LOCAL") self.wait_write("aaa authentication enable console LOCAL") self.wait_write( @@ -114,6 +114,7 @@ def bootstrap_config(self): self.wait_write("nameif management") self.wait_write("ip address 10.0.0.15 255.255.255.0") self.wait_write("no shutdown") + self.wait_write("route management 0.0.0.0 0.0.0.0 10.0.0.2") self.wait_write("ssh 0.0.0.0 0.0.0.0 management") self.wait_write("ssh version 2") self.wait_write("ssh key-exchange group dh-group14-sha256") @@ -124,17 +125,17 @@ def bootstrap_config(self): class ASAv(vrnetlab.VR): - def __init__(self, username, password): + def __init__(self, username, password, conn_mode): super(ASAv, self).__init__(username, password) - self.vms = [ASAv_vm(username, password)] + self.vms = [ASAv_vm(username, password, conn_mode)] class ASAv_installer(ASAv): """ASAv installer""" - def __init__(self, username, password): + def __init__(self, username, password, conn_mode): super(ASAv, self).__init__(username, password) - self.vms = [ASAv_vm(username, password, install_mode=True)] + self.vms = [ASAv_vm(username, password, conn_mode, install_mode=True)] def install(self): self.logger.info("Installing ASAv") @@ -156,6 +157,11 @@ def install(self): parser.add_argument("--username", default="vrnetlab", help="Username") parser.add_argument("--password", default="VR-netlab9", help="Password") parser.add_argument("--install", action="store_true", help="Install ASAv") + parser.add_argument( + "--connection-mode", + default="vrxcon", + help="Connection mode to use in the datapath", + ) args = parser.parse_args() LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" @@ -167,8 +173,8 @@ def install(self): logger.setLevel(1) if args.install: - vr = ASAv_installer(args.username, args.password) + vr = ASAv_installer(args.username, args.password, args.connection_mode) vr.install() else: - vr = ASAv(args.username, args.password) + vr = ASAv(args.username, args.password, args.connection_mode) vr.start() diff --git a/c8000v/Makefile b/cisco/c8000v/Makefile similarity index 74% rename from c8000v/Makefile rename to cisco/c8000v/Makefile index daa054bd..bf76d17e 100644 --- a/c8000v/Makefile +++ b/cisco/c8000v/Makefile @@ -2,14 +2,18 @@ VENDOR=cisco NAME=c8000v IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # match versions like: # c8000v-17.11.01a.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+[a-z]\?\)\([^0-9].*\|$$\)/\1/') --include ../makefile-sanity.include --include ../makefile.include --include ../makefile-install.include +-include ../../makefile-sanity.include +-include ../../makefile.include + +docker-pre-build: + -cat cidfile | xargs --no-run-if-empty docker rm -f + -rm cidfile docker-build: docker-build-common docker run --cidfile cidfile --privileged $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) --trace --install diff --git a/c8000v/README.md b/cisco/c8000v/README.md similarity index 100% rename from c8000v/README.md rename to cisco/c8000v/README.md diff --git a/cisco/c8000v/cidfile b/cisco/c8000v/cidfile new file mode 100644 index 00000000..ad105234 --- /dev/null +++ b/cisco/c8000v/cidfile @@ -0,0 +1 @@ +dec25a2bb0046602fd3944d40220435c8c53632fa20d70e611914453a7765e3b \ No newline at end of file diff --git a/c8000v/docker/Dockerfile b/cisco/c8000v/docker/Dockerfile similarity index 72% rename from c8000v/docker/Dockerfile rename to cisco/c8000v/docker/Dockerfile index 0550287f..608afb71 100644 --- a/c8000v/docker/Dockerfile +++ b/cisco/c8000v/docker/Dockerfile @@ -3,19 +3,24 @@ FROM public.ecr.aws/docker/library/debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -qy \ - && apt-get install -y \ + && apt-get install -y --no-install-recommends \ bridge-utils \ iproute2 \ socat \ qemu-kvm \ + qemu-utils \ tcpdump \ inetutils-ping \ ssh \ telnet \ procps \ genisoimage \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG VERSION ENV VERSION=${VERSION} ARG IMAGE diff --git a/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py similarity index 93% rename from c8000v/docker/launch.py rename to cisco/c8000v/docker/launch.py index 9c129b5c..f0e43506 100755 --- a/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -104,14 +104,14 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [b"Press RETURN to get started!", b"IOSXEBOOT-4-FACTORY_RESET"], 1 ) if match: # got a match! if ridx == 0: # login - self.logger.debug("matched, Press RETURN to get started.") + self.logger.info("matched, Press RETURN to get started.") if self.install_mode: - self.logger.debug("Now we wait for the device to reload") + self.logger.info("Now we wait for the device to reload") else: self.wait_write("", wait=None) @@ -134,12 +134,12 @@ def bootstrap_spin(self): self.running = True return else: - self.log.warning("Unexpected reload while running") + self.log.debug("Unexpected reload while running") # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s", res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -197,14 +197,14 @@ def startup_config(self): """Load additional config provided by user.""" if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warn(f"User provided startup configuration is not found") return - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") with open(STARTUP_CONFIG_FILE) as file: config_lines = file.readlines() config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") diff --git a/cat9kv/Makefile b/cisco/cat9kv/Makefile similarity index 72% rename from cat9kv/Makefile rename to cisco/cat9kv/Makefile index 4e4fd6d5..efd68101 100644 --- a/cat9kv/Makefile +++ b/cisco/cat9kv/Makefile @@ -1,12 +1,13 @@ -VENDOR=Cisco +VENDOR=cisco NAME=cat9kv IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # match versions like: # csr1000v-universalk9.16.03.01a.qcow2 # csr1000v-universalk9.16.04.01.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+[a-z]\?\)\([^0-9].*\|$$\)/\1/') --include ../makefile-sanity.include --include ../makefile.include \ No newline at end of file +-include ../../makefile-sanity.include +-include ../../makefile.include \ No newline at end of file diff --git a/cat9kv/README.md b/cisco/cat9kv/README.md similarity index 100% rename from cat9kv/README.md rename to cisco/cat9kv/README.md diff --git a/cat9kv/docker/Dockerfile b/cisco/cat9kv/docker/Dockerfile similarity index 86% rename from cat9kv/docker/Dockerfile rename to cisco/cat9kv/docker/Dockerfile index 335c7dd1..56e8c844 100644 --- a/cat9kv/docker/Dockerfile +++ b/cisco/cat9kv/docker/Dockerfile @@ -16,8 +16,12 @@ RUN apt-get update -qy \ telnet \ procps \ genisoimage \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG VERSION ENV VERSION=${VERSION} ARG IMAGE diff --git a/cat9kv/docker/launch.py b/cisco/cat9kv/docker/launch.py similarity index 92% rename from cat9kv/docker/launch.py rename to cisco/cat9kv/docker/launch.py index 56e00fcc..0677aed8 100755 --- a/cat9kv/docker/launch.py +++ b/cisco/cat9kv/docker/launch.py @@ -82,7 +82,7 @@ def create_boot_image(self): try: os.makedirs("/img_dir/conf") except: - self.logger.error( + self.logger.debug( "Unable to make '/img_dir'. Does the directory already exist?" ) @@ -115,7 +115,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ b"Press RETURN to get started!", b"IOSXEBOOT-4-FACTORY_RESET", @@ -124,7 +124,7 @@ def bootstrap_spin(self): ) if match: # got a match! if ridx == 0: # login - self.logger.debug("matched, Press RETURN to get started.") + self.logger.info("matched, Press RETURN to get started.") self.wait_write("", wait=None) @@ -141,12 +141,12 @@ def bootstrap_spin(self): self.running = True return elif ridx == 1: # IOSXEBOOT-4-FACTORY_RESET - self.logger.warning("Unexpected reload while running") + self.logger.debug("Unexpected reload while running") # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s", res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -160,7 +160,7 @@ def bootstrap_config(self): self.wait_write("", None) self.wait_write("enable", wait=">") - self.wait_write("configure terminal", wait=">") + self.wait_write("configure terminal", wait="#") self.wait_write(f"hostname {self.hostname}") self.wait_write( @@ -201,14 +201,14 @@ def startup_config(self): """Load additional config provided by user.""" if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} is not found") return - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") with open(STARTUP_CONFIG_FILE) as file: config_lines = file.readlines() config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") diff --git a/csr/Makefile b/cisco/csr/Makefile similarity index 87% rename from csr/Makefile rename to cisco/csr/Makefile index 4096c798..b45f8806 100644 --- a/csr/Makefile +++ b/cisco/csr/Makefile @@ -2,14 +2,16 @@ VENDOR=cisco NAME=csr1000v IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 + # match versions like: # csr1000v-universalk9.16.03.01a.qcow2 # csr1000v-universalk9.16.04.01.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+[sb]\?\?\)\([^0-9].*\|$$\)/\1/') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include docker-pre-build: -cat cidfile | xargs --no-run-if-empty docker rm -f diff --git a/csr/README.md b/cisco/csr/README.md similarity index 100% rename from csr/README.md rename to cisco/csr/README.md diff --git a/cisco/csr/cidfile b/cisco/csr/cidfile new file mode 100644 index 00000000..80e2761b --- /dev/null +++ b/cisco/csr/cidfile @@ -0,0 +1 @@ +49222bb4e41dc73a530ab1b208d28c03ba4327f45eed4ed93a1fb029e4310bec \ No newline at end of file diff --git a/csr/docker/Dockerfile b/cisco/csr/docker/Dockerfile similarity index 77% rename from csr/docker/Dockerfile rename to cisco/csr/docker/Dockerfile index 7606384d..ffad487d 100644 --- a/csr/docker/Dockerfile +++ b/cisco/csr/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Kristian Larsson MAINTAINER Denis Pointer @@ -18,8 +18,12 @@ RUN apt-get update -qy \ dnsutils \ telnet \ genisoimage \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG VERSION ENV VERSION=${VERSION} ARG IMAGE diff --git a/csr/docker/launch.py b/cisco/csr/docker/launch.py similarity index 93% rename from csr/docker/launch.py rename to cisco/csr/docker/launch.py index a8598f3f..1d128b4c 100755 --- a/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -72,8 +72,9 @@ def __init__( def create_boot_image(self): """Creates a iso image with a bootstrap configuration""" - + cfg_file = open("/iosxe_config.txt", "w") + if self.license: cfg_file.write("do clock set 13:33:37 1 Jan 2010\r\n") cfg_file.write("interface GigabitEthernet1\r\n") @@ -109,22 +110,23 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"Press RETURN to get started!"], 1) + (ridx, match, res) = self.expect([b"Press RETURN to get started!"], 1) if match: # got a match! if ridx == 0: # login if self.install_mode: self.running = True return - self.logger.debug("matched, Press RETURN to get started.") + self.logger.info("matched, Press RETURN to get started.") self.wait_write("", wait=None) # run main config! self.bootstrap_config() self.startup_config() self.running = True - # close telnet connection + self.tn.close() + # startup time? startup_time = datetime.datetime.now() - self.start_time self.logger.info("Startup complete in: %s" % startup_time) @@ -132,13 +134,12 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 self.spins += 1 - return def bootstrap_config(self): @@ -147,7 +148,7 @@ def bootstrap_config(self): self.wait_write("", None) self.wait_write("enable", wait=">") - self.wait_write("configure terminal", wait=">") + self.wait_write("configure terminal") self.wait_write("hostname %s" % (self.hostname)) self.wait_write( @@ -186,14 +187,14 @@ def startup_config(self): """Load additional config provided by user.""" if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warning(f"User provided startup configuration is not found.") return - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") with open(STARTUP_CONFIG_FILE) as file: config_lines = file.readlines() config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") @@ -265,7 +266,7 @@ def install(self): logging.basicConfig(format=LOG_FORMAT) logger = logging.getLogger() - logger.setLevel(logging.DEBUG) + # logger.setLevel(logging.DEBUG) if args.trace: logger.setLevel(1) diff --git a/cisco/ftdv/Makefile b/cisco/ftdv/Makefile new file mode 100644 index 00000000..d0cf74a4 --- /dev/null +++ b/cisco/ftdv/Makefile @@ -0,0 +1,21 @@ +VENDOR=cisco +NAME=ftdv +IMAGE_FORMAT=qcow2 +IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 + +# match versions like: +# Cisco_Secure_Firewall_Threat_Defense_Virtual-7.2.5-208.qcow2 +VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+-\([0-9]\+\.[0-9]\+\.[0-9]\+\)-[0-9]\+.*/\1/') + +-include ../../makefile-sanity.include +-include ../../makefile.include + +docker-pre-build: + -cat cidfile | xargs --no-run-if-empty docker rm -f + -rm cidfile + +docker-build: docker-build-common + docker run --cidfile cidfile --privileged $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) --trace --install + docker commit --change='ENTRYPOINT ["/launch.py"]' $$(cat cidfile) $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) + docker rm -f $$(cat cidfile) diff --git a/ftdv/README.md b/cisco/ftdv/README.md similarity index 100% rename from ftdv/README.md rename to cisco/ftdv/README.md diff --git a/cisco/ftdv/cidfile b/cisco/ftdv/cidfile new file mode 100644 index 00000000..e63ab613 --- /dev/null +++ b/cisco/ftdv/cidfile @@ -0,0 +1 @@ +5fd60bb778efd8604e20201450d6fb31a5cfe88617592f5533265b218f72f917 \ No newline at end of file diff --git a/ftdv/docker/Dockerfile b/cisco/ftdv/docker/Dockerfile similarity index 82% rename from ftdv/docker/Dockerfile rename to cisco/ftdv/docker/Dockerfile index 5d448346..19324856 100644 --- a/ftdv/docker/Dockerfile +++ b/cisco/ftdv/docker/Dockerfile @@ -18,8 +18,12 @@ RUN apt-get update -qy \ nftables \ telnet \ genisoimage \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/ftdv/docker/launch.py b/cisco/ftdv/docker/launch.py similarity index 93% rename from ftdv/docker/launch.py rename to cisco/ftdv/docker/launch.py index 631708b2..27083e20 100755 --- a/ftdv/docker/launch.py +++ b/cisco/ftdv/docker/launch.py @@ -136,19 +136,19 @@ def bootstrap_spin(self): self.start() return - (_, l_match, l_res) = self.tn.expect([b"INFO: Power-On Self-Test"], 1) - if l_match: - self.logger.debug("LOGIN READY") - self.login_ready = True - # no match, if we saw some output from the router it's probably - # booting, so let's give it some more time - elif l_res != b"": - self.logger.trace("OUTPUT: %s" % l_res.decode()) - # reset spins if we saw some output - self.spins = 0 - - if self.login_ready or not self.install_mode: - (ridx, match, res) = self.tn.expect([b"login:"], 1) + if not self.login_ready: + (_, l_match, l_res) = self.expect([b"INFO: Power-On Self-Test"], 1) + if l_match: + self.logger.info("LOGIN READY") + self.login_ready = True + # no match, if we saw some output from the router it's probably + # booting, so let's give it some more time + elif l_res != "": + self.print(l_res) + # reset spins if we saw some output + self.spins = 0 + elif self.login_ready or not self.install_mode: + (ridx, match, _) = self.expect([b"login:"], 3) if match: # got a match! if ridx == 0: # login if self.install_mode: @@ -169,7 +169,7 @@ def bootstrap_spin(self): self.running = True return - self.logger.debug("matched, login:") + self.logger.info("matched, login:") self.wait_write("", wait=None) self.bootstrap_config() diff --git a/n9kv/Makefile b/cisco/n9kv/Makefile similarity index 78% rename from n9kv/Makefile rename to cisco/n9kv/Makefile index b673c695..4d35293d 100644 --- a/n9kv/Makefile +++ b/cisco/n9kv/Makefile @@ -1,7 +1,8 @@ -VENDOR=Cisco -NAME=NXOS 9000v +VENDOR=cisco +NAME=nxos9000v IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # Match versions similar to the following: # - nxosv-final.7.0.3.I7.5a.qcow2 @@ -13,5 +14,5 @@ IMAGE_GLOB=*.qcow2 # - nexus9300v64.10.2.2.F.qcow VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+\?\.\(\(7\.0\.3\.I[0-9]\.[0-9a-z]\+\)\|\([0-9]\+\.[0-9]\+\.[0-9]\+\)\)\(\..*\|$$\)/\1/') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/n9kv/README.md b/cisco/n9kv/README.md similarity index 100% rename from n9kv/README.md rename to cisco/n9kv/README.md diff --git a/n9kv/docker/Dockerfile b/cisco/n9kv/docker/Dockerfile similarity index 79% rename from n9kv/docker/Dockerfile rename to cisco/n9kv/docker/Dockerfile index 53c78a09..4debcb71 100644 --- a/n9kv/docker/Dockerfile +++ b/cisco/n9kv/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL maintainer="Kristian Larsson " LABEL maintainer="Roman Dodin " @@ -20,8 +20,12 @@ RUN apt-get update -qy \ iptables \ nftables \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY OVMF.fd / diff --git a/n9kv/docker/OVMF.fd b/cisco/n9kv/docker/OVMF.fd similarity index 100% rename from n9kv/docker/OVMF.fd rename to cisco/n9kv/docker/OVMF.fd diff --git a/n9kv/docker/launch.py b/cisco/n9kv/docker/launch.py similarity index 91% rename from n9kv/docker/launch.py rename to cisco/n9kv/docker/launch.py index 1115f595..afde399e 100755 --- a/n9kv/docker/launch.py +++ b/cisco/n9kv/docker/launch.py @@ -88,10 +88,10 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"\(yes\/skip\/no\)\[no\]:",b"\(yes\/no\)\[n\]:", b"\(yes\/no\)\[no\]:", b"login:"], 1) + (ridx, match, res) = self.expect([b"\(yes\/skip\/no\)\[no\]:",b"\(yes\/no\)\[n\]:", b"\(yes\/no\)\[no\]:", b"login:"], 1) if match: # got a match! if ridx in (0, 1, 2): - self.logger.debug("matched poap prompt") + self.logger.info("matched poap prompt") self.wait_write("yes", wait=None) self.wait_write( "no", wait="Do you want to enforce secure password standard" @@ -102,8 +102,8 @@ def bootstrap_spin(self): "no", wait="Would you like to enter the basic configuration dialog" ) elif ridx == 3: # login - self.logger.debug("matched login prompt") - self.logger.debug(f'trying to log in with "admin" / {self.password}') + self.logger.info("matched login prompt") + self.logger.info(f'trying to log in with "admin" / {self.password}') self.wait_write("admin", wait=None) self.wait_write(self.password, wait="Password:") @@ -121,8 +121,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -164,14 +164,14 @@ def startup_config(self): """Load additional config provided by user.""" if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} is not found") return - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") with open(STARTUP_CONFIG_FILE) as file: config_lines = file.readlines() config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") diff --git a/nxos/Makefile b/cisco/nxos/Makefile similarity index 100% rename from nxos/Makefile rename to cisco/nxos/Makefile diff --git a/nxos/README.md b/cisco/nxos/README.md similarity index 100% rename from nxos/README.md rename to cisco/nxos/README.md diff --git a/nxos/docker/Dockerfile b/cisco/nxos/docker/Dockerfile similarity index 73% rename from nxos/docker/Dockerfile rename to cisco/nxos/docker/Dockerfile index 705006df..e8461e0e 100644 --- a/nxos/docker/Dockerfile +++ b/cisco/nxos/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL org.opencontainers.image.authors="roman@dodin.dev" ENV DEBIAN_FRONTEND=noninteractive @@ -14,8 +14,12 @@ RUN apt-get update -qy \ tcpdump \ procps \ openvswitch-switch \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/nxos/docker/launch.py b/cisco/nxos/docker/launch.py similarity index 97% rename from nxos/docker/launch.py rename to cisco/nxos/docker/launch.py index a06eefe9..8844f24c 100755 --- a/nxos/docker/launch.py +++ b/cisco/nxos/docker/launch.py @@ -59,7 +59,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login:"], 1) + (ridx, match, res) = self.expect([b"login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("matched login prompt") @@ -88,8 +88,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vios/Makefile b/cisco/vios/Makefile similarity index 67% rename from vios/Makefile rename to cisco/vios/Makefile index 5a28b837..edc3b874 100644 --- a/vios/Makefile +++ b/cisco/vios/Makefile @@ -1,12 +1,13 @@ -VENDOR=Cisco -NAME=vIOS +VENDOR=cisco +NAME=vios IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # Match images like: # - cisco_vios-158-3.M2.qcow2 # Extract version cisco_vios and qcow2, for example: 158-3.M2 VERSION=$(shell echo $(IMAGE) | sed -e 's/cisco_vios-\(.*\)\.qcow2/\1/') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/vios/README.md b/cisco/vios/README.md similarity index 100% rename from vios/README.md rename to cisco/vios/README.md diff --git a/vios/docker/Dockerfile b/cisco/vios/docker/Dockerfile similarity index 81% rename from vios/docker/Dockerfile rename to cisco/vios/docker/Dockerfile index d9e8aada..e3eeb004 100644 --- a/vios/docker/Dockerfile +++ b/cisco/vios/docker/Dockerfile @@ -12,8 +12,12 @@ RUN apt-get update -qy \ qemu-kvm \ qemu-utils \ tcpdump \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/vios/docker/launch.py b/cisco/vios/docker/launch.py similarity index 95% rename from vios/docker/launch.py rename to cisco/vios/docker/launch.py index a7b70339..3df4b5a5 100755 --- a/vios/docker/launch.py +++ b/cisco/vios/docker/launch.py @@ -68,7 +68,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ rb"Would you like to enter the initial configuration dialog\? \[yes/no\]:", b"Press RETURN to get started!", @@ -101,8 +101,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace(f"OUTPUT: {res.decode()}") + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -163,10 +163,10 @@ def _bootstrap_config(self): def _load_startup_config(self): if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} not found") + self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} not found") return - self.logger.trace(f"Loading startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Loading startup config file {STARTUP_CONFIG_FILE}") with open(STARTUP_CONFIG_FILE) as file: for line in (line.rstrip() for line in file): self.wait_write(line) diff --git a/xrv/Makefile b/cisco/xrv/Makefile similarity index 76% rename from xrv/Makefile rename to cisco/xrv/Makefile index 59c24901..ac38953a 100644 --- a/xrv/Makefile +++ b/cisco/xrv/Makefile @@ -1,7 +1,8 @@ -VENDOR=Cisco -NAME=XRv +VENDOR=cisco +NAME=xrv IMAGE_FORMAT=vmdk IMAGE_GLOB=*vmdk* +VENDOR_SUBDIR=1 # match versions like: # iosxrv-k9-demo-5.3.3.51U.vmdk @@ -11,5 +12,5 @@ IMAGE_GLOB=*vmdk* # iosxrv-k9-demo-6.2.2.22I.vmdk VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+\(\.[0-9A-Z]\+\)\?\)\([^0-9].*\|$$\)/\1/') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/xrv/README.md b/cisco/xrv/README.md similarity index 100% rename from xrv/README.md rename to cisco/xrv/README.md diff --git a/xrv/docker/Dockerfile b/cisco/xrv/docker/Dockerfile similarity index 73% rename from xrv/docker/Dockerfile rename to cisco/xrv/docker/Dockerfile index fb4a41aa..98853989 100644 --- a/xrv/docker/Dockerfile +++ b/cisco/xrv/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL org.opencontainers.image.authors="roman@dodin.dev" ARG DEBIAN_FRONTEND=noninteractive @@ -14,8 +14,12 @@ RUN apt-get update -qy \ tcpdump \ procps \ openvswitch-switch \ + python3-pip \ + git \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/xrv/docker/launch.py b/cisco/xrv/docker/launch.py similarity index 82% rename from xrv/docker/launch.py rename to cisco/xrv/docker/launch.py index 7c0557cd..3ae033ce 100755 --- a/xrv/docker/launch.py +++ b/cisco/xrv/docker/launch.py @@ -64,19 +64,19 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ b"Press RETURN to get started", b"SYSTEM CONFIGURATION COMPLETE", b"Enter root-system username", b"Username:", - b"^[^ ]+#", + b"#", ], 1, ) if match: # got a match! if ridx == 0: # press return to get started, so we press return! - self.logger.debug("got 'press return to get started...'") + self.logger.info("got 'press return to get started...'") self.wait_write("", wait=None) if ridx == 1: # system configuration complete self.logger.info( @@ -92,13 +92,13 @@ def bootstrap_spin(self): self.wait_write(self.password, wait="Enter secret again:") self.credentials.insert(0, [self.username, self.password]) if ridx == 3: # matched login prompt, so should login - self.logger.debug("matched login prompt") + self.logger.info("matched login prompt") try: username, password = self.credentials.pop(0) except IndexError as exc: self.logger.error("no more credentials to try") return - self.logger.debug( + self.logger.info( "trying to log in with %s / %s" % (username, password) ) self.wait_write(username, wait=None) @@ -118,8 +118,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -135,35 +135,12 @@ def bootstrap_config(self): self.wait_write("terminal length 0") self.wait_write("crypto key generate rsa") - # check if we are prompted to overwrite current keys - (ridx, match, res) = self.tn.expect( - [ - b"How many bits in the modulus", - b"Do you really want to replace them", - b"^[^ ]+#", - ], - 10, - ) - if match: # got a match! - if ridx == 0: - self.wait_write("2048", None) - elif ridx == 1: # press return to get started, so we press return! - self.wait_write("no", None) + + self.wait_write("2048", wait=None) # make sure we get our prompt back self.wait_write("") - if self.username and self.password: - self.wait_write("admin") - self.wait_write("configure") - self.wait_write("username %s group root-system" % (self.username)) - self.wait_write("username %s group cisco-support" % (self.username)) - self.wait_write("username %s secret %s" % (self.username, self.password)) - self.wait_write("commit") - self.wait_write("exit") - self.wait_write("exit") - - self.wait_write("show interface description") self.wait_write("configure") self.wait_write("hostname {}".format(self.hostname)) @@ -212,14 +189,14 @@ def startup_config(self): """Load additional config provided by user.""" if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} is not found") return - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") with open(STARTUP_CONFIG_FILE) as file: config_lines = file.readlines() config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") @@ -273,4 +250,4 @@ def __init__(self, hostname, username, password, conn_mode): vrnetlab.boot_delay() vr = XRV(args.hostname, args.username, args.password, args.connection_mode) - vr.start() + vr.start() \ No newline at end of file diff --git a/xrv9k/LICENSE b/cisco/xrv9k/LICENSE similarity index 100% rename from xrv9k/LICENSE rename to cisco/xrv9k/LICENSE diff --git a/cisco/xrv9k/Makefile b/cisco/xrv9k/Makefile new file mode 100644 index 00000000..863702f1 --- /dev/null +++ b/cisco/xrv9k/Makefile @@ -0,0 +1,31 @@ +VENDOR=cisco +NAME=xrv9k +IMAGE_FORMAT=qcow2 +IMAGE_GLOB=*.qcow2 +INSTALL=true +VENDOR_SUBDIR=1 + +# match versions like: +# TODO: add example file names here +# xrv9k-fullk9-x.vrr-6.1.3.qcow2 +# xrv9k-fullk9-x.vrr-6.2.1.qcow2 +# xrv9k-fullk9-x-7.10.1.qcow2 +VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+\(\.[0-9A-Z]\+\)\?\)\([^0-9].*\|$$\)/\1/') + +-include ../../makefile-sanity.include +-include ../../makefile.include + +ifeq ($(INSTALL),false) +$(info Install mode disabled) +else +$(info Install mode enabled) +docker-pre-build: + -cat cidfile | xargs --no-run-if-empty docker rm -f + -rm cidfile + +docker-build: docker-build-common + docker run --cidfile cidfile --privileged $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) --trace --install + docker commit --change='ENTRYPOINT ["/launch.py"]' $$(cat cidfile) $(REGISTRY)$(VENDOR)_$(NAME):$(VERSION) + docker rm -f $$(cat cidfile) +endif + diff --git a/xrv9k/README.md b/cisco/xrv9k/README.md similarity index 100% rename from xrv9k/README.md rename to cisco/xrv9k/README.md diff --git a/cisco/xrv9k/cidfile b/cisco/xrv9k/cidfile new file mode 100644 index 00000000..385b1191 --- /dev/null +++ b/cisco/xrv9k/cidfile @@ -0,0 +1 @@ +eec20e5056c30adc092444430c04256dbaac927eb492e7e97c386bfbad5b60dd \ No newline at end of file diff --git a/xrv9k/docker/Dockerfile b/cisco/xrv9k/docker/Dockerfile similarity index 75% rename from xrv9k/docker/Dockerfile rename to cisco/xrv9k/docker/Dockerfile index 4658bdf3..c46d0bc7 100644 --- a/xrv9k/docker/Dockerfile +++ b/cisco/xrv9k/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL org.opencontainers.image.authors="roman@dodin.dev" ARG DEBIAN_FRONTEND=noninteractive @@ -17,8 +17,12 @@ RUN apt-get update -qy \ inetutils-ping \ dnsutils \ openvswitch-switch \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py similarity index 54% rename from xrv9k/docker/launch.py rename to cisco/xrv9k/docker/launch.py index 601b624e..0d97f3c9 100755 --- a/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -10,6 +10,7 @@ import vrnetlab +from scrapli.driver.core import IOSXRDriver STARTUP_CONFIG_FILE = "/config/startup-config.cfg" @@ -46,10 +47,12 @@ def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram, ins if not disk_image and re.search(".qcow2", e): disk_image = "/" + e super(XRV_vm, self).__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") + self.hostname = hostname self.conn_mode = conn_mode self.num_nics = nics self.install_mode = install + self.qemu_args.extend( [ "-machine", @@ -67,15 +70,28 @@ def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram, ins ] ) self.credentials = [] - - self.xr_ready = False + + # init scrapli + xr_scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + self.num, + "auth_username": self.username, + "auth_password": self.password, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 600, + "timeout_transport": 600, + "timeout_ops": 600, + } + + self.xr_conn = IOSXRDriver(**xr_scrapli_dev) def gen_mgmt(self): """Generate qemu args for the mgmt interface(s)""" res = [] # mgmt interface res.extend( - ["-device", "virtio-net-pci,netdev=mgmt,mac=%s" % vrnetlab.gen_mac(0)] + ["-device", "e1000,netdev=mgmt,mac=%s" % vrnetlab.gen_mac(0)] ) res.extend( [ @@ -92,7 +108,7 @@ def gen_mgmt(self): res.extend( [ "-device", - "virtio-net-pci,netdev=ctrl-dummy,id=ctrl-dummy,mac=%s" + "e1000,netdev=ctrl-dummy,id=ctrl-dummy,mac=%s" % vrnetlab.gen_mac(0), "-netdev", "tap,ifname=ctrl-dummy,id=ctrl-dummy,script=no,downscript=no", @@ -102,7 +118,7 @@ def gen_mgmt(self): res.extend( [ "-device", - "virtio-net-pci,netdev=dev-dummy,id=dev-dummy,mac=%s" + "e1000,netdev=dev-dummy,id=dev-dummy,mac=%s" % vrnetlab.gen_mac(0), "-netdev", "tap,ifname=dev-dummy,id=dev-dummy,script=no,downscript=no", @@ -112,41 +128,29 @@ def gen_mgmt(self): return res def bootstrap_spin(self): - """""" if self.spins > 600: # too many spins with no result -> give up - self.logger.debug( + self.logger.error( "node is failing to boot or we can't catch the right prompt. Restarting..." ) self.stop() self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ b"Press RETURN to get started", - b"Not settable: Success", # no SYSTEM CONFIGURATION COMPLETE in xrv9k? b"Enter root-system [U|u]sername", - b"Username:", ], 1, ) - - xr_login = False # whether we are logged into the shell or not if match: # got a match! if ridx == 0: # press return to get started, so we press return! - self.logger.debug("got 'press return to get started...'") + self.logger.info("got 'press return to get started...'") self.wait_write("", wait=None) if ridx == 1: # system configuration complete - self.logger.info( - "IOS XR system configuration is complete, should be able to proceed with bootstrap configuration" - ) - self.wait_write("", wait=None) - self.xr_ready = True - if ridx == 2: # initial user config - # if we are installing and we reach this point, we are finished and don't need to bootstrap if self.install_mode: self.running = True return @@ -155,39 +159,14 @@ def bootstrap_spin(self): self.wait_write(self.password, wait="Enter secret:") self.wait_write(self.password, wait="Enter secret again:") self.credentials.insert(0, [self.username, self.password]) - if ridx == 3: # matched login prompt, so should login - self.logger.debug("matched login prompt") - - try: - username, password = self.credentials[0] - except IndexError: - self.logger.error("no credentials populated") - return - - self.logger.debug( - "trying to log in with %s / %s" % (username, password) - ) - self.wait_write(username, wait=None) - self.wait_write(password, wait="Password:") - _, match, res = self.tn.expect([b"ios#"], 3) - if match: - self.logger.debug("logged in with %s / %s successfully" % (username, password)) - xr_login = True - else: - self.logger.error("could not login with %s / %s" % (username, password)) - - if self.xr_ready is True and xr_login is True: - # run main config! if not self.bootstrap_config(): # main config failed :/ - self.logger.debug("bootstrap_config failed, restarting device") + self.logger.error("Failed to load bootstrap configuration. Restarting XRv9k.") self.stop() self.start() return - self.startup_config() - # close telnet connection - self.tn.close() + # startup time? startup_time = datetime.datetime.now() - self.start_time self.logger.info("Startup complete in: %s" % startup_time) @@ -197,8 +176,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -207,123 +186,83 @@ def bootstrap_spin(self): return def bootstrap_config(self): - """Do the actual bootstrap config""" - self.logger.info("applying bootstrap configuration") - self.wait_write("", None) - - self.wait_write("terminal length 0") - - self.wait_write("crypto key generate rsa") - # check if we are prompted to overwrite current keys - (ridx, match, res) = self.tn.expect( + self.tn.close() + + self.xr_conn.open() + + res = self.xr_conn.send_interactive( [ - b"How many bits in the modulus", - b"Do you really want to replace them", - b"^[^ ]+#", - ], - 10, + ("crypto key generate rsa", "Do you really want to replace them? [yes/no]:", False), + ("yes", "How many bits in the modulus [2048]:", False), + ("2048", "", False), + ] ) - if match: # got a match! - if ridx == 0: - self.wait_write("2048", None) - elif ridx == 1: # press return to get started, so we press return! - self.wait_write("no", None) - - # make sure we get our prompt back - self.wait_write("") - - # wait for call-home in config - if not self._wait_config("show running-config call-home", "service active"): - return False - self.wait_write("configure") - self.wait_write(f"hostname {self.hostname}") - - # configure management vrf - self.wait_write("vrf clab-mgmt") - self.wait_write("description Containerlab management VRF (DO NOT DELETE)") - self.wait_write("address-family ipv4 unicast") - self.wait_write("exit") - self.wait_write("exit") + self.logger.info(res.result) - # add static route for management - self.wait_write("router static") - self.wait_write("vrf clab-mgmt") - self.wait_write("address-family ipv4 unicast") - self.wait_write("0.0.0.0/0 10.0.0.2") - self.wait_write("exit") - self.wait_write("exit") - self.wait_write("exit") - - # configure ssh & netconf w/ vrf - self.wait_write("ssh server v2") - self.wait_write("ssh server vrf clab-mgmt") - self.wait_write("ssh server netconf port 830") # for 5.1.1 - self.wait_write("ssh server netconf vrf clab-mgmt") # for 5.3.3 - self.wait_write("netconf agent ssh") # for 5.1.1 - self.wait_write("netconf-yang agent ssh") # for 5.3.3 - # configure gNMI - self.wait_write("grpc port 57400") - self.wait_write("grpc vrf clab-mgmt") - self.wait_write("grpc no-tls") - - # configure xml agent - self.wait_write("xml agent tty") + XR_CONFIG = f"""hostname {self.hostname} +vrf clab-mgmt +description Containerlab management VRF. DO NOT DELETE. +address-family ipv4 unicast +! +router static +vrf clab-mgmt +address-family ipv4 unicast +0.0.0.0/0 10.0.0.2 +! +ssh server v2 +ssh server vrf clab-mgmt +ssh server netconf port 830 +ssh server netconf vrf clab-mgmt +netconf-yang agent ssh +! +grpc port 57400 +grpc no-tls +! +xml agent tty +! +interface MgmtEth0/RP0/CPU0/0 +vrf clab-mgmt +no shutdown +ipv4 address 10.0.0.15/24 +! +commit +""" + + res = self.xr_conn.send_config(XR_CONFIG, strip_prompt=False) + + if res.failed: + self.xr_conn.close() + return False - # configure mgmt interface - self.wait_write("interface MgmtEth0/RP0/CPU0/0") - self.wait_write("vrf clab-mgmt") - self.wait_write("no shutdown") - self.wait_write("ipv4 address 10.0.0.15/24") - self.wait_write("exit") - self.wait_write("commit") - self.wait_write("exit") - - return True + self.startup_config() + self.xr_conn.close() + return True + def startup_config(self): - """Load additional config provided by user.""" - + if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + self.logger.warning(f"User provided startup configuration file is not found") return - - self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} exists") - with open(STARTUP_CONFIG_FILE) as file: - config_lines = file.readlines() - config_lines = [line.rstrip() for line in config_lines] - self.logger.trace(f"Parsed startup config file {STARTUP_CONFIG_FILE}") - - self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") - - self.wait_write("configure") - # Apply lines from file - for line in config_lines: - self.wait_write(line) - # Commit and GTFO - self.wait_write("commit") - self.wait_write("exit") - - - def _wait_config(self, show_cmd, expect): - """Some configuration takes some time to "show up". - To make sure the device is really ready, wait here. - """ - self.logger.debug("waiting for {} to appear in {}".format(expect, show_cmd)) - wait_spins = 0 - # 10s * 90 = 900s = 15min timeout - while wait_spins < 90: - self.wait_write(show_cmd, wait=None) - _, match, data = self.tn.expect([expect.encode("UTF-8")], timeout=10) - self.logger.trace(data.decode("UTF-8")) - if match: - self.logger.debug("a wild {} has appeared!".format(expect)) - return True - wait_spins += 1 - self.logger.error("{} not found in {}".format(expect, show_cmd)) - return False - - + + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} found") + + startup_config = "" + + # append commit to end of file + with open(STARTUP_CONFIG_FILE, "r") as cfg: + startup_config = cfg.read() + + startup_config += "commit" + + res = self.xr_conn.send_config(startup_config, strip_prompt=False) + + if res.failed: + self.logger.error(f"Failed to load startup configuration.") + + return + class XRV(vrnetlab.VR): def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram): super(XRV, self).__init__(username, password) @@ -365,7 +304,7 @@ def install(self): parser.add_argument("--nics", type=int, default=128, help="Number of NICS") parser.add_argument('--install', action="store_true", help="Pre-install image") parser.add_argument( - "--vcpu", type=int, default=2, help="Number of cpu cores to use" + "--vcpu", type=int, default=4, help="Number of cpu cores to use" ) parser.add_argument( "--ram", type=int, default=16384, help="Number RAM to use in MB" diff --git a/cmglinux/docker/Dockerfile b/cmglinux/docker/Dockerfile index 103cb747..956ffeb5 100644 --- a/cmglinux/docker/Dockerfile +++ b/cmglinux/docker/Dockerfile @@ -19,8 +19,12 @@ RUN apt-get update -qy \ genisoimage \ python3-yaml \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/cmglinux/docker/launch.py b/cmglinux/docker/launch.py index a7a17d82..757135d8 100755 --- a/cmglinux/docker/launch.py +++ b/cmglinux/docker/launch.py @@ -202,7 +202,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login: "], 1) + (ridx, match, res) = self.expect([b"login: "], 1) # got am match and login if match and ridx == 0: self.logger.debug("matched, login: ") @@ -218,8 +218,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/common/vrnetlab.py b/common/vrnetlab.py index 26ef22fb..5df1c15d 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -8,10 +8,13 @@ import random import re import subprocess -import telnetlib import time +import sys from pathlib import Path +from scrapli import Driver +from scrapli.logging import enable_basic_logging + MAX_RETRIES = 60 @@ -79,7 +82,39 @@ def __init__( smp="1", min_dp_nics=0, ): - self.logger = logging.getLogger() + + # set fancy logging colours + logging.addLevelName( logging.INFO, "\033[1;92m%s\033[1;0m" % logging.getLevelName(logging.INFO)) + logging.addLevelName( logging.WARN, "\033[38;5;220m%s\033[1;0m" % logging.getLevelName(logging.WARN)) + logging.addLevelName( logging.DEBUG, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.DEBUG)) + logging.addLevelName( logging.ERROR, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.ERROR)) + logging.addLevelName( logging.CRITICAL, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.CRITICAL)) + + # set default logger for vrnetlab VM class + self.logger = logging.getLogger("vrnetlab") + self.logger.setLevel(logging.DEBUG) + + """ + Configure root logger to only be INFO level. + Scrapli uses root logger by default, and + will write all channel input as DEBUG logs. + """ + root_logger = logging.getLogger() + root_logger.setLevel(logging.INFO) + + # init scrapli + self.scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + num, + "auth_bypass": True, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 3600, + "timeout_transport": 3600, + "timeout_ops": 3600, + } + + self.tn = Driver(**self.scrapli_dev) # username / password to configure self.username = username @@ -91,7 +126,6 @@ def __init__( self.running = False self.spins = 0 self.p = None - self.tn = None self._ram = ram self._cpu = cpu @@ -132,7 +166,7 @@ def __init__( overlay_disk_image = ".".join(tokens) if not os.path.exists(overlay_disk_image): - self.logger.debug("Creating overlay disk image") + self.logger.info("Creating overlay disk image") run_command( [ "qemu-img", @@ -208,7 +242,7 @@ def start(self): if self.insuffucient_nics: cmd.extend(self.gen_dummy_nics()) - self.logger.debug("qemu cmd: {}".format(" ".join(cmd))) + self.logger.info("qemu cmd: {}".format(" ".join(cmd))) self.p = subprocess.Popen( " ".join(cmd), @@ -228,30 +262,12 @@ def start(self): for i in range(1, MAX_RETRIES + 1): try: - self.qm = telnetlib.Telnet("127.0.0.1", 4000 + self.num) - break - except: - self.logger.info( - "Unable to connect to qemu monitor (port {}), retrying in a second (attempt {})".format( - 4000 + self.num, i - ) - ) - time.sleep(1) - if i == MAX_RETRIES: - raise QemuBroken( - "Unable to connect to qemu monitor on port {}".format( - 4000 + self.num - ) - ) - - for i in range(1, MAX_RETRIES + 1): - try: - self.tn = telnetlib.Telnet("127.0.0.1", 5000 + self.num) + self.tn.open() break - except: - self.logger.info( - "Unable to connect to qemu monitor (port {}), retrying in a second (attempt {})".format( - 5000 + self.num, i + except Exception as e: + self.logger.debug( + "Unable to connect to qemu monitor (port {}), retrying in a second (attempt {})\nError: {}".format( + 5000 + self.num, i, e ) ) time.sleep(1) @@ -322,7 +338,7 @@ def gen_mgmt(self): return res def nic_provision_delay(self) -> None: - self.logger.debug( + self.logger.info( f"number of provisioned data plane interfaces is {self.num_provisioned_nics}" ) @@ -333,7 +349,7 @@ def nic_provision_delay(self) -> None: self.insuffucient_nics = True return - self.logger.debug("waiting for provisioned interfaces to appear...") + self.logger.info("waiting for provisioned interfaces to appear...") # start_eth means eth index for VM # particularly for multiple slot LC @@ -356,10 +372,10 @@ def nic_provision_delay(self) -> None: if nics: self.highest_provisioned_nic_num = max(nics) - self.logger.debug( + self.logger.info( f"highest allocated interface id determined to be: {self.highest_provisioned_nic_num}..." ) - self.logger.debug("interfaces provisioned, continuing...") + self.logger.info("interfaces provisioned, continuing...") break time.sleep(5) @@ -372,7 +388,7 @@ def gen_dummy_nics(self): # calculate required num of nics to generate nics = self.min_nics - self.num_provisioned_nics - self.logger.debug(f"Insuffucient NICs defined. Generating {nics} dummy nics") + self.logger.warning(f"Insuffucient NICs defined. Generating {nics} dummy nics") res = [] @@ -496,55 +512,119 @@ def restart(self): self.stop() self.start() - def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=False): - """Wait for something on the serial port and then send command - - Defaults to using self.tn as connection but this can be overridden - by passing a telnetlib.Telnet object in the con argument. + def work(self): + self.check_qemu() + if not self.running: + try: + self.bootstrap_spin() + except EOFError: + self.logger.error("Telnet session was disconnected, restarting") + self.restart() + + def read_until(self, match_str, timeout=None): + """Read until a given string is encountered or until timeout. + + When no match is found, return whatever is available instead, + possibly the empty string. + + Arguments: + - match_str: string to match on (string) + - timeout: timeout in seconds, defaults to None (float) """ - con_name = "custom con" - if con is None: - con = self.tn + buf = b"" + + if timeout: + t_end = time.time() + timeout + + while True: + current_buf = self.tn.channel.read() + buf += current_buf + + match = re.search(match_str, current_buf.decode()) + + # for reliability purposes, doublecheck the entire buffer + # maybe the current buffer only has partial output + if match is None: + match = re.search(match_str, buf.decode()) + + self.print(current_buf) + + if match: + break + if timeout and time.time() > t_end: + break + + return buf - if con == self.tn: - con_name = "serial console" - if con == self.qm: - con_name = "qemu monitor" + def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=None, timeout=None): + """ + Wait for something on the serial port and then send command + + Arguments are: + - cmd: command to send (string) + - wait: prompt to wait for before sending command, defaults to # (string) + - timeout: if prompt is not found after x amounts of seconds, send command anyways. Defaults to None (float) + """ + if con is not None: + raise ValueError("wait_write: con argument is no longer supported. Please raise GitHub issue on hellt/vrnetlab, or report in containerlab discord.") + if clean_buffer is not None: + raise ValueError("wait_write: clean_buffer argument is no longer supported. Please raise GitHub issue on hellt/vrnetlab, or report in containerlab discord.") if wait: # use class default wait pattern if none was explicitly specified if wait == "__defaultpattern__": wait = self.wait_pattern - self.logger.trace(f"waiting for '{wait}' on {con_name}") - res = con.read_until(wait.encode()) - - cleaned_buf = ( - (con.read_very_eager()) if clean_buffer else None - ) # Clear any remaining characters in buffer - - self.logger.trace(f"read from {con_name}: '{res.decode()}'") - # log the cleaned buffer if it's not empty - if cleaned_buf: - self.logger.trace(f"cleaned buffer: '{cleaned_buf.decode()}'") + + self.logger.info(f"waiting for '{wait}' on console.") + + self.read_until(wait, timeout) + + time.sleep(0.1) # don't write to the console too fast + + self.logger.info(f"writing to console: '{cmd}'") + self.tn.channel.write(f"{cmd}\r") + + def expect(self, regex_list, timeout=None): + """Wait for something on the serial port. + + Takes list of strings and timeout as arguments. + + Returns tuple of: + - index of matched object from regex. + - match object. + - buffer of cosole read until match, or function exit. + """ + buf = self.tn.channel.read() + + if timeout: + t_end = time.time() + timeout + + for i, obj in enumerate(regex_list): + match = re.search(obj.decode(), buf.decode()) + if match: + return i, match, buf + if timeout and time.time() > t_end: + break + + return -1, None, buf - self.logger.debug(f"writing to {con_name}: '{cmd}'") - con.write("{}\r".format(cmd).encode()) + def print(self, bytes): + """ + Quick and dirty way to write to stdout (docker logs) instead of + using the python logger which poorly formats the output. + + Useful for printing console to docker logs + """ + sys.stdout.buffer.write(bytes) + sys.stdout.buffer.flush() - def work(self): - self.check_qemu() - if not self.running: - try: - self.bootstrap_spin() - except EOFError: - self.logger.error("Telnet session was disconnected, restarting") - self.restart() def check_qemu(self): """Check health of qemu. This is mostly just seeing if there's error output on STDOUT from qemu which means we restart it. """ if self.p is None: - self.logger.debug("VM not started; starting!") + self.logger.info("VM not started; starting!") self.start() # check for output @@ -640,8 +720,8 @@ def update_health(self, exit_status, message): def start(self): """Start the virtual router""" - self.logger.debug("Starting vrnetlab %s" % self.__class__.__name__) - self.logger.debug("VMs: %s" % self.vms) + self.logger.info("Starting vrnetlab %s" % self.__class__.__name__) + self.logger.info("VMs: %s" % self.vms) started = False while True: diff --git a/dell_sonic/docker/Dockerfile b/dell_sonic/docker/Dockerfile index 44c51ee9..384d3bb6 100644 --- a/dell_sonic/docker/Dockerfile +++ b/dell_sonic/docker/Dockerfile @@ -12,8 +12,12 @@ RUN apt-get update -qy \ socat \ ssh \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/dell_sonic/docker/launch.py b/dell_sonic/docker/launch.py index a5151e48..1211e2d2 100755 --- a/dell_sonic/docker/launch.py +++ b/dell_sonic/docker/launch.py @@ -65,7 +65,7 @@ def bootstrap_spin(self): self.start() return - ridx, match, res = self.tn.expect([b"login:"], 1) + ridx, match, res = self.expect([b"login:"], 1) if match and ridx == 0: # login self.logger.info("VM started") @@ -90,8 +90,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/fortigate/docker/Dockerfile b/fortigate/docker/Dockerfile index e04fbecf..3bbe361e 100644 --- a/fortigate/docker/Dockerfile +++ b/fortigate/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim +FROM public.ecr.aws/docker/library/debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -qy \ @@ -21,8 +21,12 @@ RUN apt-get update -qy \ dos2unix \ vim \ curl \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE diff --git a/fortigate/docker/launch.py b/fortigate/docker/launch.py index f420e2d6..6cd53d76 100755 --- a/fortigate/docker/launch.py +++ b/fortigate/docker/launch.py @@ -86,7 +86,7 @@ def bootstrap_spin(self): self.spins = 0 return - (ridx, match, res) = self.tn.expect([b"login:", b"FortiGate-VM64-KVM #"], 1) + (ridx, match, res) = self.expect([b"login:", b"FortiGate-VM64-KVM #"], 1) if match: # got a match! if ridx == 0: # matched login prompt, so should login self.logger.debug("ridx == 0") @@ -115,8 +115,8 @@ def bootstrap_spin(self): else: # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace(f"OUTPUT FORTIGATE: {res.decode()}") + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -131,8 +131,8 @@ def _wait_reset(self): self.logger.debug("waiting for reset") wait_spins = 0 while wait_spins < 90: - _, match, data = self.tn.expect([b"login: "], timeout=10) - self.logger.trace(data.decode("UTF-8")) + _, match, data = self.expect([b"login: "], timeout=10) + self.print(data) if match: self.logger.debug("reset finished") return True diff --git a/freebsd/docker/Dockerfile b/freebsd/docker/Dockerfile index 75bed9cb..1f66471d 100644 --- a/freebsd/docker/Dockerfile +++ b/freebsd/docker/Dockerfile @@ -20,8 +20,12 @@ RUN apt-get update -qy \ telnet \ cloud-utils \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/freebsd/docker/launch.py b/freebsd/docker/launch.py index 6cfd180d..10b436fb 100755 --- a/freebsd/docker/launch.py +++ b/freebsd/docker/launch.py @@ -129,7 +129,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login: "], 1) + (ridx, match, res) = self.expect([b"login: "], 1) if match: # got a match! if ridx == 0: # login @@ -148,8 +148,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/ftdv/Makefile b/ftdv/Makefile deleted file mode 100644 index db28850c..00000000 --- a/ftdv/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -VENDOR=Cisco -NAME=FTDv -IMAGE_FORMAT=qcow2 -IMAGE_GLOB=*.qcow2 - -# match versions like: -# Cisco_Secure_Firewall_Threat_Defense_Virtual-7.2.5-208.qcow2 -VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+-\([0-9]\+\.[0-9]\+\.[0-9]\+\)-[0-9]\+.*/\1/') - --include ../makefile-sanity.include --include ../makefile.include --include ../makefile-install.include diff --git a/ftosv/docker/Dockerfile b/ftosv/docker/Dockerfile index 7e407275..d2b6b6ad 100644 --- a/ftosv/docker/Dockerfile +++ b/ftosv/docker/Dockerfile @@ -18,7 +18,11 @@ RUN apt-get update -qy \ inetutils-ping \ dnsutils \ telnet \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE* / diff --git a/ftosv/docker/launch.py b/ftosv/docker/launch.py index 86150dda..68ca2a09 100755 --- a/ftosv/docker/launch.py +++ b/ftosv/docker/launch.py @@ -102,7 +102,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login:"], 1) + (ridx, match, res) = self.expect([b"login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("matched login prompt") @@ -131,8 +131,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/huawei_vrp/docker/Dockerfile b/huawei_vrp/docker/Dockerfile index 210423ca..45577335 100644 --- a/huawei_vrp/docker/Dockerfile +++ b/huawei_vrp/docker/Dockerfile @@ -10,8 +10,12 @@ RUN apt-get update -qy \ qemu-kvm \ qemu-utils \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/huawei_vrp/docker/launch.py b/huawei_vrp/docker/launch.py index d71f9242..46971b9e 100755 --- a/huawei_vrp/docker/launch.py +++ b/huawei_vrp/docker/launch.py @@ -73,7 +73,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b""], 1) + (ridx, match, res) = self.expect([b""], 1) if match and ridx == 0: # got a match! # run main config! @@ -94,8 +94,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -118,7 +118,7 @@ def bootstrap_mgmt_interface(self): # Error: The system is busy in building configuration. Please wait for a moment... while True: self.wait_write(cmd="clear configuration this", wait=None) - (idx, match, res) = self.tn.expect([rb"Error"], 1) + (idx, match, res) = self.expect([rb"Error"], 1) if match and idx == 0: time.sleep(5) else: diff --git a/vjunosevolved/Makefile b/juniper/vjunosevolved/Makefile similarity index 76% rename from vjunosevolved/Makefile rename to juniper/vjunosevolved/Makefile index a3bffb6a..6682f873 100644 --- a/vjunosevolved/Makefile +++ b/juniper/vjunosevolved/Makefile @@ -2,6 +2,7 @@ VENDOR=Juniper NAME=vJunosEvolved IMAGE_FORMAT=qcow IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # match versions like: # vJunosEvolved-23.1R1.8.qcow2 @@ -11,5 +12,5 @@ IMAGE_GLOB=*.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/vjunosevolved-//i' | sed -e 's/.qcow2//i') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/vjunosevolved/README.md b/juniper/vjunosevolved/README.md similarity index 100% rename from vjunosevolved/README.md rename to juniper/vjunosevolved/README.md diff --git a/vjunosevolved/docker/Dockerfile b/juniper/vjunosevolved/docker/Dockerfile similarity index 82% rename from vjunosevolved/docker/Dockerfile rename to juniper/vjunosevolved/docker/Dockerfile index 173e4d81..37b4f96d 100644 --- a/vjunosevolved/docker/Dockerfile +++ b/juniper/vjunosevolved/docker/Dockerfile @@ -17,7 +17,11 @@ RUN apt-get update -qy \ inetutils-ping \ dnsutils \ telnet \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE* / diff --git a/vjunosevolved/docker/init.conf b/juniper/vjunosevolved/docker/init.conf similarity index 100% rename from vjunosevolved/docker/init.conf rename to juniper/vjunosevolved/docker/init.conf diff --git a/vjunosevolved/docker/launch.py b/juniper/vjunosevolved/docker/launch.py similarity index 98% rename from vjunosevolved/docker/launch.py rename to juniper/vjunosevolved/docker/launch.py index a2fbfd30..62ba158a 100755 --- a/vjunosevolved/docker/launch.py +++ b/juniper/vjunosevolved/docker/launch.py @@ -141,7 +141,7 @@ def bootstrap_spin(self): # lets wait for the OS/platform log to determine if VM is booted, # login prompt can get lost in boot logs - (ridx, match, res) = self.tn.expect([b"Juniper"], 1) + (ridx, match, res) = self.expect([b"Juniper"], 1) if match: # got a match! if ridx == 0: # login self.logger.info("VM started") @@ -164,8 +164,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vjunosevolved/docker/make-config.sh b/juniper/vjunosevolved/docker/make-config.sh similarity index 100% rename from vjunosevolved/docker/make-config.sh rename to juniper/vjunosevolved/docker/make-config.sh diff --git a/vjunosrouter/Makefile b/juniper/vjunosrouter/Makefile similarity index 71% rename from vjunosrouter/Makefile rename to juniper/vjunosrouter/Makefile index dcf94171..06f8e9f1 100644 --- a/vjunosrouter/Makefile +++ b/juniper/vjunosrouter/Makefile @@ -2,11 +2,12 @@ VENDOR=Juniper NAME=vJunos-router IMAGE_FORMAT=qcow IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # match versions like: # vJunos-router-23.2R1.15.qcow2 # ... VERSION=$(shell echo $(IMAGE) | sed -e 's/vJunos-router-//i' | sed -e 's/.qcow2//i') --include ../makefile-sanity.include --include ../makefile.include \ No newline at end of file +-include ../../makefile-sanity.include +-include ../../makefile.include \ No newline at end of file diff --git a/vjunosrouter/README.md b/juniper/vjunosrouter/README.md similarity index 100% rename from vjunosrouter/README.md rename to juniper/vjunosrouter/README.md diff --git a/vjunosswitch/docker/Dockerfile b/juniper/vjunosrouter/docker/Dockerfile similarity index 81% rename from vjunosswitch/docker/Dockerfile rename to juniper/vjunosrouter/docker/Dockerfile index dc246787..a1afc75c 100644 --- a/vjunosswitch/docker/Dockerfile +++ b/juniper/vjunosrouter/docker/Dockerfile @@ -16,7 +16,11 @@ RUN apt-get update -qy \ inetutils-ping \ dnsutils \ telnet \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE* / diff --git a/vjunosrouter/docker/init.conf b/juniper/vjunosrouter/docker/init.conf similarity index 100% rename from vjunosrouter/docker/init.conf rename to juniper/vjunosrouter/docker/init.conf diff --git a/vjunosrouter/docker/launch.py b/juniper/vjunosrouter/docker/launch.py similarity index 97% rename from vjunosrouter/docker/launch.py rename to juniper/vjunosrouter/docker/launch.py index 6c07034e..f1896d40 100755 --- a/vjunosrouter/docker/launch.py +++ b/juniper/vjunosrouter/docker/launch.py @@ -126,7 +126,7 @@ def bootstrap_spin(self): # lets wait for the OS/platform log to determine if VM is booted, # login prompt can get lost in boot logs - (ridx, match, res) = self.tn.expect([b"FreeBSD/amd64"], 1) + (ridx, match, res) = self.expect([b"FreeBSD/amd64"], 1) if match: # got a match! if ridx == 0: # login self.logger.info("VM started") @@ -149,8 +149,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vjunosrouter/docker/make-config.sh b/juniper/vjunosrouter/docker/make-config.sh similarity index 100% rename from vjunosrouter/docker/make-config.sh rename to juniper/vjunosrouter/docker/make-config.sh diff --git a/vjunosswitch/Makefile b/juniper/vjunosswitch/Makefile similarity index 76% rename from vjunosswitch/Makefile rename to juniper/vjunosswitch/Makefile index 6cf35e7e..ed79054f 100644 --- a/vjunosswitch/Makefile +++ b/juniper/vjunosswitch/Makefile @@ -2,6 +2,7 @@ VENDOR=Juniper NAME=vJunos-switch IMAGE_FORMAT=qcow IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # match versions like: # vJunos-switch-23.1R1.8.qcow2 @@ -11,5 +12,5 @@ IMAGE_GLOB=*.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/vjunos-switch-//i' | sed -e 's/.qcow2//i') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/vjunosswitch/README.md b/juniper/vjunosswitch/README.md similarity index 100% rename from vjunosswitch/README.md rename to juniper/vjunosswitch/README.md diff --git a/vjunosrouter/docker/Dockerfile b/juniper/vjunosswitch/docker/Dockerfile similarity index 81% rename from vjunosrouter/docker/Dockerfile rename to juniper/vjunosswitch/docker/Dockerfile index dc246787..a1afc75c 100644 --- a/vjunosrouter/docker/Dockerfile +++ b/juniper/vjunosswitch/docker/Dockerfile @@ -16,7 +16,11 @@ RUN apt-get update -qy \ inetutils-ping \ dnsutils \ telnet \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE* / diff --git a/vjunosswitch/docker/init.conf b/juniper/vjunosswitch/docker/init.conf similarity index 100% rename from vjunosswitch/docker/init.conf rename to juniper/vjunosswitch/docker/init.conf diff --git a/vjunosswitch/docker/launch.py b/juniper/vjunosswitch/docker/launch.py similarity index 97% rename from vjunosswitch/docker/launch.py rename to juniper/vjunosswitch/docker/launch.py index 7f23d13d..47b1d578 100755 --- a/vjunosswitch/docker/launch.py +++ b/juniper/vjunosswitch/docker/launch.py @@ -126,7 +126,7 @@ def bootstrap_spin(self): # lets wait for the OS/platform log to determine if VM is booted, # login prompt can get lost in boot logs - (ridx, match, res) = self.tn.expect([b"FreeBSD/amd64"], 1) + (ridx, match, res) = self.expect([b"FreeBSD/amd64"], 1) if match: # got a match! if ridx == 0: # login self.logger.info("VM started") @@ -149,8 +149,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vjunosswitch/docker/make-config.sh b/juniper/vjunosswitch/docker/make-config.sh similarity index 100% rename from vjunosswitch/docker/make-config.sh rename to juniper/vjunosswitch/docker/make-config.sh diff --git a/vmx/Makefile b/juniper/vmx/Makefile similarity index 79% rename from vmx/Makefile rename to juniper/vmx/Makefile index bc12ce99..94ec859e 100644 --- a/vmx/Makefile +++ b/juniper/vmx/Makefile @@ -2,6 +2,7 @@ VENDOR=Juniper NAME=vMX IMAGE_FORMAT=tgz IMAGE_GLOB=*.tgz +VENDOR_SUBDIR=1 # match versions like: # vmx-14.1R6.4.tgz @@ -14,9 +15,9 @@ IMAGE_GLOB=*.tgz # vmx-bundle-17.1R1-S1.tgz VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9][0-9]\.[0-9][A-Z][0-9]\+\(\.[0-9]\+\|-[SD][0-9]\+\(\.[0-9]\+\)\?\)\)[^0-9].*$$/\1/') --include ../makefile-sanity.include --include ../makefile.include --include ../makefile-install.include +-include ../../makefile-sanity.include +-include ../../makefile.include +-include ../../makefile-install.include docker-build-image-copy: ./vmx-extract.sh $(IMAGE) diff --git a/vmx/README.md b/juniper/vmx/README.md similarity index 100% rename from vmx/README.md rename to juniper/vmx/README.md diff --git a/vmx/docker/Dockerfile b/juniper/vmx/docker/Dockerfile similarity index 73% rename from vmx/docker/Dockerfile rename to juniper/vmx/docker/Dockerfile index ee64b618..ef4a01e3 100644 --- a/vmx/docker/Dockerfile +++ b/juniper/vmx/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL org.opencontainers.image.authors="roman@dodin.dev" ENV DEBIAN_FRONTEND=noninteractive @@ -14,8 +14,12 @@ RUN apt-get update -qy \ tcpdump \ procps \ openvswitch-switch \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + COPY vmx /vmx COPY *.py / diff --git a/vmx/docker/launch.py b/juniper/vmx/docker/launch.py similarity index 97% rename from vmx/docker/launch.py rename to juniper/vmx/docker/launch.py index 72f72e87..60214476 100755 --- a/vmx/docker/launch.py +++ b/juniper/vmx/docker/launch.py @@ -116,7 +116,7 @@ def bootstrap_spin(self): self.spins = 0 return - (ridx, match, res) = self.tn.expect([b"login:", b"root@(%|:~ #)"], 1) + (ridx, match, res) = self.expect([b"login:", b"root@(%|:~ #)"], 1) if match: # got a match! if ridx == 0: # matched login prompt, so should login self.logger.info("matched login prompt") @@ -155,8 +155,8 @@ def bootstrap_spin(self): else: # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT VCP: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -237,7 +237,7 @@ def wait_write(self, cmd, wait="#", timeout=None): if wait: self.logger.trace("Waiting for {} before writing {}".format(wait, cmd)) while True: - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [wait.encode(), b"Retry connection attempts"], timeout=timeout ) if match: @@ -245,7 +245,7 @@ def wait_write(self, cmd, wait="#", timeout=None): break if ridx == 1: self.tn.write("yes\r".encode()) - self.logger.trace("Read: %s" % res.decode()) + self.print(res) self.logger.debug("writing to serial console: %s" % cmd) self.tn.write("{}\r".format(cmd).encode()) @@ -308,7 +308,7 @@ def start(self): vrnetlab.run_command(["ip", "link", "set", "vfpc-int", "up"]) def bootstrap_spin(self): - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ b"localhost login", b"qemux86-64 login", @@ -423,9 +423,9 @@ def install(self): break try: - (ridx, match, res) = vcp.tn.expect([b"Powering system off"], 1) - if res != b"": - self.logger.trace("OUTPUT VCP: %s" % res.decode()) + (ridx, match, res) = vcp.expect([b"Powering system off"], 1) + if res != "": + self.print(res) except Exception as exc: # assume it's dead self.logger.info( diff --git a/vmx/vmx-extract.sh b/juniper/vmx/vmx-extract.sh similarity index 100% rename from vmx/vmx-extract.sh rename to juniper/vmx/vmx-extract.sh diff --git a/vqfx/Makefile b/juniper/vqfx/Makefile similarity index 94% rename from vqfx/Makefile rename to juniper/vqfx/Makefile index 1b185055..0c02e179 100644 --- a/vqfx/Makefile +++ b/juniper/vqfx/Makefile @@ -2,6 +2,7 @@ VENDOR=Juniper NAME=vQFX IMAGE_FORMAT=qcow2 IMAGE_GLOB=*.qcow2 +VENDOR_SUBDIR=1 # New vqfx are named: vqfx-19.4R1.10-re-qemu.qcow2 VERSION=$(shell echo $(IMAGE) | sed -e 's/^vqfx-//'|sed -e 's/-re-qemu.qcow2//' ) @@ -11,8 +12,8 @@ VMDK_VERSION:=$(shell ls *-re-*.vmdk | sed -re 's/vqfx10k-re-([^;]*)\.vmdk.*$$/\ PFE_BASE_VERSION=$(shell echo $VERSION | sed -e s'/.*//') PFE_IMAGE=$(shell ls vqfx-$(PFE_BASE_VERSION)*-pfe-qemu.qcow*) --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include format-legacy-images: @if ls *.vmdk; then echo "VMDKs exist, converting them to qcow format"; qemu-img convert -f vmdk -O qcow2 *-re-*.vmdk vqfx-$(VMDK_VERSION)-re-qemu.qcow2 && qemu-img convert -f vmdk -O qcow *-pfe-*.vmdk vqfx-$(VMDK_VERSION)-pfe-qemu.qcow; echo "VMDKs have been converted"; fi @@ -30,6 +31,6 @@ docker-build-common: docker-clean-build docker-pre-build @if [ -z "$$IMAGE" ]; then echo "ERROR: No IMAGE specified"; exit 1; fi @if [ "$(IMAGE)" = "$(VERSION)" ]; then echo "ERROR: Incorrect version string ($(IMAGE)). The regexp for extracting version information is likely incorrect, check the regexp in the Makefile or open an issue at https://github.com/hellt/vrnetlab/issues/new including the image file name you are using."; exit 1; fi @echo "Building docker image using $(IMAGE) as $(REGISTRY)vr-$(VR_NAME):$(VERSION)" - cp ../common/* docker/ + cp ../../common/* docker/ $(MAKE) IMAGE=$$IMAGE docker-build-image-copy (cd docker; docker build --build-arg http_proxy=$(http_proxy) --build-arg https_proxy=$(https_proxy) --build-arg RE_IMAGE=$(IMAGE) --build-arg PFE_IMAGE=$(PFE_IMAGE) -t $(REGISTRY)vr-$(VR_NAME):$(VERSION) .) diff --git a/vqfx/README.md b/juniper/vqfx/README.md similarity index 100% rename from vqfx/README.md rename to juniper/vqfx/README.md diff --git a/vqfx/docker/Dockerfile b/juniper/vqfx/docker/Dockerfile similarity index 73% rename from vqfx/docker/Dockerfile rename to juniper/vqfx/docker/Dockerfile index 49c233de..b50e5229 100644 --- a/vqfx/docker/Dockerfile +++ b/juniper/vqfx/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -qy \ @@ -11,8 +11,12 @@ RUN apt-get update -qy \ qemu-kvm \ procps \ tcpdump \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG RE_IMAGE ARG PFE_IMAGE diff --git a/vqfx/docker/launch.py b/juniper/vqfx/docker/launch.py similarity index 97% rename from vqfx/docker/launch.py rename to juniper/vqfx/docker/launch.py index 2436190e..58f1b726 100755 --- a/vqfx/docker/launch.py +++ b/juniper/vqfx/docker/launch.py @@ -99,7 +99,7 @@ def bootstrap_spin(self): if self._version["major"] < 20: logged_in_prompt = b"root@vqfx-re:RE:0%" - (ridx, match, res) = self.tn.expect([b"login:", logged_in_prompt], 1) + (ridx, match, res) = self.expect([b"login:", logged_in_prompt], 1) if match: # got a match! if ridx == 0: # matched login prompt, so should login self.logger.info("matched login prompt") @@ -122,8 +122,8 @@ def bootstrap_spin(self): else: # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT VCP: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -185,7 +185,7 @@ def wait_write(self, cmd, wait="#", timeout=None): if wait: self.logger.trace("Waiting for %s" % wait) while True: - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [wait.encode(), b"Retry connection attempts"], timeout=timeout ) if match: @@ -193,7 +193,7 @@ def wait_write(self, cmd, wait="#", timeout=None): break if ridx == 1: self.tn.write("yes\r".encode()) - self.logger.trace("Read: %s" % res.decode()) + self.print(res) self.logger.debug("writing to serial console: %s" % cmd) self.tn.write("{}\r".format(cmd).encode()) diff --git a/vsrx/Makefile b/juniper/vsrx/Makefile similarity index 72% rename from vsrx/Makefile rename to juniper/vsrx/Makefile index 3f2978b8..e343228b 100644 --- a/vsrx/Makefile +++ b/juniper/vsrx/Makefile @@ -3,10 +3,11 @@ NAME=vSRX IMAGE_FORMAT=qcow IMAGE_GLOB=*.qcow2 IMAGE=ffp-12.1X47-D15.4-packetmode.qcow2 +VENDOR_SUBDIR=1 # match versions like: # 12.1X47-D15.4 VERSION=$(shell echo $(IMAGE) | sed -e 's/junos-vsrx3-x86-64-//' | sed -e 's/.qcow2//') --include ../makefile-sanity.include --include ../makefile.include +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/vsrx/README.md b/juniper/vsrx/README.md similarity index 100% rename from vsrx/README.md rename to juniper/vsrx/README.md diff --git a/vsrx/docker/Dockerfile b/juniper/vsrx/docker/Dockerfile similarity index 83% rename from vsrx/docker/Dockerfile rename to juniper/vsrx/docker/Dockerfile index bcb21dd7..958ed27f 100644 --- a/vsrx/docker/Dockerfile +++ b/juniper/vsrx/docker/Dockerfile @@ -12,8 +12,12 @@ RUN apt-get update -qy \ qemu-kvm \ qemu-utils \ genisoimage \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / diff --git a/vsrx/docker/init.conf b/juniper/vsrx/docker/init.conf similarity index 100% rename from vsrx/docker/init.conf rename to juniper/vsrx/docker/init.conf diff --git a/vsrx/docker/launch.py b/juniper/vsrx/docker/launch.py similarity index 97% rename from vsrx/docker/launch.py rename to juniper/vsrx/docker/launch.py index e6f886dc..86156c7b 100755 --- a/vsrx/docker/launch.py +++ b/juniper/vsrx/docker/launch.py @@ -110,7 +110,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login:"], 1) + (ridx, match, res) = self.expect([b"login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.info("VM started") @@ -125,8 +125,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vsrx/docker/make-config-iso.sh b/juniper/vsrx/docker/make-config-iso.sh similarity index 100% rename from vsrx/docker/make-config-iso.sh rename to juniper/vsrx/docker/make-config-iso.sh diff --git a/makefile.include b/makefile.include index 574b7360..032cb9cf 100644 --- a/makefile.include +++ b/makefile.include @@ -31,6 +31,9 @@ docker-build-common: docker-clean-build docker-pre-build @echo "Building docker image using $(IMAGE) as $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)" ifeq ($(NOT_VM_IMAGE), 1) echo "ok" +else ifeq($(VENDOR_SUBDIR), 1) + echo "Copying to vendor subdirectory" + cp ../../common/* docker/ else cp ../common/* docker/ endif diff --git a/ocnos/docker/Dockerfile b/ocnos/docker/Dockerfile index 572be609..a13cf7e7 100644 --- a/ocnos/docker/Dockerfile +++ b/ocnos/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -qy \ @@ -18,8 +18,12 @@ RUN apt-get update -qy \ iptables \ nftables \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/ocnos/docker/launch.py b/ocnos/docker/launch.py index d3236f1b..3b083d53 100755 --- a/ocnos/docker/launch.py +++ b/ocnos/docker/launch.py @@ -65,7 +65,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"OcNOS login:"], 1) + (ridx, match, res) = self.expect([b"OcNOS login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("matched login prompt") @@ -87,8 +87,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/openbsd/docker/Dockerfile b/openbsd/docker/Dockerfile index 75bed9cb..ceeef1ef 100644 --- a/openbsd/docker/Dockerfile +++ b/openbsd/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim +FROM public.ecr.aws/docker/library/debian:bookworm-slim ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G @@ -20,8 +20,12 @@ RUN apt-get update -qy \ telnet \ cloud-utils \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/openbsd/docker/launch.py b/openbsd/docker/launch.py index ffade909..3b5fa3a8 100755 --- a/openbsd/docker/launch.py +++ b/openbsd/docker/launch.py @@ -129,7 +129,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login: "], 1) + (ridx, match, res) = self.expect([b"login: "], 1) if match: # got a match! if ridx == 0: # login @@ -148,8 +148,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/openwrt/docker/Dockerfile b/openwrt/docker/Dockerfile index 14d5bbc7..27f15b97 100644 --- a/openwrt/docker/Dockerfile +++ b/openwrt/docker/Dockerfile @@ -1,4 +1,5 @@ FROM alpine:3.18 +# FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Andreas Cymbal takalele@konnex.me RUN apk add --no-cache bash \ @@ -12,10 +13,14 @@ RUN apk add --no-cache bash \ py3-click \ nano \ vim \ + git \ py3-pip && ln -sf python3 /usr/bin/python + RUN pip3 install --no-cache --upgrade pip setuptools RUN pip install IPy +RUN pip install git+https://github.com/carlmontanari/scrapli +# might need to add -- ARG IMAGE COPY $IMAGE* / diff --git a/openwrt/docker/launch.py b/openwrt/docker/launch.py index 6f8a8a46..d03d3036 100755 --- a/openwrt/docker/launch.py +++ b/openwrt/docker/launch.py @@ -51,7 +51,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"br-lan"], 1) + (ridx, match, res) = self.expect([b"br-lan"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("VM started") @@ -68,8 +68,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b'': - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/pan/docker/Dockerfile b/pan/docker/Dockerfile index 8ee0108a..3c698910 100644 --- a/pan/docker/Dockerfile +++ b/pan/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL maintainer="Kristian Larsson " LABEL maintainer="Roman Dodin " @@ -20,8 +20,12 @@ RUN apt-get update -qy \ iptables \ nftables \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/pan/docker/launch.py b/pan/docker/launch.py index 8237dcf5..a570ba61 100755 --- a/pan/docker/launch.py +++ b/pan/docker/launch.py @@ -69,7 +69,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect( + (ridx, match, res) = self.expect( [ b"Login incorrect", b"vm login:", @@ -123,8 +123,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -145,7 +145,7 @@ def bootstrap_config(self): # whatever reason self.wait_write("", None) while True: - (ridx, match, res) = self.tn.expect([b"FIN", b"PEND"], 1) + (ridx, match, res) = self.expect([b"FIN", b"PEND"], 1) if match: if ridx == 0: # login self.logger.debug("auto commit complete, begin configuration") diff --git a/routeros/docker/Dockerfile b/routeros/docker/Dockerfile index 72da4f08..861421ea 100644 --- a/routeros/docker/Dockerfile +++ b/routeros/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL org.opencontainers.image.authors="roman@dodin.dev" ARG DEBIAN_FRONTEND=noninteractive @@ -19,8 +19,12 @@ RUN apt-get update -qy \ nftables \ telnet \ ftp \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/routeros/docker/launch.py b/routeros/docker/launch.py index 773c8f2d..e209fb32 100755 --- a/routeros/docker/launch.py +++ b/routeros/docker/launch.py @@ -100,7 +100,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"MikroTik Login", b"RouterOS Login"], 1) + (ridx, match, res) = self.expect([b"MikroTik Login", b"RouterOS Login"], 1) if match: # got a match! if ridx in (0, 1): # login self.logger.debug("VM started") @@ -121,7 +121,7 @@ def bootstrap_spin(self): # ROSv7 requires changing the password right away. ROSv6 does not require changing the password - (ridx2, match2, _) = self.tn.expect([b"new password>"], 1) + (ridx2, match2, _) = self.expect([b"new password>"], 1) if match2 and ridx2 == 0: # got a match! login self.logger.debug("ROSv7 detected, setting admin password") self.wait_write(f"{self.password}", wait="new password>") @@ -147,8 +147,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/sonic/docker/Dockerfile b/sonic/docker/Dockerfile index 44c51ee9..384d3bb6 100644 --- a/sonic/docker/Dockerfile +++ b/sonic/docker/Dockerfile @@ -12,8 +12,12 @@ RUN apt-get update -qy \ socat \ ssh \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/sonic/docker/launch.py b/sonic/docker/launch.py index 778ad867..812976f9 100755 --- a/sonic/docker/launch.py +++ b/sonic/docker/launch.py @@ -65,7 +65,7 @@ def bootstrap_spin(self): self.start() return - ridx, match, res = self.tn.expect([b"login:"], 1) + ridx, match, res = self.expect([b"login:"], 1) if match and ridx == 0: # login self.logger.info("VM started") @@ -90,8 +90,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/sros/docker/Dockerfile b/sros/docker/Dockerfile index c18f8741..a535f18c 100644 --- a/sros/docker/Dockerfile +++ b/sros/docker/Dockerfile @@ -18,8 +18,12 @@ RUN apt-get update -qy \ iptables \ nftables \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/sros/docker/launch.py b/sros/docker/launch.py index cfa92712..39979c92 100755 --- a/sros/docker/launch.py +++ b/sros/docker/launch.py @@ -971,7 +971,7 @@ def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=True def bootstrap_spin(self): """This function should be called periodically to do work.""" - (ridx, match, res) = self.tn.expect([b"Login:", b"^[^ ]+#"], 1) + (ridx, match, res) = self.expect([b"Login:", b"^[^ ]+#"], 1) if match: # got a match! if ridx == 0: # matched login prompt, so should login self.logger.debug("matched login prompt") @@ -989,8 +989,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/ubuntu/docker/Dockerfile b/ubuntu/docker/Dockerfile index 417aa776..df408463 100644 --- a/ubuntu/docker/Dockerfile +++ b/ubuntu/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim +FROM public.ecr.aws/docker/library/debian:bookworm-slim ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G @@ -18,8 +18,12 @@ RUN apt-get update -qy \ telnet \ cloud-utils \ sshpass \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/ubuntu/docker/launch.py b/ubuntu/docker/launch.py index b9778c8c..d08dff3c 100755 --- a/ubuntu/docker/launch.py +++ b/ubuntu/docker/launch.py @@ -118,7 +118,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login: "], 1) + (ridx, match, res) = self.expect([b"login: "], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("matched, login: ") @@ -134,8 +134,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/veos/docker/Dockerfile b/veos/docker/Dockerfile index 8ee0108a..3c698910 100644 --- a/veos/docker/Dockerfile +++ b/veos/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM public.ecr.aws/docker/library/debian:bookworm-slim LABEL maintainer="Kristian Larsson " LABEL maintainer="Roman Dodin " @@ -20,8 +20,12 @@ RUN apt-get update -qy \ iptables \ nftables \ telnet \ + git \ + python3-pip \ && rm -rf /var/lib/apt/lists/* +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/veos/docker/launch.py b/veos/docker/launch.py index 3b6a2d9d..1cbf81ed 100755 --- a/veos/docker/launch.py +++ b/veos/docker/launch.py @@ -65,7 +65,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"login:"], 1) + (ridx, match, res) = self.expect([b"login:"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("matched login prompt") @@ -86,8 +86,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b"": - self.logger.trace(f"OUTPUT: {res.decode()}") + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/vrp/docker/Dockerfile b/vrp/docker/Dockerfile index e41511f1..1b8995f0 100644 --- a/vrp/docker/Dockerfile +++ b/vrp/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Kristian Larsson RUN apt-get update -qy \ @@ -11,7 +11,11 @@ RUN apt-get update -qy \ python3-ipy \ python3-pexpect \ ssh \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE / diff --git a/vrp/docker/launch.py b/vrp/docker/launch.py index f170bb71..945efb4a 100755 --- a/vrp/docker/launch.py +++ b/vrp/docker/launch.py @@ -68,7 +68,7 @@ def bootstrap_spin(self): 4: '\n' # Press Enter to Continue } - (ridx, match, res) = self.tn.expect([b'localhost login: ', + (ridx, match, res) = self.expect([b'localhost login: ', b'Password: ', b'Enter Password:', b'Confirm Password:', @@ -97,8 +97,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b'': - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 @@ -120,7 +120,7 @@ def bootstrap_config(self): # when simulator booting, config is not ok # Error: The system is busy in building configuration. Please wait for a moment... while True: - (idx, match, res) = self.tn.expect([b'Error:'], 1) + (idx, match, res) = self.expect([b'Error:'], 1) if match: if idx == 0: self.wait_write(cmd="commit", wait=None) diff --git a/vsr1000/docker/Dockerfile b/vsr1000/docker/Dockerfile index b588d42f..615baf3d 100644 --- a/vsr1000/docker/Dockerfile +++ b/vsr1000/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM public.ecr.aws/docker/library/debian:bookworm-slim MAINTAINER Kristian Larsson ENV DEBIAN_FRONTEND=noninteractive @@ -11,7 +11,11 @@ RUN apt-get update -qy \ python3-ipy \ socat \ qemu-kvm \ - && rm -rf /var/lib/apt/lists/* + git \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages ARG IMAGE COPY $IMAGE* / diff --git a/vsr1000/docker/launch.py b/vsr1000/docker/launch.py index ca50d631..9ce0c626 100755 --- a/vsr1000/docker/launch.py +++ b/vsr1000/docker/launch.py @@ -49,7 +49,7 @@ def bootstrap_spin(self): self.start() return - (ridx, match, res) = self.tn.expect([b"Performing automatic"], 1) + (ridx, match, res) = self.expect([b"Performing automatic"], 1) if match: # got a match! if ridx == 0: # login self.logger.debug("VM started") @@ -103,8 +103,8 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != b'': - self.logger.trace("OUTPUT: %s" % res.decode()) + if res != "": + self.print(res) # reset spins if we saw some output self.spins = 0 diff --git a/xrv9k/Makefile b/xrv9k/Makefile deleted file mode 100644 index fede02f3..00000000 --- a/xrv9k/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -VENDOR=Cisco -NAME=XRv9k -IMAGE_FORMAT=qcow2 -IMAGE_GLOB=*qcow2* -INSTALL=true - -# match versions like: -# TODO: add example file names here -# xrv9k-fullk9-x.vrr-6.1.3.qcow2 -# xrv9k-fullk9-x.vrr-6.2.1.qcow2 -# xrv9k-fullk9-x-7.10.1.qcow2 -VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+\(\.[0-9A-Z]\+\)\?\)\([^0-9].*\|$$\)/\1/') - --include ../makefile-sanity.include --include ../makefile.include - -ifeq ($(INSTALL),false) -$(info Install mode disabled) -else -$(info Install mode enabled) --include ../makefile-install.include -endif - From 75198942bd9538ef2e717ed18a30a50f7a2fb8bf Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 02:33:27 +0000 Subject: [PATCH 02/34] Change QEMU connection error log message to 'error' instead of 'debug' --- common/vrnetlab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/vrnetlab.py b/common/vrnetlab.py index 5df1c15d..20128a1e 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -265,7 +265,7 @@ def start(self): self.tn.open() break except Exception as e: - self.logger.debug( + self.logger.error( "Unable to connect to qemu monitor (port {}), retrying in a second (attempt {})\nError: {}".format( 5000 + self.num, i, e ) From d5c06fb34f67d867e906b359858aa8152302691c Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 06:08:54 +0000 Subject: [PATCH 03/34] Migrate IOS-XE devices to Scrapli driver for bootstrap and startup configurations - Startup and bootstrap configurations use scrapli with IOSXEDriver - Changed variable name from 'csr' to 'cat8kv' in cat8kv install function. - Reverted change in bootstrap_spin so that console output is evaluated against empty byte-string instead of regular string. --- cisco/c8000v/docker/launch.py | 159 ++++++++++++++++------------------ cisco/cat9kv/docker/launch.py | 156 +++++++++++++++------------------ cisco/csr/docker/launch.py | 129 ++++++++++++++------------- 3 files changed, 212 insertions(+), 232 deletions(-) diff --git a/cisco/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py index f0e43506..be3945fe 100755 --- a/cisco/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -9,6 +9,7 @@ import sys import vrnetlab +from scrapli.driver.core import IOSXEDriver STARTUP_CONFIG_FILE = "/config/startup-config.cfg" @@ -115,12 +116,11 @@ def bootstrap_spin(self): else: self.wait_write("", wait=None) - # run main config! - self.bootstrap_config() - # add startup config if present - self.startup_config() - # close telnet connection - self.tn.close() + self.logger.info("Applying configuration") + + # apply bootstrap and startup configuration + self.apply_config() + # startup time? startup_time = datetime.datetime.now() - self.start_time self.logger.info("Startup complete in: %s", startup_time) @@ -134,11 +134,11 @@ def bootstrap_spin(self): self.running = True return else: - self.log.debug("Unexpected reload while running") + self.log.error("Unexpected reload while running") # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != "": + if res != b"": self.print(res) # reset spins if we saw some output self.spins = 0 @@ -146,77 +146,72 @@ def bootstrap_spin(self): self.spins += 1 return - - def bootstrap_config(self): - """Do the actual bootstrap config""" - self.logger.info("applying bootstrap configuration") - - self.wait_write("", None) - self.wait_write("enable", wait=">") - self.wait_write("configure terminal", wait=">") - - self.wait_write(f"hostname {self.hostname}") - self.wait_write( - "username %s privilege 15 password %s" % (self.username, self.password) - ) - if int(self.version.split(".")[0]) >= 16: - self.wait_write("ip domain name example.com") - else: - self.wait_write("ip domain-name example.com") - self.wait_write("crypto key generate rsa modulus 2048") + + def apply_config(self): - self.wait_write("vrf definition clab-mgmt") - self.wait_write("address-family ipv4") - self.wait_write("exit") - self.wait_write("description Containerlab management VRF (DO NOT DELETE)") - self.wait_write("exit") - - self.wait_write("ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 10.0.0.2") - - self.wait_write("interface GigabitEthernet1") - self.wait_write("vrf forwarding clab-mgmt") - self.wait_write("ip address 10.0.0.15 255.255.255.0") - self.wait_write("no shut") - self.wait_write("exit") - self.wait_write("restconf") - self.wait_write("netconf-yang") - self.wait_write("netconf max-sessions 16") - # I did not find any documentation about this, but is seems like a good idea!? - self.wait_write("netconf detailed-error") - self.wait_write("ip ssh server algorithm mac hmac-sha2-512") - self.wait_write("ip ssh maxstartups 128") - - self.wait_write("line vty 0 4") - self.wait_write("login local") - self.wait_write("transport input all") - self.wait_write("end") - self.wait_write("copy running-config startup-config") - self.wait_write("\r", "Destination") - - def startup_config(self): - """Load additional config provided by user.""" - - if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.warn(f"User provided startup configuration is not found") - return - - self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") - with open(STARTUP_CONFIG_FILE) as file: - config_lines = file.readlines() - config_lines = [line.rstrip() for line in config_lines] - self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") - - self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") - - self.wait_write("configure terminal") - # Apply lines from file - for line in config_lines: - self.wait_write(line) - # End and Save - self.wait_write("end") - self.wait_write("copy running-config startup-config") - self.wait_write("\r", "Destination") - + self.tn.close() + + # init scrapli + cat8k_scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + self.num, + "auth_bypass": True, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 300, + "timeout_transport": 300, + "timeout_ops": 90, + } + + cat8k_config = f"""hostname {self.hostname} +username {self.username} privilege 15 password {self.password} +ip domain name example.com +no ip domain lookup + +vrf definition clab-mgmt +description Containerlab management VRF (DO NOT DELETE) +address-family ipv4 +exit + +ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 10.0.0.2 + +interface GigabitEthernet 1 +description Containerlab management interface +vrf forwarding clab-mgmt +ip address 10.0.0.15 255.255.255.0 +no shut +exit + +crypto key generate rsa modulus 2048 + +ip ssh version 2 +ip ssh server algorithm mac hmac-sha2-512 +ip ssh maxstartups 128 + +restconf +netconf-yang +netconf detailed-error +netconf max-sessions 16 + +line vty 0 4 +login local +transport input all +""" + + with IOSXEDriver(**cat8k_scrapli_dev) as con: + con.send_config(cat8k_config) + + if not os.path.exists(STARTUP_CONFIG_FILE): + self.logger.warning(f"User provided startup configuration is not found.") + return + + self.logger.info("Startup configuration file found") + # send startup config + res = con.send_configs_from_file(STARTUP_CONFIG_FILE) + # print startup config and result + for response in res: + self.logger.info(f"CONFIG: {response.channel_input}") + self.logger.info(f"CONFIG RESULT: {response.result}") class C8000v(vrnetlab.VR): def __init__(self, hostname, username, password, conn_mode): @@ -239,10 +234,10 @@ def __init__(self, hostname, username, password, conn_mode): def install(self): self.logger.info("Installing C8000v") - csr = self.vms[0] - while not csr.running: - csr.work() - csr.stop() + cat8kv = self.vms[0] + while not cat8kv.running: + cat8kv.work() + cat8kv.stop() self.logger.info("Installation complete") diff --git a/cisco/cat9kv/docker/launch.py b/cisco/cat9kv/docker/launch.py index 0677aed8..05bb34e0 100755 --- a/cisco/cat9kv/docker/launch.py +++ b/cisco/cat9kv/docker/launch.py @@ -9,6 +9,7 @@ import sys import vrnetlab +from scrapli.driver.core import IOSXEDriver STARTUP_CONFIG_FILE = "/config/startup-config.cfg" @@ -44,13 +45,6 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2$", e): disk_image = "/" + e - if re.search(r"\.license$", e): - os.rename("/" + e, "/tftpboot/license.lic") - - self.license = False - if os.path.isfile("/tftpboot/license.lic"): - logger.info("License found") - self.license = True super().__init__( username, @@ -82,14 +76,14 @@ def create_boot_image(self): try: os.makedirs("/img_dir/conf") except: - self.logger.debug( + self.logger.error( "Unable to make '/img_dir'. Does the directory already exist?" ) try: os.popen("cp /vswitch.xml /img_dir/conf/") except: - self.logger.debug("No vswitch.xml file provided.") + self.logger.warning("No vswitch.xml file provided.") with open("/img_dir/iosxe_config.txt", "w") as cfg_file: cfg_file.write(f"hostname {self.hostname}\r\n") @@ -103,7 +97,7 @@ def create_boot_image(self): "/img_dir", ] - self.logger.debug("Generating boot ISO") + self.logger.info("Generating boot ISO") subprocess.Popen(genisoimage_args) def bootstrap_spin(self): @@ -128,12 +122,11 @@ def bootstrap_spin(self): self.wait_write("", wait=None) - # run main config! - self.bootstrap_config() - # add startup config if present - self.startup_config() - # close telnet connection - self.tn.close() + self.logger.info("Applying configuration") + + # apply bootstrap and startup configuration + self.apply_config() + # startup time? startup_time = datetime.datetime.now() - self.start_time self.logger.info("Startup complete in: %s", startup_time) @@ -141,11 +134,11 @@ def bootstrap_spin(self): self.running = True return elif ridx == 1: # IOSXEBOOT-4-FACTORY_RESET - self.logger.debug("Unexpected reload while running") + self.logger.error("Unexpected reload while running") # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != "": + if res != b"": self.print(res) # reset spins if we saw some output self.spins = 0 @@ -154,73 +147,66 @@ def bootstrap_spin(self): return - def bootstrap_config(self): - """Do the actual bootstrap config""" - self.logger.info("applying bootstrap configuration") - - self.wait_write("", None) - self.wait_write("enable", wait=">") - self.wait_write("configure terminal", wait="#") - - self.wait_write(f"hostname {self.hostname}") - self.wait_write( - "username %s privilege 15 password %s" % (self.username, self.password) - ) - if int(self.version.split(".")[0]) >= 16: - self.wait_write("ip domain name example.com") - else: - self.wait_write("ip domain-name example.com") - self.wait_write("crypto key generate rsa modulus 2048") - - self.wait_write("no ip domain lookup") - - # add mgmt vrf static route - self.wait_write("ip route vrf Mgmt-vrf 0.0.0.0 0.0.0.0 10.0.0.2") - - self.wait_write("interface GigabitEthernet0/0") - self.wait_write("ip address 10.0.0.15 255.255.255.0") - self.wait_write("no shut") - self.wait_write("exit") - - self.wait_write("restconf") - self.wait_write("netconf-yang") - self.wait_write("netconf max-sessions 16") - # I did not find any documentation about this, but is seems like a good idea!? - self.wait_write("netconf detailed-error") - self.wait_write("ip ssh server algorithm mac hmac-sha2-512") - self.wait_write("ip ssh maxstartups 128") - - self.wait_write("line vty 0 4") - self.wait_write("login local") - self.wait_write("transport input all") - self.wait_write("end") - self.wait_write("copy running-config startup-config") - self.wait_write("\r", "Destination") - - def startup_config(self): - """Load additional config provided by user.""" - - if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} is not found") - return - - self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") - with open(STARTUP_CONFIG_FILE) as file: - config_lines = file.readlines() - config_lines = [line.rstrip() for line in config_lines] - self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") - - self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") - - self.wait_write("configure terminal") - # Apply lines from file - for line in config_lines: - self.wait_write(line) - # End and Save - self.wait_write("end") - self.wait_write("copy running-config startup-config") - self.wait_write("\r", "Destination") - + def apply_config(self): + + self.tn.close() + + # init scrapli + cat9k_scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + self.num, + "auth_bypass": True, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 300, + "timeout_transport": 300, + "timeout_ops": 90, + } + + # bootstrap configuration + cat9k_config = f"""hostname {self.hostname} +username {self.username} privilege 15 password {self.password} +ip domain name example.com +no ip domain lookup + +ip route vrf Mgmt-vrf 0.0.0.0 0.0.0.0 10.0.0.2 + +interface GigabitEthernet 0/0 +description Containerlab management interface +ip address 10.0.0.15 255.255.255.0 +no shut +exit + +crypto key generate rsa modulus 2048 + +ip ssh version 2 +ip ssh server algorithm mac hmac-sha2-512 +ip ssh maxstartups 128 + +restconf +netconf-yang +netconf detailed-error +netconf max-sessions 16 + +line vty 0 4 +login local +transport input all +""" + + with IOSXEDriver(**cat9k_scrapli_dev) as con: + con.send_config(cat9k_config) + + if not os.path.exists(STARTUP_CONFIG_FILE): + self.logger.warning(f"User provided startup configuration is not found.") + return + + self.logger.info("Startup configuration file found") + # send startup config + res = con.send_configs_from_file(STARTUP_CONFIG_FILE) + # print startup config and result + for response in res: + self.logger.info(f"CONFIG: {response.channel_input}") + self.logger.info(f"CONFIG RESULT: {response.result}") class cat9kv(vrnetlab.VR): def __init__(self, hostname, username, password, conn_mode, vcpu, ram): diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index 1d128b4c..8f7d2671 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -10,6 +10,8 @@ import time import vrnetlab +from scrapli.driver.core import IOSXEDriver + STARTUP_CONFIG_FILE = "/config/startup-config.cfg" @@ -69,7 +71,7 @@ def __init__( self.create_boot_image() self.qemu_args.extend(["-cdrom", "/" + self.image_name]) - + def create_boot_image(self): """Creates a iso image with a bootstrap configuration""" @@ -119,14 +121,14 @@ def bootstrap_spin(self): self.logger.info("matched, Press RETURN to get started.") self.wait_write("", wait=None) + + self.logger.info("Applying configuration") + + # apply bootstrap and startup configuration + self.apply_config() - # run main config! - self.bootstrap_config() - self.startup_config() self.running = True - self.tn.close() - # startup time? startup_time = datetime.datetime.now() - self.start_time self.logger.info("Startup complete in: %s" % startup_time) @@ -134,78 +136,75 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != "": + if res != b"": self.print(res) # reset spins if we saw some output self.spins = 0 self.spins += 1 return + + def apply_config(self): + + self.tn.close() + + # init scrapli + csr_scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + self.num, + "auth_bypass": True, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 300, + "timeout_transport": 300, + "timeout_ops": 90, + } + + csr_config = f"""hostname {self.hostname} +username {self.username} privilege 15 password {self.password} +ip domain name example.com +no ip domain lookup - def bootstrap_config(self): - """Do the actual bootstrap config""" - self.logger.info("applying bootstrap configuration") +vrf definition clab-mgmt +description Containerlab management VRF (DO NOT DELETE) +address-family ipv4 +exit - self.wait_write("", None) - self.wait_write("enable", wait=">") - self.wait_write("configure terminal") +ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 10.0.0.2 - self.wait_write("hostname %s" % (self.hostname)) - self.wait_write( - "username %s privilege 15 password %s" % (self.username, self.password) - ) - if int(self.version.split('.')[0]) >= 16: - self.wait_write("ip domain name example.com") - else: - self.wait_write("ip domain-name example.com") - self.wait_write("crypto key generate rsa modulus 2048") - - self.wait_write("vrf definition clab-mgmt") - self.wait_write("address-family ipv4") - self.wait_write("exit") - self.wait_write("description Containerlab management VRF (DO NOT DELETE)") - self.wait_write("exit") - - self.wait_write("ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 10.0.0.2") - - self.wait_write("interface GigabitEthernet1") - self.wait_write("vrf forwarding clab-mgmt") - self.wait_write("ip address 10.0.0.15 255.255.255.0") - self.wait_write("no shut") - self.wait_write("exit") - self.wait_write("restconf") - self.wait_write("netconf-yang") - - self.wait_write("line vty 0 4") - self.wait_write("login local") - self.wait_write("transport input all") - self.wait_write("end") - self.wait_write("copy running-config startup-config") - self.wait_write("\r", None) - - def startup_config(self): - """Load additional config provided by user.""" - - if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.warning(f"User provided startup configuration is not found.") - return +interface GigabitEthernet 1 +description Containerlab management interface +vrf forwarding clab-mgmt +ip address 10.0.0.15 255.255.255.0 +no shut +exit - self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") - with open(STARTUP_CONFIG_FILE) as file: - config_lines = file.readlines() - config_lines = [line.rstrip() for line in config_lines] - self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") +crypto key generate rsa modulus 2048 - self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") +ip ssh version 2 - self.wait_write("configure terminal") - # Apply lines from file - for line in config_lines: - self.wait_write(line) - # End and Save - self.wait_write("end") - self.wait_write("copy running-config startup-config") +restconf +netconf-yang +line vty 0 4 +login local +transport input all +""" + + with IOSXEDriver(**csr_scrapli_dev) as con: + con.send_config(csr_config) + + if not os.path.exists(STARTUP_CONFIG_FILE): + self.logger.warning(f"User provided startup configuration is not found.") + return + + self.logger.info("Startup configuration file found") + # send startup config + res = con.send_configs_from_file(STARTUP_CONFIG_FILE) + # print startup config and result + for response in res: + self.logger.info(f"CONFIG: {response.channel_input}") + self.logger.info(f"CONFIG RESULT: {response.result}") class CSR(vrnetlab.VR): def __init__(self, hostname, username, password, nics, conn_mode): From 6d23587efab7d72cb70347aeaaba44cee4a63851 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 06:19:27 +0000 Subject: [PATCH 04/34] Delete cidfiles --- cisco/asav/cidfile | 1 - cisco/c8000v/cidfile | 1 - cisco/csr/cidfile | 1 - cisco/ftdv/cidfile | 1 - cisco/xrv9k/cidfile | 1 - 5 files changed, 5 deletions(-) delete mode 100644 cisco/asav/cidfile delete mode 100644 cisco/c8000v/cidfile delete mode 100644 cisco/csr/cidfile delete mode 100644 cisco/ftdv/cidfile delete mode 100644 cisco/xrv9k/cidfile diff --git a/cisco/asav/cidfile b/cisco/asav/cidfile deleted file mode 100644 index d7be3343..00000000 --- a/cisco/asav/cidfile +++ /dev/null @@ -1 +0,0 @@ -4ea02de7db6f6d3d77e1c19085eb0d18cbf994a094cb91a5c214de8086db8b24 \ No newline at end of file diff --git a/cisco/c8000v/cidfile b/cisco/c8000v/cidfile deleted file mode 100644 index ad105234..00000000 --- a/cisco/c8000v/cidfile +++ /dev/null @@ -1 +0,0 @@ -dec25a2bb0046602fd3944d40220435c8c53632fa20d70e611914453a7765e3b \ No newline at end of file diff --git a/cisco/csr/cidfile b/cisco/csr/cidfile deleted file mode 100644 index 80e2761b..00000000 --- a/cisco/csr/cidfile +++ /dev/null @@ -1 +0,0 @@ -49222bb4e41dc73a530ab1b208d28c03ba4327f45eed4ed93a1fb029e4310bec \ No newline at end of file diff --git a/cisco/ftdv/cidfile b/cisco/ftdv/cidfile deleted file mode 100644 index e63ab613..00000000 --- a/cisco/ftdv/cidfile +++ /dev/null @@ -1 +0,0 @@ -5fd60bb778efd8604e20201450d6fb31a5cfe88617592f5533265b218f72f917 \ No newline at end of file diff --git a/cisco/xrv9k/cidfile b/cisco/xrv9k/cidfile deleted file mode 100644 index 385b1191..00000000 --- a/cisco/xrv9k/cidfile +++ /dev/null @@ -1 +0,0 @@ -eec20e5056c30adc092444430c04256dbaac927eb492e7e97c386bfbad5b60dd \ No newline at end of file From 1726fd4d42ccf5f0f034759f0a3875cb4a98cdd9 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 06:20:11 +0000 Subject: [PATCH 05/34] Ignore cidfile --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8ec320e9..66e099f9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ cisco*.bin *.xml # ignore clab- dirs -clab-* \ No newline at end of file +clab-* + +# Ignore container cidfile +**cidfile \ No newline at end of file From 2a21182935fbf448c47029e1a5f04e422b9d126e Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 08:11:56 +0000 Subject: [PATCH 06/34] Rename XRv9k class names and use context manager for scrapli --- cisco/xrv9k/docker/launch.py | 139 +++++++++++++++-------------------- 1 file changed, 60 insertions(+), 79 deletions(-) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index 0d97f3c9..fd0a2d35 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -40,13 +40,13 @@ def trace(self, message, *args, **kws): logging.Logger.trace = trace -class XRV_vm(vrnetlab.VM): +class XRv9k_vm(vrnetlab.VM): def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram, install=False): disk_image = None for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2", e): disk_image = "/" + e - super(XRV_vm, self).__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") + super(XRv9k_vm, self).__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") self.hostname = hostname self.conn_mode = conn_mode @@ -70,21 +70,6 @@ def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram, ins ] ) self.credentials = [] - - # init scrapli - xr_scrapli_dev = { - "host": "127.0.0.1", - "port": 5000 + self.num, - "auth_username": self.username, - "auth_password": self.password, - "auth_strict_key": False, - "transport": "telnet", - "timeout_socket": 600, - "timeout_transport": 600, - "timeout_ops": 600, - } - - self.xr_conn = IOSXRDriver(**xr_scrapli_dev) def gen_mgmt(self): """Generate qemu args for the mgmt interface(s)""" @@ -160,12 +145,10 @@ def bootstrap_spin(self): self.wait_write(self.password, wait="Enter secret again:") self.credentials.insert(0, [self.username, self.password]) - if not self.bootstrap_config(): - # main config failed :/ - self.logger.error("Failed to load bootstrap configuration. Restarting XRv9k.") - self.stop() - self.start() - return + self.logger.info("Applying configuration") + + # apply bootstrap and startup configuration + self.apply_config() # startup time? startup_time = datetime.datetime.now() - self.start_time @@ -176,7 +159,7 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != "": + if res != b"": self.print(res) # reset spins if we saw some output self.spins = 0 @@ -184,23 +167,25 @@ def bootstrap_spin(self): self.spins += 1 return - - def bootstrap_config(self): - self.tn.close() + + def apply_config(self): - self.xr_conn.open() + self.tn.close() - res = self.xr_conn.send_interactive( - [ - ("crypto key generate rsa", "Do you really want to replace them? [yes/no]:", False), - ("yes", "How many bits in the modulus [2048]:", False), - ("2048", "", False), - ] - ) - - self.logger.info(res.result) + # init scrapli + xrv9k_scrapli_dev = { + "host": "127.0.0.1", + "port": 5000 + self.num, + "auth_username": self.username, + "auth_password": self.password, + "auth_strict_key": False, + "transport": "telnet", + "timeout_socket": 300, + "timeout_transport": 300, + "timeout_ops": 90, + } - XR_CONFIG = f"""hostname {self.hostname} + xrv9k_config = f"""hostname {self.hostname} vrf clab-mgmt description Containerlab management VRF. DO NOT DELETE. address-family ipv4 unicast @@ -229,47 +214,43 @@ def bootstrap_config(self): commit """ - res = self.xr_conn.send_config(XR_CONFIG, strip_prompt=False) - - if res.failed: - self.xr_conn.close() - return False - - self.startup_config() + with IOSXRDriver(**xrv9k_scrapli_dev) as con: + con.send_config(xrv9k_config) - self.xr_conn.close() - return True - - def startup_config(self): - - if not os.path.exists(STARTUP_CONFIG_FILE): - self.logger.warning(f"User provided startup configuration file is not found") - return - - self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} found") - - startup_config = "" - - # append commit to end of file - with open(STARTUP_CONFIG_FILE, "r") as cfg: - startup_config = cfg.read() - - startup_config += "commit" - - res = self.xr_conn.send_config(startup_config, strip_prompt=False) - - if res.failed: - self.logger.error(f"Failed to load startup configuration.") - - return - -class XRV(vrnetlab.VR): + if not os.path.exists(STARTUP_CONFIG_FILE): + self.logger.warning(f"User provided startup configuration is not found.") + return + + self.logger.info("Startup configuration file found") + + # need to append 'commit' to end of startup config file + startup_cfg = [] + + with open(STARTUP_CONFIG_FILE, 'r') as cfg: + for line in cfg: + # remove trailing \n from each line + startup_cfg.append(line.strip()) + startup_cfg.append("commit") + + # send startup config + res = con.send_configs(startup_cfg) + # print startup config and result + for response in res: + self.logger.info(f"CONFIG: {response.channel_input}") + self.logger.info(f"CONFIG RESULT: {response.result}") + + if res.failed: + self.logger.error(f"Failed to load startup configuration.") + return + + +class XRv9k(vrnetlab.VR): def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram): - super(XRV, self).__init__(username, password) - self.vms = [XRV_vm(hostname, username, password, nics, conn_mode, vcpu, ram)] + super(XRv9k, self).__init__(username, password) + self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode, vcpu, ram)] -class XRV_Installer(XRV): +class XRv9k_Installer(XRv9k): """ XRV installer Will start the XRV and then shut it down. Booting the XRV for the first time requires the XRV itself to install internal packages @@ -278,8 +259,8 @@ class XRV_Installer(XRV): decrease the normal startup time of the XRV. """ def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram): - super(XRV, self).__init__(username, password) - self.vms = [XRV_vm(hostname, username, password, nics, conn_mode, vcpu, ram, install=True)] + super(XRv9k, self).__init__(username, password) + self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode, vcpu, ram, install=True)] def install(self): self.logger.info("Installing XRv9k") @@ -328,7 +309,7 @@ def install(self): vrnetlab.boot_delay() if args.install: - vr = XRV_Installer( + vr = XRv9k_Installer( args.hostname, args.username, args.password, @@ -339,7 +320,7 @@ def install(self): ) vr.install() else: - vr = XRV( + vr = XRv9k( args.hostname, args.username, args.password, From decb03683e55a457559721ac11d23ff79eaaff58 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 08:22:49 +0000 Subject: [PATCH 07/34] Format environment variables nicely --- cisco/xrv9k/docker/launch.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index fd0a2d35..86f841fe 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -305,7 +305,10 @@ def install(self): if args.trace: logger.setLevel(1) - logger.debug(f"Environment variables: {os.environ}") + logger.debug("ENVIRONMENT VARIABLES") + for var, value in os.environ.items(): + logger.debug(f"{var}: {value}") + # logger.debug(f"Environment variables: {os.environ}") vrnetlab.boot_delay() if args.install: From ad8fdfc01b4600b70e10a53bcaa759fddc96e5fa Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 10:23:13 +0000 Subject: [PATCH 08/34] Add XRv 'qemu' node - existing XRv node uses vmdk image only. This one will use the qcow2 image. --- cisco/xrv/README.md | 2 +- cisco/xrv_qemu/Makefile | 12 ++ cisco/xrv_qemu/README.md | 30 ++++ cisco/xrv_qemu/docker/Dockerfile | 29 ++++ cisco/xrv_qemu/docker/launch.py | 254 +++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 cisco/xrv_qemu/Makefile create mode 100644 cisco/xrv_qemu/README.md create mode 100644 cisco/xrv_qemu/docker/Dockerfile create mode 100755 cisco/xrv_qemu/docker/launch.py diff --git a/cisco/xrv/README.md b/cisco/xrv/README.md index 68af8dc7..37374d79 100644 --- a/cisco/xrv/README.md +++ b/cisco/xrv/README.md @@ -1,6 +1,6 @@ # vrnetlab / Cisco IOS XRv -This is the vrnetlab docker image for Cisco IOS XRv. +This is the vrnetlab docker image for Cisco IOS XRv. This image uses the vmdk image, if you have the Qemu image use the 'xrv_qemu' directory. > Originally developed by Kristian Larsson (@plajjan), adapted by @hellt to be integrated with [containerlab](https://containerlab.srlinux.dev) networking. diff --git a/cisco/xrv_qemu/Makefile b/cisco/xrv_qemu/Makefile new file mode 100644 index 00000000..3bc33253 --- /dev/null +++ b/cisco/xrv_qemu/Makefile @@ -0,0 +1,12 @@ +VENDOR=cisco +NAME=xrv +IMAGE_FORMAT=qcow2 +IMAGE_GLOB=*qcow2 +VENDOR_SUBDIR=1 + +# match versions like: +# iosxrv-k9-demo-6.3.1.qcow2 +VERSION=$(shell echo $(IMAGE) | sed -e 's/.\+[^0-9]\([0-9]\+\.[0-9]\+\.[0-9]\+\(\.[0-9A-Z]\+\)\?\)\([^0-9].*\|$$\)/\1/') + +-include ../../makefile-sanity.include +-include ../../makefile.include diff --git a/cisco/xrv_qemu/README.md b/cisco/xrv_qemu/README.md new file mode 100644 index 00000000..1d1f585b --- /dev/null +++ b/cisco/xrv_qemu/README.md @@ -0,0 +1,30 @@ +# vrnetlab / Cisco IOS XRv + +This is the vrnetlab docker image for Cisco IOS XRv. Using the QCOW2 image, rather than the VMDK. You can obtain the QCOW2 from CML/VIRL. +- If you have the vmdk image for XRv use the 'xrv' directory instead. + +> Originally developed by Kristian Larsson (@plajjan), adapted by @hellt to be integrated with [containerlab](https://containerlab.srlinux.dev) networking. + +There are two flavours of virtual XR routers, XRv and XRv9000 where the latter +has a much more complete forwarding plane. This is for XRv, if you have the +XRv9k see the 'xrv9k' directory instead. + +It's not recommended to run XRv with less than 4GB of RAM. I have experienced +weird issues when trying to use less RAM. + +## Added in this fork + +* integration with containerlab as [vr-vmx](https://containerlab.srlinux.dev/manual/kinds/vr-vmx/) kind. +* docker networking using `--connection-mode` flag +* hostname, username and password configuration via flags +* added support for [boot delay](https://containerlab.srlinux.dev/manual/vrnetlab/#boot-delay) to allow for a smooth start of the big topologies +* enabled gNMI +* fixes for auto image upgrade disrupted node config +* base image updated to Ubuntu:20.04 + +## Building the docker image + +Obtain XRv qcow2 image and put the .qcow2 file in this directory and run `make docker-image`. The resulting image is called `vrnetlab/vr-xrv`. You can tag it with something else if you want, like `my-repo.example.com/vr-xrv` and then +push it to your repo. The tag is the same as the version of the XRv image, so if you have iosxrv-k9-demo-6.3.1.qcow2 your final docker image will be called `vrnetlab/cisco_xrv:6.3.1` + + * 6.1.2 diff --git a/cisco/xrv_qemu/docker/Dockerfile b/cisco/xrv_qemu/docker/Dockerfile new file mode 100644 index 00000000..98853989 --- /dev/null +++ b/cisco/xrv_qemu/docker/Dockerfile @@ -0,0 +1,29 @@ +FROM public.ecr.aws/docker/library/debian:bookworm-slim +LABEL org.opencontainers.image.authors="roman@dodin.dev" + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update -qy \ + && apt-get upgrade -qy \ + && apt-get install -y \ + bridge-utils \ + iproute2 \ + python3-ipy \ + socat \ + qemu-kvm \ + tcpdump \ + procps \ + openvswitch-switch \ + python3-pip \ + git \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages + +ARG IMAGE +COPY $IMAGE* / +COPY *.py / + +EXPOSE 22 161/udp 830 5000 10000-10099 57400 +HEALTHCHECK CMD ["/healthcheck.py"] +ENTRYPOINT ["/launch.py"] diff --git a/cisco/xrv_qemu/docker/launch.py b/cisco/xrv_qemu/docker/launch.py new file mode 100755 index 00000000..cd5a31c8 --- /dev/null +++ b/cisco/xrv_qemu/docker/launch.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 + +import datetime +import logging +import os +import re +import signal +import sys +import time + +import vrnetlab + +STARTUP_CONFIG_FILE = "/config/startup-config.cfg" + + +def handle_SIGCHLD(signal, frame): + os.waitpid(-1, os.WNOHANG) + + +def handle_SIGTERM(signal, frame): + sys.exit(0) + + +signal.signal(signal.SIGINT, handle_SIGTERM) +signal.signal(signal.SIGTERM, handle_SIGTERM) +signal.signal(signal.SIGCHLD, handle_SIGCHLD) + +TRACE_LEVEL_NUM = 9 +logging.addLevelName(TRACE_LEVEL_NUM, "TRACE") + + +def trace(self, message, *args, **kws): + # Yes, logger takes its '*args' as 'args'. + if self.isEnabledFor(TRACE_LEVEL_NUM): + self._log(TRACE_LEVEL_NUM, message, args, **kws) + + +logging.Logger.trace = trace + +class XRV_vm(vrnetlab.VM): + def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + for e in os.listdir("/"): + if re.search(".qcow2", e): + disk_image = "/" + e + super(XRV_vm, self).__init__( + username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1" + ) + self.hostname = hostname + self.conn_mode = conn_mode + self.num_nics = 128 + + self.xr_ready = False + + self.qemu_args.extend( + [ + "-nographic" + ] + ) + + self.credentials = [] + + def bootstrap_spin(self): + """""" + + if self.spins > 300: + # too many spins with no result -> give up + self.stop() + self.start() + return + + (ridx, match, res) = self.expect( + [ + b"Press RETURN to get started", + b"SYSTEM CONFIGURATION COMPLETE", + b"Enter root-system username", + b"Username:", + b"#", + ], + 1, + ) + if match: # got a match! + if ridx == 0: # press return to get started, so we press return! + self.logger.info("got 'press return to get started...'") + self.wait_write("", wait=None) + if ridx == 1: # system configuration complete + self.logger.info( + "IOS XR system configuration is complete, should be able to proceed with bootstrap configuration" + ) + self.wait_write("", wait=None) + self.xr_ready = True + if ridx == 2: # initial user config + self.logger.info("Creating initial user") + time.sleep(15) + self.wait_write(self.username, wait=None) + self.wait_write(self.password, wait="Enter secret:") + self.wait_write(self.password, wait="Enter secret again:") + self.credentials.insert(0, [self.username, self.password]) + if ridx == 3: # matched login prompt, so should login + self.logger.info("matched login prompt") + try: + username, password = self.credentials.pop(0) + except IndexError as exc: + self.logger.error("no more credentials to try") + return + self.logger.info( + "trying to log in with %s / %s" % (username, password) + ) + self.wait_write(username, wait=None) + self.wait_write(password, wait="Password:") + if self.xr_ready == True and ridx == 4: + # run main config! + self.bootstrap_config() + self.startup_config() + # close telnet connection + self.tn.close() + # startup time? + startup_time = datetime.datetime.now() - self.start_time + self.logger.info("Startup complete in: %s" % startup_time) + # mark as running + self.running = True + return + + # no match, if we saw some output from the router it's probably + # booting, so let's give it some more time + if res != "": + self.print(res) + # reset spins if we saw some output + self.spins = 0 + + self.spins += 1 + + return + + def bootstrap_config(self): + """Do the actual bootstrap config""" + self.logger.info("applying bootstrap configuration") + self.wait_write("", None) + + self.wait_write("terminal length 0") + + self.wait_write("crypto key generate rsa") + + self.wait_write("2048", wait=None) + + # make sure we get our prompt back + self.wait_write("") + + self.wait_write("configure") + self.wait_write("hostname {}".format(self.hostname)) + + # configure management vrf + self.wait_write("vrf clab-mgmt") + self.wait_write("description Containerlab management VRF (DO NOT DELETE)") + self.wait_write("address-family ipv4 unicast") + self.wait_write("exit") + self.wait_write("exit") + + # add static route for management + self.wait_write("router static") + self.wait_write("vrf clab-mgmt") + self.wait_write("address-family ipv4 unicast") + self.wait_write("0.0.0.0/0 10.0.0.2") + self.wait_write("exit") + self.wait_write("exit") + self.wait_write("exit") + + # configure ssh & netconf w/ vrf + self.wait_write("ssh server v2") + self.wait_write("ssh server vrf clab-mgmt") + self.wait_write("ssh server netconf port 830") # for 5.1.1 + self.wait_write("ssh server netconf vrf clab-mgmt") # for 5.3.3 + self.wait_write("netconf agent ssh") # for 5.1.1 + self.wait_write("netconf-yang agent ssh") # for 5.3.3 + + # configure xml agent + self.wait_write("xml agent tty") + + # configure mgmt interface + self.wait_write("interface MgmtEth 0/0/CPU0/0") + self.wait_write("vrf clab-mgmt") + self.wait_write("no shutdown") + self.wait_write("ipv4 address 10.0.0.15/24") + self.wait_write("exit") + self.wait_write("commit") + self.wait_write("exit") + + def startup_config(self): + """Load additional config provided by user.""" + + if not os.path.exists(STARTUP_CONFIG_FILE): + self.logger.warning(f"Startup config file {STARTUP_CONFIG_FILE} is not found") + return + + self.logger.info(f"Startup config file {STARTUP_CONFIG_FILE} exists") + with open(STARTUP_CONFIG_FILE) as file: + config_lines = file.readlines() + config_lines = [line.rstrip() for line in config_lines] + self.logger.info(f"Parsed startup config file {STARTUP_CONFIG_FILE}") + + self.logger.info(f"Writing lines from {STARTUP_CONFIG_FILE}") + + self.wait_write("configure") + # Apply lines from file + for line in config_lines: + self.wait_write(line) + # Commit and GTFO + self.wait_write("commit") + self.wait_write("exit") + +class XRV(vrnetlab.VR): + def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + super(XRV, self).__init__(username, password) + self.vms = [XRV_vm(hostname, username, password, conn_mode, vcpu, ram)] + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="") + parser.add_argument("--hostname", default="vr-xrv", help="Router hostname") + parser.add_argument( + "--trace", action="store_true", help="enable trace level logging" + ) + parser.add_argument("--username", default="clab", help="Username") + parser.add_argument("--password", default="clab@123", help="Password") + parser.add_argument( + "--connection-mode", + default="vrxcon", + help="Connection mode to use in the datapath", + ) + parser.add_argument( + "--vcpu", type=int, default=2, help="Number of cpu cores to use" + ) + parser.add_argument( + "--ram", type=int, default=4096, help="Amonut of RAM to allocate in MB" + ) + args = parser.parse_args() + + LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" + logging.basicConfig(format=LOG_FORMAT) + logger = logging.getLogger() + + logger.setLevel(logging.DEBUG) + if args.trace: + logger.setLevel(1) + + logger.debug("ENVIRONMENT VARIABLES") + for var, value in os.environ.items(): + logger.debug(f"{var}: {value}") + + vrnetlab.boot_delay() + + vr = XRV(args.hostname, args.username, args.password, args.connection_mode, args.vcpu, args.ram) + vr.start() \ No newline at end of file From f5183cc7570b9d45cc3b7d196b4d2b43ac6346df Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 10:31:14 +0000 Subject: [PATCH 09/34] Alter vrnetlab logging. - Alter the log levels for some logs from debug->error - Move the VM startup log message and add information about qemu smp and startup RAM. --- common/vrnetlab.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/vrnetlab.py b/common/vrnetlab.py index 20128a1e..f4e3e0e1 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -210,7 +210,7 @@ def __init__( self.qemu_args.insert(1, "-enable-kvm") def start(self): - self.logger.info("Starting %s" % self.__class__.__name__) + self.logger.info(f"Launching {self.__class__.__name__} with {self.smp} and {self.ram}M of RAM.") self.start_time = datetime.datetime.now() cmd = list(self.qemu_args) @@ -241,7 +241,7 @@ def start(self): # generate dummy NICs if self.insuffucient_nics: cmd.extend(self.gen_dummy_nics()) - + self.logger.info("qemu cmd: {}".format(" ".join(cmd))) self.p = subprocess.Popen( @@ -624,7 +624,7 @@ def check_qemu(self): output on STDOUT from qemu which means we restart it. """ if self.p is None: - self.logger.info("VM not started; starting!") + self.logger.warning("VM not started; starting!") self.start() # check for output @@ -636,7 +636,7 @@ def check_qemu(self): self.logger.info("STDERR: %s" % errs) if errs != "": - self.logger.debug("KVM error, restarting") + self.logger.error("KVM error, restarting") self.stop() self.start() @@ -720,7 +720,6 @@ def update_health(self, exit_status, message): def start(self): """Start the virtual router""" - self.logger.info("Starting vrnetlab %s" % self.__class__.__name__) self.logger.info("VMs: %s" % self.vms) started = False From a1928533ce9dc5e7b83cc146dd2219971b52b933 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 13:23:07 +0000 Subject: [PATCH 10/34] Move environment variable printing to vrnetlab.py - This means for all nodes, environment variables are displayed in the logs. --- cisco/n9kv/docker/launch.py | 1 - cisco/xrv/docker/launch.py | 1 - cisco/xrv9k/docker/launch.py | 4 ---- cisco/xrv_qemu/docker/launch.py | 4 ---- common/vrnetlab.py | 5 +++++ 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/cisco/n9kv/docker/launch.py b/cisco/n9kv/docker/launch.py index afde399e..e9e62214 100755 --- a/cisco/n9kv/docker/launch.py +++ b/cisco/n9kv/docker/launch.py @@ -215,7 +215,6 @@ def __init__(self, hostname, username, password, conn_mode): if args.trace: logger.setLevel(1) - logger.debug(f"Environment variables: {os.environ}") vrnetlab.boot_delay() vr = N9KV(args.hostname, args.username, args.password, args.connection_mode) diff --git a/cisco/xrv/docker/launch.py b/cisco/xrv/docker/launch.py index 3ae033ce..cbbbdd99 100755 --- a/cisco/xrv/docker/launch.py +++ b/cisco/xrv/docker/launch.py @@ -246,7 +246,6 @@ def __init__(self, hostname, username, password, conn_mode): ) ) - logger.debug(f"Environment variables: {os.environ}") vrnetlab.boot_delay() vr = XRV(args.hostname, args.username, args.password, args.connection_mode) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index 86f841fe..b8c55567 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -305,10 +305,6 @@ def install(self): if args.trace: logger.setLevel(1) - logger.debug("ENVIRONMENT VARIABLES") - for var, value in os.environ.items(): - logger.debug(f"{var}: {value}") - # logger.debug(f"Environment variables: {os.environ}") vrnetlab.boot_delay() if args.install: diff --git a/cisco/xrv_qemu/docker/launch.py b/cisco/xrv_qemu/docker/launch.py index cd5a31c8..f44e9871 100755 --- a/cisco/xrv_qemu/docker/launch.py +++ b/cisco/xrv_qemu/docker/launch.py @@ -244,10 +244,6 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): if args.trace: logger.setLevel(1) - logger.debug("ENVIRONMENT VARIABLES") - for var, value in os.environ.items(): - logger.debug(f"{var}: {value}") - vrnetlab.boot_delay() vr = XRV(args.hostname, args.username, args.password, args.connection_mode, args.vcpu, args.ram) diff --git a/common/vrnetlab.py b/common/vrnetlab.py index f4e3e0e1..eb939a86 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -210,6 +210,11 @@ def __init__( self.qemu_args.insert(1, "-enable-kvm") def start(self): + + self.logger.info("ENVIRONMENT VARIABLES") + for var, value in os.environ.items(): + self.logger.info(f"{var}: {value}") + self.logger.info(f"Launching {self.__class__.__name__} with {self.smp} and {self.ram}M of RAM.") self.start_time = datetime.datetime.now() From f8bff21e1cf2f7a6544ff8d1db47096d15010208 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 18:01:28 +0000 Subject: [PATCH 11/34] Fix missing space after 'ifeq' in VENDOR_SUBDIR check. --- makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile.include b/makefile.include index 032cb9cf..f2bb6d47 100644 --- a/makefile.include +++ b/makefile.include @@ -31,7 +31,7 @@ docker-build-common: docker-clean-build docker-pre-build @echo "Building docker image using $(IMAGE) as $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)" ifeq ($(NOT_VM_IMAGE), 1) echo "ok" -else ifeq($(VENDOR_SUBDIR), 1) +else ifeq ($(VENDOR_SUBDIR), 1) echo "Copying to vendor subdirectory" cp ../../common/* docker/ else From 4826cfdd23d14b4f8d8959de0939f0833c0d47ee Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 18:03:54 +0000 Subject: [PATCH 12/34] Update gitignore to ignore .qcow and .tgz --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 66e099f9..43fbb2f1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,10 @@ .idea venv *.qcow2 +*.qcow cisco*.bin *.gz +*.tgz *.xz *.vmdk *.iso From 54cddd6747c45b818c4394ad7eae1d431d62516b Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 18:35:55 +0000 Subject: [PATCH 13/34] Add 'yes' statement after netconf-yang command in CSR config (for IOS-XE 16.x compatibility) --- cisco/csr/docker/launch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index 8f7d2671..86246cc1 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -185,6 +185,7 @@ def apply_config(self): restconf netconf-yang +yes !required for IOS-XE 16.x line vty 0 4 login local From a2219a5278d8ae35c042e88da2f66f094ae0a098 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 9 Nov 2024 19:04:54 +0000 Subject: [PATCH 14/34] Bump scrapli operation timeouts for Cisco devices. - Useful for lower-spec systems where some operations may take time. --- cisco/c8000v/docker/launch.py | 2 +- cisco/cat9kv/docker/launch.py | 2 +- cisco/csr/docker/launch.py | 2 +- cisco/xrv9k/docker/launch.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cisco/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py index be3945fe..13cc6480 100755 --- a/cisco/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -160,7 +160,7 @@ def apply_config(self): "transport": "telnet", "timeout_socket": 300, "timeout_transport": 300, - "timeout_ops": 90, + "timeout_ops": 150, } cat8k_config = f"""hostname {self.hostname} diff --git a/cisco/cat9kv/docker/launch.py b/cisco/cat9kv/docker/launch.py index 05bb34e0..b290b9cd 100755 --- a/cisco/cat9kv/docker/launch.py +++ b/cisco/cat9kv/docker/launch.py @@ -160,7 +160,7 @@ def apply_config(self): "transport": "telnet", "timeout_socket": 300, "timeout_transport": 300, - "timeout_ops": 90, + "timeout_ops": 150, } # bootstrap configuration diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index 86246cc1..fdb6cb2b 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -157,7 +157,7 @@ def apply_config(self): "transport": "telnet", "timeout_socket": 300, "timeout_transport": 300, - "timeout_ops": 90, + "timeout_ops": 150, } csr_config = f"""hostname {self.hostname} diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index b8c55567..dd71045d 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -182,7 +182,7 @@ def apply_config(self): "transport": "telnet", "timeout_socket": 300, "timeout_transport": 300, - "timeout_ops": 90, + "timeout_ops": 150, } xrv9k_config = f"""hostname {self.hostname} From 5765434e906d0f1580f0e08acd3a2b6eacea0aac Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sun, 10 Nov 2024 05:08:52 +0000 Subject: [PATCH 15/34] Bump timeout of XE devices to 10 minutes (600 seconds) --- cisco/c8000v/docker/launch.py | 6 +++--- cisco/cat9kv/docker/launch.py | 6 +++--- cisco/csr/docker/launch.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cisco/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py index 13cc6480..b8b48d1c 100755 --- a/cisco/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -158,9 +158,9 @@ def apply_config(self): "auth_bypass": True, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 300, - "timeout_transport": 300, - "timeout_ops": 150, + "timeout_socket": 600, + "timeout_transport": 600, + "timeout_ops": 600, } cat8k_config = f"""hostname {self.hostname} diff --git a/cisco/cat9kv/docker/launch.py b/cisco/cat9kv/docker/launch.py index b290b9cd..29188b8d 100755 --- a/cisco/cat9kv/docker/launch.py +++ b/cisco/cat9kv/docker/launch.py @@ -158,9 +158,9 @@ def apply_config(self): "auth_bypass": True, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 300, - "timeout_transport": 300, - "timeout_ops": 150, + "timeout_socket": 600, + "timeout_transport": 600, + "timeout_ops": 600, } # bootstrap configuration diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index fdb6cb2b..5294b173 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -155,9 +155,9 @@ def apply_config(self): "auth_bypass": True, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 300, - "timeout_transport": 300, - "timeout_ops": 150, + "timeout_socket": 600, + "timeout_transport": 600, + "timeout_ops": 600, } csr_config = f"""hostname {self.hostname} From e1c71a435deabc1b836f30da253d7c37c6fdb4c4 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Wed, 13 Nov 2024 17:00:28 +0000 Subject: [PATCH 16/34] Use env var for IOSXEDriver Scrapli timeout on csr1000v - New env var SCRAPLI_TIMEOUT added, defaults to 3600 seconds (1 hour). - It's used to enable the user to modify the operation, transport and socket timeout for the Scrapli driver used to apply the config to the CSR. --- cisco/csr/docker/launch.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index 5294b173..0be8ea18 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -155,9 +155,9 @@ def apply_config(self): "auth_bypass": True, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 600, - "timeout_transport": 600, - "timeout_ops": 600, + "timeout_socket": scrapli_timeout, + "timeout_transport": scrapli_timeout, + "timeout_ops": scrapli_timeout, } csr_config = f"""hostname {self.hostname} @@ -184,8 +184,6 @@ def apply_config(self): ip ssh version 2 restconf -netconf-yang -yes !required for IOS-XE 16.x line vty 0 4 login local @@ -269,6 +267,8 @@ def install(self): # logger.setLevel(logging.DEBUG) if args.trace: logger.setLevel(1) + + scrapli_timeout = os.getenv("SCRAPLI_TIMEOUT", 3600) if args.install: vr = CSR_installer( From 2b89d11dc972348f443c30afd3d73b6e10224cc5 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Wed, 13 Nov 2024 17:16:11 +0000 Subject: [PATCH 17/34] SROS Fixes - Remove env var printing in SROS launch.py (this is done in vrnetlab now) - Comment out clean buffer parameter default (this isn't supported anymore), but just incase we might want to add it back. --- sros/docker/healthcheck.py | 18 ------------------ sros/docker/launch.py | 8 +++----- 2 files changed, 3 insertions(+), 23 deletions(-) delete mode 100755 sros/docker/healthcheck.py diff --git a/sros/docker/healthcheck.py b/sros/docker/healthcheck.py deleted file mode 100755 index 49053b63..00000000 --- a/sros/docker/healthcheck.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 - -import sys - -try: - health_file = open("/health", "r") - health = health_file.read() - health_file.close() -except FileNotFoundError: - print("health status file not found") - sys.exit(2) - -exit_status, message = health.strip().split(" ", 1) - -if message != "": - print(message) - -sys.exit(int(exit_status)) diff --git a/sros/docker/launch.py b/sros/docker/launch.py index 39979c92..9b5c64a4 100755 --- a/sros/docker/launch.py +++ b/sros/docker/launch.py @@ -964,9 +964,9 @@ def attach_cf(self, slot, cfname, size): self.qemu_args.extend(["-drive", f"if=virtio,index={disk_idx},file={path}"]) - # override wait_write clean_buffer parameter default - def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=True): - super().wait_write(cmd, wait, con, clean_buffer) + # # override wait_write clean_buffer parameter default + # def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=True): + # super().wait_write(cmd, wait, con, clean_buffer) def bootstrap_spin(self): """This function should be called periodically to do work.""" @@ -1679,8 +1679,6 @@ def getDefaultConfig() -> str: f"acting flags: username '{args.username}', password '{args.password}', connection-mode '{args.connection_mode}', variant '{args.variant}'" ) - logger.debug(f"Environment variables: {os.environ}") - vrnetlab.boot_delay() ia = SROS( From 13b3cc6cd22b1149cc3bc157ee4436cb77e59442 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Tue, 19 Nov 2024 13:20:00 +0200 Subject: [PATCH 18/34] added vrnetlab base image --- build-base-image.sh | 13 +++++++++++++ vrnetlab-base.dockerfile | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100755 build-base-image.sh create mode 100644 vrnetlab-base.dockerfile diff --git a/build-base-image.sh b/build-base-image.sh new file mode 100755 index 00000000..0ff3fd0b --- /dev/null +++ b/build-base-image.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# this script builds the vrnetlab base container image +# that is used in the dockerfiles of the NOS images + +set -e + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +sudo docker build -t ghcr.io/srl-labs/vrnetlab-base:$1 \ + -f vrnetlab-base.dockerfile . \ No newline at end of file diff --git a/vrnetlab-base.dockerfile b/vrnetlab-base.dockerfile new file mode 100644 index 00000000..c27fb076 --- /dev/null +++ b/vrnetlab-base.dockerfile @@ -0,0 +1,26 @@ +FROM public.ecr.aws/docker/library/debian:bookworm-slim +LABEL org.opencontainers.image.authors="roman@dodin.dev" + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update -qy \ + && apt-get install -y --no-install-recommends \ + bridge-utils \ + iproute2 \ + python3 \ + socat \ + qemu-kvm \ + qemu-utils \ + tcpdump \ + tftpd-hpa \ + ssh \ + inetutils-ping \ + dnsutils \ + iptables \ + nftables \ + telnet \ + python3-pip \ + python3-passlib \ + dosfstools \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.zip --break-system-packages \ No newline at end of file From a95ef39f3cbb2938b1cf99c04dc612fbbeb9cf98 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Tue, 19 Nov 2024 13:26:17 +0200 Subject: [PATCH 19/34] add empty line --- vrnetlab-base.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrnetlab-base.dockerfile b/vrnetlab-base.dockerfile index c27fb076..a961d273 100644 --- a/vrnetlab-base.dockerfile +++ b/vrnetlab-base.dockerfile @@ -23,4 +23,4 @@ RUN apt-get update -qy \ dosfstools \ && rm -rf /var/lib/apt/lists/* -RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.zip --break-system-packages \ No newline at end of file +RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.zip --break-system-packages From a1f0aa60196b1c57bb0aad1ee5754f0f972c70d4 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Wed, 20 Nov 2024 11:02:52 +0200 Subject: [PATCH 20/34] added genisoimage --- vrnetlab-base.dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/vrnetlab-base.dockerfile b/vrnetlab-base.dockerfile index a961d273..9e61b73c 100644 --- a/vrnetlab-base.dockerfile +++ b/vrnetlab-base.dockerfile @@ -21,6 +21,7 @@ RUN apt-get update -qy \ python3-pip \ python3-passlib \ dosfstools \ + genisoimage \ && rm -rf /var/lib/apt/lists/* RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.zip --break-system-packages From 15e8224af4c7dc442c85554b1ce6382886841b4e Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Thu, 21 Nov 2024 22:16:27 +0200 Subject: [PATCH 21/34] update scrapli --- vrnetlab-base.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrnetlab-base.dockerfile b/vrnetlab-base.dockerfile index 9e61b73c..3e61c07a 100644 --- a/vrnetlab-base.dockerfile +++ b/vrnetlab-base.dockerfile @@ -24,4 +24,4 @@ RUN apt-get update -qy \ genisoimage \ && rm -rf /var/lib/apt/lists/* -RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.zip --break-system-packages +RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.post1.zip --break-system-packages From 9ef133ff84a52dfdf3aaba17e17fade8187d452d Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 05:52:16 +0000 Subject: [PATCH 22/34] Update Cisco Dockerfiles to use vrnetlab base img --- cisco/asav/docker/Dockerfile | 25 +------------------------ cisco/c8000v/docker/Dockerfile | 23 +---------------------- cisco/cat9kv/docker/Dockerfile | 24 +----------------------- cisco/csr/docker/Dockerfile | 26 +------------------------- cisco/ftdv/docker/Dockerfile | 26 +------------------------- cisco/iol/docker/Dockerfile | 16 ++-------------- cisco/n9kv/docker/Dockerfile | 28 +--------------------------- cisco/nxos/docker/Dockerfile | 22 +--------------------- cisco/vios/docker/Dockerfile | 20 +------------------- cisco/xrv/docker/Dockerfile | 22 +--------------------- cisco/xrv9k/docker/Dockerfile | 25 +------------------------ cisco/xrv_qemu/docker/Dockerfile | 22 +--------------------- 12 files changed, 13 insertions(+), 266 deletions(-) diff --git a/cisco/asav/docker/Dockerfile b/cisco/asav/docker/Dockerfile index 31fe70c6..2fab6fd7 100755 --- a/cisco/asav/docker/Dockerfile +++ b/cisco/asav/docker/Dockerfile @@ -1,27 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Kristian Larsson - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - telnet \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG VERSION ENV VERSION=${VERSION} diff --git a/cisco/c8000v/docker/Dockerfile b/cisco/c8000v/docker/Dockerfile index 608afb71..2fab6fd7 100644 --- a/cisco/c8000v/docker/Dockerfile +++ b/cisco/c8000v/docker/Dockerfile @@ -1,25 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - socat \ - qemu-kvm \ - qemu-utils \ - tcpdump \ - inetutils-ping \ - ssh \ - telnet \ - procps \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG VERSION ENV VERSION=${VERSION} diff --git a/cisco/cat9kv/docker/Dockerfile b/cisco/cat9kv/docker/Dockerfile index 56e8c844..8f79f39d 100644 --- a/cisco/cat9kv/docker/Dockerfile +++ b/cisco/cat9kv/docker/Dockerfile @@ -1,26 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - socat \ - qemu-kvm \ - qemu-utils \ - python3 \ - tcpdump \ - inetutils-ping \ - ssh \ - telnet \ - procps \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG VERSION ENV VERSION=${VERSION} diff --git a/cisco/csr/docker/Dockerfile b/cisco/csr/docker/Dockerfile index ffad487d..2fab6fd7 100644 --- a/cisco/csr/docker/Dockerfile +++ b/cisco/csr/docker/Dockerfile @@ -1,28 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Kristian Larsson -MAINTAINER Denis Pointer - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - telnet \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG VERSION ENV VERSION=${VERSION} diff --git a/cisco/ftdv/docker/Dockerfile b/cisco/ftdv/docker/Dockerfile index 19324856..b2121cf7 100644 --- a/cisco/ftdv/docker/Dockerfile +++ b/cisco/ftdv/docker/Dockerfile @@ -1,28 +1,4 @@ -FROM debian:bookworm-slim - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/iol/docker/Dockerfile b/cisco/iol/docker/Dockerfile index 8fbab4b2..78473dcc 100644 --- a/cisco/iol/docker/Dockerfile +++ b/cisco/iol/docker/Dockerfile @@ -1,20 +1,8 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -# Install dependencies -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - iproute2 \ - iputils-ping \ - net-tools \ - sudo \ - curl \ - ca-certificates \ - gnupg \ - && apt-get clean +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 # Add containerlab gemfury for custom IOUYAP RUN echo "deb [trusted=yes] https://netdevops.fury.site/apt/ /" | \ -sudo tee -a /etc/apt/sources.list.d/netdevops.list +tee -a /etc/apt/sources.list.d/netdevops.list # Update and install IOUYAP RUN apt-get update && \ diff --git a/cisco/n9kv/docker/Dockerfile b/cisco/n9kv/docker/Dockerfile index 4debcb71..a72c601e 100644 --- a/cisco/n9kv/docker/Dockerfile +++ b/cisco/n9kv/docker/Dockerfile @@ -1,30 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL maintainer="Kristian Larsson " -LABEL maintainer="Roman Dodin " - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - iptables \ - nftables \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/nxos/docker/Dockerfile b/cisco/nxos/docker/Dockerfile index e8461e0e..1527c896 100644 --- a/cisco/nxos/docker/Dockerfile +++ b/cisco/nxos/docker/Dockerfile @@ -1,24 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - procps \ - openvswitch-switch \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/vios/docker/Dockerfile b/cisco/vios/docker/Dockerfile index e3eeb004..6427f83f 100644 --- a/cisco/vios/docker/Dockerfile +++ b/cisco/vios/docker/Dockerfile @@ -1,22 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="xtothj@gmail.com" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - qemu-kvm \ - qemu-utils \ - tcpdump \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/xrv/docker/Dockerfile b/cisco/xrv/docker/Dockerfile index 98853989..34aa6673 100644 --- a/cisco/xrv/docker/Dockerfile +++ b/cisco/xrv/docker/Dockerfile @@ -1,24 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - procps \ - openvswitch-switch \ - python3-pip \ - git \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/xrv9k/docker/Dockerfile b/cisco/xrv9k/docker/Dockerfile index c46d0bc7..8c6a929e 100644 --- a/cisco/xrv9k/docker/Dockerfile +++ b/cisco/xrv9k/docker/Dockerfile @@ -1,27 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - telnet \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cisco/xrv_qemu/docker/Dockerfile b/cisco/xrv_qemu/docker/Dockerfile index 98853989..34aa6673 100644 --- a/cisco/xrv_qemu/docker/Dockerfile +++ b/cisco/xrv_qemu/docker/Dockerfile @@ -1,24 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - procps \ - openvswitch-switch \ - python3-pip \ - git \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / From 5dc58c5ed24d498ff62e0cf2a2cfb25a68415db1 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 07:33:57 +0000 Subject: [PATCH 23/34] Alter log formatting in vrnetlab.py - Use format string in log colour formatting - Print newlines before log messages in wait_write() to improve visual clarity of log messages. --- common/vrnetlab.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/common/vrnetlab.py b/common/vrnetlab.py index eb939a86..3a5c4d40 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -84,11 +84,11 @@ def __init__( ): # set fancy logging colours - logging.addLevelName( logging.INFO, "\033[1;92m%s\033[1;0m" % logging.getLevelName(logging.INFO)) - logging.addLevelName( logging.WARN, "\033[38;5;220m%s\033[1;0m" % logging.getLevelName(logging.WARN)) - logging.addLevelName( logging.DEBUG, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.DEBUG)) - logging.addLevelName( logging.ERROR, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.ERROR)) - logging.addLevelName( logging.CRITICAL, "\033[1;91m%s\033[1;0m" % logging.getLevelName(logging.CRITICAL)) + logging.addLevelName( logging.INFO, f"\033[1;92m{logging.getLevelName(logging.INFO)}\033[1;0m") + logging.addLevelName( logging.WARN, f"\033[38;5;220m{logging.getLevelName(logging.WARN)}\033[1;0m") + logging.addLevelName( logging.DEBUG, f"\033[1;91m{logging.getLevelName(logging.DEBUG)}\033[1;0m") + logging.addLevelName( logging.ERROR, f"\033[1;91m{logging.getLevelName(logging.ERROR)}\033[1;0m") + logging.addLevelName( logging.CRITICAL, f"\033[1;91m{logging.getLevelName(logging.CRITICAL)}\033[1;0m") # set default logger for vrnetlab VM class self.logger = logging.getLogger("vrnetlab") @@ -580,12 +580,14 @@ def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=None if wait == "__defaultpattern__": wait = self.wait_pattern + self.print(b"\n") self.logger.info(f"waiting for '{wait}' on console.") self.read_until(wait, timeout) time.sleep(0.1) # don't write to the console too fast + self.print(b"\n") self.logger.info(f"writing to console: '{cmd}'") self.tn.channel.write(f"{cmd}\r") From 7b9bada9e70f822de7074f2b58208fc674364ef9 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 09:40:17 +0000 Subject: [PATCH 24/34] Imrpove XRv9k install process + use env vars for vcpu, ram. --- cisco/xrv9k/docker/launch.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index dd71045d..48c69273 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -14,6 +14,9 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SCRAPLI_TIMEOUT = 2700 +DEFAULT_VCPU = 4 +DEFAULT_RAM = 16384 def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -127,6 +130,7 @@ def bootstrap_spin(self): [ b"Press RETURN to get started", b"Enter root-system [U|u]sername", + b"SDR\/XR partition preparation completed successfully", ], 1, ) @@ -137,7 +141,6 @@ def bootstrap_spin(self): self.wait_write("", wait=None) if ridx == 1: # system configuration complete if self.install_mode: - self.running = True return self.logger.info("Creating initial user") self.wait_write(self.username, wait=None) @@ -156,6 +159,9 @@ def bootstrap_spin(self): # mark as running self.running = True return + if ridx == 2 and self.install_mode: + self.running = True + return # no match, if we saw some output from the router it's probably # booting, so let's give it some more time @@ -172,6 +178,8 @@ def apply_config(self): self.tn.close() + self.logger.info(f"Scrapli timeout is {scrapli_timeout} seconds. (Default: {DEFAULT_SCRAPLI_TIMEOUT})") + # init scrapli xrv9k_scrapli_dev = { "host": "127.0.0.1", @@ -180,9 +188,9 @@ def apply_config(self): "auth_password": self.password, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 300, - "timeout_transport": 300, - "timeout_ops": 150, + "timeout_socket": scrapli_timeout, + "timeout_transport": scrapli_timeout, + "timeout_ops": scrapli_timeout, } xrv9k_config = f"""hostname {self.hostname} @@ -267,7 +275,7 @@ def install(self): xrv = self.vms[0] while not xrv.running: xrv.work() - time.sleep(30) + time.sleep(2) xrv.stop() self.logger.info("Installation complete") @@ -296,6 +304,11 @@ def install(self): help="Connection mode to use in the datapath", ) args = parser.parse_args() + + scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) + vcpu = os.getenv('VCPU', DEFAULT_VCPU) + ram = os.getenv('RAM', DEFAULT_RAM) + LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" logging.basicConfig(format=LOG_FORMAT) @@ -314,8 +327,8 @@ def install(self): args.password, args.nics, args.connection_mode, - args.vcpu, - args.ram, + vcpu, + ram, ) vr.install() else: @@ -325,7 +338,7 @@ def install(self): args.password, args.nics, args.connection_mode, - args.vcpu, - args.ram, + vcpu, + ram, ) vr.start() From 628fd33ecbfedaa58527922297eb6c49e438162a Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 09:46:16 +0000 Subject: [PATCH 25/34] Rename xrv_qemu to xrv_qcow - Hopefully this is more intuitive to users, they see they have a .qcow2 file and can use the xrv_qcow dir? --- cisco/{xrv_qemu => xrv_qcow}/Makefile | 0 cisco/{xrv_qemu => xrv_qcow}/README.md | 0 cisco/{xrv_qemu => xrv_qcow}/docker/Dockerfile | 0 cisco/{xrv_qemu => xrv_qcow}/docker/launch.py | 7 ------- 4 files changed, 7 deletions(-) rename cisco/{xrv_qemu => xrv_qcow}/Makefile (100%) rename cisco/{xrv_qemu => xrv_qcow}/README.md (100%) rename cisco/{xrv_qemu => xrv_qcow}/docker/Dockerfile (100%) rename cisco/{xrv_qemu => xrv_qcow}/docker/launch.py (98%) diff --git a/cisco/xrv_qemu/Makefile b/cisco/xrv_qcow/Makefile similarity index 100% rename from cisco/xrv_qemu/Makefile rename to cisco/xrv_qcow/Makefile diff --git a/cisco/xrv_qemu/README.md b/cisco/xrv_qcow/README.md similarity index 100% rename from cisco/xrv_qemu/README.md rename to cisco/xrv_qcow/README.md diff --git a/cisco/xrv_qemu/docker/Dockerfile b/cisco/xrv_qcow/docker/Dockerfile similarity index 100% rename from cisco/xrv_qemu/docker/Dockerfile rename to cisco/xrv_qcow/docker/Dockerfile diff --git a/cisco/xrv_qemu/docker/launch.py b/cisco/xrv_qcow/docker/launch.py similarity index 98% rename from cisco/xrv_qemu/docker/launch.py rename to cisco/xrv_qcow/docker/launch.py index f44e9871..d00301af 100755 --- a/cisco/xrv_qemu/docker/launch.py +++ b/cisco/xrv_qcow/docker/launch.py @@ -50,13 +50,6 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): self.num_nics = 128 self.xr_ready = False - - self.qemu_args.extend( - [ - "-nographic" - ] - ) - self.credentials = [] def bootstrap_spin(self): From 6249321fa146dc70fc85c5f82d76c0ec65164755 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 09:59:09 +0000 Subject: [PATCH 26/34] Add VCPU, RAM and SCRAPLI_TIMEOUT env vars to cat8k --- cisco/c8000v/docker/launch.py | 51 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/cisco/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py index b8b48d1c..f35e4c12 100755 --- a/cisco/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -13,6 +13,11 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +# scrapli timeouts in seconds +DEFAULT_SCRAPLI_TIMEOUT = 2700 +DEFAULT_VCPU = 1 +DEFAULT_RAM = 4096 # in MB + def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -40,7 +45,7 @@ def trace(self, message, *args, **kws): class C8000v_vm(vrnetlab.VM): - def __init__(self, hostname, username, password, conn_mode, install_mode=False): + def __init__(self, hostname, username, password, conn_mode, vcpu, ram, install_mode=False): disk_image = None for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2$", e): @@ -53,7 +58,7 @@ def __init__(self, hostname, username, password, conn_mode, install_mode=False): logger.info("License found") self.license = True - super().__init__(username, password, disk_image=disk_image, ram=4096) + super().__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") self.install_mode = install_mode self.hostname = hostname self.conn_mode = conn_mode @@ -151,6 +156,8 @@ def apply_config(self): self.tn.close() + self.logger.info(f"Scrapli timeout is {scrapli_timeout} seconds. (Default: {DEFAULT_SCRAPLI_TIMEOUT})") + # init scrapli cat8k_scrapli_dev = { "host": "127.0.0.1", @@ -158,41 +165,33 @@ def apply_config(self): "auth_bypass": True, "auth_strict_key": False, "transport": "telnet", - "timeout_socket": 600, - "timeout_transport": 600, - "timeout_ops": 600, + "timeout_socket": scrapli_timeout, + "timeout_transport": scrapli_timeout, + "timeout_ops": scrapli_timeout, } cat8k_config = f"""hostname {self.hostname} username {self.username} privilege 15 password {self.password} ip domain name example.com no ip domain lookup - +crypto key generate rsa modulus 2048 vrf definition clab-mgmt description Containerlab management VRF (DO NOT DELETE) address-family ipv4 exit - ip route vrf clab-mgmt 0.0.0.0 0.0.0.0 10.0.0.2 - interface GigabitEthernet 1 description Containerlab management interface vrf forwarding clab-mgmt ip address 10.0.0.15 255.255.255.0 no shut exit - -crypto key generate rsa modulus 2048 - -ip ssh version 2 -ip ssh server algorithm mac hmac-sha2-512 -ip ssh maxstartups 128 - restconf netconf-yang -netconf detailed-error netconf max-sessions 16 - +netconf detailed-error +ip ssh server algorithm mac hmac-sha2-512 +ip ssh maxstartups 128 line vty 0 4 login local transport input all @@ -214,9 +213,9 @@ def apply_config(self): self.logger.info(f"CONFIG RESULT: {response.result}") class C8000v(vrnetlab.VR): - def __init__(self, hostname, username, password, conn_mode): + def __init__(self, hostname, username, password, conn_mode, vcpu, ram): super(C8000v, self).__init__(username, password) - self.vms = [C8000v_vm(hostname, username, password, conn_mode)] + self.vms = [C8000v_vm(hostname, username, password, conn_mode, vcpu, ram)] class C8000v_installer(C8000v): @@ -226,10 +225,10 @@ class C8000v_installer(C8000v): console output on serial, not vga. """ - def __init__(self, hostname, username, password, conn_mode): - super(C8000v_installer, self).__init__(hostname, username, password, conn_mode) + def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + super(C8000v_installer, self).__init__(hostname, username, password, conn_mode, vcpu, ram) self.vms = [ - C8000v_vm(hostname, username, password, conn_mode, install_mode=True) + C8000v_vm(hostname, username, password, conn_mode, vcpu, ram, install_mode=True) ] def install(self): @@ -267,11 +266,15 @@ def install(self): if args.trace: logger.setLevel(1) + scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) + vcpu = os.getenv('VCPU', DEFAULT_VCPU) + ram = os.getenv('RAM', DEFAULT_RAM) + if args.install: vr = C8000v_installer( - args.hostname, args.username, args.password, args.connection_mode + args.hostname, args.username, args.password, args.connection_mode, vcpu, ram ) vr.install() else: - vr = C8000v(args.hostname, args.username, args.password, args.connection_mode) + vr = C8000v(args.hostname, args.username, args.password, args.connection_mode, vcpu, ram) vr.start() From d09e24d7a4adfd2dfa9f3ada4eb39409e71387f7 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 11:06:47 +0000 Subject: [PATCH 27/34] Update SROS Dockerfile to use vrnetlab-base image --- sros/docker/Dockerfile | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/sros/docker/Dockerfile b/sros/docker/Dockerfile index a535f18c..8c6a929e 100644 --- a/sros/docker/Dockerfile +++ b/sros/docker/Dockerfile @@ -1,28 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - qemu-kvm \ - qemu-utils \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / From 4e483edacc5d74077e2be5a820341fa008d60529 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 11:40:52 +0000 Subject: [PATCH 28/34] Update Cisco nodes to use env vars for smp/ram - Added default SMP/RAM for all nodes, this can be overriden by the env var(s) `QEMU_SMP` & `QEMU_MEMORY` --- cisco/asav/docker/launch.py | 5 ++++- cisco/c8000v/docker/launch.py | 24 ++++++++++-------------- cisco/cat9kv/docker/launch.py | 14 +++++++------- cisco/csr/docker/launch.py | 4 +++- cisco/ftdv/docker/launch.py | 4 +++- cisco/n9kv/docker/launch.py | 6 ++++-- cisco/nxos/docker/launch.py | 4 +++- cisco/vios/docker/launch.py | 6 ++++-- cisco/xrv/docker/launch.py | 12 ++++-------- cisco/xrv9k/docker/launch.py | 26 +++++++++----------------- cisco/xrv_qcow/docker/launch.py | 22 +++++++++------------- common/vrnetlab.py | 2 +- 12 files changed, 61 insertions(+), 68 deletions(-) diff --git a/cisco/asav/docker/launch.py b/cisco/asav/docker/launch.py index ca75ea85..04903837 100755 --- a/cisco/asav/docker/launch.py +++ b/cisco/asav/docker/launch.py @@ -11,6 +11,9 @@ import vrnetlab +DEFAULT_SMP = 4 +DEFAULT_RAM = 8192 # in MB + def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -43,7 +46,7 @@ def __init__(self, username, password, conn_mode, install_mode=False): disk_image = "/" + e super(ASAv_vm, self).__init__( - username, password, disk_image=disk_image, ram=2048 + username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1" ) self.nic_type = "e1000" self.install_mode = install_mode diff --git a/cisco/c8000v/docker/launch.py b/cisco/c8000v/docker/launch.py index f35e4c12..fc855557 100755 --- a/cisco/c8000v/docker/launch.py +++ b/cisco/c8000v/docker/launch.py @@ -18,7 +18,6 @@ DEFAULT_VCPU = 1 DEFAULT_RAM = 4096 # in MB - def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -45,7 +44,7 @@ def trace(self, message, *args, **kws): class C8000v_vm(vrnetlab.VM): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram, install_mode=False): + def __init__(self, hostname, username, password, conn_mode, install_mode=False): disk_image = None for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2$", e): @@ -58,7 +57,7 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram, install_m logger.info("License found") self.license = True - super().__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") + super().__init__(username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_VCPU},threads=1,sockets=1") self.install_mode = install_mode self.hostname = hostname self.conn_mode = conn_mode @@ -156,6 +155,7 @@ def apply_config(self): self.tn.close() + scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) self.logger.info(f"Scrapli timeout is {scrapli_timeout} seconds. (Default: {DEFAULT_SCRAPLI_TIMEOUT})") # init scrapli @@ -213,9 +213,9 @@ def apply_config(self): self.logger.info(f"CONFIG RESULT: {response.result}") class C8000v(vrnetlab.VR): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, conn_mode): super(C8000v, self).__init__(username, password) - self.vms = [C8000v_vm(hostname, username, password, conn_mode, vcpu, ram)] + self.vms = [C8000v_vm(hostname, username, password, conn_mode)] class C8000v_installer(C8000v): @@ -225,10 +225,10 @@ class C8000v_installer(C8000v): console output on serial, not vga. """ - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): - super(C8000v_installer, self).__init__(hostname, username, password, conn_mode, vcpu, ram) + def __init__(self, hostname, username, password, conn_mode): + super(C8000v_installer, self).__init__(hostname, username, password, conn_mode) self.vms = [ - C8000v_vm(hostname, username, password, conn_mode, vcpu, ram, install_mode=True) + C8000v_vm(hostname, username, password, conn_mode, install_mode=True) ] def install(self): @@ -266,15 +266,11 @@ def install(self): if args.trace: logger.setLevel(1) - scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) - vcpu = os.getenv('VCPU', DEFAULT_VCPU) - ram = os.getenv('RAM', DEFAULT_RAM) - if args.install: vr = C8000v_installer( - args.hostname, args.username, args.password, args.connection_mode, vcpu, ram + args.hostname, args.username, args.password, args.connection_mode ) vr.install() else: - vr = C8000v(args.hostname, args.username, args.password, args.connection_mode, vcpu, ram) + vr = C8000v(args.hostname, args.username, args.password, args.connection_mode) vr.start() diff --git a/cisco/cat9kv/docker/launch.py b/cisco/cat9kv/docker/launch.py index 29188b8d..1c095000 100755 --- a/cisco/cat9kv/docker/launch.py +++ b/cisco/cat9kv/docker/launch.py @@ -13,6 +13,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 4 +DEFAULT_RAM = 18432 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -40,7 +42,7 @@ def trace(self, message, *args, **kws): class cat9kv_vm(vrnetlab.VM): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, conn_mode): disk_image = None for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2$", e): @@ -50,8 +52,8 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): username, password, disk_image=disk_image, - smp=f"cores={vcpu},threads=1,sockets=1", - ram=ram, + smp=f"cores={DEFAULT_SMP},threads=1,sockets=1", + ram=DEFAULT_RAM, min_dp_nics=8, ) self.hostname = hostname @@ -209,9 +211,9 @@ def apply_config(self): self.logger.info(f"CONFIG RESULT: {response.result}") class cat9kv(vrnetlab.VR): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, conn_mode): super(cat9kv, self).__init__(username, password) - self.vms = [cat9kv_vm(hostname, username, password, conn_mode, vcpu, ram)] + self.vms = [cat9kv_vm(hostname, username, password, conn_mode)] if __name__ == "__main__": @@ -247,7 +249,5 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): args.username, args.password, args.connection_mode, - args.vcpu, - args.ram, ) vr.start() diff --git a/cisco/csr/docker/launch.py b/cisco/csr/docker/launch.py index 0be8ea18..58d4e550 100755 --- a/cisco/csr/docker/launch.py +++ b/cisco/csr/docker/launch.py @@ -15,6 +15,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 1 +DEFAULT_RAM = 4096 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -57,7 +59,7 @@ def __init__( logger.info("License found") self.license = True - super(CSR_vm, self).__init__(username, password, disk_image=disk_image) + super(CSR_vm, self).__init__(username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1") self.install_mode = install_mode self.num_nics = nics diff --git a/cisco/ftdv/docker/launch.py b/cisco/ftdv/docker/launch.py index 27083e20..af6b07c8 100755 --- a/cisco/ftdv/docker/launch.py +++ b/cisco/ftdv/docker/launch.py @@ -11,6 +11,8 @@ import vrnetlab +DEFAULT_SMP = 4 +DEFAULT_RAM = 8192 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -50,7 +52,7 @@ def __init__( self.license = False super(FTDV_vm, self).__init__( - username, password, disk_image=disk_image, ram=8192, smp="4,sockets=1,cores=4,threads=1" + username, password, disk_image=disk_image, ram=8192, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1" ) self.login_ready = False diff --git a/cisco/n9kv/docker/launch.py b/cisco/n9kv/docker/launch.py index e9e62214..4643b5d4 100755 --- a/cisco/n9kv/docker/launch.py +++ b/cisco/n9kv/docker/launch.py @@ -12,6 +12,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 4 +DEFAULT_RAM = 10240 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -48,8 +50,8 @@ def __init__(self, hostname, username, password, conn_mode): logging.getLogger().info("Disk image was not found") exit(1) super(N9KV_vm, self).__init__( - username, password, disk_image=disk_image, ram=10240, - smp=4, cpu="host,level=9" + username, password, disk_image=disk_image, ram=DEFAULT_RAM, + smp=DEFAULT_SMP, cpu="host,level=9" ) self.hostname = hostname self.conn_mode = conn_mode diff --git a/cisco/nxos/docker/launch.py b/cisco/nxos/docker/launch.py index 8844f24c..a82ad078 100755 --- a/cisco/nxos/docker/launch.py +++ b/cisco/nxos/docker/launch.py @@ -12,6 +12,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 2 +DEFAULT_RAM = 4096 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -44,7 +46,7 @@ def __init__(self, hostname, username, password, conn_mode): if re.search(".qcow2$", e): disk_image = "/" + e super(NXOS_vm, self).__init__( - username, password, disk_image=disk_image, ram=4096, smp="2" + username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=DEFAULT_SMP ) self.credentials = [["admin", "admin"]] self.hostname = hostname diff --git a/cisco/vios/docker/launch.py b/cisco/vios/docker/launch.py index 3df4b5a5..c05cdcdc 100755 --- a/cisco/vios/docker/launch.py +++ b/cisco/vios/docker/launch.py @@ -10,6 +10,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_VCPU = 1 +DEFAULT_RAM = 512 # in MB def handle_SIGCHLD(_signal, _frame): os.waitpid(-1, os.WNOHANG) @@ -49,8 +51,8 @@ def __init__(self, hostname: str, username: str, password: str, conn_mode: str): username=username, password=password, disk_image=disk_image, - smp="1", - ram=512, + smp=DEFAULT_VCPU, + ram=DEFAULT_RAM, driveif="virtio", ) diff --git a/cisco/xrv/docker/launch.py b/cisco/xrv/docker/launch.py index cbbbdd99..1725b911 100755 --- a/cisco/xrv/docker/launch.py +++ b/cisco/xrv/docker/launch.py @@ -14,6 +14,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 2 +DEFAULT_RAM = 4096 def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -46,12 +48,12 @@ def __init__(self, hostname, username, password, conn_mode): if re.search(".vmdk", e): disk_image = "/" + e super(XRV_vm, self).__init__( - username, password, disk_image=disk_image, ram=3072 + username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1" ) self.hostname = hostname self.conn_mode = conn_mode self.num_nics = 128 - self.credentials = [["admin", "admin"]] + self.credentials = [] self.xr_ready = False @@ -240,12 +242,6 @@ def __init__(self, hostname, username, password, conn_mode): if args.trace: logger.setLevel(1) - logger.debug( - "acting flags: username '{}', password '{}', connection-mode '{}'".format( - args.username, args.password, args.connection_mode - ) - ) - vrnetlab.boot_delay() vr = XRV(args.hostname, args.username, args.password, args.connection_mode) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index 48c69273..f381d683 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -15,8 +15,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" DEFAULT_SCRAPLI_TIMEOUT = 2700 -DEFAULT_VCPU = 4 -DEFAULT_RAM = 16384 +DEFAULT_SMP = 4 +DEFAULT_RAM = 16384 # in MB def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -44,12 +44,12 @@ def trace(self, message, *args, **kws): class XRv9k_vm(vrnetlab.VM): - def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram, install=False): + def __init__(self, hostname, username, password, nics, conn_mode, install=False): disk_image = None for e in sorted(os.listdir("/")): if not disk_image and re.search(".qcow2", e): disk_image = "/" + e - super(XRv9k_vm, self).__init__(username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1") + super(XRv9k_vm, self).__init__(username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1") self.hostname = hostname self.conn_mode = conn_mode @@ -178,6 +178,7 @@ def apply_config(self): self.tn.close() + scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) self.logger.info(f"Scrapli timeout is {scrapli_timeout} seconds. (Default: {DEFAULT_SCRAPLI_TIMEOUT})") # init scrapli @@ -253,9 +254,9 @@ def apply_config(self): class XRv9k(vrnetlab.VR): - def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, nics, conn_mode): super(XRv9k, self).__init__(username, password) - self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode, vcpu, ram)] + self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode)] class XRv9k_Installer(XRv9k): @@ -266,9 +267,9 @@ class XRv9k_Installer(XRv9k): By running this "install" when building the docker image we can decrease the normal startup time of the XRV. """ - def __init__(self, hostname, username, password, nics, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, nics, conn_mode): super(XRv9k, self).__init__(username, password) - self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode, vcpu, ram, install=True)] + self.vms = [XRv9k_vm(hostname, username, password, nics, conn_mode, install=True)] def install(self): self.logger.info("Installing XRv9k") @@ -305,11 +306,6 @@ def install(self): ) args = parser.parse_args() - scrapli_timeout = os.getenv('SCRAPLI_TIMEOUT', DEFAULT_SCRAPLI_TIMEOUT) - vcpu = os.getenv('VCPU', DEFAULT_VCPU) - ram = os.getenv('RAM', DEFAULT_RAM) - - LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" logging.basicConfig(format=LOG_FORMAT) logger = logging.getLogger() @@ -327,8 +323,6 @@ def install(self): args.password, args.nics, args.connection_mode, - vcpu, - ram, ) vr.install() else: @@ -338,7 +332,5 @@ def install(self): args.password, args.nics, args.connection_mode, - vcpu, - ram, ) vr.start() diff --git a/cisco/xrv_qcow/docker/launch.py b/cisco/xrv_qcow/docker/launch.py index d00301af..da3c1f4c 100755 --- a/cisco/xrv_qcow/docker/launch.py +++ b/cisco/xrv_qcow/docker/launch.py @@ -12,6 +12,8 @@ STARTUP_CONFIG_FILE = "/config/startup-config.cfg" +DEFAULT_SMP = 2 +DEFAULT_RAM = 4096 def handle_SIGCHLD(signal, frame): os.waitpid(-1, os.WNOHANG) @@ -38,12 +40,12 @@ def trace(self, message, *args, **kws): logging.Logger.trace = trace class XRV_vm(vrnetlab.VM): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, conn_mode): for e in os.listdir("/"): if re.search(".qcow2", e): disk_image = "/" + e super(XRV_vm, self).__init__( - username, password, disk_image=disk_image, ram=ram, smp=f"cores={vcpu},threads=1,sockets=1" + username, password, disk_image=disk_image, ram=DEFAULT_RAM, smp=f"cores={DEFAULT_SMP},threads=1,sockets=1" ) self.hostname = hostname self.conn_mode = conn_mode @@ -115,7 +117,7 @@ def bootstrap_spin(self): # no match, if we saw some output from the router it's probably # booting, so let's give it some more time - if res != "": + if res != b"": self.print(res) # reset spins if we saw some output self.spins = 0 @@ -201,16 +203,16 @@ def startup_config(self): self.wait_write("exit") class XRV(vrnetlab.VR): - def __init__(self, hostname, username, password, conn_mode, vcpu, ram): + def __init__(self, hostname, username, password, conn_mode): super(XRV, self).__init__(username, password) - self.vms = [XRV_vm(hostname, username, password, conn_mode, vcpu, ram)] + self.vms = [XRV_vm(hostname, username, password, conn_mode)] if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="") - parser.add_argument("--hostname", default="vr-xrv", help="Router hostname") + parser.add_argument("--hostname", default="cisco_xrv", help="Router hostname") parser.add_argument( "--trace", action="store_true", help="enable trace level logging" ) @@ -221,12 +223,6 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): default="vrxcon", help="Connection mode to use in the datapath", ) - parser.add_argument( - "--vcpu", type=int, default=2, help="Number of cpu cores to use" - ) - parser.add_argument( - "--ram", type=int, default=4096, help="Amonut of RAM to allocate in MB" - ) args = parser.parse_args() LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" @@ -239,5 +235,5 @@ def __init__(self, hostname, username, password, conn_mode, vcpu, ram): vrnetlab.boot_delay() - vr = XRV(args.hostname, args.username, args.password, args.connection_mode, args.vcpu, args.ram) + vr = XRV(args.hostname, args.username, args.password, args.connection_mode) vr.start() \ No newline at end of file diff --git a/common/vrnetlab.py b/common/vrnetlab.py index 3a5c4d40..7222d18a 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -215,7 +215,7 @@ def start(self): for var, value in os.environ.items(): self.logger.info(f"{var}: {value}") - self.logger.info(f"Launching {self.__class__.__name__} with {self.smp} and {self.ram}M of RAM.") + self.logger.info(f"Launching {self.__class__.__name__} with {self.smp} SMP/VCPU and {self.ram} M of RAM.") self.start_time = datetime.datetime.now() cmd = list(self.qemu_args) From 89277f7318c7d0bb29c2efe0abf7ee8272b02611 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 11:57:34 +0000 Subject: [PATCH 29/34] Override smp property in XRv9k to set single socket config --- cisco/xrv9k/docker/launch.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index f381d683..32923900 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -252,6 +252,18 @@ def apply_config(self): self.logger.error(f"Failed to load startup configuration.") return + @property + def smp(self): + """ + Read SMP parameter (e.g. number of CPU cores) from the QEMU_SMP environment variable. + If the QEMU_SMP parameter is not set, the default value is used. + Should be provided as a number, e.g. 2 + """ + + if "QEMU_SMP" in os.environ: + return str(f'cores={os.getenv("QEMU_SMP")},threads=1,sockets=1') + + return str(self._smp) class XRv9k(vrnetlab.VR): def __init__(self, hostname, username, password, nics, conn_mode): From 9a9bf7e15a924c16cd5b4d11f88bd498c83818ef Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Fri, 22 Nov 2024 14:09:06 +0200 Subject: [PATCH 30/34] added scrapli community --- vrnetlab-base.dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vrnetlab-base.dockerfile b/vrnetlab-base.dockerfile index 3e61c07a..fa954754 100644 --- a/vrnetlab-base.dockerfile +++ b/vrnetlab-base.dockerfile @@ -24,4 +24,6 @@ RUN apt-get update -qy \ genisoimage \ && rm -rf /var/lib/apt/lists/* -RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.post1.zip --break-system-packages +RUN pip install https://github.com/carlmontanari/scrapli/archive/refs/tags/2024.07.30.post1.zip \ + https://github.com/scrapli/scrapli_community/archive/refs/tags/2024.07.30.zip --break-system-packages + From ce49fd94694c3dbdad44d75b21257dfdaaac7e05 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 22 Nov 2024 12:10:29 +0000 Subject: [PATCH 31/34] Update other Dockefiles to use new base image. --- aoscx/docker/Dockerfile | 27 +-------------------- cmglinux/docker/Dockerfile | 26 +-------------------- dell_sonic/docker/Dockerfile | 20 +--------------- fortigate/docker/Dockerfile | 31 +------------------------ freebsd/docker/Dockerfile | 27 +-------------------- ftosv/docker/Dockerfile | 26 +-------------------- huawei_vrp/docker/Dockerfile | 18 +------------- juniper/vjunosevolved/docker/Dockerfile | 25 +------------------- juniper/vjunosrouter/docker/Dockerfile | 24 +------------------ juniper/vjunosswitch/docker/Dockerfile | 24 +------------------ juniper/vmx/docker/Dockerfile | 22 +----------------- juniper/vqfx/docker/Dockerfile | 19 +-------------- juniper/vsrx/docker/Dockerfile | 20 +--------------- ocnos/docker/Dockerfile | 26 +-------------------- openbsd/docker/Dockerfile | 27 +-------------------- openwrt/docker/Dockerfile | 21 +---------------- pan/docker/Dockerfile | 28 +--------------------- routeros/docker/Dockerfile | 27 +-------------------- sonic/docker/Dockerfile | 20 +--------------- ubuntu/docker/Dockerfile | 24 +------------------ veos/docker/Dockerfile | 28 +--------------------- vrp/docker/Dockerfile | 19 +-------------- vsr1000/docker/Dockerfile | 19 +-------------- 23 files changed, 23 insertions(+), 525 deletions(-) diff --git a/aoscx/docker/Dockerfile b/aoscx/docker/Dockerfile index b12bb5ed..ad81d289 100644 --- a/aoscx/docker/Dockerfile +++ b/aoscx/docker/Dockerfile @@ -1,29 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Stefano Sasso - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - telnet \ - ftp \ - qemu-system-x86=1:5.2+dfsg-11+deb11u2 \ - qemu-utils=1:5.2+dfsg-11+deb11u2 \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/cmglinux/docker/Dockerfile b/cmglinux/docker/Dockerfile index 956ffeb5..e80fbe2f 100644 --- a/cmglinux/docker/Dockerfile +++ b/cmglinux/docker/Dockerfile @@ -1,30 +1,6 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 -ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - genisoimage \ - python3-yaml \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages - ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/dell_sonic/docker/Dockerfile b/dell_sonic/docker/Dockerfile index 384d3bb6..fb02eed1 100644 --- a/dell_sonic/docker/Dockerfile +++ b/dell_sonic/docker/Dockerfile @@ -1,22 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - python3-ipy \ - qemu-kvm \ - qemu-utils \ - socat \ - ssh \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/fortigate/docker/Dockerfile b/fortigate/docker/Dockerfile index 3bbe361e..42646fda 100644 --- a/fortigate/docker/Dockerfile +++ b/fortigate/docker/Dockerfile @@ -1,35 +1,6 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - iptables \ - telnet \ - genisoimage \ - dos2unix \ - vim \ - curl \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE - - COPY *.py / COPY $IMAGE / EXPOSE 22 161/udp 830 5000 10000-10099 3443 80 443 diff --git a/freebsd/docker/Dockerfile b/freebsd/docker/Dockerfile index 1f66471d..820c70f2 100644 --- a/freebsd/docker/Dockerfile +++ b/freebsd/docker/Dockerfile @@ -1,31 +1,6 @@ -FROM debian:bookworm-slim +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 -ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - cloud-utils \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages - ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/ftosv/docker/Dockerfile b/ftosv/docker/Dockerfile index d2b6b6ad..4819516e 100644 --- a/ftosv/docker/Dockerfile +++ b/ftosv/docker/Dockerfile @@ -1,28 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL maintainer="Kristian Larsson " -LABEL maintainer="Roman Dodin " -LABEL maintainer="Stefano Sasso " - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - dosfstools \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - ssh \ - tcpdump \ - qemu-kvm \ - qemu-utils \ - inetutils-ping \ - dnsutils \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/huawei_vrp/docker/Dockerfile b/huawei_vrp/docker/Dockerfile index 45577335..18c175a6 100644 --- a/huawei_vrp/docker/Dockerfile +++ b/huawei_vrp/docker/Dockerfile @@ -1,20 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - iproute2 \ - python3 \ - socat \ - qemu-kvm \ - qemu-utils \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/juniper/vjunosevolved/docker/Dockerfile b/juniper/vjunosevolved/docker/Dockerfile index 37b4f96d..4453c597 100644 --- a/juniper/vjunosevolved/docker/Dockerfile +++ b/juniper/vjunosevolved/docker/Dockerfile @@ -1,27 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev,vista@birb.network" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - dosfstools \ - bridge-utils \ - iproute2 \ - python3 \ - python3-passlib \ - socat \ - ssh \ - qemu-kvm \ - qemu-utils \ - inetutils-ping \ - dnsutils \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/juniper/vjunosrouter/docker/Dockerfile b/juniper/vjunosrouter/docker/Dockerfile index a1afc75c..4453c597 100644 --- a/juniper/vjunosrouter/docker/Dockerfile +++ b/juniper/vjunosrouter/docker/Dockerfile @@ -1,26 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev,vista@birb.network" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - dosfstools \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - ssh \ - qemu-kvm \ - qemu-utils \ - inetutils-ping \ - dnsutils \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/juniper/vjunosswitch/docker/Dockerfile b/juniper/vjunosswitch/docker/Dockerfile index a1afc75c..4453c597 100644 --- a/juniper/vjunosswitch/docker/Dockerfile +++ b/juniper/vjunosswitch/docker/Dockerfile @@ -1,26 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev,vista@birb.network" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - dosfstools \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - ssh \ - qemu-kvm \ - qemu-utils \ - inetutils-ping \ - dnsutils \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/juniper/vmx/docker/Dockerfile b/juniper/vmx/docker/Dockerfile index ef4a01e3..be364af7 100644 --- a/juniper/vmx/docker/Dockerfile +++ b/juniper/vmx/docker/Dockerfile @@ -1,24 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - procps \ - openvswitch-switch \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 COPY vmx /vmx COPY *.py / diff --git a/juniper/vqfx/docker/Dockerfile b/juniper/vqfx/docker/Dockerfile index b50e5229..25ed4b6e 100644 --- a/juniper/vqfx/docker/Dockerfile +++ b/juniper/vqfx/docker/Dockerfile @@ -1,21 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - procps \ - tcpdump \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG RE_IMAGE ARG PFE_IMAGE diff --git a/juniper/vsrx/docker/Dockerfile b/juniper/vsrx/docker/Dockerfile index 958ed27f..9a4a0234 100644 --- a/juniper/vsrx/docker/Dockerfile +++ b/juniper/vsrx/docker/Dockerfile @@ -1,22 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev,vista@birb.network" - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install --no-install-recommends -y \ - bridge-utils \ - iproute2 \ - python3 \ - socat \ - qemu-kvm \ - qemu-utils \ - genisoimage \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/ocnos/docker/Dockerfile b/ocnos/docker/Dockerfile index a13cf7e7..32854cf0 100644 --- a/ocnos/docker/Dockerfile +++ b/ocnos/docker/Dockerfile @@ -1,28 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - iptables \ - nftables \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/openbsd/docker/Dockerfile b/openbsd/docker/Dockerfile index ceeef1ef..820c70f2 100644 --- a/openbsd/docker/Dockerfile +++ b/openbsd/docker/Dockerfile @@ -1,31 +1,6 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 -ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - cloud-utils \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages - ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/openwrt/docker/Dockerfile b/openwrt/docker/Dockerfile index 27f15b97..7e15c4b7 100644 --- a/openwrt/docker/Dockerfile +++ b/openwrt/docker/Dockerfile @@ -1,26 +1,7 @@ -FROM alpine:3.18 -# FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Andreas Cymbal takalele@konnex.me - -RUN apk add --no-cache bash \ - qemu-system-x86_64 \ - qemu-img \ - socat \ - net-tools \ - iproute2 \ - bridge-utils \ - python3 \ - py3-click \ - nano \ - vim \ - git \ - py3-pip && ln -sf python3 /usr/bin/python - +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 RUN pip3 install --no-cache --upgrade pip setuptools RUN pip install IPy -RUN pip install git+https://github.com/carlmontanari/scrapli -# might need to add -- ARG IMAGE COPY $IMAGE* / diff --git a/pan/docker/Dockerfile b/pan/docker/Dockerfile index 3c698910..4819516e 100644 --- a/pan/docker/Dockerfile +++ b/pan/docker/Dockerfile @@ -1,30 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL maintainer="Kristian Larsson " -LABEL maintainer="Roman Dodin " - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - iptables \ - nftables \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/routeros/docker/Dockerfile b/routeros/docker/Dockerfile index 861421ea..fe123020 100644 --- a/routeros/docker/Dockerfile +++ b/routeros/docker/Dockerfile @@ -1,29 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL org.opencontainers.image.authors="roman@dodin.dev" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - ftp \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/sonic/docker/Dockerfile b/sonic/docker/Dockerfile index 384d3bb6..fb02eed1 100644 --- a/sonic/docker/Dockerfile +++ b/sonic/docker/Dockerfile @@ -1,22 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get install -y --no-install-recommends \ - bridge-utils \ - iproute2 \ - python3-ipy \ - qemu-kvm \ - qemu-utils \ - socat \ - ssh \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/ubuntu/docker/Dockerfile b/ubuntu/docker/Dockerfile index df408463..00649405 100644 --- a/ubuntu/docker/Dockerfile +++ b/ubuntu/docker/Dockerfile @@ -1,29 +1,7 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 -ARG DEBIAN_FRONTEND=noninteractive ARG DISK_SIZE=4G -RUN apt-get update -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - socat \ - qemu-kvm \ - tcpdump \ - ssh \ - inetutils-ping \ - dnsutils \ - iptables \ - nftables \ - telnet \ - cloud-utils \ - sshpass \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages - ARG IMAGE COPY $IMAGE* / COPY *.py / diff --git a/veos/docker/Dockerfile b/veos/docker/Dockerfile index 3c698910..4819516e 100644 --- a/veos/docker/Dockerfile +++ b/veos/docker/Dockerfile @@ -1,30 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -LABEL maintainer="Kristian Larsson " -LABEL maintainer="Roman Dodin " - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - tcpdump \ - tftpd-hpa \ - ssh \ - inetutils-ping \ - dnsutils \ - openvswitch-switch \ - iptables \ - nftables \ - telnet \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / diff --git a/vrp/docker/Dockerfile b/vrp/docker/Dockerfile index 1b8995f0..a7ee27e7 100644 --- a/vrp/docker/Dockerfile +++ b/vrp/docker/Dockerfile @@ -1,21 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Kristian Larsson - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - qemu-kvm \ - bridge-utils \ - socat \ - iproute2 \ - python3-ipy \ - python3-pexpect \ - ssh \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE / diff --git a/vsr1000/docker/Dockerfile b/vsr1000/docker/Dockerfile index 615baf3d..78bd905b 100644 --- a/vsr1000/docker/Dockerfile +++ b/vsr1000/docker/Dockerfile @@ -1,21 +1,4 @@ -FROM public.ecr.aws/docker/library/debian:bookworm-slim -MAINTAINER Kristian Larsson - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update -qy \ - && apt-get upgrade -qy \ - && apt-get install -y \ - bridge-utils \ - iproute2 \ - python3-ipy \ - socat \ - qemu-kvm \ - git \ - python3-pip \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install git+https://github.com/carlmontanari/scrapli --break-system-packages +FROM ghcr.io/srl-labs/vrnetlab-base:0.0.1 ARG IMAGE COPY $IMAGE* / From e46c174ca40e79e92d25f7987313b3c65418da49 Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Sat, 23 Nov 2024 05:03:32 +0000 Subject: [PATCH 32/34] Allow custom SMP for XRv9k --- cisco/xrv9k/docker/launch.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cisco/xrv9k/docker/launch.py b/cisco/xrv9k/docker/launch.py index 32923900..f7b0aab5 100755 --- a/cisco/xrv9k/docker/launch.py +++ b/cisco/xrv9k/docker/launch.py @@ -160,6 +160,7 @@ def bootstrap_spin(self): self.running = True return if ridx == 2 and self.install_mode: + self.logger.info(f"Installation completed, took {datetime.datetime.now() - self.start_time}") self.running = True return @@ -261,7 +262,12 @@ def smp(self): """ if "QEMU_SMP" in os.environ: - return str(f'cores={os.getenv("QEMU_SMP")},threads=1,sockets=1') + qemu_smp = os.getenv("QEMU_SMP") + # if the user has supplied only a core count set the correct smp + if re.match('^[0-9]+$', str(qemu_smp)): + return str(f'cores={qemu_smp},threads=1,sockets=1') + else: + return str(qemu_smp) return str(self._smp) @@ -290,7 +296,6 @@ def install(self): xrv.work() time.sleep(2) xrv.stop() - self.logger.info("Installation complete") if __name__ == "__main__": From 0710b1de703cc489e304e3f6f04f5923460620aa Mon Sep 17 00:00:00 2001 From: kaelemc <62122480+kaelemc@users.noreply.github.com.> Date: Fri, 29 Nov 2024 11:50:41 +0000 Subject: [PATCH 33/34] Set "scrapli" logger to info (instead of root logger) --- common/vrnetlab.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/vrnetlab.py b/common/vrnetlab.py index 7222d18a..40b0a042 100644 --- a/common/vrnetlab.py +++ b/common/vrnetlab.py @@ -95,12 +95,12 @@ def __init__( self.logger.setLevel(logging.DEBUG) """ - Configure root logger to only be INFO level. - Scrapli uses root logger by default, and - will write all channel input as DEBUG logs. + Configure Scrapli logger to only be INFO level. + Scrapli uses 'scrapli' logger by default, and + will write all channel i/o as DEBUG logs. """ - root_logger = logging.getLogger() - root_logger.setLevel(logging.INFO) + scrapli_logger = logging.getLogger("scrapli") + scrapli_logger.setLevel(logging.INFO) # init scrapli self.scrapli_dev = { @@ -588,7 +588,7 @@ def wait_write(self, cmd, wait="__defaultpattern__", con=None, clean_buffer=None time.sleep(0.1) # don't write to the console too fast self.print(b"\n") - self.logger.info(f"writing to console: '{cmd}'") + self.logger.info(f"Writing to console: '{cmd}'") self.tn.channel.write(f"{cmd}\r") def expect(self, regex_list, timeout=None): From b181cd418c80e7790948ddfd6518e353c25a5b10 Mon Sep 17 00:00:00 2001 From: Kaelem <62122480+kaelemc@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:12:15 +1300 Subject: [PATCH 34/34] Fix `makefile-install.include` image naming schema (#283) --- makefile-install.include | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/makefile-install.include b/makefile-install.include index 2f2bf9ae..bf681f86 100644 --- a/makefile-install.include +++ b/makefile-install.include @@ -31,18 +31,18 @@ docker-pre-build: -cat cidfile | xargs --no-run-if-empty docker rm -f -rm cidfile - -docker tag $(REGISTRY)vr-$(VR_NAME):$(VERSION) $(REGISTRY)vr-$(VR_NAME):$(VERSION)-previous-build + -docker tag $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION) $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)-previous-build docker-build: docker-build-common - -docker inspect --format '{{.RootFS.Layers}}' $(REGISTRY)vr-$(VR_NAME):$(VERSION)-previous-build | tr -d '][' | awk '{ $$(NF)=""; print }' > built-image-sha-previous - docker inspect --format '{{.RootFS.Layers}}' $(REGISTRY)vr-$(VR_NAME):$(VERSION) | tr -d '][' > built-image-sha-current + -docker inspect --format '{{.RootFS.Layers}}' $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)-previous-build | tr -d '][' | awk '{ $$(NF)=""; print }' > built-image-sha-previous + docker inspect --format '{{.RootFS.Layers}}' $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION) | tr -d '][' > built-image-sha-current if [ "$$(cat built-image-sha-previous | sed -e 's/[[:space:]]*$$//')" = "$$(cat built-image-sha-current)" ]; then echo "Previous image is the same as current, retagging!"; \ - docker tag $(REGISTRY)vr-$(VR_NAME):$(VERSION)-previous-build $(REGISTRY)vr-$(VR_NAME):$(VERSION) || true; \ + docker tag $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)-previous-build $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION) || true; \ else \ echo "Current build differ from previous, running install!"; \ - docker run --cidfile cidfile --privileged $(REGISTRY)vr-$(VR_NAME):$(VERSION) --trace --install; \ - docker commit --change='ENTRYPOINT ["/launch.py"]' $$(cat cidfile) $(REGISTRY)vr-$(VR_NAME):$(VERSION); \ + docker run --cidfile cidfile --privileged $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION) --trace --install; \ + docker commit --change='ENTRYPOINT ["/launch.py"]' $$(cat cidfile) $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION); \ docker rm -f $$(cat cidfile); \ fi - docker rmi -f $(REGISTRY)vr-$(VR_NAME):$(VERSION)-previous-build || true + docker rmi -f $(REGISTRY)$(IMG_VENDOR)_$(IMG_NAME):$(VERSION)-previous-build || true rm built-image-sha*