Skip to content

Commit

Permalink
Add support for EXOS (#271)
Browse files Browse the repository at this point in the history
* Add support for EXOS

* change startup config file type and remove unnecessary use of fstring

---------

Co-authored-by: Rafael Brandao <rafael.brandao@pop-go.rnp.br>
  • Loading branch information
rafabr and Rafael Brandao authored Nov 8, 2024
1 parent cea756c commit 75d5060
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 0 deletions.
11 changes: 11 additions & 0 deletions exos/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
VENDOR=Extreme
NAME=EXOS
IMAGE_FORMAT=qcow2
IMAGE_GLOB=*.qcow2

# match versions like:
# v15.3.2.11
VERSION=$(shell echo $(IMAGE) | sed -n 's/^EXOS-VM_\(.*\)\.qcow.*/\1/p' | cut -d'-' -f1)

-include ../makefile-sanity.include
-include ../makefile.include
21 changes: 21 additions & 0 deletions exos/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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 \
&& rm -rf /var/lib/apt/lists/*

ARG IMAGE
COPY $IMAGE* /
COPY *.py /

EXPOSE 22 80 161/udp 443 830
HEALTHCHECK CMD ["/healthcheck.py"]
ENTRYPOINT ["/launch.py"]
175 changes: 175 additions & 0 deletions exos/docker/launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env python3

import datetime
import logging
import re
import signal
import sys
import time
import os

import vrnetlab

STARTUP_CONFIG_FILE = "/config/startup-config.xsf"


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 EXOS_vm(vrnetlab.VM):
def __init__(self, username, password, hostname, conn_mode):
disk_image = None
for e in sorted(os.listdir("/")):
if not disk_image and re.search(".qcow2$", e):
disk_image = "/" + e

super(EXOS_vm, self).__init__(
username,
password,
disk_image=disk_image,
ram=512,
cpu="core2duo",
driveif="ide",
)

self.hostname = hostname
self.conn_mode = conn_mode
self.num_nics = 13
self.nic_type = "rtl8139"

def bootstrap_spin(self):
""" This function should be called periodically to do work.
"""

if self.spins > 300:
# too many spins with no result -> give up
self.stop()
self.start()
return

(ridx, match, res) = self.tn.expect([rb'node is now available for login.',
rb'\[[yY]\/[nN]\/q\]'], 1)

if match: # got a match!
if ridx == 0:
time.sleep(1)
self.wait_write(cmd='', wait=None)
self.wait_write(cmd='admin', wait='login:')
self.wait_write(cmd='', wait='password:')
else:
self.wait_write(cmd='q', wait=None)
self.wait_write(cmd='', wait='#')
self.logger.info("Found config prompt")
# run main config!
self.logger.info("Running bootstrap_config()")
self.bootstrap_config()
self.startup_config()
(ridx, match, res) = self.tn.expect([rb'node is now available for login.'],1)
time.sleep(1)
# 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 != b'':
self.logger.trace("OUTPUT: %s" % res.decode())
# reset spins if we saw some output
self.spins = 0

self.spins += 1

return

def bootstrap_config(self):
""" Do the actual bootstrap config
"""
self.wait_write(cmd=f"configure snmp sysName {self.hostname}", wait=None)
self.wait_write(cmd="configure vlan Mgmt ipaddress 10.0.0.15/24", wait="#")
self.wait_write(cmd="configure iproute add default 10.0.0.2 vr VR-Mgmt", wait="#")
if self.username == 'admin':
self.wait_write(cmd="configure account admin password", wait="#")
self.wait_write(cmd="", wait="Current user's password:")
self.wait_write(cmd=self.password, wait="New password:")
self.wait_write(cmd=self.password, wait="Reenter password:")
else:
self.wait_write(cmd=f"create account admin {self.username} {self.password}", wait="#")
self.wait_write(cmd="disable cli prompting", wait="#")
self.wait_write(cmd="enable ssh2", wait="#")
self.wait_write(cmd="save", wait="#")

def startup_config(self):
if not os.path.exists(STARTUP_CONFIG_FILE):
self.logger.trace(f"Startup config file {STARTUP_CONFIG_FILE} not found")
self.wait_write(cmd="enable cli prompting", wait="#")
return
vrnetlab.run_command(["cp", STARTUP_CONFIG_FILE, "/tftpboot/containerlab.xsf"])
self.wait_write(cmd="tftp get 10.0.0.2 vr VR-Mgmt containerlab.xsf", wait=None)
self.wait_write(cmd="load script containerlab.xsf", wait="#")
self.wait_write(cmd="save", wait="#")
self.wait_write(cmd="enable cli prompting", wait="#")


class EXOS(vrnetlab.VR):
def __init__(self, hostname, username, password, conn_mode):
super(EXOS, self).__init__(username, password)
self.vms = [EXOS_vm(username, password,hostname, conn_mode)]


if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument(
"--trace", action="store_true", help="enable trace level logging"
)
parser.add_argument('--hostname', default='vr-exos', help='Router hostname')
parser.add_argument('--username', default='vrnetlab', help='Username')
parser.add_argument('--password', default='VR-netlab9', help='Password')
parser.add_argument(
"--connection-mode",
default="tc",
help="Connection mode to use in the datapath",
)

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)

vr = EXOS(
args.hostname, args.username, args.password, conn_mode=args.connection_mode
)
vr.start()

0 comments on commit 75d5060

Please sign in to comment.