Add a host system to the specified cluster or datacenter in this VMware environment

    .. note::

        To use this function, you need to specify ``esxi_host_user`` and
        ``esxi_host_password`` under your provider configuration set up at
        ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/vmware.conf``:

        .. code-block:: yaml

            vcenter01:
              driver: vmware
              user: 'DOMAIN\\user'
              password: 'verybadpass'
              url: 'vcenter01.domain.com'

              # Required when adding a host system
              esxi_host_user: 'root'
              esxi_host_password: 'myhostpassword'
              # Optional fields that can be specified when adding a host system
              esxi_host_ssl_thumbprint: '12:A3:45:B6:CD:7E:F8:90:A1:BC:23:45:D6:78:9E:FA:01:2B:34:CD'

        The SSL thumbprint of the host system can be optionally specified by setting
        ``esxi_host_ssl_thumbprint`` under your provider configuration. To get the SSL
        thumbprint of the host system, execute the following command from a remote
        server:

        .. code-block:: bash

            echo -n | openssl s_client -connect <YOUR-HOSTSYSTEM-DNS/IP>:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1

    CLI Example:

    .. code-block:: bash

        salt-cloud -f add_host my-vmware-config host="myHostSystemName" cluster="myClusterName"
        salt-cloud -f add_host my-vmware-config host="myHostSystemName" datacenter="myDatacenterName"

def add_host(kwargs=None, call=None):
    '''
    Add a host system to the specified cluster or datacenter in this VMware environment

    .. note::

        To use this function, you need to specify ``esxi_host_user`` and
        ``esxi_host_password`` under your provider configuration set up at
        ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/vmware.conf``:

        .. code-block:: yaml

            vcenter01:
              driver: vmware
              user: 'DOMAIN\\user'
              password: 'verybadpass'
              url: 'vcenter01.domain.com'

              # Required when adding a host system
              esxi_host_user: 'root'
              esxi_host_password: 'myhostpassword'
              # Optional fields that can be specified when adding a host system
              esxi_host_ssl_thumbprint: '12:A3:45:B6:CD:7E:F8:90:A1:BC:23:45:D6:78:9E:FA:01:2B:34:CD'

        The SSL thumbprint of the host system can be optionally specified by setting
        ``esxi_host_ssl_thumbprint`` under your provider configuration. To get the SSL
        thumbprint of the host system, execute the following command from a remote
        server:

        .. code-block:: bash

            echo -n | openssl s_client -connect <YOUR-HOSTSYSTEM-DNS/IP>:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1

    CLI Example:

    .. code-block:: bash

        salt-cloud -f add_host my-vmware-config host="myHostSystemName" cluster="myClusterName"
        salt-cloud -f add_host my-vmware-config host="myHostSystemName" datacenter="myDatacenterName"
    '''
    if call != 'function':
        raise SaltCloudSystemExit(
            'The add_host function must be called with '
            '-f or --function.'
        )

    host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None
    cluster_name = kwargs.get('cluster') if kwargs and 'cluster' in kwargs else None
    datacenter_name = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None

    host_user = config.get_cloud_config_value(
        'esxi_host_user', get_configured_provider(), __opts__, search_global=False
    )
    host_password = config.get_cloud_config_value(
        'esxi_host_password', get_configured_provider(), __opts__, search_global=False
    )
    host_ssl_thumbprint = config.get_cloud_config_value(
        'esxi_host_ssl_thumbprint', get_configured_provider(), __opts__, search_global=False
    )

    if not host_user:
        raise SaltCloudSystemExit(
            'You must specify the ESXi host username in your providers config.'
        )

    if not host_password:
        raise SaltCloudSystemExit(
            'You must specify the ESXi host password in your providers config.'
        )

    if not host_name:
        raise SaltCloudSystemExit(
            'You must specify either the IP or DNS name of the host system.'
        )

    if (cluster_name and datacenter_name) or not(cluster_name or datacenter_name):
        raise SaltCloudSystemExit(
            'You must specify either the cluster name or the datacenter name.'
        )

    # Get the service instance
    si = _get_si()

    if cluster_name:
        cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster_name)
        if not cluster_ref:
            raise SaltCloudSystemExit(
                'Specified cluster does not exist.'
            )

    if datacenter_name:
        datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name)
        if not datacenter_ref:
            raise SaltCloudSystemExit(
                'Specified datacenter does not exist.'
            )

    spec = vim.host.ConnectSpec(
        hostName=host_name,
        userName=host_user,
        password=host_password,
    )

    if host_ssl_thumbprint:
        spec.sslThumbprint = host_ssl_thumbprint
    else:
        log.warning('SSL thumbprint has not been specified in provider configuration')
        # This smells like a not-so-good idea. A plenty of VMWare VCenters
        # do not listen to the default port 443.
        try:
            log.debug('Trying to get the SSL thumbprint directly from the host system')
            p1 = subprocess.Popen(('echo', '-n'),
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
            p2 = subprocess.Popen(('openssl',
                                   's_client',
                                   '-connect',
                                   '{0}:443'.format(host_name)),
                                  stdin=p1.stdout,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
            p3 = subprocess.Popen(('openssl',
                                   'x509',
                                   '-noout',
                                   '-fingerprint',
                                   '-sha1'),
                                  stdin=p2.stdout,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
            out = salt.utils.stringutils.to_str(p3.stdout.read())
            ssl_thumbprint = out.split('=')[-1].strip()
            log.debug('SSL thumbprint received from the host system: %s', ssl_thumbprint)
            spec.sslThumbprint = ssl_thumbprint
        except Exception as exc:
            log.error(
                'Error while trying to get SSL thumbprint of host %s: %s',
                host_name, exc,
                # Show the traceback if the debug logging level is enabled
                exc_info_on_loglevel=logging.DEBUG
            )
            return {host_name: 'failed to add host'}

    try:
        if cluster_name:
            task = cluster_ref.AddHost(spec=spec, asConnected=True)
            ret = 'added host system to cluster {0}'.format(cluster_name)
        if datacenter_name:
            task = datacenter_ref.hostFolder.AddStandaloneHost(spec=spec, addConnected=True)
            ret = 'added host system to datacenter {0}'.format(datacenter_name)
        salt.utils.vmware.wait_for_task(task, host_name, 'add host system', 5, 'info')
    except Exception as exc:
        if isinstance(exc, vim.fault.SSLVerifyFault):
            log.error('Authenticity of the host\'s SSL certificate is not verified')
            log.info('Try again after setting the esxi_host_ssl_thumbprint '
                     'to %s in provider configuration', spec.sslThumbprint)
        log.error(
            'Error while adding host %s: %s',
            host_name, exc,
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG
        )
        return {host_name: 'failed to add host'}

    return {host_name: ret}