mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into 42713_backport_2017.7
This commit is contained in:
commit
6806d83314
5 changed files with 280 additions and 4 deletions
|
@ -6,6 +6,7 @@ Create ssh executor system
|
|||
from __future__ import absolute_import
|
||||
# Import python libs
|
||||
import os
|
||||
import time
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
|
@ -21,6 +22,8 @@ import salt.loader
|
|||
import salt.minion
|
||||
import salt.log
|
||||
from salt.ext.six import string_types
|
||||
import salt.ext.six as six
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
__func_alias__ = {
|
||||
'apply_': 'apply'
|
||||
|
@ -28,6 +31,47 @@ __func_alias__ = {
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _set_retcode(ret, highstate=None):
|
||||
'''
|
||||
Set the return code based on the data back from the state system
|
||||
'''
|
||||
|
||||
# Set default retcode to 0
|
||||
__context__['retcode'] = 0
|
||||
|
||||
if isinstance(ret, list):
|
||||
__context__['retcode'] = 1
|
||||
return
|
||||
if not salt.utils.check_state_result(ret, highstate=highstate):
|
||||
|
||||
__context__['retcode'] = 2
|
||||
|
||||
|
||||
def _check_pillar(kwargs, pillar=None):
|
||||
'''
|
||||
Check the pillar for errors, refuse to run the state if there are errors
|
||||
in the pillar and return the pillar errors
|
||||
'''
|
||||
if kwargs.get('force'):
|
||||
return True
|
||||
pillar_dict = pillar if pillar is not None else __pillar__
|
||||
if '_errors' in pillar_dict:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _wait(jid):
|
||||
'''
|
||||
Wait for all previously started state jobs to finish running
|
||||
'''
|
||||
if jid is None:
|
||||
jid = salt.utils.jid.gen_jid()
|
||||
states = _prior_running_states(jid)
|
||||
while states:
|
||||
time.sleep(1)
|
||||
states = _prior_running_states(jid)
|
||||
|
||||
|
||||
def _merge_extra_filerefs(*args):
|
||||
'''
|
||||
Takes a list of filerefs and returns a merged list
|
||||
|
@ -127,6 +171,100 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs):
|
|||
return stdout
|
||||
|
||||
|
||||
def running(concurrent=False):
|
||||
'''
|
||||
Return a list of strings that contain state return data if a state function
|
||||
is already running. This function is used to prevent multiple state calls
|
||||
from being run at the same time.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.running
|
||||
'''
|
||||
ret = []
|
||||
if concurrent:
|
||||
return ret
|
||||
active = __salt__['saltutil.is_running']('state.*')
|
||||
for data in active:
|
||||
err = (
|
||||
'The function "{0}" is running as PID {1} and was started at '
|
||||
'{2} with jid {3}'
|
||||
).format(
|
||||
data['fun'],
|
||||
data['pid'],
|
||||
salt.utils.jid.jid_to_time(data['jid']),
|
||||
data['jid'],
|
||||
)
|
||||
ret.append(err)
|
||||
return ret
|
||||
|
||||
|
||||
def _prior_running_states(jid):
|
||||
'''
|
||||
Return a list of dicts of prior calls to state functions. This function is
|
||||
used to queue state calls so only one is run at a time.
|
||||
'''
|
||||
|
||||
ret = []
|
||||
active = __salt__['saltutil.is_running']('state.*')
|
||||
for data in active:
|
||||
try:
|
||||
data_jid = int(data['jid'])
|
||||
except ValueError:
|
||||
continue
|
||||
if data_jid < int(jid):
|
||||
ret.append(data)
|
||||
return ret
|
||||
|
||||
|
||||
def _check_queue(queue, kwargs):
|
||||
'''
|
||||
Utility function to queue the state run if requested
|
||||
and to check for conflicts in currently running states
|
||||
'''
|
||||
if queue:
|
||||
_wait(kwargs.get('__pub_jid'))
|
||||
else:
|
||||
conflict = running(concurrent=kwargs.get('concurrent', False))
|
||||
if conflict:
|
||||
__context__['retcode'] = 1
|
||||
return conflict
|
||||
|
||||
|
||||
def _get_opts(**kwargs):
|
||||
'''
|
||||
Return a copy of the opts for use, optionally load a local config on top
|
||||
'''
|
||||
opts = copy.deepcopy(__opts__)
|
||||
|
||||
if 'localconfig' in kwargs:
|
||||
return salt.config.minion_config(kwargs['localconfig'], defaults=opts)
|
||||
|
||||
if 'saltenv' in kwargs:
|
||||
saltenv = kwargs['saltenv']
|
||||
if saltenv is not None and not isinstance(saltenv, six.string_types):
|
||||
opts['environment'] = str(kwargs['saltenv'])
|
||||
else:
|
||||
opts['environment'] = kwargs['saltenv']
|
||||
|
||||
if 'pillarenv' in kwargs:
|
||||
pillarenv = kwargs['pillarenv']
|
||||
if pillarenv is not None and not isinstance(pillarenv, six.string_types):
|
||||
opts['pillarenv'] = str(kwargs['pillarenv'])
|
||||
else:
|
||||
opts['pillarenv'] = kwargs['pillarenv']
|
||||
|
||||
return opts
|
||||
|
||||
|
||||
def _get_initial_pillar(opts):
|
||||
return __pillar__ if __opts__['__cli'] == 'salt-call' \
|
||||
and opts['pillarenv'] == __opts__['pillarenv'] \
|
||||
else None
|
||||
|
||||
|
||||
def low(data, **kwargs):
|
||||
'''
|
||||
Execute a single low data call
|
||||
|
@ -199,6 +337,21 @@ def low(data, **kwargs):
|
|||
return stdout
|
||||
|
||||
|
||||
def _get_test_value(test=None, **kwargs):
|
||||
'''
|
||||
Determine the correct value for the test flag.
|
||||
'''
|
||||
ret = True
|
||||
if test is None:
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
ret = True
|
||||
else:
|
||||
ret = __opts__.get('test', None)
|
||||
else:
|
||||
ret = test
|
||||
return ret
|
||||
|
||||
|
||||
def high(data, **kwargs):
|
||||
'''
|
||||
Execute the compound calls stored in a single set of high data
|
||||
|
@ -615,6 +768,99 @@ def show_lowstate():
|
|||
return st_.compile_low_chunks()
|
||||
|
||||
|
||||
def sls_id(id_, mods, test=None, queue=False, **kwargs):
|
||||
'''
|
||||
Call a single ID from the named module(s) and handle all requisites
|
||||
|
||||
The state ID comes *before* the module ID(s) on the command line.
|
||||
|
||||
id
|
||||
ID to call
|
||||
|
||||
mods
|
||||
Comma-delimited list of modules to search for given id and its requisites
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
saltenv : base
|
||||
Specify a salt fileserver environment to be used when applying states
|
||||
|
||||
pillarenv
|
||||
Specify a Pillar environment to be used when applying states. This
|
||||
can also be set in the minion config file using the
|
||||
:conf_minion:`pillarenv` option. When neither the
|
||||
:conf_minion:`pillarenv` minion config option nor this CLI argument is
|
||||
used, all Pillar environments will be merged together.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.sls_id my_state my_module
|
||||
|
||||
salt '*' state.sls_id my_state my_module,a_common_module
|
||||
'''
|
||||
conflict = _check_queue(queue, kwargs)
|
||||
if conflict is not None:
|
||||
return conflict
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(**kwargs)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
# Since this is running a specific ID within a specific SLS file, fall back
|
||||
# to the 'base' saltenv if none is configured and none was passed.
|
||||
if opts['environment'] is None:
|
||||
opts['environment'] = 'base'
|
||||
|
||||
try:
|
||||
st_ = salt.state.HighState(opts,
|
||||
proxy=__proxy__,
|
||||
initial_pillar=_get_initial_pillar(opts))
|
||||
except NameError:
|
||||
st_ = salt.state.HighState(opts,
|
||||
initial_pillar=_get_initial_pillar(opts))
|
||||
|
||||
if not _check_pillar(kwargs, st_.opts['pillar']):
|
||||
__context__['retcode'] = 5
|
||||
err = ['Pillar failed to render with the following messages:']
|
||||
err += __pillar__['_errors']
|
||||
return err
|
||||
|
||||
if isinstance(mods, six.string_types):
|
||||
split_mods = mods.split(',')
|
||||
st_.push_active()
|
||||
try:
|
||||
high_, errors = st_.render_highstate({opts['environment']: split_mods})
|
||||
finally:
|
||||
st_.pop_active()
|
||||
errors += st_.state.verify_high(high_)
|
||||
# Apply requisites to high data
|
||||
high_, req_in_errors = st_.state.requisite_in(high_)
|
||||
if req_in_errors:
|
||||
# This if statement should not be necessary if there were no errors,
|
||||
# but it is required to get the unit tests to pass.
|
||||
errors.extend(req_in_errors)
|
||||
if errors:
|
||||
__context__['retcode'] = 1
|
||||
return errors
|
||||
chunks = st_.state.compile_high_data(high_)
|
||||
ret = {}
|
||||
for chunk in chunks:
|
||||
if chunk.get('__id__', '') == id_:
|
||||
ret.update(st_.state.call_chunk(chunk, {}, chunks))
|
||||
|
||||
_set_retcode(ret, highstate=highstate)
|
||||
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
||||
# value from before this function was run.
|
||||
__opts__['test'] = orig_test
|
||||
if not ret:
|
||||
raise SaltInvocationError(
|
||||
'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv '
|
||||
'\'{2}\''.format(id_, mods, opts['environment'])
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
def show_sls(mods, saltenv='base', test=None, **kwargs):
|
||||
'''
|
||||
Display the state data from a specific sls or list of sls files on the
|
||||
|
|
|
@ -805,6 +805,12 @@ class TestDaemon(object):
|
|||
os.path.join(FILES, 'pillar', 'base'),
|
||||
]
|
||||
}
|
||||
minion_opts['pillar_roots'] = {
|
||||
'base': [
|
||||
RUNTIME_VARS.TMP_PILLAR_TREE,
|
||||
os.path.join(FILES, 'pillar', 'base'),
|
||||
]
|
||||
}
|
||||
master_opts['file_roots'] = syndic_master_opts['file_roots'] = {
|
||||
'base': [
|
||||
os.path.join(FILES, 'file', 'base'),
|
||||
|
|
|
@ -441,6 +441,17 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
|
|||
log.debug('salt-call output:\n\n%s', '\n'.join(ret))
|
||||
self.fail('CLI pillar override not found in pillar data')
|
||||
|
||||
def test_pillar_items_masterless(self):
|
||||
'''
|
||||
Test to ensure we get expected output
|
||||
from pillar.items with salt-call
|
||||
'''
|
||||
get_items = self.run_call('pillar.items', local=True)
|
||||
exp_out = [' - Lancelot', ' - Galahad', ' - Bedevere',
|
||||
' monty:', ' python']
|
||||
for out in exp_out:
|
||||
self.assertIn(out, get_items)
|
||||
|
||||
def tearDown(self):
|
||||
'''
|
||||
Teardown method to remove installed packages
|
||||
|
|
|
@ -42,6 +42,16 @@ class SSHStateTest(SSHCase):
|
|||
check_file = self.run_function('file.file_exists', ['/tmp/test'])
|
||||
self.assertTrue(check_file)
|
||||
|
||||
def test_state_sls_id(self):
|
||||
'''
|
||||
test state.sls_id with salt-ssh
|
||||
'''
|
||||
ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS])
|
||||
self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS)
|
||||
|
||||
check_file = self.run_function('file.file_exists', ['/tmp/test'])
|
||||
self.assertTrue(check_file)
|
||||
|
||||
def test_state_show_sls(self):
|
||||
'''
|
||||
test state.show_sls with salt-ssh
|
||||
|
|
|
@ -210,8 +210,10 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
|||
arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str)
|
||||
return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr)
|
||||
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False):
|
||||
arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str)
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False, local=False):
|
||||
arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '',
|
||||
self.get_config_dir(), arg_str)
|
||||
|
||||
return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr)
|
||||
|
||||
def run_cloud(self, arg_str, catch_stderr=False, timeout=None):
|
||||
|
@ -549,11 +551,12 @@ class ShellCase(ShellTestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixi
|
|||
catch_stderr=catch_stderr,
|
||||
timeout=60)
|
||||
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False):
|
||||
def run_call(self, arg_str, with_retcode=False, catch_stderr=False, local=False):
|
||||
'''
|
||||
Execute salt-call.
|
||||
'''
|
||||
arg_str = '--config-dir {0} {1}'.format(self.get_config_dir(), arg_str)
|
||||
arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '',
|
||||
self.get_config_dir(), arg_str)
|
||||
return self.run_script('salt-call',
|
||||
arg_str,
|
||||
with_retcode=with_retcode,
|
||||
|
|
Loading…
Add table
Reference in a new issue