Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated RPU test #253

Draft
wants to merge 70 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
abd28d8
get_vdi_sr_uuid: fix intermediate-style call
ydirson Sep 6, 2024
8cd3d87
pool: add a log for better understanding of INFO level
ydirson Sep 6, 2024
8f4161e
Add temporary post-install jobs
stormi Sep 5, 2024
d249b24
Host: on VM cache miss print a key pastable into IMAGE_*EQUIVS
ydirson Sep 6, 2024
097fcd4
fixup: only one pool required for postinstall job
stormi Sep 18, 2024
4a4071e
xcpng-fs-diff: remove no-op statement looking like a decorator
ydirson Sep 11, 2024
5aa7463
WIP xcpng-fs-diff: use lib.commands.ssh and activate logging
ydirson Sep 11, 2024
b531c66
xcpng-fs-diff: avoid non-standard "exit(-1)"
ydirson Sep 11, 2024
9801f30
xcpng-fs-diff: change prints to logs
ydirson Sep 11, 2024
850960d
xcpng-fs-diff: new test to check against reference files
ydirson Sep 11, 2024
e76d4ba
xcpng-fs-diff: ignore /etc/xapi.d/plugins/vmssc
ydirson Sep 16, 2024
cac89ad
install 1/n: fixture to create VMs from template
ydirson May 7, 2024
afb5eca
install 2/n: insert ISO in host VM
ydirson Aug 9, 2024
827be31
install 3/n: use iso-remaster to plug an hardcoded answerfile
ydirson Aug 9, 2024
f78847c
install 4/n: boot and monitor installer
ydirson Aug 9, 2024
0c5abe9
install 5/n: make sure host running installer appears in PXE ARP tables
ydirson Jun 25, 2024
2718537
install 6/n: install test-pingpxe service on host
ydirson Aug 2, 2024
4ea952c
install 7/n: answerfile generation
ydirson Oct 7, 2024
70d5be9
install 8/n: use VM cache to chain tests
ydirson Jun 25, 2024
169eca2
install 9/n: add firstboot test
ydirson Jul 25, 2024
d269e95
Image caching: include commit hash in caching key
ydirson Sep 4, 2024
ab91ea4
Image caching: allow to declare image equivalence
ydirson Jul 9, 2024
0d3aeb9
install: use xcpng_chained/continuation_of to simplify dependency spec
ydirson Jun 21, 2024
0e88ab1
remastered_iso: support for unsigned ISOs
ydirson Sep 4, 2024
68418be
install: add "version" test parameter and test-sequences
ydirson Aug 26, 2024
6694b80
Add upgrade test
ydirson Sep 4, 2024
956ef5e
install: add a "firmware" parameter
ydirson Oct 9, 2024
cad6d5a
install: add "restore" test using 8.3 ISO
ydirson Sep 5, 2024
f720446
install/firstboot: check installed version
ydirson Jun 17, 2024
fdc86e4
install: add XS/CH support
ydirson Sep 5, 2024
05bd2c8
install: add installation of xcp-ng 8.0 and 8.1, upgrades to 8.3
ydirson Aug 29, 2024
6d8be9a
install: 7.5, 7.6, and XS 7.0
ydirson Jun 14, 2024
1281265
install: produce several hosts from single install
ydirson Oct 8, 2024
1349fb5
install: adjust host IP, name, UUIDs in firstboot data before booting
ydirson Sep 9, 2024
7a6b454
Add local_sr parameter to test_install
ydirson Oct 9, 2024
559277e
import_vm: add clone:// and clone+start:// URIs
ydirson Jul 15, 2024
5211c04
hosts: make setup_host a nested func
ydirson Sep 19, 2024
6735aac
Add support for --nest=... --hosts=cache://...
ydirson Jul 23, 2024
3e3c746
Add support for netinstall
gthvn1 Sep 9, 2024
aeef1d4
New test: pool_join
ydirson Aug 30, 2024
ab1f2c2
firstboot: reordering cleanup
ydirson Oct 8, 2024
2354c4e
firstboot: move _upg and _rst tests to group them with upgrade and re…
ydirson Oct 9, 2024
04f5059
fixup! install: add installation of xcp-ng 8.0 and 8.1, upgrades to 8.3
ydirson Oct 10, 2024
85d49bf
fixup! install/firstboot: check installed version
ydirson Oct 10, 2024
b1d7b31
fixup! install/firstboot: check installed version
ydirson Oct 10, 2024
d8a1ccf
fixup! install 7/n: answerfile generation
ydirson Oct 17, 2024
d0fe790
fixup! install: 7.5, 7.6, and XS 7.0
ydirson Oct 18, 2024
030331a
fixup! install 7/n: answerfile generation
ydirson Oct 18, 2024
b2df9bf
New script to generate reference install nested hosts
ydirson Sep 6, 2024
a1639ab
WIP Support for listing fallback images for old versions installations
ydirson Sep 4, 2024
d131191
New script to run upgrade tests in nested hosts
ydirson Sep 10, 2024
8141bf4
New script to run install tests in nested hosts
ydirson Sep 11, 2024
b305562
WIP New scripts to run post-install tests in nested hosts
ydirson Sep 11, 2024
88416a7
WIP post-install: check against reference files
ydirson Oct 4, 2024
e52e3a2
WIP New test to check logs for errors
ydirson Sep 13, 2024
71802fe
jobs.py: don't ignore --host-version when --hosts is passed
ydirson Sep 11, 2024
77b2330
Merge branch 'stormi/post-install-test-job'
ydirson Sep 18, 2024
92e1266
Merge branch 'ydi/fsdiff'
ydirson Oct 4, 2024
63fb9fe
WIP (failing) indirect upgrade from 7.5
ydirson Oct 8, 2024
807b5e6
squash! WIP New test: join 2 hosts into a pool
ydirson Oct 17, 2024
c8fdb71
WIP New test: join 2 hosts into a pool
ydirson Jul 3, 2024
b7dd052
WIP arp_clear
ydirson Aug 27, 2024
5ba2773
WIP test RPU
ydirson Sep 5, 2024
888163f
Allow installation of a 3rd host
ydirson Aug 30, 2024
01475e7
jobs.py: enable logging
ydirson Sep 11, 2024
4daacb3
local_cmd: fix error display
ydirson Sep 25, 2024
854eba1
ssh: simplify addition of ssh options
ydirson Sep 26, 2024
e7ed145
WIP indirect upgrades
ydirson Sep 30, 2024
b352411
WIP installation on a disk with a Dell utility partition
ydirson Sep 30, 2024
36d79f5
WIP installation on a disk with a MBR partition
ydirson Oct 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/test-sequences.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Check test-sequences consistency

on: [push]

jobs:
jobs-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v4
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/base.txt
- name: Create a dummy data.py
run: cp data.py-dist data.py
- name: jobs-check
run: |
FAILURES=""
for seq in $(find -name "*.lst"); do
if ! pytest @$seq --collect-only --quiet; then
FAILURES="$FAILURES $seq"
fi
done
[ -z "$FAILURES" ] || { echo >&2 "ERROR: test sequences failed consistency check: $FAILURES"; exit 1; }
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ __pycache__
*/__pycache__
data.py
vm_data.py
/test*.log
250 changes: 243 additions & 7 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import itertools
import git
import logging
import os
import pytest
import tempfile

from packaging import version

import lib.config as global_config

from lib import pxe
from lib.commands import SSHCommandFailed
from lib.common import callable_marker, shortened_nodeid
from lib.common import wait_for, vm_image, is_uuid
from lib.common import prefix_object_name
from lib.common import setup_formatted_and_mounted_disk, teardown_formatted_and_mounted_disk
from lib.netutil import is_ipv6
from lib.pool import Pool
from lib.vm import VM
from lib.sr import SR
from lib.vm import VM, vm_cache_key_from_def, vm_default_cache_key_from_def
from lib.xo import xo_cli

# Import package-scoped fixtures. Although we need to define them in a separate file so that we can
Expand All @@ -29,6 +36,12 @@
# pytest hooks

def pytest_addoption(parser):
parser.addoption(
"--nest",
action="store",
default=None,
help="XCP-ng or XS master of pool to use for nesting hosts under test",
)
parser.addoption(
"--hosts",
action="append",
Expand Down Expand Up @@ -136,22 +149,72 @@ def pytest_runtest_makereport(item, call):

# fixtures

def setup_host(hostname_or_ip):
pool = Pool(hostname_or_ip)
h = pool.master
return h

@pytest.fixture(scope='session')
def hosts(pytestconfig):
nested_list = []

def setup_host(hostname_or_ip, *, config=None):
host_vm = None
if hostname_or_ip.startswith("cache://"):
nest_hostname = config.getoption("nest")
if not nest_hostname:
pytest.fail("--hosts=cache://... requires --nest parameter")
nest = Pool(nest_hostname).master

protocol, rest = hostname_or_ip.split(":", 1)
host_vm = nest.import_vm(f"clone:{rest}", nest.main_sr_uuid(),
use_cache=True)
nested_list.append(host_vm)

vif = host_vm.vifs()[0]
mac_address = vif.param_get('MAC')
logging.info("Nested host has MAC %s", mac_address)
pxe.arp_clear_for(mac_address)

host_vm.start()
wait_for(host_vm.is_running, "Wait for nested host VM running")

# catch host-vm IP address
wait_for(lambda: pxe.arp_addresses_for(mac_address),
"Wait for DHCP server to see nested host in ARP tables",
timeout_secs=10 * 60)
ips = pxe.arp_addresses_for(mac_address)
logging.info("Nested host has IPs %s", ips)
assert len(ips) == 1
host_vm.ip = ips[0]

wait_for(lambda: not os.system(f"nc -zw5 {host_vm.ip} 22"),
"Wait for ssh up on nested host", retry_delay_secs=5)

hostname_or_ip = host_vm.ip

pool = Pool(hostname_or_ip)
h = pool.master
return h

def cleanup_hosts():
for vm in nested_list:
logging.info("Destroying nested host VM %s", vm.uuid)
vm.destroy(verify=True)

# a list of master hosts, each from a different pool
hosts_args = pytestconfig.getoption("hosts")
hosts_split = [hostlist.split(',') for hostlist in hosts_args]
hostname_list = list(itertools.chain(*hosts_split))
host_list = [setup_host(hostname_or_ip) for hostname_or_ip in hostname_list]

try:
host_list = [setup_host(hostname_or_ip, config=pytestconfig)
for hostname_or_ip in hostname_list]
except Exception:
cleanup_hosts()
raise

if not host_list:
pytest.fail("This test requires at least one --hosts parameter")
yield host_list

cleanup_hosts()

@pytest.fixture(scope='session')
def registered_xo_cli():
# The fixture is not responsible for establishing the connection.
Expand Down Expand Up @@ -375,6 +438,179 @@ def imported_vm(host, vm_ref):
logging.info("<< Destroy VM")
vm.destroy(verify=True)

@pytest.fixture(scope="session")
def tests_git_revision():
"""
Get the git revision string for this tests repo.

Use of this fixture means impacted tests cannot run unless all
modifications are commited.
"""
test_repo = git.Repo(".")
assert not test_repo.is_dirty(), "test repo must not be dirty"
yield test_repo.head.commit.hexsha

@pytest.fixture(scope="function")
def create_vms(request, host, tests_git_revision):
"""
Returns list of VM objects created from `vm_definitions` marker.

`vm_definitions` marker test author to specify one or more VMs, by
giving for each VM one `dict`, or a callable taking fixtures as
arguments and returning such a `dict`.

Mandatory keys:
- `name`: name of the VM to create (str)
- `template`: name (or UUID) of template to use (str)

Optional keys: see example below

Example:
-------
> @pytest.mark.vm_definitions(
> dict(name="vm1", template="Other install media"),
> dict(name="vm2",
> template="CentOS 7",
> params=(
> dict(param_name="memory-static-max", value="4GiB"),
> dict(param_name="HVM-boot-params", key="order", value="dcn"),
> ),
> vdis=[dict(name="vm 2 system disk",
> size="100GiB",
> device="xvda",
> userdevice="0",
> )],
> cd_vbd=dict(device="xvdd", userdevice="3"),
> vifs=(dict(index=0, network_name=NETWORKS["MGMT"]),
> dict(index=1, network_uuid=NETWORKS["MYNET_UUID"]),
> ),
> ))
> def test_foo(create_vms):
> ...

Example:
-------
> @pytest.mark.dependency(depends=["test_foo"])
> @pytest.mark.vm_definitions(dict(name="vm1", image_test="test_foo", image_vm="vm2"))
> def test_bar(create_vms):
> ...

"""
marker = request.node.get_closest_marker("vm_definitions")
if marker is None:
raise Exception("No vm_definitions marker specified.")

vm_defs = []
for vm_def in marker.args:
vm_def = callable_marker(vm_def, request)
assert "name" in vm_def
assert "template" in vm_def or "image_test" in vm_def
if "template" in vm_def:
assert "image_test" not in vm_def
# FIXME should check optional vdis contents
# FIXME should check for extra args
vm_defs.append(vm_def)

try:
vms = []
vdis = []
vbds = []
for vm_def in vm_defs:
if "template" in vm_def:
_create_vm(request, vm_def, host, vms, vdis, vbds)
elif "image_test" in vm_def:
_vm_from_cache(request, vm_def, host, vms, tests_git_revision)
yield vms

# request.node is an "item" because this fixture has "function" scope
report = request.node.stash.get(PHASE_REPORT_KEY, None)
if report is None:
# user interruption during setup
logging.warning("test setup result not available: not exporting VMs")
elif report["setup"].failed:
logging.warning("setting up a test failed or skipped: not exporting VMs")
elif ("call" not in report) or report["call"].failed:
logging.warning("executing test failed or skipped: not exporting VMs")
else:
# record this state
for vm_def, vm in zip(vm_defs, vms):
nodeid = shortened_nodeid(request.node.nodeid)
vm.save_to_cache(f"{nodeid}-{vm_def['name']}-{tests_git_revision}")

except Exception:
logging.error("exception caught...")
raise

finally:
for vbd in vbds:
logging.info("<< Destroy VBD %s", vbd.uuid)
vbd.destroy()
for vdi in vdis:
logging.info("<< Destroy VDI %s", vdi.uuid)
vdi.destroy()
for vm in vms:
logging.info("<< Destroy VM %s", vm.uuid)
vm.destroy(verify=True)

def _vm_name(request, vm_def):
return prefix_object_name(f"{vm_def['name']} in {request.node.nodeid}")

def _create_vm(request, vm_def, host, vms, vdis, vbds):
vm_name = _vm_name(request, vm_def)
vm_template = vm_def["template"]

logging.info("Installing VM %r from template %r", vm_name, vm_template)

vm = host.vm_from_template(vm_name, vm_template)

# VM is now created, make sure we clean it up on any subsequent failure
vms.append(vm)

if "vdis" in vm_def:
for vdi_def in vm_def["vdis"]:
sr = SR(host.main_sr_uuid(), host.pool)
vdi = sr.create_vdi(vdi_def["name"], vdi_def["size"])
vdis.append(vdi)
# connect to VM
vbd = vm.create_vbd(vdi_def["device"], vdi.uuid)
vbds.append(vbd)
vbd.param_set(param_name="userdevice", value=vdi_def["userdevice"])

if "cd_vbd" in vm_def:
vm.create_cd_vbd(**vm_def["cd_vbd"])

if "vifs" in vm_def:
for vif_def in vm_def["vifs"]:
vm.create_vif(vif_def["index"],
network_uuid=vif_def.get("network_uuid", None),
network_name=vif_def.get("network_name", None))

if "params" in vm_def:
for param_def in vm_def["params"]:
logging.info("Setting param %s", param_def)
vm.param_set(**param_def)

def _vm_from_cache(request, vm_def, host, vms, tests_hexsha):
# try get exact image or an equivalence from data.py
base_vm = host.cached_vm(vm_cache_key_from_def(vm_def, request.node.nodeid, tests_hexsha),
sr_uuid=host.main_sr_uuid())
if base_vm is None:
default_key = vm_default_cache_key_from_def(vm_def, request.node.nodeid)
if default_key is None:
raise RuntimeError("No cache or IMAGE_DEFAULT_EQUIVS found")
# default to an image from reference cache if any
base_vm = host.cached_vm(default_key, sr_uuid=host.main_sr_uuid())
if base_vm is None:
raise RuntimeError(f"IMAGE_DEFAULT_EQUIVS points to non-existent {default_key!r}")

# Clone the VM before running tests, so that the original VM remains untouched
logging.info("Cloning VM from cache")
vm = base_vm.clone(name=_vm_name(request, vm_def))
# Remove the description, which may contain a cache identifier
vm.param_set('name-description', "")

vms.append(vm)

@pytest.fixture(scope="module")
def started_vm(imported_vm):
vm = imported_vm
Expand Down
Loading
Loading