mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Merge pull request #32560 from rallytime/merge-develop
[develop] Merge forward from 2016.3 to develop
This commit is contained in:
commit
04efcf52b6
@ -11,6 +11,22 @@ Salt 2015.8.7 Release Notes
|
||||
Changes for v2015.8.4..v2015.8.7
|
||||
--------------------------------
|
||||
|
||||
For :py:mod:`pkg.installed <salt.states.pkg.installed>` states, on Linux
|
||||
distributions which use yum/dnf, packages which have a non-zero epoch in the
|
||||
version number now require this epoch to be included when specifying an exact
|
||||
version for a package. For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
The :py:mod:`pkg.latest_version <salt.modules.yumpkg.latest_version>` and
|
||||
:py:mod:`pkg.list_repo_pkgs <salt.modules.yumpkg.list_repo_pkgs>` functions can
|
||||
be used to get the correct version string to use, as they will now contain the
|
||||
epoch when it is non-zero.
|
||||
|
||||
Extended changelog courtesy of Todd Stansell (https://github.com/tjstansell/salt-changelogs):
|
||||
|
||||
*Generated at: 2016-02-11T22:13:51Z*
|
||||
|
@ -25,7 +25,8 @@ except ImportError:
|
||||
HAS_LDAP = False
|
||||
|
||||
# Defaults, override in master config
|
||||
__defopts__ = {'auth.ldap.uri': '',
|
||||
__defopts__ = {'auth.ldap.basedn': '',
|
||||
'auth.ldap.uri': '',
|
||||
'auth.ldap.server': 'localhost',
|
||||
'auth.ldap.port': '389',
|
||||
'auth.ldap.tls': False,
|
||||
@ -45,16 +46,10 @@ def _config(key, mandatory=True, opts=None):
|
||||
'''
|
||||
Return a value for 'name' from master config file options or defaults.
|
||||
'''
|
||||
try:
|
||||
if opts:
|
||||
try:
|
||||
return opts['auth.ldap.{0}'.format(key)]
|
||||
except KeyError:
|
||||
if mandatory:
|
||||
msg = 'missing auth.ldap.{0} in master config'.format(key)
|
||||
raise SaltInvocationError(msg)
|
||||
return False
|
||||
value = opts['auth.ldap.{0}'.format(key)]
|
||||
else:
|
||||
try:
|
||||
value = __opts__['auth.ldap.{0}'.format(key)]
|
||||
except KeyError:
|
||||
try:
|
||||
|
@ -18,6 +18,7 @@ import stat
|
||||
import traceback
|
||||
import binascii
|
||||
import weakref
|
||||
import getpass
|
||||
|
||||
# Import third party libs
|
||||
import salt.ext.six as six
|
||||
@ -102,6 +103,11 @@ def gen_keys(keydir, keyname, keysize, user=None):
|
||||
# Between first checking and the generation another process has made
|
||||
# a key! Use the winner's key
|
||||
return priv
|
||||
|
||||
# Do not try writing anything, if directory has no permissions.
|
||||
if not os.access(keydir, os.W_OK):
|
||||
raise IOError('Write access denied to "{0}" for user "{1}".'.format(os.path.abspath(keydir), getpass.getuser()))
|
||||
|
||||
cumask = os.umask(191)
|
||||
with salt.utils.fopen(priv, 'wb+') as f:
|
||||
f.write(gen.exportKey('PEM'))
|
||||
|
@ -15,6 +15,14 @@ framer jobcleaner be active first setup
|
||||
frame fsclean
|
||||
enter
|
||||
do salt raet maint fileserver clean
|
||||
go clearfslocks
|
||||
frame clearfslocks
|
||||
enter
|
||||
do salt raet maint fileserver clear locks
|
||||
go cleargitpillarlocks
|
||||
frame cleargitpillarlocks
|
||||
enter
|
||||
do salt raet maint git pillar clear locks
|
||||
go start
|
||||
frame start
|
||||
do salt raet maint old jobs clear
|
||||
|
@ -111,6 +111,40 @@ class SaltRaetMaintFileserverClean(ioflo.base.deeding.Deed):
|
||||
salt.daemons.masterapi.clean_fsbackend(self.opts.value)
|
||||
|
||||
|
||||
class SaltRaetMaintFileserverClearLocks(ioflo.base.deeding.Deed):
|
||||
'''
|
||||
Clear the fileserver backend caches
|
||||
FloScript:
|
||||
|
||||
do salt raet maint fileserver clear locks at enter
|
||||
|
||||
'''
|
||||
Ioinits = {'opts': '.salt.opts'}
|
||||
|
||||
def action(self):
|
||||
'''
|
||||
Clean!
|
||||
'''
|
||||
salt.daemons.masterapi.clear_fsbackend_locks(self.opts.value)
|
||||
|
||||
|
||||
class SaltRaetMaintGitPillarClearLocks(ioflo.base.deeding.Deed):
|
||||
'''
|
||||
Clear the fileserver backend caches
|
||||
FloScript:
|
||||
|
||||
do salt raet maint git pillar clear locks at enter
|
||||
|
||||
'''
|
||||
Ioinits = {'opts': '.salt.opts'}
|
||||
|
||||
def action(self):
|
||||
'''
|
||||
Clean!
|
||||
'''
|
||||
salt.daemons.masterapi.clear_git_pillar_locks(self.opts.value)
|
||||
|
||||
|
||||
class SaltRaetMaintOldJobsClear(ioflo.base.deeding.Deed):
|
||||
'''
|
||||
Iterate over the jobs directory and clean out the old jobs
|
||||
|
@ -30,6 +30,7 @@ import salt.key
|
||||
import salt.fileserver
|
||||
import salt.utils.atomicfile
|
||||
import salt.utils.event
|
||||
import salt.utils.gitfs
|
||||
import salt.utils.verify
|
||||
import salt.utils.minions
|
||||
import salt.utils.gzip_util
|
||||
@ -137,6 +138,36 @@ def clean_fsbackend(opts):
|
||||
)
|
||||
|
||||
|
||||
def clear_fsbackend_locks(opts):
|
||||
'''
|
||||
Clear any locks from configured backends
|
||||
'''
|
||||
for back_name in ('git', 'hg', 'svn'):
|
||||
if back_name in opts['fileserver_backend']:
|
||||
full_name = back_name + 'fs'
|
||||
backend = getattr(salt.fileserver, full_name, None)
|
||||
if backend is None:
|
||||
log.warning('Unable to access %s backend', full_name)
|
||||
continue
|
||||
backend.__opts__ = opts
|
||||
backend.clear_lock()
|
||||
|
||||
|
||||
def clear_git_pillar_locks(opts):
|
||||
'''
|
||||
Clear any update/checkout locks present in git_pillar remotes
|
||||
'''
|
||||
for ext_pillar in opts.get('ext_pillar', []):
|
||||
pillar_type = next(iter(ext_pillar))
|
||||
if pillar_type == 'git' and isinstance(ext_pillar[pillar_type], list):
|
||||
pillar = salt.utils.gitfs.GitPillar(opts)
|
||||
pillar.init_remotes(ext_pillar[pillar_type],
|
||||
git_pillar.PER_REMOTE_OVERRIDES)
|
||||
for lock_type in ('update', 'checkout'):
|
||||
for remote in pillar.remotes:
|
||||
remote.clear_lock(lock_type=lock_type)
|
||||
|
||||
|
||||
def clean_expired_tokens(opts):
|
||||
'''
|
||||
Clean expired tokens from the master
|
||||
|
@ -17,7 +17,7 @@ from salt.utils.process import SignalHandlingMultiprocessingProcess
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start_engines(opts, proc_mgr):
|
||||
def start_engines(opts, proc_mgr, proxy=None):
|
||||
'''
|
||||
Fire up the configured engines!
|
||||
'''
|
||||
@ -27,7 +27,7 @@ def start_engines(opts, proc_mgr):
|
||||
runners = []
|
||||
utils = salt.loader.utils(opts)
|
||||
funcs = salt.loader.minion_mods(opts, utils=utils)
|
||||
engines = salt.loader.engines(opts, funcs, runners)
|
||||
engines = salt.loader.engines(opts, funcs, runners, proxy=proxy)
|
||||
|
||||
engines_opt = opts.get('engines', [])
|
||||
if isinstance(engines_opt, dict):
|
||||
@ -58,7 +58,8 @@ def start_engines(opts, proc_mgr):
|
||||
fun,
|
||||
engine_opts,
|
||||
funcs,
|
||||
runners
|
||||
runners,
|
||||
proxy
|
||||
),
|
||||
name=name
|
||||
)
|
||||
@ -68,7 +69,7 @@ class Engine(SignalHandlingMultiprocessingProcess):
|
||||
'''
|
||||
Execute the given engine in a new process
|
||||
'''
|
||||
def __init__(self, opts, fun, config, funcs, runners, log_queue=None):
|
||||
def __init__(self, opts, fun, config, funcs, runners, proxy, log_queue=None):
|
||||
'''
|
||||
Set up the process executor
|
||||
'''
|
||||
@ -78,6 +79,7 @@ class Engine(SignalHandlingMultiprocessingProcess):
|
||||
self.fun = fun
|
||||
self.funcs = funcs
|
||||
self.runners = runners
|
||||
self.proxy = proxy
|
||||
|
||||
# __setstate__ and __getstate__ are only used on Windows.
|
||||
# We do this so that __init__ will be invoked on Windows in the child
|
||||
@ -90,6 +92,7 @@ class Engine(SignalHandlingMultiprocessingProcess):
|
||||
state['config'],
|
||||
state['funcs'],
|
||||
state['runners'],
|
||||
state['proxy'],
|
||||
log_queue=state['log_queue']
|
||||
)
|
||||
|
||||
@ -99,6 +102,7 @@ class Engine(SignalHandlingMultiprocessingProcess):
|
||||
'config': self.config,
|
||||
'funcs': self.funcs,
|
||||
'runners': self.runners,
|
||||
'proxy': self.proxy,
|
||||
'log_queue': self.log_queue}
|
||||
|
||||
def run(self):
|
||||
@ -116,7 +120,8 @@ class Engine(SignalHandlingMultiprocessingProcess):
|
||||
|
||||
self.engine = salt.loader.engines(self.opts,
|
||||
self.funcs,
|
||||
self.runners)
|
||||
self.runners,
|
||||
proxy=self.proxy)
|
||||
kwargs = self.config or {}
|
||||
try:
|
||||
self.engine[self.fun](**kwargs)
|
||||
|
@ -61,6 +61,7 @@ if salt.utils.is_windows():
|
||||
try:
|
||||
import wmi # pylint: disable=import-error
|
||||
import salt.utils.winapi
|
||||
import win32api
|
||||
HAS_WMI = True
|
||||
except ImportError:
|
||||
log.exception(
|
||||
@ -409,11 +410,8 @@ def _memdata(osdata):
|
||||
if comps[0].strip() == 'Memory' and comps[1].strip() == 'size:':
|
||||
grains['mem_total'] = int(comps[2].strip())
|
||||
elif osdata['kernel'] == 'Windows' and HAS_WMI:
|
||||
with salt.utils.winapi.Com():
|
||||
wmi_c = wmi.WMI()
|
||||
# this is a list of each stick of ram in a system
|
||||
# WMI returns it as the string value of the number of bytes
|
||||
tot_bytes = sum([int(x.Capacity) for x in wmi_c.Win32_PhysicalMemory()], 0)
|
||||
# get the Total Physical memory as reported by msinfo32
|
||||
tot_bytes = win32api.GlobalMemoryStatusEx()['TotalPhys']
|
||||
# return memory info in gigabytes
|
||||
grains['mem_total'] = int(tot_bytes / (1024 ** 2))
|
||||
return grains
|
||||
|
@ -272,12 +272,13 @@ def raw_mod(opts, name, functions, mod='modules'):
|
||||
return dict(loader._dict) # return a copy of *just* the funcs for `name`
|
||||
|
||||
|
||||
def engines(opts, functions, runners):
|
||||
def engines(opts, functions, runners, proxy=None):
|
||||
'''
|
||||
Return the master services plugins
|
||||
'''
|
||||
pack = {'__salt__': functions,
|
||||
'__runners__': runners}
|
||||
'__runners__': runners,
|
||||
'__proxy__': proxy}
|
||||
return LazyLoader(
|
||||
_module_dirs(opts, 'engines', 'engines'),
|
||||
opts,
|
||||
|
@ -225,6 +225,10 @@ class Maintenance(SignalHandlingMultiprocessingProcess):
|
||||
last = int(time.time())
|
||||
# Clean out the fileserver backend cache
|
||||
salt.daemons.masterapi.clean_fsbackend(self.opts)
|
||||
# Clear any locks set for the active fileserver backends
|
||||
salt.daemons.masterapi.clear_fsbackend_locks(self.opts)
|
||||
# Clear any locks set for git_pillar
|
||||
salt.daemons.masterapi.clear_git_pillar_locks(self.opts)
|
||||
# Clean out pub auth
|
||||
salt.daemons.masterapi.clean_pub_auth(self.opts)
|
||||
|
||||
|
@ -855,7 +855,11 @@ class Minion(MinionBase):
|
||||
log.info('Creating minion process manager')
|
||||
self.process_manager = ProcessManager(name='MinionProcessManager')
|
||||
self.io_loop.spawn_callback(self.process_manager.run, async=True)
|
||||
self.io_loop.spawn_callback(salt.engines.start_engines, self.opts, self.process_manager)
|
||||
# We don't have the proxy setup yet, so we can't start engines
|
||||
# Engines need to be able to access __proxy__
|
||||
if not salt.utils.is_proxy():
|
||||
self.io_loop.spawn_callback(salt.engines.start_engines, self.opts,
|
||||
self.process_manager)
|
||||
|
||||
# Install the SIGINT/SIGTERM handlers if not done so far
|
||||
if signal.getsignal(signal.SIGINT) is signal.SIG_DFL:
|
||||
@ -2964,7 +2968,7 @@ class ProxyMinion(Minion):
|
||||
# Then load the proxy module
|
||||
self.proxy = salt.loader.proxy(self.opts)
|
||||
|
||||
# Check config 'add_proxymodule_to_opts' Remove this in Boron.
|
||||
# Check config 'add_proxymodule_to_opts' Remove this in Carbon.
|
||||
if self.opts['add_proxymodule_to_opts']:
|
||||
self.opts['proxymodule'] = self.proxy
|
||||
|
||||
@ -2975,6 +2979,12 @@ class ProxyMinion(Minion):
|
||||
self.proxy.pack['__ret__'] = self.returners
|
||||
self.proxy.pack['__pillar__'] = self.opts['pillar']
|
||||
|
||||
# Start engines here instead of in the Minion superclass __init__
|
||||
# This is because we need to inject the __proxy__ variable but
|
||||
# it is not setup until now.
|
||||
self.io_loop.spawn_callback(salt.engines.start_engines, self.opts,
|
||||
self.process_manager, proxy=self.proxy)
|
||||
|
||||
if ('{0}.init'.format(fq_proxyname) not in self.proxy
|
||||
or '{0}.shutdown'.format(fq_proxyname) not in self.proxy):
|
||||
log.error('Proxymodule {0} is missing an init() or a shutdown() or both.'.format(fq_proxyname))
|
||||
|
@ -680,7 +680,7 @@ def _change_state(name, action, expected, *args, **kwargs):
|
||||
'state': {'old': expected, 'new': expected},
|
||||
'comment': ('Container \'{0}\' already {1}'
|
||||
.format(name, expected))}
|
||||
response = _client_wrapper(action, name, *args, **kwargs)
|
||||
_client_wrapper(action, name, *args, **kwargs)
|
||||
_clear_context()
|
||||
try:
|
||||
post = state(name)
|
||||
@ -689,8 +689,6 @@ def _change_state(name, action, expected, *args, **kwargs):
|
||||
post = None
|
||||
ret = {'result': post == expected,
|
||||
'state': {'old': pre, 'new': post}}
|
||||
if action == 'wait':
|
||||
ret['exit_status'] = response
|
||||
return ret
|
||||
|
||||
|
||||
@ -4856,7 +4854,7 @@ def unpause(name):
|
||||
unfreeze = salt.utils.alias_function(unpause, 'unfreeze')
|
||||
|
||||
|
||||
def wait(name):
|
||||
def wait(name, ignore_already_stopped=False, fail_on_exit_status=False):
|
||||
'''
|
||||
Wait for the container to exit gracefully, and return its exit code
|
||||
|
||||
@ -4867,6 +4865,13 @@ def wait(name):
|
||||
name
|
||||
Container name or ID
|
||||
|
||||
ignore_already_stopped
|
||||
Boolean flag that prevent execution to fail, if a container
|
||||
is already stopped.
|
||||
|
||||
fail_on_exit_status
|
||||
Boolean flag to report execution as failure if ``exit_status``
|
||||
is different than 0.
|
||||
|
||||
**RETURN DATA**
|
||||
|
||||
@ -4885,7 +4890,36 @@ def wait(name):
|
||||
|
||||
salt myminion dockerng.wait mycontainer
|
||||
'''
|
||||
return _change_state(name, 'wait', 'stopped')
|
||||
try:
|
||||
pre = state(name)
|
||||
except CommandExecutionError:
|
||||
# Container doesn't exist anymore
|
||||
return {'result': ignore_already_stopped,
|
||||
'comment': 'Container \'{0}\' absent'.format(name)}
|
||||
already_stopped = pre == 'stopped'
|
||||
response = _client_wrapper('wait', name)
|
||||
_clear_context()
|
||||
try:
|
||||
post = state(name)
|
||||
except CommandExecutionError:
|
||||
# Container doesn't exist anymore
|
||||
post = None
|
||||
|
||||
if already_stopped:
|
||||
success = ignore_already_stopped
|
||||
elif post == 'stopped':
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
|
||||
result = {'result': success,
|
||||
'state': {'old': pre, 'new': post},
|
||||
'exit_status': response}
|
||||
if already_stopped:
|
||||
result['comment'] = 'Container \'{0}\' already stopped'.format(name)
|
||||
if fail_on_exit_status and result['result']:
|
||||
result['result'] = result['exit_status'] == 0
|
||||
return result
|
||||
|
||||
|
||||
# Functions to run commands inside containers
|
||||
|
@ -408,6 +408,34 @@ def sync_proxymodules(saltenv=None, refresh=False):
|
||||
return ret
|
||||
|
||||
|
||||
def sync_engines(saltenv=None, refresh=False):
|
||||
'''
|
||||
.. versionadded:: 2016.3.0
|
||||
|
||||
Sync engine modules from ``salt://_engines`` to the minion
|
||||
|
||||
saltenv : base
|
||||
The fileserver environment from which to sync. To sync from more than
|
||||
one environment, pass a comma-separated list.
|
||||
|
||||
refresh : True
|
||||
If ``True``, refresh the available execution modules on the minion.
|
||||
This refresh will be performed even if no new engine modules are synced.
|
||||
Set to ``False`` to prevent this refresh.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_engines
|
||||
salt '*' saltutil.sync_engines saltenv=base,dev
|
||||
'''
|
||||
ret = _sync('engines', saltenv)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
return ret
|
||||
|
||||
|
||||
def sync_output(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync outputters from ``salt://_output`` to the minion
|
||||
@ -540,6 +568,7 @@ def sync_all(saltenv=None, refresh=True):
|
||||
ret['utils'] = sync_utils(saltenv, False)
|
||||
ret['log_handlers'] = sync_log_handlers(saltenv, False)
|
||||
ret['proxymodules'] = sync_proxymodules(saltenv, False)
|
||||
ret['engines'] = sync_engines(saltenv, False)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
refresh_pillar()
|
||||
|
@ -1434,12 +1434,19 @@ def list_products(all=False, refresh=False):
|
||||
|
||||
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
||||
doc = dom.parseString(_zypper_check_result(call, xml=True))
|
||||
for prd in doc.getElementsByTagName('product-list')[0].getElementsByTagName('product'):
|
||||
product_list = doc.getElementsByTagName('product-list')
|
||||
if not product_list:
|
||||
return ret # No products found
|
||||
|
||||
for prd in product_list[0].getElementsByTagName('product'):
|
||||
p_nfo = dict()
|
||||
for k_p_nfo, v_p_nfo in prd.attributes.items():
|
||||
p_nfo[k_p_nfo] = k_p_nfo not in ['isbase', 'installed'] and v_p_nfo or v_p_nfo in ['true', '1']
|
||||
p_nfo['eol'] = prd.getElementsByTagName('endoflife')[0].getAttribute('text')
|
||||
p_nfo['eol_t'] = int(prd.getElementsByTagName('endoflife')[0].getAttribute('time_t'))
|
||||
|
||||
eol = prd.getElementsByTagName('endoflife')
|
||||
if eol:
|
||||
p_nfo['eol'] = eol[0].getAttribute('text')
|
||||
p_nfo['eol_t'] = int(eol[0].getAttribute('time_t') or 0)
|
||||
p_nfo['description'] = " ".join(
|
||||
[line.strip() for line in _get_first_aggregate_text(
|
||||
prd.getElementsByTagName('description')
|
||||
|
@ -299,6 +299,8 @@ def salt_key():
|
||||
SystemExit('\nExiting gracefully on Ctrl-c'),
|
||||
err,
|
||||
hardcrash, trace=trace)
|
||||
except Exception as err:
|
||||
sys.stderr.write("Error: {0}\n".format(err.message))
|
||||
|
||||
|
||||
def salt_cp():
|
||||
|
@ -145,16 +145,18 @@ def _get_comparison_spec(pkgver):
|
||||
return oper, verstr
|
||||
|
||||
|
||||
def _fulfills_version_spec(versions, oper, desired_version):
|
||||
def _fulfills_version_spec(versions, oper, desired_version,
|
||||
ignore_epoch=False):
|
||||
'''
|
||||
Returns True if any of the installed versions match the specified version,
|
||||
otherwise returns False
|
||||
'''
|
||||
normalize = lambda x: x.split(':', 1)[-1] if ignore_epoch else x
|
||||
cmp_func = __salt__.get('pkg.version_cmp')
|
||||
for ver in versions:
|
||||
if salt.utils.compare_versions(ver1=ver,
|
||||
if salt.utils.compare_versions(ver1=normalize(ver),
|
||||
oper=oper,
|
||||
ver2=desired_version,
|
||||
ver2=normalize(desired_version),
|
||||
cmp_func=cmp_func):
|
||||
return True
|
||||
return False
|
||||
@ -177,6 +179,7 @@ def _find_remove_targets(name=None,
|
||||
version=None,
|
||||
pkgs=None,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Inspect the arguments to pkg.removed and discover what packages need to
|
||||
@ -227,7 +230,8 @@ def _find_remove_targets(name=None,
|
||||
except CommandExecutionError as exc:
|
||||
problems.append(exc.strerror)
|
||||
continue
|
||||
if not _fulfills_version_spec(cver, oper, verstr):
|
||||
if not _fulfills_version_spec(cver, oper, verstr,
|
||||
ignore_epoch=ignore_epoch):
|
||||
log.debug(
|
||||
'Current version ({0}) did not match desired version '
|
||||
'specification ({1}), will not remove'
|
||||
@ -263,6 +267,7 @@ def _find_install_targets(name=None,
|
||||
skip_suggestions=False,
|
||||
pkg_verify=False,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
reinstall=False,
|
||||
**kwargs):
|
||||
'''
|
||||
@ -471,7 +476,8 @@ def _find_install_targets(name=None,
|
||||
if not sources and 'allow_updates' in kwargs:
|
||||
if kwargs['allow_updates']:
|
||||
oper = '>='
|
||||
if _fulfills_version_spec(cver, oper, verstr):
|
||||
if not _fulfills_version_spec(cver, oper, verstr,
|
||||
ignore_epoch=ignore_epoch):
|
||||
if reinstall:
|
||||
to_reinstall[key] = val
|
||||
elif pkg_verify and oper == '==':
|
||||
@ -510,7 +516,7 @@ def _find_install_targets(name=None,
|
||||
return desired, targets, to_unpurge, to_reinstall, altered_files, warnings
|
||||
|
||||
|
||||
def _verify_install(desired, new_pkgs):
|
||||
def _verify_install(desired, new_pkgs, ignore_epoch=False):
|
||||
'''
|
||||
Determine whether or not the installed packages match what was requested in
|
||||
the SLS file.
|
||||
@ -541,7 +547,8 @@ def _verify_install(desired, new_pkgs):
|
||||
ok.append(pkgname)
|
||||
continue
|
||||
oper, verstr = _get_comparison_spec(pkgver)
|
||||
if _fulfills_version_spec(cver, oper, verstr):
|
||||
if _fulfills_version_spec(cver, oper, verstr,
|
||||
ignore_epoch=ignore_epoch):
|
||||
ok.append(pkgname)
|
||||
else:
|
||||
failed.append(pkgname)
|
||||
@ -602,6 +609,7 @@ def installed(
|
||||
allow_updates=False,
|
||||
pkg_verify=False,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
reinstall=False,
|
||||
**kwargs):
|
||||
'''
|
||||
@ -630,9 +638,29 @@ def installed(
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt myminion pkg.latest_version httpd
|
||||
# salt myminion pkg.latest_version vim-enhanced
|
||||
myminion:
|
||||
2.2.15-30.el6.centos
|
||||
2:7.4.160-1.el7
|
||||
|
||||
.. important::
|
||||
As of version 2015.8.7, for distros which use yum/dnf, packages
|
||||
which have a version with a nonzero epoch (that is, versions which
|
||||
start with a number followed by a colon like in the
|
||||
``pkg.latest_version`` output above) must have the epoch included
|
||||
when specifying the version number. For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed.
|
||||
|
||||
Also, while this function is not yet implemented for all pkg frontends,
|
||||
:mod:`pkg.list_repo_pkgs <salt.modules.yumpkg.list_repo_pkgs>` will
|
||||
@ -736,6 +764,111 @@ def installed(
|
||||
|
||||
.. versionadded:: 2014.1.1
|
||||
|
||||
:param bool allow_updates:
|
||||
Allow the package to be updated outside Salt's control (e.g. auto
|
||||
updates on Windows). This means a package on the Minion can have a
|
||||
newer version than the latest available in the repository without
|
||||
enforcing a re-installation of the package.
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
httpd:
|
||||
pkg.installed:
|
||||
- fromrepo: mycustomrepo
|
||||
- skip_verify: True
|
||||
- skip_suggestions: True
|
||||
- version: 2.0.6~ubuntu3
|
||||
- refresh: True
|
||||
- allow_updates: True
|
||||
- hold: False
|
||||
|
||||
:param bool pkg_verify:
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
For requested packages that are already installed and would not be
|
||||
targeted for upgrade or downgrade, use pkg.verify to determine if any
|
||||
of the files installed by the package have been altered. If files have
|
||||
been altered, the reinstall option of pkg.install is used to force a
|
||||
reinstall. Types to ignore can be passed to pkg.verify (see example
|
||||
below). Currently, this option is supported for the following pkg
|
||||
providers: :mod:`yumpkg <salt.modules.yumpkg>`.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
httpd:
|
||||
pkg.installed:
|
||||
- version: 2.2.15-30.el6.centos
|
||||
- pkg_verify: True
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mypkgs:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- foo
|
||||
- bar: 1.2.3-4
|
||||
- baz
|
||||
- pkg_verify:
|
||||
- ignore_types: [config,doc]
|
||||
|
||||
:param bool normalize:
|
||||
Normalize the package name by removing the architecture, if the
|
||||
architecture of the package is different from the architecture of the
|
||||
operating system. The ability to disable this behavior is useful for
|
||||
poorly-created packages which include the architecture as an actual
|
||||
part of the name, such as kernel modules which match a specific kernel
|
||||
version.
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
gpfs.gplbin-2.6.32-279.31.1.el6.x86_64:
|
||||
pkg.installed:
|
||||
- normalize: False
|
||||
|
||||
:param bool ignore_epoch:
|
||||
When a package version contains an non-zero epoch (e.g.
|
||||
``1:3.14.159-2.el7``, and a specific version of a package is desired,
|
||||
set this option to ``True`` to ignore the epoch when comparing
|
||||
versions. This allows for the following SLS to be used:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Actual vim-enhanced version: 2:7.4.160-1.el7
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 7.4.160-1.el7
|
||||
- ignore_epoch: True
|
||||
|
||||
Without this option set to ``True`` in the above example, the package
|
||||
would be installed, but the state would report as failed because the
|
||||
actual installed version would be ``2:7.4.160-1.el7``. Alternatively,
|
||||
this option can be left as ``False`` and the full version string (with
|
||||
epoch) can be specified in the SLS file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
.. versionadded:: 2015.8.9
|
||||
|
||||
|
|
||||
|
||||
**MULTIPLE PACKAGE INSTALLATION OPTIONS: (not supported in Windows or
|
||||
pkgng)**
|
||||
|
||||
:param list pkgs:
|
||||
A list of packages to install from a software repository. All packages
|
||||
listed under ``pkgs`` will be installed via a single command.
|
||||
@ -802,9 +935,6 @@ def installed(
|
||||
- bar: '~>=1.2:slot::overlay[use,-otheruse]'
|
||||
- baz
|
||||
|
||||
**Multiple Package Installation Options: (not supported in Windows or
|
||||
pkgng)**
|
||||
|
||||
:param list sources:
|
||||
A list of packages to install, along with the source URI or local path
|
||||
from which to install each package. In the example below, ``foo``,
|
||||
@ -822,105 +952,8 @@ def installed(
|
||||
- baz: ftp://someothersite.org/baz.rpm
|
||||
- qux: /minion/path/to/qux.rpm
|
||||
|
||||
:param bool allow_updates:
|
||||
Allow the package to be updated outside Salt's control (e.g. auto
|
||||
updates on Windows). This means a package on the Minion can have a
|
||||
newer version than the latest available in the repository without
|
||||
enforcing a re-installation of the package.
|
||||
**PLATFORM-SPECIFIC ARGUMENTS**
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
httpd:
|
||||
pkg.installed:
|
||||
- fromrepo: mycustomrepo
|
||||
- skip_verify: True
|
||||
- skip_suggestions: True
|
||||
- version: 2.0.6~ubuntu3
|
||||
- refresh: True
|
||||
- allow_updates: True
|
||||
- hold: False
|
||||
|
||||
:param bool pkg_verify:
|
||||
|
||||
For requested packages that are already installed and would not be
|
||||
targeted for upgrade or downgrade, use ``pkg.verify`` to determine if
|
||||
any of the files installed by the package have been altered. If files
|
||||
have been altered, the reinstall option of ``pkg.install`` is used to
|
||||
force a reinstall. Types to ignore can be passed to ``pkg.verify`` (see
|
||||
example below). Currently, this option is supported for the following
|
||||
pkg providers: :mod:`yumpkg <salt.modules.yumpkg>`.
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
.. note::
|
||||
If ``reinstall`` is set to ``True``, then ``pkg.verify`` will not
|
||||
be run and any targeted package which is installed and would not be
|
||||
targeted for upgrade/downgrade will be reinstalled.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
httpd:
|
||||
pkg.installed:
|
||||
- version: 2.2.15-30.el6.centos
|
||||
- pkg_verify: True
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mypkgs:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- foo
|
||||
- bar: 1.2.3-4
|
||||
- baz
|
||||
- pkg_verify:
|
||||
- ignore_types: [config,doc]
|
||||
|
||||
:param bool normalize:
|
||||
Normalize the package name by removing the architecture, if the
|
||||
architecture of the package is different from the architecture of the
|
||||
operating system. The ability to disable this behavior is useful for
|
||||
poorly-created packages which include the architecture as an actual
|
||||
part of the name, such as kernel modules which match a specific kernel
|
||||
version.
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
gpfs.gplbin-2.6.32-279.31.1.el6.x86_64:
|
||||
pkg.installed:
|
||||
- normalize: False
|
||||
|
||||
:param bool reinstall:
|
||||
If any of the specified packages are already installed, and this option
|
||||
is set to ``True``, then these packages will (where supported) be
|
||||
reinstalled. This is supported in both :mod:`apt <salt.modules.aptpkg>`
|
||||
and :mod:`yumpkg <salt.modules.yumpkg>`.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zsh:
|
||||
pkg.installed:
|
||||
- reinstall: True
|
||||
|
||||
.. note::
|
||||
Setting and leaving this option as ``True`` will result in
|
||||
reinstallation every time the state is run, which may not be
|
||||
desired.
|
||||
|
||||
:param kwargs:
|
||||
These are specific to each OS. If it does not apply to the execution
|
||||
module for your OS, it is ignored.
|
||||
|
||||
@ -1008,6 +1041,7 @@ def installed(
|
||||
skip_suggestions=skip_suggestions,
|
||||
pkg_verify=pkg_verify,
|
||||
normalize=normalize,
|
||||
ignore_epoch=ignore_epoch,
|
||||
reinstall=reinstall,
|
||||
**kwargs)
|
||||
|
||||
@ -1237,12 +1271,9 @@ def installed(
|
||||
else:
|
||||
if __grains__['os'] == 'FreeBSD':
|
||||
kwargs['with_origin'] = True
|
||||
ok, failed = \
|
||||
_verify_install(
|
||||
desired, __salt__['pkg.list_pkgs'](
|
||||
versions_as_list=True, **kwargs
|
||||
)
|
||||
)
|
||||
new_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs)
|
||||
ok, failed = _verify_install(desired, new_pkgs,
|
||||
ignore_epoch=ignore_epoch)
|
||||
modified = [x for x in ok if x in targets]
|
||||
not_modified = [x for x in ok
|
||||
if x not in targets
|
||||
@ -1693,6 +1724,7 @@ def _uninstall(
|
||||
version=None,
|
||||
pkgs=None,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Common function for package removal
|
||||
@ -1715,7 +1747,8 @@ def _uninstall(
|
||||
'result': False,
|
||||
'comment': 'An error was encountered while parsing targets: '
|
||||
'{0}'.format(exc)}
|
||||
targets = _find_remove_targets(name, version, pkgs, normalize, **kwargs)
|
||||
targets = _find_remove_targets(name, version, pkgs, normalize,
|
||||
ignore_epoch=ignore_epoch, **kwargs)
|
||||
if isinstance(targets, dict) and 'result' in targets:
|
||||
return targets
|
||||
elif not isinstance(targets, list):
|
||||
@ -1779,7 +1812,12 @@ def _uninstall(
|
||||
'comment': ' '.join(comments)}
|
||||
|
||||
|
||||
def removed(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
def removed(name,
|
||||
version=None,
|
||||
pkgs=None,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Verify that a package is not installed, calling ``pkg.remove`` if necessary
|
||||
to remove the package.
|
||||
@ -1791,6 +1829,30 @@ def removed(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
The version of the package that should be removed. Don't do anything if
|
||||
the package is installed with an unmatching version.
|
||||
|
||||
.. important::
|
||||
As of version 2015.8.7, for distros which use yum/dnf, packages
|
||||
which have a version with a nonzero epoch (that is, versions which
|
||||
start with a number followed by a colon like in the example above)
|
||||
must have the epoch included when specifying the version number.
|
||||
For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed. If **ignore_epoch** was not set
|
||||
to ``True``, and instead of ``2:7.4.160-1.el7`` a version of
|
||||
``7.4.160-1.el7`` were used, this state would report success since
|
||||
the actual installed version includes the epoch, and the specified
|
||||
version would not match.
|
||||
|
||||
normalize : True
|
||||
Normalize the package name by removing the architecture, if the
|
||||
architecture of the package is different from the architecture of the
|
||||
@ -1801,6 +1863,34 @@ def removed(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
ignore_epoch : False
|
||||
When a package version contains an non-zero epoch (e.g.
|
||||
``1:3.14.159-2.el7``, and a specific version of a package is desired,
|
||||
set this option to ``True`` to ignore the epoch when comparing
|
||||
versions. This allows for the following SLS to be used:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Actual vim-enhanced version: 2:7.4.160-1.el7
|
||||
vim-enhanced:
|
||||
pkg.removed:
|
||||
- version: 7.4.160-1.el7
|
||||
- ignore_epoch: True
|
||||
|
||||
Without this option set to ``True`` in the above example, the state
|
||||
would falsely report success since the actual installed version is
|
||||
``2:7.4.160-1.el7``. Alternatively, this option can be left as
|
||||
``False`` and the full version string (with epoch) can be specified in
|
||||
the SLS file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.removed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
.. versionadded:: 2015.8.9
|
||||
|
||||
Multiple Package Options:
|
||||
|
||||
pkgs
|
||||
@ -1812,7 +1902,8 @@ def removed(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
'''
|
||||
try:
|
||||
return _uninstall(action='remove', name=name, version=version,
|
||||
pkgs=pkgs, normalize=normalize, **kwargs)
|
||||
pkgs=pkgs, normalize=normalize,
|
||||
ignore_epoch=ignore_epoch, **kwargs)
|
||||
except CommandExecutionError as exc:
|
||||
ret = {'name': name, 'result': False}
|
||||
if exc.info:
|
||||
@ -1826,7 +1917,12 @@ def removed(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
return ret
|
||||
|
||||
|
||||
def purged(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
def purged(name,
|
||||
version=None,
|
||||
pkgs=None,
|
||||
normalize=True,
|
||||
ignore_epoch=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Verify that a package is not installed, calling ``pkg.purge`` if necessary
|
||||
to purge the package. All configuration files are also removed.
|
||||
@ -1838,6 +1934,30 @@ def purged(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
The version of the package that should be removed. Don't do anything if
|
||||
the package is installed with an unmatching version.
|
||||
|
||||
.. important::
|
||||
As of version 2015.8.7, for distros which use yum/dnf, packages
|
||||
which have a version with a nonzero epoch (that is, versions which
|
||||
start with a number followed by a colon like in the example above)
|
||||
must have the epoch included when specifying the version number.
|
||||
For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.installed:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
In version 2015.8.9, an **ignore_epoch** argument has been added to
|
||||
:py:mod:`pkg.installed <salt.states.pkg.installed>`,
|
||||
:py:mod:`pkg.removed <salt.states.pkg.installed>`, and
|
||||
:py:mod:`pkg.purged <salt.states.pkg.installed>` states, which
|
||||
causes the epoch to be disregarded when the state checks to see if
|
||||
the desired version was installed. If **ignore_epoch** was not set
|
||||
to ``True``, and instead of ``2:7.4.160-1.el7`` a version of
|
||||
``7.4.160-1.el7`` were used, this state would report success since
|
||||
the actual installed version includes the epoch, and the specified
|
||||
version would not match.
|
||||
|
||||
normalize : True
|
||||
Normalize the package name by removing the architecture, if the
|
||||
architecture of the package is different from the architecture of the
|
||||
@ -1848,6 +1968,34 @@ def purged(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
ignore_epoch : False
|
||||
When a package version contains an non-zero epoch (e.g.
|
||||
``1:3.14.159-2.el7``, and a specific version of a package is desired,
|
||||
set this option to ``True`` to ignore the epoch when comparing
|
||||
versions. This allows for the following SLS to be used:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Actual vim-enhanced version: 2:7.4.160-1.el7
|
||||
vim-enhanced:
|
||||
pkg.purged:
|
||||
- version: 7.4.160-1.el7
|
||||
- ignore_epoch: True
|
||||
|
||||
Without this option set to ``True`` in the above example, the state
|
||||
would falsely report success since the actual installed version is
|
||||
``2:7.4.160-1.el7``. Alternatively, this option can be left as
|
||||
``False`` and the full version string (with epoch) can be specified in
|
||||
the SLS file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
vim-enhanced:
|
||||
pkg.purged:
|
||||
- version: 2:7.4.160-1.el7
|
||||
|
||||
.. versionadded:: 2015.8.9
|
||||
|
||||
Multiple Package Options:
|
||||
|
||||
pkgs
|
||||
@ -1859,7 +2007,8 @@ def purged(name, version=None, pkgs=None, normalize=True, **kwargs):
|
||||
'''
|
||||
try:
|
||||
return _uninstall(action='purge', name=name, version=version,
|
||||
pkgs=pkgs, normalize=normalize, **kwargs)
|
||||
pkgs=pkgs, normalize=normalize,
|
||||
ignore_epoch=ignore_epoch, **kwargs)
|
||||
except CommandExecutionError as exc:
|
||||
ret = {'name': name, 'result': False}
|
||||
if exc.info:
|
||||
|
@ -650,7 +650,8 @@ def versions_report(include_salt_cloud=False):
|
||||
info = []
|
||||
for ver_type in ('Salt Version', 'Dependency Versions', 'System Versions'):
|
||||
info.append('{0}:'.format(ver_type))
|
||||
for name in sorted(ver_info[ver_type]):
|
||||
# List dependencies in alphabetical, case insensitive order
|
||||
for name in sorted(ver_info[ver_type], cmp=lambda x, y: cmp(x.lower(), y.lower())):
|
||||
ver = fmt.format(name,
|
||||
ver_info[ver_type][name] or 'Not Installed',
|
||||
pad=padding)
|
||||
|
@ -86,6 +86,7 @@ class CryptTestCase(TestCase):
|
||||
@patch('os.umask', MagicMock())
|
||||
@patch('os.chmod', MagicMock())
|
||||
@patch('os.chown', MagicMock())
|
||||
@patch('os.access', MagicMock(return_value=True))
|
||||
def test_gen_keys(self):
|
||||
with patch('salt.utils.fopen', mock_open()):
|
||||
open_priv_wb = call('/keydir/keyname.pem', 'wb+')
|
||||
|
@ -21,7 +21,7 @@ ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.modules import dockerng as dockerng_mod
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
dockerng_mod.__context__ = {'docker.docker_version': ''}
|
||||
dockerng_mod.__salt__ = {}
|
||||
@ -509,6 +509,116 @@ class DockerngTestCase(TestCase):
|
||||
dockerng_mod.inspect_volume('foo')
|
||||
client.inspect_volume.assert_called_once_with('foo')
|
||||
|
||||
def test_wait_success(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
client.wait = Mock(return_value=0)
|
||||
dockerng_inspect_container = Mock(side_effect=[
|
||||
{'State': {'Running': True}},
|
||||
{'State': {'Stopped': True}}])
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo')
|
||||
self.assertEqual(result, {'result': True,
|
||||
'exit_status': 0,
|
||||
'state': {'new': 'stopped',
|
||||
'old': 'running'}})
|
||||
|
||||
def test_wait_fails_already_stopped(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
client.wait = Mock(return_value=0)
|
||||
dockerng_inspect_container = Mock(side_effect=[
|
||||
{'State': {'Stopped': True}},
|
||||
{'State': {'Stopped': True}},
|
||||
])
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo')
|
||||
self.assertEqual(result, {'result': False,
|
||||
'comment': "Container 'foo' already stopped",
|
||||
'exit_status': 0,
|
||||
'state': {'new': 'stopped',
|
||||
'old': 'stopped'}})
|
||||
|
||||
def test_wait_success_already_stopped(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
client.wait = Mock(return_value=0)
|
||||
dockerng_inspect_container = Mock(side_effect=[
|
||||
{'State': {'Stopped': True}},
|
||||
{'State': {'Stopped': True}},
|
||||
])
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo', ignore_already_stopped=True)
|
||||
self.assertEqual(result, {'result': True,
|
||||
'comment': "Container 'foo' already stopped",
|
||||
'exit_status': 0,
|
||||
'state': {'new': 'stopped',
|
||||
'old': 'stopped'}})
|
||||
|
||||
def test_wait_success_absent_container(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
dockerng_inspect_container = Mock(side_effect=CommandExecutionError)
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo', ignore_already_stopped=True)
|
||||
self.assertEqual(result, {'result': True,
|
||||
'comment': "Container 'foo' absent"})
|
||||
|
||||
def test_wait_fails_on_exit_status(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
client.wait = Mock(return_value=1)
|
||||
dockerng_inspect_container = Mock(side_effect=[
|
||||
{'State': {'Running': True}},
|
||||
{'State': {'Stopped': True}}])
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo', fail_on_exit_status=True)
|
||||
self.assertEqual(result, {'result': False,
|
||||
'exit_status': 1,
|
||||
'state': {'new': 'stopped',
|
||||
'old': 'running'}})
|
||||
|
||||
def test_wait_fails_on_exit_status_and_already_stopped(self):
|
||||
client = Mock()
|
||||
client.api_version = '1.21'
|
||||
client.wait = Mock(return_value=1)
|
||||
dockerng_inspect_container = Mock(side_effect=[
|
||||
{'State': {'Stopped': True}},
|
||||
{'State': {'Stopped': True}}])
|
||||
with patch.object(dockerng_mod, 'inspect_container',
|
||||
dockerng_inspect_container):
|
||||
with patch.dict(dockerng_mod.__context__,
|
||||
{'docker.client': client}):
|
||||
dockerng_mod._clear_context()
|
||||
result = dockerng_mod.wait('foo',
|
||||
ignore_already_stopped=True,
|
||||
fail_on_exit_status=True)
|
||||
self.assertEqual(result, {'result': False,
|
||||
'comment': "Container 'foo' already stopped",
|
||||
'exit_status': 1,
|
||||
'state': {'new': 'stopped',
|
||||
'old': 'stopped'}})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
|
@ -31,7 +31,17 @@
|
||||
offers common management tools and technology
|
||||
certifications across the platform, and
|
||||
each product is enterprise-class.</description></product>
|
||||
<product name="SUSE_SLES" version="11.3" release="1.201" epoch="0" arch="x86_64" productline="" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Linux Enterprise Server 11 SP3 No EOL" shortname="" flavor="" isbase="0" repo="nu_novell_com:SLES11-SP3-Updates" installed="0">0x7ffdb538e948<description>SUSE Linux Enterprise offers a comprehensive
|
||||
suite of products built on a single code base.
|
||||
The platform addresses business needs from
|
||||
the smallest thin-client devices to the world’s
|
||||
most powerful high-performance computing
|
||||
and mainframe servers. SUSE Linux Enterprise
|
||||
offers common management tools and technology
|
||||
certifications across the platform, and
|
||||
each product is enterprise-class.</description></product>
|
||||
<product name="SUSE-Manager-Server" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="0" repo="nu_novell_com:SUSE-Manager-Server-2.1-Pool" installed="0"><endoflife time_t="0" text="1970-01-01T01:00:00+0100"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
||||
<product name="SUSE-Manager-Server" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="manager" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="1" repo="@System" installed="1"><endoflife time_t="0" text="1970-01-01T01:00:00+0100"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
||||
<product name="SUSE-Manager-Server-Broken-EOL" version="2.1" release="1.2" epoch="0" arch="x86_64" productline="manager" registerrelease="" vendor="SUSE LINUX Products GmbH, Nuernberg, Germany" summary="SUSE Manager Server" shortname="" flavor="cd" isbase="1" repo="@System" installed="1"><endoflife wrong="attribute"/>0x7ffdb538e948<description>SUSE Manager Server appliance</description></product>
|
||||
</product-list>
|
||||
</stream>
|
||||
|
@ -24,6 +24,14 @@ provisioning.</description></product>
|
||||
SUSE Manager Tools provide packages required to connect to a
|
||||
SUSE Manager Server.
|
||||
<p></description></product>
|
||||
<product name="sle-manager-tools-beta-no-eol" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><registerflavor>extension</registerflavor><description><p>
|
||||
SUSE Manager Tools provide packages required to connect to a
|
||||
SUSE Manager Server.
|
||||
<p></description></product>
|
||||
<product name="sle-manager-tools-beta-broken-eol" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><endoflife wrong="attribute"/><registerflavor>extension</registerflavor><description><p>
|
||||
SUSE Manager Tools provide packages required to connect to a
|
||||
SUSE Manager Server.
|
||||
<p></description></product>
|
||||
<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE" summary="SUSE Linux Enterprise Server 12 SP1" repo="@System" productline="sles" registerrelease="" shortname="SLES12-SP1" flavor="DVD" isbase="true" installed="true"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
|
||||
suite of products built on a single code base.
|
||||
The platform addresses business needs from
|
||||
|
@ -153,24 +153,26 @@ class ZypperTestCase(TestCase):
|
||||
for filename, test_data in {
|
||||
'zypper-products-sle12sp1.xml': {
|
||||
'name': ['SLES', 'SLES', 'SUSE-Manager-Proxy',
|
||||
'SUSE-Manager-Server', 'sle-manager-tools-beta'],
|
||||
'SUSE-Manager-Server', 'sle-manager-tools-beta',
|
||||
'sle-manager-tools-beta-broken-eol', 'sle-manager-tools-beta-no-eol'],
|
||||
'vendor': 'SUSE LLC <https://www.suse.com/>',
|
||||
'release': ['0', '0', '0', '0', '0'],
|
||||
'productline': [False, False, False, False, 'sles'],
|
||||
'eol_t': [1509408000, 1522454400, 1522454400, 1730332800, 1730332800],
|
||||
'isbase': [False, False, False, False, True],
|
||||
'installed': [False, False, False, False, True],
|
||||
'release': ['0', '0', '0', '0', '0', '0', '0'],
|
||||
'productline': [False, False, False, False, False, False, 'sles'],
|
||||
'eol_t': [None, 0, 1509408000, 1522454400, 1522454400, 1730332800, 1730332800],
|
||||
'isbase': [False, False, False, False, False, False, True],
|
||||
'installed': [False, False, False, False, False, False, True],
|
||||
},
|
||||
'zypper-products-sle11sp3.xml': {
|
||||
'name': ['SUSE-Manager-Server', 'SUSE-Manager-Server',
|
||||
'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES-SP4-migration'],
|
||||
'name': ['SUSE-Manager-Server', 'SUSE-Manager-Server', 'SUSE-Manager-Server-Broken-EOL',
|
||||
'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES-SP4-migration'],
|
||||
'vendor': 'SUSE LINUX Products GmbH, Nuernberg, Germany',
|
||||
'release': ['1.138', '1.2', '1.2', '1.201', '1.4'],
|
||||
'productline': [False, False, False, False, 'manager'],
|
||||
'eol_t': [0, 0, 0, 0, 0],
|
||||
'isbase': [False, False, False, False, True],
|
||||
'installed': [False, False, False, False, True],
|
||||
'release': ['1.138', '1.2', '1.2', '1.2', '1.201', '1.201', '1.4'],
|
||||
'productline': [False, False, False, False, False, 'manager', 'manager'],
|
||||
'eol_t': [None, 0, 0, 0, 0, 0, 0],
|
||||
'isbase': [False, False, False, False, False, True, True],
|
||||
'installed': [False, False, False, False, False, True, True],
|
||||
}}.items():
|
||||
|
||||
ref_out = {
|
||||
'retcode': 0,
|
||||
'stdout': get_test_data(filename)
|
||||
@ -178,10 +180,10 @@ class ZypperTestCase(TestCase):
|
||||
|
||||
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
|
||||
products = zypper.list_products()
|
||||
self.assertEqual(len(products), 5)
|
||||
self.assertEqual(len(products), 7)
|
||||
self.assertIn(test_data['vendor'], [product['vendor'] for product in products])
|
||||
for kwd in ['name', 'isbase', 'installed', 'release', 'productline', 'eol_t']:
|
||||
self.assertEqual(test_data[kwd], sorted([prod[kwd] for prod in products]))
|
||||
self.assertEqual(test_data[kwd], sorted([prod.get(kwd) for prod in products]))
|
||||
|
||||
def test_refresh_db(self):
|
||||
'''
|
||||
|
@ -871,6 +871,31 @@ class DockerngTestCase(TestCase):
|
||||
'removed': 'removed'},
|
||||
'result': True})
|
||||
|
||||
def test_volume_present_wo_existing_volumes(self):
|
||||
'''
|
||||
Test dockerng.volume_present without existing volumes.
|
||||
'''
|
||||
dockerng_create_volume = Mock(return_value='created')
|
||||
dockerng_remove_volume = Mock(return_value='removed')
|
||||
__salt__ = {'dockerng.create_volume': dockerng_create_volume,
|
||||
'dockerng.remove_volume': dockerng_remove_volume,
|
||||
'dockerng.volumes': Mock(return_value={'Volumes': None}),
|
||||
}
|
||||
with patch.dict(dockerng_state.__dict__,
|
||||
{'__salt__': __salt__}):
|
||||
ret = dockerng_state.volume_present(
|
||||
'volume_foo',
|
||||
driver='bar',
|
||||
force=True,
|
||||
)
|
||||
dockerng_create_volume.assert_called_with('volume_foo',
|
||||
driver='bar',
|
||||
driver_opts=None)
|
||||
self.assertEqual(ret, {'name': 'volume_foo',
|
||||
'comment': '',
|
||||
'changes': {'created': 'created'},
|
||||
'result': True})
|
||||
|
||||
def test_volume_absent(self):
|
||||
'''
|
||||
Test dockerng.volume_absent
|
||||
|
Loading…
Reference in New Issue
Block a user