mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #33508 from rallytime/merge-2016.3
[2016.3] Merge forward from 2015.8 to 2016.3
This commit is contained in:
commit
9199101ef2
25 changed files with 513 additions and 96 deletions
|
@ -377,6 +377,10 @@
|
|||
#include:
|
||||
# - /etc/salt/extra_config
|
||||
# - /etc/roles/webserver
|
||||
|
||||
# The syndic minion can verify that it is talking to the correct master via the
|
||||
# key fingerprint of the higher-level master with the "syndic_finger" config.
|
||||
#syndic_finger: ''
|
||||
#
|
||||
#
|
||||
#
|
||||
|
|
|
@ -2236,6 +2236,26 @@ configuration is the same as :conf_master:`file_roots`:
|
|||
prod:
|
||||
- /srv/pillar/prod
|
||||
|
||||
.. conf_master:: pillar_opts
|
||||
|
||||
``pillar_opts``
|
||||
---------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
The ``pillar_opts`` option adds the master configuration file data to a dict in
|
||||
the pillar called ``master``. This can be used to set simple configurations in
|
||||
the master config file that can then be used on minions.
|
||||
|
||||
Note that setting this option to ``True`` means the master config file will be
|
||||
included in all minion's pillars. While this makes global configuration of services
|
||||
and systems easy, it may not be desired if sensitive data is stored in the master
|
||||
configuration.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillar_opts: False
|
||||
|
||||
.. _master-configuration-ext-pillar:
|
||||
|
||||
.. conf_master:: ext_pillar
|
||||
|
|
|
@ -909,6 +909,20 @@ what you are doing! Transports are explained in :ref:`Salt Transports
|
|||
|
||||
transport: zeromq
|
||||
|
||||
.. conf_minion:: syndic_finger
|
||||
|
||||
``syndic_finger``
|
||||
-----------------
|
||||
|
||||
Default: ``''``
|
||||
|
||||
The key fingerprint of the higher-level master for the syndic to verify it is
|
||||
talking to the intended master.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
syndic_finger: 'ab:30:65:2a:d6:9e:20:4f:d8:b2:f3:a7:d4:65:50:10'
|
||||
|
||||
|
||||
Minion Module Management
|
||||
========================
|
||||
|
|
|
@ -341,3 +341,13 @@ string with quotes:
|
|||
ValueError: month must be in 1..12
|
||||
>>> yaml.safe_load('"4017-16-20"')
|
||||
'4017-16-20'
|
||||
|
||||
|
||||
Keys Limited to 1024 Characters
|
||||
===============================
|
||||
|
||||
Simple keys are limited to a single line and cannot be longer that 1024 characters.
|
||||
This is a limitation from PyYaml, as seen in a comment in `PyYAML's code`_, and
|
||||
applies to anything parsed by YAML in Salt.
|
||||
|
||||
.. _PyYAML's code: http://pyyaml.org/browser/pyyaml/trunk/lib/yaml/scanner.py#L91
|
||||
|
|
|
@ -17,6 +17,20 @@ things:
|
|||
Otherwise, it will attempt to connect to a master and fail. The salt-call
|
||||
command stands on its own and does not need the salt-minion daemon.
|
||||
|
||||
|
||||
Minion Configuration
|
||||
--------------------
|
||||
|
||||
Throughout this document there are several references to setting different
|
||||
options to configure a masterless Minion. Salt Minions are easy to configure
|
||||
via a configuration file that is located, by default, in ``/etc/salt/minion``.
|
||||
Note, however, that on FreeBSD systems, the minion configuration file is located
|
||||
in ``/usr/local/etc/salt/minion``.
|
||||
|
||||
You can learn more about minion configuration options in the
|
||||
:ref:`Configuring the Salt Minion <configuration-salt-minion>` docs.
|
||||
|
||||
|
||||
Telling Salt Call to Run Masterless
|
||||
===================================
|
||||
|
||||
|
@ -39,7 +53,6 @@ Now the salt-call command will not look for a master and will assume that the
|
|||
local system has all of the file and pillar resources.
|
||||
|
||||
|
||||
|
||||
Running States Masterless
|
||||
=========================
|
||||
|
||||
|
@ -81,6 +94,7 @@ it unnecessary to change the configuration file:
|
|||
|
||||
salt-call state.apply --local
|
||||
|
||||
|
||||
External Pillars
|
||||
================
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ RETVAL=0
|
|||
start() {
|
||||
echo -n $"Starting salt-minion daemon: "
|
||||
if [ -f $SUSE_RELEASE ]; then
|
||||
startproc -f -p /var/run/$SERVICE.pid $SALTMINION -d $MINION_ARGS
|
||||
startproc -p /var/run/$SERVICE.pid $SALTMINION -d $MINION_ARGS
|
||||
rc_status -v
|
||||
elif [ -e $DEBIAN_VERSION ]; then
|
||||
if [ -f $LOCKFILE ]; then
|
||||
|
|
|
@ -117,6 +117,10 @@ try:
|
|||
PAM_AUTHENTICATE.restype = c_int
|
||||
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
|
||||
|
||||
PAM_ACCT_MGMT = LIBPAM.pam_acct_mgmt
|
||||
PAM_ACCT_MGMT.restype = c_int
|
||||
PAM_ACCT_MGMT.argtypes = [PamHandle, c_int]
|
||||
|
||||
PAM_END = LIBPAM.pam_end
|
||||
PAM_END.restype = c_int
|
||||
PAM_END.argtypes = [PamHandle, c_int]
|
||||
|
@ -171,6 +175,8 @@ def authenticate(username, password):
|
|||
return False
|
||||
|
||||
retval = PAM_AUTHENTICATE(handle, 0)
|
||||
if retval == 0:
|
||||
PAM_ACCT_MGMT(handle, 0)
|
||||
PAM_END(handle, 0)
|
||||
return retval == 0
|
||||
|
||||
|
|
|
@ -68,8 +68,7 @@ def beacon(config):
|
|||
|
||||
'''
|
||||
ret = []
|
||||
for diskusage in config:
|
||||
mount = diskusage.keys()[0]
|
||||
for mount in config:
|
||||
|
||||
try:
|
||||
_current_usage = psutil.disk_usage(mount)
|
||||
|
@ -79,7 +78,7 @@ def beacon(config):
|
|||
continue
|
||||
|
||||
current_usage = _current_usage.percent
|
||||
monitor_usage = diskusage[mount]
|
||||
monitor_usage = config[mount]
|
||||
if '%' in monitor_usage:
|
||||
monitor_usage = re.sub('%', '', monitor_usage)
|
||||
monitor_usage = float(monitor_usage)
|
||||
|
|
|
@ -4,8 +4,11 @@ Execution module to work with etcd
|
|||
|
||||
:depends: - python-etcd
|
||||
|
||||
In order to use an etcd server, a profile should be created in the master
|
||||
configuration file:
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To work with an etcd server you must configure an etcd profile. The etcd config
|
||||
can be set in either the Salt Minion configuration file or in pillar:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -21,6 +24,17 @@ or clusters are available.
|
|||
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
.. note::
|
||||
|
||||
The etcd configuration can also be set in the Salt Master config file,
|
||||
but in order to use any etcd configurations defined in the Salt Master
|
||||
config, the :conf_master:`pillar_opts` must be set to ``True``.
|
||||
|
||||
Be aware that setting ``pillar_opts`` to ``True`` has security implications
|
||||
as this makes all master configuration settings available in all minion's
|
||||
pillars.
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
|
|
@ -448,6 +448,12 @@ def image_schema(profile=None):
|
|||
'''
|
||||
Returns names and descriptions of the schema "image"'s
|
||||
properties for this profile's instance of glance
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glance.image_schema
|
||||
'''
|
||||
return schema_get('image', profile)
|
||||
|
||||
|
@ -459,6 +465,13 @@ def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable
|
|||
- min_ram (in MB)
|
||||
- protected (bool)
|
||||
- visibility ('public' or 'private')
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glance.image_update id=c2eb2eb0-53e1-4a80-b990-8ec887eae7df
|
||||
salt '*' glance.image_update name=f16-jeos
|
||||
'''
|
||||
if id:
|
||||
image = image_show(id=id, profile=profile)
|
||||
|
@ -512,6 +525,12 @@ def schema_get(name, profile=None):
|
|||
- images
|
||||
- member
|
||||
- members
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' glance.schema_get name=f16-jeos
|
||||
'''
|
||||
g_client = _auth(profile)
|
||||
pformat = pprint.PrettyPrinter(indent=4).pformat
|
||||
|
|
|
@ -562,6 +562,12 @@ def list_(profile=None):
|
|||
'''
|
||||
To maintain the feel of the nova command line, this function simply calls
|
||||
the server_list function.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' nova.list
|
||||
'''
|
||||
return server_list(profile=profile)
|
||||
|
||||
|
|
|
@ -346,6 +346,27 @@ def psql_query(query, user=None, host=None, port=None, maintenance_db=None,
|
|||
WITH updated AS (UPDATE pg_authid SET rolconnlimit = 2000 WHERE
|
||||
rolname = 'rolename' RETURNING rolconnlimit) SELECT * FROM updated;
|
||||
|
||||
query
|
||||
The query string.
|
||||
|
||||
user
|
||||
Database username, if different from config or default.
|
||||
|
||||
host
|
||||
Database host, if different from config or default.
|
||||
|
||||
port
|
||||
Database port, if different from the config or default.
|
||||
|
||||
maintenance_db
|
||||
The database to run the query against.
|
||||
|
||||
password
|
||||
User password, if different from the config or default.
|
||||
|
||||
runas
|
||||
User to run the command as.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
|
|
@ -606,3 +606,29 @@ def version_cmp(ver1, ver2):
|
|||
log.warning("Failed to compare version '{0}' to '{1}' using RPM: {2}".format(ver1, ver2, exc))
|
||||
|
||||
return salt.utils.version_cmp(ver1, ver2)
|
||||
|
||||
|
||||
def checksum(*paths):
|
||||
'''
|
||||
Return if the signature of a RPM file is valid.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' lowpkg.checksum /path/to/package1.rpm
|
||||
salt '*' lowpkg.checksum /path/to/package1.rpm /path/to/package2.rpm
|
||||
'''
|
||||
ret = dict()
|
||||
|
||||
if not paths:
|
||||
raise CommandExecutionError("No package files has been specified.")
|
||||
|
||||
for package_file in paths:
|
||||
ret[package_file] = (bool(__salt__['file.file_exists'](package_file)) and
|
||||
not __salt__['cmd.retcode'](["rpm", "-K", "--quiet", package_file],
|
||||
ignore_retcode=True,
|
||||
output_loglevel='trace',
|
||||
python_shell=False))
|
||||
|
||||
return ret
|
||||
|
|
|
@ -52,7 +52,7 @@ import salt.utils.process
|
|||
import salt.utils.url
|
||||
import salt.wheel
|
||||
from salt.exceptions import (
|
||||
SaltReqTimeoutError, SaltRenderError, CommandExecutionError
|
||||
SaltReqTimeoutError, SaltRenderError, CommandExecutionError, SaltInvocationError
|
||||
)
|
||||
|
||||
__proxyenabled__ = ['*']
|
||||
|
@ -1046,7 +1046,7 @@ def runner(_fun, **kwargs):
|
|||
return rclient.cmd(_fun, kwarg=kwargs)
|
||||
|
||||
|
||||
def wheel(_fun, **kwargs):
|
||||
def wheel(_fun, *args, **kwargs):
|
||||
'''
|
||||
Execute a wheel module (this function must be run on the master)
|
||||
|
||||
|
@ -1054,6 +1054,13 @@ def wheel(_fun, **kwargs):
|
|||
|
||||
name
|
||||
The name of the function to run
|
||||
|
||||
args
|
||||
Any positional arguments to pass to the wheel function. A common example
|
||||
of this would be the ``match`` arg needed for key functions.
|
||||
|
||||
.. versionadded:: v2015.8.11
|
||||
|
||||
kwargs
|
||||
Any keyword arguments to pass to the wheel function
|
||||
|
||||
|
@ -1061,10 +1068,33 @@ def wheel(_fun, **kwargs):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.wheel key.accept match=jerry
|
||||
salt '*' saltutil.wheel key.accept jerry
|
||||
'''
|
||||
wclient = salt.wheel.WheelClient(__opts__)
|
||||
return wclient.cmd(_fun, kwarg=kwargs)
|
||||
if __opts__['__role'] == 'minion':
|
||||
master_config = os.path.join(os.path.dirname(__opts__['conf_file']),
|
||||
'master')
|
||||
master_opts = salt.config.client_config(master_config)
|
||||
wheel_client = salt.wheel.WheelClient(master_opts)
|
||||
else:
|
||||
wheel_client = salt.wheel.WheelClient(__opts__)
|
||||
|
||||
# The WheelClient cmd needs args, kwargs, and pub_data separated out from
|
||||
# the "normal" kwargs structure, which at this point contains __pub_x keys.
|
||||
pub_data = {}
|
||||
valid_kwargs = {}
|
||||
for key, val in six.iteritems(kwargs):
|
||||
if key.startswith('__'):
|
||||
pub_data[key] = val
|
||||
else:
|
||||
valid_kwargs[key] = val
|
||||
|
||||
try:
|
||||
ret = wheel_client.cmd(_fun, arg=args, pub_data=pub_data, kwarg=valid_kwargs)
|
||||
except SaltInvocationError:
|
||||
raise CommandExecutionError('This command can only be executed on a minion '
|
||||
'that is located on the master.')
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# this is the only way I could figure out how to get the REAL file_roots
|
||||
|
|
|
@ -207,7 +207,22 @@ def low(data, queue=False, **kwargs):
|
|||
return ret
|
||||
|
||||
|
||||
def high(data, test=False, queue=False, **kwargs):
|
||||
def _get_test_value(test=None, **kwargs):
|
||||
'''
|
||||
Determine the correct value for the test flag.
|
||||
'''
|
||||
ret = True
|
||||
if test is None:
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
ret = True
|
||||
else:
|
||||
ret = __opts__.get('test', None)
|
||||
else:
|
||||
ret = test
|
||||
return ret
|
||||
|
||||
|
||||
def high(data, test=None, queue=False, **kwargs):
|
||||
'''
|
||||
Execute the compound calls stored in a single set of high data
|
||||
|
||||
|
@ -225,12 +240,7 @@ def high(data, test=False, queue=False, **kwargs):
|
|||
return conflict
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
elif test is not None:
|
||||
opts['test'] = test
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
pillar = kwargs.get('pillar')
|
||||
pillar_enc = kwargs.get('pillar_enc')
|
||||
|
@ -662,13 +672,7 @@ def highstate(test=None,
|
|||
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
|
||||
if test is None:
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
else:
|
||||
opts['test'] = test
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
if 'env' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
|
@ -876,12 +880,7 @@ def sls(mods,
|
|||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
elif test is not None:
|
||||
opts['test'] = test
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
pillar = kwargs.get('pillar')
|
||||
pillar_enc = kwargs.get('pillar_enc')
|
||||
|
@ -1003,10 +1002,7 @@ def top(topfn,
|
|||
return err
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
pillar = kwargs.get('pillar')
|
||||
pillar_enc = kwargs.get('pillar_enc')
|
||||
|
@ -1119,10 +1115,7 @@ def sls_id(
|
|||
return conflict
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
if 'pillarenv' in kwargs:
|
||||
opts['pillarenv'] = kwargs['pillarenv']
|
||||
st_ = salt.state.HighState(opts)
|
||||
|
@ -1183,10 +1176,7 @@ def show_low_sls(mods,
|
|||
return conflict
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
if 'pillarenv' in kwargs:
|
||||
opts['pillarenv'] = kwargs['pillarenv']
|
||||
st_ = salt.state.HighState(opts)
|
||||
|
@ -1239,10 +1229,7 @@ def show_sls(mods, saltenv='base', test=None, queue=False, env=None, **kwargs):
|
|||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
pillar = kwargs.get('pillar')
|
||||
pillar_enc = kwargs.get('pillar_enc')
|
||||
|
@ -1339,10 +1326,7 @@ def single(fun, name, test=None, queue=False, **kwargs):
|
|||
'name': name})
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(kwargs.get('localconfig'))
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
opts['test'] = True
|
||||
else:
|
||||
opts['test'] = __opts__.get('test', None)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
pillar = kwargs.get('pillar')
|
||||
pillar_enc = kwargs.get('pillar_enc')
|
||||
|
@ -1398,7 +1382,7 @@ def clear_cache():
|
|||
return ret
|
||||
|
||||
|
||||
def pkg(pkg_path, pkg_sum, hash_type, test=False, **kwargs):
|
||||
def pkg(pkg_path, pkg_sum, hash_type, test=None, **kwargs):
|
||||
'''
|
||||
Execute a packaged state run, the packaged state run will exist in a
|
||||
tarball available locally. This packaged state
|
||||
|
@ -1442,10 +1426,7 @@ def pkg(pkg_path, pkg_sum, hash_type, test=False, **kwargs):
|
|||
popts = _get_opts(kwargs.get('localconfig'))
|
||||
popts['fileclient'] = 'local'
|
||||
popts['file_roots'] = {}
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
popts['test'] = True
|
||||
else:
|
||||
popts['test'] = __opts__.get('test', None)
|
||||
popts['test'] = _get_test_value(test, **kwargs)
|
||||
envs = os.listdir(root)
|
||||
for fn_ in envs:
|
||||
full = os.path.join(root, fn_)
|
||||
|
|
|
@ -769,6 +769,7 @@ def mod_repo(repo, **kwargs):
|
|||
|
||||
# Modify added or existing repo according to the options
|
||||
cmd_opt = []
|
||||
global_cmd_opt = []
|
||||
|
||||
if 'enabled' in kwargs:
|
||||
cmd_opt.append(kwargs['enabled'] and '--enable' or '--disable')
|
||||
|
@ -784,18 +785,18 @@ def mod_repo(repo, **kwargs):
|
|||
if 'gpgcheck' in kwargs:
|
||||
cmd_opt.append(kwargs['gpgcheck'] and '--gpgcheck' or '--no-gpgcheck')
|
||||
|
||||
if kwargs.get('gpgautoimport') is True:
|
||||
cmd_opt.append('--gpg-auto-import-keys')
|
||||
|
||||
if 'priority' in kwargs:
|
||||
cmd_opt.append("--priority={0}".format(kwargs.get('priority', DEFAULT_PRIORITY)))
|
||||
|
||||
if 'humanname' in kwargs:
|
||||
cmd_opt.append("--name='{0}'".format(kwargs.get('humanname')))
|
||||
|
||||
if kwargs.get('gpgautoimport') is True:
|
||||
global_cmd_opt.append('--gpg-auto-import-keys')
|
||||
|
||||
if cmd_opt:
|
||||
cmd_opt.append(repo)
|
||||
__zypper__.refreshable.xml.call('mr', *cmd_opt)
|
||||
cmd_opt = global_cmd_opt + ['mr'] + cmd_opt + [repo]
|
||||
__zypper__.refreshable.xml.call(*cmd_opt)
|
||||
|
||||
# If repo nor added neither modified, error should be thrown
|
||||
if not added and not cmd_opt:
|
||||
|
@ -1583,6 +1584,9 @@ def download(*packages, **kwargs):
|
|||
pkg_ret[key] = pkg_info
|
||||
|
||||
if pkg_ret:
|
||||
failed = [pkg for pkg in packages if pkg not in pkg_ret]
|
||||
if failed:
|
||||
pkg_ret['_error'] = ('The following package(s) failed to download: {0}'.format(', '.join(failed)))
|
||||
return pkg_ret
|
||||
|
||||
raise CommandExecutionError(
|
||||
|
|
|
@ -349,9 +349,13 @@ def list_jobs(ext_source=None,
|
|||
if search_target and _match:
|
||||
_match = False
|
||||
if 'Target' in ret[item]:
|
||||
for key in salt.utils.split_input(search_target):
|
||||
if fnmatch.fnmatch(ret[item]['Target'], key):
|
||||
_match = True
|
||||
targets = ret[item]['Target']
|
||||
if isinstance(targets, six.string_types):
|
||||
targets = [targets]
|
||||
for target in targets:
|
||||
for key in salt.utils.split_input(search_target):
|
||||
if fnmatch.fnmatch(target, key):
|
||||
_match = True
|
||||
|
||||
if search_function and _match:
|
||||
_match = False
|
||||
|
@ -488,6 +492,33 @@ def print_job(jid, ext_source=None, outputter=None):
|
|||
return ret
|
||||
|
||||
|
||||
def exit_success(jid, ext_source=None):
|
||||
'''
|
||||
Check if a job has been executed and exit successfully
|
||||
|
||||
jid
|
||||
The jid to look up.
|
||||
ext_source
|
||||
The external job cache to use. Default: `None`.
|
||||
|
||||
CLI Example:
|
||||
.. code-block:: bash
|
||||
salt-run jobs.exit_success 20160520145827701627
|
||||
'''
|
||||
ret = dict()
|
||||
|
||||
data = lookup_jid(
|
||||
jid,
|
||||
ext_source=ext_source
|
||||
)
|
||||
|
||||
for minion in data:
|
||||
if "retcode" in data[minion]:
|
||||
ret[minion] = True if not data[minion]['retcode'] else False
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def last_run(ext_source=None,
|
||||
outputter=None,
|
||||
metadata=None,
|
||||
|
|
|
@ -10,11 +10,11 @@ Manage etcd Keys
|
|||
|
||||
This state module supports setting and removing keys from etcd.
|
||||
|
||||
Salt Master Configuration
|
||||
-------------------------
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To work with an etcd server you must configure an etcd profile in the Salt
|
||||
Master configuration, for example:
|
||||
To work with an etcd server you must configure an etcd profile. The etcd config
|
||||
can be set in either the Salt Minion configuration file or in pillar:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -22,14 +22,25 @@ Master configuration, for example:
|
|||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
You can also configure etcd without a profile however it is recommended that
|
||||
you use profiles:
|
||||
It is technically possible to configure etcd without using a profile, but this
|
||||
is not considered to be a best practice, especially when multiple etcd servers
|
||||
or clusters are available.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
.. note::
|
||||
|
||||
The etcd configuration can also be set in the Salt Master config file,
|
||||
but in order to use any etcd configurations defined in the Salt Master
|
||||
config, the :conf_master:`pillar_opts` must be set to ``True``.
|
||||
|
||||
Be aware that setting ``pillar_opts`` to ``True`` has security implications
|
||||
as this makes all master configuration settings available in all minion's
|
||||
pillars.
|
||||
|
||||
Available Functions
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -10,16 +10,19 @@ file on the minions, by default at: /etc/salt/grains
|
|||
Note: This does NOT override any grains set in the minion file.
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
from salt.defaults import DEFAULT_TARGET_DELIM
|
||||
import re
|
||||
|
||||
# Import Salt libs
|
||||
from salt.defaults import DEFAULT_TARGET_DELIM
|
||||
|
||||
|
||||
def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
|
||||
'''
|
||||
Ensure that a grain is set
|
||||
|
||||
.. versionchanged:: 2016.3.0
|
||||
.. versionchanged:: v2015.8.2
|
||||
|
||||
name
|
||||
The grain name
|
||||
|
@ -27,14 +30,16 @@ def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
|
|||
value
|
||||
The value to set on the grain
|
||||
|
||||
:param force: If force is True, the existing grain will be overwritten
|
||||
force
|
||||
If force is True, the existing grain will be overwritten
|
||||
regardless of its existing or provided value type. Defaults to False
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
:param delimiter: A delimiter different from the default can be provided.
|
||||
delimiter
|
||||
A delimiter different from the default can be provided.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
It is now capable to set a grain to a complex value (ie. lists and dicts)
|
||||
and supports nested grains as well.
|
||||
|
@ -59,11 +64,11 @@ def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False):
|
|||
- name: icinga:Apache SSL
|
||||
- value:
|
||||
- command: check_https
|
||||
- params: -H localhost -p 443 -S
|
||||
- params: -H localhost -p 443 -S
|
||||
|
||||
with,a,custom,delimiter:
|
||||
grains.present:
|
||||
- value: yay
|
||||
- value: yay
|
||||
- delimiter: ,
|
||||
'''
|
||||
name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name)
|
||||
|
@ -106,9 +111,10 @@ def list_present(name, value, delimiter=DEFAULT_TARGET_DELIM):
|
|||
value
|
||||
The value is present in the list type grain.
|
||||
|
||||
:param delimiter: A delimiter different from the default ``:`` can be provided.
|
||||
delimiter
|
||||
A delimiter different from the default ``:`` can be provided.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
The grain should be `list type <http://docs.python.org/2/tutorial/datastructures.html#data-structures>`_
|
||||
|
||||
|
@ -198,9 +204,10 @@ def list_absent(name, value, delimiter=DEFAULT_TARGET_DELIM):
|
|||
value
|
||||
The value to delete from the grain list.
|
||||
|
||||
:param delimiter: A delimiter different from the default ``:`` can be provided.
|
||||
delimiter
|
||||
A delimiter different from the default ``:`` can be provided.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
The grain should be `list type <http://docs.python.org/2/tutorial/datastructures.html#data-structures>`_
|
||||
|
||||
|
@ -273,19 +280,23 @@ def absent(name,
|
|||
name
|
||||
The grain name
|
||||
|
||||
:param destructive: If destructive is True, delete the entire grain. If
|
||||
destructive
|
||||
If destructive is True, delete the entire grain. If
|
||||
destructive is False, set the grain's value to None. Defaults to False.
|
||||
|
||||
:param force: If force is True, the existing grain will be overwritten
|
||||
force
|
||||
If force is True, the existing grain will be overwritten
|
||||
regardless of its existing or provided value type. Defaults to False
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
:param delimiter: A delimiter different from the default can be provided.
|
||||
delimiter
|
||||
A delimiter different from the default can be provided.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
.. versionchanged:: v2015.8.2
|
||||
|
||||
.. versionchanged:: 2016.3.0
|
||||
This state now support nested grains and complex values. It is also more
|
||||
conservative: if a grain has a value that is a list or a dict, it will
|
||||
not be removed unless the `force` parameter is True.
|
||||
|
@ -367,13 +378,15 @@ def append(name, value, convert=False,
|
|||
value
|
||||
The value to append
|
||||
|
||||
:param convert: If convert is True, convert non-list contents into a list.
|
||||
convert
|
||||
If convert is True, convert non-list contents into a list.
|
||||
If convert is False and the grain contains non-list contents, an error
|
||||
is given. Defaults to False.
|
||||
|
||||
:param delimiter: A delimiter different from the default can be provided.
|
||||
delimiter
|
||||
A delimiter different from the default can be provided.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
.. versionadded:: v2015.8.2
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
|
|
@ -274,6 +274,11 @@ def managed(name, ppa=None, **kwargs):
|
|||
ret['result'] = False
|
||||
ret['comment'] = 'You may not use both "keyid"/"keyserver" and ' \
|
||||
'"key_url" argument.'
|
||||
if 'repo' in kwargs:
|
||||
ret['result'] = False
|
||||
ret['comment'] = ('\'repo\' is not a supported argument for this '
|
||||
'state. The \'name\' argument is probably what was '
|
||||
'intended.')
|
||||
return ret
|
||||
|
||||
repo = name
|
||||
|
|
|
@ -65,13 +65,21 @@ import time
|
|||
# Import Salt libs
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
__virtualname__ = 'service'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only make these states available if a service provider has been detected or
|
||||
assigned for this minion
|
||||
'''
|
||||
return 'service.start' in __salt__
|
||||
if 'service.start' in __salt__:
|
||||
return __virtualname__
|
||||
else:
|
||||
return (False, 'No service execution module loaded: '
|
||||
'check support for service management on {0} '
|
||||
''.format(__grains__.get('osfinger', __grains__['os']))
|
||||
)
|
||||
|
||||
|
||||
def _enabled_used_error(ret):
|
||||
|
@ -283,7 +291,7 @@ def _available(name, ret):
|
|||
|
||||
def running(name, enable=None, sig=None, init_delay=None, **kwargs):
|
||||
'''
|
||||
Verify that the service is running
|
||||
Ensure that the service is running
|
||||
|
||||
name
|
||||
The name of the init or rc script used to manage the service
|
||||
|
@ -473,7 +481,7 @@ def dead(name, enable=None, sig=None, **kwargs):
|
|||
|
||||
def enabled(name, **kwargs):
|
||||
'''
|
||||
Verify that the service is enabled on boot, only use this state if you
|
||||
Ensure that the service is enabled on boot, only use this state if you
|
||||
don't want to manage the running process, remember that if you want to
|
||||
enable a running service to use the enable: True option for the running
|
||||
or dead function.
|
||||
|
@ -492,7 +500,7 @@ def enabled(name, **kwargs):
|
|||
|
||||
def disabled(name, **kwargs):
|
||||
'''
|
||||
Verify that the service is disabled on boot, only use this state if you
|
||||
Ensure that the service is disabled on boot, only use this state if you
|
||||
don't want to manage the running process, remember that if you want to
|
||||
disable a service to use the enable: False option for the running or dead
|
||||
function.
|
||||
|
|
59
tests/integration/modules/saltutil.py
Normal file
59
tests/integration/modules/saltutil.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Integration tests for the saltutil module.
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing libs
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt libs
|
||||
import integration
|
||||
|
||||
|
||||
class SaltUtilModuleTest(integration.ModuleCase):
|
||||
'''
|
||||
Testcase for the saltutil execution module
|
||||
'''
|
||||
|
||||
# Tests for the wheel function
|
||||
|
||||
def test_wheel_just_function(self):
|
||||
'''
|
||||
Tests using the saltutil.wheel function when passing only a function.
|
||||
'''
|
||||
ret = self.run_function('saltutil.wheel', ['minions.connected'])
|
||||
self.assertEqual(ret, ['minion', 'sub_minion'])
|
||||
|
||||
def test_wheel_with_arg(self):
|
||||
'''
|
||||
Tests using the saltutil.wheel function when passing a function and an arg.
|
||||
'''
|
||||
ret = self.run_function('saltutil.wheel', ['key.list', 'minion'])
|
||||
self.assertEqual(ret, {})
|
||||
|
||||
def test_wheel_no_arg_raise_error(self):
|
||||
'''
|
||||
Tests using the saltutil.wheel function when passing a function that requires
|
||||
an arg, but one isn't supplied.
|
||||
'''
|
||||
self.assertRaises(TypeError, 'saltutil.wheel', ['key.list'])
|
||||
|
||||
def test_wheel_with_kwarg(self):
|
||||
'''
|
||||
Tests using the saltutil.wheel function when passing a function and a kwarg.
|
||||
This function just generates a key pair, but doesn't do anything with it. We
|
||||
just need this for testing purposes.
|
||||
'''
|
||||
ret = self.run_function('saltutil.wheel', ['key.gen'], keysize=1024)
|
||||
self.assertIn('pub', ret)
|
||||
self.assertIn('priv', ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(SaltUtilModuleTest)
|
|
@ -66,6 +66,7 @@ class SysModuleTest(integration.ModuleCase):
|
|||
'runtests_decorators.depends_will_fallback',
|
||||
'runtests_decorators.missing_depends',
|
||||
'runtests_decorators.missing_depends_will_fallback',
|
||||
'swift.head',
|
||||
'yumpkg.expand_repo_def',
|
||||
'yumpkg5.expand_repo_def',
|
||||
'container_resource.run',
|
||||
|
|
|
@ -9,6 +9,7 @@ from __future__ import absolute_import
|
|||
# Import Salt Testing Libs
|
||||
from salttesting import TestCase, skipIf
|
||||
from salttesting.mock import (
|
||||
Mock,
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
|
@ -413,6 +414,39 @@ class ZypperTestCase(TestCase):
|
|||
self.assertEqual(r_info['enabled'], alias == 'SLE12-SP1-x86_64-Update')
|
||||
self.assertEqual(r_info['autorefresh'], alias == 'SLE12-SP1-x86_64-Update')
|
||||
|
||||
def test_modify_repo_gpg_auto_import_keys_parameter_position(self):
|
||||
'''
|
||||
Tests if when modifying a repo, --gpg-auto-import-keys is a global option
|
||||
|
||||
:return:
|
||||
'''
|
||||
zypper_patcher = patch.multiple(
|
||||
'salt.modules.zypper',
|
||||
**{
|
||||
'_get_configured_repos': Mock(
|
||||
**{
|
||||
'return_value.sections.return_value': ['mock-repo-name']
|
||||
}
|
||||
),
|
||||
'__zypper__': Mock(),
|
||||
'get_repo': Mock()
|
||||
}
|
||||
)
|
||||
with zypper_patcher:
|
||||
zypper.mod_repo(
|
||||
'mock-repo-name',
|
||||
**{
|
||||
'disabled': False,
|
||||
'url': 'http://repo.url/some/path',
|
||||
'gpgkey': 'http://repo.key',
|
||||
'refresh': True,
|
||||
'gpgautoimport': True
|
||||
}
|
||||
)
|
||||
zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
|
||||
'--gpg-auto-import-keys', 'mr', '--refresh', 'mock-repo-name'
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(ZypperTestCase, needs_daemon=False)
|
||||
|
|
87
tests/unit/runners/jobs_test.py
Normal file
87
tests/unit/runners/jobs_test.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
unit tests for the jobs runner
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
patch
|
||||
)
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.runners import jobs
|
||||
import salt.minion
|
||||
|
||||
jobs.__opts__ = {'ext_job_cache': None, 'master_job_cache': 'local_cache'}
|
||||
jobs.__salt__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class JobsTest(TestCase):
|
||||
'''
|
||||
Validate the jobs runner
|
||||
'''
|
||||
def test_list_jobs_with_search_target(self):
|
||||
'''
|
||||
test jobs.list_jobs runner with search_target args
|
||||
'''
|
||||
mock_jobs_cache = {
|
||||
'20160524035503086853': {'Arguments': [],
|
||||
'Function': 'test.ping',
|
||||
'StartTime': '2016, May 24 03:55:03.086853',
|
||||
'Target': 'node-1-1.com',
|
||||
'Target-type': 'glob',
|
||||
'User': 'root'},
|
||||
'20160524035524895387': {'Arguments': [],
|
||||
'Function': 'test.ping',
|
||||
'StartTime': '2016, May 24 03:55:24.895387',
|
||||
'Target': ['node-1-2.com', 'node-1-1.com'],
|
||||
'Target-type': 'list',
|
||||
'User': 'sudo_ubuntu'}
|
||||
}
|
||||
|
||||
def return_mock_jobs():
|
||||
return mock_jobs_cache
|
||||
|
||||
class MockMasterMinion(object):
|
||||
|
||||
returners = {'local_cache.get_jids': return_mock_jobs}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
returns = {'all': mock_jobs_cache,
|
||||
'node-1-1.com': mock_jobs_cache,
|
||||
'node-1-2.com': {'20160524035524895387':
|
||||
mock_jobs_cache['20160524035524895387']},
|
||||
'non-existant': {}}
|
||||
|
||||
with patch.object(salt.minion, 'MasterMinion', MockMasterMinion):
|
||||
self.assertEqual(jobs.list_jobs(), returns['all'])
|
||||
|
||||
self.assertEqual(jobs.list_jobs(search_target=['node-1-1*',
|
||||
'node-1-2*']),
|
||||
returns['all'])
|
||||
|
||||
self.assertEqual(jobs.list_jobs(search_target='node-1-1.com'),
|
||||
returns['node-1-1.com'])
|
||||
|
||||
self.assertEqual(jobs.list_jobs(search_target='node-1-2.com'),
|
||||
returns['node-1-2.com'])
|
||||
|
||||
self.assertEqual(jobs.list_jobs(search_target='non-existant'),
|
||||
returns['non-existant'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(JobsTest, needs_daemon=False)
|
Loading…
Add table
Reference in a new issue