@@ -36,7 +36,28 @@ class PlacementPCIReportingTests(test_pci_sriov_servers._PCIServersTestBase):
36
36
# Just placeholders to satisfy the base class. The real value will be
37
37
# redefined by the tests
38
38
PCI_DEVICE_SPEC = []
39
- PCI_ALIAS = None
39
+ PCI_ALIAS = [
40
+ jsonutils .dumps (x )
41
+ for x in (
42
+ {
43
+ "vendor_id" : fakelibvirt .PCI_VEND_ID ,
44
+ "product_id" : fakelibvirt .PCI_PROD_ID ,
45
+ "name" : "a-pci-dev" ,
46
+ },
47
+ {
48
+ "vendor_id" : fakelibvirt .PCI_VEND_ID ,
49
+ "product_id" : fakelibvirt .PF_PROD_ID ,
50
+ "device_type" : "type-PF" ,
51
+ "name" : "a-pf" ,
52
+ },
53
+ {
54
+ "vendor_id" : fakelibvirt .PCI_VEND_ID ,
55
+ "product_id" : fakelibvirt .VF_PROD_ID ,
56
+ "device_type" : "type-VF" ,
57
+ "name" : "a-vf" ,
58
+ },
59
+ )
60
+ ]
40
61
41
62
def setUp (self ):
42
63
super ().setUp ()
@@ -681,6 +702,179 @@ def test_device_reconfiguration(self):
681
702
},
682
703
)
683
704
705
+ def _create_one_compute_with_a_pf_consumed_by_an_instance (self ):
706
+ # The fake libvirt will emulate on the host:
707
+ # * two type-PFs in slot 0, with one type-VF
708
+ pci_info = fakelibvirt .HostPCIDevicesInfo (
709
+ num_pci = 0 , num_pfs = 1 , num_vfs = 1 )
710
+ # we match the PF only and ignore the VF
711
+ device_spec = self ._to_device_spec_conf (
712
+ [
713
+ {
714
+ "vendor_id" : fakelibvirt .PCI_VEND_ID ,
715
+ "product_id" : fakelibvirt .PF_PROD_ID ,
716
+ "address" : "0000:81:00.0" ,
717
+ },
718
+ ]
719
+ )
720
+ self .flags (group = 'pci' , device_spec = device_spec )
721
+ self .mock_pci_report_in_placement .return_value = True
722
+ self .start_compute (hostname = "compute1" , pci_info = pci_info )
723
+
724
+ self .assertPCIDeviceCounts ("compute1" , total = 1 , free = 1 )
725
+ compute1_expected_placement_view = {
726
+ "inventories" : {
727
+ "0000:81:00.0" : {self .PF_RC : 1 },
728
+ },
729
+ "traits" : {
730
+ "0000:81:00.0" : [],
731
+ },
732
+ "usages" : {
733
+ "0000:81:00.0" : {self .PF_RC : 0 },
734
+ },
735
+ "allocations" : {},
736
+ }
737
+ self .assert_placement_pci_view (
738
+ "compute1" , ** compute1_expected_placement_view )
739
+
740
+ # Create an instance consuming the PF
741
+ extra_spec = {"pci_passthrough:alias" : "a-pf:1" }
742
+ flavor_id = self ._create_flavor (extra_spec = extra_spec )
743
+ server = self ._create_server (flavor_id = flavor_id , networks = [])
744
+
745
+ self .assertPCIDeviceCounts ("compute1" , total = 1 , free = 0 )
746
+ compute1_expected_placement_view ["usages" ] = {
747
+ "0000:81:00.0" : {self .PF_RC : 1 },
748
+ }
749
+ compute1_expected_placement_view ["allocations" ][server ["id" ]] = {
750
+ "0000:81:00.0" : {self .PF_RC : 1 },
751
+ }
752
+ self .assert_placement_pci_view (
753
+ "compute1" , ** compute1_expected_placement_view )
754
+ self ._run_periodics ()
755
+ self .assert_placement_pci_view (
756
+ "compute1" , ** compute1_expected_placement_view )
757
+
758
+ return server , compute1_expected_placement_view
759
+
760
+ def test_device_reconfiguration_with_allocations_config_change_warn (self ):
761
+ server , compute1_expected_placement_view = (
762
+ self ._create_one_compute_with_a_pf_consumed_by_an_instance ())
763
+
764
+ # remove 0000:81:00.0 from the device spec and restart the compute
765
+ device_spec = self ._to_device_spec_conf ([])
766
+ self .flags (group = 'pci' , device_spec = device_spec )
767
+ # The PF is used but removed from the config. The PciTracker warns
768
+ # but keeps the device so the placement logic mimic this and only warns
769
+ # but keeps the RP and the allocation in placement intact.
770
+ self .restart_compute_service (hostname = "compute1" )
771
+ self .assert_placement_pci_view (
772
+ "compute1" , ** compute1_expected_placement_view )
773
+ self ._run_periodics ()
774
+ self .assert_placement_pci_view (
775
+ "compute1" , ** compute1_expected_placement_view )
776
+ # the warning from the PciTracker
777
+ self .assertIn (
778
+ "WARNING [nova.pci.manager] Unable to remove device with status "
779
+ "'allocated' and ownership %s because of PCI device "
780
+ "1:0000:81:00.0 is allocated instead of ['available', "
781
+ "'unavailable', 'unclaimable']. Check your [pci]device_spec "
782
+ "configuration to make sure this allocated device is whitelisted. "
783
+ "If you have removed the device from the whitelist intentionally "
784
+ "or the device is no longer available on the host you will need "
785
+ "to delete the server or migrate it to another host to silence "
786
+ "this warning."
787
+ % server ['id' ],
788
+ self .stdlog .logger .output ,
789
+ )
790
+ # the warning from the placement PCI tracking logic
791
+ self .assertIn (
792
+ "WARNING [nova.compute.pci_placement_translator] Device spec is "
793
+ "not found for device 0000:81:00.0 in [pci]device_spec. We are "
794
+ "skipping this devices during Placement update. The device is "
795
+ "allocated by %s. You should not remove an allocated device from "
796
+ "the configuration. Please restore the configuration or cold "
797
+ "migrate the instance to resolve the inconsistency."
798
+ % server ['id' ],
799
+ self .stdlog .logger .output ,
800
+ )
801
+
802
+ def test_device_reconfiguration_with_allocations_config_change_stop (self ):
803
+ self ._create_one_compute_with_a_pf_consumed_by_an_instance ()
804
+
805
+ # switch 0000:81:00.0 PF to 0000:81:00.1 VF
806
+ # in the config, then restart the compute service
807
+
808
+ # only match the VF now
809
+ device_spec = self ._to_device_spec_conf (
810
+ [
811
+ {
812
+ "vendor_id" : fakelibvirt .PCI_VEND_ID ,
813
+ "product_id" : fakelibvirt .VF_PROD_ID ,
814
+ "address" : "0000:81:00.1" ,
815
+ },
816
+ ]
817
+ )
818
+ self .flags (group = 'pci' , device_spec = device_spec )
819
+ # The compute fails to start as the new config would mean that the PF
820
+ # inventory is removed from the 0000:81:00.0 RP and the PF inventory is
821
+ # added instead there, but the VF inventory has allocations. Keeping
822
+ # the old inventory as in
823
+ # test_device_reconfiguration_with_allocations_config_change_warn is
824
+ # not an option as it would result in two resource class on the same RP
825
+ # one for the PF and one for the VF. That would allow consuming
826
+ # the same physical device twice. Such dependent device configuration
827
+ # is intentionally not supported so we are stopping the compute
828
+ # service.
829
+ ex = self .assertRaises (
830
+ exception .PlacementPciException ,
831
+ self .restart_compute_service ,
832
+ hostname = "compute1"
833
+ )
834
+ self .assertRegex (
835
+ str (ex ),
836
+ "Failed to gather or report PCI resources to Placement: There was "
837
+ "a conflict when trying to complete your request.\n \n "
838
+ "update conflict: Inventory for 'CUSTOM_PCI_8086_1528' on "
839
+ "resource provider '.*' in use." ,
840
+ )
841
+
842
+ def test_device_reconfiguration_with_allocations_hyp_change (self ):
843
+ server , compute1_expected_placement_view = (
844
+ self ._create_one_compute_with_a_pf_consumed_by_an_instance ())
845
+
846
+ # restart the compute but simulate that the device 0000:81:00.0 is
847
+ # removed from the hypervisor while the device spec config left
848
+ # intact. The PciTracker will notice this and log a warning. The
849
+ # placement tracking logic simply keeps the allocation intact in
850
+ # placement as both the PciDevice and the DeviceSpec is available.
851
+ pci_info = fakelibvirt .HostPCIDevicesInfo (
852
+ num_pci = 0 , num_pfs = 0 , num_vfs = 0 )
853
+ self .restart_compute_service (
854
+ hostname = "compute1" ,
855
+ pci_info = pci_info ,
856
+ keep_hypervisor_state = False
857
+ )
858
+ self .assert_placement_pci_view (
859
+ "compute1" , ** compute1_expected_placement_view )
860
+ self ._run_periodics ()
861
+ self .assert_placement_pci_view (
862
+ "compute1" , ** compute1_expected_placement_view )
863
+ # the warning from the PciTracker
864
+ self .assertIn (
865
+ "WARNING [nova.pci.manager] Unable to remove device with status "
866
+ "'allocated' and ownership %s because of PCI device "
867
+ "1:0000:81:00.0 is allocated instead of ['available', "
868
+ "'unavailable', 'unclaimable']. Check your [pci]device_spec "
869
+ "configuration to make sure this allocated device is whitelisted. "
870
+ "If you have removed the device from the whitelist intentionally "
871
+ "or the device is no longer available on the host you will need "
872
+ "to delete the server or migrate it to another host to silence "
873
+ "this warning."
874
+ % server ['id' ],
875
+ self .stdlog .logger .output ,
876
+ )
877
+
684
878
def test_reporting_disabled_nothing_is_reported (self ):
685
879
# The fake libvirt will emulate on the host:
686
880
# * one type-PCI in slot 0
@@ -765,32 +959,6 @@ def setUp(self):
765
959
)
766
960
)
767
961
768
- # Pre-configure a PCI alias to consume our devs
769
- alias_pci = {
770
- "vendor_id" : fakelibvirt .PCI_VEND_ID ,
771
- "product_id" : fakelibvirt .PCI_PROD_ID ,
772
- "name" : "a-pci-dev" ,
773
- }
774
- alias_pf = {
775
- "vendor_id" : fakelibvirt .PCI_VEND_ID ,
776
- "product_id" : fakelibvirt .PF_PROD_ID ,
777
- "device_type" : "type-PF" ,
778
- "name" : "a-pf" ,
779
- }
780
- alias_vf = {
781
- "vendor_id" : fakelibvirt .PCI_VEND_ID ,
782
- "product_id" : fakelibvirt .VF_PROD_ID ,
783
- "device_type" : "type-VF" ,
784
- "name" : "a-vf" ,
785
- }
786
- self .flags (
787
- group = 'pci' ,
788
- alias = self ._to_pci_alias_conf ([alias_pci , alias_pf , alias_vf ]))
789
-
790
- @staticmethod
791
- def _to_pci_alias_conf (alias_list ):
792
- return [jsonutils .dumps (x ) for x in alias_list ]
793
-
794
962
@staticmethod
795
963
def _move_allocation (allocations , from_uuid , to_uuid ):
796
964
allocations [to_uuid ] = allocations [from_uuid ]
0 commit comments