summaryrefslogtreecommitdiff
path: root/cloudinit/sources
diff options
context:
space:
mode:
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