Merge pull request #24539 from basepi/merge-forward-develop

Merge forward from 2015.5 to develop
This commit is contained in:
Colton Myers 2015-06-09 15:15:13 -06:00
commit 359efdfc1c
52 changed files with 872 additions and 191 deletions

View File

@ -15,6 +15,14 @@ Description
The salt-call command is used to run module functions locally on a minion
instead of executing them from the master.
salt-call is used to run a :ref:`Standalone Minion <tutorial-standalone-minion>`,
and was originally created for :ref:`troubleshooting <troubleshooting-minion-salt-call>`.
Be aware that ``salt-call`` commands execute from the current user's shell
context, while ``salt`` commands execute from the system's default context.
By default, the Salt Master is contacted to retrieve state files and other resources
during execution unless the ``--local`` option is specified.:w
Options
=======

View File

@ -87,11 +87,6 @@ Execution Options
for this influx of vm creation. When creating large groups of VMs watch the
cloud provider carefully.
.. option:: -Q, --query
Execute a query and print out information about all cloud VMs. Can be used
in conjunction with -m to display only information about the specified map.
.. option:: -u, --update-bootstrap
Update salt-bootstrap to the latest develop version on GitHub.

View File

@ -2437,3 +2437,15 @@ List of git repositories to include with the local repo.
win_gitrepos:
- 'https://github.com/saltstack/salt-winrepo.git'
To specify a specific revision of the repository, preface the
repository location with a commit ID:
.. code-block:: yaml
win_gitrepos:
- '<commit_id> https://github.com/saltstack/salt-winrepo.git'
Replacing ``<commit_id>`` with the ID from GitHub. Specifying a commit
ID is useful if you need to revert to a previous version if an error
is introduced in the latest version.

View File

@ -313,6 +313,22 @@ executed. By default this feature is disabled, to enable set cache_jobs to
cache_jobs: False
.. conf_minion:: grains_cache
``grains_cache``
----------------
Default: ``False``
The minion can locally cache grain data instead of refreshing the data
each time the grain is referenced. By default this feature is disabled,
to enable set grains_cache to ``True``.
.. code-block:: yaml
cache_jobs: False
.. conf_minion:: sock_dir
``sock_dir``

View File

@ -24,6 +24,21 @@ The cp module is the home of minion side file server operations. The cp module
is used by the Salt state system, salt-cp, and can be used to distribute files
presented by the Salt file server.
Escaping Special Characters
```````````````````````````
The ``salt://`` url format can potentially contain a query string, for example
``salt://dir/file.txt?saltenv=base``. You can prevent the fileclient/fileserver from
interpreting ``?`` as the initial token of a query string by referencing the file
with ``salt://|`` rather than ``salt://``.
.. code-block:: yaml
/etc/marathon/conf/?checkpoint:
file.managed:
- source: salt://|hw/config/?checkpoint
- makedirs: True
Environments
````````````

View File

@ -3,4 +3,4 @@ salt.modules.ipmi
=================
.. automodule:: salt.modules.ipmi
:members
:members:

View File

@ -158,6 +158,7 @@ Full list of builtin state modules
sysrc
test
timezone
tls
tomcat
tuned
uptime

View File

@ -554,7 +554,7 @@ at the end of a state run, after all states have completed.
configure-apache2:
file.managed:
- path: /etc/apache2/apache2.conf
- name: /etc/apache2/apache2.conf
- source: salt://apache2/apache2.conf
This example will cause apache2 to be restarted when the apache2.conf file is
@ -568,7 +568,7 @@ changed, but the apache2 restart will happen at the end of the state run.
configure-apache2:
file.managed:
- path: /etc/apache2/apache2.conf
- name: /etc/apache2/apache2.conf
- source: salt://apache2/apache2.conf
- listen_in:
- service: apache2
@ -593,7 +593,7 @@ same privileges as the salt-minion.
comment-repo:
file.replace:
- path: /etc/yum.repos.d/fedora.repo
- name: /etc/yum.repos.d/fedora.repo
- pattern: ^enabled=0
- repl: enabled=1
- check_cmd:

View File

@ -104,7 +104,7 @@ added to the folder the original VM belongs to.
host
----
The MOR of the host where the vm should be registered.
The MOR of the host where the vm should be registered.
If not specified:
* if resourcepool is not specified, the current host is used.

View File

@ -1,6 +1,8 @@
===================
.. _external-pillars:
================
External Pillars
===================
================
Salt provides a mechanism for generating pillar data by calling external
pillar interfaces. This document will describe an outline of an ext_pillar

View File

@ -212,7 +212,7 @@ To configure an LDAP group, append a ``%`` to the ID:
.. code-block:: yaml
external_auth:
ldap:
test_ldap_group%:
- '*':
- test.echo
ldap:
test_ldap_group%:
- '*':
- test.echo

View File

@ -43,8 +43,9 @@ minion exe>` should match the contents of the corresponding md5 file.
* 2014.7.0
* Salt-Minion-2014.7.0-1-win32-Setup.exe | md5
* Salt-Minion-2014.7.0-AMD64-Setup.exe | md5
.. note::
The 2014.7.0 installers have been removed because of a regression. Please use the 2014.7.1 release instead.
.. note::
The 2014.7.0 installers have been removed because of a regression. Please use the 2014.7.1 release instead.
* 2014.1.13
* `Salt-Minion-2014.1.13-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe.md5>`__

View File

@ -66,6 +66,8 @@ check that no additional access control system such as `SELinux`_ or
.. _`SELinux`: https://en.wikipedia.org/wiki/Security-Enhanced_Linux
.. _`AppArmor`: http://wiki.apparmor.net/index.php/Main_Page
.. _troubleshooting-minion-salt-call:
Using salt-call
===============

View File

@ -34,9 +34,9 @@ Variables:
minions can be defined in pillar and then accessed inside sls formulas
and template files.
Arbitrary Data:
Pillar can contain any basic data structure, so a list of values, or a
key/value store can be defined making it easy to iterate over a group
of values in sls formulas
Pillar can contain any basic data structure in dictionary format,
so a key/value store can be defined making it easy to iterate over a group
of values in sls formulas.
Pillar is therefore one of the most important systems when using Salt. This
walkthrough is designed to get a simple Pillar up and running in a few minutes
@ -309,12 +309,6 @@ line:
salt '*' state.sls my_sls_file pillar='{"hello": "world"}'
Lists can be passed in pillar as well:
.. code-block:: bash
salt '*' state.highstate pillar='["foo", "bar", "baz"]'
.. note::
If a key is passed on the command line that already exists on the minion,

View File

@ -1,3 +1,5 @@
.. _tutorial-standalone-minion:
=================
Standalone Minion
=================
@ -77,4 +79,10 @@ it unnecessary to change the configuration file:
.. code-block:: bash
salt-call state.highstate --local
salt-call state.highstate --local
External Pillars
================
:ref:`External pillars <external-pillars>` are supported when running in masterless mode.

View File

@ -185,8 +185,8 @@ and all changes made.
salt-minion -l debug
Run the minion in the foreground
By not starting the minion in daemon mode (:option:`-d <salt-minion
-d>`) one can view any output from the minion as it works:
By not starting the minion in daemon mode (:option:`-d <salt-minion -d>`)
one can view any output from the minion as it works:
.. code-block:: bash

View File

@ -153,24 +153,26 @@ def beacon(config):
The mask list can contain the following events (the default mask is create,
delete, and modify):
* access File accessed
* attrib File metadata changed
* close_nowrite Unwritable file closed
* close_write Writable file closed
* create File created in watched directory
* delete File deleted from watched directory
* delete_self Watched file or directory deleted
* modify File modified
* moved_from File moved out of watched directory
* moved_to File moved into watched directory
* move_self Watched file moved
* open File opened
* access File accessed
* attrib File metadata changed
* close_nowrite Unwritable file closed
* close_write Writable file closed
* create File created in watched directory
* delete File deleted from watched directory
* delete_self Watched file or directory deleted
* modify File modified
* moved_from File moved out of watched directory
* moved_to File moved into watched directory
* move_self Watched file moved
* open File opened
The mask can also contain the following options:
* dont_follow Don't dereference symbolic links
* excl_unlink Omit events for children after they have been unlinked
* oneshot Remove watch after one event
* onlydir Operate only if name is directory
* dont_follow Don't dereference symbolic links
* excl_unlink Omit events for children after they have been unlinked
* oneshot Remove watch after one event
* onlydir Operate only if name is directory
recurse:
Recursively watch files in the directory

View File

@ -13,6 +13,7 @@ class SaltSSH(parsers.SaltSSHOptionParser):
def run(self):
self.parse_args()
self.setup_logfile_logger()
ssh = salt.client.ssh.SSH(self.config)
ssh.run()

View File

@ -720,7 +720,8 @@ class Minion(MinionBase):
' {0}'.format(opts['master']))
if opts['master_shuffle']:
shuffle(opts['master'])
elif isinstance(opts['master'], str):
# if opts['master'] is a str and we have never created opts['master_list']
elif isinstance(opts['master'], str) and ('master_list' not in opts):
# We have a string, but a list was what was intended. Convert.
# See issue 23611 for details
opts['master'] = list(opts['master'])

View File

@ -1281,13 +1281,18 @@ def get_repo(repo, **kwargs):
ppa_name, dist)
else:
if HAS_SOFTWAREPROPERTIES:
if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'):
repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand(
__grains__['lsb_distrib_codename'])[0]
else:
repo = softwareproperties.ppa.expand_ppa_line(
repo,
__grains__['lsb_distrib_codename'])[0]
try:
if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'):
repo = softwareproperties.ppa.PPAShortcutHandler(
repo).expand(dist)[0]
else:
repo = softwareproperties.ppa.expand_ppa_line(
repo,
dist)[0]
except NameError as name_error:
raise CommandExecutionError(
'Could not find ppa {0}: {1}'.format(repo, name_error)
)
else:
repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist)

View File

@ -745,7 +745,9 @@ def shell(cmd,
.. versionadded:: 2015.5.0
.. warning:: This passes the cmd argument directly to the shell
.. warning::
This passes the cmd argument directly to the shell
without any further processing! Be absolutely sure that you
have properly santized the command passed to this function
and do not use untrusted inputs.

View File

@ -575,7 +575,8 @@ def create_container(image,
volumes_from=None,
name=None,
cpu_shares=None,
cpuset=None):
cpuset=None,
binds=None):
'''
Create a new container
@ -594,10 +595,26 @@ def create_container(image,
ports
port redirections ``({'222': {}})``
volumes
list of volume mappings::
list of volume mappings in either local volume, bound volume, or read-only
bound volume form::
(['/mountpoint/in/container:/guest/foo', '/same/path/mounted/point'])
(['/var/lib/mysql/', '/usr/local/etc/ssl:/etc/ssl', '/etc/passwd:/etc/passwd:ro'])
binds
complete dictionary of bound volume mappings::
{ '/usr/local/etc/ssl/certs/internal.crt': {
'bind': '/etc/ssl/certs/com.example.internal.crt',
'ro': True
},
'/var/lib/mysql': {
'bind': '/var/lib/mysql/',
'ro': False
}
}
This dictionary is suitable for feeding directly into the Docker API, and all
keys are required.
(see http://docker-py.readthedocs.org/en/latest/volumes/)
tty
attach ttys, Default is ``False``
stdin_open
@ -616,23 +633,31 @@ def create_container(image,
salt '*' docker.create_container o/ubuntu volumes="['/s','/m:/f']"
'''
log.trace("modules.dockerio.create_container() called for image " + image)
status = base_status.copy()
client = _get_client()
# In order to permit specification of bind volumes in the volumes field,
# we'll look through it for bind-style specs and move them. This is purely
# for CLI convenience and backwards-compatibility, as states.dockerio
# should parse volumes before this, and the binds argument duplicates this.
# N.B. this duplicates code in states.dockerio._parse_volumes()
if isinstance(volumes, list):
for volume in volumes:
if ':' in volume:
volspec = volume.split(':')
source = volspec[0]
target = volspec[1]
ro = False
try:
if len(volspec) > 2:
ro = volspec[2] == "ro"
except IndexError:
pass
binds[source] = {'bind': target, 'ro': ro}
volumes.remove(volume)
try:
mountpoints = {}
binds = {}
# create empty mountpoints for them to be
# editable
# either we have a list of guest or host:guest
if isinstance(volumes, list):
for mountpoint in volumes:
mounted = mountpoint
if ':' in mountpoint:
parts = mountpoint.split(':')
mountpoint = parts[1]
mounted = parts[0]
mountpoints[mountpoint] = {}
binds[mounted] = mountpoint
container_info = client.create_container(
image=image,
command=command,
@ -645,12 +670,14 @@ def create_container(image,
ports=ports,
environment=environment,
dns=dns,
volumes=mountpoints,
volumes=volumes,
volumes_from=volumes_from,
name=name,
cpu_shares=cpu_shares,
cpuset=cpuset
cpuset=cpuset,
host_config=docker.utils.create_host_config(binds=binds)
)
log.trace("docker.client.create_container returned: " + str(container_info))
container = container_info['Id']
callback = _valid
comment = 'Container created'
@ -660,8 +687,9 @@ def create_container(image,
}
__salt__['mine.send']('docker.get_containers', host=True)
return callback(status, id_=container, comment=comment, out=out)
except Exception:
except Exception as e:
_invalid(status, id_=image, out=traceback.format_exc())
raise e
__salt__['mine.send']('docker.get_containers', host=True)
return status

View File

@ -776,7 +776,6 @@ def trust_key(keyid=None,
salt '*' gpg.trust_key keyid='3FAD9F1E' trust_level='marginally'
salt '*' gpg.trust_key fingerprint='53C96788253E58416D20BCD352952C84C3252192' trust_level='not_trusted'
salt '*' gpg.trust_key keys=3FAD9F1E trust_level='ultimately' user='username'
'''
ret = {
'res': True,

View File

@ -279,8 +279,11 @@ def get_channel_access(channel=14, read_mode='non_volatile', **kwargs):
- api_port=623
- api_kg=None
:return: A Python dict with the following keys/values:
return
A Python dict with the following keys/values:
.. code-block:: python
{
alerting:
per_msg_auth:
@ -322,14 +325,17 @@ def get_channel_info(channel=14, **kwargs):
- api_port=623
- api_kg=None
:return:
session_support:
no_session: channel is session-less
single: channel is single-session
multi: channel is multi-session
auto: channel is session-based (channel could alternate between
single- and multi-session operation, as can occur with a
serial/modem channel that supports connection mode auto-detect)
return
channel session supports:
.. code-block:: none
- no_session: channel is session-less
- single: channel is single-session
- multi: channel is multi-session
- auto: channel is session-based (channel could alternate between
single- and multi-session operation, as can occur with a
serial/modem channel that supports connection mode auto-detect)
CLI Examples:
@ -419,19 +425,20 @@ def get_user_access(uid, channel=14, **kwargs):
- api_port=623
- api_kg=None
:return:
channel_info:
- max_user_count = maximum number of user IDs on this channel
- enabled_users = count of User ID slots presently in use
- users_with_fixed_names = count of user IDs with fixed names
access:
- callback
- link_auth
- ipmi_msg
- privilege_level: [reserved, callback, user, operator
administrator, proprietary, no_access]
return
.. code-block:: none
channel_info:
- max_user_count = maximum number of user IDs on this channel
- enabled_users = count of User ID slots presently in use
- users_with_fixed_names = count of user IDs with fixed names
access:
- callback
- link_auth
- ipmi_msg
- privilege_level: [reserved, callback, user, operator
administrator, proprietary, no_access]
CLI Examples:
@ -770,16 +777,20 @@ def get_user(uid, channel=14, **kwargs):
- api_port=623
- api_kg=None
:return:
name: (str)
uid: (int)
channel: (int)
access:
- callback (bool)
- link_auth (bool)
- ipmi_msg (bool)
- privilege_level: (str)[callback, user, operatorm administrator,
proprietary, no_access]
return
.. code-block:: none
name: (str)
uid: (int)
channel: (int)
access:
- callback (bool)
- link_auth (bool)
- ipmi_msg (bool)
- privilege_level: (str)[callback, user, operatorm administrator,
proprietary, no_access]
CLI Examples:
.. code-block:: bash

View File

@ -696,7 +696,7 @@ def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None):
opts = opts.split(',')
if not os.path.exists(name) and mkmnt:
__salt__['file.mkdir'](name=name, user=user)
__salt__['file.mkdir'](name, user=user)
args = ''
if opts is not None:

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
'''
:requires: libnacl
https://github.com/saltstack/libnacl
This module helps include encrypted passwords in pillars, grains and salt state files.
This is often usefull if you wish to store your pillars in source control or
share your pillar data with others that you trust. I dont advise making your pillars public
:depends: libnacl, https://github.com/saltstack/libnacl
This is often useful if you wish to store your pillars in source control or
share your pillar data with others that you trust. I don't advise making your pillars public
regardless if they are encrypted or not.
When generating keys and encrypting passwords use --local when using salt-call for extra
@ -14,20 +14,28 @@ security. Also consider using just the salt runner nacl when encrypting pillar p
The nacl lib uses 32byte keys, these keys are base64 encoded to make your life more simple.
To generate your `key` or `keyfile` you can use:
.. code-block:: bash
salt-call --local nacl.keygen keyfile=/root/.nacl
Now with your key, you can encrypt some data
Now with your key, you can encrypt some data:
.. code-block:: bash
salt-call --local nacl.enc mypass keyfile=/root/.nacl
DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=
To decrypt the data
To decrypt the data:
.. code-block:: bash
salt-call --local nacl.dec data='DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=' keyfile=/root/.nacl
mypass
The following optional configurations can be defined in the
minion or master config. Avoide storeing the config in pillars!
minion or master config. Avoid storing the config in pillars!
.. code-block:: yaml
cat /etc/salt/master.d/nacl.conf
nacl.config:
@ -36,15 +44,21 @@ minion or master config. Avoide storeing the config in pillars!
When the key is defined in the master config you can use it from the nacl runner:
.. code-block:: bash
salt-run nacl.enc 'myotherpass'
Now you can create a pillar with protected data like:
.. code-block:: yaml
pillarexample:
user: root
password: {{ salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=') }}
Or do somthing interesting with grains like:
Or do something interesting with grains like:
.. code-block:: yaml
salt-call nacl.enc minionname:dbrole
AL24Z2C5OlkReer3DuQTFdrNLchLuz3NGIhGjZkLtKRYry/b/CksWM8O9yskLwH2AGVLoEXI5jAa

View File

@ -119,6 +119,11 @@ def install(pkg=None,
elif pkgs:
cmd += ' "{0}"'.format('" "'.join(pkgs))
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas, env=env)
if result['retcode'] != 0:
@ -151,7 +156,8 @@ def install(pkg=None,
def uninstall(pkg,
dir=None,
runas=None):
runas=None,
env=None):
'''
Uninstall an NPM package.
@ -167,6 +173,13 @@ def uninstall(pkg,
runas
The user to run NPM with
env
Environment variables to set when invoking npm. Uses the same ``env``
format as the :py:func:`cmd.run <salt.modules.cmdmod.run>` execution
function.
.. versionadded:: 2015.5.3
CLI Example:
.. code-block:: bash
@ -175,6 +188,11 @@ def uninstall(pkg,
'''
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = 'npm uninstall'
if dir is None:
@ -182,7 +200,7 @@ def uninstall(pkg,
cmd += ' "{0}"'.format(pkg)
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas)
result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=dir, runas=runas, env=env)
if result['retcode'] != 0:
log.error(result['stderr'])
@ -227,6 +245,11 @@ def list_(pkg=None,
'''
if runas:
uid = salt.utils.get_uid(runas)
if uid:
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = 'npm list --silent --json'
if dir is None:

View File

@ -128,10 +128,7 @@ def list_upgrades(refresh=False):
if refresh:
options.append('-y')
cmd = (
'pacman {0} | egrep -v '
r'"^\s|^:"'
).format(' '.join(options))
cmd = ('pacman {0}').format(' '.join(options))
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
@ -147,7 +144,9 @@ def list_upgrades(refresh=False):
else:
out = call['stdout']
for line in out.splitlines():
output = iter(out.splitlines())
next(output) # Skip informational output line
for line in output:
comps = line.split(' ')
if len(comps) < 2:
continue

View File

@ -228,11 +228,13 @@ def enable():
def disable(message=None):
'''
.. versionadded:: 2014.7.0
Disable the puppet agent
message
.. versionadded:: 2015.5.2
disable message to send to puppet
Disable message to send to puppet
CLI Example:
@ -240,7 +242,6 @@ def disable(message=None):
salt '*' puppet.disable
salt '*' puppet.disable 'disabled for a good reason'
'''
_check_puppet()

View File

@ -681,10 +681,9 @@ def generateBlobs(api_key=None,
.. code-block:: bash
salt '*' get_intergers number=5 min=1 max=6
salt '*' get_intergers number=5 min=1 max=6
salt '*' get_integers number=5 min=1 max=6
salt '*' get_integers number=5 min=1 max=6
'''
ret = {'res': True}

View File

@ -1,6 +1,12 @@
# -*- coding: utf-8 -*-
'''
Control the state system on the minion
Control the state system on the minion.
State Caching
-------------
When a highstate is called, the minion automatically caches a copy of the last high data.
If you then run a highstate with cache=True it will use that cached highdata and won't hit the fileserver.
'''
# Import python libs

View File

@ -58,8 +58,7 @@ def _call_system_profiler(datatype):
def receipts():
'''
Return the results of a call to
`system_profiler -xml -detail full
SPInstallHistoryDataType`
``system_profiler -xml -detail full SPInstallHistoryDataType``
as a dictionary. Top-level keys of the dictionary
are the names of each set of install receipts, since
there can be multiple receipts with the same name.
@ -99,12 +98,11 @@ def receipts():
def applications():
'''
Return the results of a call to
`system_profiler -xml -detail full
SPApplicationsDataType`
``system_profiler -xml -detail full SPApplicationsDataType``
as a dictionary. Top-level keys of the dictionary
are the names of each set of install receipts, since
there can be multiple receipts with the same name.
Contents of each key are a list of dicttionaries.
Contents of each key are a list of dictionaries.
Note that this can take a long time depending on how many
applications are installed on the target Mac.

View File

@ -7,17 +7,18 @@ A REST API for Salt
.. py:currentmodule:: salt.netapi.rest_cherrypy.app
:depends: - CherryPy Python module. Versions 3.2.{2,3,4} are strongly
recommended due to a known `SSL error
<https://bitbucket.org/cherrypy/cherrypy/issue/1298/ssl-not-working>`_
introduced in version 3.2.5. The issue was reportedly resolved with
CherryPy milestone 3.3, but the patch was committed for version 3.6.1.
- salt-api package
:depends:
- CherryPy Python module. Versions 3.2.{2,3,4} are strongly
recommended due to a known `SSL error
<https://bitbucket.org/cherrypy/cherrypy/issue/1298/ssl-not-working>`_
introduced in version 3.2.5. The issue was reportedly resolved with
CherryPy milestone 3.3, but the patch was committed for version 3.6.1.
:optdepends: - ws4py Python module for websockets support.
:client_libraries:
- Java: https://github.com/SUSE/saltstack-netapi-client-java
- Python: https://github.com/saltstack/pepper
:configuration: All authentication is done through Salt's :ref:`external auth
:configuration:
All authentication is done through Salt's :ref:`external auth
<acl-eauth>` system which requires additional configuration not described
here.

View File

@ -16,7 +16,7 @@ them.
An example Django module that registers a function called
'returner_callback' with this module's 'returner' function:
.. code-block:: python
.. code-block:: python
import salt.returners.django_return
from django.dispatch import receiver

View File

@ -349,7 +349,7 @@ def lock(backend=None, remote=None):
.. note::
This will only operate on enabled backends (those configured in
:master_conf:`fileserver_backend`).
:conf_master:`fileserver_backend`).
backend
Only set the update lock for the specified backend(s).

View File

@ -19,7 +19,7 @@ def query(url, output=True, **kwargs):
'''
Query a resource, and decode the return data
.. versionadded:: 2015.2
.. versionadded:: 2015.5.0
CLI Example:
@ -44,7 +44,7 @@ def update_ca_bundle(target=None, source=None, merge_files=None):
'''
Update the local CA bundle file from a URL
.. versionadded:: 2015.2
.. versionadded:: 2015.5.0
CLI Example:

View File

@ -236,10 +236,10 @@ def present(
listeners
A list of listener lists; example::
[
['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'],
['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']
]
[
['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'],
['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']
]
subnets
A list of subnet IDs in your VPC to attach to your LoadBalancer.

View File

@ -69,6 +69,7 @@ Available Functions
my_service:
docker.running:
- container: mysuperdocker
- image: corp/mysuperdocker_img
- ports:
"5000/tcp":
HostIp: ""
@ -210,6 +211,103 @@ def _get_image_name(image, tag):
return image
def _parse_volumes(volumes):
'''
Parse a given volumes state specification for later use in
modules.docker.create_container(). This produces a dict that can be directly
consumed by the Docker API /containers/create.
Note: this only really exists for backwards-compatibility, and because
modules.dockerio.start() currently takes a binds argument.
volumes
A structure containing information about the volumes to be included in the
container that will be created, either:
- a bare dictionary
- a list of dictionaries and lists
.. code-block:: yaml
# bare dict style
- volumes:
/usr/local/etc/ssl/certs/example.crt:
bind: /etc/ssl/certs/com.example.internal.crt
ro: True
/var/run:
bind: /var/run/host/
ro: False
# list of dicts style:
- volumes:
- /usr/local/etc/ssl/certs/example.crt:
bind: /etc/ssl/certs/com.example.internal.crt
ro: True
- /var/run: /var/run/host/ # read-write bound volume
- /var/lib/mysql # un-bound, container-only volume
note: bind mounts specified like "/etc/timezone:/tmp/host_tz" will fall
through this parser.
Returns a dict of volume specifications:
.. code-block:: yaml
{
'bindvols': {
'/usr/local/etc/ssl/certs/example.crt': {
'bind': '/etc/ssl/certs/com.example.internal.crt',
'ro': True
},
'/var/run/': {
'bind': '/var/run/host',
'ro': False
},
},
'contvols': [ '/var/lib/mysql/' ]
}
'''
log.trace("Parsing given volumes dict: " + str(volumes))
bindvolumes = {}
contvolumes = []
if isinstance(volumes, dict):
# If volumes as a whole is a dict, then there's no way to specify a non-bound volume
# so we exit early and assume the dict is properly formed.
bindvolumes = volumes
if isinstance(volumes, list):
for vol in volumes:
if isinstance(vol, dict):
for volsource, voldef in vol.items():
if isinstance(voldef, dict):
target = voldef['bind']
read_only = voldef.get('ro', False)
else:
target = str(voldef)
read_only = False
source = volsource
else: # isinstance(vol, dict)
if ':' in vol:
volspec = vol.split(':')
source = volspec[0]
target = volspec[1]
read_only = False
try:
if len(volspec) > 2:
read_only = volspec[2] == "ro"
except IndexError:
pass
else:
contvolumes.append(str(vol))
continue
bindvolumes[source] = {
'bind': target,
'ro': read_only
}
result = {'bindvols': bindvolumes, 'contvols': contvolumes}
log.trace("Finished parsing volumes, with result: " + str(result))
return result
def mod_watch(name, sfun=None, *args, **kw):
if sfun == 'built':
# Needs to refresh the image
@ -531,7 +629,7 @@ def installed(name,
- a port to map
- a mapping of mapping portInHost : PortInContainer
volumes
List of volumes
List of volumes (see notes for the running function)
For other parameters, see absolutely first the salt.modules.dockerio
execution module and the docker-py python bindings for docker
@ -555,7 +653,7 @@ def installed(name,
# if container exists but is not started, try to start it
if already_exists:
return _valid(comment='image {0!r} already exists'.format(name))
dports, dvolumes, denvironment = {}, [], {}
dports, denvironment = {}, {}
if not ports:
ports = []
if not volumes:
@ -574,15 +672,13 @@ def installed(name,
else:
for k in p:
dports[str(p)] = {}
for p in volumes:
vals = []
if not isinstance(p, dict):
vals.append('{0}'.format(p))
else:
for k in p:
vals.append('{0}:{1}'.format(k, p[k]))
dvolumes.extend(vals)
parsed_volumes = _parse_volumes(volumes)
bindvolumes = parsed_volumes['bindvols']
contvolumes = parsed_volumes['contvols']
kw = dict(
binds=bindvolumes,
command=command,
hostname=hostname,
user=user,
@ -593,7 +689,7 @@ def installed(name,
ports=dports,
environment=denvironment,
dns=dns,
volumes=dvolumes,
volumes=contvolumes,
volumes_from=volumes_from,
name=name,
cpu_shares=cpu_shares,
@ -860,44 +956,47 @@ def running(name,
volumes
List of volumes to mount or create in the container (like ``-v`` of ``docker run`` command),
mapping host directory to container directory.
To create a volume in the container:
To specify a volume in the container in terse list format:
.. code-block:: yaml
- volumes:
- "/var/log/service"
- "/var/log/service" # container-only volume
- "/srv/timezone:/etc/timezone" # bound volume
- "/usr/local/etc/passwd:/etc/passwd:ro" # read-only bound volume
For read-write mounting, use the short form (note that the notion of
You can also use the short dictionary form (note that the notion of
source:target from docker is preserved):
.. code-block:: yaml
- volumes:
- /var/log/service: /var/log/service
- /var/log/service: /var/log/service # mandatory read-write implied
Or, to specify read-only mounting, use the extended form:
Or, alternatively, to specify read-only mounting, use the extended form:
.. code-block:: yaml
- volumes:
- /home/user1:
bind: /mnt/vol2
ro: true
ro: True
- /var/www:
bind: /mnt/vol1
ro: false
ro: False
Or (mostly for backwards compatibility) a dict style
Or (for backwards compatibility) another dict style:
.. code-block:: yaml
- volumes:
/home/user1:
bind: /mnt/vol2
ro: true
/var/www:
bind: /mnt/vol1
ro: false
/home/user1:
bind: /mnt/vol2
ro: True
/var/www:
bind: /mnt/vol1
ro: False
volumes_from
List of containers to share volumes with
@ -1084,6 +1183,11 @@ def running(name,
else:
# assume just a port to expose
exposeports.append(str(port))
parsed_volumes = _parse_volumes(volumes)
bindvolumes = parsed_volumes['bindvols']
contvolumes = parsed_volumes['contvols']
if not already_exists:
kwargs = dict(command=command,
hostname=hostname,
@ -1095,6 +1199,7 @@ def running(name,
ports=exposeports,
environment=denvironment,
dns=dns,
binds=bindvolumes,
volumes=contvolumes,
name=name,
cpu_shares=cpu_shares,

View File

@ -163,8 +163,10 @@ def user_present(name, uid, password, channel=14, callback=False,
callback
User Restricted to Callback
False = User Privilege Limit is determined by the User Privilege Limit
parameter privilege_level, for both callback and non-callback connections.
True = User Privilege Limit is determined by the privilege_level
parameter for callback connections, but is restricted to Callback
level for non-callback connections. Thus, a user can only initiate

View File

@ -4,7 +4,7 @@ Linux File Access Control Lists
Ensure a Linux ACL is present
.. code-block:: yaml
.. code-block:: yaml
root:
acl.present:
@ -15,7 +15,7 @@ Ensure a Linux ACL is present
Ensure a Linux ACL does not exist
.. code-block:: yaml
.. code-block:: yaml
root:
acl.absent:

View File

@ -228,7 +228,7 @@ def run(name, **kwargs):
ret['result'] = False
return ret
else:
if mret is not None:
if mret is not None or mret is not {}:
ret['changes']['ret'] = mret
if 'returner' in kwargs:

View File

@ -19,7 +19,7 @@ def license_present(name):
on the host.
name
The licnese key to ensure is present
The license key to ensure is present
'''
ret = {'name': name,
'changes': {},
@ -61,7 +61,7 @@ def license_absent(name):
on the host.
name
The licnese key to ensure is absent
The license key to ensure is absent
'''
ret = {'name': name,
'changes': {},

View File

@ -81,7 +81,7 @@ def present(name,
'information or see #6961.'
)
if any(user, owner, conf, write, read):
if any((user, owner, conf, write, read)):
salt.utils.warn_until(
'Beryllium',
'Passed \'owner\', \'user\', \'conf\', \'write\' or \'read\' '

View File

@ -69,7 +69,6 @@ def post_message(name,
api_key
The api key for Slack to use for authentication,
if not specified in the configuration options of master or minion.
'''
ret = {'name': name,
'changes': {},

View File

@ -83,15 +83,16 @@ def removed(name):
the server is restarted.
Example:
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
name listed for each feature, not the indented one. Do not use the role or feature names mentioned in the
PKGMGR documentation.
.. code-block:: yaml
.. code-block:: yaml
ISWebserverRole:
win_servermanager.removed:
- name: Web-Server
ISWebserverRole:
win_servermanager.removed:
- name: Web-Server
'''
ret = {'name': name,
'result': True,

View File

@ -111,9 +111,12 @@ def yamlify_arg(arg):
import salt.utils.yamlloader as yamlloader
original_arg = arg
if '#' in arg:
# Don't yamlify this argument or the '#' and everything after
# it will be interpreted as a comment.
return arg
# Only yamlify if it parses into a non-string type, to prevent
# loss of content due to # as comment character
parsed_arg = yamlloader.load(arg, Loader=yamlloader.SaltYamlSafeLoader)
if isinstance(parsed_arg, six.string_types):
return arg
return parsed_arg
if arg == 'None':
arg = None
else:

View File

@ -31,6 +31,12 @@ try:
except ImportError:
# Older jinja does not need markupsafe
HAS_MARKUPSAFE = False
try:
import xml
HAS_XML = True
except ImportError:
HAS_XML = False
# pylint: enable=import-error,no-name-in-module
try:
@ -117,6 +123,10 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods=''):
if HAS_SSL_MATCH_HOSTNAME:
tops.append(os.path.dirname(os.path.dirname(ssl_match_hostname.__file__)))
if HAS_XML:
# For openSUSE, which apparently doesn't include the whole stdlib
tops.append(os.path.dirname(xml.__file__))
for mod in [m for m in extra_mods.split(',') if m]:
if mod not in locals() and mod not in globals():
try:

View File

@ -52,10 +52,12 @@ def update_config(file_name, yaml_contents):
specified by ``default_include``.
This folder is named ``master.d`` by
default. Please look at
http://docs.saltstack.com/en/latest/ref/configuration/master.html#include-configuration
:conf_master:`include-configuration`
for more information.
Example low data::
Example low data:
.. code-block:: yaml
data = {
'username': 'salt',

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import pyrax_queues
pyrax_queues.__opts__ = {}
pyrax_queues.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PyraxQueuesTestCase(TestCase):
'''
Test cases for salt.states.pyrax_queues
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure the RackSpace queue exists.
'''
name = 'myqueue'
provider = 'my-pyrax'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock_dct = MagicMock(side_effect=[{provider: {'salt': True}},
{provider: {'salt': False}},
{provider: {'salt': False}}, False])
with patch.dict(pyrax_queues.__salt__, {'cloud.action': mock_dct}):
comt = ('{0} present.'.format(name))
ret.update({'comment': comt})
self.assertDictEqual(pyrax_queues.present(name, provider), ret)
with patch.dict(pyrax_queues.__opts__, {'test': True}):
comt = ('Rackspace queue myqueue is set to be created.')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(pyrax_queues.present(name, provider), ret)
with patch.dict(pyrax_queues.__opts__, {'test': False}):
comt = ('Failed to create myqueue Rackspace queue.')
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(pyrax_queues.present(name, provider), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure the named Rackspace queue is deleted.
'''
name = 'myqueue'
provider = 'my-pyrax'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock_dct = MagicMock(side_effect=[{provider: {'salt': False}},
{provider: {'salt': True}}])
with patch.dict(pyrax_queues.__salt__, {'cloud.action': mock_dct}):
comt = ('myqueue does not exist.')
ret.update({'comment': comt})
self.assertDictEqual(pyrax_queues.absent(name, provider), ret)
with patch.dict(pyrax_queues.__opts__, {'test': True}):
comt = ('Rackspace queue myqueue is set to be removed.')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(pyrax_queues.absent(name, provider), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PyraxQueuesTestCase, needs_daemon=False)

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import rabbitmq_user
rabbitmq_user.__opts__ = {}
rabbitmq_user.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RabbitmqUserTestCase(TestCase):
'''
Test cases for salt.states.rabbitmq_user
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure the RabbitMQ user exists.
'''
name = 'foo'
passwd = 'password'
tag = 'user'
perms = [{'/': ['.*', '.*']}]
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=[True, False, True, True,
True, True, True])
mock_dct = MagicMock(return_value={name: set(tag)})
mock_pr = MagicMock(return_value=perms)
mock_add = MagicMock(return_value={'Added': name})
with patch.dict(rabbitmq_user.__salt__,
{'rabbitmq.user_exists': mock,
'rabbitmq.list_users': mock_dct,
'rabbitmq.list_user_permissions': mock_pr,
'rabbitmq.set_user_tags': mock_add}):
comt = ('User foo already presents')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name), ret)
with patch.dict(rabbitmq_user.__opts__, {'test': True}):
comt = ('User foo is set to be created')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rabbitmq_user.present(name), ret)
comt = ("User foo's password is set to be updated")
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name,
password=passwd,
force=True), ret)
comt = ("User foo's password is set to be removed")
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, force=True),
ret)
comt = ('Tags for user foo is set to be changed')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, tags=tag), ret)
comt = ('Permissions for user foo is set to be changed')
ret.update({'comment': comt})
self.assertDictEqual(rabbitmq_user.present(name, perms=perms),
ret)
with patch.dict(rabbitmq_user.__opts__, {'test': False}):
ret.update({'comment': name, 'result': True,
'changes': {'new': 'Set tags: user\n', 'old': ''}})
self.assertDictEqual(rabbitmq_user.present(name, tags=tag), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure the named user is absent.
'''
name = 'foo'
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'User {0} is not present'.format(name)}
mock = MagicMock(return_value=False)
with patch.dict(rabbitmq_user.__salt__, {'rabbitmq.user_exists': mock}):
self.assertDictEqual(rabbitmq_user.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(RabbitmqUserTestCase, needs_daemon=False)

View File

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import rabbitmq_vhost
rabbitmq_vhost.__opts__ = {}
rabbitmq_vhost.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RabbitmqVhostTestCase(TestCase):
'''
Test cases for salt.states.rabbitmq_vhost
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure the RabbitMQ VHost exists.
'''
name = 'virtual_host'
ret = {'name': name,
'changes': {},
'result': None,
'comment': 'Creating VHost virtual_host'}
mock = MagicMock(return_value=False)
with patch.dict(rabbitmq_vhost.__salt__,
{'rabbitmq.vhost_exists': mock}):
with patch.dict(rabbitmq_vhost.__opts__, {'test': True}):
self.assertDictEqual(rabbitmq_vhost.present(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure the named user is absent.
'''
name = 'myqueue'
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'Virtual Host {0} is not present'.format(name)}
mock = MagicMock(return_value=False)
with patch.dict(rabbitmq_vhost.__salt__,
{'rabbitmq.vhost_exists': mock}):
self.assertDictEqual(rabbitmq_vhost.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(RabbitmqVhostTestCase, needs_daemon=False)

View File

@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import rbenv
rbenv.__opts__ = {}
rbenv.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RbenvTestCase(TestCase):
'''
Test cases for salt.states.rbenv
'''
# 'installed' function tests: 1
def test_installed(self):
'''
Test to verify that the specified ruby is installed with rbenv.
'''
name = 'rbenv-deps'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock_t = MagicMock(side_effect=[False, True, True])
mock_f = MagicMock(return_value=False)
mock_def = MagicMock(return_value='2.7')
mock_ver = MagicMock(return_value=['2.7'])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock_f,
'rbenv.install': mock_t,
'rbenv.default': mock_def,
'rbenv.versions': mock_ver,
'rbenv.install_ruby': mock_t}):
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Ruby rbenv-deps is set to be installed')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.installed(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
comt = ('Rbenv failed to install')
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(rbenv.installed(name), ret)
comt = ('Successfully installed ruby')
ret.update({'comment': comt, 'result': True, 'default': False,
'changes': {name: 'Installed'}})
self.assertDictEqual(rbenv.installed(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to verify that the specified ruby is not installed with rbenv.
'''
name = 'myqueue'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(side_effect=[False, True])
mock_def = MagicMock(return_value='2.7')
mock_ver = MagicMock(return_value=['2.7'])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock,
'rbenv.default': mock_def,
'rbenv.versions': mock_ver}):
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Ruby myqueue is set to be uninstalled')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.absent(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
comt = ('Rbenv not installed, myqueue not either')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.absent(name), ret)
comt = ('Ruby myqueue is already absent')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.absent(name), ret)
# 'install_rbenv' function tests: 1
def test_install_rbenv(self):
'''
Test to install rbenv if not installed.
'''
name = 'myqueue'
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
with patch.dict(rbenv.__opts__, {'test': True}):
comt = ('Rbenv is set to be installed')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(rbenv.install_rbenv(name), ret)
with patch.dict(rbenv.__opts__, {'test': False}):
mock = MagicMock(side_effect=[False, True])
with patch.dict(rbenv.__salt__,
{'rbenv.is_installed': mock,
'rbenv.install': mock}):
comt = ('Rbenv installed')
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(rbenv.install_rbenv(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(RbenvTestCase, needs_daemon=False)