Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package open-vmdk for openSUSE:Factory checked in at 2023-12-18 22:57:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/open-vmdk (Old) and /work/SRC/openSUSE:Factory/.open-vmdk.new.9037 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "open-vmdk" Mon Dec 18 22:57:19 2023 rev:2 rq:1133892 version:0.3.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/open-vmdk/open-vmdk.changes 2023-09-04 22:55:01.026810765 +0200 +++ /work/SRC/openSUSE:Factory/.open-vmdk.new.9037/open-vmdk.changes 2023-12-18 22:57:28.394974055 +0100 @@ -1,0 +2,27 @@ +Tue Nov 21 19:41:56 UTC 2023 - [email protected] + +- Update to version 0.3.6.0: + * -add a test for the manifest file + * add option to support sha256 checksum type + * add github workflow for pytest + * add basic tests with pytest + * do not fail if 'transports' or 'categories' are missing + * Update README.md for categories and extra_configs + * don't fail if no network is configured + * add 'no_default_configs' option to 'system' + * add categories, fixes issue #35 + * add extra_configs + * Add doc for OVF properties to README.md + * fix for split failure when there is more than one equals sign in 'param' + * fix error when product section doesn't exist + * Add OVF template for hardware version 21 + * Fix typo, from PR #29 + * fix vmdisk ids + * add OVF properties + +------------------------------------------------------------------- +Thu Nov 16 20:42:19 UTC 2023 - Guilherme Moro <[email protected]> + +- Patch: shebang.patch fix bash/sh shebang issue + +------------------------------------------------------------------- Old: ---- open-vmdk-0.3.1.0.obscpio New: ---- open-vmdk-0.3.6.0.obscpio open-vmdk-0.3.6.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ open-vmdk.spec ++++++ --- /var/tmp/diff_new_pack.OUKgGK/_old 2023-12-18 22:57:29.431011997 +0100 +++ /var/tmp/diff_new_pack.OUKgGK/_new 2023-12-18 22:57:29.435012144 +0100 @@ -17,7 +17,7 @@ Name: open-vmdk -Version: 0.3.1.0 +Version: 0.3.6.0 Release: 0 Summary: Tools to create OVA files from raw disk images License: Apache-2.0 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.OUKgGK/_old 2023-12-18 22:57:29.483013902 +0100 +++ /var/tmp/diff_new_pack.OUKgGK/_new 2023-12-18 22:57:29.487014048 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/vmware/open-vmdk</param> - <param name="changesrevision">5afbfb5270099586b93e28ae7585f3c661f379b5</param></service></servicedata> + <param name="changesrevision">ba658b05c4bf95631e9b7ae55ce5bc248192b798</param></service></servicedata> (No newline at EOF) ++++++ open-vmdk-0.3.1.0.obscpio -> open-vmdk-0.3.6.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/.github/workflows/pytest.yaml new/open-vmdk-0.3.6.0/.github/workflows/pytest.yaml --- old/open-vmdk-0.3.1.0/.github/workflows/pytest.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/.github/workflows/pytest.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,32 @@ +name: open-vmdk pytests + +on: [pull_request, push, workflow_dispatch] + +jobs: + pytests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: install build deps + run: sudo apt-get -y install zlib1g-dev + + - name: build + working-directory: ${{ github.workspace }} + run: make + + - name: install + working-directory: ${{ github.workspace }} + run: sudo make install + + - name: set up python 3 + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: install pytest + run: pip install pytest PyYAML lxml xmltodict + + - name: run tests + working-directory: ${{ github.workspace }} + run: pytest pytest/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/.gitignore new/open-vmdk-0.3.6.0/.gitignore --- old/open-vmdk-0.3.1.0/.gitignore 2023-07-25 23:40:09.000000000 +0200 +++ new/open-vmdk-0.3.6.0/.gitignore 2023-11-14 19:12:25.000000000 +0100 @@ -1,2 +1,4 @@ /build/ ._* +__pycache__/ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/README.md new/open-vmdk-0.3.6.0/README.md --- old/open-vmdk-0.3.1.0/README.md 2023-07-25 23:40:09.000000000 +0200 +++ new/open-vmdk-0.3.6.0/README.md 2023-11-14 19:12:25.000000000 +0100 @@ -12,7 +12,7 @@ The OVF file that will be embedded can be generated using `ova-compose` from a simple yaml config file. -`ova-compose` will can then create the final OVA from the OVF file, the vmdk images and a manifest (a file that contains checksums of the other files). +`ova-compose` will then create the final OVA from the OVF file, the vmdk images and a manifest (a file that contains checksums of the other files). There is also the legacy tool `mkova.sh` that generates OVF files from templates. @@ -160,6 +160,28 @@ label: Venti description: too much +environment: + transports: + - com.vmware.guestInfo + - iso + categories: + email: Email Settings + properties: + guestinfo.admin.email: + value: [email protected] + user_configurable: true + type: string + description: "The Admin's email address" + label: "Email Address" + category: email + +extra_configs: + feature.enabled: + required: false + value: true + log.rotateSize: + value: 2048000 + product: product: An Example VM vendor: A Company Inc. @@ -205,6 +227,8 @@ description: just right ``` The default can also be set with `default_configuration` in the `system` section. +* `environment` is for setting OVF properties. Variables are added under the new `environment` section as a `properties` map. The key is the name of the variable. Each variable has a mandatory `type`. `value`, `user_configurable` (default: `false`), `qualifiers`, `password` (default `false`),`label`, `description` are optional. Additionally, `transports` can be set in a list. Valid values are `iso` and `com.vmware.guestInfo`. Note that at least one of them must be set to make the properties visible inside the guest. Optionally, categories are set with `categories` to a map with an id as key and a description as value. Each property can have a `category` set to a category id. +* `extra_configs` is a map of settings with the fields `value` and optionally the boolean `required`. * `annotation` has the fields `info`, `text` and `file`. `text` and `file` are mutually exclusive - `text` is text inline, `file` can be set to a text file that will be filled in. The annotation text will appear for example as a comment in VMware Fusion. * `eula` also has the fields `info`, `text` and `file`. It contains the EULA agreement the user has to agree to when deploying the VM. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/ova-compose/ova-compose.py new/open-vmdk-0.3.6.0/ova-compose/ova-compose.py --- old/open-vmdk-0.3.1.0/ova-compose/ova-compose.py 2023-07-25 23:40:09.000000000 +0200 +++ new/open-vmdk-0.3.6.0/ova-compose/ova-compose.py 2023-11-14 19:12:25.000000000 +0100 @@ -532,7 +532,7 @@ def __init__(self, path): - self.id = f"vmdisk{OVFFile.next_id}" + self.id = f"vmdisk{OVFDisk.next_id}" OVFDisk.next_id += 1 self.file = OVFFile(path) @@ -558,7 +558,7 @@ class OVFEmptyDisk(OVFDisk): def __init__(self, capacity, units="MB"): - self.id = f"vmdisk{OVFFile.next_id}" + self.id = f"vmdisk{OVFDisk.next_id}" OVFDisk.next_id += 1 self.capacity = capacity if units == "KB": @@ -585,6 +585,51 @@ return ''.join(x.title() for x in snake_str.split('_')) +class OVFProperty(object): + + def __init__(self, key, type, + password = False, + value=None, + user_configurable=False, qualifiers=None, + label=None, description=None, category=None): + self.key = key + self.type = type + self.password = False + self.value = value + self.user_configurable = user_configurable + self.qualifiers = qualifiers + self.label = label + self.description = description + self.category = category + + + @classmethod + def from_dict(cls, key, type, d): + item = cls(key, type, **d) + return item + + + def xml_item(self): + xml_attrs = { + '{%s}key' % NS_OVF: self.key, + '{%s}type' % NS_OVF: self.type + } + if self.value is not None: + xml_attrs['{%s}value' % NS_OVF] = str(self.value) + if self.qualifiers is not None: + xml_attrs['{%s}qualifiers' % NS_OVF] = self.qualifiers + if self.user_configurable: + xml_attrs['{%s}userConfigurable' % NS_OVF] = "true" + if self.password: + xml_attrs['{%s}password' % NS_OVF] = "true" + xml_property = ET.Element('{%s}Property' % NS_OVF, xml_attrs) + if self.label is not None: + xml_property.append(xml_text_element('{%s}Label' % NS_OVF, self.label)) + if self.description is not None: + xml_property.append(xml_text_element('{%s}Description' % NS_OVF, self.description)) + return xml_property + + class OVFProduct(object): # snake case will be converted to camel case in XML keys = ['info', 'product', 'vendor', 'version', 'full_version'] @@ -593,6 +638,20 @@ self.info = "Information about the installed software" self.__dict__.update((k, v) for k, v in kwargs.items() if k in self.keys) + self.properties = [] + if 'properties' in kwargs: + props = kwargs['properties'] + for k, v in props.items(): + self.properties.append(OVFProperty(k, **v)) + + self.transports = kwargs.get('transports', []) + self.categories = kwargs.get('categories', {}) + + # if a property references a non-existing category it will be dropped + for prop in self.properties: + assert prop.category is None or prop.category in self.categories,\ + f"property '{prop.key}' references unknown category '{prop.category}'" + @classmethod def from_dict(cls, d): @@ -607,6 +666,18 @@ if hasattr(self, k) and getattr(self, k) is not None: xml_name = to_camel_case(k) xml_product.append(xml_text_element('{%s}%s' % (NS_OVF, xml_name), getattr(self, k))) + + # append category-less properties first + for prop in self.properties: + if prop.category is None: + xml_product.append(prop.xml_item()) + # then go through all categories, and append matching props + for cat_id, cat_name in self.categories.items(): + xml_product.append(xml_text_element('{%s}Category' % NS_OVF, cat_name)) + for prop in self.properties: + if prop.category == cat_id: + xml_product.append(prop.xml_item()) + return xml_product @@ -679,7 +750,7 @@ @classmethod - def from_dict(cls, config): + def from_dict(cls, d): item = cls(**d) return item @@ -694,6 +765,34 @@ return elem +class VmwExtraConfigItem(object): + + def __init__(self, key, value, required=None): + self.key = key + self.value = value + self.required = required + + + @classmethod + def from_dict(cls, d): + item = cls(**d) + return item + + + def xml_item(self): + value = self.value + if type(value) is bool: + value = "true" if value else "false" + elif type(value) is not str: + value = str(value) + + attrs = {'{%s}key' % NS_VMW: self.key, '{%s}value' % NS_VMW: value} + if self.required is not None: + attrs['{%s}required' % NS_VMW] = "true" if self.required else "false" + elem = ET.Element('{%s}ExtraConfig' % NS_VMW, attrs) + return elem + + class OVF(object): CONFIG_DEFAULTS = { @@ -722,10 +821,13 @@ def __init__(self, system, files, disks, - networks, vssd_system, rasd_items, + networks, + vssd_system, rasd_items, extra_configs, product, annotation, eula, configurations): - self.hardware_config = OVF.CONFIG_DEFAULTS.copy() + self.hardware_config = {} + if not system.get('no_default_configs', False): + self.hardware_config.update(OVF.CONFIG_DEFAULTS) self.name = system['name'] self.os_cim = system.get('os_cim', 100) self.os_vmw = system.get('os_vmw', "other4xLinux64Guest") @@ -742,6 +844,7 @@ self.networks = networks self.vssd_system = vssd_system self.rasd_items = rasd_items + self.extra_configs = extra_configs self.product = product self.annotation = annotation self.eula = eula @@ -764,6 +867,8 @@ annotation = None eula = None + assert 'hardware' in config, "config needs a 'hardware' section" + hardware = config['hardware'] for hw_id, hw in hardware.items(): if isinstance(hw, dict): @@ -782,25 +887,41 @@ hw['disk'] = disk networks = {} - for nw_id, nw in config['networks'].items(): - network = OVFNetwork.from_dict(nw) - networks[nw_id] = network + if 'networks' in config: + for nw_id, nw in config['networks'].items(): + network = OVFNetwork.from_dict(nw) + networks[nw_id] = network vssd_system = VssdSystem.from_dict(config) rasd_items = cls.rasd_items_from_dict(config) + extra_configs = cls.vmw_extra_config_items_from_dict(config) + + # we want properties in their own section ('environment') + # but in OVF they are part of the ProductSection, so copy it + if 'environment' in config: + env = config['environment'] + if 'product' not in config: + config['product'] = {} + for cfg in ['transports', 'properties', 'categories']: + if cfg in env: + config['product'][cfg] = env[cfg] + if 'product' in config: product = OVFProduct.from_dict(config['product']) + if 'annotation' in config: annotation = OVFAnnotation.from_dict(config['annotation']) + if 'eula' in config: eula = OVFEula.from_dict(config['eula']) + configurations = {} if 'configurations' in config: for k, v in config['configurations'].items(): configurations[k] = OVFConfiguration(k, **v) ovf = cls(config['system'], files, disks, - networks, vssd_system, rasd_items, + networks, vssd_system, rasd_items, extra_configs, product, annotation, eula, configurations) @@ -830,6 +951,19 @@ return rasd_items + @classmethod + def vmw_extra_config_items_from_dict(cls, config): + xtra_cfgs = [] + xconfigs = config.get('extra_configs', None) + if xconfigs is None: + return [] + for key, cfg in xconfigs.items(): + cfg['key'] = key + xtra_cfg_item = VmwExtraConfigItem.from_dict(cfg) + xtra_cfgs.append(xtra_cfg_item) + return xtra_cfgs + + def connect(self): for hw_id, rasd_item in self.rasd_items.items(): rasd_item.connect(self) @@ -882,7 +1016,13 @@ oss.append(xml_text_element('{%s}Info' % NS_OVF, "Operating System")) virtual_system.append(oss) - hw = ET.Element('{%s}VirtualHardwareSection' % NS_OVF) + if self.product and self.product.transports: + transports = " ".join(self.product.transports) + hw_attrs = {'{%s}transport' % NS_OVF: transports} + else: + hw_attrs = None + hw = ET.Element('{%s}VirtualHardwareSection' % NS_OVF, hw_attrs) + hw.append(xml_text_element('{%s}Info' % NS_OVF, "Virtual Hardware")) virtual_system.append(hw) @@ -893,6 +1033,9 @@ xml_item[:] = sorted(xml_item, key=lambda child: child.tag) hw.append(xml_item) + for xcfg in self.extra_configs: + hw.append(xcfg.xml_item()) + for key, val in sorted(self.hardware_config.items()): hw.append(xml_config(key, val)) @@ -933,13 +1076,14 @@ @staticmethod - def _get_sha512(filename): + def _get_hash(filename, hash_type): + hash = hashlib.new(hash_type) with open(filename, "rb") as f: - hash = hashlib.sha512(f.read()).hexdigest(); - return hash + hash.update(f.read()) + return hash.hexdigest() - def write_manifest(self, ovf_file=None, mf_file=None): + def write_manifest(self, ovf_file=None, mf_file=None, hash_type="sha512"): if ovf_file == None: ovf_file = f"{self.name}.ovf" if mf_file == None: @@ -950,9 +1094,9 @@ filenames.append(file.path) with open(mf_file, "wt") as f: for fname in filenames: - hash = OVF._get_sha512(fname) + hash = OVF._get_hash(fname, hash_type) fname = os.path.basename(fname) - f.write(f"SHA512({fname})= {hash}\n") + f.write(f"{hash_type.upper()}({fname})= {hash}\n") def usage(): @@ -963,6 +1107,7 @@ print(" -o, --output-file <file> output file or directory name") print(" -f, --format ova|ovf|dir output format") print(" -m, --manifest create manifest file along with ovf (default true for output formats ova and dir)") + print(" --checksum-type sha256|sha512 set the checksum type for the manifest. Must be sha256 or sha512.") print(" -q quiet mode") print(" -h print help") print("") @@ -985,7 +1130,7 @@ assert type(key) is str, f"param name must be a string" if '=' in key: - key, default = [t.strip() for t in key.split('=')] + key, default = [t.strip() for t in key.split('=', maxsplit=1)] default = yaml.safe_load(default) value = params.get(key, default) @@ -1002,9 +1147,10 @@ do_quiet = False do_manifest = False params = {} + checksum_type = "sha512" try: - opts, args = getopt.getopt(sys.argv[1:], 'f:hi:mo:q', longopts=['format=', 'input-file=', 'manifest', 'output-file=', 'param=']) + opts, args = getopt.getopt(sys.argv[1:], 'f:hi:mo:q', longopts=['format=', 'input-file=', 'manifest', 'output-file=', 'param=', 'checksum-type=']) except: print ("invalid option") sys.exit(2) @@ -1018,8 +1164,10 @@ output_format = a elif o in ['-m', '--manifest']: do_manifest = True + elif o in ['--checksum-type']: + checksum_type = a elif o in ['--param']: - k,v = a.split('=') + k,v = a.split('=', maxsplit=1) params[k] = yaml.safe_load(v) elif o in ['-q']: do_quiet = True @@ -1032,6 +1180,8 @@ assert config_file != None, "no input file specified" assert output_file != None, "no output file/directory specified" + assert checksum_type in ["sha512", "sha256"], f"checksum-type '{checksum_type}' is invalid" + if config_file != None: f = open(config_file, 'r') @@ -1069,7 +1219,7 @@ ovf_file = output_file ovf.write_xml(ovf_file=ovf_file) if do_manifest: - ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file) + ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file, hash_type=checksum_type) elif output_format == "ova" or output_format == "dir": pwd = os.getcwd() tmpdir = tempfile.mkdtemp(prefix=f"{basename}-", dir=pwd) @@ -1084,7 +1234,7 @@ os.symlink(os.path.join(pwd, file.path), dst) all_files.append(dst) - ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file) + ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file, hash_type=checksum_type) if output_format == "ova": ret = subprocess.check_call(["tar", "--format=ustar", "-h", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/basic.yaml new/open-vmdk-0.3.6.0/pytest/configs/basic.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/basic.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/basic.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,34 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmw.vmwarePhoton64Guest + +networks: + vm_network: + name: "None" + description: "The None network" + +hardware: + cpus: 2 + memory: + type: memory + size: 4096 + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + usb1: + type: usb_controller + ethernet1: + type: ethernet + subtype: VmxNet3 + network: vm_network + videocard1: + type: video_card + vmci1: + type: vmci diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/basic_no_network.yaml new/open-vmdk-0.3.6.0/pytest/configs/basic_no_network.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/basic_no_network.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/basic_no_network.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,25 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmwarePhoton64Guest + +hardware: + cpus: 2 + memory: + type: memory + size: 4096 + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + usb1: + type: usb_controller + videocard1: + type: video_card + vmci1: + type: vmci diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/configuration.yaml new/open-vmdk-0.3.6.0/pytest/configs/configuration.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/configuration.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/configuration.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,70 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmwarePhoton64Guest + default_configuration: grande + +networks: + vm_network: + name: "None" + description: "The None network" + +hardware: + cpus: 2 + memory_tall: + type: memory + size: 2048 + configuration: tall + memory_grande: + type: memory + size: 4096 + configuration: grande + memory_venti: + type: memory + size: 8192 + configuration: venti + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + homedisk_tall: + type: hard_disk + parent: sata1 + disk_capacity: 10000 + configuration: tall + homedisk_grande: + type: hard_disk + parent: sata1 + disk_capacity: 20000 + configuration: grande + homedisk_venti: + type: hard_disk + parent: sata1 + disk_capacity: 40000 + configuration: venti + usb1: + type: usb_controller + ethernet1: + type: ethernet + subtype: VmxNet3 + network: vm_network + videocard1: + type: video_card + vmci1: + type: vmci + +configurations: + tall: + label: Tall + description: too little for the money + grande: + label: Grande + description: just right + venti: + label: Venti + description: too much \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/environment.yaml new/open-vmdk-0.3.6.0/pytest/configs/environment.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/environment.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/environment.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,56 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmwarePhoton64Guest + +networks: + vm_network: + name: "None" + description: "The None network" + +hardware: + cpus: 2 + memory: + type: memory + size: 4096 + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + usb1: + type: usb_controller + ethernet1: + type: ethernet + subtype: VmxNet3 + network: vm_network + videocard1: + type: video_card + vmci1: + type: vmci + +environment: + transports: + - iso + - com.vmware.guestInfo + categories: + some: Some Properties + properties: + prop.int: + user_configurable: true + type: uint16 + value: '50000' + description: "an unsigned integer" + label: "integer" + category: some + prop.string: + user_configurable: true + type: string + value: 'a string' + description: "a string, yo" + label: "string" + category: some diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/extra_configs.yaml new/open-vmdk-0.3.6.0/pytest/configs/extra_configs.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/extra_configs.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/extra_configs.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,51 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmwarePhoton64Guest + +networks: + vm_network: + name: "None" + description: "The None network" + +hardware: + cpus: 2 + memory: + type: memory + size: 4096 + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + usb1: + type: usb_controller + ethernet1: + type: ethernet + subtype: VmxNet3 + network: vm_network + videocard1: + type: video_card + vmci1: + type: vmci + +extra_configs: + type.boolean: + required: false + value: true + type.int: + value: 2048000 + type.string: + value: "foo bar" + is_required: + value: 1 + required: true + not_required: + value: 2 + required: false + no_required: + value: 3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/configs/product.yaml new/open-vmdk-0.3.6.0/pytest/configs/product.yaml --- old/open-vmdk-0.3.1.0/pytest/configs/product.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/configs/product.yaml 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,38 @@ +system: + name: minimal + type: vmx-14 vmx-20 + os_vmw: vmwarePhoton64Guest + +networks: + vm_network: + name: "None" + description: "The None network" + +hardware: + cpus: 2 + memory: + type: memory + size: 4096 + sata1: + type: sata_controller + cdrom1: + type: cd_drive + parent: sata1 + rootdisk: + type: hard_disk + parent: sata1 + disk_image: dummy.vmdk + usb1: + type: usb_controller + ethernet1: + type: ethernet + subtype: VmxNet3 + network: vm_network + videocard1: + type: video_card + vmci1: + type: vmci + +product: + product: An Example VM + vendor: A Company Inc. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/test_configs.py new/open-vmdk-0.3.6.0/pytest/test_configs.py --- old/open-vmdk-0.3.1.0/pytest/test_configs.py 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/test_configs.py 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,76 @@ +# Copyright (c) 2023 VMware, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the âLicenseâ); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an âAS ISâ BASIS, without warranties or +# conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the +# specific language governing permissions and limitations under the License. + +import glob +import os +import pytest +import shutil +import subprocess +import yaml +import xmltodict + + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +OVA_COMPOSE = os.path.join(THIS_DIR, "..", "ova-compose", "ova-compose.py") + +VMDK_CONVERT=os.path.join(THIS_DIR, "..", "build", "vmdk", "vmdk-convert") + +CONFIG_DIR=os.path.join(THIS_DIR, "configs") + +WORK_DIR=os.path.join(os.getcwd(), "pytest-tmp") + + [email protected](scope='module', autouse=True) +def setup_test(): + os.makedirs(WORK_DIR, exist_ok=True) + + process = subprocess.run(["dd", "if=/dev/zero", "of=dummy.img", "bs=1024", "count=1024"], cwd=WORK_DIR) + assert process.returncode == 0 + + process = subprocess.run([VMDK_CONVERT, "dummy.img", "dummy.vmdk"], cwd=WORK_DIR) + assert process.returncode == 0 + + yield + shutil.rmtree(WORK_DIR) + + [email protected]("in_yaml", glob.glob(os.path.join(CONFIG_DIR, "*.yaml"))) +def test_configs(in_yaml): + basename = os.path.basename(in_yaml.rsplit(".", 1)[0]) + out_ovf = os.path.join(WORK_DIR, f"{basename}.ovf") + + process = subprocess.run([OVA_COMPOSE, "-i", in_yaml, "-o", out_ovf], cwd=WORK_DIR) + assert process.returncode == 0 + + with open(in_yaml) as f: + yaml_loader = yaml.SafeLoader + config = yaml.load(f, Loader=yaml_loader) + + with open(out_ovf) as f: + ovf = xmltodict.parse(f.read()) + + cfg_system = config['system'] + assert cfg_system['name'] == ovf['ovf:Envelope']['ovf:VirtualSystem']['ovf:Name'] + assert cfg_system['os_vmw'] == ovf['ovf:Envelope']['ovf:VirtualSystem']['ovf:OperatingSystemSection']['@vmw:osType'] + assert cfg_system['type'] == ovf['ovf:Envelope']['ovf:VirtualSystem']['ovf:VirtualHardwareSection']['ovf:System']['vssd:VirtualSystemType'] + + vmw_configs = ovf['ovf:Envelope']['ovf:VirtualSystem']['ovf:VirtualHardwareSection']['vmw:Config'] + + # TODO: check if default is set to bios unless no_default_configs is set + if 'firmware' in cfg_system: + firmware = None + if type(vmw_configs) == list: + for vmw_config in vmw_configs: + if vmw_config['@vmw:key'] == "firmware": + firmware = vmw_config['@vmw:value'] + assert cfg_system['firmware'] == firmware diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/pytest/test_manifest.py new/open-vmdk-0.3.6.0/pytest/test_manifest.py --- old/open-vmdk-0.3.1.0/pytest/test_manifest.py 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/pytest/test_manifest.py 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,115 @@ +# Copyright (c) 2023 VMware, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the âLicenseâ); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an âAS ISâ BASIS, without warranties or +# conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the +# specific language governing permissions and limitations under the License. + +import glob +import hashlib +import os +import pytest +import shutil +import subprocess +import yaml +import xmltodict + + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +OVA_COMPOSE = os.path.join(THIS_DIR, "..", "ova-compose", "ova-compose.py") + +VMDK_CONVERT=os.path.join(THIS_DIR, "..", "build", "vmdk", "vmdk-convert") + +CONFIG_DIR=os.path.join(THIS_DIR, "configs") + +WORK_DIR=os.path.join(os.getcwd(), "pytest-tmp") + + [email protected](scope='module', autouse=True) +def setup_test(): + os.makedirs(WORK_DIR, exist_ok=True) + + process = subprocess.run(["dd", "if=/dev/zero", "of=dummy.img", "bs=1024", "count=1024"], cwd=WORK_DIR) + assert process.returncode == 0 + + process = subprocess.run([VMDK_CONVERT, "dummy.img", "dummy.vmdk"], cwd=WORK_DIR) + assert process.returncode == 0 + + yield + shutil.rmtree(WORK_DIR) + + +def check_mf(mf_path, hash_type, work_dir=WORK_DIR): + with open(mf_path, "rt") as f: + for line in f: + left, hash_mf = line.split("=") + hash_mf = hash_mf.strip() + + assert left.startswith(hash_type.upper()) + + filename = left[len(hash_type):].strip("()") + hash = hashlib.new(hash_type) + with open(os.path.join(work_dir, filename), "rb") as f: + hash.update(f.read()) + + assert hash.hexdigest() == hash_mf + + [email protected]("hash_type", [None, "sha256", "sha512"]) +def test_ovf_manifest(hash_type): + in_yaml = os.path.join(CONFIG_DIR, "basic.yaml") + basename = os.path.basename(in_yaml.rsplit(".", 1)[0]) + out_ovf = os.path.join(WORK_DIR, f"{basename}.ovf") + out_mf = os.path.join(WORK_DIR, f"{basename}.mf") + + args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ovf, "-m"] + if hash_type is not None: + args += ["--checksum-type", hash_type] + else: + hash_type = "sha512" + + process = subprocess.run(args, cwd=WORK_DIR) + assert process.returncode == 0 + + assert os.path.isfile(out_mf) + + check_mf(out_mf, hash_type) + + [email protected]("hash_type", [None, "sha256", "sha512"]) +def test_ova_manifest(hash_type): + in_yaml = os.path.join(CONFIG_DIR, "basic.yaml") + basename = os.path.basename(in_yaml.rsplit(".", 1)[0]) + out_ova = os.path.join(WORK_DIR, f"{basename}.ova") + out_mf = os.path.join(WORK_DIR, f"{basename}.mf") + + args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ova] + if hash_type is not None: + args += ["--checksum-type", hash_type] + else: + hash_type = "sha512" + + process = subprocess.run(args, cwd=WORK_DIR) + assert process.returncode == 0 + + subprocess.run(["tar", "xf", out_ova], cwd=WORK_DIR) + + check_mf(out_mf, hash_type) + + +def test_manifest_invalid_checksum_type(): + in_yaml = os.path.join(CONFIG_DIR, "basic.yaml") + basename = os.path.basename(in_yaml.rsplit(".", 1)[0]) + out_ovf = os.path.join(WORK_DIR, f"{basename}.ovf") + out_mf = os.path.join(WORK_DIR, f"{basename}.mf") + + args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ovf, "-m", "--checksum-type", "foobar"] + process = subprocess.run(args, cwd=WORK_DIR) + assert process.returncode != 0 + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/open-vmdk-0.3.1.0/templates/template-hw21.ovf new/open-vmdk-0.3.6.0/templates/template-hw21.ovf --- old/open-vmdk-0.3.1.0/templates/template-hw21.ovf 1970-01-01 01:00:00.000000000 +0100 +++ new/open-vmdk-0.3.6.0/templates/template-hw21.ovf 2023-11-14 19:12:25.000000000 +0100 @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2023 VMware, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the âLicenseâ); you may not + use this file except in compliance with the License. You may obtain a copy of + the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an âAS ISâ BASIS, without warranties or + conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the + specific language governing permissions and limitations under the License. +--> +<Envelope 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"> + <References> + <File ovf:href="@@NAME@@-disk1.vmdk" ovf:id="file1" ovf:size="@@VMDK_FILE_SIZE@@"/> + </References> + <DiskSection> + <Info>Virtual disk information</Info> + <Disk ovf:capacity="@@VMDK_CAPACITY@@" ovf:capacityAllocationUnits="byte" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"/> + </DiskSection> + <NetworkSection> + <Info>The list of logical networks</Info> + <Network ovf:name="VM Network"> + <Description>The VM Network network</Description> + </Network> + </NetworkSection> + <VirtualSystem ovf:id="@@NAME@@"> + <Info>A virtual machine</Info> + <Name>@@NAME@@</Name> + <OperatingSystemSection ovf:id="1" vmw:osType="other4xLinux64Guest"> + <Info>The kind of installed guest operating system</Info> + </OperatingSystemSection> + <VirtualHardwareSection> + <Info>Virtual hardware requirements</Info> + <System> + <vssd:ElementName>Virtual Hardware Family</vssd:ElementName> + <vssd:InstanceID>0</vssd:InstanceID> + <vssd:VirtualSystemIdentifier>@@NAME@@</vssd:VirtualSystemIdentifier> + <vssd:VirtualSystemType>vmx-21</vssd:VirtualSystemType> + </System> + <Item> + <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits> + <rasd:Description>Number of Virtual CPUs</rasd:Description> + <rasd:ElementName>@@NUM_CPUS@@ virtual CPU(s)</rasd:ElementName> + <rasd:InstanceID>1</rasd:InstanceID> + <rasd:ResourceType>3</rasd:ResourceType> + <rasd:VirtualQuantity>@@NUM_CPUS@@</rasd:VirtualQuantity> + </Item> + <Item> + <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits> + <rasd:Description>Memory Size</rasd:Description> + <rasd:ElementName>@@MEM_SIZE@@MB of memory</rasd:ElementName> + <rasd:InstanceID>2</rasd:InstanceID> + <rasd:ResourceType>4</rasd:ResourceType> + <rasd:VirtualQuantity>@@MEM_SIZE@@</rasd:VirtualQuantity> + </Item> + <Item> + <rasd:Address>0</rasd:Address> + <rasd:Description>SCSI Controller</rasd:Description> + <rasd:ElementName>SCSI controller 0</rasd:ElementName> + <rasd:InstanceID>3</rasd:InstanceID> + <rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType> + <rasd:ResourceType>6</rasd:ResourceType> + </Item> + <Item> + <rasd:Address>1</rasd:Address> + <rasd:Description>IDE Controller</rasd:Description> + <rasd:ElementName>IDE 1</rasd:ElementName> + <rasd:InstanceID>4</rasd:InstanceID> + <rasd:ResourceType>5</rasd:ResourceType> + </Item> + <Item> + <rasd:Address>0</rasd:Address> + <rasd:Description>IDE Controller</rasd:Description> + <rasd:ElementName>IDE 0</rasd:ElementName> + <rasd:InstanceID>5</rasd:InstanceID> + <rasd:ResourceType>5</rasd:ResourceType> + </Item> + <Item ovf:required="false"> + <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation> + <rasd:ElementName>Video card</rasd:ElementName> + <rasd:InstanceID>6</rasd:InstanceID> + <rasd:ResourceType>24</rasd:ResourceType> + <vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/> + <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/> + <vmw:Config ovf:required="false" vmw:key="graphicsMemorySizeInKB" vmw:value="262144"/> + </Item> + <Item ovf:required="false"> + <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation> + <rasd:ElementName>VMCI device</rasd:ElementName> + <rasd:InstanceID>7</rasd:InstanceID> + <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType> + <rasd:ResourceType>1</rasd:ResourceType> + <vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false"/> + </Item> + <Item ovf:required="false"> + <rasd:AddressOnParent>0</rasd:AddressOnParent> + <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation> + <rasd:ElementName>CD/DVD drive 1</rasd:ElementName> + <rasd:InstanceID>8</rasd:InstanceID> + <rasd:Parent>5</rasd:Parent> + <rasd:ResourceSubType>vmware.cdrom.remotepassthrough</rasd:ResourceSubType> + <rasd:ResourceType>15</rasd:ResourceType> + <vmw:Config ovf:required="false" vmw:key="backing.exclusive" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/> + </Item> + <Item> + <rasd:AddressOnParent>0</rasd:AddressOnParent> + <rasd:ElementName>Hard disk 1</rasd:ElementName> + <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource> + <rasd:InstanceID>9</rasd:InstanceID> + <rasd:Parent>3</rasd:Parent> + <rasd:ResourceType>17</rasd:ResourceType> + <vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/> + </Item> + <Item> + <rasd:AddressOnParent>7</rasd:AddressOnParent> + <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation> + <rasd:Connection>VM Network</rasd:Connection> + <rasd:Description>VmxNet3 ethernet adapter on "VM Network"</rasd:Description> + <rasd:ElementName>Network adapter 1</rasd:ElementName> + <rasd:InstanceID>10</rasd:InstanceID> + <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType> + <rasd:ResourceType>10</rasd:ResourceType> + <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/> + <vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/> + </Item> + <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="@@FIRMWARE@@"/> + <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="tools.afterPowerOn" vmw:value="true"/> + <vmw:Config ovf:required="false" vmw:key="tools.afterResume" vmw:value="true"/> + <vmw:Config ovf:required="false" vmw:key="tools.beforeGuestShutdown" vmw:value="true"/> + <vmw:Config ovf:required="false" vmw:key="tools.beforeGuestStandby" vmw:value="true"/> + <vmw:Config ovf:required="false" vmw:key="tools.toolsUpgradePolicy" vmw:value="manual"/> + <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/> + <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/> + <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="hard"/> + <vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="vPMCEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="flags.vvtdEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="flags.vbsEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="bootOptions.efiSecureBootEnabled" vmw:value="false"/> + <vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="checkpoint"/> + </VirtualHardwareSection> + </VirtualSystem> +</Envelope> ++++++ open-vmdk.obsinfo ++++++ --- /var/tmp/diff_new_pack.OUKgGK/_old 2023-12-18 22:57:29.603018296 +0100 +++ /var/tmp/diff_new_pack.OUKgGK/_new 2023-12-18 22:57:29.603018296 +0100 @@ -1,5 +1,5 @@ name: open-vmdk -version: 0.3.1.0 -mtime: 1690321209 -commit: 5afbfb5270099586b93e28ae7585f3c661f379b5 +version: 0.3.6.0 +mtime: 1699985545 +commit: ba658b05c4bf95631e9b7ae55ce5bc248192b798
