@@ -47,13 +47,18 @@ def pytest_addoption(parser):
4747 parser .addoption ("--dvsname" ,
4848 action = "store" ,
4949 default = None ,
50- help = "Name of a persistent DVS container to run the tests with" )
50+ help = "Name of a persistent DVS container to run the tests with. Mutually exclusive with --force-recreate-dvs " )
5151
5252 parser .addoption ("--forcedvs" ,
5353 action = "store_true" ,
5454 default = False ,
5555 help = "Force tests to run in persistent DVS containers with <32 ports" )
5656
57+ parser .addoption ("--force-recreate-dvs" ,
58+ action = "store_true" ,
59+ default = False ,
60+ help = "Force the DVS container to be recreated between each test module. Mutually exclusive with --dvsname" )
61+
5762 parser .addoption ("--keeptb" ,
5863 action = "store_true" ,
5964 default = False ,
@@ -192,6 +197,9 @@ def __init__(self, ctn_name: str, pid: int, i: int):
192197 ensure_system (f"nsenter -t { pid } -n ip link set arp off dev { self .pifname } " )
193198 ensure_system (f"nsenter -t { pid } -n sysctl -w net.ipv6.conf.{ self .pifname } .disable_ipv6=1" )
194199
200+ def __repr__ (self ):
201+ return f'<VirtualServer> { self .nsname } '
202+
195203 def kill_all_processes (self ) -> None :
196204 pids = subprocess .check_output (f"ip netns pids { self .nsname } " , shell = True ).decode ("utf-8" )
197205 if pids :
@@ -339,9 +347,7 @@ def __init__(
339347
340348 # create virtual server
341349 self .servers = []
342- for i in range (NUM_PORTS ):
343- server = VirtualServer (self .ctn_sw .name , self .ctn_sw_pid , i )
344- self .servers .append (server )
350+ self .create_servers ()
345351
346352 if self .vct :
347353 self .vct_connect (newctnname )
@@ -377,13 +383,7 @@ def __init__(
377383 self .redis_sock = os .path .join (self .mount , "redis.sock" )
378384 self .redis_chassis_sock = os .path .join (self .mount , "redis_chassis.sock" )
379385
380- # DB wrappers are declared here, lazy-loaded in the tests
381- self .app_db = None
382- self .asic_db = None
383- self .counters_db = None
384- self .config_db = None
385- self .flex_db = None
386- self .state_db = None
386+ self .reset_dbs ()
387387
388388 # Make sure everything is up and running before turning over control to the caller
389389 self .check_ready_status_and_init_db ()
@@ -392,23 +392,40 @@ def __init__(
392392 if buffer_model == 'dynamic' :
393393 enable_dynamic_buffer (self .get_config_db (), self .runcmd )
394394
395+ def create_servers (self ):
396+ for i in range (NUM_PORTS ):
397+ server = VirtualServer (self .ctn_sw .name , self .ctn_sw_pid , i )
398+ self .servers .append (server )
399+
400+ def reset_dbs (self ):
401+ # DB wrappers are declared here, lazy-loaded in the tests
402+ self .app_db = None
403+ self .asic_db = None
404+ self .counters_db = None
405+ self .config_db = None
406+ self .flex_db = None
407+ self .state_db = None
408+
395409 def destroy (self ) -> None :
396- if self . appldb :
410+ if getattr ( self , ' appldb' , False ) :
397411 del self .appldb
398412
399413 # In case persistent dvs was used removed all the extra server link
400414 # that were created
401415 if self .persistent :
402- for s in self .servers :
403- s .destroy ()
416+ self .destroy_servers ()
404417
405418 # persistent and clean-up flag are mutually exclusive
406419 elif self .cleanup :
407420 self .ctn .remove (force = True )
408421 self .ctn_sw .remove (force = True )
409422 os .system (f"rm -rf { self .mount } " )
410- for s in self .servers :
411- s .destroy ()
423+ self .destroy_servers ()
424+
425+ def destroy_servers (self ):
426+ for s in self .servers :
427+ s .destroy ()
428+ self .servers = []
412429
413430 def check_ready_status_and_init_db (self ) -> None :
414431 try :
@@ -423,6 +440,7 @@ def check_ready_status_and_init_db(self) -> None:
423440 # Initialize the databases.
424441 self .init_asic_db_validator ()
425442 self .init_appl_db_validator ()
443+ self .reset_dbs ()
426444
427445 # Verify that SWSS has finished initializing.
428446 self .check_swss_ready ()
@@ -451,9 +469,9 @@ def _polling_function():
451469
452470 for pname in self .alld :
453471 if process_status .get (pname , None ) != "RUNNING" :
454- return (False , None )
472+ return (False , process_status )
455473
456- return (process_status .get ("start.sh" , None ) == "EXITED" , None )
474+ return (process_status .get ("start.sh" , None ) == "EXITED" , process_status )
457475
458476 wait_for_result (_polling_function , service_polling_config )
459477
@@ -1556,35 +1574,92 @@ def verify_vct(self):
15561574 print ("vct verifications passed ? %s" % (ret1 and ret2 ))
15571575 return ret1 and ret2
15581576
1559- @pytest .yield_fixture (scope = "module" )
1560- def dvs (request ) -> DockerVirtualSwitch :
1577+ @pytest .fixture (scope = "session" )
1578+ def manage_dvs (request ) -> str :
1579+ """
1580+ Main fixture to manage the lifecycle of the DVS (Docker Virtual Switch) for testing
1581+
1582+ Returns:
1583+ (func) update_dvs function which can be called on a per-module basis
1584+ to handle re-creating the DVS if necessary
1585+ """
15611586 if sys .version_info [0 ] < 3 :
15621587 raise NameError ("Python 2 is not supported, please install python 3" )
15631588
15641589 if subprocess .check_call (["/sbin/modprobe" , "team" ]):
15651590 raise NameError ("Cannot install kernel team module, please install a generic kernel" )
15661591
15671592 name = request .config .getoption ("--dvsname" )
1593+ using_persistent_dvs = name is not None
15681594 forcedvs = request .config .getoption ("--forcedvs" )
15691595 keeptb = request .config .getoption ("--keeptb" )
15701596 imgname = request .config .getoption ("--imgname" )
15711597 max_cpu = request .config .getoption ("--max_cpu" )
15721598 buffer_model = request .config .getoption ("--buffer_model" )
1573- fakeplatform = getattr (request .module , "DVS_FAKE_PLATFORM" , None )
1574- log_path = name if name else request .module .__name__
1599+ force_recreate = request .config .getoption ("--force-recreate-dvs" )
1600+ dvs = None
1601+ curr_fake_platform = None # lgtm[py/unused-local-variable]
1602+
1603+ if using_persistent_dvs and force_recreate :
1604+ pytest .fail ("Options --dvsname and --force-recreate-dvs are mutually exclusive" )
1605+
1606+ def update_dvs (log_path , new_fake_platform = None ):
1607+ """
1608+ Decides whether or not to create a new DVS
1609+
1610+ Create a new the DVS in the following cases:
1611+ 1. CLI option `--force-recreate-dvs` was specified (recreate for every module)
1612+ 2. The fake_platform has changed (this can only be set at container creation,
1613+ so it is necessary to spin up a new DVS)
1614+ 3. No DVS currently exists (i.e. first time startup)
1615+
1616+ Otherwise, restart the existing DVS (to get to a clean state)
1617+
1618+ Returns:
1619+ (DockerVirtualSwitch) a DVS object
1620+ """
1621+ nonlocal curr_fake_platform , dvs
1622+ if force_recreate or \
1623+ new_fake_platform != curr_fake_platform or \
1624+ dvs is None :
15751625
1576- dvs = DockerVirtualSwitch (name , imgname , keeptb , fakeplatform , log_path , max_cpu , forcedvs , buffer_model = buffer_model )
1626+ if dvs is not None :
1627+ dvs .get_logs ()
1628+ dvs .destroy ()
15771629
1578- yield dvs
1630+ dvs = DockerVirtualSwitch (name , imgname , keeptb , new_fake_platform , log_path , max_cpu , forcedvs , buffer_model = buffer_model )
1631+
1632+ curr_fake_platform = new_fake_platform
1633+
1634+ else :
1635+ # First generate GCDA files for GCov
1636+ dvs .runcmd ('killall5 -15' )
1637+ # If not re-creating the DVS, restart container
1638+ # between modules to ensure a consistent start state
1639+ dvs .net_cleanup ()
1640+ dvs .destroy_servers ()
1641+ dvs .create_servers ()
1642+ dvs .restart ()
1643+
1644+ return dvs
1645+
1646+ yield update_dvs
15791647
15801648 dvs .get_logs ()
15811649 dvs .destroy ()
15821650
1583- # restore original config db
15841651 if dvs .persistent :
15851652 dvs .runcmd ("mv /etc/sonic/config_db.json.orig /etc/sonic/config_db.json" )
15861653 dvs .ctn_restart ()
15871654
1655+ @pytest .yield_fixture (scope = "module" )
1656+ def dvs (request , manage_dvs ) -> DockerVirtualSwitch :
1657+ fakeplatform = getattr (request .module , "DVS_FAKE_PLATFORM" , None )
1658+ name = request .config .getoption ("--dvsname" )
1659+ log_path = name if name else request .module .__name__
1660+
1661+ return manage_dvs (log_path , fakeplatform )
1662+
15881663@pytest .yield_fixture (scope = "module" )
15891664def vct (request ):
15901665 vctns = request .config .getoption ("--vctns" )
0 commit comments