Merge branch 'develop' of github.com:saltstack/salt into 42713_mine.get_back_compat

... and fixed conflict
This commit is contained in:
Georges Racinet 2017-11-08 18:09:04 +01:00
commit 5c01c74fbc
No known key found for this signature in database
GPG Key ID: EE20CA44EF691D39
122 changed files with 3122 additions and 601 deletions

4
.github/stale.yml vendored
View File

@ -1,8 +1,8 @@
# Probot Stale configuration file
# Number of days of inactivity before an issue becomes stale
# 910 is approximately 2 years and 6 months
daysUntilStale: 910
# 900 is approximately 2 years and 5 months
daysUntilStale: 900
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7

5
.gitignore vendored
View File

@ -91,3 +91,8 @@ tests/integration/cloud/providers/pki/minions
# Ignore tox virtualenvs
/.tox/
# Kitchen tests files
.kitchen/
.bundle/
Gemfile.lock

View File

@ -44,6 +44,11 @@ may take a few moments for someone to reply.
`<http://webchat.freenode.net/?channels=salt&uio=Mj10cnVlJjk9dHJ1ZSYxMD10cnVl83>`_
**SaltStack Slack** - Alongside IRC is our SaltStack Community Slack for the
SaltStack Working groups. Use the following link to request an invitation.
`<https://saltstackcommunity.herokuapp.com/>`_
**Mailing List** - The SaltStack community users mailing list is hosted by
Google groups. Anyone can post to ask questions about SaltStack products and
anyone can help answer. Join the conversation!

View File

@ -718,7 +718,7 @@ Note that ping_on_rotate may cause high load on the master immediately after
the key rotation event as minions reconnect. Consider this carefully if this
salt master is managing a large number of minions.
.. code-black:: yaml
.. code-block:: yaml
ping_on_rotate: False
@ -904,7 +904,7 @@ is set to ``tcp`` by default on Windows.
ipc_mode: ipc
.. conf_master::
.. conf_master:: tcp_master_pub_port
``tcp_master_pub_port``
-----------------------
@ -4237,6 +4237,7 @@ Default: ``10``
The number of workers for the runner/wheel in the reactor.
.. code-block:: yaml
reactor_worker_threads: 10
.. conf_master:: reactor_worker_hwm

View File

@ -2387,6 +2387,7 @@ Default: ``10``
The number of workers for the runner/wheel in the reactor.
.. code-block:: yaml
reactor_worker_threads: 10
.. conf_minion:: reactor_worker_hwm

View File

@ -51,7 +51,7 @@ actually speed things up.
To run the above state much faster make sure that the ``sleep 5`` is evaluated
before the ``nginx`` state
.. code_block:: yaml
.. code-block:: yaml
sleep 10:
cmd.run:

View File

@ -68,8 +68,8 @@ Each salt minion establishes a connection to the master Publisher.
EventPublisher
--------------
The EventPublisher publishes events onto the event bus. It is bound to the
following:
The EventPublisher publishes master events out to any event listeners. It is
bound to the following:
* IPC: master_event_pull.ipc
* IPC: master_event_pub.ipc
@ -110,48 +110,37 @@ The typical lifecycle of a salt job from the perspective of the master
might be as follows:
1) A command is issued on the CLI. For example, 'salt my_minion test.ping'.
2) The 'salt' command uses LocalClient to generate a request to the salt master
by connecting to the ReqServer on TCP:4506 and issuing the job.
3) The salt-master ReqServer sees the request and passes it to an available
MWorker over workers.ipc.
4) A worker picks up the request and handles it. First, it checks to ensure
that the requested user has permissions to issue the command. Then, it sends
the publish command to all connected minions. For the curious, this happens
in ClearFuncs.publish().
5) The worker announces on the master event bus that it is about to publish
a job to connected minions. This happens by placing the event on the master
event bus (master_event_pull.ipc) where the EventPublisher picks it up and
distributes it to all connected event listeners on master_event_pub.ipc.
6) The message to the minions is encrypted and sent to the Publisher via IPC
on publish_pull.ipc.
7) Connected minions have a TCP session established with the Publisher on TCP
port 4505 where they await commands. When the Publisher receives the job over
publish_pull, it sends the jobs across the wire to the minions for processing.
8) After the minions receive the request, they decrypt it and perform any
requested work, if they determine that they are targeted to do so.
9) When the minion is ready to respond, it publishes the result of its job back
to the master by sending the encrypted result back to the master on TCP 4506
where it is again picked up by the ReqServer and forwarded to an available
MWorker for processing. (Again, this happens by passing this message across
workers.ipc to an available worker.)
10) When the MWorker receives the job it decrypts it and fires an event onto
the master event bus (master_event_pull.ipc). (Again for the curious, this
happens in AESFuncs._return().
11) The EventPublisher sees this event and re-publishes it on the bus to all
connected listeners of the master event bus (on master_event_pub.ipc). This
is where the LocalClient has been waiting, listening to the event bus for
minion replies. It gathers the job and stores the result.
12) When all targeted minions have replied or the timeout has been exceeded,
the salt client displays the results of the job to the user on the CLI.
@ -167,8 +156,8 @@ Salt. It can either operate as a stand-alone daemon which accepts commands
locally via 'salt-call' or it can connect back to a master and receive commands
remotely.
When starting up, salt minions connect _back_ to a master defined in the minion
config file. The connect to two ports on the master:
When starting up, salt minions connect *back* to a master defined in the minion
config file. They connect to two ports on the master:
* TCP: 4505
This is the connection to the master Publisher. It is on this port that
@ -196,8 +185,8 @@ permissions can read or write to the bus as a common interface with the salt
minion.
Job Flow
--------
Minion Job Flow
---------------
When a salt minion starts up, it attempts to connect to the Publisher and the
ReqServer on the salt master. It then attempts to authenticate and once the
@ -206,28 +195,22 @@ minion has successfully authenticated, it simply listens for jobs.
Jobs normally come either come from the 'salt-call' script run by a local user
on the salt minion or they can come directly from a master.
Master Job Flow
---------------
The job flow on a minion, coming from the master via a 'salt' command is as
follows:
1) A master publishes a job that is received by a minion as outlined by the
master's job flow above.
2) The minion is polling its receive socket that's connected to the master
Publisher (TCP 4505 on master). When it detects an incoming message, it picks it
up from the socket and decrypts it.
3) A new minion process or thread is created and provided with the contents of the
decrypted message. The _thread_return() method is provided with the contents of
the received message.
4) The new minion thread is created. The _thread_return() function starts up
and actually calls out to the requested function contained in the job.
5) The requested function runs and returns a result. [Still in thread.]
6) The result of the function that's run is encrypted and returned to the
master's ReqServer (TCP 4506 on master). [Still in thread.]
7) Thread exits. Because the main thread was only blocked for the time that it
took to initialize the worker thread, many other requests could have been
received and processed during this time.
@ -241,6 +224,5 @@ clear and when they are passed using encryption. There are two rules governing
this behaviour:
1) ClearFuncs is used for intra-master communication and during the initial
authentication handshake between a minion and master during the key exhange.
authentication handshake between a minion and master during the key exchange.
2) AESFuncs is used everywhere else.

View File

@ -4,12 +4,25 @@ Event System
The Salt Event System is used to fire off events enabling third party
applications or external processes to react to behavior within Salt.
The event system uses a publish-subscribe pattern, otherwise know as pub/sub.
The event system is comprised of a two primary components:
Event Bus
=========
The event system is comprised of a two primary components, which make up the
concept of an Event Bus:
* The event sockets which publishes events.
* The event library which can listen to events and send events into the salt system.
Events are published onto the event bus and event bus subscribers listen for the
published events.
The event bus is used for both inter-process communication as well as network transport
in Salt. Inter-process communication is provided through UNIX domain sockets (UDX).
The Salt Master and each Salt Minion has their own event bus.
Event types
===========
@ -21,7 +34,7 @@ Event types
Listening for Events
====================
Salt's Event Bus is used heavily within Salt and it is also written to
Salt's event system is used heavily within Salt and it is also written to
integrate heavily with existing tooling and scripts. There is a variety of
ways to consume it.

View File

@ -174,11 +174,13 @@ to create it.
The generated grain information will appear similar to:
.. code-block:: yaml
grains:
salt-cloud:
driver: ec2
provider: my_ec2:ec2
profile: ec2-web
The generation of salt-cloud grains can be surpressed by the
option ``enable_cloud_grains: 'False'`` in the cloud configuration file.

View File

@ -29,6 +29,7 @@ the name of the repository, and the link to the repository:
For HTTP/HTTPS Basic authorization you can define credentials:
.. code-block:: yaml
my_repo:
url: https://spm.example.com/
username: user

View File

@ -164,7 +164,7 @@ states are evaluated before ``tgt`` states.
Each of these sections needs to be evaluated as text, rather than as YAML.
Consider the following block:
.. code-block::
.. code-block:: text
pre_local_state: >
echo test > /tmp/spmtest:
@ -187,7 +187,7 @@ a minion.
the ``>`` marker to denote that the state is evaluated as text, not a data
structure.
.. code-block::
.. code-block:: text
pre_local_state: >
echo test > /tmp/spmtest:
@ -203,7 +203,7 @@ the ``spm`` command is running on is a master.
Because ``tgt`` states require that a target be specified, their code blocks
are a little different. Consider the following state:
.. code-block::
.. code-block:: text
pre_tgt_state:
tgt: '*'
@ -229,7 +229,7 @@ This means that you can use Jinja or any other supported renderer inside of
Salt. All formula variables are available to the renderer, so you can reference
``FORMULA`` data inside your state if you need to:
.. code-block::
.. code-block:: text
pre_tgt_state:
tgt: '*'

View File

@ -34,7 +34,7 @@ passing on a single socket.
TLS Support
===========
.. version_added:: 2016.11.1
.. versionadded:: 2016.11.1
The TCP transport allows for the master/minion communication to be optionally
wrapped in a TLS connection. Enabling this is simple, the master and minion need

View File

@ -260,14 +260,11 @@ the retention time defined by
250 jobs/day * 2000 minions returns = 500,000 files a day
If no job history is needed, the job cache can be disabled:
Use and External Job Cache
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
job_cache: False
If the job cache is necessary there are (currently) 2 options:
An external job cache allows for job storage to be placed on an external
system, such as a database.
- ext_job_cache: this will have the minions store their return data directly
into a returner (not sent through the Master)
@ -287,3 +284,20 @@ for up to sixty seconds by default.
To enable the master key cache, set `key_cache: 'sched'` in the master
configuration file.
Disable The Job Cache
~~~~~~~~~~~~~~~~~~~~~
The job cache is a central component of the Salt Master and many aspects of
the Salt Master will not function correctly without a running job cache.
Disabling the job cache is **STRONGLY DISCOURAGED** and should not be done
unless the master is being used to execute routines that require no history
or reliable feedback!
The job cache can be disabled:
.. code-block:: yaml
job_cache: False

View File

@ -9,3 +9,4 @@ SaltPyLint>=v2017.3.6
pytest
git+https://github.com/eisensheng/pytest-catchlog.git@develop#egg=Pytest-catchlog
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
testinfra>=1.7.0

View File

@ -13,3 +13,5 @@ httpretty
SaltPyLint>=v2017.2.29
pytest
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
git+https://github.com/eisensheng/pytest-catchlog.git@develop#egg=Pytest-catchlog
testinfra>=1.7.0

View File

@ -6,8 +6,6 @@ This system allows for authentication to be managed in a module pluggable way
so that any external authentication system can be used inside of Salt
'''
from __future__ import absolute_import
# 1. Create auth loader instance
# 2. Accept arguments as a dict
# 3. Verify with function introspection
@ -16,7 +14,7 @@ from __future__ import absolute_import
# 6. Interface to verify tokens
# Import python libs
from __future__ import print_function
from __future__ import absolute_import, print_function
import collections
import time
import logging
@ -31,6 +29,7 @@ import salt.transport.client
import salt.utils.args
import salt.utils.dictupdate
import salt.utils.files
import salt.utils.master
import salt.utils.minions
import salt.utils.user
import salt.utils.versions
@ -430,13 +429,26 @@ class LoadAuth(object):
auth_list = self.get_auth_list(load)
elif auth_type == 'user':
if not self.authenticate_key(load, key):
auth_ret = self.authenticate_key(load, key)
msg = 'Authentication failure of type "user" occurred'
if not auth_ret: # auth_ret can be a boolean or the effective user id
if show_username:
msg = 'Authentication failure of type "user" occurred for user {0}.'.format(username)
else:
msg = 'Authentication failure of type "user" occurred'
msg = '{0} for user {1}.'.format(msg, username)
ret['error'] = {'name': 'UserAuthenticationError', 'message': msg}
return ret
# Verify that the caller has root on master
if auth_ret is not True:
if AuthUser(load['user']).is_sudo():
if not self.opts['sudo_acl'] or not self.opts['publisher_acl']:
auth_ret = True
if auth_ret is not True:
auth_list = salt.utils.master.get_values_of_matching_keys(
self.opts['publisher_acl'], auth_ret)
if not auth_list:
ret['error'] = {'name': 'UserAuthenticationError', 'message': msg}
return ret
else:
ret['error'] = {'name': 'SaltInvocationError',
'message': 'Authentication type not supported.'}

View File

@ -194,6 +194,7 @@ def create(vm_):
CLI Example:
.. code-block:: bash
salt-cloud -p my_profile new_node_1
'''

View File

@ -198,7 +198,9 @@ def list_nodes_full(kwargs=None, call=None):
This is because some functions both within Salt and 3rd party will break if an expected field is not present.
This function is normally called with the -F option:
.. code-block:: bash
salt-cloud -F
@ -244,6 +246,7 @@ def list_nodes(kwargs=None, call=None):
This function is normally called with the -Q option:
.. code-block:: bash
salt-cloud -Q

View File

@ -9,7 +9,7 @@
Common salt configuration schemas
'''
# Import Pythosn libs
# Import Python libs
from __future__ import absolute_import
# Import salt libs

View File

@ -14,6 +14,7 @@ import time
import stat
# Import salt libs
import salt.acl
import salt.crypt
import salt.cache
import salt.client
@ -1186,88 +1187,50 @@ class LocalFuncs(object):
)
minions = _res['minions']
# Check for external auth calls
if extra.get('token', False):
# Authenticate
token = self.loadauth.authenticate_token(extra)
if not token:
return ''
# Get acl from eauth module.
auth_list = self.loadauth.get_auth_list(extra, token)
# Authorize the request
if not self.ckminions.auth_check(
auth_list,
load['fun'],
load['arg'],
load['tgt'],
load.get('tgt_type', 'glob'),
minions=minions,
# always accept find_job
whitelist=['saltutil.find_job'],
):
log.warning('Authentication failure of type "token" occurred.')
return ''
load['user'] = token['name']
log.debug('Minion tokenized user = "{0}"'.format(load['user']))
elif 'eauth' in extra:
# Authenticate.
if not self.loadauth.authenticate_eauth(extra):
return ''
# Get acl from eauth module.
auth_list = self.loadauth.get_auth_list(extra)
# Authorize the request
if not self.ckminions.auth_check(
auth_list,
load['fun'],
load['arg'],
load['tgt'],
load.get('tgt_type', 'glob'),
minions=minions,
# always accept find_job
whitelist=['saltutil.find_job'],
):
log.warning('Authentication failure of type "eauth" occurred.')
return ''
load['user'] = self.loadauth.load_name(extra) # The username we are attempting to auth with
# Verify that the caller has root on master
# Check for external auth calls and authenticate
auth_type, err_name, key = self._prep_auth_info(extra)
if auth_type == 'user':
auth_check = self.loadauth.check_authentication(load, auth_type, key=key)
else:
auth_ret = self.loadauth.authenticate_key(load, self.key)
if auth_ret is False:
auth_check = self.loadauth.check_authentication(extra, auth_type)
# Setup authorization list variable and error information
auth_list = auth_check.get('auth_list', [])
error = auth_check.get('error')
err_msg = 'Authentication failure of type "{0}" occurred.'.format(auth_type)
if error:
# Authentication error occurred: do not continue.
log.warning(err_msg)
return ''
# All Token, Eauth, and non-root users must pass the authorization check
if auth_type != 'user' or (auth_type == 'user' and auth_list):
# Authorize the request
authorized = self.ckminions.auth_check(
auth_list,
load['fun'],
load['arg'],
load['tgt'],
load.get('tgt_type', 'glob'),
minions=minions,
# always accept find_job
whitelist=['saltutil.find_job'],
)
if not authorized:
# Authorization error occurred. Log warning and do not continue.
log.warning(err_msg)
return ''
if auth_ret is not True:
if salt.auth.AuthUser(load['user']).is_sudo():
if not self.opts['sudo_acl'] or not self.opts['publisher_acl']:
auth_ret = True
if auth_ret is not True:
# Avoid circular import
import salt.utils.master
auth_list = salt.utils.master.get_values_of_matching_keys(
self.opts['publisher_acl'],
auth_ret)
if not auth_list:
log.warning(
'Authentication failure of type "user" occurred.'
)
return ''
if not self.ckminions.auth_check(
auth_list,
load['fun'],
load['arg'],
load['tgt'],
load.get('tgt_type', 'glob'),
minions=minions,
# always accept find_job
whitelist=['saltutil.find_job'],
):
log.warning('Authentication failure of type "user" occurred.')
return ''
# Perform some specific auth_type tasks after the authorization check
if auth_type == 'token':
username = auth_check.get('username')
load['user'] = username
log.debug('Minion tokenized user = "{0}"'.format(username))
elif auth_type == 'eauth':
# The username we are attempting to auth with
load['user'] = self.loadauth.load_name(extra)
# If we order masters (via a syndic), don't short circuit if no minions
# are found

View File

@ -40,7 +40,7 @@ event <tag> [<extra>, <data>]
Example of usage
.. code:: txt
.. code-block:: txt
08:33:57 @gtmanfred > !ping
08:33:57 gtmanbot > gtmanfred: pong
@ -49,7 +49,7 @@ Example of usage
08:34:17 @gtmanfred > !event test/tag/ircbot irc is usefull
08:34:17 gtmanbot > gtmanfred: TaDa!
.. code:: txt
.. code-block:: txt
[DEBUG ] Sending event: tag = salt/engines/ircbot/test/tag/ircbot; data = {'_stamp': '2016-11-28T14:34:16.633623', 'data': [u'irc', u'is', u'usefull']}

View File

@ -13,7 +13,6 @@ as those returned here
# Import python libs
from __future__ import absolute_import
import os
import json
import socket
import sys
import glob
@ -936,8 +935,6 @@ def _virtual(osdata):
zone = __salt__['cmd.run']('{0}'.format(zonename))
if zone != 'global':
grains['virtual'] = 'zone'
if salt.utils.platform.is_smartos_zone():
grains.update(_smartos_zone_data())
# Check if it's a branded zone (i.e. Solaris 8/9 zone)
if isdir('/.SUNWnative'):
grains['virtual'] = 'zone'
@ -1671,8 +1668,6 @@ def os_data():
])
# store a untouched copy of the timestamp in osrelease_stamp
grains['osrelease_stamp'] = uname_v
if salt.utils.platform.is_smartos_globalzone():
grains.update(_smartos_computenode_data())
elif os.path.isfile('/etc/release'):
with salt.utils.files.fopen('/etc/release', 'r') as fp_:
rel_data = fp_.read()
@ -1777,9 +1772,6 @@ def os_data():
# Get the hardware and bios data
grains.update(_hw_data(grains))
# Get zpool data
grains.update(_zpool_data(grains))
# Load the virtual machine info
grains.update(_virtual(grains))
grains.update(_ps(grains))
@ -2371,123 +2363,6 @@ def _hw_data(osdata):
return grains
def _smartos_computenode_data():
'''
Return useful information from a SmartOS compute node
'''
# Provides:
# vms_total
# vms_running
# vms_stopped
# sdc_version
# vm_capable
# vm_hw_virt
if salt.utils.platform.is_proxy():
return {}
grains = {}
# *_vms grains
grains['computenode_vms_total'] = len(__salt__['cmd.run']('vmadm list -p').split("\n"))
grains['computenode_vms_running'] = len(__salt__['cmd.run']('vmadm list -p state=running').split("\n"))
grains['computenode_vms_stopped'] = len(__salt__['cmd.run']('vmadm list -p state=stopped').split("\n"))
# sysinfo derived grains
sysinfo = json.loads(__salt__['cmd.run']('sysinfo'))
grains['computenode_sdc_version'] = sysinfo['SDC Version']
grains['computenode_vm_capable'] = sysinfo['VM Capable']
if sysinfo['VM Capable']:
grains['computenode_vm_hw_virt'] = sysinfo['CPU Virtualization']
# sysinfo derived smbios grains
grains['manufacturer'] = sysinfo['Manufacturer']
grains['productname'] = sysinfo['Product']
grains['uuid'] = sysinfo['UUID']
return grains
def _smartos_zone_data():
'''
Return useful information from a SmartOS zone
'''
# Provides:
# pkgsrcversion
# imageversion
# pkgsrcpath
# zonename
# zoneid
# hypervisor_uuid
# datacenter
if salt.utils.platform.is_proxy():
return {}
grains = {}
pkgsrcversion = re.compile('^release:\\s(.+)')
imageversion = re.compile('Image:\\s(.+)')
pkgsrcpath = re.compile('PKG_PATH=(.+)')
if os.path.isfile('/etc/pkgsrc_version'):
with salt.utils.files.fopen('/etc/pkgsrc_version', 'r') as fp_:
for line in fp_:
match = pkgsrcversion.match(line)
if match:
grains['pkgsrcversion'] = match.group(1)
if os.path.isfile('/etc/product'):
with salt.utils.files.fopen('/etc/product', 'r') as fp_:
for line in fp_:
match = imageversion.match(line)
if match:
grains['imageversion'] = match.group(1)
if os.path.isfile('/opt/local/etc/pkg_install.conf'):
with salt.utils.files.fopen('/opt/local/etc/pkg_install.conf', 'r') as fp_:
for line in fp_:
match = pkgsrcpath.match(line)
if match:
grains['pkgsrcpath'] = match.group(1)
if 'pkgsrcversion' not in grains:
grains['pkgsrcversion'] = 'Unknown'
if 'imageversion' not in grains:
grains['imageversion'] = 'Unknown'
if 'pkgsrcpath' not in grains:
grains['pkgsrcpath'] = 'Unknown'
grains['zonename'] = __salt__['cmd.run']('zonename')
grains['zoneid'] = __salt__['cmd.run']('zoneadm list -p | awk -F: \'{ print $1 }\'', python_shell=True)
return grains
def _zpool_data(grains):
'''
Provide grains about zpools
'''
# quickly return if windows or proxy
if salt.utils.platform.is_windows() or 'proxyminion' in __opts__:
return {}
# quickly return if NetBSD (ZFS still under development)
if salt.utils.platform.is_netbsd():
return {}
# quickly return if no zpool and zfs command
if not salt.utils.path.which('zpool'):
return {}
# collect zpool data
zpool_grains = {}
for zpool in __salt__['cmd.run']('zpool list -H -o name,size').splitlines():
zpool = zpool.split()
zpool_grains[zpool[0]] = zpool[1]
# return grain data
if len(zpool_grains.keys()) < 1:
return {}
return {'zpool': zpool_grains}
def get_server_id():
'''
Provides an integer based on the FQDN of a machine.

196
salt/grains/smartos.py Normal file
View File

@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
'''
SmartOS grain provider
:maintainer: Jorge Schrauwen <sjorge@blackdot.be>
:maturity: new
:depends: salt.utils, salt.ext.six, salt.module.cmdmod
:platform: SmartOS
.. versionadded:: nitrogen
'''
from __future__ import absolute_import
# Import python libs
import os
import re
import json
import logging
# Import salt libs
import salt.utils.dictupdate
import salt.utils.path
import salt.utils.platform
from salt.ext.six.moves import zip
# Solve the Chicken and egg problem where grains need to run before any
# of the modules are loaded and are generally available for any usage.
import salt.modules.cmdmod
__virtualname__ = 'smartos'
__salt__ = {
'cmd.run': salt.modules.cmdmod.run,
}
log = logging.getLogger(__name__)
def __virtual__():
'''
Only load when we are on SmartOS
'''
if salt.utils.platform.is_smartos():
return __virtualname__
return False
def _smartos_computenode_data():
'''
Return useful information from a SmartOS compute node
'''
# Provides:
# vms_total
# vms_running
# vms_stopped
# vms_type
# sdc_version
# vm_capable
# vm_hw_virt
grains = {}
# collect vm data
vms = {}
for vm in __salt__['cmd.run']('vmadm list -p -o uuid,alias,state,type').split("\n"):
vm = dict(list(zip(['uuid', 'alias', 'state', 'type'], vm.split(':'))))
vms[vm['uuid']] = vm
del vms[vm['uuid']]['uuid']
# set vm grains
grains['computenode_vms_total'] = len(vms)
grains['computenode_vms_running'] = 0
grains['computenode_vms_stopped'] = 0
grains['computenode_vms_type'] = {'KVM': 0, 'LX': 0, 'OS': 0}
for vm in vms:
if vms[vm]['state'].lower() == 'running':
grains['computenode_vms_running'] += 1
elif vms[vm]['state'].lower() == 'stopped':
grains['computenode_vms_stopped'] += 1
if vms[vm]['type'] not in grains['computenode_vms_type']:
# NOTE: be prepared for when bhyve gets its own type
grains['computenode_vms_type'][vms[vm]['type']] = 0
grains['computenode_vms_type'][vms[vm]['type']] += 1
# sysinfo derived grains
sysinfo = json.loads(__salt__['cmd.run']('sysinfo'))
grains['computenode_sdc_version'] = sysinfo['SDC Version']
grains['computenode_vm_capable'] = sysinfo['VM Capable']
if sysinfo['VM Capable']:
grains['computenode_vm_hw_virt'] = sysinfo['CPU Virtualization']
# sysinfo derived smbios grains
grains['manufacturer'] = sysinfo['Manufacturer']
grains['productname'] = sysinfo['Product']
grains['uuid'] = sysinfo['UUID']
return grains
def _smartos_zone_data():
'''
Return useful information from a SmartOS zone
'''
# Provides:
# zoneid
# zonename
# imageversion
grains = {
'zoneid': __salt__['cmd.run']('zoneadm list -p | awk -F: \'{ print $1 }\'', python_shell=True),
'zonename': __salt__['cmd.run']('zonename'),
'imageversion': 'Unknown',
}
imageversion = re.compile('Image:\\s(.+)')
if os.path.isfile('/etc/product'):
with salt.utils.files.fopen('/etc/product', 'r') as fp_:
for line in fp_:
match = imageversion.match(line)
if match:
grains['imageversion'] = match.group(1)
return grains
def _smartos_zone_pkgsrc_data():
'''
SmartOS zone pkgsrc information
'''
# Provides:
# pkgsrcversion
# pkgsrcpath
grains = {
'pkgsrcversion': 'Unknown',
'pkgsrcpath': 'Unknown',
}
pkgsrcversion = re.compile('^release:\\s(.+)')
if os.path.isfile('/etc/pkgsrc_version'):
with salt.utils.files.fopen('/etc/pkgsrc_version', 'r') as fp_:
for line in fp_:
match = pkgsrcversion.match(line)
if match:
grains['pkgsrcversion'] = match.group(1)
pkgsrcpath = re.compile('PKG_PATH=(.+)')
if os.path.isfile('/opt/local/etc/pkg_install.conf'):
with salt.utils.files.fopen('/opt/local/etc/pkg_install.conf', 'r') as fp_:
for line in fp_:
match = pkgsrcpath.match(line)
if match:
grains['pkgsrcpath'] = match.group(1)
return grains
def _smartos_zone_pkgin_data():
'''
SmartOS zone pkgsrc information
'''
# Provides:
# pkgin_repositories
grains = {
'pkgin_repositories': [],
}
pkginrepo = re.compile('^(?:https|http|ftp|file)://.*$')
if os.path.isfile('/opt/local/etc/pkgin/repositories.conf'):
with salt.utils.files.fopen('/opt/local/etc/pkgin/repositories.conf', 'r') as fp_:
for line in fp_:
if pkginrepo.match(line):
grains['pkgin_repositories'].append(line)
return grains
def smartos():
'''
Provide grains for SmartOS
'''
grains = {}
if salt.utils.platform.is_smartos_zone():
grains = salt.utils.dictupdate.update(grains, _smartos_zone_data(), merge_lists=True)
grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgsrc_data(), merge_lists=True)
grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgin_data(), merge_lists=True)
elif salt.utils.platform.is_smartos_globalzone():
grains = salt.utils.dictupdate.update(grains, _smartos_computenode_data(), merge_lists=True)
return grains
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

115
salt/grains/zfs.py Normal file
View File

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
'''
ZFS grain provider
:maintainer: Jorge Schrauwen <sjorge@blackdot.be>
:maturity: new
:depends: salt.utils, salt.module.cmdmod
:platform: illumos,freebsd,linux
.. versionadded:: Oxygen
'''
from __future__ import absolute_import
# Import python libs
import logging
# Import salt libs
import salt.utils.dictupdate
import salt.utils.path
import salt.utils.platform
# Solve the Chicken and egg problem where grains need to run before any
# of the modules are loaded and are generally available for any usage.
import salt.modules.cmdmod
__virtualname__ = 'zfs'
__salt__ = {
'cmd.run': salt.modules.cmdmod.run,
}
log = logging.getLogger(__name__)
def __virtual__():
'''
Load zfs grains
'''
# NOTE: we always load this grain so we can properly export
# atleast the zfs_support grain
return __virtualname__
def _check_retcode(cmd):
'''
Simple internal wrapper for cmdmod.retcode
'''
return salt.modules.cmdmod.retcode(cmd, output_loglevel='quiet', ignore_retcode=True) == 0
def _zfs_support():
'''
Provide information about zfs kernel module
'''
grains = {'zfs_support': False}
# Check for zfs support
# NOTE: ZFS on Windows is in development
# NOTE: ZFS on NetBSD is in development
on_supported_platform = False
if salt.utils.platform.is_sunos() and salt.utils.path.which('zfs'):
on_supported_platform = True
elif salt.utils.platform.is_freebsd() and _check_retcode('kldstat -q -m zfs'):
on_supported_platform = True
elif salt.utils.platform.is_linux():
modinfo = salt.utils.path.which('modinfo')
if modinfo:
on_supported_platform = _check_retcode('{0} zfs'.format(modinfo))
else:
on_supported_platform = _check_retcode('ls /sys/module/zfs')
# NOTE: fallback to zfs-fuse if needed
if not on_supported_platform:
_zfs_fuse = lambda f: __salt__['service.' + f]('zfs-fuse')
if _zfs_fuse('available') and (_zfs_fuse('status') or _zfs_fuse('start')):
on_supported_platform = True
# Additional check for the zpool command
if on_supported_platform and salt.utils.path.which('zpool'):
grains['zfs_support'] = True
return grains
def _zfs_pool_data():
'''
Provide grains about zpools
'''
grains = {}
# collect zpool data
zpool_cmd = salt.utils.path.which('zpool')
for zpool in __salt__['cmd.run']('{zpool} list -H -o name,size'.format(zpool=zpool_cmd)).splitlines():
if 'zpool' not in grains:
grains['zpool'] = {}
zpool = zpool.split()
grains['zpool'][zpool[0]] = zpool[1]
# return grain data
return grains
def zfs():
'''
Provide grains for zfs/zpool
'''
grains = {}
grains = salt.utils.dictupdate.update(grains, _zfs_support(), merge_lists=True)
if grains['zfs_support']:
grains = salt.utils.dictupdate.update(grains, _zfs_pool_data(), merge_lists=True)
return grains
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -1840,89 +1840,52 @@ class ClearFuncs(object):
clear_load.get(u'tgt_type', u'glob'),
delimiter
)
minions = _res.get('minions', list())
missing = _res.get('missing', list())
minions = _res.get(u'minions', list())
missing = _res.get(u'missing', list())
# Check for external auth calls
if extra.get(u'token', False):
# Authenticate.
token = self.loadauth.authenticate_token(extra)
if not token:
return u''
# Get acl
auth_list = self.loadauth.get_auth_list(extra, token)
# Authorize the request
if not self.ckminions.auth_check(
auth_list,
clear_load[u'fun'],
clear_load[u'arg'],
clear_load[u'tgt'],
clear_load.get(u'tgt_type', u'glob'),
minions=minions,
# always accept find_job
whitelist=[u'saltutil.find_job'],
):
log.warning(u'Authentication failure of type "token" occurred.')
return u''
clear_load[u'user'] = token[u'name']
log.debug(u'Minion tokenized user = "%s"', clear_load[u'user'])
elif u'eauth' in extra:
# Authenticate.
if not self.loadauth.authenticate_eauth(extra):
return u''
# Get acl from eauth module.
auth_list = self.loadauth.get_auth_list(extra)
# Authorize the request
if not self.ckminions.auth_check(
auth_list,
clear_load[u'fun'],
clear_load[u'arg'],
clear_load[u'tgt'],
clear_load.get(u'tgt_type', u'glob'),
minions=minions,
# always accept find_job
whitelist=[u'saltutil.find_job'],
):
log.warning(u'Authentication failure of type "eauth" occurred.')
return u''
clear_load[u'user'] = self.loadauth.load_name(extra) # The username we are attempting to auth with
# Verify that the caller has root on master
# Check for external auth calls and authenticate
auth_type, err_name, key, sensitive_load_keys = self._prep_auth_info(extra)
if auth_type == 'user':
auth_check = self.loadauth.check_authentication(clear_load, auth_type, key=key)
else:
auth_ret = self.loadauth.authenticate_key(clear_load, self.key)
if auth_ret is False:
auth_check = self.loadauth.check_authentication(extra, auth_type)
# Setup authorization list variable and error information
auth_list = auth_check.get(u'auth_list', [])
err_msg = u'Authentication failure of type "{0}" occurred.'.format(auth_type)
if auth_check.get(u'error'):
# Authentication error occurred: do not continue.
log.warning(err_msg)
return u''
# All Token, Eauth, and non-root users must pass the authorization check
if auth_type != u'user' or (auth_type == u'user' and auth_list):
# Authorize the request
authorized = self.ckminions.auth_check(
auth_list,
clear_load[u'fun'],
clear_load[u'arg'],
clear_load[u'tgt'],
clear_load.get(u'tgt_type', u'glob'),
minions=minions,
# always accept find_job
whitelist=[u'saltutil.find_job'],
)
if not authorized:
# Authorization error occurred. Do not continue.
log.warning(err_msg)
return u''
if auth_ret is not True:
if salt.auth.AuthUser(clear_load[u'user']).is_sudo():
if not self.opts[u'sudo_acl'] or not self.opts[u'publisher_acl']:
auth_ret = True
if auth_ret is not True:
auth_list = salt.utils.master.get_values_of_matching_keys(
self.opts[u'publisher_acl'],
auth_ret)
if not auth_list:
log.warning(
u'Authentication failure of type "user" occurred.'
)
return u''
if not self.ckminions.auth_check(
auth_list,
clear_load[u'fun'],
clear_load[u'arg'],
clear_load[u'tgt'],
clear_load.get(u'tgt_type', u'glob'),
minions=minions,
# always accept find_job
whitelist=[u'saltutil.find_job'],
):
log.warning(u'Authentication failure of type "user" occurred.')
return u''
# Perform some specific auth_type tasks after the authorization check
if auth_type == u'token':
username = auth_check.get(u'username')
clear_load[u'user'] = username
log.debug(u'Minion tokenized user = "%s"', username)
elif auth_type == u'eauth':
# The username we are attempting to auth with
clear_load[u'user'] = self.loadauth.load_name(extra)
# If we order masters (via a syndic), don't short circuit if no minions
# are found

View File

@ -1037,7 +1037,7 @@ class Minion(MinionBase):
u'may result in loss of contact with minions. Please '
u'upgrade your ZMQ!'
)
# Late setup the of the opts grains, so we can log from the grains
# Late setup of the opts grains, so we can log from the grains
# module. If this is a proxy, however, we need to init the proxymodule
# before we can get the grains. We do this for proxies in the
# post_master_init

View File

@ -2,7 +2,7 @@
'''
Connection module for Amazon APIGateway
.. versionadded::
.. versionadded:: 2016.11.0
:configuration: This module accepts explicit Lambda credentials but can also
utilize IAM roles assigned to the instance trough Instance Profiles.

View File

@ -1747,6 +1747,7 @@ def set_volumes_tags(tag_maps, authoritative=False, dry_run=False,
YAML example fragment:
.. code-block:: yaml
- filters:
attachment.instance_id: i-abcdef12
tags:

View File

@ -156,7 +156,7 @@ def create_file_system(name,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.create_file_system efs-name generalPurpose
'''
@ -222,7 +222,7 @@ def create_mount_target(filesystemid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.create_mount_target filesystemid subnetid
'''
@ -269,7 +269,7 @@ def create_tags(filesystemid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.create_tags
'''
@ -301,7 +301,7 @@ def delete_file_system(filesystemid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.delete_file_system filesystemid
'''
@ -335,7 +335,7 @@ def delete_mount_target(mounttargetid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.delete_mount_target mounttargetid
'''
@ -363,7 +363,7 @@ def delete_tags(filesystemid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.delete_tags
'''
@ -398,7 +398,7 @@ def get_file_systems(filesystemid=None,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.get_file_systems efs-id
'''
@ -454,7 +454,7 @@ def get_mount_targets(filesystemid=None,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.get_mount_targets
'''
@ -493,7 +493,7 @@ def get_tags(filesystemid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.get_tags efs-id
'''
@ -527,7 +527,7 @@ def set_security_groups(mounttargetid,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' boto_efs.set_security_groups my-mount-target-id my-sec-group
'''

View File

@ -1703,7 +1703,6 @@ def build_interface(iface, iface_type, enabled, **settings):
salt '*' ip.build_interface eth0 eth <settings>
'''
iface = iface.lower()
iface_type = iface_type.lower()
if iface_type not in _IFACE_TYPES:
@ -1773,7 +1772,6 @@ def build_routes(iface, **settings):
salt '*' ip.build_routes eth0 <settings>
'''
iface = iface.lower()
opts = _parse_routes(iface, settings)
try:
template = JINJA.get_template('route_eth.jinja')

View File

@ -1,4 +1,9 @@
# -*- coding: utf-8 -*-
'''
Module to work with salt formula defaults files
'''
from __future__ import absolute_import
import json
import logging

View File

@ -1777,9 +1777,9 @@ def line(path, content=None, match=None, mode=None, location=None,
location
Defines where to place content in the line. Note this option is only
used when ``mode=insert`` is specified. If a location is passed in, it
takes precedence over both the ``before`` and ``after`` kwargs. Valid
locations are:
used when ``mode=insert`` or ``mode=ensure`` is specified. If a location
is passed in, it takes precedence over both the ``before`` and ``after``
kwargs. Valid locations are:``
- start
Place the content at the beginning of the file.
@ -1930,63 +1930,83 @@ def line(path, content=None, match=None, mode=None, location=None,
after = after and after.strip()
before = before and before.strip()
if before and after:
_assert_occurrence(body, before, 'before')
_assert_occurrence(body, after, 'after')
a_idx = b_idx = -1
idx = 0
body = body.split(os.linesep)
for _line in body:
idx += 1
if _line.find(before) > -1 and b_idx < 0:
b_idx = idx
if _line.find(after) > -1 and a_idx < 0:
a_idx = idx
# Add
if not b_idx - a_idx - 1:
body = body[:a_idx] + [content] + body[b_idx - 1:]
elif b_idx - a_idx - 1 == 1:
if _starts_till(body[a_idx:b_idx - 1][0], content) > -1:
body[a_idx] = _get_line_indent(body[a_idx - 1], content, indent)
else:
raise CommandExecutionError('Found more than one line between boundaries "before" and "after".')
body = os.linesep.join(body)
elif before and not after:
_assert_occurrence(body, before, 'before')
body = body.split(os.linesep)
if location:
found = False
out = []
for idx in range(len(body)):
if body[idx].find(before) > -1:
prev = (idx > 0 and idx or 1) - 1
out.append(_get_line_indent(body[prev], content, indent))
if _starts_till(out[prev], content) > -1:
del out[prev]
out.append(body[idx])
body = os.linesep.join(out)
elif not before and after:
_assert_occurrence(body, after, 'after')
body = body.split(os.linesep)
skip = None
out = []
for idx in range(len(body)):
if skip != body[idx]:
out.append(body[idx])
if body[idx].find(after) > -1:
next_line = idx + 1 < len(body) and body[idx + 1] or None
if next_line is not None and _starts_till(next_line, content) > -1:
skip = next_line
out.append(_get_line_indent(body[idx], content, indent))
body = os.linesep.join(out)
if body:
for file_line in body.split(os.linesep):
if file_line.find(match) > -1 and not file_line == content:
out.append(_get_line_indent(file_line, content, indent))
found = True
elif file_line == content:
out.append(file_line)
found = True
else:
out.append(file_line)
body = os.linesep.join(out)
if not found:
if location == 'start':
body = os.linesep.join([content, body])
elif location == 'end':
body = os.linesep.join([body, _get_line_indent(body[-1], content, indent) if body else content])
else:
raise CommandExecutionError("Wrong conditions? "
"Unable to ensure line without knowing "
"where to put it before and/or after.")
if before and after:
_assert_occurrence(body, before, 'before')
_assert_occurrence(body, after, 'after')
a_idx = b_idx = -1
idx = 0
body = body.split(os.linesep)
for _line in body:
idx += 1
if _line.find(before) > -1 and b_idx < 0:
b_idx = idx
if _line.find(after) > -1 and a_idx < 0:
a_idx = idx
# Add
if not b_idx - a_idx - 1:
body = body[:a_idx] + [content] + body[b_idx - 1:]
elif b_idx - a_idx - 1 == 1:
if _starts_till(body[a_idx:b_idx - 1][0], content) > -1:
body[a_idx] = _get_line_indent(body[a_idx - 1], content, indent)
else:
raise CommandExecutionError('Found more than one line between boundaries "before" and "after".')
body = os.linesep.join(body)
elif before and not after:
_assert_occurrence(body, before, 'before')
body = body.split(os.linesep)
out = []
for idx in range(len(body)):
if body[idx].find(before) > -1:
prev = (idx > 0 and idx or 1) - 1
out.append(_get_line_indent(body[prev], content, indent))
if _starts_till(out[prev], content) > -1:
del out[prev]
out.append(body[idx])
body = os.linesep.join(out)
elif not before and after:
_assert_occurrence(body, after, 'after')
body = body.split(os.linesep)
skip = None
out = []
for idx in range(len(body)):
if skip != body[idx]:
out.append(body[idx])
if body[idx].find(after) > -1:
next_line = idx + 1 < len(body) and body[idx + 1] or None
if next_line is not None and _starts_till(next_line, content) > -1:
skip = next_line
out.append(_get_line_indent(body[idx], content, indent))
body = os.linesep.join(out)
else:
raise CommandExecutionError("Wrong conditions? "
"Unable to ensure line without knowing "
"where to put it before and/or after.")
changed = body_before != hashlib.sha256(salt.utils.stringutils.to_bytes(body)).hexdigest()

View File

@ -413,6 +413,7 @@ def delkey(key):
CLI Example:
.. code-block:: bash
salt '*' grains.delkey key
'''
setval(key, None, destructive=True)

View File

@ -112,7 +112,7 @@ def run(script):
CLI Example:
.. code-block::
.. code-block:: bash
salt '*' jenkins.run 'Jenkins.instance.doSafeRestart()'

View File

@ -28,6 +28,7 @@ For an item only one field should be provided. Either a `data` or a `file` entry
In case both are provided the `file` entry is prefered.
.. code-block:: bash
salt '*' kubernetes.nodes api_url=http://k8s-api-server:port api_user=myuser api_password=pass
.. versionadded: 2017.7.0
@ -35,6 +36,7 @@ In case both are provided the `file` entry is prefered.
# Import Python Futures
from __future__ import absolute_import
import sys
import os.path
import base64
import logging
@ -164,13 +166,16 @@ def _setup_conn(**kwargs):
if client_key_file:
kubernetes.client.configuration.key_file = client_key_file
if client_key:
elif client_key:
with tempfile.NamedTemporaryFile(prefix='salt-kube-', delete=False) as k:
k.write(base64.b64decode(client_key))
kubernetes.client.configuration.key_file = k.name
else:
kubernetes.client.configuration.key_file = None
# The return makes unit testing easier
return vars(kubernetes.client.configuration)
def _cleanup(**kwargs):
ca = kubernetes.client.configuration.ssl_ca_cert
@ -269,7 +274,7 @@ def node_labels(name, **kwargs):
match = node(name, **kwargs)
if match is not None:
return match.metadata.labels
return match['metadata']['labels']
return {}
@ -1050,14 +1055,22 @@ def create_service(
def create_secret(
name,
namespace,
data,
source,
template,
saltenv,
namespace='default',
data=None,
source=None,
template=None,
saltenv='base',
**kwargs):
'''
Creates the kubernetes secret as defined by the user.
CLI Examples::
salt 'minion1' kubernetes.create_secret \
passwords default '{"db": "letmein"}'
salt 'minion2' kubernetes.create_secret \
name=passwords namespace=default data='{"db": "letmein"}'
'''
if source:
data = __read_and_render_yaml_file(source, template, saltenv)
@ -1099,12 +1112,20 @@ def create_configmap(
name,
namespace,
data,
source,
template,
saltenv,
source=None,
template=None,
saltenv='base',
**kwargs):
'''
Creates the kubernetes configmap as defined by the user.
CLI Examples::
salt 'minion1' kubernetes.create_configmap \
settings default '{"example.conf": "# example file"}'
salt 'minion2' kubernetes.create_configmap \
name=settings namespace=default data='{"example.conf": "# example file"}'
'''
if source:
data = __read_and_render_yaml_file(source, template, saltenv)
@ -1273,14 +1294,22 @@ def replace_service(name,
def replace_secret(name,
data,
source,
template,
saltenv,
source=None,
template=None,
saltenv='base',
namespace='default',
**kwargs):
'''
Replaces an existing secret with a new one defined by name and namespace,
having the specificed data.
CLI Examples::
salt 'minion1' kubernetes.replace_secret \
name=passwords data='{"db": "letmein"}'
salt 'minion2' kubernetes.replace_secret \
name=passwords namespace=saltstack data='{"db": "passw0rd"}'
'''
if source:
data = __read_and_render_yaml_file(source, template, saltenv)
@ -1320,14 +1349,22 @@ def replace_secret(name,
def replace_configmap(name,
data,
source,
template,
saltenv,
source=None,
template=None,
saltenv='base',
namespace='default',
**kwargs):
'''
Replaces an existing configmap with a new one defined by name and
namespace, having the specificed data.
namespace with the specified data.
CLI Examples::
salt 'minion1' kubernetes.replace_configmap \
settings default '{"example.conf": "# example file"}'
salt 'minion2' kubernetes.replace_configmap \
name=settings namespace=default data='{"example.conf": "# example file"}'
'''
if source:
data = __read_and_render_yaml_file(source, template, saltenv)
@ -1444,6 +1481,13 @@ def __dict_to_object_meta(name, namespace, metadata):
'''
meta_obj = kubernetes.client.V1ObjectMeta()
meta_obj.namespace = namespace
# Replicate `kubectl [create|replace|apply] --record`
if 'annotations' not in metadata:
metadata['annotations'] = {}
if 'kubernetes.io/change-cause' not in metadata['annotations']:
metadata['annotations']['kubernetes.io/change-cause'] = ' '.join(sys.argv)
for key, value in iteritems(metadata):
if hasattr(meta_obj, key):
setattr(meta_obj, key, value)

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
'''
.. versionadded:: 2016.3.0
Manage macOS local directory passwords and policies
Manage macOS local directory passwords and policies.
.. versionadded:: 2016.3.0
Note that it is usually better to apply password policies through the creation
of a configuration profile.

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
'''
.. versionadded:: 2016.3.0
System module for sleeping, restarting, and shutting down the system on Mac OS X
System module for sleeping, restarting, and shutting down the system on Mac OS
X.
.. versionadded:: 2016.3.0
.. warning::
Using this module will enable ``atrun`` on the system if it is disabled.

View File

@ -85,6 +85,7 @@ without extra parameters:
salt-run nacl.dec 'tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58='
.. code-block:: yam
# a salt developers minion could have pillar data that includes a nacl public key
nacl.config:
pk: '/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0='

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
Namecheap management
Namecheap dns management
.. versionadded:: 2017.7.0
@ -17,6 +17,7 @@
the namecheap API:
* ``requests``
.. code-block:: bash
pip install requests

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
Namecheap management
Namecheap domains management
.. versionadded:: 2017.7.0
@ -73,7 +73,7 @@ def reactivate(domain_name):
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_domains.reactivate my-domain-name

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
Namecheap management
Namecheap nameservers management
.. versionadded:: 2017.7.0
@ -17,6 +17,7 @@
the namecheap API:
* ``requests``
.. code-block:: bash
pip install requests

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
Namecheap management
Namecheap ssl management
.. versionadded:: 2017.7.0
@ -110,7 +110,7 @@ def reissue(csr_file,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.reissue my-csr-file my-cert-id apachessl
'''
@ -164,7 +164,7 @@ def activate(csr_file,
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.activate my-csr-file my-cert-id apachessl
'''
@ -298,7 +298,7 @@ def renew(years, certificate_id, certificate_type, promotion_code=None):
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.renew 1 my-cert-id RapidSSL
'''
@ -460,7 +460,7 @@ Symantec Secure Site 1 25 24
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.create 2 RapidSSL
'''
@ -557,7 +557,7 @@ def parse_csr(csr_file, certificate_type, http_dc_validation=False):
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.parse_csr my-csr-file PremiumSSL
'''
@ -644,7 +644,7 @@ def get_list(**kwargs):
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.get_list Processing
'''
@ -688,7 +688,7 @@ def get_info(certificate_id, returncertificate=False, returntype=None):
CLI Example:
.. code-block::
.. code-block:: bash
salt 'my-minion' namecheap_ssl.get_info my-cert-id
'''

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
Namecheap management
Namecheap users management
.. versionadded:: 2017.7.0

View File

@ -500,7 +500,7 @@ def cli(*commands, **kwargs): # pylint: disable=unused-argument
CLI Example with TextFSM template:
.. code-block::
.. code-block:: bash
salt '*' net.cli textfsm_parse=True textfsm_path=salt://textfsm/
@ -1238,6 +1238,7 @@ def load_config(filename=None,
Example output:
.. code-block:: python
{
'comment': 'Configuration discarded.',
'already_configured': False,

View File

@ -1,4 +1,9 @@
# -*- coding: utf-8 -*-
'''
Module for OpenSCAP Management
'''
from __future__ import absolute_import
import tempfile
import shlex

View File

@ -10,7 +10,7 @@ What has not been implemented yet can be accessed through ``parallels.prlctl``
and ``parallels.prlsrvctl`` (note the preceding double dash ``--`` as
necessary):
.. code-block::
.. code-block:: bash
salt '*' parallels.prlctl installtools macvm runas=macdev
salt -- '*' parallels.prlctl capture 'macvm --file macvm.display.png' runas=macdev

View File

@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
'''
===========================
Manage the Windows registry
===========================
-----
Hives

View File

@ -1079,7 +1079,6 @@ def build_routes(iface, **settings):
pass
log.debug('Template name: ' + template)
iface = iface.lower()
opts = _parse_routes(iface, settings)
log.debug("Opts: \n {0}".format(opts))
try:

View File

@ -58,11 +58,16 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs):
'''
Parse a string or file through Salt's renderer system
.. versionchanged:: Oxygen
Add support for Salt fileserver URIs.
This is an open-ended function and can be used for a variety of tasks. It
makes use of Salt's "renderer pipes" system to run a string or file through
a pipe of any of the loaded renderer modules.
:param path: The path to a file on the filesystem.
:param path: The path to a file on Salt's fileserver (any URIs supported by
:py:func:`cp.get_url <salt.modules.cp.get_url>`) or on the local file
system.
:param string: An inline string to be used as the file to send through the
renderer system. Note, not all renderer modules can work with strings;
the 'py' renderer requires a file, for example.
@ -113,6 +118,7 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs):
.. code-block:: bash
salt '*' slsutil.renderer salt://path/to/file
salt '*' slsutil.renderer /path/to/file
salt '*' slsutil.renderer /path/to/file.jinja 'jinja'
salt '*' slsutil.renderer /path/to/file.sls 'jinja|yaml'
@ -126,7 +132,7 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs):
renderers = salt.loader.render(__opts__, __salt__)
if path:
path_or_string = path
path_or_string = __salt__['cp.get_url'](path)
elif string:
path_or_string = ':string:'
kwargs['input_data'] = string

View File

@ -229,6 +229,7 @@ def set_config(name='root', **kwargs):
snapper convention. The above example is equivalent to:
.. code-block:: bash
salt '*' snapper.set_config sync_acl=True
'''
try:

View File

@ -186,7 +186,7 @@ def extract_war_version(war):
Examples:
.. code-block::
.. code-block:: text
/path/salt-2015.8.6.war -> 2015.8.6
/path/V6R2013xD5.war -> None

View File

@ -170,6 +170,7 @@ def exportdb():
CLI Example:
.. code-block:: bash
salt '*' udev.exportdb
'''

View File

@ -1,13 +1,16 @@
# -*- coding: utf-8 -*-
'''
Functions to interact with Hashicorp Vault.
:maintainer: SaltStack
:maturity: new
:platform: all
Functions to interact with Hashicorp Vault.
:note: If you see the following error, you'll need to upgrade ``requests`` to atleast 2.4.2
.. code-block:: shell
.. code-block:: text
<timestamp> [salt.pillar][CRITICAL][14337] Pillar render error: Failed to load ext_pillar vault: {'error': "request() got an unexpected keyword argument 'json'"}
@ -151,6 +154,7 @@ def write_secret(path, **kwargs):
CLI Example:
.. code-block:: bash
salt '*' vault.write_secret "secret/my/secret" user="foo" password="bar"
'''
log.debug(
@ -176,6 +180,7 @@ def delete_secret(path):
CLI Example:
.. code-block:: bash
salt '*' vault.delete_secret "secret/my/secret"
'''
log.debug('Deleting vault secrets for {0} in {1}'
@ -199,6 +204,7 @@ def list_secrets(path):
CLI Example:
.. code-block:: bash
salt '*' vault.list_secrets "secret/my/"
'''
log.debug('Listing vault secret keys for {0} in {1}'

View File

@ -4211,6 +4211,7 @@ def list_dvportgroups(dvs=None, portgroup_names=None, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_dvporgroups
salt '*' vsphere.list_dvportgroups dvs=dvs1
@ -4662,6 +4663,7 @@ def list_storage_policies(policy_names=None, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_storage_policies
salt '*' vsphere.list_storage_policy policy_names=[policy_name]
@ -4688,6 +4690,7 @@ def list_default_vsan_policy(service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_storage_policies
salt '*' vsphere.list_storage_policy policy_names=[policy_name]
@ -4726,6 +4729,7 @@ def list_capability_definitions(service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_capabilities
'''
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
@ -4806,6 +4810,7 @@ def create_storage_policy(policy_name, policy_dict, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.create_storage_policy policy_name='policy name'
policy_dict="$policy_dict"
'''
@ -4845,6 +4850,7 @@ def update_storage_policy(policy, policy_dict, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.update_storage_policy policy='policy name'
policy_dict="$policy_dict"
'''
@ -4882,6 +4888,7 @@ def list_default_storage_policy_of_datastore(datastore, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_default_storage_policy_of_datastore datastore=ds1
'''
log.trace('Listing the default storage policy of datastore \'{0}\''
@ -4920,6 +4927,7 @@ def assign_default_storage_policy_to_datastore(policy, datastore,
Default is None.
.. code-block:: bash
salt '*' vsphere.assign_storage_policy_to_datastore
policy='policy name' datastore=ds1
'''
@ -4964,6 +4972,7 @@ def list_datacenters_via_proxy(datacenter_names=None, service_instance=None):
Default is None.
.. code-block:: bash
salt '*' vsphere.list_datacenters_via_proxy
salt '*' vsphere.list_datacenters_via_proxy dc1

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
'''
This module is Alpha
Module for working with Windows PowerShell DSC (Desired State Configuration)
This module is Alpha
This module applies DSC Configurations in the form of PowerShell scripts or
MOF (Managed Object Format) schema files.

View File

@ -5374,7 +5374,7 @@ def set_(computer_policy=None, user_policy=None,
_regedits[regedit]['policy']['Registry']['Type'])
else:
_ret = __salt__['reg.delete_value'](
_regedits[regedit]['polic']['Registry']['Hive'],
_regedits[regedit]['policy']['Registry']['Hive'],
_regedits[regedit]['policy']['Registry']['Path'],
_regedits[regedit]['policy']['Registry']['Value'])
if not _ret:

225
salt/modules/wordpress.py Normal file
View File

@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
'''
This module is used to manage Wordpress installations
:depends: wp binary from http://wp-cli.org/
'''
from __future__ import absolute_import
# Import Python Modules
import collections
# Import Salt Modules
import salt.utils
from salt.ext.six.moves import map
Plugin = collections.namedtuple('Plugin', 'name status update versino')
def __virtual__():
if salt.utils.which('wp'):
return True
return False
def _get_plugins(stuff):
return Plugin(stuff)
def list_plugins(path, user):
'''
List plugins in an installed wordpress path
path
path to wordpress install location
user
user to run the command as
CLI Example:
.. code-block:: bash
salt '*' wordpress.list_plugins /var/www/html apache
'''
ret = []
resp = __salt__['cmd.shell']((
'wp --path={0} plugin list'
).format(path), runas=user)
for line in resp.split('\n')[1:]:
ret.append(line.split('\t'))
return [plugin.__dict__ for plugin in map(_get_plugins, ret)]
def show_plugin(name, path, user):
'''
Show a plugin in a wordpress install and check if it is installed
name
Wordpress plugin name
path
path to wordpress install location
user
user to run the command as
CLI Example:
.. code-block:: bash
salt '*' wordpress.show_plugin HyperDB /var/www/html apache
'''
ret = {'name': name}
resp = __salt__['cmd.shell']((
'wp --path={0} plugin status {1}'
).format(path, name), runas=user).split('\n')
for line in resp:
if 'Status' in line:
ret['status'] = line.split(' ')[-1].lower()
elif 'Version' in line:
ret['version'] = line.split(' ')[-1].lower()
return ret
def activate(name, path, user):
'''
Activate a wordpress plugin
name
Wordpress plugin name
path
path to wordpress install location
user
user to run the command as
CLI Example:
.. code-block:: bash
salt '*' wordpress.activate HyperDB /var/www/html apache
'''
check = show_plugin(name, path, user)
if check['status'] == 'active':
# already active
return None
resp = __salt__['cmd.shell']((
'wp --path={0} plugin activate {1}'
).format(path, name), runas=user)
if 'Success' in resp:
return True
elif show_plugin(name, path, user)['status'] == 'active':
return True
return False
def deactivate(name, path, user):
'''
Deactivate a wordpress plugin
name
Wordpress plugin name
path
path to wordpress install location
user
user to run the command as
CLI Example:
.. code-block:: bash
salt '*' wordpress.deactivate HyperDB /var/www/html apache
'''
check = show_plugin(name, path, user)
if check['status'] == 'inactive':
# already inactive
return None
resp = __salt__['cmd.shell']((
'wp --path={0} plugin deactivate {1}'
).format(path, name), runas=user)
if 'Success' in resp:
return True
elif show_plugin(name, path, user)['status'] == 'inactive':
return True
return False
def is_installed(path, user=None):
'''
Check if wordpress is installed and setup
path
path to wordpress install location
user
user to run the command as
CLI Example:
.. code-block:: bash
salt '*' wordpress.is_installed /var/www/html apache
'''
retcode = __salt__['cmd.retcode']((
'wp --path={0} core is-installed'
).format(path), runas=user)
if retcode == 0:
return True
return False
def install(path, user, admin_user, admin_password, admin_email, title, url):
'''
Run the initial setup functions for a wordpress install
path
path to wordpress install location
user
user to run the command as
admin_user
Username for the Administrative user for the wordpress install
admin_password
Initial Password for the Administrative user for the wordpress install
admin_email
Email for the Administrative user for the wordpress install
title
Title of the wordpress website for the wordpress install
url
Url for the wordpress install
CLI Example:
.. code-block:: bash
salt '*' wordpress.install /var/www/html apache dwallace password123 \
dwallace@example.com "Daniel's Awesome Blog" https://blog.dwallace.com
'''
retcode = __salt__['cmd.retcode']((
'wp --path={0} core install '
'--title="{1}" '
'--admin_user={2} '
"--admin_password='{3}' "
'--admin_email={4} '
'--url={5}'
).format(
path,
title,
admin_user,
admin_password,
admin_email,
url
), runas=user)
if retcode == 0:
return True
return False

View File

@ -17,6 +17,7 @@ import salt.modules.cmdmod
import salt.utils.decorators as decorators
from salt.utils.odict import OrderedDict
__virtualname__ = 'zfs'
log = logging.getLogger(__name__)
# Function alias to set mapping.
@ -25,6 +26,16 @@ __func_alias__ = {
}
def __virtual__():
'''
Only load when the platform has zfs support
'''
if __grains__['zfs_support']:
return __virtualname__
else:
return (False, "The zfs module cannot be loaded: zfs not supported")
@decorators.memoize
def _check_zfs():
'''
@ -51,42 +62,6 @@ def _check_features():
return res['retcode'] == 0
def __virtual__():
'''
Makes sure that ZFS kernel module is loaded.
'''
on_freebsd = __grains__['kernel'] == 'FreeBSD'
on_linux = __grains__['kernel'] == 'Linux'
on_solaris = __grains__['kernel'] == 'SunOS' and __grains__['kernelrelease'] == '5.11'
cmd = ''
if on_freebsd:
cmd = 'kldstat -q -m zfs'
elif on_linux:
modinfo = salt.utils.path.which('modinfo')
if modinfo:
cmd = '{0} zfs'.format(modinfo)
else:
cmd = 'ls /sys/module/zfs'
elif on_solaris:
# not using salt.utils.path.which('zfs') to keep compatible with others
cmd = 'which zfs'
if cmd and salt.modules.cmdmod.retcode(
cmd, output_loglevel='quiet', ignore_retcode=True
) == 0:
return 'zfs'
if __grains__['kernel'] == 'OpenBSD':
return False
_zfs_fuse = lambda f: __salt__['service.' + f]('zfs-fuse')
if _zfs_fuse('available') and (_zfs_fuse('status') or _zfs_fuse('start')):
return 'zfs'
return (False, "The zfs module cannot be loaded: zfs not found")
def exists(name, **kwargs):
'''
.. versionadded:: 2015.5.0

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
'''
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
Concurrency controls in zookeeper
=========================================================================
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
This module allows you to acquire and release a slot. This is primarily useful
for ensureing that no more than N hosts take a specific action at once. This can
also be used to coordinate between masters.

View File

@ -34,6 +34,7 @@ Configuration
be set up as different configuration profiles. For example:
.. code-block:: yaml
zookeeper:
prod:
hosts: zoo1,zoo2,zoo3

View File

@ -19,12 +19,23 @@ from salt.utils.odict import OrderedDict
log = logging.getLogger(__name__)
__virtualname__ = 'zpool'
__func_alias__ = {
'import_': 'import',
'list_': 'list',
}
def __virtual__():
'''
Only load when the platform has zfs support
'''
if __grains__['zfs_support']:
return __virtualname__
else:
return (False, "The zpool module cannot be loaded: zfs not supported")
@salt.utils.decorators.memoize
def _check_zpool():
'''
@ -58,15 +69,6 @@ def _check_mkfile():
return salt.utils.path.which('mkfile')
def __virtual__():
'''
Provides zpool.
'''
if _check_zpool():
return 'zpool'
return (False, "Module zpool: zpool not found")
def healthy():
'''
.. versionadded:: 2016.3.0

View File

@ -71,12 +71,12 @@ A REST API for Salt
log_access_file
Path to a file to write HTTP access logs.
.. versionaddedd:: 2016.11.0
.. versionadded:: 2016.11.0
log_error_file
Path to a file to write HTTP error logs.
.. versionaddedd:: 2016.11.0
.. versionadded:: 2016.11.0
ssl_crt
The path to a SSL certificate. (See below)

View File

@ -221,6 +221,8 @@ def _format_host(host, data):
tcolor = colors['GREEN']
orchestration = ret.get('__orchestration__', False)
schanged, ctext = _format_changes(ret['changes'], orchestration)
if not ctext and 'pchanges' in ret:
schanged, ctext = _format_changes(ret['pchanges'], orchestration)
nchanges += 1 if schanged else 0
# Skip this state if it was successful & diff output was requested

View File

@ -2,7 +2,7 @@
'''
Store key/value pairs in a CSV file
.. versionaddedd:: 2016.11.0
.. versionadded:: 2016.11.0
Example configuration:

View File

@ -16,6 +16,7 @@ Command Line
------------
.. code-block:: bash
salt-call pillar.get nodegroups
local:
- class_infra

View File

@ -466,7 +466,9 @@ def exit_success(jid, ext_source=None):
The external job cache to use. Default: `None`.
CLI Example:
.. code-block:: bash
salt-run jobs.exit_success 20160520145827701627
'''
ret = dict()

View File

@ -85,6 +85,7 @@ without extra parameters:
salt-run nacl.dec 'tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58='
.. code-block:: yam
# a salt developers minion could have pillar data that includes a nacl public key
nacl.config:
pk: '/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0='

View File

@ -85,7 +85,7 @@ def get_opts():
CLI Example:
.. code-block::
.. code-block:: bash
salt-run test.get_opts
'''

View File

@ -13,6 +13,7 @@ be configured in either the minion or master configuration file. This profile
requires very little. For example:
.. code-block:: yaml
myconsul:
driver: consul
host: 127.0.0.1

View File

@ -124,7 +124,9 @@ def present(name, api_name, swagger_file, stage_name, api_key_required,
with the following schema. The lambda functions should throw exceptions for any non successful responses.
An optional pattern field can be specified in errorMessage field to aid the response mapping from Lambda
to the proper error return status codes.
.. code-block:: yaml
Error:
type: object
properties:

View File

@ -984,7 +984,7 @@ def instance_absent(name, instance_name=None, instance_id=None,
'''
Ensure an EC2 instance does not exist (is stopped and removed).
.. versionupdated:: 2016.11.0
.. versionchanged:: 2016.11.0
name
(string) - The name of the state definition.
@ -1010,6 +1010,7 @@ def instance_absent(name, instance_name=None, instance_id=None,
YAML example fragment:
.. code-block:: yaml
- filters:
vpc-id: vpc-abcdef12
@ -1230,6 +1231,7 @@ def volumes_tagged(name, tag_maps, authoritative=False, region=None, key=None,
YAML example fragment:
.. code-block:: yaml
- filters:
attachment.instance_id: i-abcdef12
tags:

View File

@ -1636,7 +1636,7 @@ def policy_absent(name,
def saml_provider_present(name, saml_metadata_document, region=None, key=None, keyid=None, profile=None):
'''
.. versionadded::
.. versionadded:: 2016.11.0
Ensure the SAML provider with the specified name is present.
@ -1694,7 +1694,7 @@ def saml_provider_present(name, saml_metadata_document, region=None, key=None, k
def saml_provider_absent(name, region=None, key=None, keyid=None, profile=None):
'''
.. versionadded::
.. versionadded:: 2016.11.0
Ensure the SAML provider with the specified name is absent.

View File

@ -153,6 +153,7 @@ def function_present(name, FunctionName, Runtime, Role, Handler, ZipFile=None,
to the same VPC. This is a dict of the form:
.. code-block:: yaml
VpcConfig:
SecurityGroupNames:
- mysecgroup1

View File

@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from salt.ext.six.moves import map
'''
CSF Ip tables management
========================
@ -18,6 +15,9 @@ CSF Ip tables management
method: allow
''' # pylint: disable=W0105
from __future__ import absolute_import
from salt.ext.six.moves import map
def __virtual__():
return 'csf'

View File

@ -300,7 +300,7 @@ def running(name,
Additionally, the specified selinux context will be set within the
container.
``<read_only>`` can be either ``ro`` for read-write access, or ``ro``
``<read_only>`` can be either ``rw`` for read-write access, or ``ro``
for read-only access. When omitted, it is assumed to be read-write.
``<selinux_context>`` can be ``z`` if the volume is shared between
@ -1235,6 +1235,7 @@ def running(name,
If ``True``, runs the exec process with extended privileges
.. code-block:: yaml
foo:
docker_container.running:
- image: bar/baz:lates

View File

@ -65,6 +65,7 @@ def present(name, definition=None):
**Example:**
.. code-block:: yaml
# Default settings
mytestindex:
elasticsearch_index.present

View File

@ -65,6 +65,7 @@ def present(name, definition):
**Example:**
.. code-block:: yaml
mytestindex2_template:
elasticsearch_index_template.present:
- definition:

View File

@ -45,6 +45,7 @@ Etcd profile configuration can be overriden using following arguments: ``host``,
``port``, ``username``, ``password``, ``ca``, ``client_key`` and ``client_cert``.
.. code-block:: yaml
my-value:
etcd.set:
- name: /path/to/key

View File

@ -2364,10 +2364,8 @@ def managed(name,
elif ret['pchanges']:
ret['result'] = None
ret['comment'] = u'The file {0} is set to be changed'.format(name)
if show_changes and 'diff' in ret['pchanges']:
ret['changes']['diff'] = ret['pchanges']['diff']
if not show_changes:
ret['changes']['diff'] = '<show_changes=False>'
if 'diff' in ret['pchanges'] and not show_changes:
ret['pchanges']['diff'] = '<show_changes=False>'
else:
ret['result'] = True
ret['comment'] = u'The file {0} is in the correct state'.format(name)
@ -3674,20 +3672,20 @@ def line(name, content=None, match=None, mode=None, location=None,
.. versionadded:: 2015.8.0
name
:param name:
Filesystem path to the file to be edited.
content
:param content:
Content of the line. Allowed to be empty if mode=delete.
match
:param match:
Match the target line for an action by
a fragment of a string or regular expression.
If neither ``before`` nor ``after`` are provided, and ``match``
is also ``None``, match becomes the ``content`` value.
mode
:param mode:
Defines how to edit a line. One of the following options is
required:
@ -3707,7 +3705,7 @@ def line(name, content=None, match=None, mode=None, location=None,
``after``. If ``location`` is used, it takes precedence
over the other two options.
location
:param location:
Defines where to place content in the line. Note this option is only
used when ``mode=insert`` is specified. If a location is passed in, it
takes precedence over both the ``before`` and ``after`` kwargs. Valid
@ -3718,17 +3716,17 @@ def line(name, content=None, match=None, mode=None, location=None,
- end
Place the content at the end of the file.
before
:param before:
Regular expression or an exact case-sensitive fragment of the string.
This option is only used when either the ``ensure`` or ``insert`` mode
is defined.
after
:param after:
Regular expression or an exact case-sensitive fragment of the string.
This option is only used when either the ``ensure`` or ``insert`` mode
is defined.
show_changes
:param show_changes:
Output a unified diff of the old file and the new file.
If ``False`` return a boolean if any changes were made.
Default is ``True``
@ -3737,15 +3735,15 @@ def line(name, content=None, match=None, mode=None, location=None,
Using this option will store two copies of the file in-memory
(the original version and the edited version) in order to generate the diff.
backup
:param backup:
Create a backup of the original file with the extension:
"Year-Month-Day-Hour-Minutes-Seconds".
quiet
:param quiet:
Do not raise any exceptions. E.g. ignore the fact that the file that is
tried to be edited does not exist and nothing really happened.
indent
:param indent:
Keep indentation with the previous line. This option is not considered when
the ``delete`` mode is specified.

View File

@ -432,7 +432,7 @@ def namespace_present(name, **kwargs):
Ensures that the named namespace is present.
name
The name of the deployment.
The name of the namespace.
'''
ret = {'name': name,
@ -449,6 +449,7 @@ def namespace_present(name, **kwargs):
return ret
res = __salt__['kubernetes.create_namespace'](name, **kwargs)
ret['result'] = True
ret['changes']['namespace'] = {
'old': {},
'new': res}
@ -504,8 +505,8 @@ def secret_present(
name,
namespace='default',
data=None,
source='',
template='',
source=None,
template=None,
**kwargs):
'''
Ensures that the named secret is present inside of the specified namespace
@ -562,6 +563,7 @@ def secret_present(
else:
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'The secret is going to be replaced'
return ret
# TODO: improve checks # pylint: disable=fixme
@ -594,7 +596,8 @@ def configmap_absent(name, namespace='default', **kwargs):
The name of the configmap
namespace
The name of the namespace
The namespace holding the configmap. The 'default' one is going to be
used unless a different one is specified.
'''
ret = {'name': name,
@ -631,8 +634,8 @@ def configmap_present(
name,
namespace='default',
data=None,
source='',
template='',
source=None,
template=None,
**kwargs):
'''
Ensures that the named configmap is present inside of the specified namespace
@ -665,6 +668,8 @@ def configmap_present(
ret,
'\'source\' cannot be used in combination with \'data\''
)
elif data is None:
data = {}
configmap = __salt__['kubernetes.show_configmap'](name, namespace, **kwargs)
@ -686,6 +691,7 @@ def configmap_present(
else:
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'The configmap is going to be replaced'
return ret
# TODO: improve checks # pylint: disable=fixme
@ -975,6 +981,7 @@ def node_label_present(
else:
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'The label is going to be updated'
return ret
ret['comment'] = 'The label is already set, changing the value'

View File

@ -34,6 +34,7 @@ Example:
Using States to deploy a load balancer with extended arguments to specify region
.. code-block:: yaml
lb_test:
libcloud_loadbalancer.balancer_present:
- name: example

View File

@ -91,6 +91,7 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk
The command and version that we want to check against, the version number can use regex.
.. code-block:: yaml
version_check: python --version_check=2.7.[0-9]
'''

View File

@ -2,6 +2,7 @@
'''
Send a message card to Microsoft Teams
=======================
This state is useful for sending messages to Teams during state runs.
.. versionadded:: 2017.7.0
.. code-block:: yaml

View File

@ -121,14 +121,15 @@ def present(name,
if flag in mtdata:
toupgrade = True
mode = 'upgrade'
if __opts__['test']:
ret['result'] = None
if mode:
ret['comment'] = 'Extension {0} is set to be {1}ed'.format(
name, mode).replace('eed', 'ed')
return ret
cret = None
if toinstall or toupgrade:
if __opts__['test']:
ret['result'] = None
if mode:
ret['comment'] = 'Extension {0} is set to be {1}ed'.format(
name, mode).replace('eed', 'ed')
return ret
cret = __salt__['postgres.create_extension'](
name=name,
if_not_exists=if_not_exists,

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
r'''
===========================
Manage the Windows registry
===========================
Many python developers think of registry keys as if they were python keys in a
dictionary which is not the case. The windows registry is broken down into the
following components:

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
'''
State to work with sysrc
'''
# Import Python libs
from __future__ import absolute_import

View File

@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
'''
.. versionadded:: 2016.3.0.
Manage Telemetry alert configurations
=====================================
.. versionadded:: 2016.3.0
Create, Update and destroy Mongo Telemetry alert configurations.
This module uses requests, which can be installed via package, or pip.

View File

@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
r'''
.. index:: Vagrant state function
Manage Vagrant VMs
==================
.. index:: Vagrant state function
Manange execution of Vagrant virtual machines on Salt minions.
Vagrant_ is a tool for building and managing virtual machine environments.

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
'''
States for managing Hashicorp Vault.
Currently handles policies. Configuration instructions are documented in the execution module docs.
:maintainer: SaltStack
:maturity: new
:platform: all
.. versionadded:: 2017.7.0
States for managing Hashicorp Vault. Currently handles policies. Configuration
instructions are documented in the execution module docs.
'''
from __future__ import absolute_import
import logging

View File

@ -65,7 +65,8 @@ def exists(name, index=None):
'''
Add the directory to the system PATH at index location
index: where the directory should be placed in the PATH (default: None)
index: where the directory should be placed in the PATH (default: None).
This is 0-indexed, so 0 means to prepend at the very start of the PATH.
[Note: Providing no index will append directory to PATH and
will not enforce its location within the PATH.]
@ -96,7 +97,7 @@ def exists(name, index=None):
try:
currIndex = sysPath.index(path)
if index:
if index is not None:
index = int(index)
if index < 0:
index = len(sysPath) + index + 1
@ -115,7 +116,7 @@ def exists(name, index=None):
except ValueError:
pass
if not index:
if index is None:
index = len(sysPath) # put it at the end
ret['changes']['added'] = '{0} will be added at index {1}'.format(name, index)
if __opts__['test']:

189
salt/states/wordpress.py Normal file
View File

@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
'''
This state module is used to manage Wordpress installations
:depends: wp binary from http://wp-cli.org/
'''
def __virtual__():
return 'wordpress.show_plugin' in __salt__
def installed(name, user, admin_user, admin_password, admin_email, title, url):
'''
Run the initial setup of wordpress
name
path to the wordpress installation
user
user that owns the files for the wordpress installation
admin_user
username for wordpress website administrator user
admin_password
password for wordpress website administrator user
admin_email
email for wordpress website administrator user
title
title for the wordpress website
url
url for the wordpress website
.. code-block:: yaml
/var/www/html:
wordpress.installed:
- title: Daniel's Awesome Blog
- user: apache
- admin_user: dwallace
- admin_email: dwallace@example.com
- admin_password: password123
- url: https://blog.dwallace.com
'''
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.is_installed'](name, user)
if check:
ret['result'] = True
ret['comment'] = 'Wordpress is already installed: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Wordpress will be installed: {0}'.format(name)
return ret
resp = __salt__['wordpress.install'](name, user, admin_user, admin_password, admin_email, title, url)
if resp:
ret['result'] = True
ret['comment'] = 'Wordpress Installed: {0}'.format(name)
ret['changes'] = {
'new': resp
}
else:
ret['comment'] = 'Failed to install wordpress: {0}'.format(name)
return ret
def activated(name, path, user):
'''
Activate wordpress plugins
name
name of plugin to activate
path
path to wordpress installation
user
user who should own the files in the wordpress installation
.. code-block:: yaml
HyperDB:
wordpress.activated:
- path: /var/www/html
- user: apache
'''
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.show_plugin'](name, path, user)
if check['status'] == 'active':
ret['result'] = True
ret['comment'] = 'Plugin already activated: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Plugin will be activated: {0}'.format(name)
return ret
resp = __salt__['wordpress.activate'](name, path, user)
if resp is True:
ret['result'] = True
ret['comment'] = 'Plugin activated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
elif resp is None:
ret['result'] = True
ret['comment'] = 'Plugin already activated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
else:
ret['comment'] = 'Plugin failed to activate: {0}'.format(name)
return ret
def deactivated(name, path, user):
'''
Deactivate wordpress plugins
name
name of plugin to deactivate
path
path to wordpress installation
user
user who should own the files in the wordpress installation
.. code-block:: yaml
HyperDB:
wordpress.deactivated:
- path: /var/www/html
- user: apache
'''
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.show_plugin'](name, path, user)
if check['status'] == 'inactive':
ret['result'] = True
ret['comment'] = 'Plugin already deactivated: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Plugin will be deactivated: {0}'.format(name)
return ret
resp = __salt__['wordpress.deactivate'](name, path, user)
if resp is True:
ret['result'] = True
ret['comment'] = 'Plugin deactivated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
elif resp is None:
ret['result'] = True
ret['comment'] = 'Plugin already deactivated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
else:
ret['comment'] = 'Plugin failed to deactivate: {0}'.format(name)
return ret

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
'''
Control concurrency of steps within state execution using zookeeper
===================================================================
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
Control concurrency of steps within state execution using zookeeper
===================================================================
This module allows you to "wrap" a state's execution with concurrency control.
This is useful to protect against all hosts executing highstate simultaneously

View File

@ -177,7 +177,7 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel):
@tornado.gen.coroutine
def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60):
if not self.auth.authenticated:
# Return controle back to the caller, continue when authentication succeeds
# Return control back to the caller, continue when authentication succeeds
yield self.auth.authenticate()
# Return control to the caller. When send() completes, resume by populating ret with the Future.result
ret = yield self.message_client.send(
@ -569,7 +569,7 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.
@tornado.gen.coroutine
def handle_message(self, stream, payload):
'''
Handle incoming messages from underylying TCP streams
Handle incoming messages from underlying TCP streams
:stream ZMQStream stream: A ZeroMQ stream.
See http://zeromq.github.io/pyzmq/api/generated/zmq.eventloop.zmqstream.html
@ -876,7 +876,7 @@ class AsyncReqMessageClientPool(salt.transport.MessageClientPool):
# TODO: unit tests!
class AsyncReqMessageClient(object):
'''
This class wraps the underylying zeromq REQ socket and gives a future-based
This class wraps the underlying zeromq REQ socket and gives a future-based
interface to sending and recieving messages. This works around the primary
limitation of serialized send/recv on the underlying socket by queueing the
message sends in this class. In the future if we decide to attempt to multiplex

View File

@ -420,7 +420,7 @@ class SaltEvent(object):
self.pulluri,
io_loop=self.io_loop
)
# For the async case, the connect will be defered to when
# For the async case, the connect will be deferred to when
# fire_event() is invoked.
self.cpush = True
return self.cpush
@ -576,16 +576,17 @@ class SaltEvent(object):
auto_reconnect=False):
'''
Get a single publication.
IF no publication available THEN block for up to wait seconds
AND either return publication OR None IF no publication available.
If no publication is available, then block for up to ``wait`` seconds.
Return publication if it is available or ``None`` if no publication is
available.
IF wait is 0 then block forever.
If wait is 0, then block forever.
tag
Only return events matching the given tag. If not specified, or set
to an empty string, all events are returned. It is recommended to
always be selective on what is to be returned in the event that
multiple requests are being multiplexed
multiple requests are being multiplexed.
match_type
Set the function to match the search tag with event tags.
@ -646,7 +647,8 @@ class SaltEvent(object):
return ret['data']
def get_event_noblock(self):
'''Get the raw event without blocking or any other niceties
'''
Get the raw event without blocking or any other niceties
'''
assert self._run_io_loop_sync
@ -660,8 +662,9 @@ class SaltEvent(object):
return {'data': data, 'tag': mtag}
def get_event_block(self):
'''Get the raw event in a blocking fashion
Slower, but decreases the possibility of dropped events
'''
Get the raw event in a blocking fashion. This is slower, but it decreases the
possibility of dropped events.
'''
assert self._run_io_loop_sync

View File

@ -0,0 +1,85 @@
driver:
name: docker
use_sudo: false
privileged: true
<% if File.exists?('driver.yml') %>
<% File.read('driver.yml').split(/\n/).each do |line| %>
<%= line %>
<% end %>
<% end %>
provisioner:
name: salt_solo
salt_install: pip
pip_pkg: <%= ENV['SALT_SDIST_PATH'] || 'salt' %>
pip_index_url: <%= ENV['SALT_INDEX_URL'] || 'https://pypi.python.org/simple' %>
require_chef: false
formula: states
<% if File.exists?('provisioner.yml') %>
<% File.read('provisioner.yml').split(/\n/).each do |line| %>
<%= line %>
<% end %>
<% end %>
<% if File.exists?('state_top.yml') %>
<% File.read('state_top.yml').split(/\n/).each do |line| %>
<%= line %>
<% end %>
<% else %>
state_top:
base:
'*':
- states
<% end %>
<% if File.exists?('pillars.yml') %>
<% File.read('pillars.yml').split(/\n/).each do |line| %>
<%= line %>
<% end %>
<% end %>
<% if File.exists?('platforms.yml') %>
<%= File.read('platforms.yml') %>
<% else %>
platforms:
- name: centos
driver_config:
run_command: /usr/lib/systemd/systemd
provision_command:
- yum install -y epel-release
- yum install -y python-pip python-devel gcc git gcc-c++
- name: opensuse
driver_config:
run_command: /usr/lib/systemd/systemd
provision_command:
- systemctl enable sshd.service
- zypper install -y python-pip python-devel gcc git gcc-c++
- name: ubuntu
driver_config:
run_command: /lib/systemd/systemd
provision_command:
- DEBIAN_FRONTEND=noninteractive apt-get install -y python-pip python-dev gcc git locales console-data
- name: debian
driver_config:
run_command: /lib/systemd/systemd
provision_command:
- DEBIAN_FRONTEND=noninteractive apt-get install -y python-pip python-dev gcc git locales console-data
<% end %>
<% if File.exists?('suites.yml') %>
<%= File.read('suites.yml') %>
<% else %>
suites:
- name: salt
<% end %>
<% if File.exists?('verifier.yml') %>
<%= File.read('verifier.yml') %>
<% else %>
verifier:
name: shell
remote_exec: false
<% if ENV['TESTS_JUNIT_XML_PATH'].nil? %>
command: pytest -v tests/$KITCHEN_SUITE
<% else %>
command: pytest --junit-xml <%= ENV['TESTS_JUNIT_XML_PATH'] %> -v tests/$KITCHEN_SUITE
<% end %>
<% end %>

9
tests/kitchen/Gemfile Normal file
View File

@ -0,0 +1,9 @@
source "https://rubygems.org"
gem 'test-kitchen'
gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git'
gem 'kitchen-docker', :git => 'https://github.com/test-kitchen/kitchen-docker.git'
gem 'vagrant-wrapper'
gem 'kitchen-vagrant'
gem 'winrm', '~>2.0'
gem 'winrm-fs', '~>1.0'

View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

0
tests/kitchen/pytest.ini Normal file
View File

View File

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
'''
Test wrapper for running all KitchenSalt tests
All directories in 'tests/kitchen/' will be treated as a separate test under
the KitchenTestCase.
'''
from __future__ import absolute_import
import os
from tests.support.unit import TestCase, skipIf
import setup
import salt.utils.path
from salt.modules import cmdmod as cmd
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
@skipIf(not salt.utils.path.which('bundle'), 'Bundler is not installed')
class KitchenTestCase(TestCase):
'''
Test kitchen environments
'''
@classmethod
def setUpClass(cls):
'''
setup kitchen tests
'''
cls.topdir = '/' + os.path.join(*CURRENT_DIR.split('/')[:-2])
cls.use_vt = int(os.environ.get('TESTS_LOG_LEVEL')) >= 5
cmd.run('python setup.py sdist', cwd=cls.topdir)
cmd.run('bundle install', cwd=CURRENT_DIR)
cls.env = {
'KITCHEN_YAML': os.path.join(CURRENT_DIR, '.kitchen.yml'),
'SALT_SDIST_PATH': os.path.join(cls.topdir, 'dist', 'salt-{0}.tar.gz'.format(setup.__version__)),
}
@classmethod
def tearDownClass(cls):
del cls.topdir
del cls.env
def tearDown(self):
cmd.run(
'bundle exec kitchen destroy all',
cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir),
env=self.env,
use_vt=self.use_vt,
)
del self.testdir
def func_builder(testdir):
def func(self):
self.testdir = testdir
if 'TESTS_XML_OUTPUT_DIR' in os.environ:
self.env['TESTS_JUNIT_XML_PATH'] = '{0}/kitchen.tests.{1}.$KITCHEN_SUITE.$KITCHEN_PLATFORM.xml'.format(
os.environ.get('TESTS_XML_OUTPUT_DIR'),
self.testdir,
)
self.assertEqual(
cmd.retcode(
'bundle exec kitchen converge -c 999 all',
cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir),
env=self.env,
use_vt=self.use_vt,
),
0
)
self.assertEqual(
cmd.retcode(
'bundle exec kitchen verify all',
cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir),
env=self.env,
use_vt=self.use_vt,
),
0
)
return func
for testdir in os.listdir(os.path.join(CURRENT_DIR, 'tests')):
setattr(KitchenTestCase, 'test_kitchen_{0}'.format(testdir), func_builder(testdir))

View File

@ -0,0 +1,2 @@
forward:
- 80

Some files were not shown because too many files have changed in this diff Show More