@@ -358,6 +358,138 @@ def test_live_migrate_server_with_VF(self):
358358 self .assertEqual (500 , ex .response .status_code )
359359 self .assertIn ('NoValidHost' , str (ex ))
360360
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+
361493 def test_live_migrate_server_with_neutron (self ):
362494 """Live migrate an instance using a neutron-provisioned SR-IOV VIF.
363495
0 commit comments