From 11630044d235e0c6e1ffd2b12ff8906613ccdac6 Mon Sep 17 00:00:00 2001 From: xiaofengw-vmware <42736879+xiaofengw-vmware@users.noreply.github.com> Date: Thu, 14 Jan 2021 07:18:28 +0800 Subject: [VMware] Support cloudinit raw data feature (#691) This feature will modify VMware datasource to read from meta data and user data which are specified by VMware vSphere user. If meta data/user data are found in cloud-init configuration directory, datasource will parse the meta data/network and user data from the configuration file, otherwise it will continue to parse them from traditional customization configuration file as before. The supported meta data file is in json or yaml format. --- cloudinit/sources/DataSourceOVF.py | 162 ++++++++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 12 deletions(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index 741c140a..94d9f1b9 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -16,6 +16,7 @@ from xml.dom import minidom from cloudinit import dmi from cloudinit import log as logging +from cloudinit import safeyaml from cloudinit import sources from cloudinit import subp from cloudinit import util @@ -47,6 +48,7 @@ LOG = logging.getLogger(__name__) CONFGROUPNAME_GUESTCUSTOMIZATION = "deployPkg" GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS = "enable-custom-scripts" +VMWARE_IMC_DIR = "/var/run/vmware-imc" class DataSourceOVF(sources.DataSource): @@ -99,9 +101,7 @@ class DataSourceOVF(sources.DataSource): if not self.vmware_customization_supported: LOG.debug("Skipping the check for " "VMware Customization support") - elif not util.get_cfg_option_bool( - self.sys_cfg, "disable_vmware_customization", True): - + else: search_paths = ( "/usr/lib/vmware-tools", "/usr/lib64/vmware-tools", "/usr/lib/open-vm-tools", "/usr/lib64/open-vm-tools") @@ -119,7 +119,9 @@ class DataSourceOVF(sources.DataSource): # When the VM is powered on, the "VMware Tools" daemon # copies the customization specification file to # /var/run/vmware-imc directory. cloud-init code needs - # to search for the file in that directory. + # to search for the file in that directory which indicates + # that required metadata and userdata files are now + # present. max_wait = get_max_wait_from_cfg(self.ds_cfg) vmwareImcConfigFilePath = util.log_time( logfunc=LOG.debug, @@ -129,26 +131,83 @@ class DataSourceOVF(sources.DataSource): else: LOG.debug("Did not find the customization plugin.") + md_path = None if vmwareImcConfigFilePath: + imcdirpath = os.path.dirname(vmwareImcConfigFilePath) + cf = ConfigFile(vmwareImcConfigFilePath) + self._vmware_cust_conf = Config(cf) LOG.debug("Found VMware Customization Config File at %s", vmwareImcConfigFilePath) - nicspath = wait_for_imc_cfg_file( - filename="nics.txt", maxwait=10, naplen=5) + try: + (md_path, ud_path, nicspath) = collect_imc_file_paths( + self._vmware_cust_conf) + except FileNotFoundError as e: + _raise_error_status( + "File(s) missing in directory", + e, + GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED, + vmwareImcConfigFilePath, + self._vmware_cust_conf) else: LOG.debug("Did not find VMware Customization Config File") - else: - LOG.debug("Customization for VMware platform is disabled.") - if vmwareImcConfigFilePath: + # Honor disable_vmware_customization setting on metadata absent + if not md_path: + if util.get_cfg_option_bool(self.sys_cfg, + "disable_vmware_customization", + True): + LOG.debug( + "Customization for VMware platform is disabled.") + # reset vmwareImcConfigFilePath to None to avoid + # customization for VMware platform + vmwareImcConfigFilePath = None + + use_raw_data = bool(vmwareImcConfigFilePath and md_path) + if use_raw_data: + set_gc_status(self._vmware_cust_conf, "Started") + LOG.debug("Start to load cloud-init meta data and user data") + try: + (md, ud, cfg, network) = load_cloudinit_data(md_path, ud_path) + + if network: + self._network_config = network + else: + self._network_config = ( + self.distro.generate_fallback_config() + ) + + except safeyaml.YAMLError as e: + _raise_error_status( + "Error parsing the cloud-init meta data", + e, + GuestCustErrorEnum.GUESTCUST_ERROR_WRONG_META_FORMAT, + vmwareImcConfigFilePath, + self._vmware_cust_conf) + except Exception as e: + _raise_error_status( + "Error loading cloud-init configuration", + e, + GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED, + vmwareImcConfigFilePath, + self._vmware_cust_conf) + + self._vmware_cust_found = True + found.append('vmware-tools') + + util.del_dir(imcdirpath) + set_customization_status( + GuestCustStateEnum.GUESTCUST_STATE_DONE, + GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS) + set_gc_status(self._vmware_cust_conf, "Successful") + + elif vmwareImcConfigFilePath: + # Load configuration from vmware_imc self._vmware_nics_to_enable = "" try: - cf = ConfigFile(vmwareImcConfigFilePath) - self._vmware_cust_conf = Config(cf) set_gc_status(self._vmware_cust_conf, "Started") (md, ud, cfg) = read_vmware_imc(self._vmware_cust_conf) self._vmware_nics_to_enable = get_nics_to_enable(nicspath) - imcdirpath = os.path.dirname(vmwareImcConfigFilePath) product_marker = self._vmware_cust_conf.marker_id hasmarkerfile = check_marker_exists( product_marker, os.path.join(self.paths.cloud_dir, 'data')) @@ -684,4 +743,83 @@ def _raise_error_status(prefix, error, event, config_file, conf): util.del_dir(os.path.dirname(config_file)) raise error + +def load_cloudinit_data(md_path, ud_path): + """ + Load the cloud-init meta data, user data, cfg and network from the + given files + + @return: 4-tuple of configuration + metadata, userdata, cfg={}, network + + @raises: FileNotFoundError if md_path or ud_path are absent + """ + LOG.debug('load meta data from: %s: user data from: %s', + md_path, ud_path) + md = {} + ud = None + network = None + + md = safeload_yaml_or_dict(util.load_file(md_path)) + + if 'network' in md: + network = md['network'] + + if ud_path: + ud = util.load_file(ud_path).replace("\r", "") + return md, ud, {}, network + + +def safeload_yaml_or_dict(data): + ''' + The meta data could be JSON or YAML. Since YAML is a strict superset of + JSON, we will unmarshal the data as YAML. If data is None then a new + dictionary is returned. + ''' + if not data: + return {} + return safeyaml.load(data) + + +def collect_imc_file_paths(cust_conf): + ''' + collect all the other imc files. + + metadata is preferred to nics.txt configuration data. + + If metadata file exists because it is specified in customization + configuration, then metadata is required and userdata is optional. + + @return a 3-tuple containing desired configuration file paths if present + Expected returns: + 1. user provided metadata and userdata (md_path, ud_path, None) + 2. user provided metadata (md_path, None, None) + 3. user-provided network config (None, None, nics_path) + 4. No config found (None, None, None) + ''' + md_path = None + ud_path = None + nics_path = None + md_file = cust_conf.meta_data_name + if md_file: + md_path = os.path.join(VMWARE_IMC_DIR, md_file) + if not os.path.exists(md_path): + raise FileNotFoundError("meta data file is not found: %s" + % md_path) + + ud_file = cust_conf.user_data_name + if ud_file: + ud_path = os.path.join(VMWARE_IMC_DIR, ud_file) + if not os.path.exists(ud_path): + raise FileNotFoundError("user data file is not found: %s" + % ud_path) + else: + nics_path = os.path.join(VMWARE_IMC_DIR, "nics.txt") + if not os.path.exists(nics_path): + LOG.debug('%s does not exist.', nics_path) + nics_path = None + + return md_path, ud_path, nics_path + + # vi: ts=4 expandtab -- cgit v1.2.3 From 6efe16d8f28b89423f7e6e60fc0d4ab1f385f4d8 Mon Sep 17 00:00:00 2001 From: xiaofengw-vmware <42736879+xiaofengw-vmware@users.noreply.github.com> Date: Thu, 28 Jan 2021 04:38:47 +0800 Subject: [VMware] change default max wait time to 15s (#774) If cloud-init is enabled on VMware platform, cloud-init will wait until its configuration file is ready and currently the max wait is 90 seconds by default. With our test, this configuration file should be ready within 1 second, so change it to 15 seconds for better performance. Also update the documentation about how to change the default value in cloud-init configuration file. --- cloudinit/sources/DataSourceOVF.py | 2 +- doc/rtd/topics/datasources/ovf.rst | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index 94d9f1b9..bbeada0b 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -416,7 +416,7 @@ class DataSourceOVFNet(DataSourceOVF): def get_max_wait_from_cfg(cfg): - default_max_wait = 90 + default_max_wait = 15 max_wait_cfg_option = 'vmware_cust_file_max_wait' max_wait = default_max_wait diff --git a/doc/rtd/topics/datasources/ovf.rst b/doc/rtd/topics/datasources/ovf.rst index 6256e624..85b0c377 100644 --- a/doc/rtd/topics/datasources/ovf.rst +++ b/doc/rtd/topics/datasources/ovf.rst @@ -13,6 +13,15 @@ source code tree in doc/sources/ovf Configuration ------------- +The following configuration can be set for the datasource in system +configuration (in `/etc/cloud/cloud.cfg` or `/etc/cloud/cloud.cfg.d/`). + +The settings that may be configured are: + + * vmware_cust_file_max_wait: the maximum amount of clock time in seconds that + should be spent waiting for vmware customization files. (default: 15) + + On VMware platforms, VMTools use is required for OVF datasource configuration settings as well as vCloud and vSphere admin configuration. User could change the VMTools configuration options with command:: -- cgit v1.2.3 From 108611aee26e09bec683e6cf1b8e03bec9362de9 Mon Sep 17 00:00:00 2001 From: xiaofengw-vmware <42736879+xiaofengw-vmware@users.noreply.github.com> Date: Thu, 8 Jul 2021 23:14:33 +0800 Subject: VMware: new "allow_raw_data" switch (#939) Add a new switch allow_raw_data to control raw data feature, update the documentation. Fix bugs about max_wait. --- cloudinit/sources/DataSourceOVF.py | 35 +++++++++---- doc/rtd/topics/datasources/ovf.rst | 4 ++ tests/unittests/test_datasource/test_ovf.py | 79 ++++++++++++++++++++++++++--- 3 files changed, 100 insertions(+), 18 deletions(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index bbeada0b..9e83dccc 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -98,9 +98,20 @@ class DataSourceOVF(sources.DataSource): found.append(seed) elif system_type and 'vmware' in system_type.lower(): LOG.debug("VMware Virtualization Platform found") + allow_vmware_cust = False + allow_raw_data = False if not self.vmware_customization_supported: LOG.debug("Skipping the check for " "VMware Customization support") + else: + allow_vmware_cust = not util.get_cfg_option_bool( + self.sys_cfg, "disable_vmware_customization", True) + allow_raw_data = util.get_cfg_option_bool( + self.ds_cfg, "allow_raw_data", True) + + if not (allow_vmware_cust or allow_raw_data): + LOG.debug( + "Customization for VMware platform is disabled.") else: search_paths = ( "/usr/lib/vmware-tools", "/usr/lib64/vmware-tools", @@ -148,19 +159,21 @@ class DataSourceOVF(sources.DataSource): GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED, vmwareImcConfigFilePath, self._vmware_cust_conf) - else: - LOG.debug("Did not find VMware Customization Config File") - - # Honor disable_vmware_customization setting on metadata absent - if not md_path: - if util.get_cfg_option_bool(self.sys_cfg, - "disable_vmware_customization", - True): + # Don't handle the customization for below 2 cases: + # 1. meta data is found, allow_raw_data is False. + # 2. no meta data is found, allow_vmware_cust is False. + if md_path and not allow_raw_data: LOG.debug( - "Customization for VMware platform is disabled.") + "Customization using raw data is disabled.") # reset vmwareImcConfigFilePath to None to avoid # customization for VMware platform vmwareImcConfigFilePath = None + if md_path is None and not allow_vmware_cust: + LOG.debug( + "Customization using VMware config is disabled.") + vmwareImcConfigFilePath = None + else: + LOG.debug("Did not find VMware Customization Config File") use_raw_data = bool(vmwareImcConfigFilePath and md_path) if use_raw_data: @@ -429,7 +442,7 @@ def get_max_wait_from_cfg(cfg): LOG.warning("Failed to get '%s', using %s", max_wait_cfg_option, default_max_wait) - if max_wait <= 0: + if max_wait < 0: LOG.warning("Invalid value '%s' for '%s', using '%s' instead", max_wait, max_wait_cfg_option, default_max_wait) max_wait = default_max_wait @@ -440,6 +453,8 @@ def get_max_wait_from_cfg(cfg): def wait_for_imc_cfg_file(filename, maxwait=180, naplen=5, dirpath="/var/run/vmware-imc"): waited = 0 + if maxwait <= naplen: + naplen = 1 while waited < maxwait: fileFullPath = os.path.join(dirpath, filename) diff --git a/doc/rtd/topics/datasources/ovf.rst b/doc/rtd/topics/datasources/ovf.rst index 43ee45ba..bd5df860 100644 --- a/doc/rtd/topics/datasources/ovf.rst +++ b/doc/rtd/topics/datasources/ovf.rst @@ -18,6 +18,10 @@ configuration (in `/etc/cloud/cloud.cfg` or `/etc/cloud/cloud.cfg.d/`). The settings that may be configured are: + * disable_vmware_customization: disable or enable the vmware customization + based on vmware customization files. (default: True) + * allow_raw_data: enable or disable the vmware customization based on raw + cloud-init data including metadata and userdata. (default: True) * vmware_cust_file_max_wait: the maximum amount of clock time in seconds that should be spent waiting for vmware customization files. (default: 15) diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py index dce01f5d..e2718077 100644 --- a/tests/unittests/test_datasource/test_ovf.py +++ b/tests/unittests/test_datasource/test_ovf.py @@ -138,18 +138,17 @@ class TestDatasourceOVF(CiTestCase): self.assertIn( 'DEBUG: No system-product-name found', self.logs.getvalue()) - def test_get_data_no_vmware_customization_disabled(self): - """When cloud-init workflow for vmware is disabled via sys_cfg and - no meta data provided, log a message. + def test_get_data_vmware_customization_disabled(self): + """When vmware customization is disabled via sys_cfg and + allow_raw_data is disabled via ds_cfg, log a message. """ paths = Paths({'cloud_dir': self.tdir}) ds = self.datasource( - sys_cfg={'disable_vmware_customization': True}, distro={}, - paths=paths) + sys_cfg={'disable_vmware_customization': True, + 'datasource': {'OVF': {'allow_raw_data': False}}}, + distro={}, paths=paths) conf_file = self.tmp_path('test-cust', self.tdir) conf_content = dedent("""\ - [CUSTOM-SCRIPT] - SCRIPT-NAME = test-script [MISC] MARKER-ID = 12345345 """) @@ -168,7 +167,71 @@ class TestDatasourceOVF(CiTestCase): 'DEBUG: Customization for VMware platform is disabled.', self.logs.getvalue()) - def test_get_data_vmware_customization_disabled(self): + def test_get_data_vmware_customization_sys_cfg_disabled(self): + """When vmware customization is disabled via sys_cfg and + no meta data is found, log a message. + """ + paths = Paths({'cloud_dir': self.tdir}) + ds = self.datasource( + sys_cfg={'disable_vmware_customization': True, + 'datasource': {'OVF': {'allow_raw_data': True}}}, + distro={}, paths=paths) + conf_file = self.tmp_path('test-cust', self.tdir) + conf_content = dedent("""\ + [MISC] + MARKER-ID = 12345345 + """) + util.write_file(conf_file, conf_content) + retcode = wrap_and_call( + 'cloudinit.sources.DataSourceOVF', + {'dmi.read_dmi_data': 'vmware', + 'transport_iso9660': NOT_FOUND, + 'transport_vmware_guestinfo': NOT_FOUND, + 'util.del_dir': True, + 'search_file': self.tdir, + 'wait_for_imc_cfg_file': conf_file}, + ds.get_data) + self.assertFalse(retcode, 'Expected False return from ds.get_data') + self.assertIn( + 'DEBUG: Customization using VMware config is disabled.', + self.logs.getvalue()) + + def test_get_data_allow_raw_data_disabled(self): + """When allow_raw_data is disabled via ds_cfg and + meta data is found, log a message. + """ + paths = Paths({'cloud_dir': self.tdir}) + ds = self.datasource( + sys_cfg={'disable_vmware_customization': False, + 'datasource': {'OVF': {'allow_raw_data': False}}}, + distro={}, paths=paths) + + # Prepare the conf file + conf_file = self.tmp_path('test-cust', self.tdir) + conf_content = dedent("""\ + [CLOUDINIT] + METADATA = test-meta + """) + util.write_file(conf_file, conf_content) + # Prepare the meta data file + metadata_file = self.tmp_path('test-meta', self.tdir) + util.write_file(metadata_file, "This is meta data") + retcode = wrap_and_call( + 'cloudinit.sources.DataSourceOVF', + {'dmi.read_dmi_data': 'vmware', + 'transport_iso9660': NOT_FOUND, + 'transport_vmware_guestinfo': NOT_FOUND, + 'util.del_dir': True, + 'search_file': self.tdir, + 'wait_for_imc_cfg_file': conf_file, + 'collect_imc_file_paths': [self.tdir + '/test-meta', '', '']}, + ds.get_data) + self.assertFalse(retcode, 'Expected False return from ds.get_data') + self.assertIn( + 'DEBUG: Customization using raw data is disabled.', + self.logs.getvalue()) + + def test_get_data_vmware_customization_enabled(self): """When cloud-init workflow for vmware is enabled via sys_cfg log a message. """ -- cgit v1.2.3 From f0ab1e64852d50f4fe0de84e0bca0ee8bb516a9f Mon Sep 17 00:00:00 2001 From: PengpengSun <40026211+PengpengSun@users.noreply.github.com> Date: Wed, 21 Jul 2021 00:49:37 +0800 Subject: VMware: add network-config support in ovf-env.xml (#947) Details: 1. Support guest set network config through guestinfo.ovfEnv using OVF 2. 'network-config' Property is optional 3. 'network-config' Property's value has to be base64 encoded Added unittests and updated ovf-env.xml example --- cloudinit/sources/DataSourceOVF.py | 14 ++++- doc/sources/ovf/example/ovf-env.xml | 8 +++ tests/unittests/test_datasource/test_ovf.py | 97 +++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index 9e83dccc..e909f058 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -358,8 +358,11 @@ class DataSourceOVF(sources.DataSource): if contents: break if contents: - (md, ud, cfg) = read_ovf_environment(contents) + read_network = ('com.vmware.guestinfo' == name) + (md, ud, cfg) = read_ovf_environment(contents, read_network) self.environment = contents + if 'network-config' in md and md['network-config']: + self._network_config = md['network-config'] found.append(name) # There was no OVF transports found @@ -507,13 +510,14 @@ def read_vmware_imc(config): # This will return a dict with some content # meta-data, user-data, some config -def read_ovf_environment(contents): +def read_ovf_environment(contents, read_network=False): props = get_properties(contents) md = {} cfg = {} ud = None cfg_props = ['password'] md_props = ['seedfrom', 'local-hostname', 'public-keys', 'instance-id'] + network_props = ['network-config'] for (prop, val) in props.items(): if prop == 'hostname': prop = "local-hostname" @@ -521,6 +525,12 @@ def read_ovf_environment(contents): md[prop] = val elif prop in cfg_props: cfg[prop] = val + elif prop in network_props and read_network: + try: + network_config = base64.b64decode(val.encode()) + md[prop] = safeload_yaml_or_dict(network_config).get('network') + except Exception: + LOG.debug("Ignore network-config in wrong format") elif prop == "user-data": try: ud = base64.b64decode(val.encode()) diff --git a/doc/sources/ovf/example/ovf-env.xml b/doc/sources/ovf/example/ovf-env.xml index 13e8f104..4ef4ee63 100644 --- a/doc/sources/ovf/example/ovf-env.xml +++ b/doc/sources/ovf/example/ovf-env.xml @@ -41,6 +41,14 @@ --> + + diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py index e2718077..9f52b504 100644 --- a/tests/unittests/test_datasource/test_ovf.py +++ b/tests/unittests/test_datasource/test_ovf.py @@ -83,6 +83,103 @@ class TestReadOvfEnv(CiTestCase): self.assertEqual({'password': "passw0rd"}, cfg) self.assertIsNone(ud) + def test_with_b64_network_config_enable_read_network(self): + network_config = dedent("""\ + network: + version: 2 + ethernets: + nics: + nameservers: + addresses: + - 127.0.0.53 + search: + - eng.vmware.com + - vmware.com + match: + name: eth* + gateway4: 10.10.10.253 + dhcp4: false + addresses: + - 10.10.10.1/24 + """) + network_config_b64 = base64.b64encode(network_config.encode()).decode() + props = {"network-config": network_config_b64, + "password": "passw0rd", + "instance-id": "inst-001"} + env = fill_properties(props) + md, ud, cfg = dsovf.read_ovf_environment(env, True) + self.assertEqual("inst-001", md["instance-id"]) + self.assertEqual({'password': "passw0rd"}, cfg) + self.assertEqual( + {'version': 2, 'ethernets': + {'nics': + {'nameservers': + {'addresses': ['127.0.0.53'], + 'search': ['eng.vmware.com', 'vmware.com']}, + 'match': {'name': 'eth*'}, + 'gateway4': '10.10.10.253', + 'dhcp4': False, + 'addresses': ['10.10.10.1/24']}}}, + md["network-config"]) + self.assertIsNone(ud) + + def test_with_non_b64_network_config_enable_read_network(self): + network_config = dedent("""\ + network: + version: 2 + ethernets: + nics: + nameservers: + addresses: + - 127.0.0.53 + search: + - eng.vmware.com + - vmware.com + match: + name: eth* + gateway4: 10.10.10.253 + dhcp4: false + addresses: + - 10.10.10.1/24 + """) + props = {"network-config": network_config, + "password": "passw0rd", + "instance-id": "inst-001"} + env = fill_properties(props) + md, ud, cfg = dsovf.read_ovf_environment(env, True) + self.assertEqual({"instance-id": "inst-001"}, md) + self.assertEqual({'password': "passw0rd"}, cfg) + self.assertIsNone(ud) + + def test_with_b64_network_config_disable_read_network(self): + network_config = dedent("""\ + network: + version: 2 + ethernets: + nics: + nameservers: + addresses: + - 127.0.0.53 + search: + - eng.vmware.com + - vmware.com + match: + name: eth* + gateway4: 10.10.10.253 + dhcp4: false + addresses: + - 10.10.10.1/24 + """) + network_config_b64 = base64.b64encode(network_config.encode()).decode() + props = {"network-config": network_config_b64, + "password": "passw0rd", + "instance-id": "inst-001"} + env = fill_properties(props) + md, ud, cfg = dsovf.read_ovf_environment(env) + self.assertEqual({"instance-id": "inst-001"}, md) + self.assertEqual({'password': "passw0rd"}, cfg) + self.assertIsNone(ud) + class TestMarkerFiles(CiTestCase): -- cgit v1.2.3 From 5ea2c669d6e8a9ab30f3107bee45cecc5fa1b081 Mon Sep 17 00:00:00 2001 From: PengpengSun <40026211+PengpengSun@users.noreply.github.com> Date: Fri, 17 Sep 2021 01:43:59 +0800 Subject: VMware: Fix typo introduced in #947 and add test (#1019) --- cloudinit/sources/DataSourceOVF.py | 2 +- tests/unittests/test_datasource/test_ovf.py | 43 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index e909f058..3e436dfa 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -358,7 +358,7 @@ class DataSourceOVF(sources.DataSource): if contents: break if contents: - read_network = ('com.vmware.guestinfo' == name) + read_network = ('com.vmware.guestInfo' == name) (md, ud, cfg) = read_ovf_environment(contents, read_network) self.environment = contents if 'network-config' in md and md['network-config']: diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py index 9f52b504..2ca10781 100644 --- a/tests/unittests/test_datasource/test_ovf.py +++ b/tests/unittests/test_datasource/test_ovf.py @@ -518,6 +518,49 @@ class TestDatasourceOVF(CiTestCase): 'vmware (%s/seed/ovf-env.xml)' % self.tdir, ds.subplatform) + def test_get_data_vmware_guestinfo_with_network_config(self): + network_config = dedent("""\ + network: + version: 2 + ethernets: + nics: + nameservers: + addresses: + - 127.0.0.53 + search: + - vmware.com + match: + name: eth* + gateway4: 10.10.10.253 + dhcp4: false + addresses: + - 10.10.10.1/24 + """) + network_config_b64 = base64.b64encode(network_config.encode()).decode() + props = {"network-config": network_config_b64, + "password": "passw0rd", + "instance-id": "inst-001"} + env = fill_properties(props) + paths = Paths({'cloud_dir': self.tdir, 'run_dir': self.tdir}) + ds = self.datasource(sys_cfg={}, distro={}, paths=paths) + with mock.patch(MPATH + 'transport_vmware_guestinfo', + return_value=env): + with mock.patch(MPATH + 'transport_iso9660', + return_value=NOT_FOUND): + self.assertTrue(ds.get_data()) + self.assertEqual('inst-001', ds.metadata['instance-id']) + self.assertEqual( + {'version': 2, 'ethernets': + {'nics': + {'nameservers': + {'addresses': ['127.0.0.53'], + 'search': ['vmware.com']}, + 'match': {'name': 'eth*'}, + 'gateway4': '10.10.10.253', + 'dhcp4': False, + 'addresses': ['10.10.10.1/24']}}}, + ds.network_config) + def test_get_data_cloudinit_metadata_json(self): """Test metadata can be loaded to cloud-init metadata and network. The metadata format is json. -- cgit v1.2.3 From 76166caff42b82aa55c6bcd9528f2c1e3575232a Mon Sep 17 00:00:00 2001 From: xiaofengw-vmware <42736879+xiaofengw-vmware@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:54:31 +0800 Subject: VMWARE: search the deployPkg plugin in multiarch dir (#1061) Due to multiarch, the libdeployPkgPlugin.so is deployed into dir /usr/lib//open-vm-tools, we need to add this path into search_paths. LP: #1944946 --- cloudinit/sources/DataSourceOVF.py | 4 +++- tests/unittests/test_ds_identify.py | 24 ++++++++++++++++++++++++ tools/ds-identify | 5 +++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index 3e436dfa..08a205f1 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -115,7 +115,9 @@ class DataSourceOVF(sources.DataSource): else: search_paths = ( "/usr/lib/vmware-tools", "/usr/lib64/vmware-tools", - "/usr/lib/open-vm-tools", "/usr/lib64/open-vm-tools") + "/usr/lib/open-vm-tools", "/usr/lib64/open-vm-tools", + "/usr/lib/x86_64-linux-gnu/open-vm-tools", + "/usr/lib/aarch64-linux-gnu/open-vm-tools") plugin = "libdeployPkgPlugin.so" deployPkgPluginPath = None diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py index 8617d7bd..43603ea5 100644 --- a/tests/unittests/test_ds_identify.py +++ b/tests/unittests/test_ds_identify.py @@ -534,6 +534,30 @@ class TestDsIdentify(DsIdentifyBase): return self._check_via_dict( cust64, RC_FOUND, dslist=[cust64.get('ds'), DS_NONE]) + def test_ovf_on_vmware_iso_found_open_vm_tools_x86_64_linux_gnu(self): + """OVF is identified when open-vm-tools installed in + /usr/lib/x86_64-linux-gnu.""" + cust64 = copy.deepcopy(VALID_CFG['OVF-vmware-customization']) + p32 = 'usr/lib/vmware-tools/plugins/vmsvc/libdeployPkgPlugin.so' + x86 = 'usr/lib/x86_64-linux-gnu/open-vm-tools/plugins/vmsvc/' \ + 'libdeployPkgPlugin.so' + cust64['files'][x86] = cust64['files'][p32] + del cust64['files'][p32] + return self._check_via_dict( + cust64, RC_FOUND, dslist=[cust64.get('ds'), DS_NONE]) + + def test_ovf_on_vmware_iso_found_open_vm_tools_aarch64_linux_gnu(self): + """OVF is identified when open-vm-tools installed in + /usr/lib/aarch64-linux-gnu.""" + cust64 = copy.deepcopy(VALID_CFG['OVF-vmware-customization']) + p32 = 'usr/lib/vmware-tools/plugins/vmsvc/libdeployPkgPlugin.so' + aarch64 = 'usr/lib/aarch64-linux-gnu/open-vm-tools/plugins/vmsvc/' \ + 'libdeployPkgPlugin.so' + cust64['files'][aarch64] = cust64['files'][p32] + del cust64['files'][p32] + return self._check_via_dict( + cust64, RC_FOUND, dslist=[cust64.get('ds'), DS_NONE]) + def test_ovf_on_vmware_iso_found_by_cdrom_with_matching_fs_label(self): """OVF is identified by well-known iso9660 labels.""" ovf_cdrom_by_label = copy.deepcopy(VALID_CFG['OVF']) diff --git a/tools/ds-identify b/tools/ds-identify index 63d2f0c8..c2f710e9 100755 --- a/tools/ds-identify +++ b/tools/ds-identify @@ -899,11 +899,16 @@ ovf_vmware_guest_customization() { # we have to have the plugin to do vmware customization local found="" pkg="" pre="${PATH_ROOT}/usr/lib" + local x86="x86_64-linux-gnu" aarch="aarch64-linux-gnu" local ppath="plugins/vmsvc/libdeployPkgPlugin.so" for pkg in vmware-tools open-vm-tools; do if [ -f "$pre/$pkg/$ppath" -o -f "${pre}64/$pkg/$ppath" ]; then found="$pkg"; break; fi + # search in multiarch dir + if [ -f "$pre/$x86/$pkg/$ppath" -o -f "$pre/$aarch/$pkg/$ppath" ]; then + found="$pkg"; break; + fi done [ -n "$found" ] || return 1 # vmware customization is disabled by default -- cgit v1.2.3 From a0a68a24c34ee268962e7a3c3844c59ab4036bf9 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 19 Oct 2021 02:09:40 +0200 Subject: VMware: read network-config from ISO (#1066) There is no reason for the ISO missing this functionality. As discussed in https://github.com/canonical/cloud-init/pull/947/files#r707338489 --- cloudinit/sources/DataSourceOVF.py | 3 +-- doc/sources/ovf/example/ovf-env.xml | 2 +- tests/unittests/test_datasource/test_ovf.py | 10 ++++++++-- tools/.github-cla-signers | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'cloudinit/sources/DataSourceOVF.py') diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index 08a205f1..5257a534 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -360,8 +360,7 @@ class DataSourceOVF(sources.DataSource): if contents: break if contents: - read_network = ('com.vmware.guestInfo' == name) - (md, ud, cfg) = read_ovf_environment(contents, read_network) + (md, ud, cfg) = read_ovf_environment(contents, True) self.environment = contents if 'network-config' in md and md['network-config']: self._network_config = md['network-config'] diff --git a/doc/sources/ovf/example/ovf-env.xml b/doc/sources/ovf/example/ovf-env.xml index 4ef4ee63..e5f4e262 100644 --- a/doc/sources/ovf/example/ovf-env.xml +++ b/doc/sources/ovf/example/ovf-env.xml @@ -42,7 +42,7 @@