@@ -358,6 +358,138 @@ def test_live_migrate_server_with_VF(self):
358
358
self .assertEqual (500 , ex .response .status_code )
359
359
self .assertIn ('NoValidHost' , str (ex ))
360
360
361
+ def _test_move_operation_with_neutron (self , move_operation ,
362
+ expect_fail = False ):
363
+ # The purpose here is to force an observable PCI slot update when
364
+ # moving from source to dest. This is accomplished by having a single
365
+ # PCI device on the source, 2 PCI devices on the test, and relying on
366
+ # the fact that our fake HostPCIDevicesInfo creates predictable PCI
367
+ # addresses. The PCI device on source and the first PCI device on dest
368
+ # will have identical PCI addresses. By sticking a "placeholder"
369
+ # instance on that first PCI device on the dest, the incoming instance
370
+ # from source will be forced to consume the second dest PCI device,
371
+ # with a different PCI address.
372
+ self .start_compute (
373
+ hostname = 'source' ,
374
+ pci_info = fakelibvirt .HostPCIDevicesInfo (
375
+ num_pfs = 1 , num_vfs = 1 ))
376
+ self .start_compute (
377
+ hostname = 'dest' ,
378
+ pci_info = fakelibvirt .HostPCIDevicesInfo (
379
+ num_pfs = 1 , num_vfs = 2 ))
380
+
381
+ source_port = self .neutron .create_port (
382
+ {'port' : self .neutron .network_4_port_1 })
383
+ dest_port1 = self .neutron .create_port (
384
+ {'port' : self .neutron .network_4_port_2 })
385
+ dest_port2 = self .neutron .create_port (
386
+ {'port' : self .neutron .network_4_port_3 })
387
+
388
+ source_server = self ._create_server (
389
+ networks = [{'port' : source_port ['port' ]['id' ]}], host = 'source' )
390
+ dest_server1 = self ._create_server (
391
+ networks = [{'port' : dest_port1 ['port' ]['id' ]}], host = 'dest' )
392
+ dest_server2 = self ._create_server (
393
+ networks = [{'port' : dest_port2 ['port' ]['id' ]}], host = 'dest' )
394
+
395
+ # Refresh the ports.
396
+ source_port = self .neutron .show_port (source_port ['port' ]['id' ])
397
+ dest_port1 = self .neutron .show_port (dest_port1 ['port' ]['id' ])
398
+ dest_port2 = self .neutron .show_port (dest_port2 ['port' ]['id' ])
399
+
400
+ # Find the server on the dest compute that's using the same pci_slot as
401
+ # the server on the source compute, and delete the other one to make
402
+ # room for the incoming server from the source.
403
+ source_pci_slot = source_port ['port' ]['binding:profile' ]['pci_slot' ]
404
+ dest_pci_slot1 = dest_port1 ['port' ]['binding:profile' ]['pci_slot' ]
405
+ if dest_pci_slot1 == source_pci_slot :
406
+ same_slot_port = dest_port1
407
+ self ._delete_server (dest_server2 )
408
+ else :
409
+ same_slot_port = dest_port2
410
+ self ._delete_server (dest_server1 )
411
+
412
+ # Before moving, explictly assert that the servers on source and dest
413
+ # have the same pci_slot in their port's binding profile
414
+ self .assertEqual (source_port ['port' ]['binding:profile' ]['pci_slot' ],
415
+ same_slot_port ['port' ]['binding:profile' ]['pci_slot' ])
416
+
417
+ # Before moving, assert that the servers on source and dest have the
418
+ # same PCI source address in their XML for their SRIOV nic.
419
+ source_conn = self .computes ['source' ].driver ._host .get_connection ()
420
+ dest_conn = self .computes ['source' ].driver ._host .get_connection ()
421
+ source_vms = [vm ._def for vm in source_conn ._vms .values ()]
422
+ dest_vms = [vm ._def for vm in dest_conn ._vms .values ()]
423
+ self .assertEqual (1 , len (source_vms ))
424
+ self .assertEqual (1 , len (dest_vms ))
425
+ self .assertEqual (1 , len (source_vms [0 ]['devices' ]['nics' ]))
426
+ self .assertEqual (1 , len (dest_vms [0 ]['devices' ]['nics' ]))
427
+ self .assertEqual (source_vms [0 ]['devices' ]['nics' ][0 ]['source' ],
428
+ dest_vms [0 ]['devices' ]['nics' ][0 ]['source' ])
429
+
430
+ move_operation (source_server )
431
+
432
+ # Refresh the ports again, keeping in mind the source_port is now bound
433
+ # on the dest after unshelving.
434
+ source_port = self .neutron .show_port (source_port ['port' ]['id' ])
435
+ same_slot_port = self .neutron .show_port (same_slot_port ['port' ]['id' ])
436
+
437
+ # FIXME(artom) Until bug 1851545 is fixed, unshelve will not update the
438
+ # pci_slot.
439
+ if expect_fail :
440
+ self .assertEqual (
441
+ source_port ['port' ]['binding:profile' ]['pci_slot' ],
442
+ same_slot_port ['port' ]['binding:profile' ]['pci_slot' ])
443
+ else :
444
+ self .assertNotEqual (
445
+ source_port ['port' ]['binding:profile' ]['pci_slot' ],
446
+ same_slot_port ['port' ]['binding:profile' ]['pci_slot' ])
447
+
448
+ conn = self .computes ['dest' ].driver ._host .get_connection ()
449
+ vms = [vm ._def for vm in conn ._vms .values ()]
450
+ self .assertEqual (2 , len (vms ))
451
+ for vm in vms :
452
+ self .assertEqual (1 , len (vm ['devices' ]['nics' ]))
453
+ # FIXME(artom) Until bug 1851545 is fixed, unshelve will not update the
454
+ # XML.
455
+ if expect_fail :
456
+ self .assertEqual (vms [0 ]['devices' ]['nics' ][0 ]['source' ],
457
+ vms [1 ]['devices' ]['nics' ][0 ]['source' ])
458
+ else :
459
+ self .assertNotEqual (vms [0 ]['devices' ]['nics' ][0 ]['source' ],
460
+ vms [1 ]['devices' ]['nics' ][0 ]['source' ])
461
+
462
+ def test_unshelve_server_with_neutron (self ):
463
+ def move_operation (source_server ):
464
+ self ._shelve_server (source_server )
465
+ # Disable the source compute, to force unshelving on the dest.
466
+ self .api .put_service (self .computes ['source' ].service_ref .uuid ,
467
+ {'status' : 'disabled' })
468
+ self ._unshelve_server (source_server )
469
+ # FIXME(artom) Bug 1851545 means we explain failure here: the pci_slot
470
+ # and XML will not get updated.
471
+ self ._test_move_operation_with_neutron (move_operation ,
472
+ expect_fail = True )
473
+
474
+ def test_cold_migrate_server_with_neutron (self ):
475
+ def move_operation (source_server ):
476
+ # TODO(stephenfin): The mock of 'migrate_disk_and_power_off' should
477
+ # probably be less...dumb
478
+ with mock .patch ('nova.virt.libvirt.driver.LibvirtDriver'
479
+ '.migrate_disk_and_power_off' , return_value = '{}' ):
480
+ self ._migrate_server (source_server )
481
+ self ._confirm_resize (source_server )
482
+ self ._test_move_operation_with_neutron (move_operation )
483
+
484
+ def test_evacuate_server_with_neutron (self ):
485
+ def move_operation (source_server ):
486
+ # Down the source compute to enable the evacuation
487
+ self .api .put_service (self .computes ['source' ].service_ref .uuid ,
488
+ {'forced_down' : True })
489
+ self .computes ['source' ].stop ()
490
+ self ._evacuate_server (source_server )
491
+ self ._test_move_operation_with_neutron (move_operation )
492
+
361
493
def test_live_migrate_server_with_neutron (self ):
362
494
"""Live migrate an instance using a neutron-provisioned SR-IOV VIF.
363
495
0 commit comments