Source code for saltext.vmware.modules.esxi

# Copyright 2021 VMware, Inc.
# SPDX-License: Apache-2.0
import logging
import os

import salt.exceptions
import saltext.vmware.utils.common as utils_common
import saltext.vmware.utils.esxi as utils_esxi
import saltext.vmware.utils.vmware as utils_vmware
from salt.defaults import DEFAULT_TARGET_DELIM
from saltext.vmware.utils.connect import get_service_instance
from saltext.vmware.utils.connect import get_username_password

log = logging.getLogger(__name__)

try:
    from pyVmomi import vmodl, vim, VmomiSupport

    HAS_PYVMOMI = True
except ImportError:
    HAS_PYVMOMI = False


__virtualname__ = "vmware_esxi"

DEFAULT_EXCEPTIONS = (
    vim.fault.InvalidState,
    vim.fault.NotFound,
    vim.fault.HostConfigFault,
    vmodl.fault.InvalidArgument,
    salt.exceptions.VMwareApiError,
    vim.fault.AlreadyExists,
    vim.fault.UserNotFound,
    salt.exceptions.CommandExecutionError,
    vmodl.fault.SystemError,
    TypeError,
)


def __virtual__():
    if not HAS_PYVMOMI:
        return False, "Unable to import pyVmomi module."
    return __virtualname__


[docs]def get_lun_ids(service_instance=None): """ Return a list of LUN (Logical Unit Number) NAA (Network Addressing Authority) IDs. """ if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts(service_instance=service_instance, get_all_hosts=True) ids = set() for host in hosts: for datastore in host.datastore: for extent in datastore.info.vmfs.extent: ids.add(extent.diskName) return list(ids)
def _get_capability_attribs(host): ret = {} for attrib in dir(host.capability): if attrib.startswith("_") or attrib.lower() == "array": continue val = getattr(host.capability, attrib) # Convert all pyvmomi str[], bool[] and int[] to list. if isinstance(val, list): val = list(val) ret.update({utils_common.camel_to_snake_case(attrib): val}) return ret
[docs]def get_capabilities(service_instance=None): """ Return ESXi host's capability information. """ if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts(service_instance=service_instance, get_all_hosts=True) capabilities = {} for host in hosts: capabilities[host.name] = _get_capability_attribs(host) return capabilities
[docs]def power_state( datacenter_name=None, cluster_name=None, host_name=None, state=None, timeout=600, force=True ): """ Manage the power state of the ESXi host. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname whose power state needs to be managed (optional). state Sets the ESXi host to this power state. Valid values: "reboot", "standby", "poweron", "shutdown". timeout Timeout when transitioning power state to standby / poweron. Default: 600 seconds force Force power state transition. Default: True .. code-block:: bash salt '*' vmware_esxi.power_state datacenter_name=dc1 cluster_name=cl1 host_name=host1 state=shutdown """ ret = None task = None service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: if state == "reboot": task = h.RebootHost_Task(force) elif state == "standby": task = h.PowerDownHostToStandBy_Task(timeout, force) elif state == "poweron": task = h.PowerUpHostFromStandBy_Task(timeout) elif state == "shutdown": task = h.ShutdownHost_Task(force) if task: utils_common.wait_for_task(task, h.name, "PowerStateTask") ret = True except (vmodl.fault.NotSupported, salt.exceptions.VMwareApiError) as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def manage_service( service_name, datacenter_name=None, cluster_name=None, host_name=None, state=None, startup_policy=None, service_instance=None, ): """ Manage the state of the service running on the EXSI host. service_name Service that needs to be managed. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname whose power state needs to be managed (optional) state Sets the service running on the ESXi host to this state. Valid values: "start", "stop", "restart". startup_policy Sets the service startup policy. If unspecified, no changes are made. Valid values "on", "off", "automatic". - on: Start and stop with host - off: Start and stop manually - automatic: Start automatically if any ports are open, and stop when all ports are closed service_instance Use this vCenter service connection instance instead of creating a new one. (optional) .. code-block:: bash salt '*' vmware_esxi.manage_service sshd datacenter_name=dc1 cluster_name=cl1 host_name=host1 state=restart startup_policy=on """ log.debug("Running vmware_esxi.manage_service") ret = None task = None if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: host_service = h.configManager.serviceSystem if not host_service: continue if state: if state == "start": host_service.StartService(id=service_name) elif state == "stop": host_service.StopService(id=service_name) elif state == "restart": host_service.RestartService(id=service_name) else: raise salt.exceptions.SaltException("Unknown state - {}".format(state)) if startup_policy is not None: if startup_policy is True: startup_policy = "on" elif startup_policy is False: startup_policy = "off" host_service.UpdateServicePolicy(id=service_name, policy=startup_policy) ret = True except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def list_services( service_name=None, datacenter_name=None, cluster_name=None, host_name=None, state=None, startup_policy=None, service_instance=None, ): """ List the state of services running on matching EXSI hosts. service_name Filter by this service name. (optional) datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) state Filter by this service state. Valid values: "running", "stopped" startup_policy Filter by this service startup policy. Valid values "on", "off", "automatic". service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.list_services """ log.debug("Running vmware_esxi.list_services") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: host_service = h.configManager.serviceSystem ret[h.name] = {} if not host_service: continue if startup_policy is not None: # salt converts command line input "on" and "off" to True and False. Handle explicitly. if startup_policy is True: startup_policy = "on" elif startup_policy is False: startup_policy = "off" services = host_service.serviceInfo.service for service in services or []: if service_name and service.key != service_name: continue if startup_policy and service.policy != startup_policy: continue if state and state == "running" and not service.running: continue if state and state == "stopped" and service.running: continue ret[h.name][service.key] = { "state": "running" if service.running else "stopped", "startup_policy": service.policy, } except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def get_acceptance_level( datacenter_name=None, cluster_name=None, host_name=None, acceptance_level=None, service_instance=None, ): """ Get acceptance level on matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) acceptance_level Filter by this acceptance level. Valid values: "community", "partner", "vmware_accepted", "vmware_certified". (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_acceptance_level Returns: .. code-block:: json { "host1": "partner", "host2": "partner" } """ log.debug("Running vmware_esxi.get_acceptance_level") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: host_config_manager = h.configManager.imageConfigManager if not host_config_manager: continue host_acceptance_level = host_config_manager.HostImageConfigGetAcceptance() if acceptance_level and host_acceptance_level != acceptance_level: continue ret[h.name] = host_acceptance_level except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def set_acceptance_level( acceptance_level, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Set acceptance level on matching EXSI hosts. acceptance_level Set to this acceptance level. Valid values: "community", "partner", "vmware_accepted", "vmware_certified". datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.set_acceptance_level Returns: .. code-block:: json { "host1": "partner", "host2": "partner" } """ log.debug("Running vmware_esxi.set_acceptance_level") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: host_config_manager = h.configManager.imageConfigManager if not host_config_manager: continue host_config_manager.UpdateHostImageAcceptanceLevel(newAcceptanceLevel=acceptance_level) ret[h.name] = acceptance_level except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def get_advanced_config( datacenter_name=None, cluster_name=None, host_name=None, config_name=None, service_instance=None, ): """ Get advanced config on matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) config_name Filter by this config_name. (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_advanced_config """ log.debug("Running vmware_esxi.get_advanced_config") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: config_manager = h.configManager.advancedOption ret[h.name] = {} if not config_manager: continue for opt in config_manager.QueryOptions(config_name): ret[h.name][opt.key] = opt.value except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def set_advanced_configs( config_dict, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Set multiple advanced configurations on matching EXSI hosts. config_dict Set the configuration key to the configuration value. Eg: {"Annotations.WelcomeMessage": "Hello"} datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.set_advanced_config config_name=Annotations.WelcomeMessage config_value=Hello Returns: .. code-block:: json { "host1": { "Annotations.WelcomeMessage": "HelloDemo" }, } """ log.debug("Running vmware_esxi.set_advanced_configs") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: config_manager = h.configManager.advancedOption ret[h.name] = {} if not config_manager: continue supported_configs = {} for opt in config_manager.supportedOption: if opt.key not in config_dict: continue supported_configs[opt.key] = opt.optionType advanced_configs = [] for opt in config_dict: opt_type = supported_configs[opt] val = config_dict[opt] if isinstance(opt_type, vim.option.BoolOption) and not isinstance(val, bool): val = val.lower() == "true" elif isinstance(opt_type, vim.option.LongOption): val = VmomiSupport.vmodlTypes["long"](val) elif isinstance(opt_type, vim.option.IntOption): val = VmomiSupport.vmodlTypes["int"](val) advanced_configs.append(vim.option.OptionValue(key=opt, value=val)) ret[h.name][opt] = config_dict[opt] config_manager.UpdateOptions(changedValue=advanced_configs) except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def set_advanced_config( config_name, config_value, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Set a single advanced configuration on matching EXSI hosts. config_name Name of the advanced configuration to be set. config_value Set the advanced configuration to this value. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.set_advanced_config config_name=Annotations.WelcomeMessage config_value=Hello Returns: .. code-block:: json { "host1": { "Annotations.WelcomeMessage": "HelloDemo" }, } """ log.debug("Running vmware_esxi.set_advanced_config") return set_advanced_configs( config_dict={config_name: config_value}, datacenter_name=datacenter_name, cluster_name=cluster_name, host_name=host_name, service_instance=service_instance, )
[docs]def get_firewall_config( datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Get Firewall configuration on matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_firewall_config """ log.debug("Running vmware_esxi.get_firewall_config") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: firewall_config = h.configManager.firewallSystem if not firewall_config: continue for ruleset in firewall_config.firewallInfo.ruleset: ret.setdefault(h.name, []).append( { "allowed_hosts": { "ip_address": list(ruleset.allowedHosts.ipAddress), "all_ip": ruleset.allowedHosts.allIp, "ip_network": [ "{}/{}".format(ip.network, ip.prefixLength) for ip in ruleset.allowedHosts.ipNetwork ], }, "key": ruleset.key, "service": ruleset.service, "enabled": ruleset.enabled, "rule": [ { "port": r.port, "end_port": r.endPort, "direction": r.direction, "port_type": r.portType, "protocol": r.protocol, } for r in ruleset.rule ], } ) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def backup_config( push_file_to_master=False, http_opts=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Backup configuration for matching EXSI hosts. push_file_to_master Push the downloaded configuration file to the salt master. (optional) Refer: https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.cp.html#salt.modules.cp.push http_opts Extra HTTP options to be passed to download from the URL. (optional) Refer: https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.http.html#salt.modules.http.query datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt * vmware_esxi.backup_config host_name=10.225.0.53 http_opts='{"verify_ssl": False}' """ log.debug("Running vmware_esxi.backup_config") ret = {} http_opts = http_opts or {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: try: url = h.configManager.firmwareSystem.BackupFirmwareConfiguration() url = url.replace("*", h.name) file_name = os.path.join(__opts__["cachedir"], url.rsplit("/", 1)[1]) data = __salt__["http.query"](url, decode_body=False, **http_opts) with open(file_name, "wb") as fp: fp.write(data["body"]) if push_file_to_master: __salt__["cp.push"](file_name) ret.setdefault(h.name, {"file_name": file_name}) ret[h.name]["url"] = url except salt.exceptions.CommandExecutionError as exc: log.error("Unable to backup configuration for host - %s. Error - %s", h.name, exc) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def restore_config( source_file, saltenv=None, http_opts=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Restore configuration for matching EXSI hosts. source_file Specify the source file from which the configuration is to be restored. The file can be either on the master, locally on the minion or url. E.g.: salt://vmware_config.tgz, /tmp/minion1/vmware_config.tgz or 10.225.0.53/downloads/5220da48-552e-5779-703e-5705367bd6d6/configBundle-ESXi-190313806785.eng.vmware.com.tgz saltenv Specify the saltenv when the source file needs to be retireved from the master. (optional) http_opts Extra HTTP options to be passed to download from the URL. (optional). Refer: https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.http.html#salt.modules.http.query datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.backup_config datacenter_name=dc1 host_name=host1 """ log.debug("Running vmware_esxi.backup_config") ret = {} http_opts = http_opts or {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) for h in hosts: try: data = None url = h.configManager.firmwareSystem.QueryFirmwareConfigUploadURL().replace("*", h.name) if source_file.startswith("salt://"): cached = __salt__["cp.cache_file"](source_file, saltenv=saltenv) with open(cached, "rb") as fp: data = fp.read() elif source_file.startswith("http"): data = __salt__["http.query"](url, decode_body=False, **http_opts) else: with open(source_file, "rb") as fp: data = fp.read() username, password = get_username_password( esxi_host=h.name, opts=__opts__, pillar=__pillar__ ) resp = __salt__["http.query"]( url, data=data, method="PUT", username=username, password=password, **http_opts ) if "error" in resp: ret[h.name] = resp["error"] continue if not h.runtime.inMaintenanceMode: log.debug("Host - %s entering maintenance mode", h.name) utils_common.wait_for_task( h.EnterMaintenanceMode_Task(timeout=60), h.name, "EnterMaintenanceMode" ) h.configManager.firmwareSystem.RestoreFirmwareConfiguration(force=False) ret[h.name] = True except Exception as exc: msg = "Unable to restore configuration for host - {}. Error - {}".format(h.name, exc) log.error(msg) ret[h.name] = msg if h.runtime.inMaintenanceMode: log.debug("Host - %s exiting maintenance mode", h.name) utils_common.wait_for_task( h.ExitMaintenanceMode_Task(timeout=60), h.name, "ExitMaintenanceMode" ) return ret
[docs]def reset_config( datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Reset configuration for matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.reset_config """ log.debug("Running vmware_esxi.reset_config") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) for h in hosts: try: if not h.runtime.inMaintenanceMode: log.debug("Host - %s entering maintenance mode", h.name) utils_common.wait_for_task( h.EnterMaintenanceMode_Task(timeout=60), h.name, "EnterMaintenanceMode" ) h.configManager.firmwareSystem.ResetFirmwareToFactoryDefaults() ret[h.name] = True except vmodl.fault.HostCommunication as exc: msg = "Unable to reach host - {}. Error - {}".format(h.name, str(exc)) ret[h.name] = msg log.error(msg) except Exception as exc: msg = "Unable to reset configuration for host - {}. Error - {}".format(h.name, str(exc)) ret[h.name] = msg log.error(msg) log.debug("Host - %s exiting maintenance mode", h.name) utils_common.wait_for_task( h.ExitMaintenanceMode_Task(timeout=60), h.name, "ExitMaintenanceMode" ) return ret
[docs]def get_dns_config( datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Get DNS configuration on matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_dns_config """ log.debug("Running vmware_esxi.get_dns_config") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: dns_config = h.config.network.dnsConfig if not dns_config: continue ret[h.name] = {} ret[h.name]["dhcp"] = dns_config.dhcp ret[h.name]["virtual_nic"] = dns_config.virtualNicDevice ret[h.name]["host_name"] = dns_config.hostName ret[h.name]["domain_name"] = dns_config.domainName ret[h.name]["ip"] = list(dns_config.address) except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc)) return ret
[docs]def get_ntp_config( datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Get NTP configuration on matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_ntp_config """ log.debug("Running vmware_esxi.get_ntp_config") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: ntp_config = h.configManager.dateTimeSystem if ntp_config: ret[h.name] = { "time_zone": ntp_config.dateTimeInfo.timeZone.key, "time_zone_name": ntp_config.dateTimeInfo.timeZone.name, "time_zone_description": ntp_config.dateTimeInfo.timeZone.description, "time_zone_gmt_offset": ntp_config.dateTimeInfo.timeZone.gmtOffset, "ntp_servers": list(ntp_config.dateTimeInfo.ntpConfig.server), "ntp_config_file": list(ntp_config.dateTimeInfo.ntpConfig.configFile) if ntp_config.dateTimeInfo.ntpConfig.configFile else None, } return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def list_hosts( datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ List ESXi hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.list_hosts """ log.debug("Running vmware_esxi.list_hosts") ret = [] if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: ret.append(h.name) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def add_user( user_name, password, description=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Add local user on matching ESXi hosts. user_name User to create on matching ESXi hosts. (required). password Password for the new user. (required). description Description for the new user. (optional). datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.add_user user_name=foo password=bar@123 descripton="new user" """ log.debug("Running vmware_esxi.add_user") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: account_spec = vim.host.LocalAccountManager.AccountSpecification() account_spec.id = user_name account_spec.password = password account_spec.description = description h.configManager.accountManager.CreateUser(account_spec) ret[h.name] = True return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def update_user( user_name, password, description=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Update local user on matching ESXi hosts. user_name Existing user to update on matching ESXi hosts. (required). password New Password for the existing user. (required). description New description for the existing user. (optional). datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.update_user user_name=foo password=bar@123 descripton="existing user" """ log.debug("Running vmware_esxi.update_user") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: account_spec = vim.host.LocalAccountManager.AccountSpecification() account_spec.id = user_name account_spec.password = password account_spec.description = description h.configManager.accountManager.UpdateUser(account_spec) ret[h.name] = True return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def remove_user( user_name, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Remove local user on matching ESXi hosts. user_name User to delete on matching ESXi hosts. (required). datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.remove_user user_name=foo """ log.debug("Running vmware_esxi.remove_user") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: h.configManager.accountManager.RemoveUser(user_name) ret[h.name] = True return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
def _get_net_stack(network_tcpip_stack): return { "default": "defaultTcpipStack", "provisioning": "vSphereProvisioning", "vmotion": "vmotion", "vxlan": "vxlan", "defaulttcpipstack": "default", "vsphereprovisioning": "provisioning", }.get(network_tcpip_stack.lower())
[docs]def create_vmkernel_adapter( port_group_name, dvswitch_name=None, vswitch_name=None, enable_fault_tolerance=None, enable_management_traffic=None, enable_provisioning=None, enable_replication=None, enable_replication_nfc=None, enable_vmotion=None, enable_vsan=None, mtu=1500, network_default_gateway=None, network_ip_address=None, network_subnet_mask=None, network_tcp_ip_stack="default", network_type="static", datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Create VMKernel Adapter on matching ESXi hosts. port_group_name The name of the port group for the VMKernel interface. (required). dvswitch_name The name of the vSphere Distributed Switch (vDS) where to add the VMKernel interface. One of dvswitch_name or vswitch_name is required. vswitch_name The name of the vSwitch where to add the VMKernel interface. One of dvswitch_name or vswitch_name is required. enable_fault_tolerance Enable Fault Tolerance traffic on the VMKernel adapter. Valid values: True, False. enable_management_traffic Enable Management traffic on the VMKernel adapter. Valid values: True, False. enable_provisioning Enable Provisioning traffic on the VMKernel adapter. Valid values: True, False. enable_replication Enable vSphere Replication traffic on the VMKernel adapter. Valid values: True, False. enable_replication_nfc Enable vSphere Replication NFC traffic on the VMKernel adapter. Valid values: True, False. enable_vmotion Enable vMotion traffic on the VMKernel adapter. Valid values: True, False. enable_vsan Enable VSAN traffic on the VMKernel adapter. Valid values: True, False. mtu The MTU for the VMKernel interface. network_default_gateway Default gateway (Override default gateway for this adapter). network_type Type of IP assignment. Valid values: "static", "dhcp". network_ip_address Static IP address. Required if type = 'static'. network_subnet_mask Static netmask required. Required if type = 'static'. network_tcpip_stack The TCP/IP stack for the VMKernel interface. Valid values: "default", "provisioning", "vmotion", "vxlan". datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.create_vmkernel_adapter port_group_name=portgroup1 dvswitch_name=dvs1 """ log.debug("Running vmware_esxi.create_vmkernel_adapter") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: vmk_device = _save_vmkernel_adapter( host=h, service_instance=service_instance, action="create", port_group_name=port_group_name, dvswitch_name=dvswitch_name, vswitch_name=vswitch_name, adapter_name=None, enable_fault_tolerance=enable_fault_tolerance, enable_management_traffic=enable_management_traffic, enable_provisioning=enable_provisioning, enable_replication=enable_replication, enable_replication_nfc=enable_replication_nfc, enable_vmotion=enable_vmotion, enable_vsan=enable_vsan, mtu=mtu, network_default_gateway=network_default_gateway, network_ip_address=network_ip_address, network_subnet_mask=network_subnet_mask, network_tcp_ip_stack=network_tcp_ip_stack, network_type=network_type, ) ret[h.name] = vmk_device return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
def _save_vmkernel_adapter( host, service_instance, action, port_group_name, dvswitch_name, vswitch_name, adapter_name, enable_fault_tolerance, enable_management_traffic, enable_provisioning, enable_replication, enable_replication_nfc, enable_vmotion, enable_vsan, mtu, network_default_gateway, network_ip_address, network_subnet_mask, network_tcp_ip_stack, network_type, ): vnic_config = vim.host.VirtualNic.Specification() ip_spec = vim.host.IpConfig() if network_type == "dhcp": ip_spec.dhcp = True else: ip_spec.dhcp = False ip_spec.ipAddress = network_ip_address ip_spec.subnetMask = network_subnet_mask if network_default_gateway: vnic_config.ipRouteSpec = vim.host.VirtualNic.IpRouteSpec() vnic_config.ipRouteSpec.ipRouteConfig = vim.host.IpRouteConfig() vnic_config.ipRouteSpec.ipRouteConfig.defaultGateway = network_default_gateway vnic_config.ip = ip_spec vnic_config.mtu = mtu vnic_config.netStackInstanceKey = _get_net_stack(network_tcp_ip_stack) port_group = None if dvswitch_name: vnic_config.distributedVirtualPort = vim.dvs.PortConnection() dvs = utils_vmware._get_dvs(service_instance, dvswitch_name) port_group = utils_vmware._get_dvs_portgroup(dvs=dvs, portgroup_name=port_group_name) vnic_config.distributedVirtualPort.switchUuid = dvs.uuid vnic_config.distributedVirtualPort.portgroupKey = port_group.key vnic = vmk_device = None if action == "update": for v in host.config.network.vnic: if v.device == adapter_name: vnic = v vmk_device = vnic.device break host.configManager.networkSystem.UpdateVirtualNic(vmk_device, vnic_config) else: vmk_device = host.configManager.networkSystem.AddVirtualNic( portgroup="" if dvswitch_name else port_group_name, nic=vnic_config ) for enable, service in [ (enable_management_traffic, "management"), (enable_fault_tolerance, "faultToleranceLogging"), (enable_provisioning, "vSphereProvisioning"), (enable_replication, "vSphereReplication"), (enable_replication_nfc, "vSphereReplicationNFC"), (enable_vmotion, "vmotion"), ]: if enable: host.configManager.virtualNicManager.SelectVnicForNicType(service, vmk_device) elif enable is False: host.configManager.virtualNicManager.DeselectVnicForNicType(service, vmk_device) vsan_config = vim.vsan.host.ConfigInfo() vsan_config.networkInfo = host.configManager.vsanSystem.config.networkInfo current_vsan_vnics = [ portConfig.device for portConfig in host.configManager.vsanSystem.config.networkInfo.port ] if enable_vsan: if vmk_device not in current_vsan_vnics: vsan_port_config = vim.vsan.host.ConfigInfo.NetworkInfo.PortConfig() vsan_port_config.device = vmk_device if vsan_config.networkInfo is None: vsan_config.networkInfo = vim.vsan.host.ConfigInfo.NetworkInfo() vsan_config.networkInfo.port = [vsan_port_config] else: vsan_config.networkInfo.port.append(vsan_port_config) elif enable_vsan is False and vmk_device in current_vsan_vnics: vsan_config.networkInfo.port = list( filter(lambda portConfig: portConfig.device != vmk_device, vsan_config.networkInfo.port) ) task = host.configManager.vsanSystem.UpdateVsan_Task(vsan_config) utils_common.wait_for_task(task, host.name, "UpdateVsan_Task") return vmk_device
[docs]def get_vmkernel_adapters( adapter_name=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Update VMKernel Adapter on matching ESXi hosts. adapter_name Filter by this vmkernel adapter name. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_vmkernel_adapter port_group_name=portgroup1 """ log.debug("Running vmware_esxi.get_vmkernel_adapter") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: vmk_devices = [] for v in h.config.network.vnic: if adapter_name and v.device != adapter_name: continue vmk_devices.append(v.device) ret[h.name] = vmk_devices return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def update_vmkernel_adapter( adapter_name, port_group_name, dvswitch_name=None, vswitch_name=None, enable_fault_tolerance=None, enable_management_traffic=None, enable_provisioning=None, enable_replication=None, enable_replication_nfc=None, enable_vmotion=None, enable_vsan=None, mtu=1500, network_default_gateway=None, network_ip_address=None, network_subnet_mask=None, network_tcp_ip_stack="default", network_type="static", datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Update VMKernel Adapter on matching ESXi hosts. adapter_name The name of the VMKernel interface to update. (required). port_group_name The name of the port group for the VMKernel interface. (required). dvswitch_name The name of the vSphere Distributed Switch (vDS) where to update the VMKernel interface. vswitch_name The name of the vSwitch where to update the VMKernel interface. enable_fault_tolerance Enable Fault Tolerance traffic on the VMKernel adapter. Valid values: True, False. enable_management_traffic Enable Management traffic on the VMKernel adapter. Valid values: True, False. enable_provisioning Enable Provisioning traffic on the VMKernel adapter. Valid values: True, False. enable_replication Enable vSphere Replication traffic on the VMKernel adapter. Valid values: True, False. enable_replication_nfc Enable vSphere Replication NFC traffic on the VMKernel adapter. Valid values: True, False. enable_vmotion Enable vMotion traffic on the VMKernel adapter. Valid values: True, False. enable_vsan Enable VSAN traffic on the VMKernel adapter. Valid values: True, False. mtu The MTU for the VMKernel interface. network_default_gateway Default gateway (Override default gateway for this adapter). network_type Type of IP assignment. Valid values: "static", "dhcp". network_ip_address Static IP address. Required if type = 'static'. network_subnet_mask Static netmask required. Required if type = 'static'. network_tcpip_stack The TCP/IP stack for the VMKernel interface. Valid values: "default", "provisioning", "vmotion", "vxlan". datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.update_vmkernel_adapter dvswitch_name=dvs1 mtu=2000 """ log.debug("Running vmware_esxi.update_vmkernel_adapter") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: ret[h.name] = _save_vmkernel_adapter( host=h, service_instance=service_instance, action="update", port_group_name=port_group_name, dvswitch_name=dvswitch_name, vswitch_name=vswitch_name, adapter_name=adapter_name, enable_fault_tolerance=enable_fault_tolerance, enable_management_traffic=enable_management_traffic, enable_provisioning=enable_provisioning, enable_replication=enable_replication, enable_replication_nfc=enable_replication_nfc, enable_vmotion=enable_vmotion, enable_vsan=enable_vsan, mtu=mtu, network_default_gateway=network_default_gateway, network_ip_address=network_ip_address, network_subnet_mask=network_subnet_mask, network_tcp_ip_stack=network_tcp_ip_stack, network_type=network_type, ) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def delete_vmkernel_adapter( adapter_name, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Delete VMKernel Adapter on matching ESXi hosts. adapter_name The name of the VMKernel Adapter to delete (required). datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.delete_vmkernel_adapter name=vmk1 """ log.debug("Running vmware_esxi.delete_vmkernel_adapter") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: try: h.configManager.networkSystem.RemoveVirtualNic(adapter_name) ret[h.name] = True except vim.fault.NotFound: ret[h.name] = False return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def get_user( user_name, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ Get local user on matching ESXi hosts. user_name Filter by this user name (required). datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_user user_name=foo """ log.debug("Running vmware_esxi.get_user") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: users = h.configManager.userDirectory.RetrieveUserGroups( searchStr=user_name, belongsToGroup=None, belongsToUser=None, domain=None, exactMatch=True, findUsers=True, findGroups=True, ) for user in users: ret[h.name] = { # user.principal is the user name user.principal: { "description": user.fullName, "group": user.group, "user_id": user.id, "shell_access": user.shellAccess, } } return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def add_role( role_name, privilege_ids, esxi_host_name=None, service_instance=None, ): """ Add local role to service instance, which may be an ESXi host or vCenter instance. role_name Role to create on service instance. (required). privilege_ids List of privileges for the role. (required). Refer: https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.security.doc/GUID-ED56F3C4-77D0-49E3-88B6-B99B8B437B62.html Example: ['Folder.Create', 'Folder.Delete']. esxi_host_name Connect to this ESXi host using your pillar's service_instance credentials. (optional). service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.add_role role_name=foo privileges=['Folder.Create'] """ log.debug("Running vmware_esxi.add_role") ret = {} if not service_instance: service_instance = get_service_instance( opts=__opts__, pillar=__pillar__, esxi_host=esxi_host_name, ) try: ret["role_id"] = service_instance.content.authorizationManager.AddAuthorizationRole( name=role_name, privIds=privilege_ids ) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def update_role( role_name, privilege_ids, esxi_host_name=None, service_instance=None, ): """ Update local role on service instance, which may be an ESXi host or vCenter instance. role_name Role to update on service instance. (required). privilege_ids List of privileges for the role. (required). Refer: https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.security.doc/GUID-ED56F3C4-77D0-49E3-88B6-B99B8B437B62.html Example: ['Folder.Create', 'Folder.Delete']. esxi_host_name Connect to this ESXi host using your pillar's service_instance credentials. (optional). service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.update_role role_name=foo privileges=['Folder.Create'] """ log.debug("Running vmware_esxi.update_role") if not service_instance: service_instance = get_service_instance( opts=__opts__, pillar=__pillar__, esxi_host=esxi_host_name, ) try: role = get_role(role_name=role_name, service_instance=service_instance) if not role: raise salt.exceptions.SaltException("Role {} not found".format(role_name)) service_instance.content.authorizationManager.UpdateAuthorizationRole( roleId=role["role_id"], newName=role_name, privIds=privilege_ids ) return True except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def remove_role( role_name, force=False, esxi_host_name=None, service_instance=None, ): """ Remove local role on service instance, which may be an ESXi host or vCenter instance. role_name Role to update on service instance. (required). force Forcefully remove a role even when in use. Default False. (optional). esxi_host_name Connect to this ESXi host using your pillar's service_instance credentials. (optional). service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.remove_role role_name=foo """ log.debug("Running vmware_esxi.update_role") if not service_instance: service_instance = get_service_instance( opts=__opts__, pillar=__pillar__, esxi_host=esxi_host_name, ) try: role = get_role(role_name=role_name, service_instance=service_instance) if not role: raise salt.exceptions.SaltException("Role {} not found".format(role_name)) service_instance.content.authorizationManager.RemoveAuthorizationRole( roleId=role["role_id"], failIfUsed=force ) return True except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def get_role( role_name, esxi_host_name=None, service_instance=None, ): """ Get local role on service instance, which may be an ESXi host or vCenter instance. role_name Retrieve this role on service instance. (required). esxi_host_name Connect to this ESXi host using your pillar's service_instance credentials. (optional). service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get_role role_name=foo """ log.debug("Running vmware_esxi.get_role") ret = {} if not service_instance: service_instance = get_service_instance( opts=__opts__, pillar=__pillar__, esxi_user=esxi_user_name, esxi_host=esxi_host_name, esxi_password=esxi_user_password, ) try: for role in service_instance.content.authorizationManager.roleList: if role.name == role_name: ret["role_id"] = role.roleId ret["role_name"] = role.name ret["privilege_ids"] = list(role.privilege) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def connect(host, service_instance=None): """ Connect an ESXi instance to a vCenter instance. host Name of ESXi instance in vCenter. service_instance The Service Instance from which to obtain managed object references. (Optional) .. code-block:: bash salt '*' vmware_esxi.connect host=host01 """ log.debug(f"Connect ESXi instance {host}.") if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) state = utils_esxi.reconnect_host(host, service_instance) return {"state": state}
[docs]def disconnect(host, service_instance=None): """ Disconnect an ESXi instance. host Name of ESXi instance in vCenter. service_instance The Service Instance from which to obtain managed object references. (Optional) .. code-block:: bash salt '*' vmware_esxi.disconnect host=host01 """ log.debug(f"Disconnect ESXi instance {host}.") if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) state = utils_esxi.disconnect_host(host, service_instance) return {"state": state}
[docs]def remove(host, service_instance=None): """ Remove an ESXi instance from a vCenter instance. host Name of ESXi instance in vCenter. service_instance The Service Instance from which to obtain managed object references. (Optional) .. code-block:: bash salt '*' vmware_esxi.remove host=host01 """ log.debug(f"Remove ESXi instance {host}.") if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) state = utils_esxi.remove_host(host, service_instance) return {"state": state}
[docs]def move(host, cluster_name, service_instance=None): """ Move an ESXi instance to a different cluster. host Name of ESXi instance in vCenter. cluster_name Name of cluster to move host to. service_instance The Service Instance from which to obtain managed object references. (Optional) .. code-block:: bash salt '*' vmware_esxi.move host=host01 cluster=cl1 """ log.debug(f"Move ESXi instance {host}.") if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) state = utils_esxi.move_host(host, cluster_name, service_instance) return {"state": state}
[docs]def add( host, root_user, password, cluster_name, datacenter_name, verify_host_cert=True, connect=True, service_instance=None, ): """ Add an ESXi instance to a vCenter instance. host IP address or hostname of ESXi instance. root_user Username with root privilege to ESXi instance. password Password to root user. cluster_name Name of cluster ESXi host is being added to. datacenter_name Datacenter that contains cluster that ESXi instance is being added to. verify_host_cert Validates the host's SSL certificate is signed by a CA, and that the hostname in the certificate matches the host. Defaults to True. connect Specifies whether host should be connected after being added. Defaults to True. service_instance The Service Instance from which to obtain managed object references. (Optional) .. code-block:: bash salt '*' vmware_esxi.add host=host01 root_user=root password=CorrectHorseBatteryStaple cluster_name=cl1 datacenter_name=dc1 verify_host_cert=False connect=True """ log.debug(f"Adding ESXi instance {host}.") if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) state = utils_esxi.add_host( host, root_user, password, cluster_name, datacenter_name, verify_host_cert, connect, service_instance, ) return {"state": state}
[docs]def list_pkgs( pkg_name=None, datacenter_name=None, cluster_name=None, host_name=None, service_instance=None, ): """ List the packages installed on matching EXSi hosts. Note: Appropriate filters are recommended for large installations. pkg_name Filter by this package name. (optional) datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.list_pkgs """ log.debug("Running vmware_esxi.list_pkgs") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: host_pkg_manager = h.configManager.imageConfigManager if not host_pkg_manager: continue ret[h.name] = {} pkgs = host_pkg_manager.FetchSoftwarePackages() for pkg in pkgs: if pkg_name and pkg.name != pkg_name: continue ret[h.name][pkg.name] = { "version": pkg.version, "vendor": pkg.vendor, "summary": pkg.summary, "description": pkg.description, "acceptance_level": pkg.acceptanceLevel, "maintenance_mode_required": pkg.maintenanceModeRequired, "creation_date": pkg.creationDate, } return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def get( datacenter_name=None, cluster_name=None, host_name=None, key=None, default="", delimiter=DEFAULT_TARGET_DELIM, service_instance=None, ): """ Get configuration information for matching EXSI hosts. datacenter_name Filter by this datacenter name (required when cluster is specified) cluster_name Filter by this cluster name (optional) host_name Filter by this ESXi hostname (optional) key Attempt to retrieve the named value from ESXi host configuration data, if the named value is not available return the passed default. The default return is an empty string. Follows the grains.get filter semantics. (optional) The value can also represent a value in a nested dict using a ":" delimiter for the dict. This means that if a dict in ESXi host configuration looks like this: {'vsan': {'health': 'good'}} To retrieve the value associated with the apache key in the pkg dict this key can be passed: vsan:health delimiter Specify an alternate delimiter to use when traversing a nested dict. This is useful for when the desired key contains a colon. (optional) service_instance Use this vCenter service connection instance instead of creating a new one. (optional). .. code-block:: bash salt '*' vmware_esxi.get dc1 cl1 """ log.debug("Running vmware_esxi.get") ret = {} if not service_instance: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) hosts = utils_esxi.get_hosts( service_instance=service_instance, host_names=[host_name] if host_name else None, cluster_name=cluster_name, datacenter_name=datacenter_name, get_all_hosts=host_name is None, ) try: for h in hosts: ret[h.name] = {} ret[h.name]["vsan"] = {} vsan_manager = h.configManager.vsanSystem if vsan_manager: vsan = vsan_manager.QueryHostStatus() ret[h.name]["vsan"]["cluster_uuid"] = vsan.uuid ret[h.name]["vsan"]["node_uuid"] = vsan.nodeUuid ret[h.name]["vsan"]["health"] = vsan.health ret[h.name]["datastores"] = {} for store in h.datastore: ret[h.name]["datastores"][store.name] = {} ret[h.name]["datastores"][store.name]["capacity"] = store.summary.capacity ret[h.name]["datastores"][store.name]["free_space"] = store.summary.freeSpace ret[h.name]["nics"] = {} for nic in h.config.network.vnic: ret[h.name]["nics"][nic.device] = {} ret[h.name]["nics"][nic.device]["ip_address"] = nic.spec.ip.ipAddress ret[h.name]["nics"][nic.device]["subnet_mask"] = nic.spec.ip.subnetMask ret[h.name]["nics"][nic.device]["mac"] = nic.spec.mac ret[h.name]["nics"][nic.device]["mtu"] = nic.spec.mtu ret[h.name]["cpu_model"] = h.summary.hardware.cpuModel ret[h.name]["num_cpu_cores"] = h.summary.hardware.numCpuCores ret[h.name]["num_cpu_pkgs"] = h.summary.hardware.numCpuPkgs ret[h.name]["num_cpu_threads"] = h.summary.hardware.numCpuThreads ret[h.name]["memory_size"] = h.summary.hardware.memorySize ret[h.name]["overall_memory_usage"] = h.summary.quickStats.overallMemoryUsage ret[h.name]["product_name"] = h.config.product.name ret[h.name]["product_version"] = h.config.product.version ret[h.name]["product_build"] = h.config.product.build ret[h.name]["product_os_type"] = h.config.product.osType ret[h.name]["host_name"] = h.summary.config.name ret[h.name]["system_vendor"] = h.hardware.systemInfo.vendor ret[h.name]["system_model"] = h.hardware.systemInfo.model ret[h.name]["bios_release_date"] = h.hardware.biosInfo.releaseDate ret[h.name]["bios_release_version"] = h.hardware.biosInfo.biosVersion ret[h.name]["uptime"] = h.summary.quickStats.uptime ret[h.name]["in_maintenance_mode"] = h.runtime.inMaintenanceMode ret[h.name]["system_uuid"] = h.hardware.systemInfo.uuid for info in h.hardware.systemInfo.otherIdentifyingInfo: ret[h.name].update( { utils_common.camel_to_snake_case( info.identifierType.key ): info.identifierValue } ) ret[h.name]["capabilities"] = _get_capability_attribs(host=h) if key: ret[h.name] = salt.utils.data.traverse_dict_and_list( ret[h.name], key, default, delimiter ) return ret except DEFAULT_EXCEPTIONS as exc: raise salt.exceptions.SaltException(str(exc))
[docs]def in_maintenance_mode(host, service_instance=None): """ Check if host is in maintenance mode. host Host IP or HostSystem/ManagedObjectReference (required). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.in_maintenance_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = "normal" if host_ref.runtime.inMaintenanceMode: mode = "inMaintenance" return {"maintenanceMode": mode}
[docs]def maintenance_mode( host, timeout=0, evacuate_powered_off_vms=False, maintenance_spec=None, catch_task_error=True, service_instance=None, ): """ Put host into maintenance mode. host Host IP or HostSystem/ManagedObjectReference (required). timeout If value is greater than 0 then task will timeout if not completed with in window (optional). evacuate_powered_off_vms Only supported by VirtualCenter (optional). If True, for DRS will fail unless all powered-off VMs have been manually registered. If False, task will successed with powered-off VMs. maintenance_spec HostMaintenanceSpec (optional). catch_task_error If False and task failed then a salt exception will be thrown (optional). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.maintenance_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = in_maintenance_mode(host_ref, service_instance) if mode["maintenanceMode"] == "inMaintenance": mode["changes"] = False return mode try: task = host_ref.EnterMaintenanceMode_Task( timeout, evacuate_powered_off_vms, maintenance_spec ) utils_common.wait_for_task(task, host_ref.name, "maintenanceMode") except salt.exceptions.SaltException as exc: if not catch_task_error: raise exc mode = in_maintenance_mode(host_ref, service_instance) mode["changes"] = mode["maintenanceMode"] == "inMaintenance" return mode
[docs]def exit_maintenance_mode(host, timeout=0, catch_task_error=True, service_instance=None): """ Put host out of maintenance mode. host Host IP or HostSystem/ManagedObjectReference (required). timeout If value is greater than 0 then task will timeout if not completed with in window (optional). catch_task_error If False and task failed then a salt exception will be thrown (optional). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.exit_maintenance_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = in_maintenance_mode(host_ref, service_instance) if mode["maintenanceMode"] == "normal": mode["changes"] = False return mode try: task = host_ref.ExitMaintenanceMode_Task(timeout) utils_common.wait_for_task(task, host_ref.name, "maintenanceMode") except salt.exceptions.SaltException as exc: if not catch_task_error: raise exc mode = in_maintenance_mode(host_ref, service_instance) mode["changes"] = mode["maintenanceMode"] == "normal" return mode
[docs]def in_lockdown_mode(host, service_instance=None): """ Check if host is in lockdown mode. host Host IP or HostSystem/ManagedObjectReference (required). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.in_lockdown_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = "normal" if host_ref.config.adminDisabled: mode = "inLockdown" return {"lockdownMode": mode}
[docs]def lockdown_mode(host, catch_task_error=True, service_instance=None): """ Put host into lockdown mode. host Host IP or HostSystem/ManagedObjectReference (required). catch_task_error If False and task failed then a salt exception will be thrown (optional). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.lockdown_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = in_lockdown_mode(host_ref) if mode["lockdownMode"] == "inLockdown": mode["changes"] = False return mode try: host_ref.EnterLockdownMode() except salt.exceptions.SaltException as exc: if not catch_task_error: raise exc mode = in_lockdown_mode(host_ref, service_instance) mode["changes"] = mode["lockdownMode"] == "inLockdown" return mode
[docs]def exit_lockdown_mode(host, catch_task_error=True, service_instance=None): """ Put host out of lockdown mode. host Host IP or HostSystem/ManagedObjectReference (required). catch_task_error If False and task failed then a salt exception will be thrown (optional). service_instance Use this vCenter service connection instance instead of creating a new one (optional). .. code-block:: bash salt '*' vmware_esxi.exit_lockdown_mode '192.0.2.117' """ if isinstance(host, vim.HostSystem): host_ref = host else: if service_instance is None: service_instance = get_service_instance(opts=__opts__, pillar=__pillar__) host_ref = utils_esxi.get_host(host, service_instance) mode = in_lockdown_mode(host_ref) if mode["lockdownMode"] == "normal": mode["changes"] = False return mode try: host_ref.ExitLockdownMode() except salt.exceptions.SaltException as exc: if not catch_task_error: raise exc mode = in_lockdown_mode(host_ref, service_instance) mode["changes"] = mode["lockdownMode"] == "normal" return mode