Skip to content

Commit

Permalink
major work on getting tests in place
Browse files Browse the repository at this point in the history
  • Loading branch information
mmguero committed Oct 30, 2024
1 parent 2136723 commit bf2a2a0
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 297 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ This project makes use of:
* [Virter](https://github.com/LINBIT/virter) for the creation and execution of libvirt-based virtual machines running Malcolm
* [pytest](https://docs.pytest.org/en/stable/) for the testing framework

## Package source highlights (under `src/malcolm_test`)
## Package source highlights (under `src/maltest`)

* 🐍 [`malcolm_test.py`](#MalcolmVMInitScript) - A Python script for running Malcolm in a VM with virter (see below)
* 🐍 [`maltest.py`](#MalcolmVMInitScript) - A Python script for running Malcolm in a VM with virter (see below)
* 🗁 `virter/` - A directory structure containing TOML files for [provisioning](https://github.com/LINBIT/virter/blob/master/doc/provisioning.md) the virter VMs in which Malcolm will run. Its subdirectories are arranged thusly:
- 🗁 `debian-12/` - A directory matching the name of the virter image (supplied to [`malcolm_test.py`](#MalcolmVMInitScript) with the `-i`/`--image` argument)
- 🗁 `debian-12/` - A directory matching the name of the virter image (supplied to [`maltest.py`](#MalcolmVMInitScript) with the `-i`/`--image` argument)
+ 🗁 `init/` - TOML files for the initial steps of provisioning the OS (before setting up and starting Malcolm)
+ 🗁 `fini/` - TOML files for the final stages of provisioning the OS (after shutting down Malcolm)
- 🗁 `malcolm-init/` - Distribution-agnostic provisioning TOML files for setting up Malcolm prior to starting it
Expand All @@ -37,14 +37,14 @@ python3 -m pip install -U 'git+https://github.com/idaholab/Malcolm-Test'

## <a name="MalcolmVMInitScript"></a> The `malcolm-test` script

`malcolm_test.py` is a Python script for Linux that uses [virter](https://github.com/LINBIT/virter) (a command line tool for simple creation and cloning of virtual machines) to run an instance of [Malcolm](https://idaholab.github.io/Malcolm/) against which automated system tests can be run.
`maltest.py` is a Python script for Linux that uses [virter](https://github.com/LINBIT/virter) (a command line tool for simple creation and cloning of virtual machines) to run an instance of [Malcolm](https://idaholab.github.io/Malcolm/) against which automated system tests can be run.

When [installed](#Installation) via pip, this script may be executed as `malcolm-test` from the Linux command line.

### Usage

```
usage: malcolm_test.py <arguments>
usage: maltest.py <arguments>
See README.md for usage details.
Expand Down Expand Up @@ -80,7 +80,7 @@ Virtual machine specifications:
--vm-provision-malcolm [true|false]
Perform VM provisioning (Malcolm-specific)
--vm-provision-path <string>
Path containing subdirectories with TOML files for VM provisioning (e.g., /home/tlacuache/.asdf/installs/python/3.12.7/lib/python3.12/site-packages/malcolm_test/virter)
Path containing subdirectories with TOML files for VM provisioning (e.g., /home/tlacuache/.asdf/installs/python/3.12.7/lib/python3.12/site-packages/maltest/virter)
--build-vm <string> The name for a new VM image to build and commit instead of running one
--build-vm-keep-layers [true|false]
Don't remove intermediate layers when building a new VM image
Expand All @@ -98,7 +98,7 @@ Malcolm runtime configuration:
*with INFO-level `-vv` verbosity, output reduced for length*

```
2024-10-25 12:42:51 INFO: /home/user/Malcolm-Test/malcolm_test.py
2024-10-25 12:42:51 INFO: /home/user/Malcolm-Test/maltest.py
2024-10-25 12:42:51 INFO: Arguments: ['-vv', '--rm', '--github-url', 'https://github.com/idaholab/Malcolm', '--github-branch', 'main']
2024-10-25 12:42:51 INFO: Arguments: Namespace(verbose=20, removeAfterExec=True, repoUrl='https://github.com/idaholab/Malcolm', repoBranch='main', vmCpuCount=8, vmMemoryGigabytes=31, vmDiskGigabytes=64, vmImage='debian-12', vmImageUsername='debian', vmNamePrefix='malcolm', vmExistingName='', vmProvision=True, vmProvisionPath='/home/user/Malcolm-Test/virter', containerImageFile='', startMalcolm=True, postInitSleep=30)
2024-10-25 12:42:51 INFO: ['virter', 'vm', 'run', 'debian-12', '--id', '0', '--name', 'malcolm-126', '--vcpus', '8', '--memory', '31GB', '--bootcapacity', '64GB', '--user', 'debian', '--wait-ssh']
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "malcolm-test"
version = "0.1.0"
version = "0.2.0"
authors = [
{ name="Seth Grover", email="[email protected]" },
]
Expand All @@ -29,7 +29,10 @@ Homepage = "https://github.com/idaholab/malcolm-test"
Issues = "https://github.com/idaholab/malcolm-test/issues"

[project.scripts]
malcolm-test = "malcolm_test:main"
malcolm-test = "maltest:main"

[tool.hatch.build.targets.wheel]
packages = ["src/maltest"]

[tool.pytest.ini_options]
pythonpath = "src"
Expand Down
4 changes: 2 additions & 2 deletions src/malcolm_test/__init__.py → src/maltest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
except PackageNotFoundError:
__version__ = None

__all__ = ["main", "MalcolmVM"]
__all__ = ["main"]

from .malcolm_test import main, MalcolmVM
from .maltest import main
308 changes: 308 additions & 0 deletions src/maltest/maltest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import json
import logging
import mmguero
import multiprocessing
import os
import psutil
import pytest
import signal
import sys

from maltest.utils import MalcolmVM, ShuttingDown, set_malcolm_vm_info

###################################################################################################
script_name = os.path.basename(__file__)
script_path = os.path.dirname(os.path.realpath(__file__))


###################################################################################################
# handle sigint/sigterm and set a global shutdown variable
def shutdown_handler(signum, frame):
ShuttingDown[0] = True


###################################################################################################
# main
def main():
parser = argparse.ArgumentParser(
description='\n'.join(
[
'See README.md for usage details.',
]
),
formatter_class=argparse.RawTextHelpFormatter,
add_help=True,
usage=f'{script_name} <arguments>',
)
parser.add_argument(
'--verbose',
'-v',
action='count',
default=1,
help='Increase verbosity (e.g., -v, -vv, etc.)',
)
parser.add_argument(
'-r',
'--rm',
dest='removeAfterExec',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=False,
help="Remove virtual Malcolm instance after execution is complete",
)

repoArgGroup = parser.add_argument_group('Malcolm Git repo')
repoArgGroup.add_argument(
'-g',
'--github-url',
required=False,
dest='repoUrl',
metavar='<string>',
type=str,
default=os.getenv('MALCOLM_REPO_URL', 'idaholab'),
help='Malcolm repository url (e.g., https://github.com/idaholab/Malcolm)',
)
repoArgGroup.add_argument(
'-b',
'--github-branch',
required=False,
dest='repoBranch',
metavar='<string>',
type=str,
default=os.getenv('MALCOLM_REPO_BRANCH', 'main'),
help='Malcolm repository branch (e.g., main)',
)

vmSpecsArgGroup = parser.add_argument_group('Virtual machine specifications')
vmSpecsArgGroup.add_argument(
'-c',
'--cpus',
dest='vmCpuCount',
required=False,
metavar='<integer>',
type=int,
default=(multiprocessing.cpu_count() // 2),
help='Number of CPUs for virtual Malcolm instance',
)
vmSpecsArgGroup.add_argument(
'-m',
'--memory',
dest='vmMemoryGigabytes',
required=False,
metavar='<integer>',
type=int,
default=max(16, int(round(psutil.virtual_memory().total / (1024.0**3))) // 2),
help='System memory (GB) for virtual Malcolm instance',
)
vmSpecsArgGroup.add_argument(
'-d',
'--disk',
dest='vmDiskGigabytes',
required=False,
metavar='<integer>',
type=int,
default=64,
help='Disk size (GB) for virtual Malcolm instance',
)
vmSpecsArgGroup.add_argument(
'-i',
'--image',
required=False,
dest='vmImage',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_IMAGE', 'debian-12'),
help='Malcolm virtual instance base image name (e.g., debian-12)',
)
vmSpecsArgGroup.add_argument(
'--image-user',
required=False,
dest='vmImageUsername',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_USER', 'debian'),
help='Malcolm virtual instance base image username (e.g., debian)',
)
vmSpecsArgGroup.add_argument(
'--vm-name-prefix',
required=False,
dest='vmNamePrefix',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_NAME_PREFIX', 'malcolm'),
help='Prefix for Malcolm VM name (e.g., malcolm)',
)
vmSpecsArgGroup.add_argument(
'--existing-vm',
required=False,
dest='vmExistingName',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_EXISTING', ''),
help='Name of an existing virter VM to use rather than starting up a new one',
)
vmSpecsArgGroup.add_argument(
'--vm-provision',
dest='vmProvision',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=True,
help=f'Perform VM provisioning',
)
vmSpecsArgGroup.add_argument(
'--vm-provision-malcolm',
dest='vmProvisionMalcolm',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=True,
help=f'Perform VM provisioning (Malcolm-specific)',
)
vmSpecsArgGroup.add_argument(
'--vm-provision-path',
required=False,
dest='vmProvisionPath',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_PROVISION_PATH', os.path.join(script_path, 'virter')),
help=f'Path containing subdirectories with TOML files for VM provisioning (e.g., {os.path.join(script_path, "virter")})',
)
vmSpecsArgGroup.add_argument(
'--build-vm',
required=False,
dest='vmBuildName',
metavar='<string>',
type=str,
default=os.getenv('VIRTER_BUILD_VM', ''),
help='The name for a new VM image to build and commit instead of running one',
)
vmSpecsArgGroup.add_argument(
'--build-vm-keep-layers',
dest='vmBuildKeepLayers',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=False,
help=f"Don't remove intermediate layers when building a new VM image",
)

configArgGroup = parser.add_argument_group('Malcolm runtime configuration')
configArgGroup.add_argument(
'--container-image-file',
required=False,
dest='containerImageFile',
metavar='<string>',
type=str,
default='',
help='Malcolm container images .tar.xz file for installation (instead of "docker pull")',
)
configArgGroup.add_argument(
'-s',
'--start',
dest='startMalcolm',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=True,
help=f'Start Malcolm once provisioning is complete (default true)',
)
configArgGroup.add_argument(
'--sleep',
dest='postInitSleep',
required=False,
metavar='<integer>',
type=int,
default=30,
help='Seconds to sleep after init before starting Malcolm (default 30)',
)

testArgGroup = parser.add_argument_group('Testing configuration')
testArgGroup.add_argument(
'--test-path',
required=False,
dest='testPath',
metavar='<string>',
type=str,
default=os.getenv('MALCOLM_TEST_PATH', os.path.join(script_path, 'tests')),
help=f'Path containing test definitions (e.g., {os.path.join(script_path, 'tests')})',
)
configArgGroup.add_argument(
'-t',
'--run-tests',
dest='runTests',
type=mmguero.str2bool,
nargs='?',
metavar="true|false",
const=True,
default=True,
help=f'Run test suite once Malcolm is started',
)

try:
parser.error = parser.exit
args = parser.parse_args()
except SystemExit as e:
mmguero.eprint(f'Invalid argument(s): {e}')
sys.exit(2)

# configure logging levels based on -v, -vv, -vvv, etc.
args.verbose = logging.CRITICAL - (10 * args.verbose) if args.verbose > 0 else 0
logging.basicConfig(
level=args.verbose, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info(os.path.join(script_path, script_name))
logging.info("Arguments: {}".format(sys.argv[1:]))
logging.info("Arguments: {}".format(args))
if args.verbose > logging.DEBUG:
sys.tracebacklimit = 0

# the whole thing runs on virter, so if we don't have that what are we even doing here
err, _ = mmguero.RunProcess(['virter', 'version'])
if err != 0:
logging.error(f'{script_name} requires virter, please see https://github.com/LINBIT/virter')
return 1

# handle sigint and sigterm for graceful shutdown
signal.signal(signal.SIGINT, shutdown_handler)
signal.signal(signal.SIGTERM, shutdown_handler)

malcolmVm = MalcolmVM(
args=args,
debug=(args.verbose <= logging.DEBUG),
logger=logging,
)
try:
if args.vmBuildName:
exitCode = malcolmVm.Build()
else:
exitCode = malcolmVm.Start()
malcolmInfo = malcolmVm.Info()
logging.info(json.dumps(malcolmInfo))
set_malcolm_vm_info(malcolmInfo)
if args.runTests and os.path.isdir(args.testPath):
exitCode = pytest.main(['-p', 'no:cacheprovider', args.testPath])
malcolmVm.WaitForShutdown()
finally:
del malcolmVm

logging.info(f'{script_name} returning {exitCode}')
return exitCode


###################################################################################################
if __name__ == '__main__':
if main() > 0:
sys.exit(0)
else:
sys.exit(1)
File renamed without changes.
9 changes: 9 additions & 0 deletions src/maltest/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-

import pytest
from maltest.utils import get_malcolm_vm_info


@pytest.fixture
def malcolm_vm_info():
return get_malcolm_vm_info()
Loading

0 comments on commit bf2a2a0

Please sign in to comment.