mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #29499 from rallytime/esxi-proxy
Initial commit of ESXi Proxy Minion
This commit is contained in:
commit
3ae096b7ac
8 changed files with 4554 additions and 1 deletions
|
@ -10,6 +10,7 @@ Full list of builtin proxy modules
|
|||
:toctree:
|
||||
:template: autosummary.rst.tmpl
|
||||
|
||||
esxi
|
||||
fx2
|
||||
junos
|
||||
rest_sample
|
||||
|
|
6
doc/ref/proxy/all/salt.proxy.esxi.rst
Normal file
6
doc/ref/proxy/all/salt.proxy.esxi.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
===============
|
||||
salt.proxy.esxi
|
||||
===============
|
||||
|
||||
.. automodule:: salt.proxy.esxi
|
||||
:members:
|
93
salt/grains/esxi.py
Normal file
93
salt/grains/esxi.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Generate baseline proxy minion grains for ESXi hosts.
|
||||
|
||||
., versionadded:: 2015.8.4
|
||||
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.exceptions import SaltSystemExit
|
||||
import salt.utils
|
||||
import salt.modules.vsphere
|
||||
|
||||
__proxyenabled__ = ['esxi']
|
||||
__virtualname__ = 'esxi'
|
||||
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
GRAINS_CACHE = {}
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if not salt.utils.is_proxy():
|
||||
return False
|
||||
else:
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def esxi():
|
||||
return _grains()
|
||||
|
||||
|
||||
def kernel():
|
||||
return {'kernel': 'proxy'}
|
||||
|
||||
|
||||
def os():
|
||||
if not GRAINS_CACHE:
|
||||
GRAINS_CACHE.update(_grains())
|
||||
|
||||
try:
|
||||
return {'os': GRAINS_CACHE.get('fullName')}
|
||||
except AttributeError:
|
||||
return {'os': 'Unknown'}
|
||||
|
||||
|
||||
def os_family():
|
||||
return {'os_family': 'proxy'}
|
||||
|
||||
|
||||
def _find_credentials(host):
|
||||
'''
|
||||
Cycle through all the possible credentials and return the first one that
|
||||
works.
|
||||
'''
|
||||
user_names = [__pillar__['proxy'].get('username', 'root')]
|
||||
passwords = __pillar__['proxy']['passwords']
|
||||
for user in user_names:
|
||||
for password in passwords:
|
||||
try:
|
||||
# Try to authenticate with the given user/password combination
|
||||
ret = salt.modules.vsphere.system_info(host=host,
|
||||
username=user,
|
||||
password=password)
|
||||
except SaltSystemExit:
|
||||
# If we can't authenticate, continue on to try the next password.
|
||||
continue
|
||||
# If we have data returned from above, we've successfully authenticated.
|
||||
if ret:
|
||||
return user, password
|
||||
# We've reached the end of the list without successfully authenticating.
|
||||
raise SaltSystemExit('Cannot complete login due to an incorrect user name or password.')
|
||||
|
||||
|
||||
def _grains():
|
||||
'''
|
||||
Get the grains from the proxied device.
|
||||
'''
|
||||
host = __pillar__['proxy']['host']
|
||||
username, password = _find_credentials(host)
|
||||
protocol = __pillar__['proxy'].get('protocol')
|
||||
port = __pillar__['proxy'].get('port')
|
||||
ret = salt.modules.vsphere.system_info(host=host,
|
||||
username=username,
|
||||
password=password,
|
||||
protocol=protocol,
|
||||
port=port)
|
||||
GRAINS_CACHE.update(ret)
|
||||
return GRAINS_CACHE
|
72
salt/modules/esxi.py
Normal file
72
salt/modules/esxi.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Glues the VMware vSphere Execution Module to the VMware ESXi Proxy Minions to the
|
||||
:doc:`esxi proxymodule </ref/proxy/all/salt.proxy.esxi>`.
|
||||
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
Depends: :doc:`vSphere Remote Execution Module (salt.modules.vsphere)
|
||||
</ref/modules/all/salt.modules.vsphere>`
|
||||
|
||||
For documentation on commands that you can direct to an ESXi host via proxy,
|
||||
look in the documentation for :doc:`salt.modules.vsphere
|
||||
</ref/modules/all/salt.modules.vsphere>`.
|
||||
|
||||
This execution module calls through to a function in the ESXi proxy module
|
||||
called ``ch_config``, which looks up the function passed in the ``command``
|
||||
parameter in :doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>`
|
||||
and calls it.
|
||||
|
||||
To execute commands with an ESXi Proxy Minion using the vSphere Execution Module,
|
||||
use the ``esxi.cmd <vsphere-function-name>`` syntax. Both args and kwargs needed
|
||||
for various vsphere execution module functions must be passed through in a kwarg-
|
||||
type manor.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'esxi-proxy' esxi.cmd system_info
|
||||
salt 'exsi-proxy' esxi.cmd get_service_policy service_name='ssh'
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
import salt.utils
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__proxyenabled__ = ['esxi']
|
||||
__virtualname__ = 'esxi'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on proxy
|
||||
'''
|
||||
if salt.utils.is_proxy():
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def cmd(command, *args, **kwargs):
|
||||
proxy_prefix = __opts__['proxy']['proxytype']
|
||||
proxy_cmd = proxy_prefix + '.ch_config'
|
||||
|
||||
host = __pillar__['proxy']['host']
|
||||
username, password = __proxy__[proxy_prefix + '.find_credentials'](host)
|
||||
|
||||
kwargs['host'] = host
|
||||
kwargs['username'] = username
|
||||
kwargs['password'] = password
|
||||
|
||||
protocol = __pillar__['proxy'].get('protocol')
|
||||
if protocol:
|
||||
kwargs['protocol'] = protocol
|
||||
|
||||
port = __pillar__['proxy'].get('port')
|
||||
if port:
|
||||
kwargs['port'] = port
|
||||
|
||||
return __proxy__[proxy_cmd](command, *args, **kwargs)
|
3014
salt/modules/vsphere.py
Normal file
3014
salt/modules/vsphere.py
Normal file
File diff suppressed because it is too large
Load diff
380
salt/proxy/esxi.py
Normal file
380
salt/proxy/esxi.py
Normal file
|
@ -0,0 +1,380 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Proxy Minion interface module for managing VMWare ESXi hosts.
|
||||
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
:depends: pyVmomi
|
||||
|
||||
**Special Note: SaltStack thanks** `Adobe Corporation <http://adobe.com/>`_
|
||||
**for their support in creating this Proxy Minion integration.**
|
||||
|
||||
This proxy minion enables VMware ESXi (hereafter referred to as simply 'ESXi')
|
||||
hosts to be treated individually like a Salt Minion.
|
||||
|
||||
Since the ESXi host may not necessarily run on an OS capable of hosting a
|
||||
Python stack, the ESXi host can't run a Salt Minion directly. Salt's
|
||||
"Proxy Minion" functionality enables you to designate another machine to host
|
||||
a minion process that "proxies" communication from the Salt Master. The master
|
||||
does not know nor care that the target is not a "real" Salt Minion.
|
||||
|
||||
More in-depth conceptual reading on Proxy Minions can be found in the
|
||||
:doc:`Proxy Minion </topics/proxyminion/index>` section of Salt's
|
||||
documentation.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
To use this integration proxy module, please configure the following:
|
||||
|
||||
Pillar
|
||||
------
|
||||
|
||||
Proxy minions get their configuration from Salt's Pillar. Every proxy must
|
||||
have a stanza in Pillar and a reference in the Pillar top-file that matches
|
||||
the ID. At a minimum for communication with the ESXi host, the pillar should
|
||||
look like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
proxy:
|
||||
proxytype: esxi
|
||||
host: <ip or dns name of esxi host>
|
||||
username: <ESXi username>
|
||||
passwords:
|
||||
- first_password
|
||||
- second_password
|
||||
- third_password
|
||||
|
||||
proxytype
|
||||
^^^^^^^^^
|
||||
The ``proxytype`` key and value pair is critical, as it tells Salt which
|
||||
interface to load from the ``proxy`` directory in Salt's install hierarchy,
|
||||
or from ``/srv/salt/_proxy`` on the Salt Master (if you have created your
|
||||
own proxy module, for example). To use this ESXi Proxy Module, set this to
|
||||
``esxi``.
|
||||
|
||||
host
|
||||
^^^^
|
||||
The location, or ip/dns, of the ESXi host. Required.
|
||||
|
||||
username
|
||||
^^^^^^^^
|
||||
The username used to login to the ESXi host, such as ``root``. Required.
|
||||
|
||||
passwords
|
||||
^^^^^^^^^
|
||||
A list of passwords to be used to try and login to the ESXi host. At least
|
||||
one password in this list is required.
|
||||
|
||||
The proxy integration will try the passwords listed in order. It is
|
||||
configured this way so you can have a regular password and the password you
|
||||
may be updating for an ESXi host either via the
|
||||
:doc:`vsphere.update_host_password </ref/modules/all/salt.modules.vsphere>`
|
||||
function or via an :doc:`ESXi state </ref/modules/all/salt.states.esxi>`
|
||||
function. This way, after the password is changed, you should not need to
|
||||
restart the proxy minion--it should just pick up the the new password provided in
|
||||
the list. You can then change pillar at will to move that password to the
|
||||
front and retire the unused ones.
|
||||
|
||||
This also allows you to use any number of potential fallback passwords.
|
||||
|
||||
.. note::
|
||||
|
||||
When a password is changed on the host to one in the list of possible
|
||||
passwords, the further down on the list the password is, the longer
|
||||
individual commands will take to return. This is due to the nature of
|
||||
pyVmomi's login system. We have to wait for the first attempt to fail
|
||||
before trying the next password on the list.
|
||||
|
||||
This scenario is especially true, and even slower, when the proxy
|
||||
minion first starts. If the correct password is not the first password
|
||||
on the list, it may take up to a minute for ``test.ping`` to respond
|
||||
with a ``True`` result. Once the initial authorization is complete, the
|
||||
responses for commands will be faster.
|
||||
|
||||
To avoid these longer waiting periods, SaltStack recommends moving
|
||||
correct password to the top of the list and restarting the proxy minion
|
||||
at your earliest convenience.
|
||||
|
||||
protocol
|
||||
^^^^^^^^
|
||||
If the ESXi host is not using the default protocol, set this value to an
|
||||
alternate protocol. Default is ``https``.
|
||||
|
||||
port
|
||||
^^^^
|
||||
If the ESXi host is not using the default port, set this value to an
|
||||
alternate port. Default is ``443``.
|
||||
|
||||
|
||||
Salt Proxy
|
||||
----------
|
||||
|
||||
After your pillar is in place, you can test the proxy. The proxy can run on
|
||||
any machine that has network connectivity to your Salt Master and to the
|
||||
ESXi host in question. SaltStack recommends that the machine running the
|
||||
salt-proxy process also run a regular minion, though it is not strictly
|
||||
necessary.
|
||||
|
||||
On the machine that will run the proxy, make sure there is an ``/etc/salt/proxy``
|
||||
file with at least the following in it:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
master: <ip or hostname of salt-master>
|
||||
|
||||
You can then start the salt-proxy process with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-proxy --proxyid <id you want to give the host>
|
||||
|
||||
You may want to add ``-l debug`` to run the above in the foreground in
|
||||
debug mode just to make sure everything is OK.
|
||||
|
||||
Next, accept the key for the proxy on your salt-master, just like you
|
||||
would for a regular minion:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-key -a <id you gave the esxi host>
|
||||
|
||||
You can confirm that the pillar data is in place for the proxy:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> pillar.items
|
||||
|
||||
And now you should be able to ping the ESXi host to make sure it is
|
||||
responding:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> test.ping
|
||||
|
||||
At this point you can execute one-off commands against the host. For
|
||||
example, you can get the ESXi host's system information:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> esxi.cmd system_info
|
||||
|
||||
Note that you don't need to provide credentials or an ip/hostname. Salt
|
||||
knows to use the credentials you stored in Pillar.
|
||||
|
||||
It's important to understand how this particular proxy works.
|
||||
:doc:`Salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` is a
|
||||
standard Salt execution module. If you pull up the docs for it you'll see
|
||||
that almost every function in the module takes credentials and a target
|
||||
host. When credentials and a host aren't passed, Salt runs commands
|
||||
through ``pyVmomi``against the local machine. If you wanted, you could run
|
||||
functions from this module on any host where an appropriate version of
|
||||
``pyVmomi`` is installed, and that host would reach out over the network
|
||||
and communicate with the ESXi host.
|
||||
|
||||
``esxi.cmd`` acts as a "shim" between the execution module and the proxy. Its
|
||||
first parameter is always the function from salt.modules.vsphere. If the
|
||||
function takes more positional or keyword arguments you can append them to the
|
||||
call. It's this shim that speaks to the ESXi host through the proxy, arranging
|
||||
for the credentials and hostname to be pulled from the Pillar section for this
|
||||
Proxy Minion.
|
||||
|
||||
Because of the presence of the shim, to lookup documentation for what
|
||||
functions you can use to interface with the ESXi host, you'll want to
|
||||
look in :doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>`
|
||||
instead of :doc:`salt.modules.esxi </ref/modules/all/salt.modules.esxi>`.
|
||||
|
||||
|
||||
States
|
||||
------
|
||||
|
||||
Associated states are thoroughly documented in
|
||||
:doc:`salt.states.vsphere </ref/states/all/salt.states.vsphere>`. Look there
|
||||
to find an example structure for Pillar as well as an example ``.sls`` file f
|
||||
or standing up an ESXi host from scratch.
|
||||
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.exceptions import SaltSystemExit
|
||||
|
||||
# This must be present or the Salt loader won't load this module.
|
||||
__proxyenabled__ = ['esxi']
|
||||
|
||||
|
||||
# Variables are scoped to this module so we can have persistent data
|
||||
# across calls to fns in here.
|
||||
GRAINS_CACHE = {}
|
||||
DETAILS = {}
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'esxi'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if the ESXi execution module is available.
|
||||
'''
|
||||
if 'vsphere.system_info' in __salt__:
|
||||
return __virtualname__
|
||||
|
||||
return False, 'The ESXi Proxy Minion module did not load.'
|
||||
|
||||
|
||||
def init(opts):
|
||||
'''
|
||||
This function gets called when the proxy starts up. For
|
||||
ESXi devices, the host, login credentials, and, if configured,
|
||||
the protocol and port are cached.
|
||||
'''
|
||||
if 'host' not in opts['proxy']:
|
||||
log.critical('No \'host\' key found in pillar for this proxy.')
|
||||
return False
|
||||
if 'username' not in opts['proxy']:
|
||||
log.critical('No \'username\' key found in pillar for this proxy.')
|
||||
return False
|
||||
if 'passwords' not in opts['proxy']:
|
||||
log.critical('No \'passwords\' key found in pillar for this proxy.')
|
||||
|
||||
host = opts['proxy']['host']
|
||||
|
||||
# Get the correct login details
|
||||
try:
|
||||
username, password = find_credentials(host)
|
||||
except SaltSystemExit as err:
|
||||
log.critical('Error: {0}'.format(err))
|
||||
return False
|
||||
|
||||
# Set configuration details
|
||||
DETAILS['host'] = host
|
||||
DETAILS['username'] = username
|
||||
DETAILS['password'] = password
|
||||
DETAILS['protocol'] = opts['proxy'].get('protocol')
|
||||
DETAILS['port'] = opts['proxy'].get('port')
|
||||
|
||||
|
||||
def grains():
|
||||
'''
|
||||
Get the grains from the proxy device.
|
||||
'''
|
||||
if not GRAINS_CACHE:
|
||||
return _grains(DETAILS['host'],
|
||||
DETAILS['protocol'],
|
||||
DETAILS['port'])
|
||||
return GRAINS_CACHE
|
||||
|
||||
|
||||
def grains_refresh():
|
||||
'''
|
||||
Refresh the grains from the proxy device.
|
||||
'''
|
||||
GRAINS_CACHE = {}
|
||||
return grains()
|
||||
|
||||
|
||||
def ping():
|
||||
'''
|
||||
Check to see if the host is responding. Returns False if the host didn't
|
||||
respond, True otherwise.
|
||||
|
||||
CLI Example::
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt esxi-host test.ping
|
||||
'''
|
||||
find_credentials(DETAILS['host'])
|
||||
try:
|
||||
__salt__['vsphere.system_info'](host=DETAILS['host'],
|
||||
username=DETAILS['username'],
|
||||
password=DETAILS['password'])
|
||||
except SaltSystemExit as err:
|
||||
log.warning(err)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def shutdown():
|
||||
'''
|
||||
Shutdown the connection to the proxy device. For this proxy,
|
||||
shutdown is a no-op.
|
||||
'''
|
||||
log.debug('ESXi proxy shutdown() called...')
|
||||
|
||||
|
||||
def ch_config(cmd, *args, **kwargs):
|
||||
'''
|
||||
This function is called by the
|
||||
:doc:`salt.modules.esxi.cmd </ref/modules/all/salt.modules.esxi>` shim.
|
||||
It then calls whatever is passed in ``cmd`` inside the
|
||||
:doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` module.
|
||||
Passes the return through from the vsphere module.
|
||||
|
||||
cmd
|
||||
The command to call inside salt.modules.vsphere
|
||||
|
||||
args
|
||||
Arguments that need to be passed to that command.
|
||||
|
||||
kwargs
|
||||
Keyword arguments that need to be passed to that command.
|
||||
|
||||
'''
|
||||
# Strip the __pub_ keys...is there a better way to do this?
|
||||
for k in kwargs.keys():
|
||||
if k.startswith('__pub_'):
|
||||
kwargs.pop(k)
|
||||
|
||||
if 'vsphere.' + cmd not in __salt__:
|
||||
return {'retcode': -1, 'message': 'vsphere.' + cmd + ' is not available.'}
|
||||
else:
|
||||
return __salt__['vsphere.' + cmd](*args, **kwargs)
|
||||
|
||||
|
||||
def find_credentials(host):
|
||||
'''
|
||||
Cycle through all the possible credentials and return the first one that
|
||||
works.
|
||||
'''
|
||||
user_names = [__pillar__['proxy'].get('username', 'root')]
|
||||
passwords = __pillar__['proxy']['passwords']
|
||||
for user in user_names:
|
||||
for password in passwords:
|
||||
try:
|
||||
# Try to authenticate with the given user/password combination
|
||||
ret = __salt__['vsphere.system_info'](host=host,
|
||||
username=user,
|
||||
password=password)
|
||||
except SaltSystemExit:
|
||||
# If we can't authenticate, continue on to try the next password.
|
||||
continue
|
||||
# If we have data returned from above, we've successfully authenticated.
|
||||
if ret:
|
||||
DETAILS['username'] = user
|
||||
DETAILS['password'] = password
|
||||
return user, password
|
||||
# We've reached the end of the list without successfully authenticating.
|
||||
raise SaltSystemExit('Cannot complete login due to an incorrect user name or password.')
|
||||
|
||||
|
||||
def _grains(host, protocol=None, port=None):
|
||||
'''
|
||||
Helper function to the grains from the proxied device.
|
||||
'''
|
||||
username, password = find_credentials(DETAILS['host'])
|
||||
ret = __salt__['vsphere.system_info'](host=host,
|
||||
username=username,
|
||||
password=password,
|
||||
protocol=protocol,
|
||||
port=port)
|
||||
GRAINS_CACHE.update(ret)
|
||||
return GRAINS_CACHE
|
935
salt/states/esxi.py
Normal file
935
salt/states/esxi.py
Normal file
|
@ -0,0 +1,935 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage VMware ESXi Hosts.
|
||||
|
||||
.. versionadded:: 2015.8.4
|
||||
|
||||
Dependencies
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- pyVmomi Python Module
|
||||
- ESXCLI
|
||||
|
||||
.. note::
|
||||
|
||||
Be aware that some functionality in this state module may depend on the
|
||||
type of license attached to the ESXi host.
|
||||
|
||||
For example, certain services are only available to manipulate service state
|
||||
or policies with a VMware vSphere Enterprise or Enterprise Plus license, while
|
||||
others are available with a Standard license. The ``ntpd`` service is restricted
|
||||
to an Enterprise Plus license, while ``ssh`` is available via the Standard
|
||||
license.
|
||||
|
||||
Please see the `vSphere Comparison`_ page for more information.
|
||||
|
||||
.. _vSphere Comparison: https://www.vmware.com/products/vsphere/compare
|
||||
|
||||
'''
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.utils
|
||||
import salt.ext.six as six
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
# Get Logging Started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
return 'esxi.cmd' in __salt__
|
||||
|
||||
|
||||
def coredump_configured(name, enabled, dump_ip, host_vnic='vmk0', dump_port=6500):
|
||||
'''
|
||||
Ensures a host's core dump configuration.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
enabled
|
||||
Sets whether or not ESXi core dump collection should be enabled.
|
||||
This is a boolean value set to ``True`` or ``False`` to enable
|
||||
or disable core dumps.
|
||||
|
||||
Note that ESXi requires that the core dump must be enabled before
|
||||
any other parameters may be set. This also affects the ``changes``
|
||||
results in the state return dictionary. If ``enabled`` is ``False``,
|
||||
we can't obtain any previous settings to compare other state variables,
|
||||
resulting in many ``old`` references returning ``None``.
|
||||
|
||||
Once ``enabled`` is ``True`` the ``changes`` dictionary comparisons
|
||||
will be more accurate. This is due to the way the system coredemp
|
||||
network configuration command returns data.
|
||||
|
||||
dump_ip
|
||||
The IP address of host that will accept the dump.
|
||||
|
||||
host_vnic
|
||||
Host VNic port through which to communicate. Defaults to ``vmk0``.
|
||||
|
||||
dump_port
|
||||
TCP port to use for the dump. Defaults to ``6500``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-coredump:
|
||||
esxi.coredump_configured:
|
||||
- enabled: True
|
||||
- dump_ip: 'my-coredump-ip.example.com`
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
enabled_msg = 'ESXi requires that the core dump must be enabled ' \
|
||||
'before any other parameters may be set.'
|
||||
|
||||
current_config = __salt__[esxi_cmd]('get_coredump_network_config')
|
||||
if isinstance(current_config, six.string_types):
|
||||
ret['comment'] = 'Error: {0}'.format(current_config)
|
||||
return ret
|
||||
elif ret.get('stderr'):
|
||||
ret['comment'] = 'Error: {0}'.format(ret.get('stderr'))
|
||||
return ret
|
||||
else:
|
||||
current_enabled = current_config.get('enabled')
|
||||
|
||||
if current_enabled != enabled:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('coredump_network_enable',
|
||||
enabled=enabled)
|
||||
if response['retcode'] != 0:
|
||||
ret['comment'] = 'Error: {0}'.format(ret['stderr'])
|
||||
return ret
|
||||
|
||||
# Allow users to disable core dump, but then return since
|
||||
# nothing else can be set if core dump is disabled.
|
||||
if not enabled:
|
||||
ret['result'] = True
|
||||
ret['comment'] = enabled_msg
|
||||
return ret
|
||||
|
||||
ret['changes'].update({'enabled':
|
||||
{'old': current_enabled,
|
||||
'new': enabled}})
|
||||
|
||||
elif not enabled:
|
||||
# If current_enabled and enabled match, but are both False,
|
||||
# We must return before configuring anything. This isn't a
|
||||
# failure as core dump may be disabled intentionally.
|
||||
ret['result'] = True
|
||||
ret['comment'] = enabled_msg
|
||||
return ret
|
||||
|
||||
changes = False
|
||||
current_ip = current_config.get('ip')
|
||||
if current_ip != dump_ip:
|
||||
ret['changes'].update({'dump_ip':
|
||||
{'old': current_ip,
|
||||
'new': dump_ip}})
|
||||
changes = True
|
||||
|
||||
current_vnic = current_config.get('host_vnic')
|
||||
if current_vnic != host_vnic:
|
||||
ret['changes'].update({'host_vnic':
|
||||
{'old': current_vnic,
|
||||
'new': host_vnic}})
|
||||
changes = True
|
||||
|
||||
current_port = current_config.get('port')
|
||||
if current_port != str(dump_port):
|
||||
ret['changes'].update({'dump_port':
|
||||
{'old': current_port,
|
||||
'new': str(dump_port)}})
|
||||
changes = True
|
||||
|
||||
# Only run the command if not using test=True and changes were detected.
|
||||
if not __opts__['test'] and changes is True:
|
||||
response = __salt__[esxi_cmd]('set_coredump_network_config',
|
||||
dump_ip=dump_ip,
|
||||
host_vnic=host_vnic,
|
||||
dump_port=dump_port)
|
||||
if response.get('success') is False:
|
||||
msg = response.get('stderr')
|
||||
if not msg:
|
||||
msg = response.get('stdout')
|
||||
ret['comment'] = 'Error: {0}'.format(msg)
|
||||
return ret
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'Core Dump configuration is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'Core dump configuration will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def password_present(name, password):
|
||||
'''
|
||||
Ensures the given password is set on the ESXi host. Passwords cannot be obtained from
|
||||
host, so if a password is set in this state, the ``vsphere.update_host_password``
|
||||
function will always run (except when using test=True functionality) and the state's
|
||||
changes dictionary will always be populated.
|
||||
|
||||
The username for which the password will change is the same username that is used to
|
||||
authenticate against the ESXi host via the Proxy Minion. For example, if the pillar
|
||||
definition for the proxy username is defined as ``root``, then the username that the
|
||||
password will be updated for via this state is ``root``.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
password
|
||||
The new password to change on the host.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-password:
|
||||
esxi.password_present:
|
||||
- password: 'new-bad-password'
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {'old': 'unknown',
|
||||
'new': '********'},
|
||||
'comment': 'Host password was updated.'}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'Host password will change.'
|
||||
return ret
|
||||
else:
|
||||
try:
|
||||
__salt__[esxi_cmd]('update_host_password',
|
||||
new_password=password)
|
||||
except CommandExecutionError as err:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Error: {0}'.format(err)
|
||||
return ret
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def ntp_configured(name,
|
||||
service_running,
|
||||
ntp_servers=None,
|
||||
service_policy=None,
|
||||
service_restart=False,
|
||||
update_datetime=False):
|
||||
'''
|
||||
Ensures a host's NTP server configuration such as setting NTP servers, ensuring the
|
||||
NTP daemon is running or stopped, or restarting the NTP daemon for the ESXi host.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
service_running
|
||||
Ensures the running state of the ntp deamon for the host. Boolean value where
|
||||
``True`` indicates that ntpd should be running and ``False`` indicates that it
|
||||
should be stopped.
|
||||
|
||||
ntp_servers
|
||||
A list of servers that should be added to the ESXi host's NTP configuration.
|
||||
|
||||
service_policy
|
||||
The policy to set for the NTP service.
|
||||
|
||||
service_restart
|
||||
If set to ``True``, the ntp daemon will be restarted, regardless of its previous
|
||||
running state. Default is ``False``.
|
||||
|
||||
update_datetime
|
||||
If set to ``True``, the date/time on the given host will be updated to UTC.
|
||||
Default setting is ``False``. This option should be used with caution since
|
||||
network delays and execution delays can result in time skews.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-ntp:
|
||||
esxi.ntp_configured:
|
||||
- service_running: True
|
||||
- ntp_servers:
|
||||
- 192.174.1.100
|
||||
- 192.174.1.200
|
||||
- service_policy: 'automatic'
|
||||
- service_restart: True
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
host = __pillar__['proxy']['host']
|
||||
ntpd = 'ntpd'
|
||||
|
||||
ntp_config = __salt__[esxi_cmd]('get_ntp_config').get(host)
|
||||
ntp_running = __salt__[esxi_cmd]('get_service_running',
|
||||
service_name=ntpd).get(host)
|
||||
error = ntp_running.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ntp_running = ntp_running.get(ntpd)
|
||||
|
||||
# Configure NTP Servers for the Host
|
||||
if ntp_servers and set(ntp_servers) != set(ntp_config):
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('set_ntp_config',
|
||||
ntp_servers=ntp_servers).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
# Set changes dictionary for ntp_servers
|
||||
ret['changes'].update({'ntp_servers':
|
||||
{'old': ntp_config,
|
||||
'new': ntp_servers}})
|
||||
|
||||
# Configure service_running state
|
||||
if service_running != ntp_running:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
# Start ntdp if service_running=True
|
||||
if ntp_running is True:
|
||||
response = __salt__[esxi_cmd]('service_start',
|
||||
service_name=ntpd).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
# Stop ntpd if service_running=False
|
||||
else:
|
||||
response = __salt__[esxi_cmd]('service_stop',
|
||||
service_name=ntpd).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'service_running':
|
||||
{'old': ntp_running,
|
||||
'new': service_running}})
|
||||
|
||||
# Configure service_policy
|
||||
if service_policy:
|
||||
current_service_policy = __salt__[esxi_cmd]('get_service_policy',
|
||||
service_name=ntpd).get(host)
|
||||
error = current_service_policy.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
current_service_policy = current_service_policy.get(ntpd)
|
||||
|
||||
if service_policy != current_service_policy:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('set_service_policy',
|
||||
service_name=ntpd,
|
||||
service_policy=service_policy).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'service_policy':
|
||||
{'old': current_service_policy,
|
||||
'new': service_policy}})
|
||||
|
||||
# Update datetime, if requested.
|
||||
if update_datetime:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('update_host_datetime').get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'update_datetime':
|
||||
{'old': '',
|
||||
'new': 'Host datetime was updated.'}})
|
||||
|
||||
# Restart ntp_service if service_restart=True
|
||||
if service_restart:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('service_restart',
|
||||
service_name=ntpd).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'service_restart':
|
||||
{'old': ntp_running,
|
||||
'new': 'NTP Deamon Restarted.'}})
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'NTP is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'NTP state will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def vmotion_configured(name, enabled, device='vmk0'):
|
||||
'''
|
||||
Configures a host's VMotion properties such as enabling VMotion and setting
|
||||
the device VirtualNic that VMotion will use.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
enabled
|
||||
Ensures whether or not VMotion should be enabled on a host as a boolean
|
||||
value where ``True`` indicates that VSAN should be enabled and ``False``
|
||||
indicates that VMotion should be disabled.
|
||||
|
||||
device
|
||||
The device that uniquely identifies the VirtualNic that will be used for
|
||||
VMotion for the host. Defaults to ``vmk0``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-vmotion:
|
||||
esxi.vmotion_configured:
|
||||
- enabled: True
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
host = __pillar__['proxy']['host']
|
||||
|
||||
current_vmotion_enabled = __salt__[esxi_cmd]('get_vmotion_enabled').get(host)
|
||||
current_vmotion_enabled = current_vmotion_enabled.get('VMotion Enabled')
|
||||
|
||||
# Configure VMotion Enabled state, if changed.
|
||||
if enabled != current_vmotion_enabled:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
# Enable VMotion if enabled=True
|
||||
if enabled is True:
|
||||
response = __salt__[esxi_cmd]('vmotion_enable',
|
||||
device=device).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
# Disable VMotion if enabled=False
|
||||
else:
|
||||
response = __salt__[esxi_cmd]('vmotion_disable').get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'enabled':
|
||||
{'old': current_vmotion_enabled,
|
||||
'new': enabled}})
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'VMotion configuration is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'VMotion configuration will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def vsan_configured(name, enabled, add_disks_to_vsan=False):
|
||||
'''
|
||||
Configures a host's VSAN properties such as enabling or disabling VSAN, or
|
||||
adding VSAN-eligible disks to the VSAN system for the host.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
enabled
|
||||
Ensures whether or not VSAN should be enabled on a host as a boolean
|
||||
value where ``True`` indicates that VSAN should be enabled and ``False``
|
||||
indicates that VSAN should be disabled.
|
||||
|
||||
add_disks_to_vsan
|
||||
If set to ``True``, any VSAN-eligible disks for the given host will be added
|
||||
to the host's VSAN system. Default is ``False``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-vsan:
|
||||
esxi.vsan_configured:
|
||||
- enabled: True
|
||||
- add_disks_to_vsan: True
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
host = __pillar__['proxy']['host']
|
||||
|
||||
current_vsan_enabled = __salt__[esxi_cmd]('get_vsan_enabled').get(host)
|
||||
error = current_vsan_enabled.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
current_vsan_enabled = current_vsan_enabled.get('VSAN Enabled')
|
||||
|
||||
# Configure VSAN Enabled state, if changed.
|
||||
if enabled != current_vsan_enabled:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
# Enable VSAN if enabled=True
|
||||
if enabled is True:
|
||||
response = __salt__[esxi_cmd]('vsan_enable').get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
# Disable VSAN if enabled=False
|
||||
else:
|
||||
response = __salt__[esxi_cmd]('vsan_disable').get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'enabled':
|
||||
{'old': current_vsan_enabled,
|
||||
'new': enabled}})
|
||||
|
||||
# Add any eligible disks to VSAN, if requested.
|
||||
if add_disks_to_vsan:
|
||||
current_eligible_disks = __salt__[esxi_cmd]('get_vsan_eligible_disks').get(host)
|
||||
error = current_eligible_disks.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
|
||||
disks = current_eligible_disks.get('Eligible')
|
||||
if disks and isinstance(disks, list):
|
||||
ret['changes'].update({'add_disks_to_vsan':
|
||||
{'old': '',
|
||||
'new': disks}})
|
||||
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('vsan_add_disks').get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'VSAN configuration is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'VSAN configuration will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def ssh_configured(name,
|
||||
service_running,
|
||||
ssh_key=None,
|
||||
ssh_key_file=None,
|
||||
service_policy=None,
|
||||
service_restart=False,
|
||||
certificate_verify=False):
|
||||
'''
|
||||
Manage the SSH configuration for a host including whether or not SSH is running or
|
||||
the presence of a given SSH key. Note: Only one ssh key can be uploaded for root.
|
||||
Uploading a second key will replace any existing key.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
service_running
|
||||
Ensures whether or not the SSH service should be running on a host. Represented
|
||||
as a boolean value where ``True`` indicates that SSH should be running and
|
||||
``False`` indicates that SSH should stopped.
|
||||
|
||||
In order to update SSH keys, the SSH service must be running.
|
||||
|
||||
ssh_key
|
||||
Public SSH key to added to the authorized_keys file on the ESXi host. You can
|
||||
use ``ssh_key`` or ``ssh_key_file``, but not both.
|
||||
|
||||
ssh_key_file
|
||||
File containing the public SSH key to be added to the authorized_keys file on
|
||||
the ESXi host. You can use ``ssh_key_file`` or ``ssh_key``, but not both.
|
||||
|
||||
service_policy
|
||||
The policy to set for the NTP service.
|
||||
|
||||
service_restart
|
||||
If set to ``True``, the SSH service will be restarted, regardless of its
|
||||
previous running state. Default is ``False``.
|
||||
|
||||
certificate_verify
|
||||
If set to ``True``, the SSL connection must present a valid certificate.
|
||||
Default is ``False``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-ssh:
|
||||
esxi.ssh_configured:
|
||||
- service_running: True
|
||||
- ssh_key_file: /etc/salt/ssh_keys/my_key.pub
|
||||
- service_policy: 'automatic'
|
||||
- service_restart: True
|
||||
- certificate_verify: True
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
host = __pillar__['proxy']['host']
|
||||
ssh = 'ssh'
|
||||
|
||||
ssh_running = __salt__[esxi_cmd]('get_service_running',
|
||||
service_name=ssh).get(host)
|
||||
error = ssh_running.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ssh_running = ssh_running.get(ssh)
|
||||
|
||||
# Configure SSH service_running state, if changed.
|
||||
if service_running != ssh_running:
|
||||
# Only actually run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
# Start SSH if service_running=True
|
||||
if service_running is True:
|
||||
enable = __salt__[esxi_cmd]('service_start',
|
||||
service_name=ssh).get(host)
|
||||
error = enable.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
# Disable SSH if service_running=False
|
||||
else:
|
||||
disable = __salt__[esxi_cmd]('service_stop',
|
||||
service_name=ssh).get(host)
|
||||
error = disable.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
|
||||
ret['changes'].update({'service_running':
|
||||
{'old': ssh_running,
|
||||
'new': service_running}})
|
||||
|
||||
# If uploading an SSH key or SSH key file, see if there's a current
|
||||
# SSH key and compare the current key to the key set in the state.
|
||||
current_ssh_key, ssh_key_changed = None, False
|
||||
if ssh_key or ssh_key_file:
|
||||
current_ssh_key = __salt__[esxi_cmd]('get_ssh_key',
|
||||
certificate_verify=certificate_verify)
|
||||
error = current_ssh_key.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
current_ssh_key = current_ssh_key.get('key')
|
||||
if current_ssh_key:
|
||||
clean_current_key = _strip_key(current_ssh_key).split(' ')
|
||||
if not ssh_key:
|
||||
ssh_key = ''
|
||||
# Open ssh key file and read in contents to create one key string
|
||||
with salt.utils.fopen(ssh_key_file, 'r') as key_file:
|
||||
for line in key_file:
|
||||
if line.startswith('#'):
|
||||
# Commented line
|
||||
continue
|
||||
ssh_key = ssh_key + line
|
||||
|
||||
clean_ssh_key = _strip_key(ssh_key).split(' ')
|
||||
# Check that the first two list items of clean key lists are equal.
|
||||
if clean_current_key[0] != clean_ssh_key[0] or clean_current_key[1] != clean_ssh_key[1]:
|
||||
ssh_key_changed = True
|
||||
|
||||
# Upload SSH key, if changed.
|
||||
if ssh_key_changed:
|
||||
if not __opts__['test']:
|
||||
# Upload key
|
||||
response = __salt__[esxi_cmd]('upload_ssh_key',
|
||||
ssh_key=ssh_key,
|
||||
ssh_key_file=ssh_key_file,
|
||||
certificate_verify=certificate_verify)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'SSH Key':
|
||||
{'old': current_ssh_key,
|
||||
'new': ssh_key if ssh_key else ssh_key_file}})
|
||||
|
||||
# Configure service_policy
|
||||
if service_policy:
|
||||
current_service_policy = __salt__[esxi_cmd]('get_service_policy',
|
||||
service_name=ssh).get(host)
|
||||
error = current_service_policy.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
current_service_policy = current_service_policy.get(ssh)
|
||||
|
||||
if service_policy != current_service_policy:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('set_service_policy',
|
||||
service_name=ssh,
|
||||
service_policy=service_policy).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'service_policy':
|
||||
{'old': current_service_policy,
|
||||
'new': service_policy}})
|
||||
|
||||
# Restart ssh_service if service_restart=True
|
||||
if service_restart:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('service_restart',
|
||||
service_name=ssh).get(host)
|
||||
error = response.get('Error')
|
||||
if error:
|
||||
ret['comment'] = 'Error: {0}'.format(error)
|
||||
return ret
|
||||
ret['changes'].update({'service_restart':
|
||||
{'old': ssh_running,
|
||||
'new': 'SSH service restarted.'}})
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'SSH service is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'SSH service state will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def syslog_configured(name,
|
||||
syslog_configs,
|
||||
firewall=True,
|
||||
reset_service=True,
|
||||
reset_syslog_config=False,
|
||||
reset_configs=None):
|
||||
'''
|
||||
Ensures the specified syslog configuration parameters. By default,
|
||||
this state will reset the syslog service after any new or changed
|
||||
parameters are set successfully.
|
||||
|
||||
name
|
||||
Name of the state.
|
||||
|
||||
syslog_configs
|
||||
Name of parameter to set (corresponds to the command line switch for
|
||||
esxcli without the double dashes (--))
|
||||
|
||||
Valid syslog_config values are ``logdir``, ``loghost``, ``logdir-unique``,
|
||||
``default-rotate``, ``default-size``, and ``default-timeout``.
|
||||
|
||||
Each syslog_config option also needs a configuration value to set.
|
||||
For example, ``loghost`` requires URLs or IP addresses to use for
|
||||
logging. Multiple log servers can be specified by listing them,
|
||||
comma-separated, but without spaces before or after commas
|
||||
|
||||
(reference: https://blogs.vmware.com/vsphere/2012/04/configuring-multiple-syslog-servers-for-esxi-5.html)
|
||||
|
||||
firewall
|
||||
Enable the firewall rule set for syslog. Defaults to ``True``.
|
||||
|
||||
reset_service
|
||||
After a successful parameter set, reset the service. Defaults to ``True``.
|
||||
|
||||
reset_syslog_config
|
||||
Resets the syslog service to it's default settings. Defaults to ``False``.
|
||||
If set to ``True``, default settings defined by the list of syslog configs
|
||||
in ``reset_configs`` will be reset before running any other syslog settings.
|
||||
|
||||
reset_configs
|
||||
A comma-delimited list of parameters to reset. Only runs if
|
||||
``reset_syslog_config`` is set to ``True``. If ``reset_syslog_config`` is set
|
||||
to ``True``, but no syslog configs are listed in ``reset_configs``, then
|
||||
``reset_configs`` will be set to ``all`` by default.
|
||||
|
||||
See ``syslog_configs`` parameter above for a list of valid options.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
configure-host-syslog:
|
||||
esxi.syslog_configured:
|
||||
- syslog_configs:
|
||||
loghost: ssl://localhost:5432,tcp://10.1.0.1:1514
|
||||
default-timeout: 120
|
||||
- firewall: True
|
||||
- reset_service: True
|
||||
- reset_syslog_config: True
|
||||
- reset_configs: loghost,default-timeout
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
esxi_cmd = 'esxi.cmd'
|
||||
|
||||
if reset_syslog_config:
|
||||
if not reset_configs:
|
||||
reset_configs = 'all'
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
reset = __salt__[esxi_cmd]('reset_syslog_config',
|
||||
syslog_config=reset_configs)
|
||||
for key, val in reset.iteritems():
|
||||
if isinstance(val, bool):
|
||||
continue
|
||||
if not val.get('success'):
|
||||
msg = val.get('message')
|
||||
if not msg:
|
||||
msg = 'There was an error resetting a syslog config \'{0}\'.' \
|
||||
'Please check debug logs.'.format(val)
|
||||
ret['comment'] = 'Error: {0}'.format(msg)
|
||||
return ret
|
||||
|
||||
ret['changes'].update({'reset_syslog_config':
|
||||
{'old': '',
|
||||
'new': reset_configs}})
|
||||
|
||||
current_firewall = __salt__[esxi_cmd]('get_firewall_status')
|
||||
if not current_firewall.get('success'):
|
||||
ret['comment'] = 'There was an error obtaining firewall statuses. ' \
|
||||
'Please check debug logs.'
|
||||
return ret
|
||||
|
||||
current_firewall = current_firewall.get('rulesets').get('syslog')
|
||||
if current_firewall != firewall:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
enabled = __salt__[esxi_cmd]('enable_firewall_ruleset',
|
||||
ruleset_enable=firewall,
|
||||
ruleset_name='syslog')
|
||||
if enabled.get('retcode') != 0:
|
||||
err = enabled.get('stderr')
|
||||
out = enabled.get('stdout')
|
||||
ret['comment'] = 'Error: {0}'.format(err if err else out)
|
||||
return ret
|
||||
|
||||
ret['changes'].update({'firewall':
|
||||
{'old': current_firewall,
|
||||
'new': firewall}})
|
||||
|
||||
current_syslog_config = __salt__[esxi_cmd]('get_syslog_config')
|
||||
for key, val in syslog_configs.iteritems():
|
||||
# The output of get_syslog_config has different keys than the keys
|
||||
# Used to set syslog_config values. We need to look them up first.
|
||||
try:
|
||||
lookup_key = _lookup_syslog_config(key)
|
||||
except KeyError:
|
||||
ret['comment'] = '\'{0}\' is not a valid config variable.'.format(key)
|
||||
return ret
|
||||
|
||||
current_val = current_syslog_config[lookup_key]
|
||||
if current_val != val:
|
||||
# Only run the command if not using test=True
|
||||
if not __opts__['test']:
|
||||
response = __salt__[esxi_cmd]('set_syslog_config',
|
||||
syslog_config=key,
|
||||
config_value=val,
|
||||
firewall=firewall,
|
||||
reset_service=reset_service)
|
||||
success = response.get('success')
|
||||
if not success:
|
||||
msg = response.get('message')
|
||||
if not msg:
|
||||
msg = 'There was an error setting syslog config \'{0}\'. ' \
|
||||
'Please check debug logs.'.format(key)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
|
||||
if not ret['changes'].get('syslog_config'):
|
||||
ret['changes'].update({'syslog_config': {}})
|
||||
ret['changes']['syslog_config'].update({key:
|
||||
{'old': current_val,
|
||||
'new': val}})
|
||||
|
||||
ret['result'] = True
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'Syslog is already in the desired state.'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'Syslog state will change.'
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _lookup_syslog_config(config):
|
||||
'''
|
||||
Helper function that looks up syslog_config keys available from
|
||||
``vsphere.get_syslog_config``.
|
||||
'''
|
||||
lookup = {'default-timeout': 'Default Network Retry Timeout',
|
||||
'logdir': 'Local Log Output',
|
||||
'default-size': 'Local Logging Default Rotation Size',
|
||||
'logdir-unique': 'Log To Unique Subdirectory',
|
||||
'default-rotate': 'Local Logging Default Rotations',
|
||||
'loghost': 'Remote Host'}
|
||||
|
||||
return lookup.get(config)
|
||||
|
||||
|
||||
def _strip_key(key_string):
|
||||
'''
|
||||
Strips an SSH key string of white space and line endings and returns the new string.
|
||||
|
||||
key_string
|
||||
The string to be stripped.
|
||||
'''
|
||||
key_string.strip()
|
||||
key_string.replace('\n', '')
|
||||
key_string.replace('\r\n', '')
|
||||
return key_string
|
|
@ -7,7 +7,13 @@ Connection library for VMWare
|
|||
This is a base library used by a number of VMWare services such as VMWare
|
||||
ESX, ESXi, and vCenter servers.
|
||||
|
||||
:depends: pyVmomi Python Module
|
||||
Dependencies
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- pyVmomi Python Module
|
||||
- ESXCLI: This dependency is only needed to use the ``esxcli`` function. No other
|
||||
functions in this module rely on ESXCLI.
|
||||
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
|
@ -18,6 +24,8 @@ import time
|
|||
|
||||
# Import Salt Libs
|
||||
from salt.exceptions import SaltSystemExit
|
||||
import salt.modules.cmdmod
|
||||
import salt.utils
|
||||
|
||||
|
||||
# Import Third Party Libs
|
||||
|
@ -42,6 +50,50 @@ def __virtual__():
|
|||
return False, 'Missing dependency: The salt.utils.vmware module requires pyVmomi.'
|
||||
|
||||
|
||||
def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None):
|
||||
'''
|
||||
Shell out and call the specified esxcli commmand, parse the result
|
||||
and return something sane.
|
||||
|
||||
:param host: ESXi or vCenter host to connect to
|
||||
:param user: User to connect as, usually root
|
||||
:param pwd: Password to connect with
|
||||
:param port: TCP port
|
||||
:param cmd: esxcli command and arguments
|
||||
:param esxi_host: If `host` is a vCenter host, then esxi_host is the
|
||||
ESXi machine on which to execute this command
|
||||
:return: Dictionary
|
||||
'''
|
||||
|
||||
esx_cmd = salt.utils.which('esxicli')
|
||||
if not esx_cmd:
|
||||
log.error('Missing dependency: The salt.utils.vmware.esxcli function requires ESXCLI.')
|
||||
return False
|
||||
|
||||
if not esxi_host:
|
||||
# Then we are connecting directly to an ESXi server,
|
||||
# 'host' points at that server, and esxi_host is a reference to the
|
||||
# ESXi instance we are manipulating
|
||||
esx_cmd += ' -s {0} -u {1} -p {2} --protocol={3} --portnumber={4} {5}'.format(host,
|
||||
user,
|
||||
pwd,
|
||||
protocol,
|
||||
port,
|
||||
cmd)
|
||||
else:
|
||||
esx_cmd += ' -s {0} -h {1} -u {2} -p {3} --protocol={4} --portnumber={5} {6}'.format(host,
|
||||
esxi_host,
|
||||
user,
|
||||
pwd,
|
||||
protocol,
|
||||
port,
|
||||
cmd)
|
||||
|
||||
ret = salt.modules.cmdmod.run_all(esx_cmd)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_service_instance(host, username, password, protocol=None, port=None):
|
||||
'''
|
||||
Authenticate with a vCenter server or ESX/ESXi host and return the service instance object.
|
||||
|
|
Loading…
Add table
Reference in a new issue