Skip to content

Commit 859e5a0

Browse files
committed
Add structlog logging
Signed-off-by: Vincent Michel <vincent.michel@vates.tech>
1 parent 32915b9 commit 859e5a0

19 files changed

Lines changed: 439 additions & 227 deletions

conftest.py

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import argparse
66
import dataclasses
77
import itertools
8-
import logging
98
import os
109
import tempfile
1110

1211
import git
12+
import structlog
1313
from cryptography.hazmat.primitives.serialization import SSHCertPrivateKeyTypes
1414
from packaging import version
1515

@@ -30,6 +30,7 @@
3030
wait_for,
3131
)
3232
from lib.host import Host
33+
from lib.logging import configure_logging
3334
from lib.netutil import is_ipv6
3435
from lib.pool import Pool
3536
from lib.sr import SR
@@ -152,6 +153,8 @@ def pytest_configure(config: pytest.Config) -> None:
152153
assert write_volume_align is not None
153154
global_config.write_volume_align = parse_size(write_volume_align)
154155

156+
configure_logging()
157+
155158
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
156159
if "vm_ref" in metafunc.fixturenames:
157160
vms = metafunc.config.getoption("vm")
@@ -239,22 +242,24 @@ def setup_host(hostname_or_ip: str, *, config: pytest.Config | None = None) -> H
239242

240243
vif = host_vm.vifs()[0]
241244
mac_address = vif.mac_address()
242-
logging.info("Nested host has MAC %s", mac_address)
245+
host_vm.logger.info("Nested host MAC address retrieved", mac_address=mac_address)
243246

244247
host_vm.start()
245-
wait_for(host_vm.is_running, "Wait for nested host VM running")
248+
wait_for(host_vm.is_running, "Wait for nested host VM running", logger=host_vm.logger)
246249

247250
# catch host-vm IP address
248251
wait_for(lambda: pxe.arp_addresses_for(mac_address),
249252
"Wait for DHCP server to see nested host in ARP tables",
250-
timeout_secs=10 * 60)
253+
timeout_secs=10 * 60,
254+
logger=host_vm.logger.bind(mac_address=mac_address))
251255
ips = pxe.arp_addresses_for(mac_address)
252-
logging.info("Nested host has IPs %s", ips)
256+
host_vm.logger.info("Nested host IPs retrieved", ips=ips)
253257
assert len(ips) == 1
254258
host_vm.ip = ips[0]
255259

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

259264
hostname_or_ip = host_vm.ip
260265

@@ -264,7 +269,7 @@ def setup_host(hostname_or_ip: str, *, config: pytest.Config | None = None) -> H
264269

265270
def cleanup_hosts() -> None:
266271
for vm in nested_list:
267-
logging.info("Destroying nested host VM %s", vm.uuid)
272+
vm.logger.info("Destroying nested host VM")
268273
vm.destroy(verify=True)
269274

270275
# a list of master hosts, each from a different pool
@@ -306,17 +311,17 @@ def registered_xo_cli() -> None:
306311
@pytest.fixture(scope='session')
307312
def hosts_with_xo(hosts: list[Host], registered_xo_cli: None) -> Generator[list[Host], None, None]:
308313
for h in hosts:
309-
logging.info(">>> Connect host %s" % h)
314+
h.logger.info(">>> Connect host")
310315
if not h.skip_xo_config:
311316
h.xo_server_add(h.user, h.password)
312317
else:
313318
h.xo_get_server_id(store=True)
314-
wait_for(h.xo_server_connected, timeout_secs=10)
319+
wait_for(h.xo_server_connected, timeout_secs=10, logger=h.logger)
315320
yield hosts
316321
# teardown
317322
for h in hosts:
318323
if not h.skip_xo_config:
319-
logging.info("<<< Disconnect host %s" % h)
324+
h.logger.info("<<< Disconnect host")
320325
h.xo_server_remove()
321326

322327
@pytest.fixture(scope='session')
@@ -334,7 +339,7 @@ def hostA2(hostA1: Host) -> Generator[Host, None, None]:
334339
""" Second host of pool A. """
335340
assert len(hostA1.pool.hosts) > 1, "A second host in first pool is required"
336341
_hostA2 = hostA1.pool.hosts[1]
337-
logging.info(">>> hostA2 present: %s" % _hostA2)
342+
_hostA2.logger.info(">>> hostA2 present")
338343
yield _hostA2
339344

340345
@pytest.fixture(scope='session')
@@ -343,7 +348,7 @@ def hostB1(hosts: list[Host]) -> Generator[Host, None, None]:
343348
assert len(hosts) > 1, "A second pool is required"
344349
assert hosts[0].pool.uuid != hosts[1].pool.uuid
345350
_hostB1 = hosts[1]
346-
logging.info(">>> hostB1 present: %s" % _hostB1)
351+
_hostB1.logger.info(">>> hostB1 present")
347352
yield _hostB1
348353

349354
@pytest.fixture(scope='session')
@@ -379,7 +384,7 @@ def host_no_ipv6(host: Host) -> None:
379384
def shared_sr(host: Host) -> Generator[SR, None, None]:
380385
sr = host.pool.first_shared_sr()
381386
assert sr, "No shared SR available on hosts"
382-
logging.info(">> Shared SR on host present: {} of type {}".format(sr.uuid, sr.get_type()))
387+
sr.logger.info(">> Shared SR on host present: {} of type {}".format(sr.uuid, sr.get_type()))
383388
yield sr
384389

385390
@pytest.fixture(scope='session')
@@ -389,7 +394,7 @@ def local_sr_on_hostA1(hostA1: Host) -> Generator[SR, None, None]:
389394
assert len(srs) > 0, "a local SR is required on the pool's master"
390395
# use the first local SR found
391396
sr = srs[0]
392-
logging.info(">> local SR on hostA1 present: {} of type {}".format(sr.uuid, sr.get_type()))
397+
sr.logger.info(">> local SR on hostA1 present: {} of type {}".format(sr.uuid, sr.get_type()))
393398
yield sr
394399

395400
@pytest.fixture(scope='session')
@@ -399,7 +404,7 @@ def local_sr_on_hostA2(hostA2: Host) -> Generator[SR, None, None]:
399404
assert len(srs) > 0, "a local SR is required on the pool's second host"
400405
# use the first local SR found
401406
sr = srs[0]
402-
logging.info(">> local SR on hostA2 present: {} of type {}".format(sr.uuid, sr.get_type()))
407+
sr.logger.info(">> local SR on hostA2 present")
403408
yield sr
404409

405410
@pytest.fixture(scope='session')
@@ -409,13 +414,15 @@ def local_sr_on_hostB1(hostB1: Host) -> Generator[SR, None, None]:
409414
assert len(srs) > 0, "a local SR is required on the second pool's master"
410415
# use the first local SR found
411416
sr = srs[0]
412-
logging.info(">> local SR on hostB1 present: {} of type {}".format(sr.uuid, sr.get_type()))
417+
sr.logger.info(">> local SR on hostB1 present")
413418
yield sr
414419

415420
@pytest.fixture(scope='session')
416421
def disks(pytestconfig: pytest.Config, pools_hosts_by_name_or_ip: dict[HostAddress, Host]
417422
) -> dict[Host, list[Host.BlockDeviceInfo]]:
418423
"""Dict identifying names of all disks for on all hosts of first pool."""
424+
logger = structlog.get_logger("disks").bind(pools=pools_hosts_by_name_or_ip)
425+
419426
def _parse_disk_option(option_text: str) -> tuple[HostAddress, list[DiskDevName]]:
420427
parsed = option_text.split(sep=":", maxsplit=1)
421428
assert len(parsed) == 2, f"--disks option {option_text!r} is not <host>:<disk>[,<disk>]*"
@@ -469,7 +476,7 @@ def _host_disks(host: Host, hosts_cli_disks: list[DiskDevName] | None) -> Iterab
469476
if disk.wwn and not disk.available and not disk.wwn.startswith("uuid.00000000-0000-0000-0000-")
470477
}
471478
if used_wwns:
472-
logging.debug("cross-host used WWNs: %s", used_wwns)
479+
logger.debug("Resport cross-host used WWNs", used_wwns=used_wwns)
473480
ret = {
474481
host: [
475482
dataclasses.replace(disk, available=False) if (disk.wwn and disk.wwn in used_wwns) else disk
@@ -491,34 +498,42 @@ def _host_disks(host: Host, hosts_cli_disks: list[DiskDevName] | None) -> Iterab
491498
except ImportError:
492499
pass
493500
if reserved_wwns:
494-
logging.debug("reserved WWNs (lvmohba/lvmoiscsi): %s", reserved_wwns)
501+
logger.debug("Report reserved WWNs (lvmohba/lvmoiscsi)", reserved_wwns=reserved_wwns)
495502
ret = {
496503
host: sorted(host_disks, key=lambda d: d.wwn in reserved_wwns)
497504
for host, host_disks in ret.items()
498505
}
499-
logging.debug("disks collected: %s", {host.hostname_or_ip: value for host, value in ret.items()})
506+
logger.debug("Disks collected", collected_disks={host.hostname_or_ip: value for host, value in ret.items()})
500507
return ret
501508

502509
@pytest.fixture(scope='session')
503510
def unused_512B_disks(disks: dict[Host, list[Host.BlockDeviceInfo]]
504511
) -> dict[Host, list[Host.BlockDeviceInfo]]:
505512
"""Dict identifying names of all 512-bytes-blocks disks for on all hosts of first pool."""
513+
logger = structlog.get_logger("unused_512B_disks").bind(disks=disks)
506514
ret = {host: [disk for disk in host_disks
507515
if disk.log_sec == 512 and disk.available]
508516
for host, host_disks in disks.items()
509517
}
510-
logging.debug("available disks collected: %s", {host.hostname_or_ip: value for host, value in ret.items()})
518+
logger.debug(
519+
"Available disks collected",
520+
available_disks={host.hostname_or_ip: value for host, value in ret.items()}
521+
)
511522
return ret
512523

513524
@pytest.fixture(scope='session')
514525
def unused_4k_disks(disks: dict[Host, list[Host.BlockDeviceInfo]]
515526
) -> dict[Host, list[Host.BlockDeviceInfo]]:
516527
"""Dict identifying names of all 4K-blocks disks for on all hosts of first pool."""
528+
logger = structlog.get_logger("unused_4k_disks").bind(disks=disks)
517529
ret = {host: [disk for disk in host_disks
518530
if disk.log_sec == 4096 and disk.available]
519531
for host, host_disks in disks.items()
520532
}
521-
logging.debug("available 4k disks collected: %s", {host.hostname_or_ip: value for host, value in ret.items()})
533+
logger.debug(
534+
"Available 4k disks collected",
535+
available_4k_disks={host.hostname_or_ip: value for host, value in ret.items()}
536+
)
522537
return ret
523538

524539
@pytest.fixture(scope='session')
@@ -531,18 +546,22 @@ def pool_with_unused_512B_disk(host: Host, unused_512B_disks: dict[Host, list[Ho
531546

532547
@pytest.fixture(scope='module')
533548
def vm_ref(request: pytest.FixtureRequest) -> str:
549+
logger = structlog.get_logger("vm_ref")
534550
ref = request.param
535551

536552
if ref is None:
537553
# get default VM from test if there's one
538554
marker = request.node.get_closest_marker("default_vm")
539555
if marker is not None:
540556
ref = marker.args[0]
541-
logging.info(">> No VM specified on CLI. Using default: %s.", ref)
557+
logger.info(">> No VM specified on CLI. Using default", default_vm=ref)
542558
else:
543559
# global default
544-
logging.info(">> No VM specified on CLI, and no default found in test definition. Using global default.")
545560
ref = 'mini-linux-x86_64-bios'
561+
logger.info(
562+
">> No VM specified on CLI, and no default found in test definition. Using global default.",
563+
default_vm=ref,
564+
)
546565

547566
if is_uuid(ref) or ref.startswith('http'):
548567
return ref
@@ -554,13 +573,13 @@ def imported_vm(host: Host, vm_ref: str) -> Generator[VM, None, None]:
554573
if is_uuid(vm_ref):
555574
vm_orig = VM(vm_ref, host)
556575
name = vm_orig.name()
557-
logging.info(">> Reuse VM %s (%s) on host %s" % (vm_ref, name, host))
576+
vm_orig.logger.info(">> Reuse VM", vm_name=name)
558577
else:
559578
vm_orig = host.import_vm(vm_ref, host.main_sr_uuid(), use_cache=CACHE_IMPORTED_VM)
560579

561580
if CACHE_IMPORTED_VM:
562581
# Clone the VM before running tests, so that the original VM remains untouched
563-
logging.info(">> Clone cached VM before running tests")
582+
vm_orig.logger.info(">> Clone cached VM before running tests")
564583
vm = vm_orig.clone()
565584
# Remove the description, which may contain a cache identifier
566585
vm.param_set('name-description', "")
@@ -570,7 +589,7 @@ def imported_vm(host: Host, vm_ref: str) -> Generator[VM, None, None]:
570589
yield vm
571590
# teardown
572591
if CACHE_IMPORTED_VM or not is_uuid(vm_ref):
573-
logging.info("<< Destroy VM")
592+
vm.logger.info("<< Destroy VM")
574593
vm.destroy(verify=True)
575594

576595
@pytest.fixture(scope="session")
@@ -661,30 +680,30 @@ def create_vms(request: pytest.FixtureRequest, host: Host, tests_git_revision: s
661680
report = request.node.stash.get(PHASE_REPORT_KEY, None)
662681
if report is None:
663682
# user interruption during setup
664-
logging.warning("test setup result not available: not exporting VMs")
683+
host.logger.warning("test setup result not available: not exporting VMs")
665684
elif report["setup"].failed:
666-
logging.warning("setting up a test failed or skipped: not exporting VMs")
685+
host.logger.warning("setting up a test failed or skipped: not exporting VMs")
667686
elif ("call" not in report) or report["call"].failed:
668-
logging.warning("executing test failed or skipped: not exporting VMs")
687+
host.logger.warning("executing test failed or skipped: not exporting VMs")
669688
else:
670689
# record this state
671690
for vm_def, vm in zip(vm_defs, vms):
672691
nodeid = shortened_nodeid(request.node.nodeid)
673692
vm.save_to_cache(f"{nodeid}-{vm_def['name']}-{tests_git_revision}")
674693

675694
except Exception:
676-
logging.error("exception caught...")
695+
host.logger.exception()
677696
raise
678697

679698
finally:
680699
for vbd in vbds:
681-
logging.info("<< Destroy VBD %s", vbd.uuid)
700+
vbd.logger.info("<< Destroy VBD")
682701
vbd.destroy()
683702
for vdi in vdis:
684-
logging.info("<< Destroy VDI %s", vdi.uuid)
703+
vdi.logger.info("<< Destroy VDI")
685704
vdi.destroy()
686705
for vm in vms:
687-
logging.info("<< Destroy VM %s", vm.uuid)
706+
vm.logger.info("<< Destroy VM")
688707
vm.destroy(verify=True)
689708

690709
def _vm_name(request: pytest.FixtureRequest, vm_def: dict[str, Any]) -> str:
@@ -696,7 +715,7 @@ def _create_vm(
696715
vm_name = _vm_name(request, vm_def)
697716
vm_template = vm_def["template"]
698717

699-
logging.info("Installing VM %r from template %r", vm_name, vm_template)
718+
host.logger.info("Installing VM from template", vm_name=vm_name, vm_template=vm_template)
700719

701720
vm = host.vm_from_template(vm_name, vm_template)
702721

@@ -724,7 +743,7 @@ def _create_vm(
724743

725744
if "params" in vm_def:
726745
for param_def in vm_def["params"]:
727-
logging.info("Setting param %s", param_def)
746+
vm.logger.info("Setting parameter", param_def=param_def)
728747
vm.param_set(**param_def)
729748

730749
def _vm_from_cache(
@@ -736,7 +755,7 @@ def _vm_from_cache(
736755
raise RuntimeError("No cache found")
737756

738757
# Clone the VM before running tests, so that the original VM remains untouched
739-
logging.info("Cloning VM from cache")
758+
base_vm.logger.info("Cloning VM from cache")
740759
vm = base_vm.clone(name=prefix_object_name(_vm_name(request, vm_def)))
741760
# Remove the description, which may contain a cache identifier
742761
vm.param_set('name-description', "")
@@ -749,15 +768,15 @@ def started_vm(imported_vm: VM) -> VM:
749768
# may be already running if we skipped the import to use an existing VM
750769
if not vm.is_running():
751770
vm.start()
752-
wait_for(vm.is_running, '> Wait for VM running')
753-
wait_for(vm.try_get_and_store_ip, "> Wait for VM IP", timeout_secs=5 * 60)
771+
wait_for(vm.is_running, 'Wait for VM running', logger=vm.logger)
772+
wait_for(vm.try_get_and_store_ip, "Wait for VM IP", timeout_secs=5 * 60, logger=vm.logger)
754773
return vm
755774
# no teardown
756775

757776
@pytest.fixture(scope="module")
758777
def running_vm(started_vm: VM) -> VM:
759778
vm = started_vm
760-
wait_for(vm.is_ssh_up, "> Wait for VM SSH up")
779+
wait_for(vm.is_ssh_up, "Wait for VM SSH up", logger=vm.logger)
761780
return vm
762781

763782
@pytest.fixture(scope='module')
@@ -852,10 +871,10 @@ def nfs_iso_sr(host: Host, nfs_iso_device_config: dict[str, Any]) -> Generator[S
852871
@pytest.fixture(scope='function')
853872
def exit_on_fistpoint(host: Host) -> Generator[None, None, None]:
854873
from lib.fistpoint import FistPoint
855-
logging.info(">> Enabling exit on fistpoint")
874+
host.logger.info(">> Enabling exit on fistpoint")
856875
FistPoint.enable_exit_on_fistpoint(host)
857876
yield
858-
logging.info("<< Disabling exit on fistpoint")
877+
host.logger.info("<< Disabling exit on fistpoint")
859878
FistPoint.disable_exit_on_fistpoint(host)
860879

861880
@pytest.fixture(scope='module')

jobs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys
77

88
from lib.commands import ssh
9+
from lib.logging import configure_logging
910

1011
from typing import NotRequired, TypedDict, cast
1112

@@ -816,6 +817,7 @@ def action_run(args: argparse.Namespace) -> None:
816817
sys.exit(1)
817818

818819
def main() -> None:
820+
configure_logging()
819821
parser = argparse.ArgumentParser(description="Manage test jobs")
820822
subparsers = parser.add_subparsers(dest="action", metavar="action")
821823
subparsers.required = True

0 commit comments

Comments
 (0)