Merge remote branch 'upstream/develop' into develop

This commit is contained in:
Forrest Alvarez 2013-10-23 04:48:17 +00:00
commit e164562c90
28 changed files with 527 additions and 153 deletions

View File

@ -90,6 +90,12 @@
# Set the directory used to hold unix sockets
#sock_dir: /var/run/salt/master
# The master can take a while to start up when lspci and/or dmidecode is used
# to populate the grains for the master. Enable if you want to see GPU hardware
# data for your master.
#
# enable_gpu_grains: False
# The master maintains a job cache, while this is a great addition it can be
# a burden on the master for larger deployments (over 5000 minions).
# Disabling the job cache will make previously executed jobs unavailable to

View File

@ -182,6 +182,18 @@
# often lower this value
#loop_interval: 60
# The grains_refresh_every setting allows for a minion to periodically check
# its grains to see if they have changed and, if so, to inform the master
# of the new grains. This operation is moderately expensive, therefore
# care should be taken not to set this value too low.
#
# Note: This value is expressed in __minutes__!
#
# A value of 10 minutes is a reasonable default.
#
# If the value is set to zero, this check is disabled.
#grains_refresh_every = 1
# When healing, a dns_check is run. This is to make sure that the originally
# resolved dns has not changed. If this is something that does not happen in
# your environment, set this value to False.

View File

@ -70395,7 +70395,7 @@ settings for software packages that support that option.
.sp
In order to facilitate managing a Salt Windows software repo with Salt on a
Standalone Minion on Windows, a new module named winrepo has been added to
Salt. wirepo matches what is available in the salt runner and allows you to
Salt. winrepo matches what is available in the salt runner and allows you to
manage the Windows software repo contents. Example: \fBsalt \(aq*\(aq
winrepo.genrepo\fP
.SS Git Hosted Repo

View File

@ -20,8 +20,8 @@ Environments
.. note::
Environments in Salt are very flexible, this section defines how the top
file can be used to define what ststates from what environments are to be
used fro specific minions.
file can be used to define what states from what environments are to be
used for specific minions.
If the intent is to bind minions to specific environments, then the
`environment` option can be set in the minion configuration file.

View File

@ -237,7 +237,7 @@ Standalone Minion Salt Windows Repo Module
In order to facilitate managing a Salt Windows software repo with Salt on a
Standalone Minion on Windows, a new module named winrepo has been added to
Salt. wirepo matches what is available in the salt runner and allows you to
Salt. winrepo matches what is available in the salt runner and allows you to
manage the Windows software repo contents. Example: ``salt '*'
winrepo.genrepo``

View File

@ -33,7 +33,7 @@ Be advised that these security issues all apply to a small subset of Salt
users and mostly apply to Salt SSH.
Insufficient Argument Validation
-------------------------------
--------------------------------
This issue allowed for a user with limited privileges to embed executions
inside of routines to execute routines that should be restricted. This applies

View File

@ -9,13 +9,13 @@
%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
%{!?pythonpath: %global pythonpath %(%{__python} -c "import os, sys; print(os.pathsep.join(sys.path))")}
%{!?pythonpath: %global pythonpath %(%{__python} -c "import os, sys; print(os.pathsep.join(x for x in sys.path if x))")}
%define _salttesting SaltTesting
%define _salttesting_ver 0.5.1
Name: salt
Version: 0.17.0
Version: 0.17.1
Release: 1%{?dist}
Summary: A parallel remote execution system
@ -50,6 +50,7 @@ BuildRequires: python26-devel
BuildRequires: python26-jinja2
BuildRequires: python26-m2crypto
BuildRequires: python26-msgpack
BuildRequires: python26-pip
BuildRequires: python26-zmq
BuildRequires: python26-PyYAML
@ -318,7 +319,10 @@ rm -rf $RPM_BUILD_ROOT
%endif
%changelog
* Mon Sep 30 2013 Erik Johnson <erik@saltstack.com> - 0.17.0-1
* Thu Oct 17 2013 Erik Johnson <erik@saltstack.com> - 0.17.1-1
- Update to bugfix release 0.17.1
* Thu Sep 26 2013 Erik Johnson <erik@saltstack.com> - 0.17.0-1
- Update to feature release 0.17.0
* Wed Sep 11 2013 David Anderson <dave@dubkat.com>

View File

@ -12,6 +12,7 @@
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include "nsProcess.nsh"; using plugin nsProcess
Var Dialog
Var Label
@ -126,11 +127,15 @@ ShowUnInstDetails show
Section "MainSection" SEC01
Exec "sc stop salt-minion" ;stopping service before upgrading
${nsProcess::CloseProcess} "salt-minion.exe" $R0
${nsProcess::Unload}
SetOutPath "$INSTDIR\"
SetOverwrite try
CreateDirectory $INSTDIR\conf\pki\minion
File /r "..\buildenv\"
Exec 'icacls c:\salt /inheritance:r /grant:r "BUILTIN\Administrators":(OI)(CI)F /grant:r "NT AUTHORITY\SYSTEM":(OI)(CI)F'
SectionEnd
@ -149,6 +154,7 @@ SectionEnd
Function .onInstSuccess
Exec "nssm.exe install salt-minion $INSTDIR\salt-minion.exe -c $INSTDIR\conf -l quiet"
RMDir /R "$INSTDIR\var\cache\salt" ; removing cache from old version
Exec "sc start salt-minion"
FunctionEnd

View File

@ -341,7 +341,7 @@ class Resolver(object):
def mk_token(self, load):
'''
Request a token fromt he master
Request a token from the master
'''
load['cmd'] = 'mk_token'
sreq = salt.payload.SREQ(
@ -354,7 +354,7 @@ class Resolver(object):
def get_token(self, token):
'''
Request a token fromt he master
Request a token from the master
'''
load = {}
load['token'] = token

View File

@ -8,6 +8,7 @@ import tarfile
import tempfile
import json
import shutil
from contextlib import closing
# Import salt libs
import salt.client.ssh.shell
@ -142,7 +143,7 @@ def prep_trans_tar(opts, chunks, file_refs):
break
cwd = os.getcwd()
os.chdir(gendir)
with tarfile.open(trans_tar, 'w:gz') as tfp:
with closing(tarfile.open(trans_tar, 'w:gz')) as tfp:
for root, dirs, files in os.walk(gendir):
for name in files:
full = os.path.join(root, name)

View File

@ -165,6 +165,8 @@ VALID_OPTS = {
'win_repo_mastercachefile': str,
'win_gitrepos': list,
'modules_max_memory': int,
'grains_refresh_every': int,
'enable_lspci': bool,
}
# default configurations
@ -253,6 +255,7 @@ DEFAULT_MINION_OPTS = {
'tcp_keepalive_cnt': -1,
'tcp_keepalive_intvl': -1,
'modules_max_memory': -1,
'grains_refresh_every': 0,
}
DEFAULT_MASTER_OPTS = {
@ -345,6 +348,7 @@ DEFAULT_MASTER_OPTS = {
'loop_interval': 60,
'nodegroups': {},
'cython_enable': False,
'enable_gpu_grains': False,
# XXX: Remove 'key_logfile' support in 0.18.0
'key_logfile': os.path.join(syspaths.LOGS_DIR, 'key'),
'verify_env': True,

View File

@ -357,10 +357,12 @@ class Client(object):
if url_data.username is not None \
and url_data.scheme in ('http', 'https'):
_, netloc = url_data.netloc.split('@', 1)
fixed_url = urlunparse((url_data.scheme, netloc, url_data.path,
url_data.params, url_data.query, url_data.fragment))
fixed_url = urlunparse(
(url_data.scheme, netloc, url_data.path,
url_data.params, url_data.query, url_data.fragment))
passwd_mgr = url_passwd_mgr()
passwd_mgr.add_password(None, fixed_url, url_data.username, url_data.password)
passwd_mgr.add_password(
None, fixed_url, url_data.username, url_data.password)
auth_handler = url_auth_handler(passwd_mgr)
opener = url_build_opener(auth_handler)
url_install_opener(opener)
@ -482,7 +484,9 @@ class LocalClient(Client):
return ret
prefix = prefix.strip('/')
for path in self.opts['file_roots'][env]:
for root, dirs, files in os.walk(os.path.join(path, prefix), followlinks=True):
for root, dirs, files in os.walk(
os.path.join(path, prefix), followlinks=True
):
for fname in files:
ret.append(
os.path.relpath(
@ -502,7 +506,9 @@ class LocalClient(Client):
if env not in self.opts['file_roots']:
return ret
for path in self.opts['file_roots'][env]:
for root, dirs, files in os.walk(os.path.join(path, prefix), followlinks=True):
for root, dirs, files in os.walk(
os.path.join(path, prefix), followlinks=True
):
if len(dirs) == 0 and len(files) == 0:
ret.append(os.path.relpath(root, path))
return ret
@ -517,7 +523,9 @@ class LocalClient(Client):
return ret
prefix = prefix.strip('/')
for path in self.opts['file_roots'][env]:
for root, dirs, files in os.walk(os.path.join(path, prefix), followlinks=True):
for root, dirs, files in os.walk(
os.path.join(path, prefix), followlinks=True
):
ret.append(os.path.relpath(root, path))
return ret
@ -605,6 +613,26 @@ class RemoteClient(Client):
self.auth = salt.crypt.SAuth(opts)
self.sreq = salt.payload.SREQ(self.opts['master_uri'])
def _crypted_transfer(self, load, tries=3, timeout=60, payload='aes'):
'''
In case of authentication errors, try to renogiate authentication
and retry the method.
Indeed, we can fail too early in case of a master restart during a
minion state executon call
'''
def _do_transfer():
return self.auth.crypticle.loads(
self.sreq.send(payload,
self.auth.crypticle.dumps(load),
tries,
timeout)
)
try:
return _do_transfer()
except salt.crypt.AuthenticationError:
self.auth = salt.crypt.SAuth(self.opts)
return _do_transfer()
def get_file(self, path, dest='', makedirs=False, env='base', gzip=None):
'''
Get a single file from the salt-master
@ -612,7 +640,8 @@ class RemoteClient(Client):
dest is omitted, then the downloaded file will be placed in the minion
cache
'''
#-- Hash compare local copy with master and skip download if no diference found.
#-- Hash compare local copy with master and skip download
# if no diference found.
dest2check = dest
if not dest2check:
rel_path = self._check_proto(path)
@ -623,7 +652,9 @@ class RemoteClient(Client):
hash_local = self.hash_file(dest2check, env)
hash_server = self.hash_file(path, env)
if hash_local == hash_server:
log.info('Fetching file ** skipped **, latest already in cache \'{0}\''.format(path))
log.info(
'Fetching file ** skipped **, '
'latest already in cache \'{0}\''.format(path))
return dest2check
log.debug('Fetching file ** attempting ** \'{0}\''.format(path))
@ -651,12 +682,7 @@ class RemoteClient(Client):
else:
load['loc'] = fn_.tell()
try:
data = self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
data = self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -707,12 +733,7 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_file_list'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -724,12 +745,7 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_file_list_emptydirs'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -741,12 +757,7 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_dir_list'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -758,12 +769,7 @@ class RemoteClient(Client):
'prefix': prefix,
'cmd': '_symlink_list'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -782,19 +788,15 @@ class RemoteClient(Client):
return {}
else:
ret = {}
ret['hsum'] = salt.utils.get_hash(path, form='md5', chunk_size=4096)
ret['hsum'] = salt.utils.get_hash(
path, form='md5', chunk_size=4096)
ret['hash_type'] = 'md5'
return ret
load = {'path': path,
'env': env,
'cmd': '_file_hash'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -805,12 +807,7 @@ class RemoteClient(Client):
load = {'env': env,
'cmd': '_file_list'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -820,12 +817,7 @@ class RemoteClient(Client):
'''
load = {'cmd': '_master_opts'}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''
@ -839,11 +831,6 @@ class RemoteClient(Client):
'opts': self.opts,
'tok': self.auth.gen_token('salt')}
try:
return self.auth.crypticle.loads(
self.sreq.send('aes',
self.auth.crypticle.dumps(load),
3,
60)
)
return self._crypted_transfer(load)
except SaltReqTimeoutError:
return ''

View File

@ -143,6 +143,13 @@ def _linux_gpu_data():
)
return {}
elif not __opts__.get('enable_gpu_grains', None):
log.info(
'Skipping lspci call because enable_gpu_grains was set to False in the config. '
'GPU grains will not be available.'
)
return {}
# dominant gpu vendors to search for (MUST be lowercase for matching below)
known_vendors = ['nvidia', 'amd', 'ati', 'intel']

View File

@ -501,6 +501,7 @@ class Minion(object):
self.opts,
self.functions,
self.returners)
self.grains_cache = self.opts['grains']
def __prep_mod_opts(self):
'''
@ -911,6 +912,24 @@ class Minion(object):
data['arg'] = []
self._handle_decoded_payload(data)
def _refresh_grains_watcher(self, refresh_interval_in_minutes):
'''
Create a loop that will fire a pillar refresh to inform a master about a change in the grains of this minion
:param refresh_interval_in_minutes:
:return: None
'''
if '__update_grains' not in self.opts.get('schedule', {}):
if not 'schedule' in self.opts:
self.opts['schedule'] = {}
self.opts['schedule'].update({
'__update_grains':
{
'function': 'event.fire',
'args': [{}, "grains_refresh"],
'minutes': refresh_interval_in_minutes
}
})
@property
def master_pub(self):
'''
@ -982,6 +1001,7 @@ class Minion(object):
def tune_in(self):
'''
Lock onto the publisher. This is the main event loop for the minion
:rtype : None
'''
try:
log.info(
@ -1134,6 +1154,29 @@ class Minion(object):
time.sleep(.5)
loop_interval = int(self.opts['loop_interval'])
try:
if self.opts['grains_refresh_every']: # If exists and is not zero. In minutes, not seconds!
if self.opts['grains_refresh_every'] > 1:
log.debug(
'Enabling the grains refresher. Will run every {0} minutes.'.format(
self.opts['grains_refresh_every'])
)
else: # Clean up minute vs. minutes in log message
log.debug(
'Enabling the grains refresher. Will run every {0} minute.'.format(
self.opts['grains_refresh_every'])
)
self._refresh_grains_watcher(
abs(self.opts['grains_refresh_every'])
)
except Exception as exc:
log.error(
'Exception occurred in attempt to initialize grain refresh routine during minion tune-in: {0}'.format(
exc)
)
while True:
try:
self.schedule.eval()
@ -1164,6 +1207,10 @@ class Minion(object):
self.module_refresh()
elif package.startswith('pillar_refresh'):
self.pillar_refresh()
elif package.startswith('grains_refresh'):
if self.grains_cache != self.opts['grains']:
self.pillar_refresh()
self.grains_cache = self.opts['grains']
self.epub_sock.send(package)
except Exception:
pass

View File

@ -384,11 +384,10 @@ def install(name=None,
if pkg_params is None or len(pkg_params) == 0:
return {}
elif pkg_type == 'file':
cmd = 'dpkg -i {confold} {verify} {pkg}'.format(
confold='--force-confold',
verify='--force-bad-verify' if skip_verify else '',
pkg=' '.join(pkg_params),
)
cmd = ['dpkg', '-i', '--force-confold']
if skip_verify:
cmd.append('--force-bad-verify')
cmd.extend(pkg_params)
elif pkg_type == 'repository':
if pkgs is None and kwargs.get('version') and len(pkg_params) == 1:
# Only use the 'version' param if 'name' was not specified as a

View File

@ -240,7 +240,7 @@ def cache_file(path, env='base'):
_mk_client()
if path.startswith('salt://|'):
# Strip pipe. Windows doesn't allow pipes in filenames
path = 'salt://{0}'.format(path.lstrip('salt://|'))
path = 'salt://{0}'.format(path[8:])
result = __context__['cp.fileclient'].cache_file(path, env)
if not result:
log.error(

View File

@ -1,8 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Management of dockers: overview of this module
==============================================
Management of dockers
=====================
.. versionadded:: Hydrogen
.. note::

View File

@ -404,6 +404,12 @@ def list_all():
'''
Lists all ports available.
CLI Example:
.. code-block:: bash
salt '*' ports.list_all
.. warning::
Takes a while to run, and returns a **LOT** of output

View File

@ -3,6 +3,10 @@
Module to provide MySQL compatibility to salt.
:depends: - MySQLdb Python module
.. note::
On CentOS 5 (and possibly RHEL 5) both MySQL-python and python26-mysqldb need to be installed.
:configuration: In order to connect to MySQL, certain configuration is required
in /etc/salt/minion on the relevant minions. Some sample configs might look
like::
@ -34,6 +38,9 @@ import sys
# Import salt libs
import salt.utils
#import shlex which should be distributed with Python
import shlex
# Import third party libs
try:
import MySQLdb
@ -148,6 +155,69 @@ def _connect(**kwargs):
return dbc
def _grant_to_tokens(grant):
'''
This should correspond fairly closely to the YAML rendering of a mysql_grants state which comes out
as follows:
OrderedDict([('whatever_identifier', OrderedDict([('mysql_grants.present',
[OrderedDict([('database', 'testdb.*')]), OrderedDict([('user', 'testuser')]),
OrderedDict([('grant', 'ALTER, SELECT, LOCK TABLES')]), OrderedDict([('host', 'localhost')])])]))])
:param grant: An un-parsed MySQL GRANT statement str, like
"GRANT SELECT, ALTER, LOCK TABLES ON `testdb`.* TO 'testuser'@'localhost'"
:return:
A Python dict with the following keys/values:
- user: MySQL User
- host: MySQL host
- grant: [grant1, grant2] (ala SELECT, USAGE, etc)
- database: MySQL DB
'''
exploded_grant = shlex.split(grant)
grant_tokens = []
multiword_statement = []
position_tracker = 1 # Skip the initial 'GRANT' word token
phrase = 'grants'
for token in exploded_grant[position_tracker:]:
if token == 'ON':
phrase = 'db'
continue
elif token == 'TO':
phrase = 'user'
continue
if phrase == 'grants':
if token.endswith(',') or exploded_grant[position_tracker+1] == 'ON': # Read-ahead
cleaned_token = token.rstrip(',')
if multiword_statement:
multiword_statement.append(cleaned_token)
grant_tokens.append(' '.join(multiword_statement))
multiword_statement = []
else:
grant_tokens.append(cleaned_token)
elif token[-1:] != ',': # This is a multi-word, ala LOCK TABLES
multiword_statement.append(token)
elif phrase == 'db':
database = token.strip('`')
phrase = 'tables'
elif phrase == 'user':
user, host = token.split('@')
position_tracker += 1
return dict(user=user,
host=host,
grant=grant_tokens,
database=database)
def query(database, query, **connection_args):
'''
Run an arbitrary SQL query and return the results or
@ -898,6 +968,15 @@ def user_remove(user,
return False
def tokenize_grant(grant):
'''
External wrapper function
:param grant:
:return: dict
'''
return _grant_to_tokens(grant)
# Maintenance
def db_check(name,
table=None,
@ -1072,17 +1151,30 @@ def grant_exists(grant,
salt '*' mysql.grant_exists 'SELECT,INSERT,UPDATE,...' 'database.*' 'frank' 'localhost'
'''
# TODO: This function is a bit tricky, since it requires the ordering to
# be exactly the same. Perhaps should be replaced/reworked with a
# better/cleaner solution.
target = __grant_generate(
grant, database, user, host, grant_option, escape
)
grants = user_grants(user, host, **connection_args)
if grants is not False and target in grants:
log.debug('Grant exists.')
return True
for grant in grants:
try:
target_tokens = None
if not target_tokens: # Avoid the overhead of re-calc in loop
target_tokens = _grant_to_tokens(target)
grant_tokens = _grant_to_tokens(grant)
if grant_tokens['user'] == target_tokens['user'] and \
grant_tokens['database'] == target_tokens['database'] and \
grant_tokens['host'] == target_tokens['host'] and \
set(grant_tokens['grant']) == set(target_tokens['grant']):
log.debug(grant_tokens)
log.debug(target_tokens)
return True
except Exception as exc: # Fallback to strict parsing
if grants is not False and target in grants:
log.debug('Grant exists.')
return True
log.debug('Grant does not exist, or is perhaps not ordered properly?')
return False

View File

@ -11,6 +11,7 @@ import shutil
import time
import logging
import tarfile
import datetime
import tempfile
# Import salt libs
@ -66,6 +67,8 @@ def _wait(jid):
'''
Wait for all previously started state jobs to finish running
'''
if jid is None:
jid = '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now())
states = _prior_running_states(jid)
while states:
time.sleep(1)
@ -126,7 +129,7 @@ def low(data, queue=False, **kwargs):
salt '*' state.low '{"state": "pkg", "fun": "installed", "name": "vi"}'
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -157,7 +160,7 @@ def high(data, queue=False, **kwargs):
salt '*' state.high '{"vim": {"pkg": ["installed"]}}'
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -180,7 +183,7 @@ def template(tem, queue=False, **kwargs):
salt '*' state.template '<Path to template on the minion>'
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -203,7 +206,7 @@ def template_str(tem, queue=False, **kwargs):
salt '*' state.template_str '<Template String>'
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -229,7 +232,7 @@ def highstate(test=None, queue=False, **kwargs):
salt '*' state.highstate exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]"
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -296,7 +299,7 @@ def sls(mods, env='base', test=None, exclude=None, queue=False, **kwargs):
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -388,7 +391,7 @@ def top(topfn, test=None, queue=False, **kwargs):
salt '*' state.top reverse_top.sls exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]"
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -429,7 +432,7 @@ def show_highstate(queue=False, **kwargs):
salt '*' state.show_highstate
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -457,7 +460,7 @@ def show_lowstate(queue=False, **kwargs):
salt '*' state.show_lowstate
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -483,7 +486,7 @@ def show_low_sls(mods, env='base', test=None, queue=False, **kwargs):
salt '*' state.show_low_sls foo
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -521,7 +524,7 @@ def show_sls(mods, env='base', test=None, queue=False, **kwargs):
salt '*' state.show_sls core,edit.vim dev
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -558,7 +561,7 @@ def show_top(queue=False, **kwargs):
salt '*' state.show_top
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:
@ -593,7 +596,7 @@ def single(fun, name, test=None, queue=False, **kwargs):
'''
if queue:
_wait(kwargs['__pub_jid'])
_wait(kwargs.get('__pub_jid'))
else:
conflict = running()
if conflict:

View File

@ -39,7 +39,8 @@ def display_output(data, out, opts=None):
ofh.write(display_data)
ofh.write('\n')
return
print(display_data)
if display_data:
print(display_data)
except IOError as exc:
# Only raise if it's NOT a broken pipe
if exc.errno != errno.EPIPE:

View File

@ -130,7 +130,7 @@ def ext_pillar(pillar,
log.error('Virtualenv {} not a directory!'.format(path))
return {}
# load the virtualenv
sys.path.append(virtualenv.path_locations(env)[1] + '/site-packages/')
sys.path[0:0] = (virtualenv.path_locations(env)[1] + '/site-packages/',)
# load the django project
sys.path.append(project_path)

View File

@ -2214,7 +2214,7 @@ class BaseHighState(object):
statefiles = fnmatch.filter(self.avail[env], sls_match)
if not statefiles:
# No matching sls file was found! Output an error
log.error(
all_errors.append(
'No matching sls found for \'{0}\' in env \'{1}\''
.format(sls_match, env)
)

View File

@ -69,6 +69,7 @@ def present(name,
host='localhost',
grant_option=False,
escape=True,
revoke_first=False,
**connection_args):
'''
Ensure that the grant is present with the specified properties
@ -93,6 +94,22 @@ def present(name,
escape
Defines if the database value gets escaped or not. default: True
revoke_first
By default, MySQL will not do anything if you issue a command to grant
privileges that are more restrictive than what's already in place. This
effectively means that you cannot downgrade permissions without first
revoking permissions applied to a db.table/user pair first.
To have Salt forcibly revoke perms before applying a new grant, enable
the 'revoke_first options.
WARNING: This will *remove* permissions for a database before attempting to apply
new permissions. There is no guarantee that new permissions will be applied correctly
which can leave your database security in an unknown and potentially dangerous state.
Use with caution!
default: False
'''
comment = 'Grant {0} on {1} to {2}@{3} is already present'
ret = {'name': name,
@ -111,6 +128,19 @@ def present(name,
ret['comment'] = err
ret['result'] = False
return ret
if revoke_first:
# for each grant, break into tokens and see if its on the same user/db as ours. (there is probably only one)
for user_grant in __salt__['mysql.user_grants'](user, host, **connection_args):
if __salt__['mysql.tokenize_grant'](user_grant)['database'].replace('`', '')\
== database.replace('`', ''):
grant_to_revoke = ','.join(__salt__['mysql.tokenize_grant'](user_grant)['grant']).rstrip(',')
__salt__['mysql.grant_revoke'](grant_to_revoke,
database,
user,
host=host,
grant_option=grant_option,
escape=escape,
connection_args=connection_args) # Probably needs some ordering love
# The grant is not present, make it!
if __opts__['test']:

View File

@ -8,7 +8,7 @@ controlled via the ssh_auth state. Defaults can be set by the enc, options,
and comment keys. These defaults can be overridden by including them in the
name.
Since the YAML specification limits the length of simple keys to 1024
Since the YAML specification limits the length of simple keys to 1024
characters, and since SSH keys are often longer than that, you may have
to use a YAML 'explicit key', as demonstrated in the second example below.

View File

@ -220,7 +220,7 @@ def daemonize(redirect_out=True):
# not cleanly redirected and the parent process dies when the
# multiprocessing process attempts to access stdout or err.
if redirect_out:
dev_null = open('/dev/null', 'rw')
dev_null = open('/dev/null', 'w')
os.dup2(dev_null.fileno(), sys.stdin.fileno())
os.dup2(dev_null.fileno(), sys.stdout.fileno())
os.dup2(dev_null.fileno(), sys.stderr.fileno())

View File

@ -1038,10 +1038,10 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
'minions to have running')
)
self.add_option(
'-a', '--auth', '--eauth', '--extended-auth',
'-a', '--auth', '--eauth', '--external-auth',
default='',
dest='eauth',
help=('Specify an extended authentication system to use.')
help=('Specify an external authentication system to use.')
)
self.add_option(
'-T', '--make-token',
@ -1080,10 +1080,16 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
)
def _mixin_after_parsed(self):
# Catch invalid invocations of salt such as: salt run
if len(self.args) <= 1 and not self.options.doc:
self.print_help()
self.exit(1)
try:
self.print_help()
except Exception:
# We get an argument that Python's optparser just can't deal
# with. Perhaps stdout was redirected, or a file glob was
# passed in. Regardless, we're in an unknown state here.
sys.stdout.write('Invalid options passed. Please try -h for '
'help.') # Try to warn if we can.
sys.exit(1)
if self.options.doc:
# Include the target
@ -1102,41 +1108,44 @@ class SaltCMDOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
else:
self.config['tgt'] = self.args[0].split()
else:
self.config['tgt'] = self.args[0]
try:
self.config['tgt'] = self.args[0]
except IndexError:
self.exit(42, '\nCannot execute command without defining a target.\n\n')
# Detect compound command and set up the data for it
if ',' in self.args[1]:
self.config['fun'] = self.args[1].split(',')
self.config['arg'] = [[]]
cmd_index = 0
if (self.args[2:].count(self.options.args_separator) ==
len(self.config['fun']) - 1):
# new style parsing: standalone argument separator
for arg in self.args[2:]:
if arg == self.options.args_separator:
cmd_index += 1
self.config['arg'].append([])
else:
self.config['arg'][cmd_index].append(arg)
if self.args:
if ',' in self.args[1]:
self.config['fun'] = self.args[1].split(',')
self.config['arg'] = [[]]
cmd_index = 0
if (self.args[2:].count(self.options.args_separator) ==
len(self.config['fun']) - 1):
# new style parsing: standalone argument separator
for arg in self.args[2:]:
if arg == self.options.args_separator:
cmd_index += 1
self.config['arg'].append([])
else:
self.config['arg'][cmd_index].append(arg)
else:
# old style parsing: argument separator can be inside args
for arg in self.args[2:]:
if self.options.args_separator in arg:
sub_args = arg.split(self.options.args_separator)
for sub_arg_index, sub_arg in enumerate(sub_args):
if sub_arg:
self.config['arg'][cmd_index].append(sub_arg)
if sub_arg_index != len(sub_args) - 1:
cmd_index += 1
self.config['arg'].append([])
else:
self.config['arg'][cmd_index].append(arg)
if len(self.config['fun']) != len(self.config['arg']):
self.exit(42, 'Cannot execute compound command without '
'defining all arguments.')
else:
# old style parsing: argument separator can be inside args
for arg in self.args[2:]:
if self.options.args_separator in arg:
sub_args = arg.split(self.options.args_separator)
for sub_arg_index, sub_arg in enumerate(sub_args):
if sub_arg:
self.config['arg'][cmd_index].append(sub_arg)
if sub_arg_index != len(sub_args) - 1:
cmd_index += 1
self.config['arg'].append([])
else:
self.config['arg'][cmd_index].append(arg)
if len(self.config['fun']) != len(self.config['arg']):
self.exit(42, 'Cannot execute compound command without '
'defining all arguments.')
else:
self.config['fun'] = self.args[1]
self.config['arg'] = self.args[2:]
self.config['fun'] = self.args[1]
self.config['arg'] = self.args[2:]
def setup_config(self):
return config.client_config(self.get_config_file_path())

View File

@ -12,21 +12,28 @@ from salttesting.helpers import (
)
ensure_in_syspath('../../')
# Import python libs
import os
import time
# Import salt libs
import integration
import salt.utils
_PKG_TARGETS = {
'Arch': ['bzr', 'finch'],
'Arch': ['python2-django', 'finch'],
'Debian': ['python-plist', 'finch'],
'RedHat': ['bzr', 'finch'],
'RedHat': ['xz-devel', 'zsh-html'],
'FreeBSD': ['aalib', 'pth'],
'Suse': ['aalib', 'finch']
}
_PKG_TARGETS_32 = {
'CentOS': 'xz-devel.i686'
}
@requires_salt_modules('pkg.latest_version')
@requires_salt_modules('pkg.version')
@requires_salt_modules('pkg.version', 'pkg.latest_version')
class PkgTest(integration.ModuleCase,
integration.SaltReturnAssertsMixIn):
'''
@ -35,7 +42,7 @@ class PkgTest(integration.ModuleCase,
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_installed(self, grains=None):
def test_pkg_001_installed(self, grains=None):
'''
This is a destructive test as it installs and then removes a package
'''
@ -55,15 +62,57 @@ class PkgTest(integration.ModuleCase,
# needs to not be installed before we run the states below
self.assertFalse(version)
ret = self.run_state('pkg.installed', name=pkg_targets[0])
ret = self.run_state('pkg.installed', name=target)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=pkg_targets[0])
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_installed_multipkg(self, grains=None):
def test_pkg_002_installed_with_version(self, grains=None):
'''
This is a destructive test as it installs and then removes a package
'''
os_family = grains.get('os_family', '')
pkg_targets = _PKG_TARGETS.get(os_family, [])
# Don't perform this test on FreeBSD since version specification is not
# supported.
if os_family == 'FreeBSD':
return
# Make sure that we have targets that match the os_family. If this
# fails then the _PKG_TARGETS dict above needs to have an entry added,
# with two packages that are not installed before these tests are run
self.assertTrue(pkg_targets)
if os_family == 'Arch':
for idx in xrange(13):
if idx == 12:
raise Exception('Package database locked after 60 seconds, '
'bailing out')
if not os.path.isfile('/var/lib/pacman/db.lck'):
break
time.sleep(5)
target = pkg_targets[0]
version = self.run_function('pkg.latest_version', [target])
# If this assert fails, we need to find new targets, this test needs to
# be able to test successful installation of packages, so this package
# needs to not be installed before we run the states below
self.assertTrue(version)
ret = self.run_state('pkg.installed', name=target, version=version)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_003_installed_multipkg(self, grains=None):
'''
This is a destructive test as it installs and then removes two packages
'''
@ -87,6 +136,116 @@ class PkgTest(integration.ModuleCase,
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_004_installed_multipkg_with_version(self, grains=None):
'''
This is a destructive test as it installs and then removes two packages
'''
os_family = grains.get('os_family', '')
pkg_targets = _PKG_TARGETS.get(os_family, [])
# Don't perform this test on FreeBSD since version specification is not
# supported.
if os_family == 'FreeBSD':
return
# Make sure that we have targets that match the os_family. If this
# fails then the _PKG_TARGETS dict above needs to have an entry added,
# with two packages that are not installed before these tests are run
self.assertTrue(pkg_targets)
if os_family == 'Arch':
for idx in xrange(13):
if idx == 12:
raise Exception('Package database locked after 60 seconds, '
'bailing out')
if not os.path.isfile('/var/lib/pacman/db.lck'):
break
time.sleep(5)
version = self.run_function('pkg.latest_version', [pkg_targets[0]])
# If this assert fails, we need to find new targets, this test needs to
# be able to test successful installation of packages, so these
# packages need to not be installed before we run the states below
self.assertTrue(version)
pkgs = [{pkg_targets[0]: version}, pkg_targets[1]]
ret = self.run_state('pkg.installed', name=None, pkgs=pkgs)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_005_installed_32bit(self, grains=None):
'''
This is a destructive test as it installs and then removes a package
'''
os_name = grains.get('os', '')
target = _PKG_TARGETS_32.get(os_name, '')
# _PKG_TARGETS_32 is only populated for platforms for which Salt has to
# munge package names for 32-bit-on-x86_64 (Currently only Ubuntu and
# RHEL-based). Don't actually perform this test on other platforms.
if target:
# CentOS 5 has .i386 arch designation for 32-bit pkgs
if os_name == 'CentOS' \
and grains['osrelease'].startswith('5.'):
target = target.replace('.i686', '.i386')
version = self.run_function('pkg.version', [target])
# If this assert fails, we need to find a new target. This test
# needs to be able to test successful installation of packages, so
# the target needs to not be installed before we run the states
# below
self.assertFalse(version)
ret = self.run_state('pkg.installed', name=target)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
@destructiveTest
@skipIf(salt.utils.is_windows(), 'minion is windows')
@requires_system_grains
def test_pkg_006_installed_32bit_with_version(self, grains=None):
'''
This is a destructive test as it installs and then removes a package
'''
os_name = grains.get('os', '')
target = _PKG_TARGETS_32.get(os_name, '')
# _PKG_TARGETS_32 is only populated for platforms for which Salt has to
# munge package names for 32-bit-on-x86_64 (Currently only Ubuntu and
# RHEL-based). Don't actually perform this test on other platforms.
if target:
if grains.get('os_family', '') == 'Arch':
self._wait_for_pkgdb_unlock()
# CentOS 5 has .i386 arch designation for 32-bit pkgs
if os_name == 'CentOS' \
and grains['osrelease'].startswith('5.'):
target = target.replace('.i686', '.i386')
version = self.run_function('pkg.latest_version', [target])
# If this assert fails, we need to find a new target. This test
# needs to be able to test successful installation of the package, so
# the target needs to not be installed before we run the states
# below
self.assertTrue(version)
ret = self.run_state('pkg.installed', name=target, version=version)
self.assertSaltTrueReturn(ret)
ret = self.run_state('pkg.removed', name=target)
self.assertSaltTrueReturn(ret)
if __name__ == '__main__':
from integration import run_tests