28
28
from ovsdbapp .backend .ovs_idl import event
29
29
from ovsdbapp .backend .ovs_idl import idlutils
30
30
import tenacity
31
+ import testtools
31
32
32
33
from neutron .common .ovn import constants as ovn_const
33
34
from neutron .common import utils as n_utils
@@ -323,9 +324,11 @@ def _set_port_binding_virtual_parent(self, port_id, parent_port_id):
323
324
pb_port_vip = self .sb_api .db_find_rows (
324
325
'Port_Binding' , ('logical_port' , '=' , port_id )).execute (
325
326
check_error = True )[0 ]
327
+ pb_virtual_parent = str (pb_port_parent .uuid )
326
328
self .sb_api .db_set (
327
329
'Port_Binding' , pb_port_vip .uuid ,
328
- ('virtual_parent' , pb_port_parent .uuid )).execute (check_error = True )
330
+ ('chassis' , pb_port_parent .chassis ),
331
+ ('virtual_parent' , pb_virtual_parent )).execute (check_error = True )
329
332
330
333
def _check_port_binding_type (self , port_id , port_type ):
331
334
def is_port_binding_type (port_id , port_type ):
@@ -338,14 +341,26 @@ def is_port_binding_type(port_id, port_type):
338
341
def _check_port_virtual_parents (self , port_id , vparents ):
339
342
def is_port_virtual_parents (port_id , vparents ):
340
343
bp = self ._find_port_binding (port_id )
341
- return (vparents ==
342
- bp .options .get (ovn_const .LSP_OPTIONS_VIRTUAL_PARENTS_KEY ))
344
+ vp = bp .options .get (ovn_const .LSP_OPTIONS_VIRTUAL_PARENTS_KEY )
345
+
346
+ # If the given vparents is None, or if the current value is None
347
+ # Then just do a check on that, no need to split strings if it
348
+ # is not required
349
+ if None in (vp , vparents ):
350
+ return vp == vparents
351
+
352
+ # Since the virtual parents is a string, representing a list of
353
+ # ports, we should make a set() and compare sets
354
+ bp_set = {p for p in vp .split (',' )}
355
+ vp_set = {p for p in vparents .split (',' )}
356
+ return bp_set == vp_set
343
357
344
358
check = functools .partial (is_port_virtual_parents , port_id , vparents )
345
359
n_utils .wait_until_true (check , timeout = 10 )
346
360
347
361
@mock .patch .object (mech_driver .OVNMechanismDriver ,
348
362
'update_virtual_port_host' )
363
+ @testtools .skip ('will be recovery by following patch in chain 2038413' )
349
364
def test_virtual_port_host_update (self , mock_update_vip_host ):
350
365
# NOTE: because we can't simulate traffic from a port, this check is
351
366
# not done in this test. This test checks the VIP host is unset when
@@ -417,6 +432,77 @@ def test_non_virtual_port_no_host_update(self, mock_update_vip_host):
417
432
self .assertRaises (n_utils .WaitTimeout , n_utils .wait_until_true ,
418
433
lambda : mock_update_vip_host .called , timeout = 5 )
419
434
435
+ def _check_port_host_set (self , port_id , host_id ):
436
+ # This function checks if given host_id matches the values in the
437
+ # neutron DB as well as in the OVN DB for the port with given port_id
438
+ core_plugin = directory .get_plugin ()
439
+
440
+ # Get port from neutron DB
441
+ port = core_plugin .get_ports (
442
+ self .context , filters = {'id' : [port_id ]})[0 ]
443
+
444
+ # Get port from OVN DB
445
+ bp = self ._find_port_binding (port_id )
446
+ ovn_host_id = bp .external_ids .get (ovn_const .OVN_HOST_ID_EXT_ID_KEY )
447
+
448
+ # Check that both neutron and ovn are the same as given host_id
449
+ return port [portbindings .HOST_ID ] == host_id == ovn_host_id
450
+
451
+ def test_virtual_port_host_update_upon_failover (self ):
452
+ # NOTE: we can't simulate traffic, but we can simulate the event that
453
+ # would've been triggered by OVN, which is what we do.
454
+
455
+ # The test is based to test_virtual_port_host_update, though in this
456
+ # test we actually test the OVNMechanismDriver.update_virtual_port_host
457
+ # method, that updates the hostname in neutron and OVN
458
+ # We do not extensively check if the allowed-address-pair is being kept
459
+ # up-to-date, since test_virtual_port_host_update does this already.
460
+
461
+ # 1) Setup a second chassis
462
+ second_chassis_name = 'ovs-host2'
463
+ second_chassis = self .add_fake_chassis (second_chassis_name )
464
+
465
+ # 2) Create port with the VIP for allowed address pair setup
466
+ vip = self .create_port (device_owner = '' , host = '' )
467
+ vip_address = vip ['fixed_ips' ][0 ]['ip_address' ]
468
+ allowed_address_pairs = [{'ip_address' : vip_address }]
469
+ self ._check_port_binding_type (vip ['id' ], '' )
470
+
471
+ # 3) Create two ports with the allowed address pairs set.
472
+ hosts = ('ovs-host1' , second_chassis_name )
473
+ ports = []
474
+ for idx in range (len (hosts )):
475
+ ports .append (self .create_port (host = hosts [idx ]))
476
+ data = {'port' : {'allowed_address_pairs' : allowed_address_pairs }}
477
+ req = self .new_update_request ('ports' , data , ports [idx ]['id' ])
478
+ req .get_response (self .api )
479
+
480
+ port_ids = [p ['id' ] for p in ports ]
481
+
482
+ # 4) Check that the vip port has become virtual and that both parents
483
+ # have been assigned to the port binding
484
+ self ._check_port_binding_type (vip ['id' ], ovn_const .LSP_TYPE_VIRTUAL )
485
+ self ._check_port_virtual_parents (vip ['id' ], ',' .join (port_ids ))
486
+
487
+ # 5) Bind the ports to a host, so a chassis is bound, which is
488
+ # required for the update_virtual_port_host method. Without this
489
+ # chassis set, it will not set a hostname in the DB's
490
+ self ._test_port_binding_and_status (ports [0 ]['id' ], 'bind' , 'ACTIVE' )
491
+ self .chassis = second_chassis
492
+ self ._test_port_binding_and_status (ports [1 ]['id' ], 'bind' , 'ACTIVE' )
493
+
494
+ # 6) For both ports, bind vip on parent and check hostname in DBs
495
+ for idx in range (len (ports )):
496
+ # Set port binding to the first port, and update the chassis
497
+ self ._set_port_binding_virtual_parent (vip ['id' ], ports [idx ]['id' ])
498
+
499
+ # Check if the host_id has been updated in OVN and DB
500
+ # by the event that eventually calls for method
501
+ # OVNMechanismDriver.update_virtual_port_host
502
+ n_utils .wait_until_true (
503
+ lambda : self ._check_port_host_set (vip ['id' ], hosts [idx ]),
504
+ timeout = 10 )
505
+
420
506
421
507
class TestNBDbMonitorOverTcp (TestNBDbMonitor ):
422
508
def get_ovsdb_server_protocol (self ):
0 commit comments