def test_set_property_no_modification(self): """Test various operations that should do nothing.""" ovf = OVF(self.input_ovf, self.temp_file) # InstanceID 4, IDE controller item = ovf.hardware.item_dict['4'] # set global value to empty for a property with no value self.assertFalse(item.modified) item.set_property(ovf.RESOURCE_SUB_TYPE, '') self.assertFalse(item.modified) # set per-profile value to empty for a property with no value item.set_property(ovf.RESOURCE_SUB_TYPE, '', ['2CPU-2GB-1NIC']) self.assertFalse(item.modified) # set global value to the same as existing global value item.set_property(ovf.ELEMENT_NAME, "VirtualIDEController 1") self.assertFalse(item.modified) # set per-profile value to the same as existing global value item.set_property(ovf.ELEMENT_NAME, "VirtualIDEController 1", ['2CPU-2GB-1NIC']) self.assertFalse(item.modified) ovf.write() ovf.destroy() self.check_diff("")
def test_input_output_bad_file(self): """Test reading/writing of an OVF with incorrect file references.""" self.staging_dir = tempfile.mkdtemp(prefix="cot_ut_ovfio_stage") input_dir = os.path.dirname(self.input_ovf) shutil.copy(os.path.join(input_dir, 'input.ovf'), self.staging_dir) shutil.copy(os.path.join(input_dir, 'input.iso'), self.staging_dir) shutil.copy(self.sample_cfg, self.staging_dir) # Copy blank.vmdk to input.vmdk so as to have the wrong size/checksum shutil.copy(self.blank_vmdk, os.path.join(self.staging_dir, 'input.vmdk')) with OVF(os.path.join(self.staging_dir, 'input.ovf'), os.path.join(self.temp_dir, "temp.ova")): # Write out to OVA (which will correct the file size information) pass self.assertLogged(msg="The size of file '%s' is expected" " to be.*but is actually.*") self.assertLogged(msg="Capacity of disk.*seems to have changed.*" "The updated OVF will reflect this change.") # Now read in the OVA with OVF(os.path.join(self.temp_dir, "temp.ova"), os.path.join(self.temp_dir, "temp.ovf")): # Replace the tar file fake .vmdk with the real .vmdk with tarfile.open(os.path.join(self.temp_dir, "temp.ova"), 'a') as tarf: tarf.add(self.input_vmdk, 'input.vmdk') self.assertLogged(msg="Size of file '%s' has changed") self.assertLogged(msg="checksum of file '%s' has changed")
def test_invalid_ova_file(self): """Check that various invalid input OVA files result in VMInitError.""" fake_file = os.path.join(self.temp_dir, "foo.ova") # .ova that is an empty file with open(fake_file, 'w') as fileobj: fileobj.write("") self.assertRaises(VMInitError, OVF, fake_file, None) # .ova that is not a TAR file with open(fake_file, 'w') as fileobj: fileobj.write("< hello world!") self.assertRaises(VMInitError, OVF, fake_file, None) # .ova that is an empty TAR file tarf = tarfile.open(fake_file, 'w') tarf.close() with self.assertRaises(VMInitError) as catcher: OVF(fake_file, None) self.assertEqual(catcher.exception.errno, 1) self.assertEqual(catcher.exception.strerror, "No files to untar") self.assertEqual(catcher.exception.filename, fake_file) # .ova that is a TAR file but does not contain an OVF descriptor tarf = tarfile.open(fake_file, 'w') try: tarf.add(self.blank_vmdk, os.path.basename(self.blank_vmdk)) finally: tarf.close() self.assertRaises(VMInitError, OVF, fake_file, None) # .ova that contains an OVF descriptor but in the wrong position tarf = tarfile.open(fake_file, 'a') try: tarf.add(self.minimal_ovf, os.path.basename(self.minimal_ovf)) finally: tarf.close() # this results in a logged error but not rejection - Postel's Law with OVF(fake_file, None): self.assertLogged(levelname="ERROR", msg="OVF file %s found, but .*not.*first") # .ova with unsafe absolute path references tarf = tarfile.open(fake_file, 'w') try: # tarfile.add() is sometimes smart enough to protect us against # such unsafe references, but we can overrule it by using # gettarinfo() and addfile() instead of just add(). tari = tarf.gettarinfo(self.minimal_ovf) tari.name = os.path.abspath( os.path.join(os.path.dirname(self.minimal_ovf), "..", "..", "..", os.path.basename(self.minimal_ovf))) with open(self.minimal_ovf, 'rb') as fileobj: tarf.addfile(tari, fileobj) finally: tarf.close() self.assertRaises(VMInitError, OVF, fake_file, None)
def test_input_output(self): """Read an OVF then write it again, verify no changes.""" with OVF(self.input_ovf, self.temp_file) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('') # Filename output too with OVF(self.input_ovf, self.temp_file + '.a.b.c'): self.assertLogged( levelname='WARNING', msg="found '%s' in mid-filename; treating as such", args=('out.ovf.a.b.c', '.ovf')) self.check_diff('', file2=(self.temp_file + ".a.b.c"))
def test_configuration_profiles(self): """Check profile id list APIs.""" # No profiles defined with OVF(self.vmware_ovf, None) as ovf: self.assertEqual(ovf.config_profiles, []) self.assertEqual(ovf.default_config_profile, None) # Profile list exists with OVF(self.input_ovf, None) as ovf: # default profile is first in the list self.assertEqual( ovf.config_profiles, ["4CPU-4GB-3NIC", "1CPU-1GB-1NIC", "2CPU-2GB-1NIC"]) self.assertEqual(ovf.default_config_profile, "4CPU-4GB-3NIC")
def test_input_output_vmware(self): """Test reading/writing of an OVF with custom extensions.""" with OVF(self.vmware_ovf, self.temp_file): pass # VMware disagrees with COT on some fiddly details of XML formatting self.check_diff( """ -<?xml version="1.0" encoding="UTF-8"?> -<ovf:Envelope vmw:buildId="build-880146" \ xmlns="http://schemas.dmtf.org/ovf/envelope/1" \ xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" \ xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" \ xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/\ CIM_ResourceAllocationSettingData" \ xmlns:vmw="http://www.vmware.com/schema/ovf" \ xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/\ CIM_VirtualSystemSettingData" \ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<?xml version='1.0' encoding='utf-8'?> +<ovf:Envelope xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" \ xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/\ CIM_ResourceAllocationSettingData" \ xmlns:vmw="http://www.vmware.com/schema/ovf" \ xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/\ CIM_VirtualSystemSettingData" vmw:buildId="build-880146"> <ovf:References> ... </ovf:VirtualSystem> -</ovf:Envelope> +</ovf:Envelope>""", file1=self.vmware_ovf) # noqa - trailing whitespace above
def test_basic_sanity(self): """Make sure basic loading is successful and APIs work.""" with OVF(self.input_ovf, None) as ovf: hardware = ovf.hardware self.assertEqual(ovf, hardware.ovf) self.assertEqual(13, len(hardware.item_dict)) self.assertEqual('14', hardware.find_unused_instance_id()) self.assertEqual(2, len(hardware.find_all_items('ide'))) self.assertEqual( 1, len( hardware.find_all_items('ide', properties={ovf.ADDRESS: '1'}))) self.assertEqual( 0, len( hardware.find_all_items('ide', properties={ovf.ADDRESS: '2'}))) self.assertNotEqual(None, hardware.find_item('harddisk')) self.assertEqual(None, hardware.find_item('usb')) self.assertRaises(LookupError, hardware.find_item, 'ide') self.assertEqual(1, hardware.get_item_count('ethernet', None)) self.assertEqual( 3, hardware.get_item_count('ethernet', "4CPU-4GB-3NIC")) self.assertEqual( 3, hardware.get_item_count_per_profile('ethernet', None)["4CPU-4GB-3NIC"])
def test_invalid_ovf_file(self): """Check that various invalid input OVF files result in VMInitError.""" fake_file = os.path.join(self.temp_dir, "foo.ovf") # .ovf that is an empty file with open(fake_file, 'w') as fileobj: fileobj.write("") self.assertRaises(VMInitError, OVF, fake_file, None) # .ovf that isn't actually XML at all with open(fake_file, 'w') as fileobj: fileobj.write("< hello world!") self.assertRaises(VMInitError, OVF, fake_file, None) # .ovf that is XML but not OVF XML with open(fake_file, 'w') as fileobj: fileobj.write("<?xml version='1.0' encoding='utf-8'?>") self.assertRaises(VMInitError, OVF, fake_file, None) with open(fake_file, 'w') as fileobj: fileobj.write("<?xml version='1.0' encoding='utf-8'?>") fileobj.write("<foo/>") self.assertRaises(VMInitError, OVF, fake_file, None) # .ovf claiming to be OVF version 3.0, which doesn't exist yet with self.assertRaises(VMInitError) as catcher: OVF(self.ersatz_v3_ovf, None) self.assertEqual(catcher.exception.errno, 2) self.assertEqual( catcher.exception.strerror, "File has an Envelope but it is in unknown namespace " "'http://schemas.dmtf.org/ovf/envelope/3'") self.assertEqual(catcher.exception.filename, self.ersatz_v3_ovf)
def test_find_item_multiple_matches(self): """Check find_item raises LookupError if multiple matches are found.""" with OVF(self.input_ovf, None) as ovf: self.assertRaisesRegex(LookupError, r"multiple matching 'ide' Items", ovf.hardware.find_item, resource_type='ide')
def test_input_output_v20_vbox(self): """Test reading/writing of a v2.0 OVF from VirtualBox.""" with OVF(self.v20_vbox_ovf, self.temp_file) as vm: self.assertEqual(vm.ovf_version, 2.0) # TODO - vbox XML is not very clean so the diffs are large... # self.check_diff('', file1=self.v20_vbox_ovf) # ovftool does not consider vbox ovfs to be valid self.validate_output_with_ovftool = False
def test_unknown_extension(self, mock_type): # pylint: disable=missing-type-doc,missing-param-doc """Test handling of unexpected behavior in detect_type_from_name.""" # unsupported input file type with self.assertRaises(VMInitError) as catcher: OVF(self.input_ovf, None) self.assertEqual(catcher.exception.errno, 2) self.assertEqual(catcher.exception.strerror, "File does not appear to be an OVA or OVF") self.assertEqual(catcher.exception.filename, self.input_ovf) # unsupported output file type mock_type.return_value = ".ovf" with self.assertRaises(NotImplementedError) as catcher, \ OVF(self.input_ovf, None) as vm: mock_type.return_value = ".qcow2" vm.output_file = os.path.join(self.temp_dir, "foo.qcow2") self.assertEqual(catcher.exception.args[0], "Not sure how to write a '.qcow2' file")
def test_filename_validation(self): """Test class method(s) for filename validation.""" self.assertEqual('.ovf', OVF.detect_type_from_name("/foo/bar/foo.ovf")) self.assertEqual('.ova', OVF.detect_type_from_name("/foo/bar/foo.ova")) # Lazy filenames should be OK too self.assertEqual('.ovf', OVF.detect_type_from_name("/foo/bar/foo.ovf.5.2.2")) self.assertLogged(levelname='WARNING', msg="found '%s' in mid-filename; treating as such", args=('foo.ovf.5.2.2', '.ovf')) self.assertEqual('.ova', OVF.detect_type_from_name("/foo/bar/foo.ova.15.4.T")) self.assertLogged(levelname='WARNING', msg="found '%s' in mid-filename; treating as such", args=('foo.ova.15.4.T', '.ova')) # Unsupported formats self.assertRaises(ValueUnsupportedError, OVF.detect_type_from_name, "/foo/bar.ovf/baz") self.assertRaises(ValueUnsupportedError, OVF.detect_type_from_name, "/foo/bar.zip")
def test_input_output(self): """Read an OVF then write it again, verify no changes.""" with OVF(self.input_ovf, self.temp_file) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('') self.check_diff(file1=self.input_manifest, file2=os.path.join(self.temp_dir, "out.mf"), expected=""" -SHA1(input.ovf)= c3bd2579c2edc76ea35b5bde7d4f4e41eab08963 +SHA1(out.ovf)= c3bd2579c2edc76ea35b5bde7d4f4e41eab08963 SHA1(input.vmdk)= 264caaa216ad928f82f727bb06d8e6e6fbd94df0 """) # Filename output too with OVF(self.input_ovf, self.temp_file + '.a.b.c'): self.assertLogged( levelname='WARNING', msg="found '%s' in mid-filename; treating as such", args=('out.ovf.a.b.c', '.ovf')) self.check_diff('', file2=(self.temp_file + ".a.b.c"))
def test_tar_links(self): """Check that OVA dereferences symlinks and hard links.""" self.staging_dir = tempfile.mkdtemp(prefix="cot_ut_ovfio_stage") shutil.copy(self.input_ovf, self.staging_dir) # Hardlink self.input_vmdk to the staging dir os.link(self.input_vmdk, os.path.join(self.staging_dir, 'input.vmdk')) # Symlink self.input_iso to the staging dir os.symlink(self.input_iso, os.path.join(self.staging_dir, 'input.iso')) shutil.copy(self.sample_cfg, self.staging_dir) ovf = OVF(os.path.join(self.staging_dir, 'input.ovf'), os.path.join(self.temp_dir, 'input.ova')) ovf.write() ovf.destroy() with tarfile.open(os.path.join(self.temp_dir, 'input.ova'), 'r') as tarf: try: vmdk = tarf.getmember('input.vmdk') self.assertTrue(vmdk.isfile(), "hardlink was not added as a regular file") self.assertFalse(vmdk.islnk()) iso = tarf.getmember('input.iso') self.assertTrue(iso.isfile(), "symlink was not added as a regular file") self.assertFalse(iso.issym()) except KeyError as exc: self.fail("KeyError: {0}\n Tarfile members = {1}".format( exc, tarf.getnames()))
def test_input_output_relative_paths(self): """Make sure relative and implicit paths are handled.""" os.chdir(os.path.dirname(self.input_ovf)) with OVF(os.path.basename(self.input_ovf), self.temp_file) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('') os.remove(self.temp_file) with OVF(os.path.join(".", os.path.basename(self.input_ovf)), self.temp_file) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('') os.remove(self.temp_file) os.chdir(os.path.dirname(self.temp_file)) with OVF(self.input_ovf, os.path.basename(self.temp_file)) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('') os.remove(self.temp_file) with OVF(self.input_ovf, os.path.join(".", os.path.basename(self.temp_file))) as vm: self.assertEqual(vm.ovf_version, 1.0) self.check_diff('')
def test_remove_profile(self): """Test case for remove_profile() method.""" with OVF(self.input_ovf, self.temp_file) as ovf: # InstanceID 11, NIC 0 (default, under all profiles) item = ovf.hardware.item_dict['11'] self.assertTrue(item.has_profile(None)) self.assertTrue(item.has_profile("1CPU-1GB-1NIC")) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) # nonexistent profile self.assertFalse(item.has_profile("nonexistent")) # Remove one profile item.remove_profile("1CPU-1GB-1NIC") self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) # no longer available self.assertFalse(item.has_profile(None)) self.assertFalse(item.has_profile("1CPU-1GB-1NIC")) self.assertFalse(item.has_profile("nonexistent")) self.assertEqual( item.get_value(ovf.ADDRESS_ON_PARENT, ["2CPU-2GB-1NIC", "4CPU-4GB-3NIC"]), "11") self.assertEqual( item.get_value(ovf.ADDRESS_ON_PARENT, ["1CPU-1GB-1NIC"]), None) self.assertEqual(item.get_value(ovf.ADDRESS_ON_PARENT, [None]), None) self.assertEqual( item.get_value( ovf.ADDRESS_ON_PARENT, ["1CPU-1GB-1NIC", "2CPU-2GB-1NIC", "4CPU-4GB-3NIC"]), None) self.check_diff(""" </ovf:Item> - <ovf:Item> + <ovf:Item ovf:configuration="2CPU-2GB-1NIC 4CPU-4GB-3NIC"> <rasd:AddressOnParent>11</rasd:AddressOnParent> """)
def test_predicted_output_size(self): """Check output size prediction against reality.""" self.validate_output_with_ovftool = False for input_file in [ self.input_ovf, self.minimal_ovf, self.iosv_ovf, self.v09_ovf, self.v20_vbox_ovf, self.vmware_ovf, ]: with OVF(input_file, self.temp_file) as ovf: predicted = ovf.predicted_output_size() actual = os.stat(self.temp_file).st_size # Up to 10% greater than reality is OK, # but aim for never less than. self.assertGreaterEqual( predicted, actual, "predicted output size of {0} was {1} but actual size is {2}". format(input_file, predicted, actual)) self.assertLessEqual( predicted, int(actual * 1.1), "predicted output size of {0} was {1} but actual size is {2}". format(input_file, predicted, actual))
def test_set_property(self): """Test cases for set_property() and related methods.""" ovf = OVF(self.input_ovf, self.temp_file) # InstanceID 1, 'CPU' - entries for 'default' plus two other profiles item = ovf.hardware.item_dict['1'] self.assertTrue(item.has_profile(None)) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) # implied by default profile self.assertTrue(item.has_profile("1CPU-1GB-1NIC")) # nonexistent profile self.assertFalse(item.has_profile("nonexistent")) self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC']), '1') self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, ['2CPU-2GB-1NIC']), '2') # value differs between profiles, so get_value returns None self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), None) self.assertFalse(item.modified) # Set profile 1 to same as default (this is a no-op) item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["1CPU-1GB-1NIC"]) self.assertFalse(item.modified) ovf.write() self.check_diff("") # Change profile 1 to same as profile 2 item.set_property(ovf.VIRTUAL_QUANTITY, '2', ["1CPU-1GB-1NIC"]) self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '2') self.assertTrue(item.modified) ovf.write() self.check_diff(""" </ovf:Item> - <ovf:Item ovf:configuration="2CPU-2GB-1NIC"> + <ovf:Item ovf:configuration="1CPU-1GB-1NIC 2CPU-2GB-1NIC"> <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits> """) # Change profile 1 back under default item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["1CPU-1GB-1NIC"]) self.assertTrue(item.modified) ovf.write() self.check_diff("") # Change profile 2 to fall under default item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["2CPU-2GB-1NIC"]) self.assertTrue(item.modified) self.assertTrue(item.has_profile(None)) self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) # implied by default profile self.assertTrue(item.has_profile("1CPU-1GB-1NIC")) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) # nonexistent profile self.assertFalse(item.has_profile("nonexistent")) self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '1') self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, [None]), '1') self.assertEqual( item.get_value(ovf.VIRTUAL_QUANTITY, [None, '1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '1') # disjoint sets self.assertEqual( item.get_value( ovf.VIRTUAL_QUANTITY, [None, '1CPU-1GB-1NIC', '2CPU-2GB-1NIC', '4CPU-4GB-3NIC']), None) ovf.write() self.check_diff(""" </ovf:Item> - <ovf:Item ovf:configuration="2CPU-2GB-1NIC"> - <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits> - <rasd:Description>Number of Virtual CPUs</rasd:Description> - <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName> - <rasd:InstanceID>1</rasd:InstanceID> - <rasd:ResourceType>3</rasd:ResourceType> - <rasd:VirtualQuantity>2</rasd:VirtualQuantity> - <vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket> - </ovf:Item> <ovf:Item ovf:configuration="4CPU-4GB-3NIC"> """) ovf.destroy()
def test_find_empty_drive_unsupported(self): """Negative test for find_empty_drive().""" with OVF(self.input_ovf, None) as ovf: self.assertRaises(ValueUnsupportedError, ovf.find_empty_drive, 'floppy')
def test_input_output_v09(self): """Test reading/writing of a v0.9 OVF.""" with OVF(self.v09_ovf, self.temp_file) as vm: self.assertEqual(vm.ovf_version, 0.9) self.check_diff('', file1=self.v09_ovf)
def test_find_item_no_matches(self): """Test that find_item returns None if no matches are found.""" with OVF(self.input_ovf, None) as ovf: self.assertEqual(None, ovf.hardware.find_item(resource_type='usb'))
def test_input_output_missing_file(self): """Test reading/writing of an OVF with missing file references.""" # Read OVF, write OVF - make sure invalid file reference is removed self.staging_dir = tempfile.mkdtemp(prefix="cot_ut_ovfio_stage") shutil.copy(self.input_ovf, self.staging_dir) shutil.copy(self.input_vmdk, self.staging_dir) shutil.copy(self.sample_cfg, self.staging_dir) # Don't copy input.iso to the staging directory. with OVF(os.path.join(self.staging_dir, 'input.ovf'), self.temp_file): self.assertLogged(**self.NONEXISTENT_FILE) self.assertLogged(**self.REMOVING_FILE) self.check_diff(""" <ovf:File ovf:href="input.vmdk" ovf:id="file1" ovf:size="{vmdk_size}" /> - <ovf:File ovf:href="input.iso" ovf:id="file2" ovf:size="{iso_size}" /> <ovf:File ovf:href="sample_cfg.txt" ovf:id="textfile" \ ovf:size="{cfg_size}" /> """.format(vmdk_size=self.FILE_SIZE['input.vmdk'], iso_size=self.FILE_SIZE['input.iso'], cfg_size=self.FILE_SIZE['sample_cfg.txt'])) # Read-only OVF with OVF(os.path.join(self.staging_dir, 'input.ovf'), None): self.assertLogged(**self.NONEXISTENT_FILE) # File exists at read time but has disappeared by write time shutil.copy(self.input_iso, self.staging_dir) with OVF(os.path.join(self.staging_dir, 'input.ovf'), self.temp_file): os.remove(os.path.join(self.staging_dir, 'input.iso')) self.assertLogged(**self.FILE_DISAPPEARED) self.assertLogged(**self.REMOVING_FILE) self.check_diff(""" <ovf:File ovf:href="input.vmdk" ovf:id="file1" ovf:size="{vmdk_size}" /> - <ovf:File ovf:href="input.iso" ovf:id="file2" ovf:size="{iso_size}" /> <ovf:File ovf:href="sample_cfg.txt" ovf:id="textfile" \ ovf:size="{cfg_size}" /> """.format(vmdk_size=self.FILE_SIZE['input.vmdk'], iso_size=self.FILE_SIZE['input.iso'], cfg_size=self.FILE_SIZE['sample_cfg.txt'])) # Read OVA, write OVF with tarfile.open(os.path.join(self.staging_dir, 'input.ova'), 'w') as tarf: tarf.add(os.path.join(self.staging_dir, 'input.ovf'), 'input.ovf') tarf.add(os.path.join(self.staging_dir, 'input.vmdk'), 'input.vmdk') tarf.add(self.sample_cfg, 'sample_cfg.txt') with OVF(os.path.join(self.staging_dir, 'input.ova'), os.path.join(self.temp_dir, 'output.ovf')): self.assertLogged(**self.NONEXISTENT_FILE) self.assertLogged(**self.REMOVING_FILE) self.check_diff(file2=os.path.join(self.temp_dir, 'output.ovf'), expected=""" <ovf:File ovf:href="input.vmdk" ovf:id="file1" ovf:size="{vmdk_size}" /> - <ovf:File ovf:href="input.iso" ovf:id="file2" ovf:size="{iso_size}" /> <ovf:File ovf:href="sample_cfg.txt" ovf:id="textfile" \ ovf:size="{cfg_size}" /> """.format(vmdk_size=self.FILE_SIZE['input.vmdk'], iso_size=self.FILE_SIZE['input.iso'], cfg_size=self.FILE_SIZE['sample_cfg.txt'])) # Also test read-only OVA logic: with OVF(os.path.join(self.staging_dir, "input.ova"), None): self.assertLogged(**self.NONEXISTENT_FILE)
def test_set_property(self): """Test cases for set_property() and related methods.""" ovf = OVF(self.input_ovf, self.temp_file) # InstanceID 1, 'CPU' - entries for 'default' plus two other profiles item = ovf.hardware.item_dict['1'] self.assertTrue(item.has_profile(None)) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) # implied by default profile self.assertTrue(item.has_profile("1CPU-1GB-1NIC")) # nonexistent profile self.assertFalse(item.has_profile("nonexistent")) self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC']), '1') self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, ['2CPU-2GB-1NIC']), '2') # value differs between profiles, so get_value returns None self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), None) self.assertFalse(item.modified) # Set profile 1 to same as default (this is a no-op) item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["1CPU-1GB-1NIC"]) self.assertFalse(item.modified) ovf.write() self.check_diff("") # Change profile 1 to same as profile 2 item.set_property(ovf.VIRTUAL_QUANTITY, '2', ["1CPU-1GB-1NIC"]) self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '2') self.assertTrue(item.modified) ovf.write() self.check_diff(""" </ovf:Item> - <ovf:Item ovf:configuration="2CPU-2GB-1NIC"> + <ovf:Item ovf:configuration="1CPU-1GB-1NIC 2CPU-2GB-1NIC"> <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits> """) # Change profile 1 back under default item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["1CPU-1GB-1NIC"]) self.assertTrue(item.modified) ovf.write() self.check_diff("") # Change profile 2 to fall under default item.set_property(ovf.VIRTUAL_QUANTITY, '1', ["2CPU-2GB-1NIC"]) self.assertTrue(item.modified) self.assertTrue(item.has_profile(None)) self.assertTrue(item.has_profile("4CPU-4GB-3NIC")) # implied by default profile self.assertTrue(item.has_profile("1CPU-1GB-1NIC")) self.assertTrue(item.has_profile("2CPU-2GB-1NIC")) # nonexistent profile self.assertFalse(item.has_profile("nonexistent")) self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, ['1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '1') self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, [None]), '1') self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, [None, '1CPU-1GB-1NIC', '2CPU-2GB-1NIC']), '1') # disjoint sets self.assertEqual(item.get_value(ovf.VIRTUAL_QUANTITY, [None, '1CPU-1GB-1NIC', '2CPU-2GB-1NIC', '4CPU-4GB-3NIC']), None) ovf.write() self.check_diff(""" </ovf:Item> - <ovf:Item ovf:configuration="2CPU-2GB-1NIC"> - <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits> - <rasd:Description>Number of Virtual CPUs</rasd:Description> - <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName> - <rasd:InstanceID>1</rasd:InstanceID> - <rasd:ResourceType>3</rasd:ResourceType> - <rasd:VirtualQuantity>2</rasd:VirtualQuantity> - <vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket> - </ovf:Item> <ovf:Item ovf:configuration="4CPU-4GB-3NIC"> """) ovf.destroy()
def test_tar_untar(self): """Output OVF to OVA and vice versa.""" # Read OVF and write to OVA ovf = OVF(self.input_ovf, os.path.join(self.temp_dir, "temp.ova")) ovf.write() ovf.destroy() # Read OVA and overwrite itself # Issue #66 resulted from the two strings not being identical # So mix relative and absolute paths: os.chdir(self.temp_dir) ova = OVF("temp.ova", os.path.join(self.temp_dir, "temp.ova")) ova.write() ova.destroy() # Another permutation of issue #66 - output file is a # symlink to input file os.symlink("temp.ova", "symlink.ova") ova = OVF("temp.ova", "symlink.ova") ova.write() ova.destroy() # A third permutation - output file is hardlink to input os.link("temp.ova", "hardlink.ova") ova = OVF("temp.ova", "hardlink.ova") ova.write() ova.destroy() # Read OVA and write to OVF ovf2 = OVF(os.path.join(self.temp_dir, "temp.ova"), os.path.join(self.temp_dir, "input.ovf")) ovf2.write() ovf2.destroy() # Make sure everything propagated over successfully input_dir = os.path.dirname(self.input_ovf) for ext in ['.ovf', '.mf', '.iso', '.vmdk']: if ext == '.mf' or ext == '.ovf': self.check_diff("", os.path.join(input_dir, "input" + ext), os.path.join(self.temp_dir, "input" + ext)) else: self.assertTrue( filecmp.cmp(os.path.join(input_dir, "input" + ext), os.path.join(self.temp_dir, "input" + ext)), "{0} file changed after OVF->OVA->OVF conversion".format( ext))