summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxiaofengw-vmware <42736879+xiaofengw-vmware@users.noreply.github.com>2020-07-21 23:52:29 +0800
committerGitHub <noreply@github.com>2020-07-21 09:52:29 -0600
commit995f8adf00509e5d2aefc9f0680c3c4894ae6666 (patch)
tree77d3f08cd1fe9280ef509240000cc35e55dd7037
parentebc145be5e15c64a31ba496625727a7308a57baf (diff)
downloadvyos-cloud-init-995f8adf00509e5d2aefc9f0680c3c4894ae6666.tar.gz
vyos-cloud-init-995f8adf00509e5d2aefc9f0680c3c4894ae6666.zip
VMware: Support parsing DEFAULT-RUN-POST-CUST-SCRIPT (#441)
Add support for VMware's vCD configuration setting DEFAULT-RUN-POST-CUST-SCRIPT. When set True, it will default vms to run post customization scripts if the VM has not been configured in VMTools with "enable-custom-scripts" set False. Add datasource documentation with a bit more context about this interaction on VMware products. With this fix, the behavior will be: * If VM administrator doesn't want others to execute a script on this VM, VMtools can set "enable-custom-scripts" to false from the utility "vmware-toolbox-cmd". * If VM administrator doesn't set value to "enable-custom-scripts", then by default this script is disabled for security purpose. * For VMware's vCD product , the preference is to enable the script if "enable-custom-scripts" is not set. vCD will generate a configuration file with "DEFAULT-RUN-POST-CUST-SCRIPT" set to true. This flag works for both VMware customization engine and cloud-init.
-rw-r--r--cloudinit/sources/DataSourceOVF.py27
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config.py15
-rw-r--r--doc/rtd/topics/datasources/ovf.rst13
-rw-r--r--tests/unittests/test_datasource/test_ovf.py82
-rw-r--r--tests/unittests/test_vmware_config_file.py14
5 files changed, 143 insertions, 8 deletions
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index 4c5eee4f..e53d2eb1 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -152,14 +152,25 @@ class DataSourceOVF(sources.DataSource):
product_marker, os.path.join(self.paths.cloud_dir, 'data'))
special_customization = product_marker and not hasmarkerfile
customscript = self._vmware_cust_conf.custom_script_name
- custScriptConfig = get_tools_config(
- CONFGROUPNAME_GUESTCUSTOMIZATION,
- GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS,
- "false")
- if custScriptConfig.lower() != "true":
- # Update the customization status if there is a
- # custom script is disabled
- if special_customization and customscript:
+
+ # In case there is a custom script, check whether VMware
+ # Tools configuration allow the custom script to run.
+ if special_customization and customscript:
+ defVal = "false"
+ if self._vmware_cust_conf.default_run_post_script:
+ LOG.debug(
+ "Set default value to true due to"
+ " customization configuration."
+ )
+ defVal = "true"
+
+ custScriptConfig = get_tools_config(
+ CONFGROUPNAME_GUESTCUSTOMIZATION,
+ GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS,
+ defVal)
+ if custScriptConfig.lower() != "true":
+ # Update the customization status if custom script
+ # is disabled
msg = "Custom script is disabled by VM Administrator"
LOG.debug(msg)
set_customization_status(
diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py
index f2a81416..7109aef3 100644
--- a/cloudinit/sources/helpers/vmware/imc/config.py
+++ b/cloudinit/sources/helpers/vmware/imc/config.py
@@ -26,6 +26,7 @@ class Config(object):
TIMEZONE = 'DATETIME|TIMEZONE'
UTC = 'DATETIME|UTC'
POST_GC_STATUS = 'MISC|POST-GC-STATUS'
+ DEFAULT_RUN_POST_SCRIPT = 'MISC|DEFAULT-RUN-POST-CUST-SCRIPT'
def __init__(self, configFile):
self._configFile = configFile
@@ -115,4 +116,18 @@ class Config(object):
raise ValueError('PostGcStatus value should be yes/no')
return postGcStatus == 'yes'
+ @property
+ def default_run_post_script(self):
+ """
+ Return enable-custom-scripts default value if enable-custom-scripts
+ is absent in VM Tools configuration
+ """
+ defaultRunPostScript = self._configFile.get(
+ Config.DEFAULT_RUN_POST_SCRIPT,
+ 'no')
+ defaultRunPostScript = defaultRunPostScript.lower()
+ if defaultRunPostScript not in ('yes', 'no'):
+ raise ValueError('defaultRunPostScript value should be yes/no')
+ return defaultRunPostScript == 'yes'
+
# vi: ts=4 expandtab
diff --git a/doc/rtd/topics/datasources/ovf.rst b/doc/rtd/topics/datasources/ovf.rst
index c312617f..9a48cda6 100644
--- a/doc/rtd/topics/datasources/ovf.rst
+++ b/doc/rtd/topics/datasources/ovf.rst
@@ -11,4 +11,17 @@ transport.
For further information see a full working example in cloud-init's
source code tree in doc/sources/ovf
+Configuration
+-------------
+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:
+vmware-toolbox-cmd config set <section> <key> <value>
+
+The following VMTools configuration options affect cloud-init's behavior on a booted VM:
+ * a: [deploypkg] enable-custom-scripts
+ If this option is absent in VMTools configuration, the custom script is disabled by default for security reasons. Some VMware products could change this default behavior (for example: enabled by default) via customization specification settings.
+
+VMWare admin can refer to (https://github.com/canonical/cloud-init/blob/master/cloudinit/sources/helpers/vmware/imc/config.py) and set the customization specification settings.
+
+For more information, see [VMware vSphere Product Documentation](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-9A5093A5-C54F-4502-941B-3F9C0F573A39.html) and specific VMTools parameters consumed.
+
.. vi: textwidth=78
diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py
index 3ef7a4b2..1d088577 100644
--- a/tests/unittests/test_datasource/test_ovf.py
+++ b/tests/unittests/test_datasource/test_ovf.py
@@ -220,6 +220,88 @@ class TestDatasourceOVF(CiTestCase):
self.assertIn('Custom script is disabled by VM Administrator',
str(context.exception))
+ def test_get_data_cust_script_enabled(self):
+ """If custom script is enabled by VMware tools configuration,
+ execute the script.
+ """
+ paths = Paths({'cloud_dir': self.tdir})
+ ds = self.datasource(
+ sys_cfg={'disable_vmware_customization': False}, distro={},
+ paths=paths)
+ # Prepare the conf file
+ conf_file = self.tmp_path('test-cust', self.tdir)
+ conf_content = dedent("""\
+ [CUSTOM-SCRIPT]
+ SCRIPT-NAME = test-script
+ [MISC]
+ MARKER-ID = 12345346
+ """)
+ util.write_file(conf_file, conf_content)
+
+ # Mock custom script is enabled by return true when calling
+ # get_tools_config
+ with mock.patch(MPATH + 'get_tools_config', return_value="true"):
+ with mock.patch(MPATH + 'set_customization_status',
+ return_value=('msg', b'')):
+ with self.assertRaises(CustomScriptNotFound) as context:
+ wrap_and_call(
+ 'cloudinit.sources.DataSourceOVF',
+ {'util.read_dmi_data': 'vmware',
+ 'util.del_dir': True,
+ 'search_file': self.tdir,
+ 'wait_for_imc_cfg_file': conf_file,
+ 'get_nics_to_enable': ''},
+ ds.get_data)
+ # Verify custom script is trying to be executed
+ customscript = self.tmp_path('test-script', self.tdir)
+ self.assertIn('Script %s not found!!' % customscript,
+ str(context.exception))
+
+ def test_get_data_force_run_post_script_is_yes(self):
+ """If DEFAULT-RUN-POST-CUST-SCRIPT is yes, custom script could run if
+ enable-custom-scripts is not defined in VM Tools configuration
+ """
+ paths = Paths({'cloud_dir': self.tdir})
+ ds = self.datasource(
+ sys_cfg={'disable_vmware_customization': False}, distro={},
+ paths=paths)
+ # Prepare the conf file
+ conf_file = self.tmp_path('test-cust', self.tdir)
+ # set DEFAULT-RUN-POST-CUST-SCRIPT = yes so that enable-custom-scripts
+ # default value is TRUE
+ conf_content = dedent("""\
+ [CUSTOM-SCRIPT]
+ SCRIPT-NAME = test-script
+ [MISC]
+ MARKER-ID = 12345346
+ DEFAULT-RUN-POST-CUST-SCRIPT = yes
+ """)
+ util.write_file(conf_file, conf_content)
+
+ # Mock get_tools_config(section, key, defaultVal) to return
+ # defaultVal
+ def my_get_tools_config(*args, **kwargs):
+ return args[2]
+
+ with mock.patch(MPATH + 'get_tools_config',
+ side_effect=my_get_tools_config):
+ with mock.patch(MPATH + 'set_customization_status',
+ return_value=('msg', b'')):
+ with self.assertRaises(CustomScriptNotFound) as context:
+ wrap_and_call(
+ 'cloudinit.sources.DataSourceOVF',
+ {'util.read_dmi_data': 'vmware',
+ 'util.del_dir': True,
+ 'search_file': self.tdir,
+ 'wait_for_imc_cfg_file': conf_file,
+ 'get_nics_to_enable': ''},
+ ds.get_data)
+ # Verify custom script still runs although it is
+ # disabled by VMware Tools
+ customscript = self.tmp_path('test-script', self.tdir)
+ self.assertIn('Script %s not found!!' % customscript,
+ str(context.exception))
+
def test_get_data_non_vmware_seed_platform_info(self):
"""Platform info properly reports when on non-vmware platforms."""
paths = Paths({'cloud_dir': self.tdir, 'run_dir': self.tdir})
diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py
index c823889c..9c7d25fa 100644
--- a/tests/unittests/test_vmware_config_file.py
+++ b/tests/unittests/test_vmware_config_file.py
@@ -356,6 +356,20 @@ class TestVmwareConfigFile(CiTestCase):
conf = Config(cf)
self.assertTrue(conf.post_gc_status)
+ def test_no_default_run_post_script(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+ conf = Config(cf)
+ self.assertFalse(conf.default_run_post_script)
+ cf._insertKey("MISC|DEFAULT-RUN-POST-CUST-SCRIPT", "NO")
+ conf = Config(cf)
+ self.assertFalse(conf.default_run_post_script)
+
+ def test_yes_default_run_post_script(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+ cf._insertKey("MISC|DEFAULT-RUN-POST-CUST-SCRIPT", "yes")
+ conf = Config(cf)
+ self.assertTrue(conf.default_run_post_script)
+
class TestVmwareNetConfig(CiTestCase):
"""Test conversion of vmware config to cloud-init config."""