def test_make_name(self, prefix, iface): for index, value in _CONVERTED_VALUES: computed = drivename.make(iface, index) expected = prefix + value assert computed == expected, \ "mismatch for %s: computed=%s expected=%s" % ( (iface, index), computed, expected)
def test_change_cd_loading(): sd_id = str(uuid.uuid4()) img_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) drive_spec = { "device": "cdrom", "domainID": sd_id, "poolID": str(uuid.uuid4()), "imageID": img_id, "volumeID": vol_id, } cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": drive_spec, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) with fake.VM( cif=ClientIF(), create_device_objects=True, xmldevices=EMPTY_CD_DEVICE_XML, metadata=EMPTY_CD_METADATA_XML ) as fakevm: fakevm._dom = fake.Domain() fakevm.changeCD(cdrom_spec) assert (sd_id, img_id, vol_id) in fakevm.cif.irs.prepared_volumes with fakevm._md_desc.device(devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(drive_spec, dev) assert "change" not in dev
def test_specParams_ignore_empty(self): conf = { 'device': 'cdrom', 'iface': 'ide', 'index': '3', 'path': '', 'readonly': 'true', 'type': 'disk', } source_xml = """<?xml version='1.0' encoding='UTF-8'?> <domain type="kvm" xmlns:ovirt-vm="http://ovirt.org/vm/1.0"> <metadata> <ovirt-vm:vm> <ovirt-vm:device name="hdd" devtype="disk"> <ovirt-vm:specParams /> </ovirt-vm:device> </ovirt-vm:vm> </metadata> </domain>""" desc = metadata.Descriptor.from_xml(source_xml) with desc.device(devtype=conf['type'], name=drivename.make(conf['iface'], conf['index'])) as dev: # test we ignore IO tune settings from metadata, should we # (unexpectedly) found it (see below to learn why) self.assertEqual(dev, {'specParams': {}})
def test_cd_recovery_after_cd_eject(rec_vm_after_eject): # Simulate recovery when user wants to eject CD and failure happened once # the metadata was updated with change element and the CD has been already # ejected from VM. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": CD_PDIV, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Prepare image for the CD to check that it is not torn down. drive = dict(CD_PDIV) drive["device"] = "cdrom" rec_vm_after_eject.cif.prepareVolumePath(drive) # Run VM with recovery turned on. rec_vm_after_eject.run() # Vm.run() waits for Vm._vmStartEvent which is set before recovery # starts. Wait little bit for recovery. wait_for_recovery(rec_vm_after_eject) volume = ( CD_PDIV["domainID"], CD_PDIV["imageID"], CD_PDIV["volumeID"] ) assert volume not in rec_vm_after_eject.cif.irs.prepared_volumes with rec_vm_after_eject._md_desc.device( devtype=hwclass.DISK, name=device) as dev: assert dev == {}
def test_change_cd(vm_with_cd): new_sd_id = str(uuid.uuid4()) new_img_id = str(uuid.uuid4()) new_vol_id = str(uuid.uuid4()) new_drive_spec = { "device": "cdrom", "domainID": new_sd_id, "poolID": str(uuid.uuid4()), "imageID": new_img_id, "volumeID": new_vol_id, } cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": new_drive_spec, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Change CD. vm_with_cd.changeCD(cdrom_spec) volume = (new_sd_id, new_img_id, new_vol_id) assert volume in vm_with_cd.cif.irs.prepared_volumes with vm_with_cd._md_desc.device(devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(new_drive_spec, dev) assert "change" not in dev
def test_make_name(self, prefix, iface): for index, value in _CONVERTED_VALUES: computed = drivename.make(iface, index) expected = prefix + value self.assertEqual( computed, expected, "mismatch for %s: computed=%s expected=%s" % ( (iface, index), computed, expected))
def test_deeply_nested_metadata_preserved(self): conf = { 'device': 'cdrom', 'iface': 'ide', 'index': '3', 'path': '', 'readonly': 'true', 'specParams': { 'vmPayload': { 'file': { 'openstack/content/0000': 'something', 'openstack/latest/meta_data.json': 'something', 'openstack/latest/user_data': 'something', }, 'volId': 'config-2', } }, 'type': 'disk', } expected_xml = """<?xml version='1.0' encoding='UTF-8'?> <vm> <device name="hdd" devtype="disk"> <device>cdrom</device> <iface>ide</iface> <index>3</index> <path /> <readonly>true</readonly> <type>disk</type> <specParams> <vmPayload> <volId>config-2</volId> <file path='openstack/content/0000'>something</file> <file path='openstack/latest/meta_data.json'>something</file> <file path='openstack/latest/user_data'>something</file> </vmPayload> </specParams> </device> </vm>""" desc = metadata.Descriptor() with desc.device(devtype=conf['type'], name=drivename.make(conf['iface'], conf['index'])) as dev: dev.update(conf) dom = FakeDomain() desc.dump(dom) # the first dump() used to -wrongly- modify the supplied data desc.dump(dom) for produced_xml in dom.xml.values(): self.assertXMLEqual(produced_xml, expected_xml)
def test_change_cd_failed_libvirt_and_vol_teardown(monkeypatch, vm_with_cd): device = drivename.make("sata", "2") vm_with_cd._dom = fake.Domain(virtError=libvirt.VIR_ERR_XML_ERROR) def failing_teardown(self, sdUUID, spUUID, imgUUID, volUUID=None): raise Exception("Image teardown failed.") monkeypatch.setattr(fake.IRS, "teardownImage", failing_teardown) # Verify, that ChangeDiskFailed is thrown when libvirt fails to update disk # device and teardown of loaded CD also fails. Contrary to # test_change_cd_failed_libvirt(), now CD is loaded so tear down of the CD # is called, which will fail as we monkey patch it. with pytest.raises(exception.ChangeDiskFailed): vm_with_cd._change_cd(device, LOADING_DRIVE_SPEC)
def test_cd_recovery_after_cd_change(rec_vm_after_change): # Simulate recovery when failure happened once the metadata was updated # with change element, new CD image was prepared and switched in VM, but # metadata after the change wasn't updated and old CD torn down. In this # case both images are prepared and old one needs to be torn down. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": CD_PDIV, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Prepare image for old CD to check that it is torn down. drive = dict(CD_PDIV) drive["device"] = "cdrom" rec_vm_after_change.cif.prepareVolumePath(drive) # Prepare image for loaded CD. drive = dict(LOADING_PDIV) drive["device"] = "cdrom" rec_vm_after_change.cif.prepareVolumePath(drive) # Run VM with recovery turned on. rec_vm_after_change.run() # Vm.run() waits for Vm._vmStartEvent which is set before recovery # starts. Wait little bit for recovery. wait_for_recovery(rec_vm_after_change) old_volume = ( CD_PDIV["domainID"], CD_PDIV["imageID"], CD_PDIV["volumeID"] ) new_volume = ( LOADING_PDIV["domainID"], LOADING_PDIV["imageID"], LOADING_PDIV["volumeID"] ) assert old_volume not in rec_vm_after_change.cif.irs.prepared_volumes assert new_volume in rec_vm_after_change.cif.irs.prepared_volumes with rec_vm_after_change._md_desc.device( devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(LOADING_PDIV, dev) assert "change" not in dev
def test_change_cd_ejecting(vm_with_cd): sd_id = "88252cf6-381e-48f0-8795-a294a32c7149" vol_id = "626a493f-5214-4337-b580-96a1ce702c2a" # Eject CD. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": None, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) vm_with_cd.changeCD(cdrom_spec) assert (sd_id, vol_id) not in vm_with_cd.cif.irs.prepared_volumes with vm_with_cd._md_desc.device(devtype=hwclass.DISK, name=device) as dev: assert dev == {}
def test_cd_recovery_after_cd_eject_no_pdiv(rec_vm_after_eject_no_pdiv): # Simulate recovery when user wants to eject CD and failure happened once # the metadata was updated with change element and the CD has been already # ejected from VM. However, in this case there are no PDIV metadata about # ejected CD and therefore we cannot tear the image down during recovery. # This can happen when migrating from older engine to new one. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": CD_PDIV, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Prepare image for the CD to check that it is not torn down. drive = dict(CD_PDIV) drive["device"] = "cdrom" rec_vm_after_eject_no_pdiv.cif.prepareVolumePath(drive) # Run VM with recovery turned on. rec_vm_after_eject_no_pdiv.run() # Vm.run() waits for Vm._vmStartEvent which is set before recovery # starts. Wait little bit for recovery. wait_for_recovery(rec_vm_after_eject_no_pdiv) volume = ( CD_PDIV["domainID"], CD_PDIV["imageID"], CD_PDIV["volumeID"] ) # As there are no metadata about ejected CD, we cannot tear it down during # recovery. assert volume in rec_vm_after_eject_no_pdiv.cif.irs.prepared_volumes with rec_vm_after_eject_no_pdiv._md_desc.device( devtype=hwclass.DISK, name=device) as dev: # No real device was found, recovery was skipped. expected = { "change": { "state": "ejecting", } } assert dev == expected
def test_change_cd_failed_libvirt_and_discard_cd_change(monkeypatch): device = drivename.make("sata", "2") def failing_discard_cd_change(self, device): raise Exception("Discard CD change failed") monkeypatch.setattr(vm.Vm, "_discard_cd_change", failing_discard_cd_change) with fake.VM( cif=ClientIF(), create_device_objects=True, xmldevices=EMPTY_CD_DEVICE_XML, metadata=EMPTY_CD_METADATA_XML ) as fakevm: fakevm._dom = fake.Domain(virtError=libvirt.VIR_ERR_XML_ERROR) # Verify, that ChangeDiskFailed is thrown when libvirt fails to update # disk device and _diskcard_cd_change() fails as well. Teardown will # pass and monkey patched _discard_cd_change() will raise. with pytest.raises(exception.ChangeDiskFailed): fakevm._change_cd(device, LOADING_DRIVE_SPEC)
def test_cd_recovery_before_cd_change(rec_vm_before_change): # Simulate recovery when failure happened once the metadata was updated # with change element and new CD image was prepared, but failed before # the CD was switched. # Failing even before preparing new CD image the situation is same, tearing # down image which is not prepared doesn't do anything. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": CD_PDIV, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Prepare image for loaded CD. drive = dict(CD_PDIV) drive["device"] = "cdrom" rec_vm_before_change.cif.prepareVolumePath(drive) # Run VM with recovery turned on. rec_vm_before_change.run() # Vm.run() waits for Vm._vmStartEvent which is set before recovery # starts. Wait little bit for recovery. wait_for_recovery(rec_vm_before_change) # Check that the new CD image was torn down. volume = ( LOADING_PDIV["domainID"], LOADING_PDIV["imageID"], LOADING_PDIV["volumeID"] ) assert volume not in rec_vm_before_change.cif.irs.prepared_volumes # Check that metadata looks like before changing CD. with rec_vm_before_change._md_desc.device( devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(CD_PDIV, dev) assert "change" not in dev
def test_specParams_ignore_iotune(self): conf = { 'device': 'cdrom', 'iface': 'ide', 'index': '3', 'path': '', 'readonly': 'true', 'type': 'disk', } source_xml = """<?xml version='1.0' encoding='UTF-8'?> <domain type="kvm" xmlns:ovirt-vm="http://ovirt.org/vm/1.0"> <metadata> <ovirt-vm:vm> <ovirt-vm:device name="hdd" devtype="disk"> <ovirt-vm:specParams> <ovirt-vm:ioTune> <ovirt-vm:read_iops_sec>100</ovirt-vm:read_iops_sec> </ovirt-vm:ioTune> </ovirt-vm:specParams> </ovirt-vm:device> </ovirt-vm:vm> </metadata> </domain>""" desc = metadata.Descriptor.from_xml(source_xml) with desc.device(devtype=conf['type'], name=drivename.make(conf['iface'], conf['index'])) as dev: # test we ignore IO tune settings from metadata, should we # (unexpectedly) found it (see below to learn why) self.assertEqual(dev, {'specParams': {}}) expected_xml = u"""<?xml version='1.0' encoding='utf-8'?> <ovirt-vm:vm xmlns:ovirt-vm="http://ovirt.org/vm/1.0"> <ovirt-vm:device devtype="disk" name="hdd" /> </ovirt-vm:vm>""" # test we never serialize IO tune settings - they belong # in the plain regular libvirt domain XML. self.assertXMLEqual(desc.to_xml(), expected_xml)
def test_cd_recovery_before_cd_eject(rec_vm_before_eject): # Simulate recovery when user wants to eject CD and failure happened once # the metadata was updated with change element, but the CD hasn't been # ejected from VM. cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": CD_PDIV, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) # Prepare image for the loaded CD. drive = dict(CD_PDIV) drive["device"] = "cdrom" rec_vm_before_eject.cif.prepareVolumePath(drive) # Run VM with recovery turned on. rec_vm_before_eject.run() # Vm.run() waits for Vm._vmStartEvent which is set before recovery # starts. Wait little bit for recovery. wait_for_recovery(rec_vm_before_eject) # Check that the CD image was not torn down. volume = ( CD_PDIV["domainID"], CD_PDIV["imageID"], CD_PDIV["volumeID"] ) assert volume in rec_vm_before_eject.cif.irs.prepared_volumes # Check that metadata looks like before ejecting CD. with rec_vm_before_eject._md_desc.device( devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(CD_PDIV, dev) assert "change" not in dev
def test_change_cd_teardown_old_cd_failed(monkeypatch, vm_with_cd): new_sd_id = str(uuid.uuid4()) new_img_id = str(uuid.uuid4()) new_vol_id = str(uuid.uuid4()) new_drive_spec = { "device": "cdrom", "domainID": new_sd_id, "poolID": str(uuid.uuid4()), "imageID": new_img_id, "volumeID": new_vol_id, } new_cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": new_drive_spec, } device = drivename.make(new_cdrom_spec["iface"], new_cdrom_spec["index"]) def failing_teardown(self, sdUUID, spUUID, imgUUID, volUUID=None): raise Exception("Image teardown failed.") monkeypatch.setattr(fake.IRS, "teardownImage", failing_teardown) # Change CD. Tearing down old CD will fail. However, we ignore the failure, # as we already successfully updated VM disk, so no exception should be # thrown from this call. vm_with_cd.changeCD(new_cdrom_spec) # Check new CD is prepared. volume = (new_sd_id, new_img_id, new_vol_id) assert volume in vm_with_cd.cif.irs.prepared_volumes # Assert metadata is in consistent state and new CD is loaded. with vm_with_cd._md_desc.device(devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(new_drive_spec, dev) assert "change" not in dev
def test_change_cd_failed_libvirt(): sd_id = str(uuid.uuid4()) vol_id = str(uuid.uuid4()) drive_spec = { "device": "cdrom", "domainID": sd_id, "poolID": str(uuid.uuid4()), "imageID": str(uuid.uuid4()), "volumeID": vol_id, } cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": drive_spec, } device = drivename.make(cdrom_spec["iface"], cdrom_spec["index"]) with fake.VM( cif=ClientIF(), create_device_objects=True, xmldevices=EMPTY_CD_DEVICE_XML, metadata=EMPTY_CD_METADATA_XML ) as fakevm: fakevm._dom = fake.Domain(virtError=libvirt.VIR_ERR_XML_ERROR) # Verify, that ChangeDiskFailed is thrown when libvirt fails to update # disk device. No CD is loaded, so if libvirt succeeded, no other # exception is thrown. with pytest.raises(exception.ChangeDiskFailed): fakevm.changeCD(cdrom_spec) # We started with empty CD. Verify that the image was torn down and # metadata was reset back to empty. assert (sd_id, vol_id) not in fakevm.cif.irs.prepared_volumes with fakevm._md_desc.device(devtype=hwclass.DISK, name=device) as dev: assert dev == {}
def _get_drive_conf_identifying_attrs(conf): return { 'devtype': conf['type'], 'name': drivename.make(conf['iface'], conf['index']), }
def _get_drive_conf_identifying_attrs(conf): return { 'devtype': conf['type'], 'name': drivename.make(conf['iface'], conf['index']), }
def test_make_name_invalid_parameters(self, iface, index): with pytest.raises(ValueError): drivename.make(iface, index)
def test_change_cd_apply_cd_change_failed(monkeypatch): old_sd_id = str(uuid.uuid4()) old_vol_id = str(uuid.uuid4()) old_drive_spec = { "device": "cdrom", "domainID": old_sd_id, "poolID": str(uuid.uuid4()), "imageID": str(uuid.uuid4()), "volumeID": old_vol_id, } old_cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": old_drive_spec, } new_drive_spec = { "device": "cdrom", "domainID": str(uuid.uuid4()), "poolID": str(uuid.uuid4()), "imageID": str(uuid.uuid4()), "volumeID": str(uuid.uuid4()), } new_cdrom_spec = { "iface": "sata", "index": "2", "drive_spec": new_drive_spec, } device = drivename.make(new_cdrom_spec["iface"], new_cdrom_spec["index"]) with fake.VM( cif=ClientIF(), create_device_objects=True, xmldevices=EMPTY_CD_DEVICE_XML, metadata=EMPTY_CD_METADATA_XML ) as fakevm: fakevm._dom = fake.Domain() # Insert CD. fakevm.changeCD(old_cdrom_spec) def failing_apply_cd_change(self, device): raise Exception("Apply CD change failed") monkeypatch.setattr( vm.Vm, "_apply_cd_change", failing_apply_cd_change) # Change CD. Apply cd change to metadata will fail, but as we already # succeeded to change CD, we ignore this error. The old disk should be # torn down and the call should succeed. fakevm.changeCD(new_cdrom_spec) # Tear down of the old image should succeed. assert (old_sd_id, old_vol_id) not in fakevm.cif.irs.prepared_volumes # As updating of metadata failed, metadata will be in inconsistent # state and should contain old CD and `change` element with new CD # PDIV. with fakevm._md_desc.device(devtype=hwclass.DISK, name=device) as dev: _assert_pdiv(old_drive_spec, dev) assert "change" in dev _assert_pdiv(new_drive_spec, dev["change"])