class TestXMLMatchesUnorderedExtraChildren(testtools.TestCase, helpers.TestMatchersInterface): matches_matcher = matchers.XMLMatches("""<?xml version="1.0"?> <root> <children> <child1 /> <child2 /> </children> </root>""", allow_mixed_nodes=True) matches_matches = [] matches_mismatches = [] describe_examples = [ ("/root/children[0]/child2[1]: XML unexpected child element <foo> " "present at index 0", """<?xml version="1.0"?> <root> <children> <child1 /> <child2> <foo>subchild</foo> </child2> </children> </root>""", matches_matcher), ] str_examples = []
def test_update_memory_backing_discard_keep(self): data = objects.LibvirtLiveMigrateData( dst_wants_file_backed_memory=True, file_backed_memory_discard=True) xml = """<domain> <memoryBacking> <source type="file"/> <access mode="shared"/> <allocation mode="immediate"/> <discard /> </memoryBacking> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_memory_backing_xml(doc, data), encoding='unicode') self.assertThat( res, matchers.XMLMatches("""<domain> <memoryBacking> <source type="file"/> <access mode="shared"/> <allocation mode="immediate"/> <discard /> </memoryBacking> </domain>"""))
def _test_update_vif_xml(self, conf, original_xml, expected_xml): """Simulates updating the guest xml for live migrating from a host using OVS to a host using vhostuser as the networking backend. """ vif_ovs = network_model.VIF(id=uuids.vif, address='DE:AD:BE:EF:CA:FE', details={'port_filter': False}, devname='tap-xxx-yyy-zzz', ovs_interfaceid=uuids.ovs) source_vif = vif_ovs migrate_vifs = [ objects.VIFMigrateData( port_id=uuids.port_id, vnic_type=network_model.VNIC_TYPE_NORMAL, vif_type=network_model.VIF_TYPE_VHOSTUSER, vif_details={'vhostuser_socket': '/vhost-user/test.sock'}, profile={}, host='dest.host', source_vif=source_vif) ] data = objects.LibvirtLiveMigrateData(vifs=migrate_vifs) get_vif_config = mock.MagicMock(return_value=conf) doc = etree.fromstring(original_xml) updated_xml = etree.tostring(migration._update_vif_xml( doc, data, get_vif_config), encoding='unicode') self.assertThat(updated_xml, matchers.XMLMatches(expected_xml))
class TestXMLMatchesUnorderedNodes(testtools.TestCase, helpers.TestMatchersInterface): matches_matcher = matchers.XMLMatches("""<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="DONTCARE"/> <children> <child3>DONTCARE</child3> <!--This is a comment--> <child2>child 2</child2> <child1>child 1</child1> <?spam processing instruction?> </children> </root>""", allow_mixed_nodes=True) matches_matches = ["""<?xml version="1.0"?> <root> <text>some text here</text> <attrs key2="spam" key1="spam"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> <text>some other text here</text> </root>""", ] matches_mismatches = ["""<?xml version="1.0"?> <root> <text>some text here</text> <text>mismatch text</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", ] describe_examples = [ ("/root: XML expected child element <text> not present at index 4", """<?xml version="1.0"?> <root> <text>some text here</text> <text>mismatch text</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", matches_matcher), ] str_examples = []
def test_update_volume_xml(self): connection_info = { 'driver_volume_type': 'iscsi', 'serial': '58a84f6d-3f0c-4e19-a0af-eb657b790657', 'data': { 'access_mode': 'rw', 'target_discovered': False, 'target_iqn': 'ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z', 'volume_id': '58a84f6d-3f0c-4e19-a0af-eb657b790657', 'device_path': '/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z' } } bdm = objects.LibvirtLiveMigrateBDMInfo( serial='58a84f6d-3f0c-4e19-a0af-eb657b790657', bus='virtio', type='disk', dev='vdb', connection_info=connection_info) data = objects.LibvirtLiveMigrateData(target_connect_addr='127.0.0.1', bdms=[bdm], block_migration=False) xml = """<domain> <devices> <disk type='block' device='disk'> <driver name='qemu' type='raw' cache='none'/> <source dev='/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X'/> <target bus='virtio' dev='vdb'/> <serial>58a84f6d-3f0c-4e19-a0af-eb657b790657</serial> <address type='pci' domain='0x0' bus='0x0' slot='0x04' function='0x0'/> </disk> </devices> </domain>""" conf = vconfig.LibvirtConfigGuestDisk() conf.source_device = bdm.type conf.driver_name = "qemu" conf.driver_format = "raw" conf.driver_cache = "none" conf.target_dev = bdm.dev conf.target_bus = bdm.bus conf.serial = bdm.connection_info.get('serial') conf.source_type = "block" conf.source_path = bdm.connection_info['data'].get('device_path') get_volume_config = mock.MagicMock(return_value=conf) doc = etree.fromstring(xml) res = etree.tostring(migration._update_volume_xml( doc, data, get_volume_config), encoding='unicode') new_xml = xml.replace('ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X', 'ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z') self.assertThat(res, matchers.XMLMatches(new_xml))
def test_update_perf_events_xml_add_new_events(self): data = objects.LibvirtLiveMigrateData( supported_perf_events=['cmt']) xml = """<domain> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_perf_events_xml(doc, data)) self.assertThat(res, matchers.XMLMatches("""<domain> <perf><event enabled="yes" name="cmt"/></perf></domain>"""))
def test_update_serial_xml_serial(self): data = objects.LibvirtLiveMigrateData(serial_listen_addr='127.0.0.100') xml = """<domain> <devices> <serial type="tcp"> <source host="127.0.0.1"/> </serial> </devices> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_serial_xml(doc, data)) new_xml = xml.replace("127.0.0.1", "127.0.0.100") self.assertThat(res, matchers.XMLMatches(new_xml))
def test_update_serial_xml_serial(self): data = objects.LibvirtLiveMigrateData(serial_listen_addr='127.0.0.100', serial_listen_ports=[2001]) xml = """<domain> <devices> <serial type="tcp"> <source host="127.0.0.1" service="2000"/> <target type="serial" port="0"/> </serial> </devices> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_serial_xml(doc, data), encoding='unicode') new_xml = xml.replace("127.0.0.1", "127.0.0.100").replace("2000", "2001") self.assertThat(res, matchers.XMLMatches(new_xml))
def test_update_graphics(self): data = objects.LibvirtLiveMigrateData( graphics_listen_addr_vnc='127.0.0.100', graphics_listen_addr_spice='127.0.0.200') xml = """<domain> <devices> <graphics type="vnc"> <listen type="address" address="127.0.0.1"/> </graphics> <graphics type="spice"> <listen type="address" address="127.0.0.2"/> </graphics> </devices> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_graphics_xml(doc, data)) new_xml = xml.replace("127.0.0.1", "127.0.0.100") new_xml = new_xml.replace("127.0.0.2", "127.0.0.200") self.assertThat(res, matchers.XMLMatches(new_xml))
def test_update_serial_xml_without_ports(self): # This test is for backwards compatibility when we don't # get the serial ports from the target node. data = objects.LibvirtLiveMigrateData(serial_listen_addr='127.0.0.100', serial_listen_ports=[]) xml = """<domain> <devices> <console type="tcp"> <source host="127.0.0.1" service="2001"/> <target type="serial" port="0"/> </console> <console type="tcp"> <source host="127.0.0.1" service="2002"/> <target type="serial" port="1"/> </console> </devices> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_serial_xml(doc, data)) new_xml = xml.replace("127.0.0.1", "127.0.0.100") self.assertThat(res, matchers.XMLMatches(new_xml))
def test_update_serial_xml_console(self): data = objects.LibvirtLiveMigrateData(serial_listen_addr='127.0.0.100', serial_listen_ports=[299, 300]) xml = """<domain> <devices> <console type="tcp"> <source host="127.0.0.1" service="2001"/> <target type="serial" port="0"/> </console> <console type="tcp"> <source host="127.0.0.1" service="2002"/> <target type="serial" port="1"/> </console> </devices> </domain>""" doc = etree.fromstring(xml) res = etree.tostring(migration._update_serial_xml(doc, data)) new_xml = xml.replace("127.0.0.1", "127.0.0.100").replace( "2001", "299").replace("2002", "300") self.assertThat(res, matchers.XMLMatches(new_xml))
def assertXmlEqual(self, expected, observed, **options): self.assertThat(observed, matchers.XMLMatches(expected, **options))
class TestXMLMatches(testtools.TestCase, helpers.TestMatchersInterface): matches_matcher = matchers.XMLMatches("""<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="DONTCARE"/> <children> <!--This is a comment--> <child1>child 1</child1> <child2>child 2</child2> <child3>DONTCARE</child3> <?spam processing instruction?> </children> </root>""", allow_mixed_nodes=False) matches_matches = [ """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key2="spam" key1="spam"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children><child1>child 1</child1> <child2>child 2</child2> <child3>blah</child3> </children> </root>""", ] matches_mismatches = [ """<?xml version="1.0"?> <root> <text>some text here</text> <text>mismatch text</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key3="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="quux" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child4>child 4</child4> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> <child4>child 4</child4> </children> </root>""", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="DONTCARE"/> <children> <!--This is a comment--> <child2>child 2</child2> <child1>child 1</child1> <child3>DONTCARE</child3> <?spam processing instruction?> </children> </root>""", """<?xml version="1.1"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="DONTCARE"/> <children> <!--This is a comment--> <child1>child 1</child1> <child2>child 2</child2> <child3>DONTCARE</child3> <?spam processing instruction?> </children> </root>""", ] str_examples = [ ("XMLMatches('<?xml version=\"1.0\"?>\\n" "<root>\\n" " <text>some text here</text>\\n" " <text>some other text here</text>\\n" " <attrs key1=\"spam\" key2=\"DONTCARE\"/>\\n" " <children>\\n" " <!--This is a comment-->\\n" " <child1>child 1</child1>\\n" " <child2>child 2</child2>\\n" " <child3>DONTCARE</child3>\\n" " <?spam processing instruction?>\\n" " </children>\\n" "</root>')", matches_matcher), ] describe_examples = [ ("/root/text[1]: XML text value mismatch: expected text value: " "['some other text here']; actual value: ['mismatch text']", """<?xml version="1.0"?> <root> <text>some text here</text> <text>mismatch text</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", matches_matcher), ("/root/attrs[2]: XML attributes mismatch: keys only in expected: " "key2; keys only in actual: key3", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key3="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", matches_matcher), ("/root/attrs[2]: XML attribute value mismatch: expected value of " "attribute key1: 'spam'; actual value: 'quux'", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="quux" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", matches_matcher), ("/root/children[3]: XML tag mismatch at index 1: expected tag " "<child2>; actual tag <child4>", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child4>child 4</child4> <child2>child 2</child2> <child3>child 3</child3> </children> </root>""", matches_matcher), ("/root/children[3]: XML expected child element <child3> not " "present at index 2", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> </children> </root>""", matches_matcher), ("/root/children[3]: XML unexpected child element <child4> " "present at index 3", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child1>child 1</child1> <child2>child 2</child2> <child3>child 3</child3> <child4>child 4</child4> </children> </root>""", matches_matcher), ("/root/children[3]: XML tag mismatch at index 0: " "expected tag <child1>; actual tag <child2>", """<?xml version="1.0"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="quux"/> <children> <child2>child 2</child2> <child1>child 1</child1> <child3>child 3</child3> </children> </root>""", matches_matcher), ("/: XML information mismatch(version, encoding) " "expected version 1.0, expected encoding UTF-8; " "actual version 1.1, actual encoding UTF-8", """<?xml version="1.1"?> <root> <text>some text here</text> <text>some other text here</text> <attrs key1="spam" key2="DONTCARE"/> <children> <!--This is a comment--> <child1>child 1</child1> <child2>child 2</child2> <child3>DONTCARE</child3> <?spam processing instruction?> </children> </root>""", matches_matcher), ]
def test_update_vif_xml(self): """Simulates updating the guest xml for live migrating from a host using OVS to a host using vhostuser as the networking backend. """ vif_ovs = network_model.VIF(id=uuids.vif, address='DE:AD:BE:EF:CA:FE', details={'port_filter': False}, devname='tap-xxx-yyy-zzz', ovs_interfaceid=uuids.ovs) source_vif = vif_ovs migrate_vifs = [ objects.VIFMigrateData( port_id=uuids.port_id, vnic_type=network_model.VNIC_TYPE_NORMAL, vif_type=network_model.VIF_TYPE_VHOSTUSER, vif_details={'vhostuser_socket': '/vhost-user/test.sock'}, profile={}, host='dest.host', source_vif=source_vif) ] data = objects.LibvirtLiveMigrateData(vifs=migrate_vifs) original_xml = """<domain> <uuid>3de6550a-8596-4937-8046-9d862036bca5</uuid> <devices> <interface type="bridge"> <mac address="DE:AD:BE:EF:CA:FE"/> <model type="virtio"/> <source bridge="qbra188171c-ea"/> <target dev="tapa188171c-ea"/> <virtualport type="openvswitch"> <parameters interfaceid="%s"/> </virtualport> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </interface> </devices> </domain>""" % uuids.ovs conf = vconfig.LibvirtConfigGuestInterface() conf.net_type = "vhostuser" conf.vhostuser_type = "unix" conf.vhostuser_mode = "server" conf.mac_addr = "DE:AD:BE:EF:CA:FE" conf.vhostuser_path = "/vhost-user/test.sock" conf.model = "virtio" get_vif_config = mock.MagicMock(return_value=conf) doc = etree.fromstring(original_xml) updated_xml = etree.tostring(migration._update_vif_xml( doc, data, get_vif_config), encoding='unicode') # Note that <target> and <virtualport> are dropped from the ovs source # interface xml since they aren't applicable to the vhostuser # destination interface xml. The type attribute value changes and the # hardware address element is retained. expected_xml = """<domain> <uuid>3de6550a-8596-4937-8046-9d862036bca5</uuid> <devices> <interface type="vhostuser"> <mac address="DE:AD:BE:EF:CA:FE"/> <model type="virtio"/> <source mode="server" path="/vhost-user/test.sock" type="unix"/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </interface> </devices> </domain>""" self.assertThat(updated_xml, matchers.XMLMatches(expected_xml))
def test_update_volume_xml_update_encryption(self): connection_info = { 'driver_volume_type': 'rbd', 'serial': 'd299a078-f0db-4993-bf03-f10fe44fd192', 'data': { 'access_mode': 'rw', 'secret_type': 'ceph', 'name': 'cinder-volumes/volume-d299a078', 'encrypted': False, 'discard': True, 'cluster_name': 'ceph', 'secret_uuid': '1a790a26-dd49-4825-8d16-3dd627cf05a9', 'qos_specs': None, 'auth_enabled': True, 'volume_id': 'd299a078-f0db-4993-bf03-f10fe44fd192', 'hosts': ['172.16.128.101', '172.16.128.121'], 'auth_username': '******', 'ports': ['6789', '6789', '6789'] } } bdm = objects.LibvirtLiveMigrateBDMInfo( serial='d299a078-f0db-4993-bf03-f10fe44fd192', bus='scsi', type='disk', dev='sdb', connection_info=connection_info, encryption_secret_uuid=uuids.encryption_secret_uuid_new) data = objects.LibvirtLiveMigrateData(target_connect_addr=None, bdms=[bdm], block_migration=False) xml = """<domain> <devices> <disk type='network' device='disk'> <driver name='qemu' type='raw' cache='writeback' discard='unmap'/> <auth username='******'> <secret type='ceph' uuid='1a790a26-dd49-4825-8d16-3dd627cf05a9'/> </auth> <source protocol='rbd' name='cinder-volumes/volume-d299a078'> <host name='172.16.128.101' port='6789'/> <host name='172.16.128.121' port='6789'/> </source> <backingStore/> <target dev='sdb' bus='scsi'/> <serial>d299a078-f0db-4993-bf03-f10fe44fd192</serial> <alias name='scsi0-0-0-1'/> <encryption format='luks'> <secret type='passphrase' uuid='%(encryption_secret_uuid)s'/> </encryption> <address type='drive' controller='0' bus='0' target='0' unit='1'/> </disk> </devices> </domain>""" % { 'encryption_secret_uuid': uuids.encryption_secret_uuid_old } conf = vconfig.LibvirtConfigGuestDisk() conf.source_device = bdm.type conf.driver_name = "qemu" conf.driver_format = "raw" conf.driver_cache = "writeback" conf.target_dev = bdm.dev conf.target_bus = bdm.bus conf.serial = bdm.connection_info.get('serial') conf.source_type = "network" conf.driver_discard = 'unmap' conf.device_addr = vconfig.LibvirtConfigGuestDeviceAddressDrive() conf.device_addr.controller = 0 get_volume_config = mock.MagicMock(return_value=conf) doc = etree.fromstring(xml) res = etree.tostring(migration._update_volume_xml( doc, data, get_volume_config), encoding='unicode') new_xml = xml.replace(uuids.encryption_secret_uuid_old, uuids.encryption_secret_uuid_new) self.assertThat(res, matchers.XMLMatches(new_xml))
def test_pci_devices_generation(self): def _cmp_pci_dev_addr(dev_xml, cmp_addr): cfgdev = vconfig.LibvirtConfigNodeDevice() cfgdev.parse_str(dev_xml) address = "%04x:%02x:%02x.%1x" % ( cfgdev.pci_capability.domain, cfgdev.pci_capability.bus, cfgdev.pci_capability.slot, cfgdev.pci_capability.function) self.assertEqual(cmp_addr, address) pf_xml = """<device> <name>pci_0000_81_00_0</name> <path>/sys/devices/pci0000:80/0000:80:01.0/0000:81:00.0</path> <parent>pci_0000_80_01_0</parent> <driver> <name>ixgbe</name> </driver> <capability type='pci'> <domain>0</domain> <bus>129</bus> <slot>0</slot> <function>0</function> <product id='0x1528'>Ethernet Controller 10-Gigabit X540-AT2</product> <vendor id='0x8086'>Intel Corporation</vendor> <capability type='virt_functions'> <address domain='0x0000' bus='0x81' slot='0x0' function='0x1'/> </capability> <iommuGroup number='40'> <address domain='0x0000' bus='0x81' slot='0x0' function='0x0'/> </iommuGroup> <numa node='0'/> <pci-express> <link validity='cap' port='0' speed='5' width='8'/> <link validity='sta' speed='5' width='8'/> </pci-express> </capability> </device>""" vf_xml = """<device> <name>pci_0000_81_00_1</name> <path>/sys/devices/pci0000:80/0000:80:01.0/0000:81:00.1</path> <parent>pci_0000_80_01_0</parent> <driver> <name>ixgbevf</name> </driver> <capability type='pci'> <domain>0</domain> <bus>129</bus> <slot>0</slot> <function>1</function> <product id='0x1515'>X540 Ethernet Controller Virtual Function</product> <vendor id='0x8086'>Intel Corporation</vendor> <capability type='phys_function'> <address domain='0x0000' bus='0x81' slot='0x0' function='0x0'/> </capability> <iommuGroup number='41'> <address domain='0x0000' bus='0x81' slot='0x0' function='0x1'/> </iommuGroup> <numa node='0'/> <pci-express> <link validity='cap' port='0' speed='5' width='8'/> <link validity='sta' speed='5' width='8'/> </pci-express> </capability> </device>""" # create fake pci devices pci_info = libvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=1) # generate xml for the created pci devices gen_pf = pci_info.get_device_by_name('pci_0000_81_00_0') gen_vf = pci_info.get_device_by_name('pci_0000_81_00_1') self.assertThat(gen_pf.XMLDesc(0), matchers.XMLMatches(pf_xml)) self.assertThat(gen_vf.XMLDesc(0), matchers.XMLMatches(vf_xml)) # parse the generated xml with a libvirt config class and compare # device address _cmp_pci_dev_addr(pf_xml, '0000:81:00.0') _cmp_pci_dev_addr(vf_xml, '0000:81:00.1')