Skip to content

Commit

Permalink
Introducing Ubuntu support and qemu args (cpu/mem/smp/args) (#198)
Browse files Browse the repository at this point in the history
* add ubuntu support

* add ubuntu to global Makefile

* using env variables for cpu, number of cpus and memory

* use the  cpu, smp and memory options of vrnetlab.VM

* add: allow additional disk using ADD_DISK ENV

* preprend qemu cpu/mem/smp args with qemu prefix

* set bash shell for the user

do not upgrade the base vm
add support for qemu additional args

---------

Co-authored-by: jmussmann <[email protected]>
  • Loading branch information
hellt and jmussmann authored Jun 9, 2024
1 parent 1e1ec73 commit 249b3ca
Show file tree
Hide file tree
Showing 24 changed files with 486 additions and 116 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
IMAGES_DIR=
VRS = vr-xcon vr-bgp csr nxos routeros sros veos vjunosswitch vjunosevolved vmx vsr1000 vqfx vrp xrv xrv9k vsrx openbsd ftdv freebsd
VRS = vr-xcon vr-bgp csr nxos routeros sros veos vjunosswitch vjunosevolved vmx vsr1000 vqfx vrp xrv xrv9k vsrx openbsd ftdv freebsd ubuntu
VRS_PUSH = $(VRS:=-push)

.PHONY: all $(VRS) $(VRS_PUSH)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Since the changes we made in this fork are VM specific, we added a few popular r
* Nokia SR OS
* OpenBSD
* FreeBSD
* Ubuntu

The rest are left untouched and can be contributed back by the community.

Expand Down
4 changes: 1 addition & 3 deletions aoscx/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@ def __init__(self, hostname, username, password, conn_mode):
logging.getLogger().info("Disk image was not found")
exit(1)
super(AOSCX_vm, self).__init__(
username, password, disk_image=disk_image, ram=4096
username, password, disk_image=disk_image, ram=4096, cpu="host,level=9", smp="2"
)
self.hostname = hostname
self.conn_mode = conn_mode
self.num_nics = 20
self.nic_type = "virtio-net-pci"
self.qemu_args.extend(["-cpu", "host,level=9"])
self.qemu_args.extend(["-smp", "2"])

def bootstrap_spin(self):
"""This function should be called periodically to do work."""
Expand Down
143 changes: 85 additions & 58 deletions common/vrnetlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ def __init__(
ram=4096,
driveif="ide",
provision_pci_bus=True,
cpu="host",
smp=1,
):
self.logger = logging.getLogger()

Expand All @@ -91,6 +93,10 @@ def __init__(
self.p = None
self.tn = None

self._ram = ram
self._cpu = cpu
self._smp = smp

# various settings
self.uuid = None
self.fake_start_date = None
Expand Down Expand Up @@ -135,20 +141,30 @@ def __init__(
]
)

self.qemu_args = ["qemu-system-x86_64", "-display", "none", "-machine", "pc"]
self.qemu_args.extend(
["-monitor", "tcp:0.0.0.0:40%02d,server,nowait" % self.num]
)
self.qemu_args.extend(
[
"-m",
str(ram),
"-serial",
"telnet:0.0.0.0:50%02d,server,nowait" % self.num,
"-drive",
f"if={driveif},file={overlay_disk_image}",
]
)
self.qemu_args = [
"qemu-system-x86_64",
"-display",
"none",
"-machine",
"pc",
"-monitor",
f"tcp:0.0.0.0:40{self.num:02d},server,nowait",
"-serial",
f"telnet:0.0.0.0:50{self.num:02d},server,nowait",
"-m", # memory
str(self.ram),
"-cpu", # cpu type
self.cpu,
"-smp",
self.smp, # cpu core configuration
"-drive",
f"if={driveif},file={overlay_disk_image}",
]

# add additional qemu args if they were provided
if self.qemu_additional_args:
self.qemu_args.extend(self.qemu_additional_args)

# enable hardware assist if KVM is available
if os.path.exists("/dev/kvm"):
self.qemu_args.insert(1, "-enable-kvm")
Expand Down Expand Up @@ -706,7 +722,6 @@ def check_qemu(self):
self.stop()
self.start()


@property
def version(self):
"""Read version number from VERSION environment variable
Expand All @@ -719,6 +734,57 @@ def version(self):
return version
raise ValueError("The VERSION environment variable is not set")

@property
def ram(self):
"""
Read memory size from the QEMU_MEMORY environment variable and use it in the qemu parameters for the VM.
If the QEMU_MEMORY environment variable is not set, use the default value.
Should be provided as a number of MB. e.g. 4096.
"""

if "QEMU_MEMORY" in os.environ:
return get_digits(str(os.getenv("QEMU_MEMORY")))

return self._ram

@property
def cpu(self):
"""
Read the CPU type the QEMU_CPU environment variable and use it in the qemu parameters for the VM.
If the QEMU_CPU environment variable is not set, use the default value.
"""

if "QEMU_CPU" in os.environ:
return str(os.getenv("QEMU_CPU"))

return str(self._cpu)

@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(os.getenv("QEMU_SMP"))

return str(self._smp)

@property
def qemu_additional_args(self):
"""
Read additional qemu arguments (e.g. number of CPU cores) from the QEMU_ADDITIONAL_ARGS environment variable.
If the QEMU_ADDITIONAL_ARGS parameter is not set, nothing is added to the default args set.
Should be provided as a space separated list of arguments, e.g. "-machine pc -display none"
"""

if "QEMU_ADDITIONAL_ARGS" in os.environ:
s = str(os.getenv("QEMU_ADDITIONAL_ARGS"))
if s:
return s.split()


class VR:
def __init__(self, username, password):
Expand Down Expand Up @@ -777,49 +843,10 @@ class QemuBroken(Exception):
"""Our Qemu instance is somehow broken"""


# getMem returns the RAM size (in Mb) for a given VM mode.
# RAM can be specified in the variant dict, provided by a user via the custom type definition,
# or set via env vars.
# If set via env vars, the getMem will return this value as the most specific one.
# Otherwise, the ram provided to this function will be converted to Mb and returned.
def getMem(vmMode: str, ram: int) -> int:
if vmMode == "integrated":
# Integrated VM can use both MEMORY and CP_MEMORY env vars
if "MEMORY" in os.environ:
return 1024 * get_digits(os.getenv("MEMORY"))
if "CP_MEMORY" in os.environ:
return 1024 * get_digits(os.getenv("CP_MEMORY"))
if vmMode == "cp":
if "CP_MEMORY" in os.environ:
return 1024 * get_digits(os.getenv("CP_MEMORY"))
if vmMode == "lc":
if "LC_MEMORY" in os.environ:
return 1024 * get_digits(os.getenv("LC_MEMORY"))
return 1024 * int(ram)


# getCpu returns the number of cpu cores for a given VM mode.
# Cpu can be specified in the variant dict, provided by a user via the custom type definition,
# or set via env vars.
# If set via env vars, the function will return this value as the most specific one.
# Otherwise, the number provided to this function via cpu param returned.
def getCpu(vsimMode: str, cpu: int) -> int:
if vsimMode == "integrated":
# Integrated VM can use both MEMORY and CP_MEMORY env vars
if "CPU" in os.environ:
return int(os.getenv("CPU"))
if "CP_CPU" in os.environ:
return int(os.getenv("CP_CPU"))
if vsimMode == "cp":
if "CP_CPU" in os.environ:
return int(os.getenv("CP_CPU"))
if vsimMode == "lc":
if "LC_CPU" in os.environ:
return int(os.getenv("LC_CPU"))
return cpu


# strip all non-numeric characters from a string
def get_digits(input_str: str) -> int:
"""
Strip all non-numeric characters from a string
"""

non_string_chars = re.findall(r"\d", input_str)
return int("".join(non_string_chars))
1 change: 0 additions & 1 deletion fortigate/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ def __init__(self, hostname, username, password, conn_mode):
self.nic_type = "virtio-net-pci"
self.highest_port = 0
self.qemu_args.extend(["-uuid", str(uuid.uuid4())])
self.qemu_args.extend(["-smp", "1", "-cpu", "host"])
self.spins = 0
self.running = None

Expand Down
6 changes: 3 additions & 3 deletions ftdv/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ def trace(self, message, *args, **kws):


class FTDV_vm(vrnetlab.VM):

# FIPS check fails without exposing cpu (ERROR: FIPS Self-Test failure, fipsPostGFSboxKat)
def __init__(
self, hostname, username, password, nics, conn_mode, install_mode=False
self, hostname, username, password, nics, conn_mode, install_mode=False, smp="4,sockets=1,cores=4,threads=1"
):
for e in os.listdir("/"):
if re.search(".qcow2$", e):
Expand All @@ -56,8 +58,6 @@ def __init__(
self.hostname = hostname
self.conn_mode = conn_mode
self.nic_type = "virtio-net-pci"
# FIPS check fails without exposing cpu (ERROR: FIPS Self-Test failure, fipsPostGFSboxKat)
self.qemu_args.extend(["-cpu", "host", "-smp", "4,sockets=1,cores=4,threads=1"])
overlay_disk_image = re.sub(r"(\.[^.]+$)", r"-overlay\1", disk_image)
# boot harddrive first
self.qemu_args.extend(["-boot", "order=c"])
Expand Down
3 changes: 1 addition & 2 deletions ftosv/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(self, hostname, username, password, conn_mode):
logging.getLogger().info("Disk image was not found")
exit(1)
super(FTOS_vm, self).__init__(
username, password, disk_image=disk_image, ram=4096
username, password, disk_image=disk_image, ram=4096, smp="4"
)
self.credentials = [["admin", "admin"]]
self.hostname = hostname
Expand All @@ -57,7 +57,6 @@ def __init__(self, hostname, username, password, conn_mode):
# available OS10 virtualization platform options: S4000,S4128,S5212,S5224,S5248,S6000,S6010
self.num_nics = 56
self.nic_type = "e1000"
self.qemu_args.extend(["-cpu", "host", "-smp", "4"])

overlay_disk_image = re.sub(r"(\.[^.]+$)", r"-overlay\1", disk_image)
# boot harddrive first
Expand Down
3 changes: 1 addition & 2 deletions n9kv/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,13 @@ 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=8192
username, password, disk_image=disk_image, ram=8192, cpu="host,level=9"
)
self.hostname = hostname
self.conn_mode = conn_mode
# mgmt + 128 that show up in the vm, may as well populate them all in vrnetlab right away
self.num_nics = 129
self.nic_type = "e1000"
self.qemu_args.extend(["-cpu", "host,level=9"])

# bios for n9kv
self.qemu_args.extend(["-bios", "/OVMF.fd"])
Expand Down
3 changes: 1 addition & 2 deletions nxos/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ 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
username, password, disk_image=disk_image, ram=4096, smp="2"
)
self.credentials = [["admin", "admin"]]
self.hostname = hostname
self.conn_mode = conn_mode
self.qemu_args.extend(["-cpu", "host", "-smp", "2"])

def bootstrap_spin(self):
"""This function should be called periodically to do work."""
Expand Down
4 changes: 1 addition & 3 deletions ocnos/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,13 @@ def __init__(self, hostname, username, password, conn_mode):
logging.getLogger().info("Disk image was not found")
exit(1)
super(OCNOS_vm, self).__init__(
username, password, disk_image=disk_image, ram=4096
username, password, disk_image=disk_image, ram=4096, cpu="host,level=9", smp="2,sockets=1,cores=1"
)
self.hostname = hostname
self.conn_mode = conn_mode
self.num_nics = 8
self.nic_type = "virtio-net-pci"

self.qemu_args.extend(["-cpu", "host,level=9"])
self.qemu_args.extend(["-smp", "2,sockets=1,cores=1"])

def bootstrap_spin(self):
"""This function should be called periodically to do work."""
Expand Down
4 changes: 1 addition & 3 deletions pan/docker/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,13 @@ def __init__(self, hostname, username, password, conn_mode):
logging.getLogger().info("Disk image was not found")
exit(1)
super(PAN_vm, self).__init__(
username, password, disk_image=disk_image, ram=6144
username, password, disk_image=disk_image, ram=6144, cpu="host,level=9", smp="2,sockets=1,cores=1"
)
self.hostname = hostname
self.conn_mode = conn_mode
# mgmt + 24 that show up in the vm, may as well populate them all in vrnetlab right away
self.num_nics = 25
self.nic_type = "virtio-net-pci"
self.qemu_args.extend(["-cpu", "host,level=9"])
self.qemu_args.extend(["-smp", "2,sockets=1,cores=1"])
# pan wants a uuid it seems (for licensing reasons?!)
self.uuid = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"

Expand Down
Loading

0 comments on commit 249b3ca

Please sign in to comment.