Skip to content

Commit

Permalink
Add basic nested vm testcase
Browse files Browse the repository at this point in the history
  • Loading branch information
Divyansh authored and dsrivastavv committed Nov 26, 2021
1 parent 9564d60 commit 8fb99d0
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 56 deletions.
6 changes: 6 additions & 0 deletions lisa/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .blkid import Blkid
from .chrony import Chrony
from .date import Date
from .df import Df
from .dhclient import Dhclient
from .dmesg import Dmesg
from .docker import Docker
Expand Down Expand Up @@ -39,9 +40,11 @@
from .nvmecli import Nvmecli
from .parted import Parted
from .pgrep import Pgrep, ProcessInfo
from .qemu import Qemu
from .reboot import Reboot
from .service import Service
from .ssh import Ssh
from .sshpass import Sshpass
from .swap import Swap
from .swapon import SwapOn
from .sysctl import Sysctl
Expand All @@ -57,6 +60,7 @@
"Cat",
"Chrony",
"Date",
"Df",
"Dhclient",
"Dmesg",
"Docker",
Expand Down Expand Up @@ -94,11 +98,13 @@
"Parted",
"Pgrep",
"ProcessInfo",
"Qemu",
"Reboot",
"Sed",
"Uname",
"Service",
"Ssh",
"Sshpass",
"Swap",
"SwapOn",
"Sysctl",
Expand Down
1 change: 1 addition & 0 deletions lisa/tools/blkid.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@


class PartitionInfo(object):
# TODO: Merge with lsblk.PartitionInfo
def __init__(
self,
name: str,
Expand Down
56 changes: 14 additions & 42 deletions lisa/tools/df.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,11 @@
# Licensed under the MIT license.

import re
from dataclasses import dataclass
from typing import List, Optional

from lisa.executable import Tool
from lisa.util import find_patterns_in_lines


@dataclass
class PartitionInfo(Tool):
partition_name: str = ""
blocks: int = 0
used: int = 0
total_size: int = 0
percentage_used: int = 0
mountpoint: str = ""

def __init__(
self,
partition_name: str,
blocks: int,
used: int,
total_size: int,
percentage_used: int,
mountpoint: str,
):
self.partition_name = partition_name
self.blocks = blocks
self.used = used
self.total_size = total_size
self.percentage_used = percentage_used
self.mountpoint = mountpoint
from lisa.tools.lsblk import PartitionInfo
from lisa.util import find_patterns_groups_in_lines


class Df(Tool):
Expand All @@ -49,33 +23,31 @@ def command(self) -> str:
def can_install(self) -> bool:
return True

def get_partition_information(self) -> List[PartitionInfo]:
def get_partitions(self, force_run: bool = False) -> List[PartitionInfo]:
# run df and parse the output
# The output is in the following format:
# <Filesystem> <1K-blocks> <Used> <Available> <Use%> <Mounted on>
# Note : <Used> and <Available> are in 1-k blocks, not in bytes
output = self.run(sudo=True).stdout
output = self.run(sudo=True, force_run=force_run).stdout
partition_info = []
df_entries = find_patterns_in_lines(output, [self._DF_ENTRY_REGEX])[0]
df_entries = find_patterns_groups_in_lines(output, [self._DF_ENTRY_REGEX])[0]
for df_entry in df_entries:
partition_info.append(
PartitionInfo(
partition_name=df_entry[0],
blocks=int(df_entry[1]),
used=int(df_entry[2]),
total_size=int(df_entry[3]),
percentage_used=int(df_entry[4]),
mountpoint=df_entry[5],
name=df_entry["name"],
mountpoint=df_entry["mountpoint"],
available_blocks=int(df_entry["blocks"]),
used_blocks=int(df_entry["used"]),
total_blocks=int(df_entry["total"]),
percentage_blocks_used=int(df_entry["percentage_use"]),
)
)
return partition_info

def get_partition_with_mountpoint(
self, partition_mountpoint: str
) -> Optional[PartitionInfo]:
def get_partition_by_mountpoint(self, mountpoint: str) -> Optional[PartitionInfo]:
# get `df` entry for the partition with the given mountpoint
df_partition_info = self.get_partition_information()
df_partition_info = self.get_partitions()
for partition in df_partition_info:
if partition.mountpoint == partition_mountpoint:
if partition.mountpoint == mountpoint:
return partition
return None
46 changes: 34 additions & 12 deletions lisa/tools/lsblk.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,39 @@
from typing import List

from lisa.executable import Tool
from lisa.util import find_patterns_in_lines
from lisa.util import find_patterns_groups_in_lines


@dataclass
class PartitionInfo(Tool):
class PartitionInfo(object):
name: str = ""
mountpoint: str = ""
size: int = 0
type: str = ""
mountpoint: str = ""
available_blocks: int = 0
used_blocks: int = 0
total_blocks: int = 0
percentage_blocks_used: int = 0

def __init__(self, name: str, size: int, type: str, mountpoint: str):
def __init__(
self,
name: str,
mountpoint: str,
size: int = 0,
type: str = "",
available_blocks: int = 0,
used_blocks: int = 0,
total_blocks: int = 0,
percentage_blocks_used: int = 0,
):
self.name = name
self.mountpoint = mountpoint
self.size = size
self.type = type
self.mountpoint = mountpoint
self.available_blocks = available_blocks
self.used_blocks = used_blocks
self.total_blocks = total_blocks
self.percentage_blocks_used = percentage_blocks_used


class Lsblk(Tool):
Expand All @@ -34,18 +52,22 @@ class Lsblk(Tool):
def command(self) -> str:
return "lsblk"

def get_partition_information(self) -> List[PartitionInfo]:
def get_partitions(self, force_run: bool = False) -> List[PartitionInfo]:
# parse output of lsblk
output = self.run("-b -P -o NAME,SIZE,TYPE,MOUNTPOINT", sudo=True).stdout
output = self.run(
"-b -P -o NAME,SIZE,TYPE,MOUNTPOINT", sudo=True, force_run=force_run
).stdout
partition_info = []
lsblk_entries = find_patterns_in_lines(output, [self._LSBLK_ENTRY_REGEX])[0]
lsblk_entries = find_patterns_groups_in_lines(
output, [self._LSBLK_ENTRY_REGEX]
)[0]
for lsblk_entry in lsblk_entries:
partition_info.append(
PartitionInfo(
name=lsblk_entry[0],
size=int(lsblk_entry[1]),
type=lsblk_entry[2],
mountpoint=lsblk_entry[3],
name=lsblk_entry["name"],
size=int(lsblk_entry["size"]),
type=lsblk_entry["type"],
mountpoint=lsblk_entry["mountpoint"],
)
)
return partition_info
6 changes: 6 additions & 0 deletions lisa/tools/lscpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ def get_cpu_info(self) -> List[CPUInfo]:
)
return output

def is_virtualization_enabled(self) -> bool:
result = self.run(sudo=True).stdout
if ("VT-x" in result) or ("AMD-V" in result):
return True
return False


class WindowsLscpu(Lscpu):
@property
Expand Down
6 changes: 4 additions & 2 deletions lisa/tools/make.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ def _install(self) -> bool:
posix_os.install_packages([self, Gcc])
return self._check_exists()

def make_install(self, cwd: PurePath, timeout: int = 600) -> None:
def make_install(
self, cwd: PurePath, timeout: int = 600, sudo: bool = True
) -> None:
self.make(arguments="", cwd=cwd, timeout=timeout)

# install with sudo
self.make(arguments="install", cwd=cwd, sudo=True, timeout=timeout)
self.make(arguments="install", cwd=cwd, timeout=timeout, sudo=sudo)

def make(
self,
Expand Down
1 change: 1 addition & 0 deletions lisa/tools/mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

@dataclass
class PartitionInfo(object):
# TODO: Merge with lsblk.PartitionInfo
name: str
disk: str
mount_point: str
Expand Down
89 changes: 89 additions & 0 deletions lisa/tools/qemu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

from typing import Any

from assertpy.assertpy import assert_that

from lisa.executable import Tool
from lisa.operating_system import Fedora, Posix
from lisa.tools import Lsmod


class Qemu(Tool):

QEMU_INSTALL_LOCATIONS = ["qemu-system-x86_64", "qemu-kvm", "/usr/libexec/qemu-kvm"]

@property
def command(self) -> str:
return self._qemu_command

def can_install(self) -> bool:
return True

def create_vm(
self,
port: int,
guest_image_path: str,
) -> None:
# start vm on the current node
# port : port of the host vm mapped to the guest's ssh port
# guest_image_path : path of the guest image

# Run qemu with following parameters:
# -m: memory size
# -smp: SMP system with `n` CPUs
# -hda : guest image path
# -device: add a device to the VM
# -netdev: Configure user mode network backend and setup port forwarding
# -enable-kvm: enable kvm
# -display: enable or disable display
# -demonize: run in background
self.run(
f"-smp 2 -m 2048 -hda {guest_image_path} "
"-device e1000,netdev=user.0 "
f"-netdev user,id=user.0,hostfwd=tcp::{port}-:22 "
"-enable-kvm -display none -daemonize",
sudo=True,
shell=True,
expected_exit_code=0,
expected_exit_code_failure_message=f"Unable to start VM {guest_image_path}",
)

# update firewall rules
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/using-and-configuring-firewalld_configuring-and-managing-networking # noqa E501
if isinstance(self.node.os, Fedora):
self.node.execute(
f"firewall-cmd --permanent --add-port={port}/tcp", sudo=True
)
self.node.execute("firewall-cmd --reload", sudo=True)

def _initialize(self, *args: Any, **kwargs: Any) -> None:
self._qemu_command = "qemu-system-x86_64"

def _install(self) -> bool:
assert isinstance(self.node.os, Posix)

# install qemu
self.node.os.install_packages("qemu-kvm")

# verify that kvm is enabled
self._is_kvm_successfully_enabled()

# find correct command for qemu
for location in self.QEMU_INSTALL_LOCATIONS:
self._qemu_command = location
if self._check_exists():
return True

return False

def _is_kvm_successfully_enabled(self) -> None:
# verify that kvm module is loaded
lsmod_output = self.node.tools[Lsmod].run().stdout
is_kvm_successfully_enabled = (
"kvm_intel" in lsmod_output or "kvm_amd" in lsmod_output
)
assert_that(
is_kvm_successfully_enabled, f"KVM could not be enabled : {lsmod_output}"
).is_true()
Loading

0 comments on commit 8fb99d0

Please sign in to comment.