summaryrefslogtreecommitdiff
path: root/cloudinit/sources
diff options
context:
space:
mode:
authorPengpengSun <40026211+PengpengSun@users.noreply.github.com>2022-11-17 05:37:20 +0800
committerGitHub <noreply@github.com>2022-11-16 15:37:20 -0600
commitf5431e50a3b29db0ee044e7fa8a6a279d6cb14f7 (patch)
treec709919207c8ca51d45d24682548e863c24ecde8 /cloudinit/sources
parent42f701751bc6bd3a3ccbd3930cecdad97c490038 (diff)
downloadcloud-init-git-f5431e50a3b29db0ee044e7fa8a6a279d6cb14f7.tar.gz
VMware: Move Guest Customization transport from OVF to VMware (#1573)
This change is moving VMware Guest Customization data transport to VMware datasource, the goal is to have a single datasource for VMware which is named DatasourceVMware. Besides Guest Customization data transport(by local file), VMware datasource already has other data transports, like Guestinfo Keys and environment arguments. The detailed changes are: 1. Remove Guest Customization data transport from OVF datasource. 2. Refactor Guest Customization data transport code and add them to VMware datasource, For backward compatibility, Guest Customization data transport is put ahead of the other existing data transport since OVF is ahead of VMware in datasource searching list. 3. Add instance-id support in customization configuration file, so that instance-id can be given from vSphere side, if instance-id is not given, this datasource will read it from the file /sys/class/dmi/id/product_uuid no matter what transport is. 4. Move Guest Customization detection from dscheck_OVF() to dscheck_VMmare() in ds_identify script, Guest Customization detection is ahead of the other existing data transports since OVF is ahead of VMware in datasource searching list. 5. Modify unittests according to above changes. 6. Modify both OVF and VMware datasource documents according to above changes. 7. Guestinfo OVF data transport is left to OVF datasource since it uses OVF which is same with iso9660 transport.
Diffstat (limited to 'cloudinit/sources')
-rw-r--r--cloudinit/sources/DataSourceOVF.py554
-rw-r--r--cloudinit/sources/DataSourceVMware.py196
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config.py6
-rw-r--r--cloudinit/sources/helpers/vmware/imc/guestcust_util.py470
4 files changed, 629 insertions, 597 deletions
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index 05bf84c2..7baef3a5 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -11,49 +11,13 @@
import base64
import os
import re
-import time
from xml.dom import minidom
-from cloudinit import dmi
from cloudinit import log as logging
from cloudinit import safeyaml, sources, subp, util
-from cloudinit.sources.helpers.vmware.imc.config import Config
-from cloudinit.sources.helpers.vmware.imc.config_custom_script import (
- PostCustomScript,
- PreCustomScript,
-)
-from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
-from cloudinit.sources.helpers.vmware.imc.config_nic import NicConfigurator
-from cloudinit.sources.helpers.vmware.imc.config_passwd import (
- PasswordConfigurator,
-)
-from cloudinit.sources.helpers.vmware.imc.guestcust_error import (
- GuestCustErrorEnum,
-)
-from cloudinit.sources.helpers.vmware.imc.guestcust_event import (
- GuestCustEventEnum as GuestCustEvent,
-)
-from cloudinit.sources.helpers.vmware.imc.guestcust_state import (
- GuestCustStateEnum,
-)
-from cloudinit.sources.helpers.vmware.imc.guestcust_util import (
- enable_nics,
- get_nics_to_enable,
- get_tools_config,
- set_customization_status,
- set_gc_status,
-)
LOG = logging.getLogger(__name__)
-CONFGROUPNAME_GUESTCUSTOMIZATION = "deployPkg"
-GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS = "enable-custom-scripts"
-VMWARE_IMC_DIR = "/var/run/vmware-imc"
-
-
-class GuestCustScriptDisabled(Exception):
- pass
-
class DataSourceOVF(sources.DataSource):
@@ -66,11 +30,7 @@ class DataSourceOVF(sources.DataSource):
self.environment = None
self.cfg = {}
self.supported_seed_starts = ("/", "file://")
- self.vmware_customization_supported = True
self._network_config = None
- self._vmware_nics_to_enable = None
- self._vmware_cust_conf = None
- self._vmware_cust_found = False
def __str__(self):
root = sources.DataSource.__str__(self)
@@ -81,8 +41,6 @@ class DataSourceOVF(sources.DataSource):
md = {}
ud = ""
vd = ""
- vmwareImcConfigFilePath = None
- nicspath = None
defaults = {
"instance-id": "iid-dsovf",
@@ -90,305 +48,12 @@ class DataSourceOVF(sources.DataSource):
(seedfile, contents) = get_ovf_env(self.paths.seed_dir)
- system_type = dmi.read_dmi_data("system-product-name")
- if system_type is None:
- LOG.debug("No system-product-name found")
-
if seedfile:
# Found a seed dir
seed = os.path.join(self.paths.seed_dir, seedfile)
(md, ud, cfg) = read_ovf_environment(contents)
self.environment = contents
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",
- "/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
- for path in search_paths:
- deployPkgPluginPath = search_file(path, plugin)
- if deployPkgPluginPath:
- LOG.debug(
- "Found the customization plugin at %s",
- deployPkgPluginPath,
- )
- break
-
- if deployPkgPluginPath:
- # 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 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,
- msg="waiting for configuration file",
- func=wait_for_imc_cfg_file,
- args=("cust.cfg", max_wait),
- )
- 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,
- )
- 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,
- )
- # 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 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:
- 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:
- 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)
- product_marker = self._vmware_cust_conf.marker_id
- hasmarkerfile = check_marker_exists(
- 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
-
- # 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(
- GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
- GuestCustErrorEnum.GUESTCUST_ERROR_SCRIPT_DISABLED,
- )
- raise GuestCustScriptDisabled(msg)
-
- ccScriptsDir = os.path.join(
- self.paths.get_cpath("scripts"), "per-instance"
- )
- except GuestCustScriptDisabled as e:
- LOG.debug("GuestCustScriptDisabled")
- _raise_error_status(
- "Error parsing the customization Config File",
- e,
- GuestCustErrorEnum.GUESTCUST_ERROR_SCRIPT_DISABLED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
- except Exception as e:
- _raise_error_status(
- "Error parsing the customization Config File",
- e,
- GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- if special_customization:
- if customscript:
- try:
- precust = PreCustomScript(customscript, imcdirpath)
- precust.execute()
- except Exception as e:
- _raise_error_status(
- "Error executing pre-customization script",
- e,
- GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- try:
- LOG.debug("Preparing the Network configuration")
- self._network_config = get_network_config_from_conf(
- self._vmware_cust_conf, True, True, self.distro.osfamily
- )
- except Exception as e:
- _raise_error_status(
- "Error preparing Network Configuration",
- e,
- GuestCustEvent.GUESTCUST_EVENT_NETWORK_SETUP_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- if special_customization:
- LOG.debug("Applying password customization")
- pwdConfigurator = PasswordConfigurator()
- adminpwd = self._vmware_cust_conf.admin_password
- try:
- resetpwd = self._vmware_cust_conf.reset_password
- if adminpwd or resetpwd:
- pwdConfigurator.configure(
- adminpwd, resetpwd, self.distro
- )
- else:
- LOG.debug("Changing password is not needed")
- except Exception as e:
- _raise_error_status(
- "Error applying Password Configuration",
- e,
- GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- if customscript:
- try:
- postcust = PostCustomScript(
- customscript, imcdirpath, ccScriptsDir
- )
- postcust.execute()
- except Exception as e:
- _raise_error_status(
- "Error executing post-customization script",
- e,
- GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- if product_marker:
- try:
- setup_marker_files(
- product_marker,
- os.path.join(self.paths.cloud_dir, "data"),
- )
- except Exception as e:
- _raise_error_status(
- "Error creating marker files",
- e,
- GuestCustEvent.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
- vmwareImcConfigFilePath,
- self._vmware_cust_conf,
- )
-
- self._vmware_cust_found = True
- found.append("vmware-tools")
-
- # TODO: Need to set the status to DONE only when the
- # customization is done successfully.
- util.del_dir(os.path.dirname(vmwareImcConfigFilePath))
- enable_nics(self._vmware_nics_to_enable)
- set_customization_status(
- GuestCustStateEnum.GUESTCUST_STATE_DONE,
- GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS,
- )
- set_gc_status(self._vmware_cust_conf, "Successful")
-
else:
np = [
("com.vmware.guestInfo", transport_vmware_guestinfo),
@@ -438,9 +103,6 @@ class DataSourceOVF(sources.DataSource):
return True
def _get_subplatform(self):
- system_type = dmi.read_dmi_data("system-product-name").lower()
- if system_type == "vmware":
- return "vmware (%s)" % self.seed
return "ovf (%s)" % self.seed
def get_public_ssh_keys(self):
@@ -468,94 +130,6 @@ class DataSourceOVFNet(DataSourceOVF):
DataSourceOVF.__init__(self, sys_cfg, distro, paths)
self.seed_dir = os.path.join(paths.seed_dir, "ovf-net")
self.supported_seed_starts = ("http://", "https://")
- self.vmware_customization_supported = False
-
-
-def get_max_wait_from_cfg(cfg):
- default_max_wait = 15
- max_wait_cfg_option = "vmware_cust_file_max_wait"
- max_wait = default_max_wait
-
- if not cfg:
- return max_wait
-
- try:
- max_wait = int(cfg.get(max_wait_cfg_option, default_max_wait))
- except ValueError:
- LOG.warning(
- "Failed to get '%s', using %s",
- max_wait_cfg_option,
- default_max_wait,
- )
-
- 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
-
- return max_wait
-
-
-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)
- if os.path.isfile(fileFullPath):
- return fileFullPath
- LOG.debug("Waiting for VMware Customization Config File")
- time.sleep(naplen)
- waited += naplen
- return None
-
-
-def get_network_config_from_conf(
- config, use_system_devices=True, configure=False, osfamily=None
-):
- nicConfigurator = NicConfigurator(config.nics, use_system_devices)
- nics_cfg_list = nicConfigurator.generate(configure, osfamily)
-
- return get_network_config(
- nics_cfg_list, config.name_servers, config.dns_suffixes
- )
-
-
-def get_network_config(nics=None, nameservers=None, search=None):
- config_list = nics
-
- if nameservers or search:
- config_list.append(
- {"type": "nameserver", "address": nameservers, "search": search}
- )
-
- return {"version": 1, "config": config_list}
-
-
-# This will return a dict with some content
-# meta-data, user-data, some config
-def read_vmware_imc(config):
- md = {}
- cfg = {}
- ud = None
- if config.host_name:
- if config.domain_name:
- md["local-hostname"] = config.host_name + "." + config.domain_name
- else:
- md["local-hostname"] = config.host_name
-
- if config.timezone:
- cfg["timezone"] = config.timezone
-
- md["instance-id"] = "iid-vmware-imc"
- return (md, ud, cfg)
# This will return a dict with some content
@@ -745,17 +319,6 @@ def get_properties(contents):
return props
-def search_file(dirpath, filename):
- if not dirpath or not filename:
- return None
-
- for root, _dirs, files in os.walk(dirpath):
- if filename in files:
- return os.path.join(root, filename)
-
- return None
-
-
class XmlError(Exception):
pass
@@ -772,80 +335,6 @@ def get_datasource_list(depends):
return sources.list_from_depends(depends, datasources)
-# To check if marker file exists
-def check_marker_exists(markerid, marker_dir):
- """
- Check the existence of a marker file.
- Presence of marker file determines whether a certain code path is to be
- executed. It is needed for partial guest customization in VMware.
- @param markerid: is an unique string representing a particular product
- marker.
- @param: marker_dir: The directory in which markers exist.
- """
- if not markerid:
- return False
- markerfile = os.path.join(marker_dir, ".markerfile-" + markerid + ".txt")
- if os.path.exists(markerfile):
- return True
- return False
-
-
-# Create a marker file
-def setup_marker_files(markerid, marker_dir):
- """
- Create a new marker file.
- Marker files are unique to a full customization workflow in VMware
- environment.
- @param markerid: is an unique string representing a particular product
- marker.
- @param: marker_dir: The directory in which markers exist.
-
- """
- LOG.debug("Handle marker creation")
- markerfile = os.path.join(marker_dir, ".markerfile-" + markerid + ".txt")
- for fname in os.listdir(marker_dir):
- if fname.startswith(".markerfile"):
- util.del_file(os.path.join(marker_dir, fname))
- open(markerfile, "w").close()
-
-
-def _raise_error_status(prefix, error, event, config_file, conf):
- """
- Raise error and send customization status to the underlying VMware
- Virtualization Platform. Also, cleanup the imc directory.
- """
- LOG.debug("%s: %s", prefix, error)
- set_customization_status(GuestCustStateEnum.GUESTCUST_STATE_RUNNING, event)
- set_gc_status(conf, prefix)
- 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
@@ -857,47 +346,4 @@ def safeload_yaml_or_dict(data):
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
diff --git a/cloudinit/sources/DataSourceVMware.py b/cloudinit/sources/DataSourceVMware.py
index 308e02e8..07a80222 100644
--- a/cloudinit/sources/DataSourceVMware.py
+++ b/cloudinit/sources/DataSourceVMware.py
@@ -1,9 +1,10 @@
# Cloud-Init DataSource for VMware
#
-# Copyright (c) 2018-2021 VMware, Inc. All Rights Reserved.
+# Copyright (c) 2018-2022 VMware, Inc. All Rights Reserved.
#
# Authors: Anish Swaminathan <anishs@vmware.com>
# Andrew Kutz <akutz@vmware.com>
+# Pengpeng Sun <pengpengs@vmware.com>
#
# This file is part of cloud-init. See LICENSE file for license information.
@@ -14,6 +15,7 @@ multiple transports types, including:
* EnvVars
* GuestInfo
+ * IMC (Guest Customization)
Netifaces (https://github.com/al45tair/netifaces)
@@ -74,6 +76,7 @@ import netifaces
from cloudinit import dmi
from cloudinit import log as logging
from cloudinit import net, sources, util
+from cloudinit.sources.helpers.vmware.imc import guestcust_util
from cloudinit.subp import ProcessExecutionError, subp, which
PRODUCT_UUID_FILE_PATH = "/sys/class/dmi/id/product_uuid"
@@ -81,8 +84,10 @@ PRODUCT_UUID_FILE_PATH = "/sys/class/dmi/id/product_uuid"
LOG = logging.getLogger(__name__)
NOVAL = "No value found"
+# Data transports names
DATA_ACCESS_METHOD_ENVVAR = "envvar"
DATA_ACCESS_METHOD_GUESTINFO = "guestinfo"
+DATA_ACCESS_METHOD_IMC = "imc"
VMWARE_RPCTOOL = which("vmware-rpctool")
REDACT = "redact"
@@ -116,14 +121,22 @@ class DataSourceVMware(sources.DataSource):
Network Config Version 2 - http://bit.ly/cloudinit-net-conf-v2
For example, CentOS 7's official cloud-init package is version
- 0.7.9 and does not support Network Config Version 2. However,
- this datasource still supports supplying Network Config Version 2
- data as long as the Linux distro's cloud-init package is new
- enough to parse the data.
+ 0.7.9 and does not support Network Config Version 2.
- The metadata key "network.encoding" may be used to indicate the
- format of the metadata key "network". Valid encodings are base64
- and gzip+base64.
+ imc transport:
+ Either Network Config Version 1 or Network Config Version 2 is
+ supported which depends on the customization type.
+ For LinuxPrep customization, Network config Version 1 data is
+ parsed from the customization specification.
+ For CloudinitPrep customization, Network config Version 2 data
+ is parsed from the customization specification.
+
+ envvar and guestinfo tranports:
+ Network Config Version 2 data is supported as long as the Linux
+ distro's cloud-init package is new enough to parse the data.
+ The metadata key "network.encoding" may be used to indicate the
+ format of the metadata key "network". Valid encodings are base64
+ and gzip+base64.
"""
dsname = "VMware"
@@ -131,9 +144,27 @@ class DataSourceVMware(sources.DataSource):
def __init__(self, sys_cfg, distro, paths, ud_proc=None):
sources.DataSource.__init__(self, sys_cfg, distro, paths, ud_proc)
+ self.cfg = {}
self.data_access_method = None
self.vmware_rpctool = VMWARE_RPCTOOL
+ # A list includes all possible data transports, each tuple represents
+ # one data transport type. This datasource will try to get data from
+ # each of transports follows the tuples order in this list.
+ # A tuple has 3 elements which are:
+ # 1. The transport name
+ # 2. The function name to get data for the transport
+ # 3. A boolean tells whether the transport requires VMware platform
+ self.possible_data_access_method_list = [
+ (DATA_ACCESS_METHOD_ENVVAR, self.get_envvar_data_fn, False),
+ (DATA_ACCESS_METHOD_GUESTINFO, self.get_guestinfo_data_fn, True),
+ (DATA_ACCESS_METHOD_IMC, self.get_imc_data_fn, True),
+ ]
+
+ def __str__(self):
+ root = sources.DataSource.__str__(self)
+ return "%s [seed=%s]" % (root, self.data_access_method)
+
def _get_data(self):
"""
_get_data loads the metadata, userdata, and vendordata from one of
@@ -141,6 +172,7 @@ class DataSourceVMware(sources.DataSource):
* envvars
* guestinfo
+ * imc
Please note when updating this function with support for new data
transports, the order should match the order in the dscheck_VMware
@@ -152,35 +184,18 @@ class DataSourceVMware(sources.DataSource):
# access method.
md, ud, vd = None, None, None
- # First check to see if there is data via env vars.
- if os.environ.get(VMX_GUESTINFO, ""):
- md = guestinfo_envvar("metadata")
- ud = guestinfo_envvar("userdata")
- vd = guestinfo_envvar("vendordata")
-
+ # Crawl data from all possible data transports
+ for (
+ data_access_method,
+ get_data_fn,
+ require_vmware_platform,
+ ) in self.possible_data_access_method_list:
+ if require_vmware_platform and not is_vmware_platform():
+ continue
+ (md, ud, vd) = get_data_fn()
if md or ud or vd:
- self.data_access_method = DATA_ACCESS_METHOD_ENVVAR
-
- # At this point, all additional data transports are valid only on
- # a VMware platform.
- if not self.data_access_method:
- system_type = dmi.read_dmi_data("system-product-name")
- if system_type is None:
- LOG.debug("No system-product-name found")
- return False
- if "vmware" not in system_type.lower():
- LOG.debug("Not a VMware platform")
- return False
-
- # If no data was detected, check the guestinfo transport next.
- if not self.data_access_method:
- if self.vmware_rpctool:
- md = guestinfo("metadata", self.vmware_rpctool)
- ud = guestinfo("userdata", self.vmware_rpctool)
- vd = guestinfo("vendordata", self.vmware_rpctool)
-
- if md or ud or vd:
- self.data_access_method = DATA_ACCESS_METHOD_GUESTINFO
+ self.data_access_method = data_access_method
+ break
if not self.data_access_method:
LOG.error("failed to find a valid data access method")
@@ -241,6 +256,8 @@ class DataSourceVMware(sources.DataSource):
get_key_name_fn = get_guestinfo_envvar_key_name
elif self.data_access_method == DATA_ACCESS_METHOD_GUESTINFO:
get_key_name_fn = get_guestinfo_key_name
+ elif self.data_access_method == DATA_ACCESS_METHOD_IMC:
+ get_key_name_fn = get_imc_key_name
else:
return sources.METADATA_UNKNOWN
@@ -249,6 +266,12 @@ class DataSourceVMware(sources.DataSource):
get_key_name_fn("metadata"),
)
+ # The data sources' config_obj is a cloud-config formatted
+ # object that came to it from ways other than cloud-config
+ # because cloud-config content would be handled elsewhere
+ def get_config_obj(self):
+ return self.cfg
+
@property
def network_config(self):
if "network" in self.metadata:
@@ -292,6 +315,98 @@ class DataSourceVMware(sources.DataSource):
if self.data_access_method == DATA_ACCESS_METHOD_GUESTINFO:
guestinfo_redact_keys(keys_to_redact, self.vmware_rpctool)
+ def get_envvar_data_fn(self):
+ """
+ check to see if there is data via env vars
+ """
+ md, ud, vd = None, None, None
+ if os.environ.get(VMX_GUESTINFO, ""):
+ md = guestinfo_envvar("metadata")
+ ud = guestinfo_envvar("userdata")
+ vd = guestinfo_envvar("vendordata")
+
+ return (md, ud, vd)
+
+ def get_guestinfo_data_fn(self):
+ """
+ check to see if there is data via the guestinfo transport
+ """
+ md, ud, vd = None, None, None
+ if self.vmware_rpctool:
+ md = guestinfo("metadata", self.vmware_rpctool)
+ ud = guestinfo("userdata", self.vmware_rpctool)
+ vd = guestinfo("vendordata", self.vmware_rpctool)
+
+ return (md, ud, vd)
+
+ def get_imc_data_fn(self):
+ """
+ check to see if there is data via vmware guest customization
+ """
+ md, ud, vd = None, None, None
+
+ # Check if vmware guest customization is enabled.
+ allow_vmware_cust = guestcust_util.is_vmware_cust_enabled(self.sys_cfg)
+ allow_raw_data_cust = guestcust_util.is_raw_data_cust_enabled(
+ self.ds_cfg
+ )
+ if not allow_vmware_cust and not allow_raw_data_cust:
+ LOG.debug("Customization for VMware platform is disabled")
+ return (md, ud, vd)
+
+ # Check if "VMware Tools" plugin is available.
+ if not guestcust_util.is_cust_plugin_available():
+ return (md, ud, vd)
+
+ # Wait for vmware guest customization configuration file.
+ cust_cfg_file = guestcust_util.get_cust_cfg_file(self.ds_cfg)
+ if cust_cfg_file is None:
+ return (md, ud, vd)
+
+ # Check what type of guest customization is this.
+ cust_cfg_dir = os.path.dirname(cust_cfg_file)
+ cust_cfg = guestcust_util.parse_cust_cfg(cust_cfg_file)
+ (
+ is_vmware_cust_cfg,
+ is_raw_data_cust_cfg,
+ ) = guestcust_util.get_cust_cfg_type(cust_cfg)
+
+ # Get data only if guest customization type and flag matches.
+ if is_vmware_cust_cfg and allow_vmware_cust:
+ LOG.debug("Getting data via VMware customization configuration")
+ (md, ud, vd, self.cfg) = guestcust_util.get_data_from_imc_cust_cfg(
+ self.paths.cloud_dir,
+ self.paths.get_cpath("scripts"),
+ cust_cfg,
+ cust_cfg_dir,
+ self.distro,
+ )
+ elif is_raw_data_cust_cfg and allow_raw_data_cust:
+ LOG.debug(
+ "Getting data via VMware raw cloudinit data "
+ "customization configuration"
+ )
+ (md, ud, vd) = guestcust_util.get_data_from_imc_raw_data_cust_cfg(
+ cust_cfg
+ )
+ else:
+ LOG.debug("No allowed customization configuration data found")
+
+ # Clean customization configuration file and directory
+ util.del_dir(cust_cfg_dir)
+ return (md, ud, vd)
+
+
+def is_vmware_platform():
+ system_type = dmi.read_dmi_data("system-product-name")
+ if system_type is None:
+ LOG.debug("No system-product-name found")
+ return False
+ elif "vmware" not in system_type.lower():
+ LOG.debug("Not a VMware platform")
+ return False
+ return True
+
def decode(key, enc_type, data):
"""
@@ -367,6 +482,10 @@ def handle_returned_guestinfo_val(key, val):
return None
+def get_imc_key_name(key):
+ return "vmware-tools"
+
+
def get_guestinfo_key_name(key):
return "guestinfo." + key
@@ -512,6 +631,9 @@ def load_json_or_yaml(data):
"""
if not data:
return {}
+ # If data is already a dictionary, here will return it directly.
+ if isinstance(data, dict):
+ return data
try:
return util.load_json(data)
except (json.JSONDecodeError, TypeError):
@@ -523,6 +645,8 @@ def process_metadata(data):
process_metadata processes metadata and loads the optional network
configuration.
"""
+ if not data:
+ return {}
network = None
if "network" in data:
network = data["network"]
diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py
index 8b2deb65..df9e5c4b 100644
--- a/cloudinit/sources/helpers/vmware/imc/config.py
+++ b/cloudinit/sources/helpers/vmware/imc/config.py
@@ -29,6 +29,7 @@ class Config:
DEFAULT_RUN_POST_SCRIPT = "MISC|DEFAULT-RUN-POST-CUST-SCRIPT"
CLOUDINIT_META_DATA = "CLOUDINIT|METADATA"
CLOUDINIT_USER_DATA = "CLOUDINIT|USERDATA"
+ CLOUDINIT_INSTANCE_ID = "CLOUDINIT|INSTANCE-ID"
def __init__(self, configFile):
self._configFile = configFile
@@ -142,5 +143,10 @@ class Config:
"""Return the name of cloud-init user data."""
return self._configFile.get(Config.CLOUDINIT_USER_DATA, None)
+ @property
+ def instance_id(self):
+ """Return instance id"""
+ return self._configFile.get(Config.CLOUDINIT_INSTANCE_ID, None)
+
# vi: ts=4 expandtab
diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
index 5b5f02ca..6ffbae40 100644
--- a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
@@ -1,7 +1,8 @@
# Copyright (C) 2016 Canonical Ltd.
-# Copyright (C) 2016 VMware Inc.
+# Copyright (C) 2016-2022 VMware Inc.
#
# Author: Sankar Tanguturi <stanguturi@vmware.com>
+# Pengpeng Sun <pegnpengs@vmware.com>
#
# This file is part of cloud-init. See LICENSE file for license information.
@@ -10,13 +11,20 @@ import os
import re
import time
-from cloudinit import subp
-from cloudinit.sources.helpers.vmware.imc.guestcust_event import (
- GuestCustEventEnum,
-)
-from cloudinit.sources.helpers.vmware.imc.guestcust_state import (
- GuestCustStateEnum,
+from cloudinit import subp, util
+
+from .config import Config
+from .config_custom_script import (
+ CustomScriptNotFound,
+ PostCustomScript,
+ PreCustomScript,
)
+from .config_file import ConfigFile
+from .config_nic import NicConfigurator
+from .config_passwd import PasswordConfigurator
+from .guestcust_error import GuestCustErrorEnum
+from .guestcust_event import GuestCustEventEnum
+from .guestcust_state import GuestCustStateEnum
logger = logging.getLogger(__name__)
@@ -24,6 +32,11 @@ logger = logging.getLogger(__name__)
CLOUDINIT_LOG_FILE = "/var/log/cloud-init.log"
QUERY_NICS_SUPPORTED = "queryNicsSupported"
NICS_STATUS_CONNECTED = "connected"
+# Path to the VMware IMC directory
+IMC_DIR_PATH = "/var/run/vmware-imc"
+# Customization script configuration in tools conf
+IMC_TOOLS_CONF_GROUPNAME = "deployPkg"
+IMC_TOOLS_CONF_ENABLE_CUST_SCRIPTS = "enable-custom-scripts"
# This will send a RPC command to the underlying
@@ -183,4 +196,447 @@ def set_gc_status(config, gcMsg):
return None
+def get_imc_dir_path():
+ return IMC_DIR_PATH
+
+
+def get_data_from_imc_cust_cfg(
+ cloud_dir,
+ scripts_cpath,
+ cust_cfg,
+ cust_cfg_dir,
+ distro,
+):
+ md, ud, vd, cfg = {}, None, None, {}
+ set_gc_status(cust_cfg, "Started")
+ (md, cfg) = get_non_network_data_from_vmware_cust_cfg(cust_cfg)
+ is_special_customization = check_markers(cloud_dir, cust_cfg)
+ if is_special_customization:
+ if not do_special_customization(
+ scripts_cpath, cust_cfg, cust_cfg_dir, distro
+ ):
+ return (None, None, None, None)
+ if not recheck_markers(cloud_dir, cust_cfg):
+ return (None, None, None, None)
+ try:
+ logger.debug("Preparing the Network configuration")
+ md["network"] = get_network_data_from_vmware_cust_cfg(
+ cust_cfg, True, True, distro.osfamily
+ )
+ except Exception as e:
+ set_cust_error_status(
+ "Error preparing Network Configuration",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_NETWORK_SETUP_FAILED,
+ cust_cfg,
+ )
+ return (None, None, None, None)
+ connect_nics(cust_cfg_dir)
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_DONE,
+ GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS,
+ )
+ set_gc_status(cust_cfg, "Successful")
+ return (md, ud, vd, cfg)
+
+
+def get_data_from_imc_raw_data_cust_cfg(cust_cfg):
+ set_gc_status(cust_cfg, "Started")
+ md, ud, vd = None, None, None
+ md_file = cust_cfg.meta_data_name
+ if md_file:
+ md_path = os.path.join(get_imc_dir_path(), md_file)
+ if not os.path.exists(md_path):
+ set_cust_error_status(
+ "Error locating the cloud-init meta data file",
+ "Meta data file is not found: %s" % md_path,
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return (None, None, None)
+ try:
+ md = util.load_file(md_path)
+ except Exception as e:
+ set_cust_error_status(
+ "Error loading cloud-init meta data file",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return (None, None, None)
+
+ ud_file = cust_cfg.user_data_name
+ if ud_file:
+ ud_path = os.path.join(get_imc_dir_path(), ud_file)
+ if not os.path.exists(ud_path):
+ set_cust_error_status(
+ "Error locating the cloud-init userdata file",
+ "Userdata file is not found: %s" % ud_path,
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return (None, None, None)
+ try:
+ ud = util.load_file(ud_path).replace("\r", "")
+ except Exception as e:
+ set_cust_error_status(
+ "Error loading cloud-init userdata file",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return (None, None, None)
+
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_DONE,
+ GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS,
+ )
+ set_gc_status(cust_cfg, "Successful")
+ return (md, ud, vd)
+
+
+def get_non_network_data_from_vmware_cust_cfg(cust_cfg):
+ md, cfg = {}, {}
+ if cust_cfg.host_name:
+ if cust_cfg.domain_name:
+ md["local-hostname"] = (
+ cust_cfg.host_name + "." + cust_cfg.domain_name
+ )
+ else:
+ md["local-hostname"] = cust_cfg.host_name
+ if cust_cfg.timezone:
+ cfg["timezone"] = cust_cfg.timezone
+ if cust_cfg.instance_id:
+ md["instance-id"] = cust_cfg.instance_id
+ return (md, cfg)
+
+
+def get_network_data_from_vmware_cust_cfg(
+ cust_cfg, use_system_devices=True, configure=False, osfamily=None
+):
+ nicConfigurator = NicConfigurator(cust_cfg.nics, use_system_devices)
+ nics_cfg_list = nicConfigurator.generate(configure, osfamily)
+
+ return get_v1_network_config(
+ nics_cfg_list, cust_cfg.name_servers, cust_cfg.dns_suffixes
+ )
+
+
+def get_v1_network_config(nics_cfg_list=None, nameservers=None, search=None):
+ config_list = nics_cfg_list
+
+ if nameservers or search:
+ config_list.append(
+ {"type": "nameserver", "address": nameservers, "search": search}
+ )
+
+ return {"version": 1, "config": config_list}
+
+
+def connect_nics(cust_cfg_dir):
+ nics_file = os.path.join(cust_cfg_dir, "nics.txt")
+ if os.path.exists(nics_file):
+ logger.debug("%s file found, to connect nics", nics_file)
+ enable_nics(get_nics_to_enable(nics_file))
+
+
+def is_vmware_cust_enabled(sys_cfg):
+ return not util.get_cfg_option_bool(
+ sys_cfg, "disable_vmware_customization", True
+ )
+
+
+def is_raw_data_cust_enabled(ds_cfg):
+ return util.get_cfg_option_bool(ds_cfg, "allow_raw_data", True)
+
+
+def get_cust_cfg_file(ds_cfg):
+ # 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 which indicates
+ # that required metadata and userdata files are now
+ # present.
+ max_wait = get_max_wait_from_cfg(ds_cfg)
+ cust_cfg_file_path = util.log_time(
+ logfunc=logger.debug,
+ msg="Waiting for VMware customization configuration file",
+ func=wait_for_cust_cfg_file,
+ args=("cust.cfg", max_wait),
+ )
+ if cust_cfg_file_path:
+ logger.debug(
+ "Found VMware customization configuration file at %s",
+ cust_cfg_file_path,
+ )
+ return cust_cfg_file_path
+ else:
+ logger.debug("No VMware customization configuration file found")
+ return None
+
+
+def wait_for_cust_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)
+ if os.path.isfile(fileFullPath):
+ return fileFullPath
+ logger.debug("Waiting for VMware customization configuration file")
+ time.sleep(naplen)
+ waited += naplen
+ return None
+
+
+def get_max_wait_from_cfg(ds_cfg):
+ default_max_wait = 15
+ max_wait_cfg_option = "vmware_cust_file_max_wait"
+ max_wait = default_max_wait
+ if not ds_cfg:
+ return max_wait
+ try:
+ max_wait = int(ds_cfg.get(max_wait_cfg_option, default_max_wait))
+ except ValueError:
+ logger.warning(
+ "Failed to get '%s', using %s",
+ max_wait_cfg_option,
+ default_max_wait,
+ )
+ if max_wait < 0:
+ logger.warning(
+ "Invalid value '%s' for '%s', using '%s' instead",
+ max_wait,
+ max_wait_cfg_option,
+ default_max_wait,
+ )
+ max_wait = default_max_wait
+ return max_wait
+
+
+def check_markers(cloud_dir, cust_cfg):
+ product_marker = cust_cfg.marker_id
+ has_marker_file = check_marker_exists(
+ product_marker, os.path.join(cloud_dir, "data")
+ )
+ return product_marker and not has_marker_file
+
+
+def check_marker_exists(markerid, marker_dir):
+ """
+ Check the existence of a marker file.
+ Presence of marker file determines whether a certain code path is to be
+ executed. It is needed for partial guest customization in VMware.
+ @param markerid: is an unique string representing a particular product
+ marker.
+ @param: marker_dir: The directory in which markers exist.
+ """
+ if not markerid:
+ return False
+ markerfile = os.path.join(marker_dir, ".markerfile-" + markerid + ".txt")
+ if os.path.exists(markerfile):
+ return True
+ return False
+
+
+def recheck_markers(cloud_dir, cust_cfg):
+ product_marker = cust_cfg.marker_id
+ if product_marker:
+ if not create_marker_file(cloud_dir, cust_cfg):
+ return False
+ return True
+
+
+def create_marker_file(cloud_dir, cust_cfg):
+ try:
+ setup_marker_files(cust_cfg.marker_id, os.path.join(cloud_dir, "data"))
+ except Exception as e:
+ set_cust_error_status(
+ "Error creating marker files",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return False
+ return True
+
+
+def setup_marker_files(marker_id, marker_dir):
+ """
+ Create a new marker file.
+ Marker files are unique to a full customization workflow in VMware
+ environment.
+ @param marker_id: is an unique string representing a particular product
+ marker.
+ @param: marker_dir: The directory in which markers exist.
+ """
+ logger.debug("Handle marker creation")
+ marker_file = os.path.join(marker_dir, ".markerfile-" + marker_id + ".txt")
+ for fname in os.listdir(marker_dir):
+ if fname.startswith(".markerfile"):
+ util.del_file(os.path.join(marker_dir, fname))
+ open(marker_file, "w").close()
+
+
+def do_special_customization(scripts_cpath, cust_cfg, cust_cfg_dir, distro):
+ is_pre_custom_successful = False
+ is_password_custom_successful = False
+ is_post_custom_successful = False
+ is_custom_script_enabled = False
+ custom_script = cust_cfg.custom_script_name
+ if custom_script:
+ is_custom_script_enabled = check_custom_script_enablement(cust_cfg)
+ if is_custom_script_enabled:
+ is_pre_custom_successful = do_pre_custom_script(
+ cust_cfg, custom_script, cust_cfg_dir
+ )
+ is_password_custom_successful = do_password_customization(cust_cfg, distro)
+ if custom_script and is_custom_script_enabled:
+ ccScriptsDir = os.path.join(scripts_cpath, "per-instance")
+ is_post_custom_successful = do_post_custom_script(
+ cust_cfg, custom_script, cust_cfg_dir, ccScriptsDir
+ )
+ if custom_script:
+ return (
+ is_pre_custom_successful
+ and is_password_custom_successful
+ and is_post_custom_successful
+ )
+ return is_password_custom_successful
+
+
+def do_pre_custom_script(cust_cfg, custom_script, cust_cfg_dir):
+ try:
+ precust = PreCustomScript(custom_script, cust_cfg_dir)
+ precust.execute()
+ except CustomScriptNotFound as e:
+ set_cust_error_status(
+ "Error executing pre-customization script",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return False
+ return True
+
+
+def do_post_custom_script(cust_cfg, custom_script, cust_cfg_dir, ccScriptsDir):
+ try:
+ postcust = PostCustomScript(custom_script, cust_cfg_dir, ccScriptsDir)
+ postcust.execute()
+ except CustomScriptNotFound as e:
+ set_cust_error_status(
+ "Error executing post-customization script",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return False
+ return True
+
+
+def check_custom_script_enablement(cust_cfg):
+ is_custom_script_enabled = False
+ default_value = "false"
+ if cust_cfg.default_run_post_script:
+ logger.debug(
+ "Set default value to true due to customization configuration."
+ )
+ default_value = "true"
+ custom_script_enablement = get_tools_config(
+ IMC_TOOLS_CONF_GROUPNAME,
+ IMC_TOOLS_CONF_ENABLE_CUST_SCRIPTS,
+ default_value,
+ )
+ if custom_script_enablement.lower() != "true":
+ set_cust_error_status(
+ "Custom script is disabled by VM Administrator",
+ "Error checking custom script enablement",
+ GuestCustErrorEnum.GUESTCUST_ERROR_SCRIPT_DISABLED,
+ cust_cfg,
+ )
+ else:
+ is_custom_script_enabled = True
+ return is_custom_script_enabled
+
+
+def do_password_customization(cust_cfg, distro):
+ logger.debug("Applying password customization")
+ pwdConfigurator = PasswordConfigurator()
+ admin_pwd = cust_cfg.admin_password
+ try:
+ reset_pwd = cust_cfg.reset_password
+ if admin_pwd or reset_pwd:
+ pwdConfigurator.configure(admin_pwd, reset_pwd, distro)
+ else:
+ logger.debug("Changing password is not needed")
+ except Exception as e:
+ set_cust_error_status(
+ "Error applying password configuration",
+ str(e),
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED,
+ cust_cfg,
+ )
+ return False
+ return True
+
+
+def parse_cust_cfg(cfg_file):
+ return Config(ConfigFile(cfg_file))
+
+
+def get_cust_cfg_type(cust_cfg):
+ is_vmware_cust_cfg, is_raw_data_cust_cfg = False, False
+ if cust_cfg.meta_data_name:
+ is_raw_data_cust_cfg = True
+ logger.debug("raw cloudinit data cust cfg found")
+ else:
+ is_vmware_cust_cfg = True
+ logger.debug("vmware cust cfg found")
+ return (is_vmware_cust_cfg, is_raw_data_cust_cfg)
+
+
+def is_cust_plugin_available():
+ search_paths = (
+ "/usr/lib/vmware-tools",
+ "/usr/lib64/vmware-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",
+ )
+ cust_plugin = "libdeployPkgPlugin.so"
+ for path in search_paths:
+ cust_plugin_path = search_file(path, cust_plugin)
+ if cust_plugin_path:
+ logger.debug(
+ "Found the customization plugin at %s", cust_plugin_path
+ )
+ return True
+ return False
+
+
+def search_file(dirpath, filename):
+ if not dirpath or not filename:
+ return None
+
+ for root, _dirs, files in os.walk(dirpath):
+ if filename in files:
+ return os.path.join(root, filename)
+
+ return None
+
+
+def set_cust_error_status(prefix, error, event, cust_cfg):
+ """
+ Set customization status to the underlying VMware Virtualization Platform
+ """
+ util.logexc(logger, "%s: %s", prefix, error)
+ set_customization_status(GuestCustStateEnum.GUESTCUST_STATE_RUNNING, event)
+ set_gc_status(cust_cfg, prefix)
+
+
# vi: ts=4 expandtab