55import argparse
66import dataclasses
77import itertools
8- import logging
98import os
109import tempfile
1110
1211import git
12+ import structlog
1313from cryptography .hazmat .primitives .serialization import SSHCertPrivateKeyTypes
1414from packaging import version
1515
3030 wait_for ,
3131)
3232from lib .host import Host
33+ from lib .logging import configure_logging
3334from lib .netutil import is_ipv6
3435from lib .pool import Pool
3536from 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+
155158def 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' )
307312def 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:
379384def 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' )
416421def 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' )
503510def 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' )
514525def 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' )
533548def 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
690709def _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
730749def _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" )
758777def 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' )
853872def 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' )
0 commit comments