Merge branch '2016.11' into 'develop'

Conflicts:
  - doc/ref/modules/all/salt.modules.napalm_route.rst
  - doc/ref/modules/all/salt.modules.napalm_snmp.rst
  - doc/ref/modules/all/salt.modules.napalm_users.rst
  - doc/ref/states/all/salt.states.netsnmp.rst
  - doc/ref/states/all/salt.states.netusers.rst
  - salt/states/cron.py
This commit is contained in:
rallytime 2016-12-22 12:15:06 -07:00
commit a2927226ea
34 changed files with 668 additions and 179 deletions

View File

@ -127,9 +127,13 @@
# the jobs system and is not generally recommended.
#job_cache: True
# Cache minion grains and pillar data in the cachedir.
# Cache minion grains, pillar and mine data via the cache subsystem in the
# cachedir or a database.
#minion_data_cache: True
# Cache subsystem module to use for minion data cache.
#cache: localfs
# Store all returns in the given returner.
# Setting this option requires that any returner-specific configuration also
# be set. See various returners in salt/returners for details on required

View File

@ -801,8 +801,16 @@
###### Returner settings ######
############################################
# Which returner(s) will be used for minion's result:
# Default Minion returners. Can be a comma delimited string or a list:
#
#return: mysql
#
#return: mysql,slack,redis
#
#return:
# - mysql
# - hipchat
# - slack
###### Miscellaneous settings ######

View File

@ -49,6 +49,13 @@ class Mock(object):
else:
data = Mock(mapping=self.__mapping)
return data
def __iter__(self):
return self
def next(self):
raise StopIteration
# pylint: enable=R0903
MOCK_MODULES = [

View File

@ -459,14 +459,28 @@ jobs dir.
Default: ``True``
The minion data cache is a cache of information about the minions stored on the
master, this information is primarily the pillar and grains data. The data is
cached in the Master cachedir under the name of the minion and used to
predetermine what minions are expected to reply from executions.
master, this information is primarily the pillar, grains and mine data. The data
is cached via the cache subsystem in the Master cachedir under the name of the
minion or in a supported database. The data is used to predetermine what minions
are expected to reply from executions.
.. code-block:: yaml
minion_data_cache: True
.. conf_master:: cache
``cache``
---------------------
Default: ``localfs``
Cache subsystem module to use for minion data cache.
.. code-block:: yaml
cache: consul
.. conf_master:: ext_job_cache
``ext_job_cache``
@ -2402,7 +2416,7 @@ exposed.
- 'mail\d+.mydomain.tld'
.. _pillar-configuration:
.. _pillar-configuration-master:
Pillar Configuration
====================

View File

@ -456,26 +456,6 @@ executed. By default this feature is disabled, to enable set cache_jobs to
cache_jobs: False
.. conf_minion:: minion_pillar_cache
``minion_pillar_cache``
-----------------------
Default: ``False``
The minion can locally cache rendered pillar data under
:conf_minion:`cachedir`/pillar. This allows a temporarily disconnected minion
to access previously cached pillar data by invoking salt-call with the --local
and --pillar_root=:conf_minion:`cachedir`/pillar options. Before enabling this
setting consider that the rendered pillar may contain security sensitive data.
Appropriate access restrictions should be in place. By default the saved pillar
data will be readable only by the user account running salt. By default this
feature is disabled, to enable set minion_pillar_cache to ``True``.
.. code-block:: yaml
minion_pillar_cache: False
.. conf_minion:: grains
``grains``
@ -1557,8 +1537,10 @@ sha512 are also supported.
hash_type: sha256
Pillar Settings
===============
.. _pillar-configuration-minion:
Pillar Configuration
====================
.. conf_minion:: pillar_roots
@ -1629,6 +1611,28 @@ Set this option to ``True`` to force a ``KeyError`` to be raised whenever an
attempt to retrieve a named value from pillar fails. When this option is set
to ``False``, the failed attempt returns an empty string.
.. conf_minion:: minion_pillar_cache
``minion_pillar_cache``
-----------------------
.. versionadded:: 2016.3.0
Default: ``False``
The minion can locally cache rendered pillar data under
:conf_minion:`cachedir`/pillar. This allows a temporarily disconnected minion
to access previously cached pillar data by invoking salt-call with the --local
and --pillar_root=:conf_minion:`cachedir`/pillar options. Before enabling this
setting consider that the rendered pillar may contain security sensitive data.
Appropriate access restrictions should be in place. By default the saved pillar
data will be readable only by the user account running salt. By default this
feature is disabled, to enable set minion_pillar_cache to ``True``.
.. code-block:: yaml
minion_pillar_cache: False
.. conf_minion:: file_recv_max_size
``file_recv_max_size``

View File

@ -347,6 +347,7 @@ execution modules
ssh
ssh_package
ssh_service
snapper
state
status
stormpath

View File

@ -1,7 +1,7 @@
================================
salt.modules.napalm_route module
================================
.. automodule:: salt.modules.napalm_route
:members:
:undoc-members:

View File

@ -1,7 +1,7 @@
===============================
salt.modules.napalm_snmp module
===============================
.. automodule:: salt.modules.napalm_snmp
:members:
:undoc-members:

View File

@ -1,7 +1,7 @@
================================
salt.modules.napalm_users module
================================
.. automodule:: salt.modules.napalm_users
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
salt.modules.snapper module
===========================
.. automodule:: salt.modules.snapper
:members:
:undoc-members:

View File

@ -8,8 +8,14 @@ Pillars
Salt includes a number of built-in external pillars, listed at
:ref:`all-salt.pillars`.
You may also wish to look at the standard pillar documentation, at
:ref:`pillar-configuration`
The below links contain documentation for the configuration options
- :ref:`master-side configuration <pillar-configuration-master>`
- :ref:`minion-side configuration <pillar-configuration-minion>`
Note that some of same the configuration options from the master are present in
the minion configuration file, these are used in :ref:`masterless
<tutorial-standalone-minion>` mode.
The source for the built-in Salt pillars can be found here:
:blob:`salt/pillar`
:blob:`salt/pillar`

View File

@ -205,6 +205,7 @@ state modules
slack
smartos
smtp
snapper
splunk
splunk_search
sqlite3

View File

@ -4,4 +4,5 @@ salt.states.netsnmp
.. automodule:: salt.states.netsnmp
:members:
:undoc-members:

View File

@ -4,4 +4,5 @@ salt.states.netusers
.. automodule:: salt.states.netusers
:members:
:undoc-members:

View File

@ -12,24 +12,25 @@ Pillar was added to Salt in version 0.9.8
.. note:: Storing sensitive data
Unlike state tree, pillar data is only available for the targeted
minion specified by the matcher type. This makes it useful for
storing sensitive data specific to a particular minion.
Pillar data is compiled on the master. Additionally, pillar data for a
given minion is only accessible by the minion for which it is targeted in
the pillar configuration. This makes pillar useful for storing sensitive
data specific to a particular minion.
Declaring the Master Pillar
===========================
The Salt Master server maintains a pillar_roots setup that matches the
structure of the file_roots used in the Salt file server. Like the
Salt file server the ``pillar_roots`` option in the master config is based
on environments mapping to directories. The pillar data is then mapped to
minions based on matchers in a top file which is laid out in the same way
as the state top file. Salt pillars can use the same matcher types as the
standard top file.
The Salt Master server maintains a :conf_master:`pillar_roots` setup that
matches the structure of the :conf_master:`file_roots` used in the Salt file
server. Like :conf_master:`file_roots`, the :conf_master:`pillar_roots` option
mapps environments to directories. The pillar data is then mapped to minions
based on matchers in a top file which is laid out in the same way as the state
top file. Salt pillars can use the same matcher types as the standard :ref:`top
file <states-top>`.
The configuration for the :conf_master:`pillar_roots` in the master config file
is identical in behavior and function as :conf_master:`file_roots`:
conf_master:`pillar_roots` is configured just like :conf_master:`file_roots`.
For example:
.. code-block:: yaml
@ -149,10 +150,15 @@ And the actual pillar file at '/srv/pillar/common_pillar.sls':
foo: bar
boo: baz
Pillar namespace flattened
==========================
Pillar Namespace Flattening
===========================
The separate pillar files all share the same namespace. Given a ``top.sls`` of:
The separate pillar SLS files all merge down into a single dictionary of
key-value pairs. When the same key is defined in multiple SLS files, this can
result in unexpected behavior if care is not taken to how the pillar SLS files
are laid out.
For example, given a ``top.sls`` containing the following:
.. code-block:: yaml
@ -161,44 +167,49 @@ The separate pillar files all share the same namespace. Given a ``top.sls`` of:
- packages
- services
a ``packages.sls`` file of:
with ``packages.sls`` containing:
.. code-block:: yaml
bind: bind9
and a ``services.sls`` file of:
and ``services.sls`` containing:
.. code-block:: yaml
bind: named
Then a request for the ``bind`` pillar will only return ``named``; the
``bind9`` value is not available. It is better to structure your pillar files
with more hierarchy. For example your ``package.sls`` file could look like:
Then a request for the ``bind`` pillar key will only return ``named``. The
``bind9`` value will be lost, because ``services.sls`` was evaluated later.
.. note::
Pillar files are applied in the order they are listed in the top file.
Therefore conflicting keys will be overwritten in a 'last one wins' manner!
For example, in the above scenario conflicting key values in ``services``
will overwrite those in ``packages`` because it's at the bottom of the list.
It can be better to structure your pillar files with more hierarchy. For
example the ``package.sls`` file could be configured like so:
.. code-block:: yaml
packages:
bind: bind9
Pillar Namespace Merges
=======================
This would make the ``packages`` pillar key a nested dictionary containing a
``bind`` key.
With some care, the pillar namespace can merge content from multiple pillar
files under a single key, so long as conflicts are avoided as described above.
Pillar Dictionary Merging
=========================
For example, if the above example were modified as follows, the values are
merged below a single key:
If the same pillar key is defined in multiple pillar SLS files, and the keys in
both files refer to nested dictionaries, then the content from these
dictionaries will be recursively merged.
.. code-block:: yaml
For example, keeping the ``top.sls`` the same, assume the following
modifications to the pillar SLS files:
base:
'*':
- packages
- services
And a ``packages.sls`` file like:
``packages.sls``:
.. code-block:: yaml
@ -206,7 +217,7 @@ And a ``packages.sls`` file like:
package-name: bind9
version: 9.9.5
And a ``services.sls`` file like:
``services.sls``:
.. code-block:: yaml
@ -214,7 +225,7 @@ And a ``services.sls`` file like:
port: 53
listen-on: any
The resulting pillar will be as follows:
The resulting pillar dictionary will be:
.. code-block:: bash
@ -230,11 +241,9 @@ The resulting pillar will be as follows:
version:
9.9.5
.. note::
Pillar files are applied in the order they are listed in the top file.
Therefore conflicting keys will be overwritten in a 'last one wins' manner!
For example, in the above scenario conflicting key values in ``services``
will overwrite those in ``packages`` because it's at the bottom of the list.
Since both pillar SLS files contained a ``bind`` key which contained a nested
dictionary, the pillar dictionary's ``bind`` key contains the combined contents
of both SLS files' ``bind`` keys.
Including Other Pillars
=======================
@ -266,34 +275,118 @@ With this form, the included file (users.sls) will be nested within the 'users'
key of the compiled pillar. Additionally, the 'sudo' value will be available
as a template variable to users.sls.
.. _pillar-in-memory:
Viewing Minion Pillar
=====================
In-Memory Pillar Data vs. On-Demand Pillar Data
===============================================
Once the pillar is set up the data can be viewed on the minion via the
``pillar`` module, the pillar module comes with functions,
:mod:`pillar.items <salt.modules.pillar.items>` and :mod:`pillar.raw
<salt.modules.pillar.raw>`. :mod:`pillar.items <salt.modules.pillar.items>`
will return a freshly reloaded pillar and :mod:`pillar.raw
<salt.modules.pillar.raw>` will return the current pillar without a refresh:
Since compiling pillar data is computationally expensive, the minion will
maintain a copy of the pillar data in memory to avoid needing to ask the master
to recompile and send it a copy of the pillar data each time pillar data is
requested. This in-memory pillar data is what is returned by the
:py:func:`pillar.item <salt.modules.pillar.item>`, :py:func:`pillar.get
<salt.modules.pillar.get>`, and :py:func:`pillar.raw <salt.modules.pillar.raw>`
functions.
Also, for those writing custom execution modules, or contributing to Salt's
existing execution modules, the in-memory pillar data is available as the
``__pillar__`` dunder dictionary.
The in-memory pillar data is generated on minion start, and can be refreshed
using the :py:func:`saltutil.refresh_pillar
<salt.modules.saltutil.refresh_pillar>` function:
.. code-block:: bash
salt '*' pillar.items
salt '*' saltutil.refresh_pillar
This function triggers the minion to asynchronously refresh the in-memory
pillar data and will always return ``None``.
In contrast to in-memory pillar data, certain actions trigger pillar data to be
compiled to ensure that the most up-to-date pillar data is available. These
actions include:
- Running states
- Running :py:func:`pillar.items <salt.modules.pillar.items>`
Performing these actions will *not* refresh the in-memory pillar data. So, if
pillar data is modified, and then states are run, the states will see the
updated pillar data, but :py:func:`pillar.item <salt.modules.pillar.item>`,
:py:func:`pillar.get <salt.modules.pillar.get>`, and :py:func:`pillar.raw
<salt.modules.pillar.raw>` will not see this data unless refreshed using
:py:func:`saltutil.refresh_pillar <salt.modules.saltutil.refresh_pillar>`.
How Pillar Environments Are Handled
===================================
When multiple pillar environments are used, the default behavior is for the
pillar data from all environments to be merged together. The pillar dictionary
will therefore contain keys from all configured environments.
The :conf_minion:`pillarenv` minion config option can be used to force the
minion to only consider pillar configuration from a single environment. This
can be useful in cases where one needs to run states with alternate pillar
data, either in a testing/QA environment or to test changes to the pillar data
before pushing them live.
For example, assume that the following is set in the minion config file:
.. code-block:: yaml
pillarenv: base
This would cause that minion to ignore all other pillar environments besides
``base`` when compiling the in-memory pillar data. Then, when running states,
the ``pillarenv`` CLI argument can be used to override the minion's
:conf_minion:`pillarenv` config value:
.. code-block:: bash
salt '*' state.apply mystates pillarenv=testing
The above command will run the states with pillar data sourced exclusively from
the ``testing`` environment, without modifying the in-memory pillar data.
.. note::
Prior to version 0.16.2, this function is named ``pillar.data``. This
function name is still supported for backwards compatibility.
When running states, the ``pillarenv`` CLI option does not require a
:conf_minion:`pillarenv` option to be set in the minion config file. When
:conf_minion:`pillarenv` is left unset, as mentioned above all configured
environments will be combined. Running states with ``pillarenv=testing`` in
this case would still restrict the states' pillar data to just that of the
``testing`` pillar environment.
Viewing Pillar Data
===================
To view pillar data, use the :mod:`pillar <salt.modules.pillar>` execution
module. This module includes several functions, each of them with their own
use. These functions include:
- :py:func:`pillar.item <salt.modules.pillar.item>` - Retrieves the value of
one or more keys from the :ref:`in-memory pillar datj <pillar-in-memory>`.
- :py:func:`pillar.items <salt.modules.pillar.items>` - Compiles a fresh pillar
dictionary and returns it, leaving the :ref:`in-memory pillar data
<pillar-in-memory>` untouched. If pillar keys are passed to this function
however, this function acts like :py:func:`pillar.item
<salt.modules.pillar.item>` and returns their values from the :ref:`in-memory
pillar data <pillar-in-memory>`.
- :py:func:`pillar.raw <salt.modules.pillar.raw>` - Like :py:func:`pillar.items
<salt.modules.pillar.items>`, it returns the entire pillar dictionary, but
from the :ref:`in-memory pillar data <pillar-in-memory>` instead of compiling
fresh pillar data.
- :py:func:`pillar.get <salt.modules.pillar.get>` - Described in detail below.
Pillar "get" Function
=====================
The :py:func:`pillar.get <salt.modules.pillar.get>` Function
============================================================
.. versionadded:: 0.14.0
The :mod:`pillar.get <salt.modules.pillar.get>` function works much in the same
way as the ``get`` method in a python dict, but with an enhancement: nested
dict components can be extracted using a `:` delimiter.
dictonaries can be traversed using a colon as a delimiter.
If a structure like this is in pillar:
@ -330,23 +423,8 @@ This makes handling nested structures much easier.
'qux')``) to get the salt function, instead of the default dictionary
behavior.
Refreshing Pillar Data
======================
When pillar data is changed on the master the minions need to refresh the data
locally. This is done with the ``saltutil.refresh_pillar`` function.
.. code-block:: bash
salt '*' saltutil.refresh_pillar
This function triggers the minion to asynchronously refresh the pillar and will
always return ``None``.
Set Pillar Data at the Command Line
===================================
Setting Pillar Data at the Command Line
=======================================
Pillar data can be set at the command line like the following example:
@ -354,7 +432,7 @@ Pillar data can be set at the command line like the following example:
salt '*' state.apply pillar='{"cheese": "spam"}'
This will add a Pillar key of ``cheese`` with its value set to ``spam``.
This will add a pillar key of ``cheese`` with its value set to ``spam``.
.. note::
@ -364,7 +442,7 @@ This will add a Pillar key of ``cheese`` with its value set to ``spam``.
a security concern in some cases.
Master Config In Pillar
Master Config in Pillar
=======================
For convenience the data stored in the master configuration file can be made
@ -372,14 +450,13 @@ available in all minion's pillars. This makes global configuration of services
and systems very easy but may not be desired if sensitive data is stored in the
master configuration. This option is disabled by default.
To enable the master config from being added to the pillar set ``pillar_opts``
to ``True``:
To enable the master config from being added to the pillar set
:conf_minion:`pillar_opts` to ``True`` in the minion config file:
.. code-block:: yaml
pillar_opts: True
Minion Config in Pillar
=======================

View File

@ -139,7 +139,7 @@ case "$1" in
RETVAL=$?
fi
;;
condrestart)
condrestart|try-restart)
[ -f $LOCKFILE ] && restart || :
;;
reload)
@ -147,7 +147,7 @@ case "$1" in
RETVAL=1
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload}"
exit 1
;;
esac

View File

@ -122,7 +122,7 @@ case "$1" in
RETVAL=$?
fi
;;
condrestart)
condrestart|try-restart)
[ -f $LOCKFILE ] && restart || :
;;
reload)
@ -130,7 +130,7 @@ case "$1" in
RETVAL=1
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload}"
exit 1
;;
esac

View File

@ -129,7 +129,7 @@ case "$1" in
RETVAL=$?
fi
;;
condrestart)
condrestart|try-restart)
[ -f $LOCKFILE ] && restart || :
;;
reload)
@ -137,7 +137,7 @@ case "$1" in
RETVAL=1
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload}"
exit 1
;;
esac

View File

@ -123,7 +123,7 @@ case "$1" in
RETVAL=$?
fi
;;
condrestart)
condrestart|try-restart)
[ -f $LOCKFILE ] && restart || :
;;
reload)
@ -131,7 +131,7 @@ case "$1" in
RETVAL=$?
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload}"
exit 1
;;
esac

View File

@ -22,7 +22,7 @@ class Cache(object):
:param cache:
The name of the cache driver to use. This is the name of the python
module of the `salt.cache` package. Defult is `localfs`.
module of the `salt.cache` package. Default is `localfs`.
:param serial:
The module of `salt.serializers` package that should be used by the cache

172
salt/cache/consul.py vendored Normal file
View File

@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
'''
Minion data cache plugin for Consul key/value data store.
It is up to the system administrator to set up and configure the Consul
infrastructure. All is needed for this plugin is a working Consul agent
with a read-write access to the key-value storae.
The related documentation can be found here: https://www.consul.io/docs/index.html
To enable this cache plugin the master will need the python client for
Consul installed that could be easily done with `pip install python-consul`.
Optionally depending on the Consul agent configuration the following values
could be set in the master config, these are the defaults:
.. code-block:: yaml
consul.host: 127.0.0.1
consul.port: 8500
consul.token: None
consul.scheme: http
consul.consistency: default
consul.dc: None
consul.verify: True
Related docs could be found here:
* python-consul: https://python-consul.readthedocs.io/en/latest/#consul
To use the consul as a minion data cache backend set the master `cache` config
value to `consul`:
.. code-block:: yaml
cache: consul
.. versionadded:: 2016.11.2
'''
from __future__ import absolute_import
import logging
try:
import consul
HAS_CONSUL = True
except ImportError:
HAS_CONSUL = False
from salt.exceptions import SaltCacheError
log = logging.getLogger(__name__)
api = None
# Define the module's virtual name
__virtualname__ = 'consul'
def __virtual__():
'''
Confirm this python-consul package is installed
'''
if not HAS_CONSUL:
return (False, "Please install python-consul package to use consul data cache driver")
consul_kwargs = {
'host': __opts__.get('consul.host', '127.0.0.1'),
'port': __opts__.get('consul.port', 8500),
'token': __opts__.get('consul.token', None),
'scheme': __opts__.get('consul.scheme', 'http'),
'consistency': __opts__.get('consul.consistency', 'default'),
'dc': __opts__.get('consul.dc', None),
'verify': __opts__.get('consul.verify', True),
}
global api
api = consul.Consul(**consul_kwargs)
return __virtualname__
def store(bank, key, data):
'''
Store a key value.
'''
c_key = '{0}/{1}'.format(bank, key)
try:
c_data = __context__['serial'].dumps(data)
api.kv.put(c_key, c_data)
except Exception as exc:
raise SaltCacheError(
'There was an error writing the key, {0}: {1}'.format(
c_key, exc
)
)
def fetch(bank, key):
'''
Fetch a key value.
'''
c_key = '{0}/{1}'.format(bank, key)
try:
_, value = api.kv.get(c_key)
if value is None:
return value
return __context__['serial'].loads(value['Value'])
except Exception as exc:
raise SaltCacheError(
'There was an error reading the key, {0}: {1}'.format(
c_key, exc
)
)
def flush(bank, key=None):
'''
Remove the key from the cache bank with all the key content.
'''
if key is None:
c_key = bank
else:
c_key = '{0}/{1}'.format(bank, key)
try:
return api.kv.delete(c_key, recurse=key is None)
except Exception as exc:
raise SaltCacheError(
'There was an error removing the key, {0}: {1}'.format(
c_key, exc
)
)
def list(bank):
'''
Return an iterable object containing all entries stored in the specified bank.
'''
try:
_, keys = api.kv.get(bank + '/', keys=True, separator='/')
except Exception as exc:
raise SaltCacheError(
'There was an error getting the key "{0}": {1}'.format(
bank, exc
)
)
if keys is None:
keys = []
else:
# Any key could be a branch and a leaf at the same time in Consul
# so we have to return a list of unique names only.
out = set()
for key in keys:
out.add(key[len(bank) + 1:].rstrip('/'))
keys = list(out)
return keys
def contains(bank, key):
'''
Checks if the specified bank contains the specified key.
'''
if key is None:
return True # any key could be a branch and a leaf at the same time in Consul
else:
try:
c_key = '{0}/{1}'.format(bank, key)
_, value = api.kv.get(c_key)
except Exception as exc:
raise SaltCacheError(
'There was an error getting the key, {0}: {1}'.format(
c_key, exc
)
)
return value is not None

View File

@ -943,6 +943,9 @@ VALID_OPTS = {
'thin_extra_mods': str,
'min_extra_mods': str,
# Default returners minion should use. List or comma-delimited string
'return': (str, list),
# TLS/SSL connection options. This could be set to a dictionary containing arguments
# corresponding to python ssl.wrap_socket method. For details see:
# http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer
@ -1697,6 +1700,10 @@ def _validate_opts(opts):
format_multi_opt(VALID_OPTS[key]))
)
# Convert list to comma-delimited string for 'return' config option
if isinstance(opts.get('return'), list):
opts['return'] = ','.join(opts['return'])
# RAET on Windows uses 'win32file.CreateMailslot()' for IPC. Due to this,
# sock_dirs must start with '\\.\mailslot\' and not contain any colons.
# We don't expect the user to know this, so we will fix up their path for

View File

@ -1567,6 +1567,15 @@ class Minion(MinionBase):
ret,
timeout=minion_instance._return_retry_timer()
)
# Add default returners from minion config
# Should have been coverted to comma-delimited string already
if isinstance(opts.get('return'), six.string_types):
if data['ret']:
data['ret'] = ','.join((data['ret'], opts['return']))
else:
data['ret'] = opts['return']
# TODO: make a list? Seems odd to split it this late :/
if data['ret'] and isinstance(data['ret'], six.string_types):
if 'ret_config' in data:

View File

@ -244,18 +244,35 @@ def item(*args, **kwargs):
Return one or more pillar entries
pillar
if specified, allows for a dictionary of pillar data to be made
If specified, allows for a dictionary of pillar data to be made
available to pillar and ext_pillar rendering. these pillar variables
will also override any variables of the same name in pillar or
ext_pillar.
.. versionadded:: 2015.5.0
delimiter
Delimiter used to traverse nested dictionaries.
.. note::
This is different from :py:func:`pillar.get
<salt.modules.pillar.get>` in that no default value can be
specified. :py:func:`pillar.get <salt.modules.pillar.get>` should
probably still be used in most cases to retrieve nested pillar
values, as it is a bit more flexible. One reason to use this
function instead of :py:func:`pillar.get <salt.modules.pillar.get>`
however is when it is desirable to retrieve the values of more than
one key, since :py:func:`pillar.get <salt.modules.pillar.get>` can
only retrieve one key at a time.
.. versionadded:: 2015.8.0
CLI Examples:
.. code-block:: bash
salt '*' pillar.item foo
salt '*' pillar.item foo:bar
salt '*' pillar.item foo bar baz
'''
ret = {}

View File

@ -970,15 +970,40 @@ def freeze(bin_env=None,
cwd
Current working directory to run pip from
.. note::
If the version of pip available is older than 8.0.3, the list will not
include the packages pip, wheel, setuptools, or distribute even if they
are installed.
CLI Example:
.. code-block:: bash
salt '*' pip.freeze /home/code/path/to/virtualenv/
.. versionchanged:: 2016.11.2
The packages pip, wheel, setuptools, and distribute are included if the
installed pip is new enough.
'''
pip_bin = _get_pip_bin(bin_env)
cmd = [pip_bin, 'freeze']
# Include pip, setuptools, distribute, wheel
min_version = '8.0.3'
cur_version = version(bin_env)
if not salt.utils.compare_versions(ver1=cur_version, oper='>=',
ver2=min_version):
logger.warning(
('The version of pip installed is {0}, which is older than {1}. '
'The packages pip, wheel, setuptools, and distribute will not be '
'included in the output of pip.freeze').format(cur_version,
min_version))
else:
cmd.append('--all')
cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False)
if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
@ -998,30 +1023,31 @@ def list_(prefix=None,
Filter list of installed apps from ``freeze`` and check to see if
``prefix`` exists in the list of packages installed.
.. note::
If the version of pip available is older than 8.0.3, the packages
wheel, setuptools, and distribute will not be reported by this function
even if they are installed. Unlike
:py:func:`pip.freeze <salt.modules.pip.freeze>`, this function always
reports the version of pip which is installed.
CLI Example:
.. code-block:: bash
salt '*' pip.list salt
.. versionchanged:: 2016.11.2
The packages wheel, setuptools, and distribute are included if the
installed pip is new enough.
'''
packages = {}
pip_bin = _get_pip_bin(bin_env)
cmd = [pip_bin, 'freeze']
cmd_kwargs = dict(runas=user, cwd=cwd, python_shell=False)
if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
if not prefix or prefix in ('p', 'pi', 'pip'):
if prefix is None or 'pip'.startswith(prefix):
packages['pip'] = version(bin_env)
result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
if result['retcode'] > 0:
raise CommandExecutionError(result['stderr'])
for line in result['stdout'].splitlines():
for line in freeze(bin_env=bin_env, user=user, cwd=cwd):
if line.startswith('-f') or line.startswith('#'):
# ignore -f line as it contains --find-links directory
# ignore comment lines
@ -1044,6 +1070,7 @@ def list_(prefix=None,
packages[name] = version_
else:
packages[name] = version_
return packages

View File

@ -2,6 +2,8 @@
'''
Module to manage filesystem snapshots with snapper
.. versionadded:: 2016.11.0
:codeauthor: Duncan Mac-Vicar P. <dmacvicar@suse.de>
:codeauthor: Pablo Suárez Hernández <psuarezhernandez@suse.de>

View File

@ -122,7 +122,7 @@ The corresponding Pillar top file would look like this:
.. code-block:: yaml
{{env}}:
{{saltenv}}:
'*':
- bar
@ -139,6 +139,10 @@ The corresponding Pillar top file would look like this:
to :conf_master:`gitfs_base`. This has been fixed in the 2016.3.5 and
2016.11.1 releases (2016.11.0 contains the incorrect behavior).
Additionally, in releases before 2016.11.0, both ``{{env}}`` and
``{{saltenv}}`` could be used as a placeholder for the environment.
Starting in 2016.11.0, ``{{env}}`` is no longer supported.
.. _git-pillar-2015-8-0-and-later:
Configuring git_pillar for Salt releases 2015.8.0 and later
@ -232,7 +236,7 @@ The corresponding Pillar top file would look like this:
.. code-block:: yaml
{{env}}:
{{saltenv}}:
'*':
- bar
@ -258,6 +262,10 @@ The corresponding Pillar top file would look like this:
(instead of the minion's) before falling back to the master's
:conf_master:`git_pillar_base`.
Additionally, in releases before 2016.11.0, both ``{{env}}`` and
``{{saltenv}}`` could be used as a placeholder for the environment.
Starting in 2016.11.0, ``{{env}}`` is no longer supported.
With the addition of pygit2_ support, git_pillar can now interact with
authenticated remotes. Authentication works just like in gitfs (as outlined in
the :ref:`Git Fileserver Backend Walkthrough <gitfs-authentication>`), only

View File

@ -40,29 +40,35 @@ Configuring Nodegroups Pillar
from __future__ import absolute_import
# Import Salt libs
from salt.minion import Matcher
from salt.utils.minions import CkMinions
# Import 3rd-party libs
import salt.ext.six as six
__version__ = '0.0.1'
__version__ = '0.0.2'
def ext_pillar(minion_id, pillar, pillar_name=None):
'''
A salt external pillar which provides the list of nodegroups of which the minion is a memeber.
A salt external pillar which provides the list of nodegroups of which the minion is a member.
:param minion_id: provided by salt, but not used by nodegroups ext_pillar
:param minion_id: used for compound matching nodegroups
:param pillar: provided by salt, but not used by nodegroups ext_pillar
:param pillar_name: optional name to use for the pillar, defaults to 'nodegroups'
:return: a dictionary which is included by the salt master in the pillars returned to the minion
'''
pillar_name = pillar_name or 'nodegroups'
m = Matcher(__opts__)
all_nodegroups = __opts__['nodegroups']
nodegroups_minion_is_in = []
ckminions = None
for nodegroup_name in six.iterkeys(all_nodegroups):
if m.nodegroup_match(nodegroup_name, all_nodegroups):
ckminions = ckminions or CkMinions(__opts__)
match = ckminions.check_minions(
all_nodegroups[nodegroup_name],
'compound')
if minion_id in match:
nodegroups_minion_is_in.append(nodegroup_name)
return {pillar_name: nodegroups_minion_is_in}

View File

@ -148,8 +148,8 @@ def init(opts):
log.error(
"Cannot connect to {hostname}{port} as {username}. Please check error: {error}".format(
hostname=NETWORK_DEVICE.get('HOSTNAME', ''),
port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
username=NETWORK_DEVICE.get('USERNAME', ''),
error=error
)
@ -227,8 +227,8 @@ def shutdown(opts):
log.error(
'Cannot close connection with {hostname}{port}! Please check error: {error}'.format(
hostname=NETWORK_DEVICE.get('HOSTNAME', '[unknown hostname]'),
port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
error=error
)
)
@ -289,8 +289,8 @@ def call(method, **params):
err_tb = traceback.format_exc() # let's get the full traceback and display for debugging reasons.
comment = 'Cannot execute "{method}" on {device}{port} as {user}. Reason: {error}!'.format(
device=NETWORK_DEVICE.get('HOSTNAME', '[unspecified hostname]'),
port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
user=NETWORK_DEVICE.get('USERNAME', ''),
method=method,
error=error

View File

@ -573,6 +573,7 @@ def file(name,
template,
source,
source_hash,
source_hash_name,
user,
group,
mode,

View File

@ -98,7 +98,7 @@ def query(name, match=None, match_type='string', status=None, wait_for=None, **k
ret['result'] = False
ret['comment'] += ' Match text "{0}" was not found.'.format(match)
elif match_type == 'pcre':
if re.search(match, data['text']):
if re.search(match, data.get('text', '')):
ret['result'] = True
ret['comment'] += ' Match pattern "{0}" was found.'.format(match)
else:

View File

@ -3,6 +3,8 @@
Managing implicit state and baselines using snapshots
=====================================================
.. versionadded:: 2016.11.0
Salt can manage state against explicitly defined state, for example
if your minion state is defined by:

View File

@ -900,23 +900,66 @@ class PipTestCase(TestCase):
}
)
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
ret = pip.freeze()
mock.assert_called_once_with(
['pip', 'freeze'],
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
)
self.assertEqual(ret, eggs)
with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')):
ret = pip.freeze()
mock.assert_called_once_with(
['pip', 'freeze'],
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
)
self.assertEqual(ret, eggs)
# Non zero returncode raises exception?
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
self.assertRaises(
CommandExecutionError,
pip.freeze,
)
with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')):
self.assertRaises(
CommandExecutionError,
pip.freeze,
)
def test_freeze_command_with_all(self):
eggs = [
'M2Crypto==0.21.1',
'-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev',
'bbfreeze==1.1.0',
'bbfreeze-loader==1.1.0',
'pip==0.9.1',
'pycrypto==2.6',
'setuptools==20.10.1'
]
mock = MagicMock(
return_value={
'retcode': 0,
'stdout': '\n'.join(eggs)
}
)
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
with patch('salt.modules.pip.version',
MagicMock(return_value='9.0.1')):
ret = pip.freeze()
mock.assert_called_once_with(
['pip', 'freeze', '--all'],
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
)
self.assertEqual(ret, eggs)
# Non zero returncode raises exception?
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
with patch('salt.modules.pip.version',
MagicMock(return_value='9.0.1')):
self.assertRaises(
CommandExecutionError,
pip.freeze,
)
def test_list_command(self):
eggs = [
@ -937,6 +980,7 @@ class PipTestCase(TestCase):
cwd=None,
runas=None,
python_shell=False,
use_vt=False,
)
self.assertEqual(
ret, {
@ -959,6 +1003,54 @@ class PipTestCase(TestCase):
pip.list_,
)
def test_list_command_with_all(self):
eggs = [
'M2Crypto==0.21.1',
'-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev',
'bbfreeze==1.1.0',
'bbfreeze-loader==1.1.0',
'pip==9.0.1',
'pycrypto==2.6',
'setuptools==20.10.1'
]
# N.B.: this is deliberately different from the "output" of pip freeze.
# This is to demonstrate that the version reported comes from freeze
# instead of from the pip.version function.
mock_version = '9.0.0'
mock = MagicMock(return_value={'retcode': 0, 'stdout': '\n'.join(eggs)})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
with patch('salt.modules.pip.version',
MagicMock(return_value=mock_version)):
ret = pip.list_()
mock.assert_called_with(
['pip', 'freeze', '--all'],
cwd=None,
runas=None,
python_shell=False,
use_vt=False,
)
self.assertEqual(
ret, {
'SaltTesting-dev': 'git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8',
'M2Crypto': '0.21.1',
'bbfreeze-loader': '1.1.0',
'bbfreeze': '1.1.0',
'pip': '9.0.1',
'pycrypto': '2.6',
'setuptools': '20.10.1'
}
)
# Non zero returncode raises exception?
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')):
self.assertRaises(
CommandExecutionError,
pip.list_,
)
def test_list_command_with_prefix(self):
eggs = [
'M2Crypto==0.21.1',
@ -974,19 +1066,22 @@ class PipTestCase(TestCase):
}
)
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
ret = pip.list_(prefix='bb')
mock.assert_called_with(
['pip', 'freeze'],
cwd=None,
runas=None,
python_shell=False,
)
self.assertEqual(
ret, {
'bbfreeze-loader': '1.1.0',
'bbfreeze': '1.1.0',
}
)
with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')):
ret = pip.list_(prefix='bb')
mock.assert_called_with(
['pip', 'freeze'],
cwd=None,
runas=None,
python_shell=False,
use_vt=False,
)
self.assertEqual(
ret, {
'bbfreeze-loader': '1.1.0',
'bbfreeze': '1.1.0',
}
)
def test_install_pre_argument_in_resulting_command(self):
pkg = 'pep8'

View File

@ -6,6 +6,7 @@ from __future__ import absolute_import
# Import Salt Testing libs
from salttesting import TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import patch, MagicMock
ensure_in_syspath('../../')
@ -15,25 +16,37 @@ from salt.pillar import nodegroups
fake_minion_id = 'fake_id'
fake_pillar = {}
fake_nodegroups = {
'a': fake_minion_id,
'b': 'nodegroup_b',
'groupA': fake_minion_id,
'groupB': 'another_minion_id',
}
fake_opts = {
'cache': 'localfs',
'nodegroups': fake_nodegroups,
'id': fake_minion_id
}
fake_opts = {'nodegroups': fake_nodegroups, 'id': fake_minion_id}
fake_pillar_name = 'fake_pillar_name'
nodegroups.__opts__ = fake_opts
def side_effect(group_sel, t):
if group_sel.find(fake_minion_id) != -1:
return [fake_minion_id, ]
return ['another_minion_id', ]
class NodegroupsPillarTestCase(TestCase):
'''
Tests for salt.pillar.nodegroups
'''
@patch('salt.utils.minions.CkMinions.check_minions',
MagicMock(side_effect=side_effect))
def _runner(self, expected_ret, pillar_name=None):
pillar_name = pillar_name or fake_pillar_name
actual_ret = nodegroups.ext_pillar(fake_minion_id, fake_pillar, pillar_name=pillar_name)
self.assertDictEqual(actual_ret, expected_ret)
def test_succeeds(self):
ret = {fake_pillar_name: ['a', ]}
ret = {fake_pillar_name: ['groupA', ]}
self._runner(ret)