"Fossies" - the Fresh Open Source Software Archive

Member "salt-3002.2/salt/utils/vmware.py" (18 Nov 2020, 132422 Bytes) of package /linux/misc/salt-3002.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "vmware.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3002.1_vs_3002.2.

    1 """
    2 Connection library for VMware
    3 
    4 .. versionadded:: 2015.8.2
    5 
    6 This is a base library used by a number of VMware services such as VMware
    7 ESX, ESXi, and vCenter servers.
    8 
    9 :codeauthor: Nitin Madhok <nmadhok@clemson.edu>
   10 :codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstanley.com>
   11 
   12 Dependencies
   13 ~~~~~~~~~~~~
   14 
   15 - pyVmomi Python Module
   16 - ESXCLI: This dependency is only needed to use the ``esxcli`` function. No other
   17   functions in this module rely on ESXCLI.
   18 
   19 pyVmomi
   20 -------
   21 
   22 PyVmomi can be installed via pip:
   23 
   24 .. code-block:: bash
   25 
   26     pip install pyVmomi
   27 
   28 .. note::
   29 
   30     Version 6.0 of pyVmomi has some problems with SSL error handling on certain
   31     versions of Python. If using version 6.0 of pyVmomi, Python 2.6,
   32     Python 2.7.9, or newer must be present. This is due to an upstream dependency
   33     in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the
   34     version of Python is not in the supported range, you will need to install an
   35     earlier version of pyVmomi. See `Issue #29537`_ for more information.
   36 
   37 .. _Issue #29537: https://github.com/saltstack/salt/issues/29537
   38 
   39 Based on the note above, to install an earlier version of pyVmomi than the
   40 version currently listed in PyPi, run the following:
   41 
   42 .. code-block:: bash
   43 
   44     pip install pyVmomi==5.5.0.2014.1.1
   45 
   46 The 5.5.0.2014.1.1 is a known stable version that this original VMware utils file
   47 was developed against.
   48 
   49 ESXCLI
   50 ------
   51 
   52 This dependency is only needed to use the ``esxcli`` function. At the time of this
   53 writing, no other functions in this module rely on ESXCLI.
   54 
   55 The ESXCLI package is also referred to as the VMware vSphere CLI, or vCLI. VMware
   56 provides vCLI package installation instructions for `vSphere 5.5`_ and
   57 `vSphere 6.0`_.
   58 
   59 .. _vSphere 5.5: http://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html
   60 .. _vSphere 6.0: http://pubs.vmware.com/vsphere-60/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html
   61 
   62 Once all of the required dependencies are in place and the vCLI package is
   63 installed, you can check to see if you can connect to your ESXi host or vCenter
   64 server by running the following command:
   65 
   66 .. code-block:: bash
   67 
   68     esxcli -s <host-location> -u <username> -p <password> system syslog config get
   69 
   70 If the connection was successful, ESXCLI was successfully installed on your system.
   71 You should see output related to the ESXi host's syslog configuration.
   72 
   73 """
   74 
   75 
   76 import atexit
   77 import errno
   78 import logging
   79 import ssl
   80 import time
   81 from http.client import BadStatusLine
   82 
   83 import requests
   84 import salt.exceptions
   85 import salt.modules.cmdmod
   86 import salt.utils.path
   87 import salt.utils.platform
   88 import salt.utils.stringutils
   89 
   90 # pylint: disable=no-name-in-module
   91 try:
   92     from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub, SoapStubAdapter
   93     from pyVmomi import vim, vmodl, VmomiSupport
   94 
   95     HAS_PYVMOMI = True
   96 except ImportError:
   97     HAS_PYVMOMI = False
   98 
   99 try:
  100     from com.vmware.vapi.std.errors_client import Unauthenticated
  101     from vmware.vapi.vsphere.client import create_vsphere_client
  102 
  103     HAS_VSPHERE_SDK = True
  104 
  105 except ImportError:
  106     HAS_VSPHERE_SDK = False
  107 # pylint: enable=no-name-in-module
  108 try:
  109     import gssapi
  110     import base64
  111 
  112     HAS_GSSAPI = True
  113 except ImportError:
  114     HAS_GSSAPI = False
  115 
  116 
  117 log = logging.getLogger(__name__)
  118 
  119 
  120 def __virtual__():
  121     """
  122     Only load if PyVmomi is installed.
  123     """
  124     if HAS_PYVMOMI:
  125         return True
  126 
  127     return False, "Missing dependency: The salt.utils.vmware module requires pyVmomi."
  128 
  129 
  130 def esxcli(
  131     host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, credstore=None
  132 ):
  133     """
  134     Shell out and call the specified esxcli command, parse the result
  135     and return something sane.
  136 
  137     :param host: ESXi or vCenter host to connect to
  138     :param user: User to connect as, usually root
  139     :param pwd: Password to connect with
  140     :param port: TCP port
  141     :param cmd: esxcli command and arguments
  142     :param esxi_host: If `host` is a vCenter host, then esxi_host is the
  143                       ESXi machine on which to execute this command
  144     :param credstore: Optional path to the credential store file
  145 
  146     :return: Dictionary
  147     """
  148 
  149     esx_cmd = salt.utils.path.which("esxcli")
  150     if not esx_cmd:
  151         log.error(
  152             "Missing dependency: The salt.utils.vmware.esxcli function requires ESXCLI."
  153         )
  154         return False
  155 
  156     # Set default port and protocol if none are provided.
  157     if port is None:
  158         port = 443
  159     if protocol is None:
  160         protocol = "https"
  161 
  162     if credstore:
  163         esx_cmd += " --credstore '{}'".format(credstore)
  164 
  165     if not esxi_host:
  166         # Then we are connecting directly to an ESXi server,
  167         # 'host' points at that server, and esxi_host is a reference to the
  168         # ESXi instance we are manipulating
  169         esx_cmd += " -s {} -u {} -p '{}' " "--protocol={} --portnumber={} {}".format(
  170             host, user, pwd, protocol, port, cmd
  171         )
  172     else:
  173         esx_cmd += (
  174             " -s {} -h {} -u {} -p '{}' "
  175             "--protocol={} --portnumber={} {}".format(
  176                 host, esxi_host, user, pwd, protocol, port, cmd
  177             )
  178         )
  179 
  180     ret = salt.modules.cmdmod.run_all(esx_cmd, output_loglevel="quiet")
  181 
  182     return ret
  183 
  184 
  185 def get_vsphere_client(server, username, password, session=None):
  186     """
  187     Internal helper method to create an instance of the vSphere API client.
  188     Please provide username and password to authenticate.
  189 
  190     :param basestring server:
  191         vCenter host name or IP address
  192     :param basestring username:
  193         Name of the user
  194     :param basestring password:
  195         Password of the user
  196     :param Session session:
  197         Request HTTP session instance. If not specified, one
  198         is automatically created and used
  199 
  200     :returns:
  201         Vsphere Client instance
  202     :rtype:
  203         :class:`vmware.vapi.vmc.client.VsphereClient`
  204     """
  205     if not session:
  206         # Create an https session to be used for a vSphere client
  207         session = requests.session()
  208         # If client uses own SSL cert, session should not verify
  209         session.verify = False
  210     client = None
  211     try:
  212         client = create_vsphere_client(
  213             server=server, username=username, password=password, session=session
  214         )
  215     except Unauthenticated as err:
  216         log.trace(err)
  217     return client
  218 
  219 
  220 def _get_service_instance(
  221     host, username, password, protocol, port, mechanism, principal, domain
  222 ):
  223     """
  224     Internal method to authenticate with a vCenter server or ESX/ESXi host
  225     and return the service instance object.
  226     """
  227     log.trace("Retrieving new service instance")
  228     token = None
  229     if mechanism == "userpass":
  230         if username is None:
  231             raise salt.exceptions.CommandExecutionError(
  232                 "Login mechanism userpass was specified but the mandatory "
  233                 "parameter 'username' is missing"
  234             )
  235         if password is None:
  236             raise salt.exceptions.CommandExecutionError(
  237                 "Login mechanism userpass was specified but the mandatory "
  238                 "parameter 'password' is missing"
  239             )
  240     elif mechanism == "sspi":
  241         if principal is not None and domain is not None:
  242             try:
  243                 token = get_gssapi_token(principal, host, domain)
  244             except Exception as exc:  # pylint: disable=broad-except
  245                 raise salt.exceptions.VMwareConnectionError(str(exc))
  246         else:
  247             err_msg = (
  248                 "Login mechanism '{}' was specified but the"
  249                 " mandatory parameters are missing".format(mechanism)
  250             )
  251             raise salt.exceptions.CommandExecutionError(err_msg)
  252     else:
  253         raise salt.exceptions.CommandExecutionError(
  254             "Unsupported mechanism: '{}'".format(mechanism)
  255         )
  256     try:
  257         log.trace(
  258             "Connecting using the '%s' mechanism, with username '%s'",
  259             mechanism,
  260             username,
  261         )
  262         service_instance = SmartConnect(
  263             host=host,
  264             user=username,
  265             pwd=password,
  266             protocol=protocol,
  267             port=port,
  268             b64token=token,
  269             mechanism=mechanism,
  270         )
  271     except TypeError as exc:
  272         if "unexpected keyword argument" in exc.message:
  273             log.error(
  274                 "Initial connect to the VMware endpoint failed with %s", exc.message
  275             )
  276             log.error(
  277                 "This may mean that a version of PyVmomi EARLIER than 6.0.0.2016.6 is installed."
  278             )
  279             log.error("We recommend updating to that version or later.")
  280             raise
  281     except Exception as exc:  # pylint: disable=broad-except
  282         # pyVmomi's SmartConnect() actually raises Exception in some cases.
  283         default_msg = (
  284             "Could not connect to host '{}'. "
  285             "Please check the debug log for more information.".format(host)
  286         )
  287 
  288         try:
  289             if (
  290                 isinstance(exc, vim.fault.HostConnectFault)
  291                 and "[SSL: CERTIFICATE_VERIFY_FAILED]" in exc.msg
  292             ) or "[SSL: CERTIFICATE_VERIFY_FAILED]" in str(exc):
  293                 service_instance = SmartConnect(
  294                     host=host,
  295                     user=username,
  296                     pwd=password,
  297                     protocol=protocol,
  298                     port=port,
  299                     sslContext=ssl._create_unverified_context(),
  300                     b64token=token,
  301                     mechanism=mechanism,
  302                 )
  303             else:
  304                 log.exception(exc)
  305                 err_msg = exc.msg if hasattr(exc, "msg") else default_msg
  306                 raise salt.exceptions.VMwareConnectionError(err_msg)
  307         except Exception as exc:  # pylint: disable=broad-except
  308             # pyVmomi's SmartConnect() actually raises Exception in some cases.
  309             if "certificate verify failed" in str(exc):
  310                 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
  311                 context.verify_mode = ssl.CERT_NONE
  312                 try:
  313                     service_instance = SmartConnect(
  314                         host=host,
  315                         user=username,
  316                         pwd=password,
  317                         protocol=protocol,
  318                         port=port,
  319                         sslContext=context,
  320                         b64token=token,
  321                         mechanism=mechanism,
  322                     )
  323                 except Exception as exc:  # pylint: disable=broad-except
  324                     log.exception(exc)
  325                     err_msg = exc.msg if hasattr(exc, "msg") else str(exc)
  326                     raise salt.exceptions.VMwareConnectionError(
  327                         "Could not connect to host '{}': " "{}".format(host, err_msg)
  328                     )
  329             else:
  330                 err_msg = exc.msg if hasattr(exc, "msg") else default_msg
  331                 log.trace(exc)
  332                 raise salt.exceptions.VMwareConnectionError(err_msg)
  333     atexit.register(Disconnect, service_instance)
  334     return service_instance
  335 
  336 
  337 def get_customizationspec_ref(si, customization_spec_name):
  338     """
  339     Get a reference to a VMware customization spec for the purposes of customizing a clone
  340 
  341     si
  342         ServiceInstance for the vSphere or ESXi server (see get_service_instance)
  343 
  344     customization_spec_name
  345         Name of the customization spec
  346 
  347     """
  348     customization_spec_name = si.content.customizationSpecManager.GetCustomizationSpec(
  349         name=customization_spec_name
  350     )
  351     return customization_spec_name
  352 
  353 
  354 def get_mor_using_container_view(si, obj_type, obj_name):
  355     """
  356     Get reference to an object of specified object type and name
  357 
  358     si
  359         ServiceInstance for the vSphere or ESXi server (see get_service_instance)
  360 
  361     obj_type
  362         Type of the object (vim.StoragePod, vim.Datastore, etc)
  363 
  364     obj_name
  365         Name of the object
  366 
  367     """
  368     inventory = get_inventory(si)
  369     container = inventory.viewManager.CreateContainerView(
  370         inventory.rootFolder, [obj_type], True
  371     )
  372     for item in container.view:
  373         if item.name == obj_name:
  374             return item
  375     return None
  376 
  377 
  378 def get_service_instance(
  379     host,
  380     username=None,
  381     password=None,
  382     protocol=None,
  383     port=None,
  384     mechanism="userpass",
  385     principal=None,
  386     domain=None,
  387 ):
  388     """
  389     Authenticate with a vCenter server or ESX/ESXi host and return the service instance object.
  390 
  391     host
  392         The location of the vCenter server or ESX/ESXi host.
  393 
  394     username
  395         The username used to login to the vCenter server or ESX/ESXi host.
  396         Required if mechanism is ``userpass``
  397 
  398     password
  399         The password used to login to the vCenter server or ESX/ESXi host.
  400         Required if mechanism is ``userpass``
  401 
  402     protocol
  403         Optionally set to alternate protocol if the vCenter server or ESX/ESXi host is not
  404         using the default protocol. Default protocol is ``https``.
  405 
  406     port
  407         Optionally set to alternate port if the vCenter server or ESX/ESXi host is not
  408         using the default port. Default port is ``443``.
  409 
  410     mechanism
  411         pyVmomi connection mechanism. Can either be ``userpass`` or ``sspi``.
  412         Default mechanism is ``userpass``.
  413 
  414     principal
  415         Kerberos service principal. Required if mechanism is ``sspi``
  416 
  417     domain
  418         Kerberos user domain. Required if mechanism is ``sspi``
  419     """
  420 
  421     if protocol is None:
  422         protocol = "https"
  423     if port is None:
  424         port = 443
  425 
  426     service_instance = GetSi()
  427     if service_instance:
  428         stub = GetStub()
  429         if salt.utils.platform.is_proxy() or (
  430             hasattr(stub, "host") and stub.host != ":".join([host, str(port)])
  431         ):
  432             # Proxies will fork and mess up the cached service instance.
  433             # If this is a proxy or we are connecting to a different host
  434             # invalidate the service instance to avoid a potential memory leak
  435             # and reconnect
  436             Disconnect(service_instance)
  437             service_instance = None
  438 
  439     if not service_instance:
  440         service_instance = _get_service_instance(
  441             host, username, password, protocol, port, mechanism, principal, domain
  442         )
  443 
  444     # Test if data can actually be retrieved or connection has gone stale
  445     log.trace("Checking connection is still authenticated")
  446     try:
  447         service_instance.CurrentTime()
  448     except vim.fault.NotAuthenticated:
  449         log.trace("Session no longer authenticating. Reconnecting")
  450         Disconnect(service_instance)
  451         service_instance = _get_service_instance(
  452             host, username, password, protocol, port, mechanism, principal, domain
  453         )
  454     except vim.fault.NoPermission as exc:
  455         log.exception(exc)
  456         raise salt.exceptions.VMwareApiError(
  457             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  458         )
  459     except vim.fault.VimFault as exc:
  460         log.exception(exc)
  461         raise salt.exceptions.VMwareApiError(exc.msg)
  462     except vmodl.RuntimeFault as exc:
  463         log.exception(exc)
  464         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  465 
  466     return service_instance
  467 
  468 
  469 def get_new_service_instance_stub(service_instance, path, ns=None, version=None):
  470     """
  471     Returns a stub that points to a different path,
  472     created from an existing connection.
  473 
  474     service_instance
  475         The Service Instance.
  476 
  477     path
  478         Path of the new stub.
  479 
  480     ns
  481         Namespace of the new stub.
  482         Default value is None
  483 
  484     version
  485         Version of the new stub.
  486         Default value is None.
  487     """
  488     # For python 2.7.9 and later, the default SSL context has more strict
  489     # connection handshaking rule. We may need turn off the hostname checking
  490     # and the client side cert verification.
  491     context = ssl.create_default_context()
  492     context.check_hostname = False
  493     context.verify_mode = ssl.CERT_NONE
  494 
  495     stub = service_instance._stub
  496     hostname = stub.host.split(":")[0]
  497     session_cookie = stub.cookie.split('"')[1]
  498     VmomiSupport.GetRequestContext()["vcSessionCookie"] = session_cookie
  499     new_stub = SoapStubAdapter(
  500         host=hostname, ns=ns, path=path, version=version, poolSize=0, sslContext=context
  501     )
  502     new_stub.cookie = stub.cookie
  503     return new_stub
  504 
  505 
  506 def get_service_instance_from_managed_object(mo_ref, name="<unnamed>"):
  507     """
  508     Retrieves the service instance from a managed object.
  509 
  510     me_ref
  511         Reference to a managed object (of type vim.ManagedEntity).
  512 
  513     name
  514         Name of managed object. This field is optional.
  515     """
  516     if not name:
  517         name = mo_ref.name
  518     log.trace("[%s] Retrieving service instance from managed object", name)
  519     si = vim.ServiceInstance("ServiceInstance")
  520     si._stub = mo_ref._stub
  521     return si
  522 
  523 
  524 def disconnect(service_instance):
  525     """
  526     Function that disconnects from the vCenter server or ESXi host
  527 
  528     service_instance
  529         The Service Instance from which to obtain managed object references.
  530     """
  531     log.trace("Disconnecting")
  532     try:
  533         Disconnect(service_instance)
  534     except vim.fault.NoPermission as exc:
  535         log.exception(exc)
  536         raise salt.exceptions.VMwareApiError(
  537             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  538         )
  539     except vim.fault.VimFault as exc:
  540         log.exception(exc)
  541         raise salt.exceptions.VMwareApiError(exc.msg)
  542     except vmodl.RuntimeFault as exc:
  543         log.exception(exc)
  544         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  545 
  546 
  547 def is_connection_to_a_vcenter(service_instance):
  548     """
  549     Function that returns True if the connection is made to a vCenter Server and
  550     False if the connection is made to an ESXi host
  551 
  552     service_instance
  553         The Service Instance from which to obtain managed object references.
  554     """
  555     try:
  556         api_type = service_instance.content.about.apiType
  557     except vim.fault.NoPermission as exc:
  558         log.exception(exc)
  559         raise salt.exceptions.VMwareApiError(
  560             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  561         )
  562     except vim.fault.VimFault as exc:
  563         log.exception(exc)
  564         raise salt.exceptions.VMwareApiError(exc.msg)
  565     except vmodl.RuntimeFault as exc:
  566         log.exception(exc)
  567         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  568     log.trace("api_type = %s", api_type)
  569     if api_type == "VirtualCenter":
  570         return True
  571     elif api_type == "HostAgent":
  572         return False
  573     else:
  574         raise salt.exceptions.VMwareApiError(
  575             "Unexpected api type '{}' . Supported types: "
  576             "'VirtualCenter/HostAgent'".format(api_type)
  577         )
  578 
  579 
  580 def get_service_info(service_instance):
  581     """
  582     Returns information of the vCenter or ESXi host
  583 
  584     service_instance
  585         The Service Instance from which to obtain managed object references.
  586     """
  587     try:
  588         return service_instance.content.about
  589     except vim.fault.NoPermission as exc:
  590         log.exception(exc)
  591         raise salt.exceptions.VMwareApiError(
  592             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  593         )
  594     except vim.fault.VimFault as exc:
  595         log.exception(exc)
  596         raise salt.exceptions.VMwareApiError(exc.msg)
  597     except vmodl.RuntimeFault as exc:
  598         log.exception(exc)
  599         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  600 
  601 
  602 def _get_dvs(service_instance, dvs_name):
  603     """
  604     Return a reference to a Distributed Virtual Switch object.
  605 
  606     :param service_instance: PyVmomi service instance
  607     :param dvs_name: Name of DVS to return
  608     :return: A PyVmomi DVS object
  609     """
  610     switches = list_dvs(service_instance)
  611     if dvs_name in switches:
  612         inventory = get_inventory(service_instance)
  613         container = inventory.viewManager.CreateContainerView(
  614             inventory.rootFolder, [vim.DistributedVirtualSwitch], True
  615         )
  616         for item in container.view:
  617             if item.name == dvs_name:
  618                 return item
  619 
  620     return None
  621 
  622 
  623 def _get_pnics(host_reference):
  624     """
  625     Helper function that returns a list of PhysicalNics and their information.
  626     """
  627     return host_reference.config.network.pnic
  628 
  629 
  630 def _get_vnics(host_reference):
  631     """
  632     Helper function that returns a list of VirtualNics and their information.
  633     """
  634     return host_reference.config.network.vnic
  635 
  636 
  637 def _get_vnic_manager(host_reference):
  638     """
  639     Helper function that returns a list of Virtual NicManagers
  640     and their information.
  641     """
  642     return host_reference.configManager.virtualNicManager
  643 
  644 
  645 def _get_dvs_portgroup(dvs, portgroup_name):
  646     """
  647     Return a portgroup object corresponding to the portgroup name on the dvs
  648 
  649     :param dvs: DVS object
  650     :param portgroup_name: Name of portgroup to return
  651     :return: Portgroup object
  652     """
  653     for portgroup in dvs.portgroup:
  654         if portgroup.name == portgroup_name:
  655             return portgroup
  656 
  657     return None
  658 
  659 
  660 def _get_dvs_uplink_portgroup(dvs, portgroup_name):
  661     """
  662     Return a portgroup object corresponding to the portgroup name on the dvs
  663 
  664     :param dvs: DVS object
  665     :param portgroup_name: Name of portgroup to return
  666     :return: Portgroup object
  667     """
  668     for portgroup in dvs.portgroup:
  669         if portgroup.name == portgroup_name:
  670             return portgroup
  671 
  672     return None
  673 
  674 
  675 def get_gssapi_token(principal, host, domain):
  676     """
  677     Get the gssapi token for Kerberos connection
  678 
  679     principal
  680        The service principal
  681     host
  682        Host url where we would like to authenticate
  683     domain
  684        Kerberos user domain
  685     """
  686 
  687     if not HAS_GSSAPI:
  688         raise ImportError("The gssapi library is not imported.")
  689 
  690     service = "{}/{}@{}".format(principal, host, domain)
  691     log.debug("Retrieving gsspi token for service %s", service)
  692     service_name = gssapi.Name(service, gssapi.C_NT_USER_NAME)
  693     ctx = gssapi.InitContext(service_name)
  694     in_token = None
  695     while not ctx.established:
  696         out_token = ctx.step(in_token)
  697         if out_token:
  698             return base64.b64encode(salt.utils.stringutils.to_bytes(out_token))
  699         if ctx.established:
  700             break
  701         if not in_token:
  702             raise salt.exceptions.CommandExecutionError(
  703                 "Can't receive token, no response from server"
  704             )
  705     raise salt.exceptions.CommandExecutionError(
  706         "Context established, but didn't receive token"
  707     )
  708 
  709 
  710 def get_hardware_grains(service_instance):
  711     """
  712     Return hardware info for standard minion grains if the service_instance is a HostAgent type
  713 
  714     service_instance
  715         The service instance object to get hardware info for
  716 
  717     .. versionadded:: 2016.11.0
  718     """
  719     hw_grain_data = {}
  720     if get_inventory(service_instance).about.apiType == "HostAgent":
  721         view = service_instance.content.viewManager.CreateContainerView(
  722             service_instance.RetrieveContent().rootFolder, [vim.HostSystem], True
  723         )
  724         if view and view.view:
  725             hw_grain_data["manufacturer"] = view.view[0].hardware.systemInfo.vendor
  726             hw_grain_data["productname"] = view.view[0].hardware.systemInfo.model
  727 
  728             for _data in view.view[0].hardware.systemInfo.otherIdentifyingInfo:
  729                 if _data.identifierType.key == "ServiceTag":
  730                     hw_grain_data["serialnumber"] = _data.identifierValue
  731 
  732             hw_grain_data["osfullname"] = view.view[0].summary.config.product.fullName
  733             hw_grain_data["osmanufacturer"] = view.view[0].summary.config.product.vendor
  734             hw_grain_data["osrelease"] = view.view[0].summary.config.product.version
  735             hw_grain_data["osbuild"] = view.view[0].summary.config.product.build
  736             hw_grain_data["os_family"] = view.view[0].summary.config.product.name
  737             hw_grain_data["os"] = view.view[0].summary.config.product.name
  738             hw_grain_data["mem_total"] = view.view[0].hardware.memorySize / 1024 / 1024
  739             hw_grain_data["biosversion"] = view.view[0].hardware.biosInfo.biosVersion
  740             hw_grain_data["biosreleasedate"] = (
  741                 view.view[0].hardware.biosInfo.releaseDate.date().strftime("%m/%d/%Y")
  742             )
  743             hw_grain_data["cpu_model"] = view.view[0].hardware.cpuPkg[0].description
  744             hw_grain_data["kernel"] = view.view[0].summary.config.product.productLineId
  745             hw_grain_data["num_cpu_sockets"] = view.view[
  746                 0
  747             ].hardware.cpuInfo.numCpuPackages
  748             hw_grain_data["num_cpu_cores"] = view.view[0].hardware.cpuInfo.numCpuCores
  749             hw_grain_data["num_cpus"] = (
  750                 hw_grain_data["num_cpu_sockets"] * hw_grain_data["num_cpu_cores"]
  751             )
  752             hw_grain_data["ip_interfaces"] = {}
  753             hw_grain_data["ip4_interfaces"] = {}
  754             hw_grain_data["ip6_interfaces"] = {}
  755             hw_grain_data["hwaddr_interfaces"] = {}
  756             for _vnic in view.view[0].configManager.networkSystem.networkConfig.vnic:
  757                 hw_grain_data["ip_interfaces"][_vnic.device] = []
  758                 hw_grain_data["ip4_interfaces"][_vnic.device] = []
  759                 hw_grain_data["ip6_interfaces"][_vnic.device] = []
  760 
  761                 hw_grain_data["ip_interfaces"][_vnic.device].append(
  762                     _vnic.spec.ip.ipAddress
  763                 )
  764                 hw_grain_data["ip4_interfaces"][_vnic.device].append(
  765                     _vnic.spec.ip.ipAddress
  766                 )
  767                 if _vnic.spec.ip.ipV6Config:
  768                     hw_grain_data["ip6_interfaces"][_vnic.device].append(
  769                         _vnic.spec.ip.ipV6Config.ipV6Address
  770                     )
  771                 hw_grain_data["hwaddr_interfaces"][_vnic.device] = _vnic.spec.mac
  772             hw_grain_data["host"] = view.view[
  773                 0
  774             ].configManager.networkSystem.dnsConfig.hostName
  775             hw_grain_data["domain"] = view.view[
  776                 0
  777             ].configManager.networkSystem.dnsConfig.domainName
  778             hw_grain_data["fqdn"] = "{}{}{}".format(
  779                 view.view[0].configManager.networkSystem.dnsConfig.hostName,
  780                 (
  781                     "."
  782                     if view.view[0].configManager.networkSystem.dnsConfig.domainName
  783                     else ""
  784                 ),
  785                 view.view[0].configManager.networkSystem.dnsConfig.domainName,
  786             )
  787 
  788             for _pnic in view.view[0].configManager.networkSystem.networkInfo.pnic:
  789                 hw_grain_data["hwaddr_interfaces"][_pnic.device] = _pnic.mac
  790 
  791             hw_grain_data["timezone"] = view.view[
  792                 0
  793             ].configManager.dateTimeSystem.dateTimeInfo.timeZone.name
  794         view = None
  795     return hw_grain_data
  796 
  797 
  798 def get_inventory(service_instance):
  799     """
  800     Return the inventory of a Service Instance Object.
  801 
  802     service_instance
  803         The Service Instance Object for which to obtain inventory.
  804     """
  805     return service_instance.RetrieveContent()
  806 
  807 
  808 def get_root_folder(service_instance):
  809     """
  810     Returns the root folder of a vCenter.
  811 
  812     service_instance
  813         The Service Instance Object for which to obtain the root folder.
  814     """
  815     try:
  816         log.trace("Retrieving root folder")
  817         return service_instance.RetrieveContent().rootFolder
  818     except vim.fault.NoPermission as exc:
  819         log.exception(exc)
  820         raise salt.exceptions.VMwareApiError(
  821             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  822         )
  823     except vim.fault.VimFault as exc:
  824         log.exception(exc)
  825         raise salt.exceptions.VMwareApiError(exc.msg)
  826     except vmodl.RuntimeFault as exc:
  827         log.exception(exc)
  828         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  829 
  830 
  831 def get_content(
  832     service_instance,
  833     obj_type,
  834     property_list=None,
  835     container_ref=None,
  836     traversal_spec=None,
  837     local_properties=False,
  838 ):
  839     """
  840     Returns the content of the specified type of object for a Service Instance.
  841 
  842     For more information, please see:
  843     http://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.wssdk.pg.doc_50%2FPG_Ch5_PropertyCollector.7.6.html
  844 
  845     service_instance
  846         The Service Instance from which to obtain content.
  847 
  848     obj_type
  849         The type of content to obtain.
  850 
  851     property_list
  852         An optional list of object properties to used to return even more filtered content results.
  853 
  854     container_ref
  855         An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter,
  856         ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory
  857         rootFolder.
  858 
  859     traversal_spec
  860         An optional TraversalSpec to be used instead of the standard
  861         ``Traverse All`` spec.
  862 
  863     local_properties
  864         Flag specifying whether the properties to be retrieved are local to the
  865         container. If that is the case, the traversal spec needs to be None.
  866     """
  867     # Start at the rootFolder if container starting point not specified
  868     if not container_ref:
  869         container_ref = get_root_folder(service_instance)
  870 
  871     # By default, the object reference used as the starting poing for the filter
  872     # is the container_ref passed in the function
  873     obj_ref = container_ref
  874     local_traversal_spec = False
  875     if not traversal_spec and not local_properties:
  876         local_traversal_spec = True
  877         # We don't have a specific traversal spec override so we are going to
  878         # get everything using a container view
  879         try:
  880             obj_ref = service_instance.content.viewManager.CreateContainerView(
  881                 container_ref, [obj_type], True
  882             )
  883         except vim.fault.NoPermission as exc:
  884             log.exception(exc)
  885             raise salt.exceptions.VMwareApiError(
  886                 "Not enough permissions. Required privilege: "
  887                 "{}".format(exc.privilegeId)
  888             )
  889         except vim.fault.VimFault as exc:
  890             log.exception(exc)
  891             raise salt.exceptions.VMwareApiError(exc.msg)
  892         except vmodl.RuntimeFault as exc:
  893             log.exception(exc)
  894             raise salt.exceptions.VMwareRuntimeError(exc.msg)
  895 
  896         # Create 'Traverse All' traversal spec to determine the path for
  897         # collection
  898         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
  899             name="traverseEntities",
  900             path="view",
  901             skip=False,
  902             type=vim.view.ContainerView,
  903         )
  904 
  905     # Create property spec to determine properties to be retrieved
  906     property_spec = vmodl.query.PropertyCollector.PropertySpec(
  907         type=obj_type, all=True if not property_list else False, pathSet=property_list
  908     )
  909 
  910     # Create object spec to navigate content
  911     obj_spec = vmodl.query.PropertyCollector.ObjectSpec(
  912         obj=obj_ref,
  913         skip=True if not local_properties else False,
  914         selectSet=[traversal_spec] if not local_properties else None,
  915     )
  916 
  917     # Create a filter spec and specify object, property spec in it
  918     filter_spec = vmodl.query.PropertyCollector.FilterSpec(
  919         objectSet=[obj_spec],
  920         propSet=[property_spec],
  921         reportMissingObjectsInResults=False,
  922     )
  923 
  924     # Retrieve the contents
  925     try:
  926         content = service_instance.content.propertyCollector.RetrieveContents(
  927             [filter_spec]
  928         )
  929     except vim.fault.NoPermission as exc:
  930         log.exception(exc)
  931         raise salt.exceptions.VMwareApiError(
  932             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
  933         )
  934     except vim.fault.VimFault as exc:
  935         log.exception(exc)
  936         raise salt.exceptions.VMwareApiError(exc.msg)
  937     except vmodl.RuntimeFault as exc:
  938         log.exception(exc)
  939         raise salt.exceptions.VMwareRuntimeError(exc.msg)
  940 
  941     # Destroy the object view
  942     if local_traversal_spec:
  943         try:
  944             obj_ref.Destroy()
  945         except vim.fault.NoPermission as exc:
  946             log.exception(exc)
  947             raise salt.exceptions.VMwareApiError(
  948                 "Not enough permissions. Required privilege: "
  949                 "{}".format(exc.privilegeId)
  950             )
  951         except vim.fault.VimFault as exc:
  952             log.exception(exc)
  953             raise salt.exceptions.VMwareApiError(exc.msg)
  954         except vmodl.RuntimeFault as exc:
  955             log.exception(exc)
  956             raise salt.exceptions.VMwareRuntimeError(exc.msg)
  957 
  958     return content
  959 
  960 
  961 def get_mor_by_property(
  962     service_instance,
  963     object_type,
  964     property_value,
  965     property_name="name",
  966     container_ref=None,
  967 ):
  968     """
  969     Returns the first managed object reference having the specified property value.
  970 
  971     service_instance
  972         The Service Instance from which to obtain managed object references.
  973 
  974     object_type
  975         The type of content for which to obtain managed object references.
  976 
  977     property_value
  978         The name of the property for which to obtain the managed object reference.
  979 
  980     property_name
  981         An object property used to return the specified object reference results. Defaults to ``name``.
  982 
  983     container_ref
  984         An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter,
  985         ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory
  986         rootFolder.
  987     """
  988     # Get list of all managed object references with specified property
  989     object_list = get_mors_with_properties(
  990         service_instance,
  991         object_type,
  992         property_list=[property_name],
  993         container_ref=container_ref,
  994     )
  995 
  996     for obj in object_list:
  997         obj_id = str(obj.get("object", "")).strip("'\"")
  998         if obj[property_name] == property_value or property_value == obj_id:
  999             return obj["object"]
 1000 
 1001     return None
 1002 
 1003 
 1004 def get_mors_with_properties(
 1005     service_instance,
 1006     object_type,
 1007     property_list=None,
 1008     container_ref=None,
 1009     traversal_spec=None,
 1010     local_properties=False,
 1011 ):
 1012     """
 1013     Returns a list containing properties and managed object references for the managed object.
 1014 
 1015     service_instance
 1016         The Service Instance from which to obtain managed object references.
 1017 
 1018     object_type
 1019         The type of content for which to obtain managed object references.
 1020 
 1021     property_list
 1022         An optional list of object properties used to return even more filtered managed object reference results.
 1023 
 1024     container_ref
 1025         An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter,
 1026         ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory
 1027         rootFolder.
 1028 
 1029     traversal_spec
 1030         An optional TraversalSpec to be used instead of the standard
 1031         ``Traverse All`` spec
 1032 
 1033     local_properties
 1034         Flag specigying whether the properties to be retrieved are local to the
 1035         container. If that is the case, the traversal spec needs to be None.
 1036     """
 1037     # Get all the content
 1038     content_args = [service_instance, object_type]
 1039     content_kwargs = {
 1040         "property_list": property_list,
 1041         "container_ref": container_ref,
 1042         "traversal_spec": traversal_spec,
 1043         "local_properties": local_properties,
 1044     }
 1045     try:
 1046         content = get_content(*content_args, **content_kwargs)
 1047     except BadStatusLine:
 1048         content = get_content(*content_args, **content_kwargs)
 1049     except OSError as exc:
 1050         if exc.errno != errno.EPIPE:
 1051             raise
 1052         content = get_content(*content_args, **content_kwargs)
 1053 
 1054     object_list = []
 1055     for obj in content:
 1056         properties = {}
 1057         for prop in obj.propSet:
 1058             properties[prop.name] = prop.val
 1059         properties["object"] = obj.obj
 1060         object_list.append(properties)
 1061     log.trace("Retrieved %s objects", len(object_list))
 1062     return object_list
 1063 
 1064 
 1065 def get_properties_of_managed_object(mo_ref, properties):
 1066     """
 1067     Returns specific properties of a managed object, retrieved in an
 1068     optimally.
 1069 
 1070     mo_ref
 1071         The managed object reference.
 1072 
 1073     properties
 1074         List of properties of the managed object to retrieve.
 1075     """
 1076     service_instance = get_service_instance_from_managed_object(mo_ref)
 1077     log.trace("Retrieving name of %s", type(mo_ref).__name__)
 1078     try:
 1079         items = get_mors_with_properties(
 1080             service_instance,
 1081             type(mo_ref),
 1082             container_ref=mo_ref,
 1083             property_list=["name"],
 1084             local_properties=True,
 1085         )
 1086         mo_name = items[0]["name"]
 1087     except vmodl.query.InvalidProperty:
 1088         mo_name = "<unnamed>"
 1089     log.trace(
 1090         "Retrieving properties '%s' of %s '%s'",
 1091         properties,
 1092         type(mo_ref).__name__,
 1093         mo_name,
 1094     )
 1095     items = get_mors_with_properties(
 1096         service_instance,
 1097         type(mo_ref),
 1098         container_ref=mo_ref,
 1099         property_list=properties,
 1100         local_properties=True,
 1101     )
 1102     if not items:
 1103         raise salt.exceptions.VMwareApiError(
 1104             "Properties of managed object '{}' weren't " "retrieved".format(mo_name)
 1105         )
 1106     return items[0]
 1107 
 1108 
 1109 def get_managed_object_name(mo_ref):
 1110     """
 1111     Returns the name of a managed object.
 1112     If the name wasn't found, it returns None.
 1113 
 1114     mo_ref
 1115         The managed object reference.
 1116     """
 1117     props = get_properties_of_managed_object(mo_ref, ["name"])
 1118     return props.get("name")
 1119 
 1120 
 1121 def get_network_adapter_type(adapter_type):
 1122     """
 1123     Return the network adapter type.
 1124 
 1125     adpater_type
 1126         The adapter type from which to obtain the network adapter type.
 1127     """
 1128     if adapter_type == "vmxnet":
 1129         return vim.vm.device.VirtualVmxnet()
 1130     elif adapter_type == "vmxnet2":
 1131         return vim.vm.device.VirtualVmxnet2()
 1132     elif adapter_type == "vmxnet3":
 1133         return vim.vm.device.VirtualVmxnet3()
 1134     elif adapter_type == "e1000":
 1135         return vim.vm.device.VirtualE1000()
 1136     elif adapter_type == "e1000e":
 1137         return vim.vm.device.VirtualE1000e()
 1138 
 1139     raise ValueError("An unknown network adapter object type name.")
 1140 
 1141 
 1142 def get_network_adapter_object_type(adapter_object):
 1143     """
 1144     Returns the network adapter type.
 1145 
 1146     adapter_object
 1147         The adapter object from which to obtain the network adapter type.
 1148     """
 1149     if isinstance(adapter_object, vim.vm.device.VirtualVmxnet2):
 1150         return "vmxnet2"
 1151     if isinstance(adapter_object, vim.vm.device.VirtualVmxnet3):
 1152         return "vmxnet3"
 1153     if isinstance(adapter_object, vim.vm.device.VirtualVmxnet):
 1154         return "vmxnet"
 1155     if isinstance(adapter_object, vim.vm.device.VirtualE1000e):
 1156         return "e1000e"
 1157     if isinstance(adapter_object, vim.vm.device.VirtualE1000):
 1158         return "e1000"
 1159 
 1160     raise ValueError("An unknown network adapter object type.")
 1161 
 1162 
 1163 def get_dvss(dc_ref, dvs_names=None, get_all_dvss=False):
 1164     """
 1165     Returns distributed virtual switches (DVSs) in a datacenter.
 1166 
 1167     dc_ref
 1168         The parent datacenter reference.
 1169 
 1170     dvs_names
 1171         The names of the DVSs to return. Default is None.
 1172 
 1173     get_all_dvss
 1174         Return all DVSs in the datacenter. Default is False.
 1175     """
 1176     dc_name = get_managed_object_name(dc_ref)
 1177     log.trace(
 1178         "Retrieving DVSs in datacenter '%s', dvs_names='%s', get_all_dvss=%s",
 1179         dc_name,
 1180         ",".join(dvs_names) if dvs_names else None,
 1181         get_all_dvss,
 1182     )
 1183     properties = ["name"]
 1184     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1185         path="networkFolder",
 1186         skip=True,
 1187         type=vim.Datacenter,
 1188         selectSet=[
 1189             vmodl.query.PropertyCollector.TraversalSpec(
 1190                 path="childEntity", skip=False, type=vim.Folder
 1191             )
 1192         ],
 1193     )
 1194     service_instance = get_service_instance_from_managed_object(dc_ref)
 1195     items = [
 1196         i["object"]
 1197         for i in get_mors_with_properties(
 1198             service_instance,
 1199             vim.DistributedVirtualSwitch,
 1200             container_ref=dc_ref,
 1201             property_list=properties,
 1202             traversal_spec=traversal_spec,
 1203         )
 1204         if get_all_dvss or (dvs_names and i["name"] in dvs_names)
 1205     ]
 1206     return items
 1207 
 1208 
 1209 def get_network_folder(dc_ref):
 1210     """
 1211     Retrieves the network folder of a datacenter
 1212     """
 1213     dc_name = get_managed_object_name(dc_ref)
 1214     log.trace("Retrieving network folder in datacenter '%s'", dc_name)
 1215     service_instance = get_service_instance_from_managed_object(dc_ref)
 1216     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1217         path="networkFolder", skip=False, type=vim.Datacenter
 1218     )
 1219     entries = get_mors_with_properties(
 1220         service_instance,
 1221         vim.Folder,
 1222         container_ref=dc_ref,
 1223         property_list=["name"],
 1224         traversal_spec=traversal_spec,
 1225     )
 1226     if not entries:
 1227         raise salt.exceptions.VMwareObjectRetrievalError(
 1228             "Network folder in datacenter '{}' wasn't retrieved" "".format(dc_name)
 1229         )
 1230     return entries[0]["object"]
 1231 
 1232 
 1233 def create_dvs(dc_ref, dvs_name, dvs_create_spec=None):
 1234     """
 1235     Creates a distributed virtual switches (DVS) in a datacenter.
 1236     Returns the reference to the newly created distributed virtual switch.
 1237 
 1238     dc_ref
 1239         The parent datacenter reference.
 1240 
 1241     dvs_name
 1242         The name of the DVS to create.
 1243 
 1244     dvs_create_spec
 1245         The DVS spec (vim.DVSCreateSpec) to use when creating the DVS.
 1246         Default is None.
 1247     """
 1248     dc_name = get_managed_object_name(dc_ref)
 1249     log.trace("Creating DVS '%s' in datacenter '%s'", dvs_name, dc_name)
 1250     if not dvs_create_spec:
 1251         dvs_create_spec = vim.DVSCreateSpec()
 1252     if not dvs_create_spec.configSpec:
 1253         dvs_create_spec.configSpec = vim.VMwareDVSConfigSpec()
 1254         dvs_create_spec.configSpec.name = dvs_name
 1255     netw_folder_ref = get_network_folder(dc_ref)
 1256     try:
 1257         task = netw_folder_ref.CreateDVS_Task(dvs_create_spec)
 1258     except vim.fault.NoPermission as exc:
 1259         log.exception(exc)
 1260         raise salt.exceptions.VMwareApiError(
 1261             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1262         )
 1263     except vim.fault.VimFault as exc:
 1264         log.exception(exc)
 1265         raise salt.exceptions.VMwareApiError(exc.msg)
 1266     except vmodl.RuntimeFault as exc:
 1267         log.exception(exc)
 1268         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1269     wait_for_task(task, dvs_name, str(task.__class__))
 1270 
 1271 
 1272 def update_dvs(dvs_ref, dvs_config_spec):
 1273     """
 1274     Updates a distributed virtual switch with the config_spec.
 1275 
 1276     dvs_ref
 1277         The DVS reference.
 1278 
 1279     dvs_config_spec
 1280         The updated config spec (vim.VMwareDVSConfigSpec) to be applied to
 1281         the DVS.
 1282     """
 1283     dvs_name = get_managed_object_name(dvs_ref)
 1284     log.trace("Updating dvs '%s'", dvs_name)
 1285     try:
 1286         task = dvs_ref.ReconfigureDvs_Task(dvs_config_spec)
 1287     except vim.fault.NoPermission as exc:
 1288         log.exception(exc)
 1289         raise salt.exceptions.VMwareApiError(
 1290             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1291         )
 1292     except vim.fault.VimFault as exc:
 1293         log.exception(exc)
 1294         raise salt.exceptions.VMwareApiError(exc.msg)
 1295     except vmodl.RuntimeFault as exc:
 1296         log.exception(exc)
 1297         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1298     wait_for_task(task, dvs_name, str(task.__class__))
 1299 
 1300 
 1301 def set_dvs_network_resource_management_enabled(dvs_ref, enabled):
 1302     """
 1303     Sets whether NIOC is enabled on a DVS.
 1304 
 1305     dvs_ref
 1306         The DVS reference.
 1307 
 1308     enabled
 1309         Flag specifying whether NIOC is enabled.
 1310     """
 1311     dvs_name = get_managed_object_name(dvs_ref)
 1312     log.trace(
 1313         "Setting network resource management enable to %s on " "dvs '%s'",
 1314         enabled,
 1315         dvs_name,
 1316     )
 1317     try:
 1318         dvs_ref.EnableNetworkResourceManagement(enable=enabled)
 1319     except vim.fault.NoPermission as exc:
 1320         log.exception(exc)
 1321         raise salt.exceptions.VMwareApiError(
 1322             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1323         )
 1324     except vim.fault.VimFault as exc:
 1325         log.exception(exc)
 1326         raise salt.exceptions.VMwareApiError(exc.msg)
 1327     except vmodl.RuntimeFault as exc:
 1328         log.exception(exc)
 1329         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1330 
 1331 
 1332 def get_dvportgroups(parent_ref, portgroup_names=None, get_all_portgroups=False):
 1333     """
 1334     Returns distributed virtual porgroups (dvportgroups).
 1335     The parent object can be either a datacenter or a dvs.
 1336 
 1337     parent_ref
 1338         The parent object reference. Can be either a datacenter or a dvs.
 1339 
 1340     portgroup_names
 1341         The names of the dvss to return. Default is None.
 1342 
 1343     get_all_portgroups
 1344         Return all portgroups in the parent. Default is False.
 1345     """
 1346     if not isinstance(parent_ref, (vim.Datacenter, vim.DistributedVirtualSwitch)):
 1347         raise salt.exceptions.ArgumentValueError(
 1348             "Parent has to be either a datacenter, " "or a distributed virtual switch"
 1349         )
 1350     parent_name = get_managed_object_name(parent_ref)
 1351     log.trace(
 1352         "Retrieving portgroup in %s '%s', portgroups_names='%s', "
 1353         "get_all_portgroups=%s",
 1354         type(parent_ref).__name__,
 1355         parent_name,
 1356         ",".join(portgroup_names) if portgroup_names else None,
 1357         get_all_portgroups,
 1358     )
 1359     properties = ["name"]
 1360     if isinstance(parent_ref, vim.Datacenter):
 1361         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1362             path="networkFolder",
 1363             skip=True,
 1364             type=vim.Datacenter,
 1365             selectSet=[
 1366                 vmodl.query.PropertyCollector.TraversalSpec(
 1367                     path="childEntity", skip=False, type=vim.Folder
 1368                 )
 1369             ],
 1370         )
 1371     else:  # parent is distributed virtual switch
 1372         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1373             path="portgroup", skip=False, type=vim.DistributedVirtualSwitch
 1374         )
 1375 
 1376     service_instance = get_service_instance_from_managed_object(parent_ref)
 1377     items = [
 1378         i["object"]
 1379         for i in get_mors_with_properties(
 1380             service_instance,
 1381             vim.DistributedVirtualPortgroup,
 1382             container_ref=parent_ref,
 1383             property_list=properties,
 1384             traversal_spec=traversal_spec,
 1385         )
 1386         if get_all_portgroups or (portgroup_names and i["name"] in portgroup_names)
 1387     ]
 1388     return items
 1389 
 1390 
 1391 def get_uplink_dvportgroup(dvs_ref):
 1392     """
 1393     Returns the uplink distributed virtual portgroup of a distributed virtual
 1394     switch (dvs)
 1395 
 1396     dvs_ref
 1397         The dvs reference
 1398     """
 1399     dvs_name = get_managed_object_name(dvs_ref)
 1400     log.trace("Retrieving uplink portgroup of dvs '%s'", dvs_name)
 1401     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1402         path="portgroup", skip=False, type=vim.DistributedVirtualSwitch
 1403     )
 1404     service_instance = get_service_instance_from_managed_object(dvs_ref)
 1405     items = [
 1406         entry["object"]
 1407         for entry in get_mors_with_properties(
 1408             service_instance,
 1409             vim.DistributedVirtualPortgroup,
 1410             container_ref=dvs_ref,
 1411             property_list=["tag"],
 1412             traversal_spec=traversal_spec,
 1413         )
 1414         if entry["tag"] and [t for t in entry["tag"] if t.key == "SYSTEM/DVS.UPLINKPG"]
 1415     ]
 1416     if not items:
 1417         raise salt.exceptions.VMwareObjectRetrievalError(
 1418             "Uplink portgroup of DVS '{}' wasn't found".format(dvs_name)
 1419         )
 1420     return items[0]
 1421 
 1422 
 1423 def create_dvportgroup(dvs_ref, spec):
 1424     """
 1425     Creates a distributed virtual portgroup on a distributed virtual switch
 1426     (dvs)
 1427 
 1428     dvs_ref
 1429         The dvs reference
 1430 
 1431     spec
 1432         Portgroup spec (vim.DVPortgroupConfigSpec)
 1433     """
 1434     dvs_name = get_managed_object_name(dvs_ref)
 1435     log.trace("Adding portgroup %s to dvs '%s'", spec.name, dvs_name)
 1436     log.trace("spec = %s", spec)
 1437     try:
 1438         task = dvs_ref.CreateDVPortgroup_Task(spec)
 1439     except vim.fault.NoPermission as exc:
 1440         log.exception(exc)
 1441         raise salt.exceptions.VMwareApiError(
 1442             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1443         )
 1444     except vim.fault.VimFault as exc:
 1445         log.exception(exc)
 1446         raise salt.exceptions.VMwareApiError(exc.msg)
 1447     except vmodl.RuntimeFault as exc:
 1448         log.exception(exc)
 1449         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1450     wait_for_task(task, dvs_name, str(task.__class__))
 1451 
 1452 
 1453 def update_dvportgroup(portgroup_ref, spec):
 1454     """
 1455     Updates a distributed virtual portgroup
 1456 
 1457     portgroup_ref
 1458         The portgroup reference
 1459 
 1460     spec
 1461         Portgroup spec (vim.DVPortgroupConfigSpec)
 1462     """
 1463     pg_name = get_managed_object_name(portgroup_ref)
 1464     log.trace("Updating portgrouo %s", pg_name)
 1465     try:
 1466         task = portgroup_ref.ReconfigureDVPortgroup_Task(spec)
 1467     except vim.fault.NoPermission as exc:
 1468         log.exception(exc)
 1469         raise salt.exceptions.VMwareApiError(
 1470             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1471         )
 1472     except vim.fault.VimFault as exc:
 1473         log.exception(exc)
 1474         raise salt.exceptions.VMwareApiError(exc.msg)
 1475     except vmodl.RuntimeFault as exc:
 1476         log.exception(exc)
 1477         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1478     wait_for_task(task, pg_name, str(task.__class__))
 1479 
 1480 
 1481 def remove_dvportgroup(portgroup_ref):
 1482     """
 1483     Removes a distributed virtual portgroup
 1484 
 1485     portgroup_ref
 1486         The portgroup reference
 1487     """
 1488     pg_name = get_managed_object_name(portgroup_ref)
 1489     log.trace("Removing portgroup %s", pg_name)
 1490     try:
 1491         task = portgroup_ref.Destroy_Task()
 1492     except vim.fault.NoPermission as exc:
 1493         log.exception(exc)
 1494         raise salt.exceptions.VMwareApiError(
 1495             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1496         )
 1497     except vim.fault.VimFault as exc:
 1498         log.exception(exc)
 1499         raise salt.exceptions.VMwareApiError(exc.msg)
 1500     except vmodl.RuntimeFault as exc:
 1501         log.exception(exc)
 1502         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1503     wait_for_task(task, pg_name, str(task.__class__))
 1504 
 1505 
 1506 def get_networks(parent_ref, network_names=None, get_all_networks=False):
 1507     """
 1508     Returns networks of standard switches.
 1509     The parent object can be a datacenter.
 1510 
 1511     parent_ref
 1512         The parent object reference. A datacenter object.
 1513 
 1514     network_names
 1515         The name of the standard switch networks. Default is None.
 1516 
 1517     get_all_networks
 1518         Boolean indicates whether to return all networks in the parent.
 1519         Default is False.
 1520     """
 1521 
 1522     if not isinstance(parent_ref, vim.Datacenter):
 1523         raise salt.exceptions.ArgumentValueError("Parent has to be a datacenter.")
 1524     parent_name = get_managed_object_name(parent_ref)
 1525     log.trace(
 1526         "Retrieving network from %s '%s', network_names='%s', " "get_all_networks=%s",
 1527         type(parent_ref).__name__,
 1528         parent_name,
 1529         ",".join(network_names) if network_names else None,
 1530         get_all_networks,
 1531     )
 1532     properties = ["name"]
 1533     service_instance = get_service_instance_from_managed_object(parent_ref)
 1534     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1535         path="networkFolder",
 1536         skip=True,
 1537         type=vim.Datacenter,
 1538         selectSet=[
 1539             vmodl.query.PropertyCollector.TraversalSpec(
 1540                 path="childEntity", skip=False, type=vim.Folder
 1541             )
 1542         ],
 1543     )
 1544     items = [
 1545         i["object"]
 1546         for i in get_mors_with_properties(
 1547             service_instance,
 1548             vim.Network,
 1549             container_ref=parent_ref,
 1550             property_list=properties,
 1551             traversal_spec=traversal_spec,
 1552         )
 1553         if get_all_networks or (network_names and i["name"] in network_names)
 1554     ]
 1555     return items
 1556 
 1557 
 1558 def list_objects(service_instance, vim_object, properties=None):
 1559     """
 1560     Returns a simple list of objects from a given service instance.
 1561 
 1562     service_instance
 1563         The Service Instance for which to obtain a list of objects.
 1564 
 1565     object_type
 1566         The type of content for which to obtain information.
 1567 
 1568     properties
 1569         An optional list of object properties used to return reference results.
 1570         If not provided, defaults to ``name``.
 1571     """
 1572     if properties is None:
 1573         properties = ["name"]
 1574 
 1575     items = []
 1576     item_list = get_mors_with_properties(service_instance, vim_object, properties)
 1577     for item in item_list:
 1578         items.append(item["name"])
 1579     return items
 1580 
 1581 
 1582 def get_license_manager(service_instance):
 1583     """
 1584     Returns the license manager.
 1585 
 1586     service_instance
 1587         The Service Instance Object from which to obrain the license manager.
 1588     """
 1589 
 1590     log.debug("Retrieving license manager")
 1591     try:
 1592         lic_manager = service_instance.content.licenseManager
 1593     except vim.fault.NoPermission as exc:
 1594         log.exception(exc)
 1595         raise salt.exceptions.VMwareApiError(
 1596             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1597         )
 1598     except vim.fault.VimFault as exc:
 1599         log.exception(exc)
 1600         raise salt.exceptions.VMwareApiError(exc.msg)
 1601     except vmodl.RuntimeFault as exc:
 1602         log.exception(exc)
 1603         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1604     return lic_manager
 1605 
 1606 
 1607 def get_license_assignment_manager(service_instance):
 1608     """
 1609     Returns the license assignment manager.
 1610 
 1611     service_instance
 1612         The Service Instance Object from which to obrain the license manager.
 1613     """
 1614 
 1615     log.debug("Retrieving license assignment manager")
 1616     try:
 1617         lic_assignment_manager = (
 1618             service_instance.content.licenseManager.licenseAssignmentManager
 1619         )
 1620     except vim.fault.NoPermission as exc:
 1621         log.exception(exc)
 1622         raise salt.exceptions.VMwareApiError(
 1623             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1624         )
 1625     except vim.fault.VimFault as exc:
 1626         log.exception(exc)
 1627         raise salt.exceptions.VMwareApiError(exc.msg)
 1628     except vmodl.RuntimeFault as exc:
 1629         log.exception(exc)
 1630         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1631     if not lic_assignment_manager:
 1632         raise salt.exceptions.VMwareObjectRetrievalError(
 1633             "License assignment manager was not retrieved"
 1634         )
 1635     return lic_assignment_manager
 1636 
 1637 
 1638 def get_licenses(service_instance, license_manager=None):
 1639     """
 1640     Returns the licenses on a specific instance.
 1641 
 1642     service_instance
 1643         The Service Instance Object from which to obrain the licenses.
 1644 
 1645     license_manager
 1646         The License Manager object of the service instance. If not provided it
 1647         will be retrieved.
 1648     """
 1649 
 1650     if not license_manager:
 1651         license_manager = get_license_manager(service_instance)
 1652     log.debug("Retrieving licenses")
 1653     try:
 1654         return license_manager.licenses
 1655     except vim.fault.NoPermission as exc:
 1656         log.exception(exc)
 1657         raise salt.exceptions.VMwareApiError(
 1658             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1659         )
 1660     except vim.fault.VimFault as exc:
 1661         log.exception(exc)
 1662         raise salt.exceptions.VMwareApiError(exc.msg)
 1663     except vmodl.RuntimeFault as exc:
 1664         log.exception(exc)
 1665         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1666 
 1667 
 1668 def add_license(service_instance, key, description, license_manager=None):
 1669     """
 1670     Adds a license.
 1671 
 1672     service_instance
 1673         The Service Instance Object.
 1674 
 1675     key
 1676         The key of the license to add.
 1677 
 1678     description
 1679         The description of the license to add.
 1680 
 1681     license_manager
 1682         The License Manager object of the service instance. If not provided it
 1683         will be retrieved.
 1684     """
 1685     if not license_manager:
 1686         license_manager = get_license_manager(service_instance)
 1687     label = vim.KeyValue()
 1688     label.key = "VpxClientLicenseLabel"
 1689     label.value = description
 1690     log.debug("Adding license '%s'", description)
 1691     try:
 1692         vmware_license = license_manager.AddLicense(key, [label])
 1693     except vim.fault.NoPermission as exc:
 1694         log.exception(exc)
 1695         raise salt.exceptions.VMwareApiError(
 1696             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1697         )
 1698     except vim.fault.VimFault as exc:
 1699         log.exception(exc)
 1700         raise salt.exceptions.VMwareApiError(exc.msg)
 1701     except vmodl.RuntimeFault as exc:
 1702         log.exception(exc)
 1703         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1704     return vmware_license
 1705 
 1706 
 1707 def get_assigned_licenses(
 1708     service_instance, entity_ref=None, entity_name=None, license_assignment_manager=None
 1709 ):
 1710     """
 1711     Returns the licenses assigned to an entity. If entity ref is not provided,
 1712     then entity_name is assumed to be the vcenter. This is later checked if
 1713     the entity name is provided.
 1714 
 1715     service_instance
 1716         The Service Instance Object from which to obtain the licenses.
 1717 
 1718     entity_ref
 1719         VMware entity to get the assigned licenses for.
 1720         If None, the entity is the vCenter itself.
 1721         Default is None.
 1722 
 1723     entity_name
 1724         Entity name used in logging.
 1725         Default is None.
 1726 
 1727     license_assignment_manager
 1728         The LicenseAssignmentManager object of the service instance.
 1729         If not provided it will be retrieved.
 1730         Default is None.
 1731     """
 1732     if not license_assignment_manager:
 1733         license_assignment_manager = get_license_assignment_manager(service_instance)
 1734     if not entity_name:
 1735         raise salt.exceptions.ArgumentValueError("No entity_name passed")
 1736     # If entity_ref is not defined, then interested in the vcenter
 1737     entity_id = None
 1738     entity_type = "moid"
 1739     check_name = False
 1740     if not entity_ref:
 1741         if entity_name:
 1742             check_name = True
 1743         entity_type = "uuid"
 1744         try:
 1745             entity_id = service_instance.content.about.instanceUuid
 1746         except vim.fault.NoPermission as exc:
 1747             log.exception(exc)
 1748             raise salt.exceptions.VMwareApiError(
 1749                 "Not enough permissions. Required privilege: "
 1750                 "{}".format(exc.privilegeId)
 1751             )
 1752         except vim.fault.VimFault as exc:
 1753             log.exception(exc)
 1754             raise salt.exceptions.VMwareApiError(exc.msg)
 1755         except vmodl.RuntimeFault as exc:
 1756             log.exception(exc)
 1757             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1758     else:
 1759         entity_id = entity_ref._moId
 1760 
 1761     log.trace("Retrieving licenses assigned to '%s'", entity_name)
 1762     try:
 1763         assignments = license_assignment_manager.QueryAssignedLicenses(entity_id)
 1764     except vim.fault.NoPermission as exc:
 1765         log.exception(exc)
 1766         raise salt.exceptions.VMwareApiError(
 1767             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1768         )
 1769     except vim.fault.VimFault as exc:
 1770         log.exception(exc)
 1771         raise salt.exceptions.VMwareApiError(exc.msg)
 1772     except vmodl.RuntimeFault as exc:
 1773         log.exception(exc)
 1774         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1775 
 1776     if entity_type == "uuid" and len(assignments) > 1:
 1777         log.trace(
 1778             "Unexpectectedly retrieved more than one" " VCenter license assignment."
 1779         )
 1780         raise salt.exceptions.VMwareObjectRetrievalError(
 1781             "Unexpected return. Expect only a single assignment"
 1782         )
 1783 
 1784     if check_name:
 1785         if entity_name != assignments[0].entityDisplayName:
 1786             log.trace(
 1787                 "Getting license info for wrong vcenter: %s != %s",
 1788                 entity_name,
 1789                 assignments[0].entityDisplayName,
 1790             )
 1791             raise salt.exceptions.VMwareObjectRetrievalError(
 1792                 "Got license assignment info for a different vcenter"
 1793             )
 1794 
 1795     return [a.assignedLicense for a in assignments]
 1796 
 1797 
 1798 def assign_license(
 1799     service_instance,
 1800     license_key,
 1801     license_name,
 1802     entity_ref=None,
 1803     entity_name=None,
 1804     license_assignment_manager=None,
 1805 ):
 1806     """
 1807     Assigns a license to an entity.
 1808 
 1809     service_instance
 1810         The Service Instance Object from which to obrain the licenses.
 1811 
 1812     license_key
 1813         The key of the license to add.
 1814 
 1815     license_name
 1816         The description of the license to add.
 1817 
 1818     entity_ref
 1819         VMware entity to assign the license to.
 1820         If None, the entity is the vCenter itself.
 1821         Default is None.
 1822 
 1823     entity_name
 1824         Entity name used in logging.
 1825         Default is None.
 1826 
 1827     license_assignment_manager
 1828         The LicenseAssignmentManager object of the service instance.
 1829         If not provided it will be retrieved
 1830         Default is None.
 1831     """
 1832     if not license_assignment_manager:
 1833         license_assignment_manager = get_license_assignment_manager(service_instance)
 1834     entity_id = None
 1835 
 1836     if not entity_ref:
 1837         # vcenter
 1838         try:
 1839             entity_id = service_instance.content.about.instanceUuid
 1840         except vim.fault.NoPermission as exc:
 1841             log.exception(exc)
 1842             raise salt.exceptions.VMwareApiError(
 1843                 "Not enough permissions. Required privilege: "
 1844                 "{}".format(exc.privilegeId)
 1845             )
 1846         except vim.fault.VimFault as exc:
 1847             raise salt.exceptions.VMwareApiError(exc.msg)
 1848         except vmodl.RuntimeFault as exc:
 1849             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1850         if not entity_name:
 1851             entity_name = "vCenter"
 1852     else:
 1853         # e.g. vsan cluster or host
 1854         entity_id = entity_ref._moId
 1855 
 1856     log.trace("Assigning license to '%s'", entity_name)
 1857     try:
 1858         vmware_license = license_assignment_manager.UpdateAssignedLicense(
 1859             entity_id, license_key, license_name
 1860         )
 1861     except vim.fault.NoPermission as exc:
 1862         log.exception(exc)
 1863         raise salt.exceptions.VMwareApiError(
 1864             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1865         )
 1866     except vim.fault.VimFault as exc:
 1867         log.exception(exc)
 1868         raise salt.exceptions.VMwareApiError(exc.msg)
 1869     except vmodl.RuntimeFault as exc:
 1870         log.exception(exc)
 1871         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1872     return vmware_license
 1873 
 1874 
 1875 def list_datacenters(service_instance):
 1876     """
 1877     Returns a list of datacenters associated with a given service instance.
 1878 
 1879     service_instance
 1880         The Service Instance Object from which to obtain datacenters.
 1881     """
 1882     return list_objects(service_instance, vim.Datacenter)
 1883 
 1884 
 1885 def get_datacenters(service_instance, datacenter_names=None, get_all_datacenters=False):
 1886     """
 1887     Returns all datacenters in a vCenter.
 1888 
 1889     service_instance
 1890         The Service Instance Object from which to obtain cluster.
 1891 
 1892     datacenter_names
 1893         List of datacenter names to filter by. Default value is None.
 1894 
 1895     get_all_datacenters
 1896         Flag specifying whether to retrieve all datacenters.
 1897         Default value is None.
 1898     """
 1899     items = [
 1900         i["object"]
 1901         for i in get_mors_with_properties(
 1902             service_instance, vim.Datacenter, property_list=["name"]
 1903         )
 1904         if get_all_datacenters or (datacenter_names and i["name"] in datacenter_names)
 1905     ]
 1906     return items
 1907 
 1908 
 1909 def get_datacenter(service_instance, datacenter_name):
 1910     """
 1911     Returns a vim.Datacenter managed object.
 1912 
 1913     service_instance
 1914         The Service Instance Object from which to obtain datacenter.
 1915 
 1916     datacenter_name
 1917         The datacenter name
 1918     """
 1919     items = get_datacenters(service_instance, datacenter_names=[datacenter_name])
 1920     if not items:
 1921         raise salt.exceptions.VMwareObjectRetrievalError(
 1922             "Datacenter '{}' was not found".format(datacenter_name)
 1923         )
 1924     return items[0]
 1925 
 1926 
 1927 def create_datacenter(service_instance, datacenter_name):
 1928     """
 1929     Creates a datacenter.
 1930 
 1931     .. versionadded:: 2017.7.0
 1932 
 1933     service_instance
 1934         The Service Instance Object
 1935 
 1936     datacenter_name
 1937         The datacenter name
 1938     """
 1939     root_folder = get_root_folder(service_instance)
 1940     log.trace("Creating datacenter '%s'", datacenter_name)
 1941     try:
 1942         dc_obj = root_folder.CreateDatacenter(datacenter_name)
 1943     except vim.fault.NoPermission as exc:
 1944         log.exception(exc)
 1945         raise salt.exceptions.VMwareApiError(
 1946             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 1947         )
 1948     except vim.fault.VimFault as exc:
 1949         log.exception(exc)
 1950         raise salt.exceptions.VMwareApiError(exc.msg)
 1951     except vmodl.RuntimeFault as exc:
 1952         log.exception(exc)
 1953         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 1954     return dc_obj
 1955 
 1956 
 1957 def get_cluster(dc_ref, cluster):
 1958     """
 1959     Returns a cluster in a datacenter.
 1960 
 1961     dc_ref
 1962         The datacenter reference
 1963 
 1964     cluster
 1965         The cluster to be retrieved
 1966     """
 1967     dc_name = get_managed_object_name(dc_ref)
 1968     log.trace("Retrieving cluster '%s' from datacenter '%s'", cluster, dc_name)
 1969     si = get_service_instance_from_managed_object(dc_ref, name=dc_name)
 1970     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 1971         path="hostFolder",
 1972         skip=True,
 1973         type=vim.Datacenter,
 1974         selectSet=[
 1975             vmodl.query.PropertyCollector.TraversalSpec(
 1976                 path="childEntity", skip=False, type=vim.Folder
 1977             )
 1978         ],
 1979     )
 1980     items = [
 1981         i["object"]
 1982         for i in get_mors_with_properties(
 1983             si,
 1984             vim.ClusterComputeResource,
 1985             container_ref=dc_ref,
 1986             property_list=["name"],
 1987             traversal_spec=traversal_spec,
 1988         )
 1989         if i["name"] == cluster
 1990     ]
 1991     if not items:
 1992         raise salt.exceptions.VMwareObjectRetrievalError(
 1993             "Cluster '{}' was not found in datacenter " "'{}'".format(cluster, dc_name)
 1994         )
 1995     return items[0]
 1996 
 1997 
 1998 def create_cluster(dc_ref, cluster_name, cluster_spec):
 1999     """
 2000     Creates a cluster in a datacenter.
 2001 
 2002     dc_ref
 2003         The parent datacenter reference.
 2004 
 2005     cluster_name
 2006         The cluster name.
 2007 
 2008     cluster_spec
 2009         The cluster spec (vim.ClusterConfigSpecEx).
 2010         Defaults to None.
 2011     """
 2012     dc_name = get_managed_object_name(dc_ref)
 2013     log.trace("Creating cluster '%s' in datacenter '%s'", cluster_name, dc_name)
 2014     try:
 2015         dc_ref.hostFolder.CreateClusterEx(cluster_name, cluster_spec)
 2016     except vim.fault.NoPermission as exc:
 2017         log.exception(exc)
 2018         raise salt.exceptions.VMwareApiError(
 2019             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2020         )
 2021     except vim.fault.VimFault as exc:
 2022         log.exception(exc)
 2023         raise salt.exceptions.VMwareApiError(exc.msg)
 2024     except vmodl.RuntimeFault as exc:
 2025         log.exception(exc)
 2026         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2027 
 2028 
 2029 def update_cluster(cluster_ref, cluster_spec):
 2030     """
 2031     Updates a cluster in a datacenter.
 2032 
 2033     cluster_ref
 2034         The cluster reference.
 2035 
 2036     cluster_spec
 2037         The cluster spec (vim.ClusterConfigSpecEx).
 2038         Defaults to None.
 2039     """
 2040     cluster_name = get_managed_object_name(cluster_ref)
 2041     log.trace("Updating cluster '%s'", cluster_name)
 2042     try:
 2043         task = cluster_ref.ReconfigureComputeResource_Task(cluster_spec, modify=True)
 2044     except vim.fault.NoPermission as exc:
 2045         log.exception(exc)
 2046         raise salt.exceptions.VMwareApiError(
 2047             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2048         )
 2049     except vim.fault.VimFault as exc:
 2050         log.exception(exc)
 2051         raise salt.exceptions.VMwareApiError(exc.msg)
 2052     except vmodl.RuntimeFault as exc:
 2053         log.exception(exc)
 2054         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2055     wait_for_task(task, cluster_name, "ClusterUpdateTask")
 2056 
 2057 
 2058 def list_clusters(service_instance):
 2059     """
 2060     Returns a list of clusters associated with a given service instance.
 2061 
 2062     service_instance
 2063         The Service Instance Object from which to obtain clusters.
 2064     """
 2065     return list_objects(service_instance, vim.ClusterComputeResource)
 2066 
 2067 
 2068 def list_datastore_clusters(service_instance):
 2069     """
 2070     Returns a list of datastore clusters associated with a given service instance.
 2071 
 2072     service_instance
 2073         The Service Instance Object from which to obtain datastore clusters.
 2074     """
 2075     return list_objects(service_instance, vim.StoragePod)
 2076 
 2077 
 2078 def list_datastores(service_instance):
 2079     """
 2080     Returns a list of datastores associated with a given service instance.
 2081 
 2082     service_instance
 2083         The Service Instance Object from which to obtain datastores.
 2084     """
 2085     return list_objects(service_instance, vim.Datastore)
 2086 
 2087 
 2088 def get_datastore_files(
 2089     service_instance, directory, datastores, container_object, browser_spec
 2090 ):
 2091     """
 2092     Get the files with a given browser specification from the datastore.
 2093 
 2094     service_instance
 2095         The Service Instance Object from which to obtain datastores.
 2096 
 2097     directory
 2098         The name of the directory where we would like to search
 2099 
 2100     datastores
 2101         Name of the datastores
 2102 
 2103     container_object
 2104         The base object for searches
 2105 
 2106     browser_spec
 2107         BrowserSpec object which defines the search criteria
 2108 
 2109     return
 2110         list of vim.host.DatastoreBrowser.SearchResults objects
 2111     """
 2112 
 2113     files = []
 2114     datastore_objects = get_datastores(
 2115         service_instance, container_object, datastore_names=datastores
 2116     )
 2117     for datobj in datastore_objects:
 2118         try:
 2119             task = datobj.browser.SearchDatastore_Task(
 2120                 datastorePath="[{}] {}".format(datobj.name, directory),
 2121                 searchSpec=browser_spec,
 2122             )
 2123         except vim.fault.NoPermission as exc:
 2124             log.exception(exc)
 2125             raise salt.exceptions.VMwareApiError(
 2126                 "Not enough permissions. Required privilege: "
 2127                 "{}".format(exc.privilegeId)
 2128             )
 2129         except vim.fault.VimFault as exc:
 2130             log.exception(exc)
 2131             raise salt.exceptions.VMwareApiError(exc.msg)
 2132         except vmodl.RuntimeFault as exc:
 2133             log.exception(exc)
 2134             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2135         try:
 2136             files.append(
 2137                 salt.utils.vmware.wait_for_task(
 2138                     task, directory, "query virtual machine files"
 2139                 )
 2140             )
 2141         except salt.exceptions.VMwareFileNotFoundError:
 2142             pass
 2143     return files
 2144 
 2145 
 2146 def get_datastores(
 2147     service_instance,
 2148     reference,
 2149     datastore_names=None,
 2150     backing_disk_ids=None,
 2151     get_all_datastores=False,
 2152 ):
 2153     """
 2154     Returns a list of vim.Datastore objects representing the datastores visible
 2155     from a VMware object, filtered by their names, or the backing disk
 2156     cannonical name or scsi_addresses
 2157 
 2158     service_instance
 2159         The Service Instance Object from which to obtain datastores.
 2160 
 2161     reference
 2162         The VMware object from which the datastores are visible.
 2163 
 2164     datastore_names
 2165         The list of datastore names to be retrieved. Default value is None.
 2166 
 2167     backing_disk_ids
 2168         The list of canonical names of the disks backing the datastores
 2169         to be retrieved. Only supported if reference is a vim.HostSystem.
 2170         Default value is None
 2171 
 2172     get_all_datastores
 2173         Specifies whether to retrieve all disks in the host.
 2174         Default value is False.
 2175     """
 2176     obj_name = get_managed_object_name(reference)
 2177     if get_all_datastores:
 2178         log.trace("Retrieving all datastores visible to '%s'", obj_name)
 2179     else:
 2180         log.trace(
 2181             "Retrieving datastores visible to '%s': names = (%s); "
 2182             "backing disk ids = (%s)",
 2183             obj_name,
 2184             datastore_names,
 2185             backing_disk_ids,
 2186         )
 2187         if backing_disk_ids and not isinstance(reference, vim.HostSystem):
 2188 
 2189             raise salt.exceptions.ArgumentValueError(
 2190                 "Unsupported reference type '{}' when backing disk filter "
 2191                 "is set".format(reference.__class__.__name__)
 2192             )
 2193     if (not get_all_datastores) and backing_disk_ids:
 2194         # At this point we know the reference is a vim.HostSystem
 2195         log.trace("Filtering datastores with backing disk ids: %s", backing_disk_ids)
 2196         storage_system = get_storage_system(service_instance, reference, obj_name)
 2197         props = salt.utils.vmware.get_properties_of_managed_object(
 2198             storage_system, ["fileSystemVolumeInfo.mountInfo"]
 2199         )
 2200         mount_infos = props.get("fileSystemVolumeInfo.mountInfo", [])
 2201         disk_datastores = []
 2202         # Non vmfs volumes aren't backed by a disk
 2203         for vol in [
 2204             i.volume for i in mount_infos if isinstance(i.volume, vim.HostVmfsVolume)
 2205         ]:
 2206 
 2207             if not [e for e in vol.extent if e.diskName in backing_disk_ids]:
 2208                 # Skip volume if it doesn't contain an extent with a
 2209                 # canonical name of interest
 2210                 continue
 2211             log.trace(
 2212                 "Found datastore '%s' for disk id(s) '%s'",
 2213                 vol.name,
 2214                 [e.diskName for e in vol.extent],
 2215             )
 2216             disk_datastores.append(vol.name)
 2217         log.trace("Datastore found for disk filter: %s", disk_datastores)
 2218         if datastore_names:
 2219             datastore_names.extend(disk_datastores)
 2220         else:
 2221             datastore_names = disk_datastores
 2222 
 2223     if (not get_all_datastores) and (not datastore_names):
 2224         log.trace(
 2225             "No datastore to be filtered after retrieving the datastores "
 2226             "backed by the disk id(s) '%s'",
 2227             backing_disk_ids,
 2228         )
 2229         return []
 2230 
 2231     log.trace("datastore_names = %s", datastore_names)
 2232 
 2233     # Use the default traversal spec
 2234     if isinstance(reference, vim.HostSystem):
 2235         # Create a different traversal spec for hosts because it looks like the
 2236         # default doesn't retrieve the datastores
 2237         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2238             name="host_datastore_traversal",
 2239             path="datastore",
 2240             skip=False,
 2241             type=vim.HostSystem,
 2242         )
 2243     elif isinstance(reference, vim.ClusterComputeResource):
 2244         # Traversal spec for clusters
 2245         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2246             name="cluster_datastore_traversal",
 2247             path="datastore",
 2248             skip=False,
 2249             type=vim.ClusterComputeResource,
 2250         )
 2251     elif isinstance(reference, vim.Datacenter):
 2252         # Traversal spec for datacenter
 2253         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2254             name="datacenter_datastore_traversal",
 2255             path="datastore",
 2256             skip=False,
 2257             type=vim.Datacenter,
 2258         )
 2259     elif isinstance(reference, vim.StoragePod):
 2260         # Traversal spec for datastore clusters
 2261         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2262             name="datastore_cluster_traversal",
 2263             path="childEntity",
 2264             skip=False,
 2265             type=vim.StoragePod,
 2266         )
 2267     elif (
 2268         isinstance(reference, vim.Folder)
 2269         and get_managed_object_name(reference) == "Datacenters"
 2270     ):
 2271         # Traversal of root folder (doesn't support multiple levels of Folders)
 2272         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2273             path="childEntity",
 2274             selectSet=[
 2275                 vmodl.query.PropertyCollector.TraversalSpec(
 2276                     path="datastore", skip=False, type=vim.Datacenter
 2277                 )
 2278             ],
 2279             skip=False,
 2280             type=vim.Folder,
 2281         )
 2282     else:
 2283         raise salt.exceptions.ArgumentValueError(
 2284             "Unsupported reference type '{}'" "".format(reference.__class__.__name__)
 2285         )
 2286 
 2287     items = get_mors_with_properties(
 2288         service_instance,
 2289         object_type=vim.Datastore,
 2290         property_list=["name"],
 2291         container_ref=reference,
 2292         traversal_spec=traversal_spec,
 2293     )
 2294     log.trace("Retrieved %s datastores", len(items))
 2295     items = [i for i in items if get_all_datastores or i["name"] in datastore_names]
 2296     log.trace("Filtered datastores: %s", [i["name"] for i in items])
 2297     return [i["object"] for i in items]
 2298 
 2299 
 2300 def rename_datastore(datastore_ref, new_datastore_name):
 2301     """
 2302     Renames a datastore
 2303 
 2304     datastore_ref
 2305         vim.Datastore reference to the datastore object to be changed
 2306 
 2307     new_datastore_name
 2308         New datastore name
 2309     """
 2310     ds_name = get_managed_object_name(datastore_ref)
 2311     log.trace("Renaming datastore '%s' to '%s'", ds_name, new_datastore_name)
 2312     try:
 2313         datastore_ref.RenameDatastore(new_datastore_name)
 2314     except vim.fault.NoPermission as exc:
 2315         log.exception(exc)
 2316         raise salt.exceptions.VMwareApiError(
 2317             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2318         )
 2319     except vim.fault.VimFault as exc:
 2320         log.exception(exc)
 2321         raise salt.exceptions.VMwareApiError(exc.msg)
 2322     except vmodl.RuntimeFault as exc:
 2323         log.exception(exc)
 2324         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2325 
 2326 
 2327 def get_storage_system(service_instance, host_ref, hostname=None):
 2328     """
 2329     Returns a host's storage system
 2330     """
 2331 
 2332     if not hostname:
 2333         hostname = get_managed_object_name(host_ref)
 2334 
 2335     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2336         path="configManager.storageSystem", type=vim.HostSystem, skip=False
 2337     )
 2338     objs = get_mors_with_properties(
 2339         service_instance,
 2340         vim.HostStorageSystem,
 2341         property_list=["systemFile"],
 2342         container_ref=host_ref,
 2343         traversal_spec=traversal_spec,
 2344     )
 2345     if not objs:
 2346         raise salt.exceptions.VMwareObjectRetrievalError(
 2347             "Host's '{}' storage system was not retrieved" "".format(hostname)
 2348         )
 2349     log.trace("[%s] Retrieved storage system", hostname)
 2350     return objs[0]["object"]
 2351 
 2352 
 2353 def _get_partition_info(storage_system, device_path):
 2354     """
 2355     Returns partition information for a device path, of type
 2356     vim.HostDiskPartitionInfo
 2357     """
 2358     try:
 2359         partition_infos = storage_system.RetrieveDiskPartitionInfo(
 2360             devicePath=[device_path]
 2361         )
 2362     except vim.fault.NoPermission as exc:
 2363         log.exception(exc)
 2364         raise salt.exceptions.VMwareApiError(
 2365             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2366         )
 2367     except vim.fault.VimFault as exc:
 2368         log.exception(exc)
 2369         raise salt.exceptions.VMwareApiError(exc.msg)
 2370     except vmodl.RuntimeFault as exc:
 2371         log.exception(exc)
 2372         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2373     log.trace("partition_info = %s", partition_infos[0])
 2374     return partition_infos[0]
 2375 
 2376 
 2377 def _get_new_computed_partition_spec(storage_system, device_path, partition_info):
 2378     """
 2379     Computes the new disk partition info when adding a new vmfs partition that
 2380     uses up the remainder of the disk; returns a tuple
 2381     (new_partition_number, vim.HostDiskPartitionSpec
 2382     """
 2383     log.trace(
 2384         "Adding a partition at the end of the disk and getting the new "
 2385         "computed partition spec"
 2386     )
 2387     # TODO implement support for multiple partitions
 2388     # We support adding a partition add the end of the disk with partitions
 2389     free_partitions = [p for p in partition_info.layout.partition if p.type == "none"]
 2390     if not free_partitions:
 2391         raise salt.exceptions.VMwareObjectNotFoundError(
 2392             "Free partition was not found on device '{}'"
 2393             "".format(partition_info.deviceName)
 2394         )
 2395     free_partition = free_partitions[0]
 2396 
 2397     # Create a layout object that copies the existing one
 2398     layout = vim.HostDiskPartitionLayout(
 2399         total=partition_info.layout.total, partition=partition_info.layout.partition
 2400     )
 2401     # Create a partition with the free space on the disk
 2402     # Change the free partition type to vmfs
 2403     free_partition.type = "vmfs"
 2404     try:
 2405         computed_partition_info = storage_system.ComputeDiskPartitionInfo(
 2406             devicePath=device_path,
 2407             partitionFormat=vim.HostDiskPartitionInfoPartitionFormat.gpt,
 2408             layout=layout,
 2409         )
 2410     except vim.fault.NoPermission as exc:
 2411         log.exception(exc)
 2412         raise salt.exceptions.VMwareApiError(
 2413             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2414         )
 2415     except vim.fault.VimFault as exc:
 2416         log.exception(exc)
 2417         raise salt.exceptions.VMwareApiError(exc.msg)
 2418     except vmodl.RuntimeFault as exc:
 2419         log.exception(exc)
 2420         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2421     log.trace("computed partition info = {0}", computed_partition_info)
 2422     log.trace("Retrieving new partition number")
 2423     partition_numbers = [
 2424         p.partition
 2425         for p in computed_partition_info.layout.partition
 2426         if (
 2427             p.start.block == free_partition.start.block
 2428             or
 2429             # XXX If the entire disk is free (i.e. the free
 2430             # disk partition starts at block 0) the newily
 2431             # created partition is created from block 1
 2432             (free_partition.start.block == 0 and p.start.block == 1)
 2433         )
 2434         and p.end.block == free_partition.end.block
 2435         and p.type == "vmfs"
 2436     ]
 2437     if not partition_numbers:
 2438         raise salt.exceptions.VMwareNotFoundError(
 2439             "New partition was not found in computed partitions of device "
 2440             "'{}'".format(partition_info.deviceName)
 2441         )
 2442     log.trace("new partition number = %s", partition_numbers[0])
 2443     return (partition_numbers[0], computed_partition_info.spec)
 2444 
 2445 
 2446 def create_vmfs_datastore(
 2447     host_ref, datastore_name, disk_ref, vmfs_major_version, storage_system=None
 2448 ):
 2449     """
 2450     Creates a VMFS datastore from a disk_id
 2451 
 2452     host_ref
 2453         vim.HostSystem object referencing a host to create the datastore on
 2454 
 2455     datastore_name
 2456         Name of the datastore
 2457 
 2458     disk_ref
 2459         vim.HostScsiDislk on which the datastore is created
 2460 
 2461     vmfs_major_version
 2462         VMFS major version to use
 2463     """
 2464     # TODO Support variable sized partitions
 2465     hostname = get_managed_object_name(host_ref)
 2466     disk_id = disk_ref.canonicalName
 2467     log.debug(
 2468         "Creating datastore '%s' on host '%s', scsi disk '%s', " "vmfs v%s",
 2469         datastore_name,
 2470         hostname,
 2471         disk_id,
 2472         vmfs_major_version,
 2473     )
 2474     if not storage_system:
 2475         si = get_service_instance_from_managed_object(host_ref, name=hostname)
 2476         storage_system = get_storage_system(si, host_ref, hostname)
 2477 
 2478     target_disk = disk_ref
 2479     partition_info = _get_partition_info(storage_system, target_disk.devicePath)
 2480     log.trace("partition_info = %s", partition_info)
 2481     new_partition_number, partition_spec = _get_new_computed_partition_spec(
 2482         storage_system, target_disk.devicePath, partition_info
 2483     )
 2484     spec = vim.VmfsDatastoreCreateSpec(
 2485         vmfs=vim.HostVmfsSpec(
 2486             majorVersion=vmfs_major_version,
 2487             volumeName=datastore_name,
 2488             extent=vim.HostScsiDiskPartition(
 2489                 diskName=disk_id, partition=new_partition_number
 2490             ),
 2491         ),
 2492         diskUuid=target_disk.uuid,
 2493         partition=partition_spec,
 2494     )
 2495     try:
 2496         ds_ref = host_ref.configManager.datastoreSystem.CreateVmfsDatastore(spec)
 2497     except vim.fault.NoPermission as exc:
 2498         log.exception(exc)
 2499         raise salt.exceptions.VMwareApiError(
 2500             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2501         )
 2502     except vim.fault.VimFault as exc:
 2503         log.exception(exc)
 2504         raise salt.exceptions.VMwareApiError(exc.msg)
 2505     except vmodl.RuntimeFault as exc:
 2506         log.exception(exc)
 2507         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2508     log.debug("Created datastore '%s' on host '%s'", datastore_name, hostname)
 2509     return ds_ref
 2510 
 2511 
 2512 def get_host_datastore_system(host_ref, hostname=None):
 2513     """
 2514     Returns a host's datastore system
 2515 
 2516     host_ref
 2517         Reference to the ESXi host
 2518 
 2519     hostname
 2520         Name of the host. This argument is optional.
 2521     """
 2522 
 2523     if not hostname:
 2524         hostname = get_managed_object_name(host_ref)
 2525     service_instance = get_service_instance_from_managed_object(host_ref)
 2526     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2527         path="configManager.datastoreSystem", type=vim.HostSystem, skip=False
 2528     )
 2529     objs = get_mors_with_properties(
 2530         service_instance,
 2531         vim.HostDatastoreSystem,
 2532         property_list=["datastore"],
 2533         container_ref=host_ref,
 2534         traversal_spec=traversal_spec,
 2535     )
 2536     if not objs:
 2537         raise salt.exceptions.VMwareObjectRetrievalError(
 2538             "Host's '{}' datastore system was not retrieved" "".format(hostname)
 2539         )
 2540     log.trace("[%s] Retrieved datastore system", hostname)
 2541     return objs[0]["object"]
 2542 
 2543 
 2544 def remove_datastore(service_instance, datastore_ref):
 2545     """
 2546     Creates a VMFS datastore from a disk_id
 2547 
 2548     service_instance
 2549         The Service Instance Object containing the datastore
 2550 
 2551     datastore_ref
 2552         The reference to the datastore to remove
 2553     """
 2554     ds_props = get_properties_of_managed_object(datastore_ref, ["host", "info", "name"])
 2555     ds_name = ds_props["name"]
 2556     log.debug("Removing datastore '%s'", ds_name)
 2557     ds_hosts = ds_props.get("host")
 2558     if not ds_hosts:
 2559         raise salt.exceptions.VMwareApiError(
 2560             "Datastore '{}' can't be removed. No "
 2561             "attached hosts found".format(ds_name)
 2562         )
 2563     hostname = get_managed_object_name(ds_hosts[0].key)
 2564     host_ds_system = get_host_datastore_system(ds_hosts[0].key, hostname=hostname)
 2565     try:
 2566         host_ds_system.RemoveDatastore(datastore_ref)
 2567     except vim.fault.NoPermission as exc:
 2568         log.exception(exc)
 2569         raise salt.exceptions.VMwareApiError(
 2570             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2571         )
 2572     except vim.fault.VimFault as exc:
 2573         log.exception(exc)
 2574         raise salt.exceptions.VMwareApiError(exc.msg)
 2575     except vmodl.RuntimeFault as exc:
 2576         log.exception(exc)
 2577         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2578     log.trace("[%s] Removed datastore '%s'", hostname, ds_name)
 2579 
 2580 
 2581 def get_hosts(
 2582     service_instance,
 2583     datacenter_name=None,
 2584     host_names=None,
 2585     cluster_name=None,
 2586     get_all_hosts=False,
 2587 ):
 2588     """
 2589     Returns a list of vim.HostSystem objects representing ESXi hosts
 2590     in a vcenter filtered by their names and/or datacenter, cluster membership.
 2591 
 2592     service_instance
 2593         The Service Instance Object from which to obtain the hosts.
 2594 
 2595     datacenter_name
 2596         The datacenter name. Default is None.
 2597 
 2598     host_names
 2599         The host_names to be retrieved. Default is None.
 2600 
 2601     cluster_name
 2602         The cluster name - used to restrict the hosts retrieved. Only used if
 2603         the datacenter is set.  This argument is optional.
 2604 
 2605     get_all_hosts
 2606         Specifies whether to retrieve all hosts in the container.
 2607         Default value is False.
 2608     """
 2609     properties = ["name"]
 2610     if cluster_name and not datacenter_name:
 2611         raise salt.exceptions.ArgumentValueError(
 2612             "Must specify the datacenter when specifying the cluster"
 2613         )
 2614     if not host_names:
 2615         host_names = []
 2616     if not datacenter_name:
 2617         # Assume the root folder is the starting point
 2618         start_point = get_root_folder(service_instance)
 2619     else:
 2620         start_point = get_datacenter(service_instance, datacenter_name)
 2621         if cluster_name:
 2622             # Retrieval to test if cluster exists. Cluster existence only makes
 2623             # sense if the datacenter has been specified
 2624             properties.append("parent")
 2625 
 2626     # Search for the objects
 2627     hosts = get_mors_with_properties(
 2628         service_instance,
 2629         vim.HostSystem,
 2630         container_ref=start_point,
 2631         property_list=properties,
 2632     )
 2633     log.trace("Retrieved hosts: %s", [h["name"] for h in hosts])
 2634     filtered_hosts = []
 2635     for h in hosts:
 2636         # Complex conditions checking if a host should be added to the
 2637         # filtered list (either due to its name and/or cluster membership)
 2638 
 2639         if cluster_name:
 2640             if not isinstance(h["parent"], vim.ClusterComputeResource):
 2641                 continue
 2642             parent_name = get_managed_object_name(h["parent"])
 2643             if parent_name != cluster_name:
 2644                 continue
 2645 
 2646         if get_all_hosts:
 2647             filtered_hosts.append(h["object"])
 2648             continue
 2649 
 2650         if h["name"] in host_names:
 2651             filtered_hosts.append(h["object"])
 2652     return filtered_hosts
 2653 
 2654 
 2655 def _get_scsi_address_to_lun_key_map(
 2656     service_instance, host_ref, storage_system=None, hostname=None
 2657 ):
 2658     """
 2659     Returns a map between the scsi addresses and the keys of all luns on an ESXi
 2660     host.
 2661         map[<scsi_address>] = <lun key>
 2662 
 2663     service_instance
 2664         The Service Instance Object from which to obtain the hosts
 2665 
 2666     host_ref
 2667         The vim.HostSystem object representing the host that contains the
 2668         requested disks.
 2669 
 2670     storage_system
 2671         The host's storage system. Default is None.
 2672 
 2673     hostname
 2674         Name of the host. Default is None.
 2675     """
 2676     if not hostname:
 2677         hostname = get_managed_object_name(host_ref)
 2678     if not storage_system:
 2679         storage_system = get_storage_system(service_instance, host_ref, hostname)
 2680     try:
 2681         device_info = storage_system.storageDeviceInfo
 2682     except vim.fault.NoPermission as exc:
 2683         log.exception(exc)
 2684         raise salt.exceptions.VMwareApiError(
 2685             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2686         )
 2687     except vim.fault.VimFault as exc:
 2688         log.exception(exc)
 2689         raise salt.exceptions.VMwareApiError(exc.msg)
 2690     except vmodl.RuntimeFault as exc:
 2691         log.exception(exc)
 2692         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2693     if not device_info:
 2694         raise salt.exceptions.VMwareObjectRetrievalError(
 2695             "Host's '{}' storage device " "info was not retrieved".format(hostname)
 2696         )
 2697     multipath_info = device_info.multipathInfo
 2698     if not multipath_info:
 2699         raise salt.exceptions.VMwareObjectRetrievalError(
 2700             "Host's '{}' multipath info was not retrieved" "".format(hostname)
 2701         )
 2702     if multipath_info.lun is None:
 2703         raise salt.exceptions.VMwareObjectRetrievalError(
 2704             "No luns were retrieved from host '{}'".format(hostname)
 2705         )
 2706     lun_key_by_scsi_addr = {}
 2707     for l in multipath_info.lun:
 2708         # The vmware scsi_address may have multiple comma separated values
 2709         # The first one is the actual scsi address
 2710         lun_key_by_scsi_addr.update({p.name.split(",")[0]: l.lun for p in l.path})
 2711     log.trace(
 2712         "Scsi address to lun id map on host '%s': %s", hostname, lun_key_by_scsi_addr
 2713     )
 2714     return lun_key_by_scsi_addr
 2715 
 2716 
 2717 def get_all_luns(host_ref, storage_system=None, hostname=None):
 2718     """
 2719     Returns a list of all vim.HostScsiDisk objects in a disk
 2720 
 2721     host_ref
 2722         The vim.HostSystem object representing the host that contains the
 2723         requested disks.
 2724 
 2725     storage_system
 2726         The host's storage system. Default is None.
 2727 
 2728     hostname
 2729         Name of the host. This argument is optional.
 2730     """
 2731     if not hostname:
 2732         hostname = get_managed_object_name(host_ref)
 2733     if not storage_system:
 2734         si = get_service_instance_from_managed_object(host_ref, name=hostname)
 2735         storage_system = get_storage_system(si, host_ref, hostname)
 2736         if not storage_system:
 2737             raise salt.exceptions.VMwareObjectRetrievalError(
 2738                 "Host's '{}' storage system was not retrieved" "".format(hostname)
 2739             )
 2740     try:
 2741         device_info = storage_system.storageDeviceInfo
 2742     except vim.fault.NoPermission as exc:
 2743         log.exception(exc)
 2744         raise salt.exceptions.VMwareApiError(
 2745             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2746         )
 2747     except vim.fault.VimFault as exc:
 2748         log.exception(exc)
 2749         raise salt.exceptions.VMwareApiError(exc.msg)
 2750     except vmodl.RuntimeFault as exc:
 2751         log.exception(exc)
 2752         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 2753     if not device_info:
 2754         raise salt.exceptions.VMwareObjectRetrievalError(
 2755             "Host's '{}' storage device info was not retrieved" "".format(hostname)
 2756         )
 2757 
 2758     scsi_luns = device_info.scsiLun
 2759     if scsi_luns:
 2760         log.trace(
 2761             "Retrieved scsi luns in host '%s': %s",
 2762             hostname,
 2763             [l.canonicalName for l in scsi_luns],
 2764         )
 2765         return scsi_luns
 2766     log.trace("Retrieved no scsi_luns in host '%s'", hostname)
 2767     return []
 2768 
 2769 
 2770 def get_scsi_address_to_lun_map(host_ref, storage_system=None, hostname=None):
 2771     """
 2772     Returns a map of all vim.ScsiLun objects on a ESXi host keyed by their
 2773     scsi address
 2774 
 2775     host_ref
 2776         The vim.HostSystem object representing the host that contains the
 2777         requested disks.
 2778 
 2779     storage_system
 2780         The host's storage system. Default is None.
 2781 
 2782     hostname
 2783         Name of the host. This argument is optional.
 2784     """
 2785     if not hostname:
 2786         hostname = get_managed_object_name(host_ref)
 2787     si = get_service_instance_from_managed_object(host_ref, name=hostname)
 2788     if not storage_system:
 2789         storage_system = get_storage_system(si, host_ref, hostname)
 2790     lun_ids_to_scsi_addr_map = _get_scsi_address_to_lun_key_map(
 2791         si, host_ref, storage_system, hostname
 2792     )
 2793     luns_to_key_map = {
 2794         d.key: d for d in get_all_luns(host_ref, storage_system, hostname)
 2795     }
 2796     return {
 2797         scsi_addr: luns_to_key_map[lun_key]
 2798         for scsi_addr, lun_key in lun_ids_to_scsi_addr_map.items()
 2799     }
 2800 
 2801 
 2802 def get_disks(host_ref, disk_ids=None, scsi_addresses=None, get_all_disks=False):
 2803     """
 2804     Returns a list of vim.HostScsiDisk objects representing disks
 2805     in a ESXi host, filtered by their cannonical names and scsi_addresses
 2806 
 2807     host_ref
 2808         The vim.HostSystem object representing the host that contains the
 2809         requested disks.
 2810 
 2811     disk_ids
 2812         The list of canonical names of the disks to be retrieved. Default value
 2813         is None
 2814 
 2815     scsi_addresses
 2816         The list of scsi addresses of the disks to be retrieved. Default value
 2817         is None
 2818 
 2819     get_all_disks
 2820         Specifies whether to retrieve all disks in the host.
 2821         Default value is False.
 2822     """
 2823     hostname = get_managed_object_name(host_ref)
 2824     if get_all_disks:
 2825         log.trace("Retrieving all disks in host '%s'", hostname)
 2826     else:
 2827         log.trace(
 2828             "Retrieving disks in host '%s': ids = (%s); scsi " "addresses = (%s)",
 2829             hostname,
 2830             disk_ids,
 2831             scsi_addresses,
 2832         )
 2833         if not (disk_ids or scsi_addresses):
 2834             return []
 2835     si = get_service_instance_from_managed_object(host_ref, name=hostname)
 2836     storage_system = get_storage_system(si, host_ref, hostname)
 2837     disk_keys = []
 2838     if scsi_addresses:
 2839         # convert the scsi addresses to disk keys
 2840         lun_key_by_scsi_addr = _get_scsi_address_to_lun_key_map(
 2841             si, host_ref, storage_system, hostname
 2842         )
 2843         disk_keys = [
 2844             key
 2845             for scsi_addr, key in lun_key_by_scsi_addr.items()
 2846             if scsi_addr in scsi_addresses
 2847         ]
 2848         log.trace("disk_keys based on scsi_addresses = %s", disk_keys)
 2849 
 2850     scsi_luns = get_all_luns(host_ref, storage_system)
 2851     scsi_disks = [
 2852         disk
 2853         for disk in scsi_luns
 2854         if isinstance(disk, vim.HostScsiDisk)
 2855         and (
 2856             get_all_disks
 2857             or
 2858             # Filter by canonical name
 2859             (disk_ids and (disk.canonicalName in disk_ids))
 2860             or
 2861             # Filter by disk keys from scsi addresses
 2862             (disk.key in disk_keys)
 2863         )
 2864     ]
 2865     log.trace(
 2866         "Retrieved disks in host '%s': %s",
 2867         hostname,
 2868         [d.canonicalName for d in scsi_disks],
 2869     )
 2870     return scsi_disks
 2871 
 2872 
 2873 def get_disk_partition_info(host_ref, disk_id, storage_system=None):
 2874     """
 2875     Returns all partitions on a disk
 2876 
 2877     host_ref
 2878         The reference of the ESXi host containing the disk
 2879 
 2880     disk_id
 2881         The canonical name of the disk whose partitions are to be removed
 2882 
 2883     storage_system
 2884         The ESXi host's storage system. Default is None.
 2885     """
 2886     hostname = get_managed_object_name(host_ref)
 2887     service_instance = get_service_instance_from_managed_object(host_ref)
 2888     if not storage_system:
 2889         storage_system = get_storage_system(service_instance, host_ref, hostname)
 2890 
 2891     props = get_properties_of_managed_object(
 2892         storage_system, ["storageDeviceInfo.scsiLun"]
 2893     )
 2894     if not props.get("storageDeviceInfo.scsiLun"):
 2895         raise salt.exceptions.VMwareObjectRetrievalError(
 2896             "No devices were retrieved in host '{}'".format(hostname)
 2897         )
 2898     log.trace(
 2899         "[%s] Retrieved %s devices: %s",
 2900         hostname,
 2901         len(props["storageDeviceInfo.scsiLun"]),
 2902         ", ".join([l.canonicalName for l in props["storageDeviceInfo.scsiLun"]]),
 2903     )
 2904     disks = [
 2905         l
 2906         for l in props["storageDeviceInfo.scsiLun"]
 2907         if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id
 2908     ]
 2909     if not disks:
 2910         raise salt.exceptions.VMwareObjectRetrievalError(
 2911             "Disk '{}' was not found in host '{}'" "".format(disk_id, hostname)
 2912         )
 2913     log.trace("[%s] device_path = %s", hostname, disks[0].devicePath)
 2914     partition_info = _get_partition_info(storage_system, disks[0].devicePath)
 2915     log.trace(
 2916         "[%s] Retrieved %s partition(s) on disk '%s'",
 2917         hostname,
 2918         len(partition_info.spec.partition),
 2919         disk_id,
 2920     )
 2921     return partition_info
 2922 
 2923 
 2924 def erase_disk_partitions(
 2925     service_instance, host_ref, disk_id, hostname=None, storage_system=None
 2926 ):
 2927     """
 2928     Erases all partitions on a disk
 2929 
 2930     in a vcenter filtered by their names and/or datacenter, cluster membership
 2931 
 2932     service_instance
 2933         The Service Instance Object from which to obtain all information
 2934 
 2935     host_ref
 2936         The reference of the ESXi host containing the disk
 2937 
 2938     disk_id
 2939         The canonical name of the disk whose partitions are to be removed
 2940 
 2941     hostname
 2942         The ESXi hostname. Default is None.
 2943 
 2944     storage_system
 2945         The ESXi host's storage system. Default is None.
 2946     """
 2947 
 2948     if not hostname:
 2949         hostname = get_managed_object_name(host_ref)
 2950     if not storage_system:
 2951         storage_system = get_storage_system(service_instance, host_ref, hostname)
 2952 
 2953     traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 2954         path="configManager.storageSystem", type=vim.HostSystem, skip=False
 2955     )
 2956     results = get_mors_with_properties(
 2957         service_instance,
 2958         vim.HostStorageSystem,
 2959         ["storageDeviceInfo.scsiLun"],
 2960         container_ref=host_ref,
 2961         traversal_spec=traversal_spec,
 2962     )
 2963     if not results:
 2964         raise salt.exceptions.VMwareObjectRetrievalError(
 2965             "Host's '{}' devices were not retrieved".format(hostname)
 2966         )
 2967     log.trace(
 2968         "[%s] Retrieved %s devices: %s",
 2969         hostname,
 2970         len(results[0].get("storageDeviceInfo.scsiLun", [])),
 2971         ", ".join(
 2972             [l.canonicalName for l in results[0].get("storageDeviceInfo.scsiLun", [])]
 2973         ),
 2974     )
 2975     disks = [
 2976         l
 2977         for l in results[0].get("storageDeviceInfo.scsiLun", [])
 2978         if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id
 2979     ]
 2980     if not disks:
 2981         raise salt.exceptions.VMwareObjectRetrievalError(
 2982             "Disk '{}' was not found in host '{}'" "".format(disk_id, hostname)
 2983         )
 2984     log.trace("[%s] device_path = %s", hostname, disks[0].devicePath)
 2985     # Erase the partitions by setting an empty partition spec
 2986     try:
 2987         storage_system.UpdateDiskPartitions(
 2988             disks[0].devicePath, vim.HostDiskPartitionSpec()
 2989         )
 2990     except vim.fault.NoPermission as exc:
 2991         log.exception(exc)
 2992         raise salt.exceptions.VMwareApiError(
 2993             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 2994         )
 2995     except vim.fault.VimFault as exc:
 2996         log.exception(exc)
 2997         raise salt.exceptions.VMwareApiError(exc.msg)
 2998     except vmodl.RuntimeFault as exc:
 2999         log.exception(exc)
 3000         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3001     log.trace("[%s] Erased partitions on disk '%s'", hostname, disk_id)
 3002 
 3003 
 3004 def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False):
 3005     """
 3006     Returns a list of vim.VsanHostDiskMapping objects representing disks
 3007     in a ESXi host, filtered by their cannonical names.
 3008 
 3009     host_ref
 3010         The vim.HostSystem object representing the host that contains the
 3011         requested disks.
 3012 
 3013     cache_disk_ids
 3014         The list of cannonical names of the cache disks to be retrieved. The
 3015         canonical name of the cache disk is enough to identify the disk group
 3016         because it is guaranteed to have one and only one cache disk.
 3017         Default is None.
 3018 
 3019     get_all_disk_groups
 3020         Specifies whether to retrieve all disks groups in the host.
 3021         Default value is False.
 3022     """
 3023     hostname = get_managed_object_name(host_ref)
 3024     if get_all_disk_groups:
 3025         log.trace("Retrieving all disk groups on host '%s'", hostname)
 3026     else:
 3027         log.trace(
 3028             "Retrieving disk groups from host '%s', with cache disk " "ids : (%s)",
 3029             hostname,
 3030             cache_disk_ids,
 3031         )
 3032         if not cache_disk_ids:
 3033             return []
 3034     try:
 3035         vsan_host_config = host_ref.config.vsanHostConfig
 3036     except vim.fault.NoPermission as exc:
 3037         log.exception(exc)
 3038         raise salt.exceptions.VMwareApiError(
 3039             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3040         )
 3041     except vim.fault.VimFault as exc:
 3042         log.exception(exc)
 3043         raise salt.exceptions.VMwareApiError(exc.msg)
 3044     except vmodl.RuntimeFault as exc:
 3045         log.exception(exc)
 3046         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3047     if not vsan_host_config:
 3048         raise salt.exceptions.VMwareObjectRetrievalError(
 3049             "No host config found on host '{}'".format(hostname)
 3050         )
 3051     vsan_storage_info = vsan_host_config.storageInfo
 3052     if not vsan_storage_info:
 3053         raise salt.exceptions.VMwareObjectRetrievalError(
 3054             "No vsan storage info found on host '{}'".format(hostname)
 3055         )
 3056     vsan_disk_mappings = vsan_storage_info.diskMapping
 3057     if not vsan_disk_mappings:
 3058         return []
 3059     disk_groups = [
 3060         dm
 3061         for dm in vsan_disk_mappings
 3062         if (get_all_disk_groups or (dm.ssd.canonicalName in cache_disk_ids))
 3063     ]
 3064     log.trace(
 3065         "Retrieved disk groups on host '%s', with cache disk ids : %s",
 3066         hostname,
 3067         [d.ssd.canonicalName for d in disk_groups],
 3068     )
 3069     return disk_groups
 3070 
 3071 
 3072 def _check_disks_in_diskgroup(disk_group, cache_disk_id, capacity_disk_ids):
 3073     """
 3074     Checks that the disks in a disk group are as expected and raises
 3075     CheckError exceptions if the check fails
 3076     """
 3077     if not disk_group.ssd.canonicalName == cache_disk_id:
 3078         raise salt.exceptions.ArgumentValueError(
 3079             "Incorrect diskgroup cache disk; got id: '{}'; expected id: "
 3080             "'{}'".format(disk_group.ssd.canonicalName, cache_disk_id)
 3081         )
 3082     non_ssd_disks = [d.canonicalName for d in disk_group.nonSsd]
 3083     if sorted(non_ssd_disks) != sorted(capacity_disk_ids):
 3084         raise salt.exceptions.ArgumentValueError(
 3085             "Incorrect capacity disks; got ids: '{}'; expected ids: '{}'"
 3086             "".format(sorted(non_ssd_disks), sorted(capacity_disk_ids))
 3087         )
 3088     log.trace("Checked disks in diskgroup with cache disk id '%s'", cache_disk_id)
 3089     return True
 3090 
 3091 
 3092 # TODO Support host caches on multiple datastores
 3093 def get_host_cache(host_ref, host_cache_manager=None):
 3094     """
 3095     Returns a vim.HostScsiDisk if the host cache is configured on the specified
 3096     host, other wise returns None
 3097 
 3098     host_ref
 3099         The vim.HostSystem object representing the host that contains the
 3100         requested disks.
 3101 
 3102     host_cache_manager
 3103         The vim.HostCacheConfigurationManager object representing the cache
 3104         configuration manager on the specified host. Default is None. If None,
 3105         it will be retrieved in the method
 3106     """
 3107     hostname = get_managed_object_name(host_ref)
 3108     service_instance = get_service_instance_from_managed_object(host_ref)
 3109     log.trace("Retrieving the host cache on host '%s'", hostname)
 3110     if not host_cache_manager:
 3111         traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 3112             path="configManager.cacheConfigurationManager",
 3113             type=vim.HostSystem,
 3114             skip=False,
 3115         )
 3116         results = get_mors_with_properties(
 3117             service_instance,
 3118             vim.HostCacheConfigurationManager,
 3119             ["cacheConfigurationInfo"],
 3120             container_ref=host_ref,
 3121             traversal_spec=traversal_spec,
 3122         )
 3123         if not results or not results[0].get("cacheConfigurationInfo"):
 3124             log.trace("Host '%s' has no host cache", hostname)
 3125             return None
 3126         return results[0]["cacheConfigurationInfo"][0]
 3127     else:
 3128         results = get_properties_of_managed_object(
 3129             host_cache_manager, ["cacheConfigurationInfo"]
 3130         )
 3131         if not results:
 3132             log.trace("Host '%s' has no host cache", hostname)
 3133             return None
 3134         return results["cacheConfigurationInfo"][0]
 3135 
 3136 
 3137 # TODO Support host caches on multiple datastores
 3138 def configure_host_cache(
 3139     host_ref, datastore_ref, swap_size_MiB, host_cache_manager=None
 3140 ):
 3141     """
 3142     Configures the host cahe of the specified host
 3143 
 3144     host_ref
 3145         The vim.HostSystem object representing the host that contains the
 3146         requested disks.
 3147 
 3148     datastore_ref
 3149         The vim.Datastore opject representing the datastore the host cache will
 3150         be configured on.
 3151 
 3152     swap_size_MiB
 3153         The size in Mibibytes of the swap.
 3154 
 3155     host_cache_manager
 3156         The vim.HostCacheConfigurationManager object representing the cache
 3157         configuration manager on the specified host. Default is None. If None,
 3158         it will be retrieved in the method
 3159     """
 3160     hostname = get_managed_object_name(host_ref)
 3161     if not host_cache_manager:
 3162         props = get_properties_of_managed_object(
 3163             host_ref, ["configManager.cacheConfigurationManager"]
 3164         )
 3165         if not props.get("configManager.cacheConfigurationManager"):
 3166             raise salt.exceptions.VMwareObjectRetrievalError(
 3167                 "Host '{}' has no host cache".format(hostname)
 3168             )
 3169         host_cache_manager = props["configManager.cacheConfigurationManager"]
 3170     log.trace(
 3171         "Configuring the host cache on host '%s', datastore '%s', " "swap size=%s MiB",
 3172         hostname,
 3173         datastore_ref.name,
 3174         swap_size_MiB,
 3175     )
 3176 
 3177     spec = vim.HostCacheConfigurationSpec(
 3178         datastore=datastore_ref, swapSize=swap_size_MiB
 3179     )
 3180     log.trace("host_cache_spec=%s", spec)
 3181     try:
 3182         task = host_cache_manager.ConfigureHostCache_Task(spec)
 3183     except vim.fault.NoPermission as exc:
 3184         log.exception(exc)
 3185         raise salt.exceptions.VMwareApiError(
 3186             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3187         )
 3188     except vim.fault.VimFault as exc:
 3189         log.exception(exc)
 3190         raise salt.exceptions.VMwareApiError(exc.msg)
 3191     except vmodl.RuntimeFault as exc:
 3192         log.exception(exc)
 3193         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3194     wait_for_task(task, hostname, "HostCacheConfigurationTask")
 3195     log.trace("Configured host cache on host '%s'", hostname)
 3196     return True
 3197 
 3198 
 3199 def list_hosts(service_instance):
 3200     """
 3201     Returns a list of hosts associated with a given service instance.
 3202 
 3203     service_instance
 3204         The Service Instance Object from which to obtain hosts.
 3205     """
 3206     return list_objects(service_instance, vim.HostSystem)
 3207 
 3208 
 3209 def get_resource_pools(
 3210     service_instance,
 3211     resource_pool_names,
 3212     datacenter_name=None,
 3213     get_all_resource_pools=False,
 3214 ):
 3215     """
 3216     Retrieves resource pool objects
 3217 
 3218     service_instance
 3219         The service instance object to query the vCenter
 3220 
 3221     resource_pool_names
 3222         Resource pool names
 3223 
 3224     datacenter_name
 3225         Name of the datacenter where the resource pool is available
 3226 
 3227     get_all_resource_pools
 3228         Boolean
 3229 
 3230     return
 3231         Resourcepool managed object reference
 3232     """
 3233 
 3234     properties = ["name"]
 3235     if not resource_pool_names:
 3236         resource_pool_names = []
 3237     if datacenter_name:
 3238         container_ref = get_datacenter(service_instance, datacenter_name)
 3239     else:
 3240         container_ref = get_root_folder(service_instance)
 3241 
 3242     resource_pools = get_mors_with_properties(
 3243         service_instance,
 3244         vim.ResourcePool,
 3245         container_ref=container_ref,
 3246         property_list=properties,
 3247     )
 3248 
 3249     selected_pools = []
 3250     for pool in resource_pools:
 3251         if get_all_resource_pools or (pool["name"] in resource_pool_names):
 3252             selected_pools.append(pool["object"])
 3253     if not selected_pools:
 3254         raise salt.exceptions.VMwareObjectRetrievalError(
 3255             "The resource pools with properties "
 3256             "names={} get_all={} could not be found".format(
 3257                 selected_pools, get_all_resource_pools
 3258             )
 3259         )
 3260 
 3261     return selected_pools
 3262 
 3263 
 3264 def list_resourcepools(service_instance):
 3265     """
 3266     Returns a list of resource pools associated with a given service instance.
 3267 
 3268     service_instance
 3269         The Service Instance Object from which to obtain resource pools.
 3270     """
 3271     return list_objects(service_instance, vim.ResourcePool)
 3272 
 3273 
 3274 def list_networks(service_instance):
 3275     """
 3276     Returns a list of networks associated with a given service instance.
 3277 
 3278     service_instance
 3279         The Service Instance Object from which to obtain networks.
 3280     """
 3281     return list_objects(service_instance, vim.Network)
 3282 
 3283 
 3284 def list_vms(service_instance):
 3285     """
 3286     Returns a list of VMs associated with a given service instance.
 3287 
 3288     service_instance
 3289         The Service Instance Object from which to obtain VMs.
 3290     """
 3291     return list_objects(service_instance, vim.VirtualMachine)
 3292 
 3293 
 3294 def list_folders(service_instance):
 3295     """
 3296     Returns a list of folders associated with a given service instance.
 3297 
 3298     service_instance
 3299         The Service Instance Object from which to obtain folders.
 3300     """
 3301     return list_objects(service_instance, vim.Folder)
 3302 
 3303 
 3304 def list_dvs(service_instance):
 3305     """
 3306     Returns a list of distributed virtual switches associated with a given service instance.
 3307 
 3308     service_instance
 3309         The Service Instance Object from which to obtain distributed virtual switches.
 3310     """
 3311     return list_objects(service_instance, vim.DistributedVirtualSwitch)
 3312 
 3313 
 3314 def list_vapps(service_instance):
 3315     """
 3316     Returns a list of vApps associated with a given service instance.
 3317 
 3318     service_instance
 3319         The Service Instance Object from which to obtain vApps.
 3320     """
 3321     return list_objects(service_instance, vim.VirtualApp)
 3322 
 3323 
 3324 def list_portgroups(service_instance):
 3325     """
 3326     Returns a list of distributed virtual portgroups associated with a given service instance.
 3327 
 3328     service_instance
 3329         The Service Instance Object from which to obtain distributed virtual switches.
 3330     """
 3331     return list_objects(service_instance, vim.dvs.DistributedVirtualPortgroup)
 3332 
 3333 
 3334 def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level="debug"):
 3335     """
 3336     Waits for a task to be completed.
 3337 
 3338     task
 3339         The task to wait for.
 3340 
 3341     instance_name
 3342         The name of the ESXi host, vCenter Server, or Virtual Machine that
 3343         the task is being run on.
 3344 
 3345     task_type
 3346         The type of task being performed. Useful information for debugging purposes.
 3347 
 3348     sleep_seconds
 3349         The number of seconds to wait before querying the task again.
 3350         Defaults to ``1`` second.
 3351 
 3352     log_level
 3353         The level at which to log task information. Default is ``debug``,
 3354         but ``info`` is also supported.
 3355     """
 3356     time_counter = 0
 3357     start_time = time.time()
 3358     log.trace("task = %s, task_type = %s", task, task.__class__.__name__)
 3359     try:
 3360         task_info = task.info
 3361     except vim.fault.NoPermission as exc:
 3362         log.exception(exc)
 3363         raise salt.exceptions.VMwareApiError(
 3364             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3365         )
 3366     except vim.fault.FileNotFound as exc:
 3367         log.exception(exc)
 3368         raise salt.exceptions.VMwareFileNotFoundError(exc.msg)
 3369     except vim.fault.VimFault as exc:
 3370         log.exception(exc)
 3371         raise salt.exceptions.VMwareApiError(exc.msg)
 3372     except vmodl.RuntimeFault as exc:
 3373         log.exception(exc)
 3374         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3375     while task_info.state == "running" or task_info.state == "queued":
 3376         if time_counter % sleep_seconds == 0:
 3377             msg = "[ {} ] Waiting for {} task to finish [{} s]".format(
 3378                 instance_name, task_type, time_counter
 3379             )
 3380             if log_level == "info":
 3381                 log.info(msg)
 3382             else:
 3383                 log.debug(msg)
 3384         time.sleep(1.0 - ((time.time() - start_time) % 1.0))
 3385         time_counter += 1
 3386         try:
 3387             task_info = task.info
 3388         except vim.fault.NoPermission as exc:
 3389             log.exception(exc)
 3390             raise salt.exceptions.VMwareApiError(
 3391                 "Not enough permissions. Required privilege: "
 3392                 "{}".format(exc.privilegeId)
 3393             )
 3394         except vim.fault.FileNotFound as exc:
 3395             log.exception(exc)
 3396             raise salt.exceptions.VMwareFileNotFoundError(exc.msg)
 3397         except vim.fault.VimFault as exc:
 3398             log.exception(exc)
 3399             raise salt.exceptions.VMwareApiError(exc.msg)
 3400         except vmodl.RuntimeFault as exc:
 3401             log.exception(exc)
 3402             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3403     if task_info.state == "success":
 3404         msg = "[ {} ] Successfully completed {} task in {} seconds".format(
 3405             instance_name, task_type, time_counter
 3406         )
 3407         if log_level == "info":
 3408             log.info(msg)
 3409         else:
 3410             log.debug(msg)
 3411         # task is in a successful state
 3412         return task_info.result
 3413     else:
 3414         # task is in an error state
 3415         try:
 3416             raise task_info.error
 3417         except vim.fault.NoPermission as exc:
 3418             log.exception(exc)
 3419             raise salt.exceptions.VMwareApiError(
 3420                 "Not enough permissions. Required privilege: "
 3421                 "{}".format(exc.privilegeId)
 3422             )
 3423         except vim.fault.FileNotFound as exc:
 3424             log.exception(exc)
 3425             raise salt.exceptions.VMwareFileNotFoundError(exc.msg)
 3426         except vim.fault.VimFault as exc:
 3427             log.exception(exc)
 3428             raise salt.exceptions.VMwareApiError(exc.msg)
 3429         except vmodl.fault.SystemError as exc:
 3430             log.exception(exc)
 3431             raise salt.exceptions.VMwareSystemError(exc.msg)
 3432         except vmodl.fault.InvalidArgument as exc:
 3433             log.exception(exc)
 3434             exc_message = exc.msg
 3435             if exc.faultMessage:
 3436                 exc_message = "{} ({})".format(exc_message, exc.faultMessage[0].message)
 3437             raise salt.exceptions.VMwareApiError(exc_message)
 3438 
 3439 
 3440 def get_vm_by_property(
 3441     service_instance,
 3442     name,
 3443     datacenter=None,
 3444     vm_properties=None,
 3445     traversal_spec=None,
 3446     parent_ref=None,
 3447 ):
 3448     """
 3449     Get virtual machine properties based on the traversal specs and properties list,
 3450     returns Virtual Machine object with properties.
 3451 
 3452     service_instance
 3453         Service instance object to access vCenter
 3454 
 3455     name
 3456         Name of the virtual machine.
 3457 
 3458     datacenter
 3459         Datacenter name
 3460 
 3461     vm_properties
 3462         List of vm properties.
 3463 
 3464     traversal_spec
 3465         Traversal Spec object(s) for searching.
 3466 
 3467     parent_ref
 3468         Container Reference object for searching under a given object.
 3469     """
 3470     if datacenter and not parent_ref:
 3471         parent_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
 3472     if not vm_properties:
 3473         vm_properties = [
 3474             "name",
 3475             "config.hardware.device",
 3476             "summary.storage.committed",
 3477             "summary.storage.uncommitted",
 3478             "summary.storage.unshared",
 3479             "layoutEx.file",
 3480             "config.guestFullName",
 3481             "config.guestId",
 3482             "guest.net",
 3483             "config.hardware.memoryMB",
 3484             "config.hardware.numCPU",
 3485             "config.files.vmPathName",
 3486             "summary.runtime.powerState",
 3487             "guest.toolsStatus",
 3488         ]
 3489     vm_list = salt.utils.vmware.get_mors_with_properties(
 3490         service_instance,
 3491         vim.VirtualMachine,
 3492         vm_properties,
 3493         container_ref=parent_ref,
 3494         traversal_spec=traversal_spec,
 3495     )
 3496     vm_formatted = [vm for vm in vm_list if vm["name"] == name]
 3497     if not vm_formatted:
 3498         raise salt.exceptions.VMwareObjectRetrievalError(
 3499             "The virtual machine was not found."
 3500         )
 3501     elif len(vm_formatted) > 1:
 3502         raise salt.exceptions.VMwareMultipleObjectsError(
 3503             " ".join(
 3504                 [
 3505                     "Multiple virtual machines were found with the"
 3506                     "same name, please specify a container."
 3507                 ]
 3508             )
 3509         )
 3510     return vm_formatted[0]
 3511 
 3512 
 3513 def get_folder(service_instance, datacenter, placement, base_vm_name=None):
 3514     """
 3515     Returns a Folder Object
 3516 
 3517     service_instance
 3518         Service instance object
 3519 
 3520     datacenter
 3521         Name of the datacenter
 3522 
 3523     placement
 3524         Placement dictionary
 3525 
 3526     base_vm_name
 3527         Existing virtual machine name (for cloning)
 3528     """
 3529     log.trace("Retrieving folder information")
 3530     if base_vm_name:
 3531         vm_object = get_vm_by_property(
 3532             service_instance, base_vm_name, vm_properties=["name"]
 3533         )
 3534         vm_props = salt.utils.vmware.get_properties_of_managed_object(
 3535             vm_object, properties=["parent"]
 3536         )
 3537         if "parent" in vm_props:
 3538             folder_object = vm_props["parent"]
 3539         else:
 3540             raise salt.exceptions.VMwareObjectRetrievalError(
 3541                 " ".join(["The virtual machine parent", "object is not defined"])
 3542             )
 3543     elif "folder" in placement:
 3544         folder_objects = salt.utils.vmware.get_folders(
 3545             service_instance, [placement["folder"]], datacenter
 3546         )
 3547         if len(folder_objects) > 1:
 3548             raise salt.exceptions.VMwareMultipleObjectsError(
 3549                 " ".join(
 3550                     [
 3551                         "Multiple instances are available of the",
 3552                         "specified folder {}".format(placement["folder"]),
 3553                     ]
 3554                 )
 3555             )
 3556         folder_object = folder_objects[0]
 3557     elif datacenter:
 3558         datacenter_object = salt.utils.vmware.get_datacenter(
 3559             service_instance, datacenter
 3560         )
 3561         dc_props = salt.utils.vmware.get_properties_of_managed_object(
 3562             datacenter_object, properties=["vmFolder"]
 3563         )
 3564         if "vmFolder" in dc_props:
 3565             folder_object = dc_props["vmFolder"]
 3566         else:
 3567             raise salt.exceptions.VMwareObjectRetrievalError(
 3568                 "The datacenter vm folder object is not defined"
 3569             )
 3570     return folder_object
 3571 
 3572 
 3573 def get_placement(service_instance, datacenter, placement=None):
 3574     """
 3575     To create a virtual machine a resource pool needs to be supplied, we would like to use the strictest as possible.
 3576 
 3577     datacenter
 3578         Name of the datacenter
 3579 
 3580     placement
 3581         Dictionary with the placement info, cluster, host resource pool name
 3582 
 3583     return
 3584         Resource pool, cluster and host object if any applies
 3585     """
 3586     log.trace("Retrieving placement information")
 3587     resourcepool_object, placement_object = None, None
 3588     if "host" in placement:
 3589         host_objects = get_hosts(
 3590             service_instance, datacenter_name=datacenter, host_names=[placement["host"]]
 3591         )
 3592         if not host_objects:
 3593             raise salt.exceptions.VMwareObjectRetrievalError(
 3594                 " ".join(
 3595                     [
 3596                         "The specified host",
 3597                         "{} cannot be found.".format(placement["host"]),
 3598                     ]
 3599                 )
 3600             )
 3601         try:
 3602             host_props = get_properties_of_managed_object(
 3603                 host_objects[0], properties=["resourcePool"]
 3604             )
 3605             resourcepool_object = host_props["resourcePool"]
 3606         except vmodl.query.InvalidProperty:
 3607             traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
 3608                 path="parent",
 3609                 skip=True,
 3610                 type=vim.HostSystem,
 3611                 selectSet=[
 3612                     vmodl.query.PropertyCollector.TraversalSpec(
 3613                         path="resourcePool", skip=False, type=vim.ClusterComputeResource
 3614                     )
 3615                 ],
 3616             )
 3617             resourcepools = get_mors_with_properties(
 3618                 service_instance,
 3619                 vim.ResourcePool,
 3620                 container_ref=host_objects[0],
 3621                 property_list=["name"],
 3622                 traversal_spec=traversal_spec,
 3623             )
 3624             if resourcepools:
 3625                 resourcepool_object = resourcepools[0]["object"]
 3626             else:
 3627                 raise salt.exceptions.VMwareObjectRetrievalError(
 3628                     "The resource pool of host {} cannot be found.".format(
 3629                         placement["host"]
 3630                     )
 3631                 )
 3632         placement_object = host_objects[0]
 3633     elif "resourcepool" in placement:
 3634         resourcepool_objects = get_resource_pools(
 3635             service_instance, [placement["resourcepool"]], datacenter_name=datacenter
 3636         )
 3637         if len(resourcepool_objects) > 1:
 3638             raise salt.exceptions.VMwareMultipleObjectsError(
 3639                 " ".join(
 3640                     [
 3641                         "Multiple instances are available of the",
 3642                         "specified host {}.".format(placement["host"]),
 3643                     ]
 3644                 )
 3645             )
 3646         resourcepool_object = resourcepool_objects[0]
 3647         res_props = get_properties_of_managed_object(
 3648             resourcepool_object, properties=["parent"]
 3649         )
 3650         if "parent" in res_props:
 3651             placement_object = res_props["parent"]
 3652         else:
 3653             raise salt.exceptions.VMwareObjectRetrievalError(
 3654                 " ".join(["The resource pool's parent", "object is not defined"])
 3655             )
 3656     elif "cluster" in placement:
 3657         datacenter_object = get_datacenter(service_instance, datacenter)
 3658         cluster_object = get_cluster(datacenter_object, placement["cluster"])
 3659         clus_props = get_properties_of_managed_object(
 3660             cluster_object, properties=["resourcePool"]
 3661         )
 3662         if "resourcePool" in clus_props:
 3663             resourcepool_object = clus_props["resourcePool"]
 3664         else:
 3665             raise salt.exceptions.VMwareObjectRetrievalError(
 3666                 " ".join(["The cluster's resource pool", "object is not defined"])
 3667             )
 3668         placement_object = cluster_object
 3669     else:
 3670         # We are checking the schema for this object, this exception should never be raised
 3671         raise salt.exceptions.VMwareObjectRetrievalError(
 3672             " ".join(["Placement is not defined."])
 3673         )
 3674     return (resourcepool_object, placement_object)
 3675 
 3676 
 3677 def convert_to_kb(unit, size):
 3678     """
 3679     Converts the given size to KB based on the unit, returns a long integer.
 3680 
 3681     unit
 3682         Unit of the size eg. GB; Note: to VMware a GB is the same as GiB = 1024MiB
 3683     size
 3684         Number which represents the size
 3685     """
 3686     if unit.lower() == "gb":
 3687         # vCenter needs long value
 3688         target_size = int(size * 1024 * 1024)
 3689     elif unit.lower() == "mb":
 3690         target_size = int(size * 1024)
 3691     elif unit.lower() == "kb":
 3692         target_size = int(size)
 3693     else:
 3694         raise salt.exceptions.ArgumentValueError("The unit is not specified")
 3695     return {"size": target_size, "unit": "KB"}
 3696 
 3697 
 3698 def power_cycle_vm(virtual_machine, action="on"):
 3699     """
 3700     Powers on/off a virtual machine specified by its name.
 3701 
 3702     virtual_machine
 3703         vim.VirtualMachine object to power on/off virtual machine
 3704 
 3705     action
 3706         Operation option to power on/off the machine
 3707     """
 3708     if action == "on":
 3709         try:
 3710             task = virtual_machine.PowerOn()
 3711             task_name = "power on"
 3712         except vim.fault.NoPermission as exc:
 3713             log.exception(exc)
 3714             raise salt.exceptions.VMwareApiError(
 3715                 "Not enough permissions. Required privilege: "
 3716                 "{}".format(exc.privilegeId)
 3717             )
 3718         except vim.fault.VimFault as exc:
 3719             log.exception(exc)
 3720             raise salt.exceptions.VMwareApiError(exc.msg)
 3721         except vmodl.RuntimeFault as exc:
 3722             log.exception(exc)
 3723             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3724     elif action == "off":
 3725         try:
 3726             task = virtual_machine.PowerOff()
 3727             task_name = "power off"
 3728         except vim.fault.NoPermission as exc:
 3729             log.exception(exc)
 3730             raise salt.exceptions.VMwareApiError(
 3731                 "Not enough permissions. Required privilege: "
 3732                 "{}".format(exc.privilegeId)
 3733             )
 3734         except vim.fault.VimFault as exc:
 3735             log.exception(exc)
 3736             raise salt.exceptions.VMwareApiError(exc.msg)
 3737         except vmodl.RuntimeFault as exc:
 3738             log.exception(exc)
 3739             raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3740     else:
 3741         raise salt.exceptions.ArgumentValueError("The given action is not supported")
 3742     try:
 3743         wait_for_task(task, get_managed_object_name(virtual_machine), task_name)
 3744     except salt.exceptions.VMwareFileNotFoundError as exc:
 3745         raise salt.exceptions.VMwarePowerOnError(
 3746             " ".join(
 3747                 [
 3748                     "An error occurred during power",
 3749                     "operation, a file was not found: {}".format(exc),
 3750                 ]
 3751             )
 3752         )
 3753     return virtual_machine
 3754 
 3755 
 3756 def create_vm(
 3757     vm_name, vm_config_spec, folder_object, resourcepool_object, host_object=None
 3758 ):
 3759     """
 3760     Creates virtual machine from config spec
 3761 
 3762     vm_name
 3763         Virtual machine name to be created
 3764 
 3765     vm_config_spec
 3766         Virtual Machine Config Spec object
 3767 
 3768     folder_object
 3769         vm Folder managed object reference
 3770 
 3771     resourcepool_object
 3772         Resource pool object where the machine will be created
 3773 
 3774     host_object
 3775         Host object where the machine will ne placed (optional)
 3776 
 3777     return
 3778         Virtual Machine managed object reference
 3779     """
 3780     try:
 3781         if host_object and isinstance(host_object, vim.HostSystem):
 3782             task = folder_object.CreateVM_Task(
 3783                 vm_config_spec, pool=resourcepool_object, host=host_object
 3784             )
 3785         else:
 3786             task = folder_object.CreateVM_Task(vm_config_spec, pool=resourcepool_object)
 3787     except vim.fault.NoPermission as exc:
 3788         log.exception(exc)
 3789         raise salt.exceptions.VMwareApiError(
 3790             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3791         )
 3792     except vim.fault.VimFault as exc:
 3793         log.exception(exc)
 3794         raise salt.exceptions.VMwareApiError(exc.msg)
 3795     except vmodl.RuntimeFault as exc:
 3796         log.exception(exc)
 3797         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3798     vm_object = wait_for_task(task, vm_name, "CreateVM Task", 10, "info")
 3799     return vm_object
 3800 
 3801 
 3802 def register_vm(datacenter, name, vmx_path, resourcepool_object, host_object=None):
 3803     """
 3804     Registers a virtual machine to the inventory with the given vmx file, on success
 3805     it returns the vim.VirtualMachine managed object reference
 3806 
 3807     datacenter
 3808         Datacenter object of the virtual machine, vim.Datacenter object
 3809 
 3810     name
 3811         Name of the virtual machine
 3812 
 3813     vmx_path:
 3814         Full path to the vmx file, datastore name should be included
 3815 
 3816     resourcepool
 3817         Placement resource pool of the virtual machine, vim.ResourcePool object
 3818 
 3819     host
 3820         Placement host of the virtual machine, vim.HostSystem object
 3821     """
 3822     try:
 3823         if host_object:
 3824             task = datacenter.vmFolder.RegisterVM_Task(
 3825                 path=vmx_path,
 3826                 name=name,
 3827                 asTemplate=False,
 3828                 host=host_object,
 3829                 pool=resourcepool_object,
 3830             )
 3831         else:
 3832             task = datacenter.vmFolder.RegisterVM_Task(
 3833                 path=vmx_path, name=name, asTemplate=False, pool=resourcepool_object
 3834             )
 3835     except vim.fault.NoPermission as exc:
 3836         log.exception(exc)
 3837         raise salt.exceptions.VMwareApiError(
 3838             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3839         )
 3840     except vim.fault.VimFault as exc:
 3841         log.exception(exc)
 3842         raise salt.exceptions.VMwareApiError(exc.msg)
 3843     except vmodl.RuntimeFault as exc:
 3844         log.exception(exc)
 3845         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3846     try:
 3847         vm_ref = wait_for_task(task, name, "RegisterVM Task")
 3848     except salt.exceptions.VMwareFileNotFoundError as exc:
 3849         raise salt.exceptions.VMwareVmRegisterError(
 3850             "An error occurred during registration operation, the "
 3851             "configuration file was not found: {}".format(exc)
 3852         )
 3853     return vm_ref
 3854 
 3855 
 3856 def update_vm(vm_ref, vm_config_spec):
 3857     """
 3858     Updates the virtual machine configuration with the given object
 3859 
 3860     vm_ref
 3861         Virtual machine managed object reference
 3862 
 3863     vm_config_spec
 3864         Virtual machine config spec object to update
 3865     """
 3866     vm_name = get_managed_object_name(vm_ref)
 3867     log.trace("Updating vm '%s'", vm_name)
 3868     try:
 3869         task = vm_ref.ReconfigVM_Task(vm_config_spec)
 3870     except vim.fault.NoPermission as exc:
 3871         log.exception(exc)
 3872         raise salt.exceptions.VMwareApiError(
 3873             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3874         )
 3875     except vim.fault.VimFault as exc:
 3876         log.exception(exc)
 3877         raise salt.exceptions.VMwareApiError(exc.msg)
 3878     except vmodl.RuntimeFault as exc:
 3879         log.exception(exc)
 3880         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3881     vm_ref = wait_for_task(task, vm_name, "ReconfigureVM Task")
 3882     return vm_ref
 3883 
 3884 
 3885 def delete_vm(vm_ref):
 3886     """
 3887     Destroys the virtual machine
 3888 
 3889     vm_ref
 3890         Managed object reference of a virtual machine object
 3891     """
 3892     vm_name = get_managed_object_name(vm_ref)
 3893     log.trace("Destroying vm '%s'", vm_name)
 3894     try:
 3895         task = vm_ref.Destroy_Task()
 3896     except vim.fault.NoPermission as exc:
 3897         log.exception(exc)
 3898         raise salt.exceptions.VMwareApiError(
 3899             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3900         )
 3901     except vim.fault.VimFault as exc:
 3902         log.exception(exc)
 3903         raise salt.exceptions.VMwareApiError(exc.msg)
 3904     except vmodl.RuntimeFault as exc:
 3905         log.exception(exc)
 3906         raise salt.exceptions.VMwareRuntimeError(exc.msg)
 3907     wait_for_task(task, vm_name, "Destroy Task")
 3908 
 3909 
 3910 def unregister_vm(vm_ref):
 3911     """
 3912     Destroys the virtual machine
 3913 
 3914     vm_ref
 3915         Managed object reference of a virtual machine object
 3916     """
 3917     vm_name = get_managed_object_name(vm_ref)
 3918     log.trace("Destroying vm '%s'", vm_name)
 3919     try:
 3920         vm_ref.UnregisterVM()
 3921     except vim.fault.NoPermission as exc:
 3922         log.exception(exc)
 3923         raise salt.exceptions.VMwareApiError(
 3924             "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId)
 3925         )
 3926     except vim.fault.VimFault as exc:
 3927         raise salt.exceptions.VMwareApiError(exc.msg)
 3928     except vmodl.RuntimeFault as exc:
 3929         raise salt.exceptions.VMwareRuntimeError(exc.msg)