mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #30752 from terminalmage/zh459
Backport systemd and yum/dnf optimizations from develop into 2015.8
This commit is contained in:
commit
a49b75e065
4 changed files with 855 additions and 647 deletions
|
@ -1,18 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Provide the service module for systemd
|
||||
|
||||
.. versionadded:: 0.10.0
|
||||
'''
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import copy
|
||||
import errno
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import shlex
|
||||
|
||||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
import salt.utils.itertools
|
||||
import salt.utils.systemd
|
||||
import salt.exceptions
|
||||
from salt.exceptions import CommandExecutionError, CommandNotFoundError
|
||||
from salt.ext import six
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -20,20 +26,25 @@ __func_alias__ = {
|
|||
'reload_': 'reload'
|
||||
}
|
||||
|
||||
SYSTEM_CONFIG_PATH = '/lib/systemd/system'
|
||||
LOCAL_CONFIG_PATH = '/etc/systemd/system'
|
||||
LEGACY_INIT_SCRIPT_PATH = '/etc/init.d'
|
||||
VALID_UNIT_TYPES = ['service', 'socket', 'device', 'mount', 'automount',
|
||||
'swap', 'target', 'path', 'timer']
|
||||
INITSCRIPT_PATH = '/etc/init.d'
|
||||
VALID_UNIT_TYPES = ('service', 'socket', 'device', 'mount', 'automount',
|
||||
'swap', 'target', 'path', 'timer')
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'service'
|
||||
|
||||
# Disable check for string substitution
|
||||
# pylint: disable=E1321
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on systems that have been booted with systemd
|
||||
'''
|
||||
if __grains__['kernel'] == 'Linux' and salt.utils.systemd.booted(__context__):
|
||||
if __grains__['kernel'] == 'Linux' \
|
||||
and salt.utils.systemd.booted(__context__):
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
@ -45,117 +56,35 @@ def _canonical_unit_name(name):
|
|||
'''
|
||||
if any(name.endswith(suffix) for suffix in VALID_UNIT_TYPES):
|
||||
return name
|
||||
return '{0}.service'.format(name)
|
||||
return '%s.service' % name
|
||||
|
||||
|
||||
def _canonical_template_unit_name(name):
|
||||
def _check_for_unit_changes(name):
|
||||
'''
|
||||
Build a canonical unit name for unit instances based on templates.
|
||||
Check for modified/updated unit files, and run a daemon-reload if any are
|
||||
found.
|
||||
'''
|
||||
return re.sub(r'@.+?(\.|$)', r'@\1', name)
|
||||
contextkey = 'systemd._check_for_unit_changes'
|
||||
if contextkey not in __context__:
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
# Set context key to avoid repeating this check
|
||||
__context__[contextkey] = True
|
||||
|
||||
|
||||
def _systemctl_cmd(action, name):
|
||||
def _clear_context():
|
||||
'''
|
||||
Build a systemctl command line. Treat unit names without one
|
||||
of the valid suffixes as a service.
|
||||
Remove context
|
||||
'''
|
||||
return 'systemctl {0} {1}'.format(action, _canonical_unit_name(name))
|
||||
|
||||
|
||||
def _get_all_units():
|
||||
'''
|
||||
Get all units and their state. Units ending in .service
|
||||
are normalized so that they can be referenced without a type suffix.
|
||||
'''
|
||||
rexp = re.compile(r'(?m)^(?P<name>.+)\.(?P<type>' +
|
||||
'|'.join(VALID_UNIT_TYPES) +
|
||||
r')\s+loaded\s+(?P<active>[^\s]+)')
|
||||
|
||||
out = __salt__['cmd.run_stdout'](
|
||||
'systemctl --all --full --no-legend --no-pager list-units'
|
||||
)
|
||||
|
||||
ret = {}
|
||||
for match in rexp.finditer(out):
|
||||
name = match.group('name')
|
||||
if match.group('type') != 'service':
|
||||
name += '.' + match.group('type')
|
||||
ret[name] = match.group('active')
|
||||
return ret
|
||||
|
||||
|
||||
def _get_all_unit_files():
|
||||
'''
|
||||
Get all unit files and their state. Unit files ending in .service
|
||||
are normalized so that they can be referenced without a type suffix.
|
||||
'''
|
||||
rexp = re.compile(r'(?m)^(?P<name>.+)\.(?P<type>' +
|
||||
'|'.join(VALID_UNIT_TYPES) +
|
||||
r')\s+(?P<state>.+)$')
|
||||
|
||||
out = __salt__['cmd.run_stdout'](
|
||||
'systemctl --full --no-legend --no-pager list-unit-files'
|
||||
)
|
||||
|
||||
ret = {}
|
||||
for match in rexp.finditer(out):
|
||||
name = match.group('name')
|
||||
if match.group('type') != 'service':
|
||||
name += '.' + match.group('type')
|
||||
ret[name] = match.group('state')
|
||||
return ret
|
||||
|
||||
|
||||
def _get_all_legacy_init_scripts():
|
||||
'''
|
||||
Get all old-fashioned init-style scripts. State is always inactive, because
|
||||
systemd would already show them otherwise.
|
||||
'''
|
||||
ret = {}
|
||||
if not os.path.isdir(LEGACY_INIT_SCRIPT_PATH):
|
||||
return ret
|
||||
for fn in os.listdir(LEGACY_INIT_SCRIPT_PATH):
|
||||
if not os.path.isfile(os.path.join(LEGACY_INIT_SCRIPT_PATH, fn)) or fn.startswith('rc'):
|
||||
# Using list() here because modifying a dictionary during iteration will
|
||||
# raise a RuntimeError.
|
||||
for key in list(__context__):
|
||||
try:
|
||||
if key.startswith('systemd._systemctl_status.') \
|
||||
or key in ('systemd.systemd_services',):
|
||||
__context__.pop(key)
|
||||
except AttributeError:
|
||||
continue
|
||||
log.info('Legacy init script: "%s".', fn)
|
||||
ret[fn] = 'inactive'
|
||||
return ret
|
||||
|
||||
|
||||
def _untracked_custom_unit_found(name):
|
||||
'''
|
||||
If the passed service name is not in the output from get_all(), but a unit
|
||||
file exist in /etc/systemd/system, return True. Otherwise, return False.
|
||||
'''
|
||||
unit_path = os.path.join('/etc/systemd/system',
|
||||
_canonical_unit_name(name))
|
||||
return name not in get_all() and os.access(unit_path, os.R_OK)
|
||||
|
||||
|
||||
def _unit_file_changed(name):
|
||||
'''
|
||||
Returns True if systemctl reports that the unit file has changed, otherwise
|
||||
returns False.
|
||||
'''
|
||||
return 'warning: unit file changed on disk' in \
|
||||
__salt__['cmd.run'](_systemctl_cmd('status', name)).lower()
|
||||
|
||||
|
||||
def systemctl_reload():
|
||||
'''
|
||||
Reloads systemctl, an action needed whenever unit files are updated.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' service.systemctl_reload
|
||||
'''
|
||||
retcode = __salt__['cmd.retcode']('systemctl --system daemon-reload')
|
||||
if retcode != 0:
|
||||
log.error('Problem performing systemctl daemon-reload')
|
||||
return retcode == 0
|
||||
|
||||
|
||||
def _default_runlevel():
|
||||
|
@ -200,22 +129,6 @@ def _default_runlevel():
|
|||
return runlevel
|
||||
|
||||
|
||||
def _runlevel():
|
||||
'''
|
||||
Return the current runlevel
|
||||
'''
|
||||
if 'systemd._runlevel' in __context__:
|
||||
return __context__['systemd._runlevel']
|
||||
out = __salt__['cmd.run']('runlevel', python_shell=False)
|
||||
try:
|
||||
ret = out.split()[1]
|
||||
except IndexError:
|
||||
# The runlevel is unknown, return the default
|
||||
ret = _default_runlevel()
|
||||
__context__['systemd._runlevel'] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def _get_service_exec():
|
||||
'''
|
||||
Debian uses update-rc.d to manage System-V style services.
|
||||
|
@ -226,53 +139,175 @@ def _get_service_exec():
|
|||
return executable
|
||||
|
||||
|
||||
def _get_systemd_services():
|
||||
'''
|
||||
Use os.listdir() to get all the unit files
|
||||
'''
|
||||
contextkey = 'systemd.systemd_services'
|
||||
if contextkey in __context__:
|
||||
return __context__[contextkey]
|
||||
ret = set()
|
||||
for path in (SYSTEM_CONFIG_PATH, LOCAL_CONFIG_PATH):
|
||||
for fullname in os.listdir(path):
|
||||
try:
|
||||
unit_name, unit_type = fullname.rsplit('.', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if unit_type in VALID_UNIT_TYPES:
|
||||
ret.add(unit_name if unit_type == 'service' else fullname)
|
||||
__context__[contextkey] = copy.deepcopy(ret)
|
||||
return ret
|
||||
|
||||
|
||||
def _get_sysv_services():
|
||||
'''
|
||||
Use os.listdir() and os.access() to get all the initscripts
|
||||
'''
|
||||
try:
|
||||
sysv_services = os.listdir(INITSCRIPT_PATH)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
pass
|
||||
elif exc.errno == errno.EACCES:
|
||||
log.error(
|
||||
'Unable to check sysvinit scripts, permission denied to %s',
|
||||
INITSCRIPT_PATH
|
||||
)
|
||||
else:
|
||||
log.error(
|
||||
'Error %d encountered trying to check sysvinit scripts: %s',
|
||||
exc.errno,
|
||||
exc.strerror
|
||||
)
|
||||
return []
|
||||
|
||||
systemd_services = _get_systemd_services()
|
||||
ret = []
|
||||
for sysv_service in sysv_services:
|
||||
if os.access(os.path.join(INITSCRIPT_PATH, sysv_service), os.X_OK):
|
||||
if sysv_service in systemd_services:
|
||||
log.debug(
|
||||
'sysvinit script \'%s\' found, but systemd unit '
|
||||
'\'%s.service\' already exists',
|
||||
sysv_service, sysv_service
|
||||
)
|
||||
continue
|
||||
ret.append(sysv_service)
|
||||
return ret
|
||||
|
||||
|
||||
def _has_sysv_exec():
|
||||
'''
|
||||
Return the current runlevel
|
||||
'''
|
||||
if 'systemd._has_sysv_exec' not in __context__:
|
||||
contextkey = 'systemd._has_sysv_exec'
|
||||
if contextkey not in __context__:
|
||||
try:
|
||||
__context__['systemd._has_sysv_exec'] = bool(_get_service_exec())
|
||||
except(
|
||||
salt.exceptions.CommandExecutionError,
|
||||
salt.exceptions.CommandNotFoundError
|
||||
):
|
||||
__context__['systemd._has_sysv_exec'] = False
|
||||
return __context__['systemd._has_sysv_exec']
|
||||
__context__[contextkey] = bool(_get_service_exec())
|
||||
except (CommandExecutionError, CommandNotFoundError):
|
||||
__context__[contextkey] = False
|
||||
return __context__[contextkey]
|
||||
|
||||
|
||||
def _sysv_exists(name):
|
||||
script = '/etc/init.d/{0}'.format(name)
|
||||
return os.access(script, os.X_OK)
|
||||
|
||||
|
||||
def _service_is_sysv(name):
|
||||
def _runlevel():
|
||||
'''
|
||||
A System-V style service will have a control script in
|
||||
/etc/init.d.
|
||||
Return True only if the service doesnt also provide a systemd unit file.
|
||||
Return the current runlevel
|
||||
'''
|
||||
return (_has_sysv_exec() and
|
||||
name in _get_all_units() and
|
||||
name not in _get_all_unit_files() and
|
||||
_sysv_exists(name))
|
||||
contextkey = 'systemd._runlevel'
|
||||
if contextkey in __context__:
|
||||
return __context__[contextkey]
|
||||
out = __salt__['cmd.run']('runlevel', python_shell=False)
|
||||
try:
|
||||
ret = out.split()[1]
|
||||
except IndexError:
|
||||
# The runlevel is unknown, return the default
|
||||
ret = _default_runlevel()
|
||||
__context__[contextkey] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def _sysv_is_disabled(name):
|
||||
def _systemctl_cmd(action, name=None):
|
||||
'''
|
||||
A System-V style service is assumed disabled if there is no
|
||||
start-up link (starts with "S") to its script in /etc/init.d in
|
||||
the current runlevel.
|
||||
Build a systemctl command line. Treat unit names without one
|
||||
of the valid suffixes as a service.
|
||||
'''
|
||||
return not bool(glob.glob('/etc/rc{0}.d/S*{1}'.format(_runlevel(), name)))
|
||||
ret = ['systemctl']
|
||||
if isinstance(action, six.string_types):
|
||||
action = shlex.split(action)
|
||||
ret.extend(action)
|
||||
if name is not None:
|
||||
ret.append(_canonical_unit_name(name))
|
||||
if 'status' in ret:
|
||||
ret.extend(['-n', '0'])
|
||||
return ret
|
||||
|
||||
|
||||
def _sysv_is_enabled(name):
|
||||
def _systemctl_status(name):
|
||||
'''
|
||||
Assume that if a System-V style service is not disabled then it
|
||||
must be enabled.
|
||||
Helper function which leverages __context__ to keep from running 'systemctl
|
||||
status' more than once.
|
||||
'''
|
||||
return not _sysv_is_disabled(name)
|
||||
contextkey = 'systemd._systemctl_status.%s' % name
|
||||
if contextkey in __context__:
|
||||
return __context__[contextkey]
|
||||
__context__[contextkey] = __salt__['cmd.run'](
|
||||
_systemctl_cmd('status', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True
|
||||
)
|
||||
return __context__[contextkey]
|
||||
|
||||
|
||||
def _sysv_enabled(name):
|
||||
'''
|
||||
A System-V style service is assumed disabled if the "startup" symlink
|
||||
(starts with "S") to its script is found in /etc/init.d in the current
|
||||
runlevel.
|
||||
'''
|
||||
return bool(glob.glob('/etc/rc%s.d/S*%s' % (_runlevel(), name)))
|
||||
|
||||
|
||||
def _untracked_custom_unit_found(name):
|
||||
'''
|
||||
If the passed service name is not available, but a unit file exist in
|
||||
/etc/systemd/system, return True. Otherwise, return False.
|
||||
'''
|
||||
unit_path = os.path.join('/etc/systemd/system',
|
||||
_canonical_unit_name(name))
|
||||
return os.access(unit_path, os.R_OK) and not available(name)
|
||||
|
||||
|
||||
def _unit_file_changed(name):
|
||||
'''
|
||||
Returns True if systemctl reports that the unit file has changed, otherwise
|
||||
returns False.
|
||||
'''
|
||||
return "'systemctl daemon-reload'" in _systemctl_status(name).lower()
|
||||
|
||||
|
||||
def systemctl_reload():
|
||||
'''
|
||||
.. versionadded:: 0.15.0
|
||||
|
||||
Reloads systemctl, an action needed whenever unit files are updated.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' service.systemctl_reload
|
||||
'''
|
||||
out = __salt__['cmd.run_all'](
|
||||
_systemctl_cmd('--system daemon-reload'),
|
||||
python_shell=False,
|
||||
redirect_stderr=True
|
||||
)
|
||||
if out['retcode'] != 0:
|
||||
raise CommandExecutionError(
|
||||
'Problem performing systemctl daemon-reload: %s' % out['stdout']
|
||||
)
|
||||
_clear_context()
|
||||
return True
|
||||
|
||||
|
||||
def get_enabled():
|
||||
|
@ -285,21 +320,33 @@ def get_enabled():
|
|||
|
||||
salt '*' service.get_enabled
|
||||
'''
|
||||
ret = []
|
||||
units = _get_all_unit_files()
|
||||
services = _get_all_units()
|
||||
for name, state in six.iteritems(units):
|
||||
if state == 'enabled':
|
||||
ret.append(name)
|
||||
for name, state in six.iteritems(services):
|
||||
if name in units:
|
||||
ret = set()
|
||||
# Get enabled systemd units. Can't use --state=enabled here because it's
|
||||
# not present until systemd 216.
|
||||
out = __salt__['cmd.run'](
|
||||
_systemctl_cmd('--full --no-legend --no-pager list-unit-files'),
|
||||
python_shell=False,
|
||||
ignore_retcode=True,
|
||||
)
|
||||
for line in salt.utils.itertools.split(out, '\n'):
|
||||
try:
|
||||
fullname, unit_state = line.strip().split(None, 1)
|
||||
except ValueError:
|
||||
continue
|
||||
# performance; if the legacy initscript doesnt exists,
|
||||
# don't contiue up with systemd query
|
||||
if not _service_is_sysv(name):
|
||||
else:
|
||||
if unit_state != 'enabled':
|
||||
continue
|
||||
try:
|
||||
unit_name, unit_type = fullname.rsplit('.', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if _sysv_is_enabled(name):
|
||||
ret.append(name)
|
||||
if unit_type in VALID_UNIT_TYPES:
|
||||
ret.add(unit_name if unit_type == 'service' else fullname)
|
||||
|
||||
# Add in any sysvinit services that are enabled
|
||||
ret.update(set(
|
||||
[x for x in _get_sysv_services() if _sysv_enabled(x)]
|
||||
))
|
||||
return sorted(ret)
|
||||
|
||||
|
||||
|
@ -313,12 +360,72 @@ def get_disabled():
|
|||
|
||||
salt '*' service.get_disabled
|
||||
'''
|
||||
ret = []
|
||||
known_services = _get_all_unit_files()
|
||||
known_services.update(_get_all_legacy_init_scripts())
|
||||
for name, state in six.iteritems(known_services):
|
||||
if state == 'disabled':
|
||||
ret.append(name)
|
||||
ret = set()
|
||||
# Get disabled systemd units. Can't use --state=disabled here because it's
|
||||
# not present until systemd 216.
|
||||
out = __salt__['cmd.run'](
|
||||
_systemctl_cmd('--full --no-legend --no-pager list-unit-files'),
|
||||
python_shell=False,
|
||||
ignore_retcode=True,
|
||||
)
|
||||
for line in salt.utils.itertools.split(out, '\n'):
|
||||
try:
|
||||
fullname, unit_state = line.strip().split(None, 1)
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
if unit_state != 'disabled':
|
||||
continue
|
||||
try:
|
||||
unit_name, unit_type = fullname.rsplit('.', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if unit_type in VALID_UNIT_TYPES:
|
||||
ret.add(unit_name if unit_type == 'service' else fullname)
|
||||
|
||||
# Add in any sysvinit services that are disabled
|
||||
ret.update(set(
|
||||
[x for x in _get_sysv_services() if not _sysv_enabled(x)]
|
||||
))
|
||||
return sorted(ret)
|
||||
|
||||
|
||||
def get_static():
|
||||
'''
|
||||
.. versionadded:: 2015.8.5
|
||||
|
||||
Return a list of all static services
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' service.get_static
|
||||
'''
|
||||
ret = set()
|
||||
# Get static systemd units. Can't use --state=static here because it's
|
||||
# not present until systemd 216.
|
||||
out = __salt__['cmd.run'](
|
||||
_systemctl_cmd('--full --no-legend --no-pager list-unit-files'),
|
||||
python_shell=False,
|
||||
ignore_retcode=True,
|
||||
)
|
||||
for line in salt.utils.itertools.split(out, '\n'):
|
||||
try:
|
||||
fullname, unit_state = line.strip().split(None, 1)
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
if unit_state != 'static':
|
||||
continue
|
||||
try:
|
||||
unit_name, unit_type = fullname.rsplit('.', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if unit_type in VALID_UNIT_TYPES:
|
||||
ret.add(unit_name if unit_type == 'service' else fullname)
|
||||
|
||||
# sysvinit services cannot be static
|
||||
return sorted(ret)
|
||||
|
||||
|
||||
|
@ -332,14 +439,17 @@ def get_all():
|
|||
|
||||
salt '*' service.get_all
|
||||
'''
|
||||
return sorted(set(list(_get_all_units().keys()) + list(_get_all_unit_files().keys())
|
||||
+ list(_get_all_legacy_init_scripts().keys())))
|
||||
ret = _get_systemd_services()
|
||||
ret.update(set(_get_sysv_services()))
|
||||
return sorted(ret)
|
||||
|
||||
|
||||
def available(name):
|
||||
'''
|
||||
Check that the given service is available taking into account
|
||||
template units.
|
||||
.. versionadded:: 0.10.4
|
||||
|
||||
Check that the given service is available taking into account template
|
||||
units.
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -347,24 +457,26 @@ def available(name):
|
|||
|
||||
salt '*' service.available sshd
|
||||
'''
|
||||
name = _canonical_template_unit_name(name)
|
||||
if name.endswith('.service'):
|
||||
name = name[:-8] # len('.service') is 8
|
||||
units = get_all()
|
||||
if name in units:
|
||||
return True
|
||||
elif '@' in name:
|
||||
templatename = name[:name.find('@') + 1]
|
||||
return templatename in units
|
||||
out = _systemctl_status(name).lower()
|
||||
for line in salt.utils.itertools.split(out, '\n'):
|
||||
match = re.match(r'\s+loaded:\s+(\S+)', line)
|
||||
if match:
|
||||
ret = match.group(1) != 'not-found'
|
||||
break
|
||||
else:
|
||||
return False
|
||||
raise CommandExecutionError(
|
||||
'Failed to get information on unit \'%s\'' % name
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
def missing(name):
|
||||
'''
|
||||
The inverse of service.available.
|
||||
Returns ``True`` if the specified service is not available, otherwise returns
|
||||
``False``.
|
||||
.. versionadded:: 2014.1.0
|
||||
|
||||
The inverse of :py:func:`service.available
|
||||
<salt.modules.systemd.available>`. Returns ``True`` if the specified
|
||||
service is not available, otherwise returns ``False``.
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -377,6 +489,8 @@ def missing(name):
|
|||
|
||||
def unmask(name):
|
||||
'''
|
||||
.. versionadded:: 2015.5.0
|
||||
|
||||
Unmask the specified service with systemd
|
||||
|
||||
CLI Example:
|
||||
|
@ -385,32 +499,64 @@ def unmask(name):
|
|||
|
||||
salt '*' service.unmask <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
return not (__salt__['cmd.retcode'](_systemctl_cmd('unmask', name))
|
||||
or __salt__['cmd.retcode'](_systemctl_cmd('unmask --runtime', name)))
|
||||
_check_for_unit_changes(name)
|
||||
mask_status = masked(name)
|
||||
if not mask_status:
|
||||
log.debug('Service \'%s\' is not masked', name)
|
||||
return True
|
||||
|
||||
cmd = 'unmask --runtime' if 'runtime' in mask_status else 'unmask'
|
||||
out = __salt__['cmd.run_all'](_systemctl_cmd(cmd, name),
|
||||
python_shell=False,
|
||||
redirect_stderr=True)
|
||||
|
||||
if out['retcode'] != 0:
|
||||
raise CommandExecutionError('Failed to unmask service \'%s\'' % name)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def mask(name):
|
||||
def mask(name, runtime=False):
|
||||
'''
|
||||
.. versionadded:: 2015.5.0
|
||||
|
||||
Mask the specified service with systemd
|
||||
|
||||
runtime : False
|
||||
Set to ``True`` to mask this service only until the next reboot
|
||||
|
||||
.. versionadded:: 2015.8.5
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' service.mask <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('mask', name))
|
||||
_check_for_unit_changes(name)
|
||||
|
||||
cmd = 'mask --runtime' if runtime else 'mask'
|
||||
out = __salt__['cmd.run_all'](_systemctl_cmd(cmd, name),
|
||||
python_shell=False,
|
||||
redirect_stderr=True)
|
||||
|
||||
if out['retcode'] != 0:
|
||||
raise CommandExecutionError('Failed to mask service \'%s\'' % name)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def masked(name):
|
||||
'''
|
||||
Return if the named service is masked.
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
.. versionchanged:: 2015.8.5
|
||||
The return data for this function has changed. If the service is
|
||||
masked, the return value will now be the output of the ``systemctl
|
||||
is-enabled`` command (so that a persistent mask can be distinguished
|
||||
from a runtime mask). If the service is not masked, then ``False`` will
|
||||
be returned.
|
||||
|
||||
Check whether or not a service is masked
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -418,10 +564,13 @@ def masked(name):
|
|||
|
||||
salt '*' service.masked <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
out = __salt__['cmd.run_all'](_systemctl_cmd('is-enabled', name), ignore_retcode=True)
|
||||
return out['retcode'] == 1 and 'masked' in out['stdout']
|
||||
_check_for_unit_changes(name)
|
||||
out = __salt__['cmd.run'](
|
||||
_systemctl_cmd('is-enabled', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True,
|
||||
)
|
||||
return out if 'masked' in out else False
|
||||
|
||||
|
||||
def start(name):
|
||||
|
@ -450,9 +599,9 @@ def stop(name):
|
|||
|
||||
salt '*' service.stop <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('stop', name))
|
||||
_check_for_unit_changes(name)
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('stop', name),
|
||||
python_shell=False) == 0
|
||||
|
||||
|
||||
def restart(name):
|
||||
|
@ -465,10 +614,10 @@ def restart(name):
|
|||
|
||||
salt '*' service.restart <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
_check_for_unit_changes(name)
|
||||
unmask(name)
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('restart', name))
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('restart', name),
|
||||
python_shell=False) == 0
|
||||
|
||||
|
||||
def reload_(name):
|
||||
|
@ -481,14 +630,16 @@ def reload_(name):
|
|||
|
||||
salt '*' service.reload <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
_check_for_unit_changes(name)
|
||||
unmask(name)
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('reload', name))
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('reload', name),
|
||||
python_shell=False) == 0
|
||||
|
||||
|
||||
def force_reload(name):
|
||||
'''
|
||||
.. versionadded:: 0.12.0
|
||||
|
||||
Force-reload the specified service with systemd
|
||||
|
||||
CLI Example:
|
||||
|
@ -497,15 +648,15 @@ def force_reload(name):
|
|||
|
||||
salt '*' service.force_reload <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
_check_for_unit_changes(name)
|
||||
unmask(name)
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('force-reload', name))
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('force-reload', name),
|
||||
python_shell=False) == 0
|
||||
|
||||
|
||||
# The unused sig argument is required to maintain consistency in the state
|
||||
# system
|
||||
def status(name, sig=None):
|
||||
# The unused sig argument is required to maintain consistency with the API
|
||||
# established by Salt's service management states.
|
||||
def status(name, sig=None): # pylint: disable=unused-argument
|
||||
'''
|
||||
Return the status for a service via systemd, returns a bool
|
||||
whether the service is running.
|
||||
|
@ -516,13 +667,15 @@ def status(name, sig=None):
|
|||
|
||||
salt '*' service.status <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('is-active', name),
|
||||
ignore_retcode=True)
|
||||
_check_for_unit_changes(name)
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('is-active', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0
|
||||
|
||||
|
||||
def enable(name, **kwargs):
|
||||
# The unused kwargs argument is required to maintain consistency with the API
|
||||
# established by Salt's service management states.
|
||||
def enable(name, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Enable the named service to start when the system boots
|
||||
|
||||
|
@ -532,17 +685,21 @@ def enable(name, **kwargs):
|
|||
|
||||
salt '*' service.enable <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
_check_for_unit_changes(name)
|
||||
unmask(name)
|
||||
if _service_is_sysv(name):
|
||||
executable = _get_service_exec()
|
||||
cmd = '{0} -f {1} defaults 99'.format(executable, name)
|
||||
return not __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('enable', name))
|
||||
if name in _get_sysv_services():
|
||||
cmd = [_get_service_exec(), '-f', name, 'defaults', '99']
|
||||
return __salt__['cmd.retcode'](cmd,
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('enable', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0
|
||||
|
||||
|
||||
def disable(name, **kwargs):
|
||||
# The unused kwargs argument is required to maintain consistency with the API
|
||||
# established by Salt's service management states.
|
||||
def disable(name, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Disable the named service to not start when the system boots
|
||||
|
||||
|
@ -552,38 +709,20 @@ def disable(name, **kwargs):
|
|||
|
||||
salt '*' service.disable <service name>
|
||||
'''
|
||||
if _untracked_custom_unit_found(name) or _unit_file_changed(name):
|
||||
systemctl_reload()
|
||||
if _service_is_sysv(name):
|
||||
executable = _get_service_exec()
|
||||
cmd = [executable, '-f', name, 'remove']
|
||||
return not __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
return not __salt__['cmd.retcode'](_systemctl_cmd('disable', name))
|
||||
_check_for_unit_changes(name)
|
||||
if name in _get_sysv_services():
|
||||
cmd = [_get_service_exec(), '-f', name, 'remove']
|
||||
return __salt__['cmd.retcode'](cmd,
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0
|
||||
return __salt__['cmd.retcode'](_systemctl_cmd('disable', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0
|
||||
|
||||
|
||||
def _templated_instance_enabled(name):
|
||||
'''
|
||||
Services instantiated based on templates can not be checked with
|
||||
systemctl is-enabled. Presence of the actual symlinks is checked
|
||||
as a fall-back.
|
||||
'''
|
||||
if '@' not in name:
|
||||
return False
|
||||
find_unit_by_name = 'find {0} -name {1} -type l -print -quit'
|
||||
return len(__salt__['cmd.run'](
|
||||
find_unit_by_name.format(LOCAL_CONFIG_PATH,
|
||||
_canonical_unit_name(name))
|
||||
))
|
||||
|
||||
|
||||
def _enabled(name):
|
||||
is_enabled = \
|
||||
not __salt__['cmd.retcode'](_systemctl_cmd('is-enabled', name),
|
||||
ignore_retcode=True)
|
||||
return is_enabled or _templated_instance_enabled(name) or _sysv_is_enabled(name)
|
||||
|
||||
|
||||
def enabled(name, **kwargs):
|
||||
# The unused kwargs argument is required to maintain consistency with the API
|
||||
# established by Salt's service management states.
|
||||
def enabled(name, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Return if the named service is enabled to start on boot
|
||||
|
||||
|
@ -593,7 +732,25 @@ def enabled(name, **kwargs):
|
|||
|
||||
salt '*' service.enabled <service name>
|
||||
'''
|
||||
return _enabled(name)
|
||||
# Try 'systemctl is-enabled' first, then look for a symlink created by
|
||||
# systemctl (older systemd releases did not support using is-enabled to
|
||||
# check templated services), and lastly check for a sysvinit service.
|
||||
if __salt__['cmd.retcode'](_systemctl_cmd('is-enabled', name),
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0:
|
||||
return True
|
||||
elif '@' in name:
|
||||
# On older systemd releases, templated services could not be checked
|
||||
# with ``systemctl is-enabled``. As a fallback, look for the symlinks
|
||||
# created by systemctl when enabling templated services.
|
||||
cmd = ['find', LOCAL_CONFIG_PATH, '-name', name,
|
||||
'-type', 'l', '-print', '-quit']
|
||||
# If the find command returns any matches, there will be output and the
|
||||
# string will be non-empty.
|
||||
if bool(__salt__['cmd.run'](cmd, python_shell=False)):
|
||||
return True
|
||||
else:
|
||||
return _sysv_enabled(name)
|
||||
|
||||
|
||||
def disabled(name):
|
||||
|
@ -606,11 +763,13 @@ def disabled(name):
|
|||
|
||||
salt '*' service.disabled <service name>
|
||||
'''
|
||||
return not _enabled(name) and not _sysv_is_enabled(name)
|
||||
return not enabled(name)
|
||||
|
||||
|
||||
def show(name):
|
||||
'''
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Show properties of one or more units/jobs or the manager
|
||||
|
||||
CLI Example:
|
||||
|
@ -638,6 +797,8 @@ def show(name):
|
|||
|
||||
def execs():
|
||||
'''
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Return a list of all files specified as ``ExecStart`` for all services.
|
||||
|
||||
CLI Example:
|
||||
|
|
|
@ -21,16 +21,18 @@ Support for YUM/DNF
|
|||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import copy
|
||||
import fnmatch
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
from distutils.version import LooseVersion as _LooseVersion # pylint: disable=no-name-in-module,import-error
|
||||
|
||||
# Import 3rd-party libs
|
||||
# pylint: disable=import-error,redefined-builtin
|
||||
import salt.ext.six as six
|
||||
from salt.ext.six.moves import shlex_quote as _cmd_quote
|
||||
# Import 3rd-party libs
|
||||
from salt.ext import six
|
||||
from salt.ext.six.moves import zip
|
||||
|
||||
try:
|
||||
import yum
|
||||
|
@ -44,10 +46,11 @@ try:
|
|||
HAS_RPMUTILS = True
|
||||
except ImportError:
|
||||
HAS_RPMUTILS = False
|
||||
# pylint: enable=import-error
|
||||
# pylint: enable=import-error,redefined-builtin
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.itertools
|
||||
import salt.utils.decorators as decorators
|
||||
import salt.utils.pkg.rpm
|
||||
from salt.exceptions import (
|
||||
|
@ -79,13 +82,29 @@ def __virtual__():
|
|||
return False
|
||||
|
||||
|
||||
def _strip_headers(output, *args):
|
||||
if not args:
|
||||
args_lc = ('installed packages',
|
||||
'available packages',
|
||||
'updated packages',
|
||||
'upgraded packages')
|
||||
else:
|
||||
args_lc = [x.lower() for x in args]
|
||||
ret = ''
|
||||
for line in salt.utils.itertools.split(output, '\n'):
|
||||
if line.lower() not in args_lc:
|
||||
ret += line + '\n'
|
||||
return ret
|
||||
|
||||
|
||||
def _yum():
|
||||
'''
|
||||
return yum or dnf depending on version
|
||||
'''
|
||||
contextkey = 'yum_bin'
|
||||
if contextkey not in __context__:
|
||||
if 'fedora' in __grains__['os'].lower() and int(__grains__['osrelease']) >= 22:
|
||||
if 'fedora' in __grains__['os'].lower() \
|
||||
and int(__grains__['osrelease']) >= 22:
|
||||
__context__[contextkey] = 'dnf'
|
||||
else:
|
||||
__context__[contextkey] = 'yum'
|
||||
|
@ -159,6 +178,41 @@ def _check_repoquery():
|
|||
raise CommandExecutionError('Unable to install yum-utils')
|
||||
|
||||
|
||||
def _yum_pkginfo(output):
|
||||
'''
|
||||
Parse yum/dnf output (which could contain irregular line breaks if package
|
||||
names are long) retrieving the name, version, etc., and return a list of
|
||||
pkginfo namedtuples.
|
||||
'''
|
||||
cur = {}
|
||||
keys = itertools.cycle(('name', 'version', 'repoid'))
|
||||
values = salt.utils.itertools.split(_strip_headers(output))
|
||||
osarch = __grains__['osarch']
|
||||
for (key, value) in zip(keys, values):
|
||||
if key == 'name':
|
||||
try:
|
||||
cur['name'], cur['arch'] = value.rsplit('.', 1)
|
||||
except ValueError:
|
||||
cur['name'] = value
|
||||
cur['arch'] = osarch
|
||||
cur['name'] = salt.utils.pkg.rpm.resolve_name(cur['name'],
|
||||
cur['arch'],
|
||||
osarch)
|
||||
else:
|
||||
if key == 'repoid':
|
||||
# Installed packages show a '@' at the beginning
|
||||
value = value.lstrip('@')
|
||||
cur[key] = value
|
||||
if key == 'repoid':
|
||||
# We're done with this package, create the pkginfo namedtuple
|
||||
pkginfo = salt.utils.pkg.rpm.pkginfo(**cur)
|
||||
# Clear the dict for the next package
|
||||
cur = {}
|
||||
# Yield the namedtuple
|
||||
if pkginfo is not None:
|
||||
yield pkginfo
|
||||
|
||||
|
||||
def _check_versionlock():
|
||||
'''
|
||||
Ensure that the appropriate versionlock plugin is present
|
||||
|
@ -184,23 +238,19 @@ def _repoquery(repoquery_args,
|
|||
'''
|
||||
_check_repoquery()
|
||||
if _yum() == 'dnf':
|
||||
cmd = 'dnf repoquery --quiet --queryformat {0} {1}'.format(
|
||||
_cmd_quote(
|
||||
query_format.replace('-%{VERSION}_', '-%{EPOCH}:%{VERSION}_')
|
||||
),
|
||||
repoquery_args
|
||||
)
|
||||
cmd = ['dnf', 'repoquery', '--quiet', '--queryformat',
|
||||
query_format.replace('-%{VERSION}_', '-%{EPOCH}:%{VERSION}_')]
|
||||
else:
|
||||
cmd = 'repoquery --plugins --queryformat {0} {1}'.format(
|
||||
_cmd_quote(query_format), repoquery_args
|
||||
)
|
||||
cmd = ['repoquery', '--plugins', '--queryformat', query_format]
|
||||
|
||||
cmd.extend(repoquery_args)
|
||||
call = __salt__['cmd.run_all'](cmd, output_loglevel='trace')
|
||||
if call['retcode'] != 0:
|
||||
comment = ''
|
||||
# When checking for packages some yum modules return data via
|
||||
# stderr that don't cause non-zero return codes. j perfect
|
||||
# example of this is when spacewalk is installed but not yet
|
||||
# registered. We should ignore those when getting pkginfo.
|
||||
# When checking for packages some yum modules return data via stderr
|
||||
# that don't cause non-zero return codes. A perfect example of this is
|
||||
# when spacewalk is installed but not yet registered. We should ignore
|
||||
# those when getting pkginfo.
|
||||
if 'stderr' in call and not salt.utils.is_true(ignore_stderr):
|
||||
comment += call['stderr']
|
||||
if 'stdout' in call:
|
||||
|
@ -230,7 +280,7 @@ def _get_repo_options(**kwargs):
|
|||
fromrepo = repo
|
||||
|
||||
use_dnf_repoquery = kwargs.get('repoquery', False) and _yum() == 'dnf'
|
||||
repo_arg = []
|
||||
ret = []
|
||||
if fromrepo:
|
||||
log.info('Restricting to repo \'{0}\''.format(fromrepo))
|
||||
if use_dnf_repoquery:
|
||||
|
@ -239,9 +289,9 @@ def _get_repo_options(**kwargs):
|
|||
# This is good, because --repo does not work at all (see
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1299261 for more
|
||||
# information). Using --repoid here so this will actually work.
|
||||
repo_arg.append('--repoid=\'{0}\''.format(fromrepo))
|
||||
ret.append('--repoid=\'{0}\''.format(fromrepo))
|
||||
else:
|
||||
repo_arg.append(
|
||||
ret.append(
|
||||
'--disablerepo=\'*\' --enablerepo=\'{0}\''.format(fromrepo)
|
||||
)
|
||||
else:
|
||||
|
@ -252,7 +302,7 @@ def _get_repo_options(**kwargs):
|
|||
)
|
||||
else:
|
||||
log.info('Disabling repo \'{0}\''.format(disablerepo))
|
||||
repo_arg.append('--disablerepo=\'{0}\''.format(disablerepo))
|
||||
ret.append('--disablerepo=\'{0}\''.format(disablerepo))
|
||||
if enablerepo:
|
||||
if use_dnf_repoquery:
|
||||
log.warning(
|
||||
|
@ -260,8 +310,8 @@ def _get_repo_options(**kwargs):
|
|||
)
|
||||
else:
|
||||
log.info('Enabling repo \'{0}\''.format(enablerepo))
|
||||
repo_arg.append('--enablerepo=\'{0}\''.format(enablerepo))
|
||||
return ' '.join(repo_arg)
|
||||
ret.append('--enablerepo=\'{0}\''.format(enablerepo))
|
||||
return ret
|
||||
|
||||
|
||||
def _get_excludes_option(**kwargs):
|
||||
|
@ -276,11 +326,11 @@ def _get_excludes_option(**kwargs):
|
|||
log.warning(
|
||||
'Ignoring disableexcludes, not supported in dnf repoquery'
|
||||
)
|
||||
return ''
|
||||
return []
|
||||
else:
|
||||
log.info('Disabling excludes for \'{0}\''.format(disable_excludes))
|
||||
return '--disableexcludes=\'{0}\''.format(disable_excludes)
|
||||
return ''
|
||||
return ['--disableexcludes=\'{0}\''.format(disable_excludes)]
|
||||
return []
|
||||
|
||||
|
||||
def _get_branch_option(**kwargs):
|
||||
|
@ -386,13 +436,13 @@ def _normalize_basedir(basedir=None):
|
|||
|
||||
Returns a list of directories.
|
||||
'''
|
||||
if basedir is None:
|
||||
basedir = []
|
||||
|
||||
# if we are passed a string (for backward compatibility), convert to a list
|
||||
if isinstance(basedir, six.string_types):
|
||||
basedir = [x.strip() for x in basedir.split(',')]
|
||||
|
||||
if basedir is None:
|
||||
basedir = []
|
||||
|
||||
# nothing specified, so use the reposdir option as the default
|
||||
if not basedir:
|
||||
basedir = _get_yum_config_value('reposdir')
|
||||
|
@ -460,18 +510,13 @@ def latest_version(*names, **kwargs):
|
|||
# Initialize the return dict with empty strings, and populate namearch_map.
|
||||
# namearch_map will provide a means of distinguishing between multiple
|
||||
# matches for the same package name, for example a target of 'glibc' on an
|
||||
# x86_64 arch would return both x86_64 and i686 versions when searched
|
||||
# using repoquery:
|
||||
#
|
||||
# $ repoquery --all --pkgnarrow=available glibc
|
||||
# glibc-0:2.12-1.132.el6.i686
|
||||
# glibc-0:2.12-1.132.el6.x86_64
|
||||
# x86_64 arch would return both x86_64 and i686 versions.
|
||||
#
|
||||
# Note that the logic in the for loop below would place the osarch into the
|
||||
# map for noarch packages, but those cases are accounted for when iterating
|
||||
# through the repoquery results later on. If the repoquery match for that
|
||||
# package is a noarch, then the package is assumed to be noarch, and the
|
||||
# namearch_map is ignored.
|
||||
# through the 'yum list' results later on. If the match for that package is
|
||||
# a noarch, then the package is assumed to be noarch, and the namearch_map
|
||||
# is ignored.
|
||||
ret = {}
|
||||
namearch_map = {}
|
||||
for name in names:
|
||||
|
@ -491,61 +536,47 @@ def latest_version(*names, **kwargs):
|
|||
if refresh:
|
||||
refresh_db(**kwargs)
|
||||
|
||||
def _query_pkgs(name, pkgs):
|
||||
'''
|
||||
Return the newest available match from the _repoquery_pkginfo() output
|
||||
'''
|
||||
matches = []
|
||||
for pkg in (x for x in pkgs if x.name == name):
|
||||
if pkg.arch == 'noarch' or pkg.arch == namearch_map[name] \
|
||||
or salt.utils.pkg.rpm.check_32(pkg.arch):
|
||||
matches.append(pkg.version)
|
||||
sorted_matches = sorted(
|
||||
[_LooseVersion(x) for x in matches],
|
||||
# Get available versions for specified package(s)
|
||||
cmd = [_yum(), '--quiet']
|
||||
cmd.extend(repo_arg)
|
||||
cmd.extend(exclude_arg)
|
||||
cmd.extend(['list', 'available'])
|
||||
cmd.extend(names)
|
||||
out = __salt__['cmd.run_all'](cmd,
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
if out['retcode'] != 0:
|
||||
if out['stderr']:
|
||||
# Check first if this is just a matter of the packages being
|
||||
# up-to-date.
|
||||
cur_pkgs = list_pkgs()
|
||||
if not all([x in cur_pkgs for x in names]):
|
||||
log.error(
|
||||
'Problem encountered getting latest version for the '
|
||||
'following package(s): {0}. Stderr follows: \n{1}'.format(
|
||||
', '.join(names),
|
||||
out['stderr']
|
||||
)
|
||||
)
|
||||
updates = []
|
||||
else:
|
||||
# Sort by version number (highest to lowest) for loop below
|
||||
updates = sorted(
|
||||
_yum_pkginfo(out['stdout']),
|
||||
key=lambda pkginfo: _LooseVersion(pkginfo.version),
|
||||
reverse=True
|
||||
)
|
||||
try:
|
||||
return sorted_matches[0].vstring
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
if _yum() == 'dnf':
|
||||
avail_pkgs = _repoquery_pkginfo(
|
||||
'{0} --available {1}'.format(repo_arg, ' '.join(names))
|
||||
)
|
||||
# When using 'dnf repoquery --available', all available versions are
|
||||
# returned, irrespective of whether or not they are installed. This is
|
||||
# different from how yum-utils' version of repoquery works.
|
||||
all_pkgs = list_pkgs(versions_as_list=True)
|
||||
for name in names:
|
||||
# Get newest available version of package
|
||||
newest_avail = _query_pkgs(name, avail_pkgs)
|
||||
if newest_avail is None:
|
||||
# No matches, no need to check if pacakge is already installed
|
||||
continue
|
||||
# Get newest installed version of package
|
||||
try:
|
||||
cver = all_pkgs.get(name, [])[-1]
|
||||
except IndexError:
|
||||
cver = None
|
||||
if cver is None \
|
||||
or salt.utils.compare_versions(ver1=newest_avail,
|
||||
oper='>',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
ret[name] = newest_avail
|
||||
else:
|
||||
avail_pkgs = _repoquery_pkginfo(
|
||||
'{0} {1} --pkgnarrow=available {2}'.format(
|
||||
repo_arg,
|
||||
exclude_arg,
|
||||
' '.join(names)
|
||||
)
|
||||
)
|
||||
for name in names:
|
||||
newest_avail = _query_pkgs(name, avail_pkgs)
|
||||
if newest_avail is not None:
|
||||
ret[name] = newest_avail
|
||||
for name in names:
|
||||
for pkg in (x for x in updates if x.name == name):
|
||||
if pkg.arch == 'noarch' or pkg.arch == namearch_map[name] \
|
||||
or salt.utils.pkg.rpm.check_32(pkg.arch):
|
||||
ret[name] = pkg.version
|
||||
# no need to check another match, if there was one
|
||||
break
|
||||
else:
|
||||
ret[name] = ''
|
||||
|
||||
# Return a string if only one package name passed
|
||||
if len(names) == 1:
|
||||
|
@ -682,6 +713,14 @@ def list_repo_pkgs(*args, **kwargs):
|
|||
can be passed and the results will be filtered to packages matching those
|
||||
names. This is recommended as it speeds up the function considerably.
|
||||
|
||||
.. warning::
|
||||
Running this function on RHEL/CentOS 6 and earlier will be more
|
||||
resource-intensive, as the version of yum that ships with older
|
||||
RHEL/CentOS has no yum subcommand for listing packages from a
|
||||
repository. Thus, a ``yum list installed`` and ``yum list available``
|
||||
are run, which generates a lot of output, which must then be analyzed
|
||||
to determine which package information to include in the return data.
|
||||
|
||||
This function can be helpful in discovering the version or repo to specify
|
||||
in a :mod:`pkg.installed <salt.states.pkg.installed>` state.
|
||||
|
||||
|
@ -732,21 +771,70 @@ def list_repo_pkgs(*args, **kwargs):
|
|||
)
|
||||
|
||||
ret = {}
|
||||
for repo in repos:
|
||||
if _yum() == 'dnf':
|
||||
# As of 0.1.15, dnf repoquery does not support showing duplicates
|
||||
repoquery_cmd = '--repoid="{0}"'.format(repo)
|
||||
else:
|
||||
repoquery_cmd = '--all --repoid="{0}" --show-duplicates'.format(repo)
|
||||
|
||||
def _check_args(args, name):
|
||||
'''
|
||||
Do glob matching on args and return True if a match was found.
|
||||
Otherwise, return False
|
||||
'''
|
||||
for arg in args:
|
||||
repoquery_cmd += ' "{0}"'.format(arg)
|
||||
all_pkgs = _repoquery_pkginfo(repoquery_cmd)
|
||||
for pkg in all_pkgs:
|
||||
if fnmatch.fnmatch(name, arg):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _no_repository_packages():
|
||||
'''
|
||||
Check yum version, the repository-packages subcommand is only in
|
||||
3.4.3 and newer.
|
||||
'''
|
||||
if _yum() == 'yum':
|
||||
yum_version = _LooseVersion(
|
||||
__salt__['cmd.run'](
|
||||
['yum', '--version'],
|
||||
python_shell=False
|
||||
).splitlines()[0].strip()
|
||||
)
|
||||
return yum_version < _LooseVersion('3.4.3')
|
||||
return False
|
||||
|
||||
def _parse_output(output, strict=False):
|
||||
for pkg in _yum_pkginfo(output):
|
||||
if strict and (pkg.repoid not in repos
|
||||
or not _check_args(args, pkg.name)):
|
||||
continue
|
||||
repo_dict = ret.setdefault(pkg.repoid, {})
|
||||
version_list = repo_dict.setdefault(pkg.name, [])
|
||||
version_list.append(pkg.version)
|
||||
version_list = repo_dict.setdefault(pkg.name, set())
|
||||
version_list.add(pkg.version)
|
||||
|
||||
if _no_repository_packages():
|
||||
cmd_prefix = ['yum', '--quiet', 'list']
|
||||
for pkg_src in ('installed', 'available'):
|
||||
# Check installed packages first
|
||||
out = __salt__['cmd.run_all'](
|
||||
cmd_prefix + [pkg_src],
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False
|
||||
)
|
||||
if out['retcode'] == 0:
|
||||
_parse_output(out['stdout'], strict=True)
|
||||
else:
|
||||
for repo in repos:
|
||||
cmd = [_yum(), '--quiet', 'repository-packages', repo,
|
||||
'list', '--showduplicates']
|
||||
# Can't concatenate because args is a tuple, using list.extend()
|
||||
cmd.extend(args)
|
||||
|
||||
out = __salt__['cmd.run_all'](cmd,
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
if out['retcode'] != 0 and 'Error:' in out['stdout']:
|
||||
continue
|
||||
_parse_output(out['stdout'])
|
||||
|
||||
for reponame in ret:
|
||||
# Sort versions newest to oldest
|
||||
for pkgname in ret[reponame]:
|
||||
sorted_versions = sorted(
|
||||
[_LooseVersion(x) for x in ret[reponame][pkgname]],
|
||||
|
@ -780,14 +868,18 @@ def list_upgrades(refresh=True, **kwargs):
|
|||
if salt.utils.is_true(refresh):
|
||||
refresh_db(**kwargs)
|
||||
|
||||
if _yum() == 'dnf':
|
||||
upgrades_cmd = '{0} --upgrades'.format(repo_arg)
|
||||
else:
|
||||
upgrades_cmd = '{0} {1} --all --pkgnarrow=updates'.format(
|
||||
repo_arg, exclude_arg)
|
||||
cmd = [_yum(), '--quiet']
|
||||
cmd.extend(repo_arg)
|
||||
cmd.extend(exclude_arg)
|
||||
cmd.extend(['list', 'upgrades' if _yum() == 'dnf' else 'updates'])
|
||||
out = __salt__['cmd.run_all'](cmd,
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
if out['retcode'] != 0 and 'Error:' in out:
|
||||
return {}
|
||||
|
||||
updates = _repoquery_pkginfo(upgrades_cmd)
|
||||
return dict([(x.name, x.version) for x in updates])
|
||||
return dict([(x.name, x.version) for x in _yum_pkginfo(out['stdout'])])
|
||||
|
||||
|
||||
def info_installed(*names):
|
||||
|
@ -849,12 +941,12 @@ def check_db(*names, **kwargs):
|
|||
exclude_arg = _get_excludes_option(repoquery=True, **kwargs)
|
||||
|
||||
if _yum() == 'dnf':
|
||||
repoquery_base = '{0} --whatprovides'.format(repo_arg)
|
||||
repoquery_base = repo_arg + ['--whatprovides']
|
||||
avail_cmd = repo_arg
|
||||
else:
|
||||
repoquery_base = '{0} {1} --all --quiet --whatprovides'.format(
|
||||
repo_arg, exclude_arg)
|
||||
avail_cmd = '{0} --pkgnarrow=all --all'.format(repo_arg)
|
||||
repoquery_base = repo_arg + exclude_arg
|
||||
repoquery_base.extend(['--all', '--quiet', '--whatprovides'])
|
||||
avail_cmd = repo_arg + ['--pkgnarrow=all', '--all']
|
||||
|
||||
if 'pkg._avail' in __context__:
|
||||
avail = __context__['pkg._avail']
|
||||
|
@ -878,7 +970,7 @@ def check_db(*names, **kwargs):
|
|||
for name in names:
|
||||
ret.setdefault(name, {})['found'] = name in avail
|
||||
if not ret[name]['found']:
|
||||
repoquery_cmd = '{0} {1}'.format(repoquery_base, name)
|
||||
repoquery_cmd = repoquery_base + [name]
|
||||
provides = sorted(
|
||||
set(x.name for x in _repoquery_pkginfo(repoquery_cmd))
|
||||
)
|
||||
|
@ -935,26 +1027,19 @@ def refresh_db(**kwargs):
|
|||
repo_arg = _get_repo_options(**kwargs)
|
||||
exclude_arg = _get_excludes_option(**kwargs)
|
||||
branch_arg = _get_branch_option(**kwargs)
|
||||
yum_cmd = _yum()
|
||||
|
||||
clean_cmd = '{yum} -q clean expire-cache {repo} {exclude} {branch}'.format(
|
||||
yum=yum_cmd,
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg
|
||||
)
|
||||
update_cmd = '{yum} -q check-update {repo} {exclude} {branch}'.format(
|
||||
yum=yum_cmd,
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg
|
||||
)
|
||||
clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache']
|
||||
update_cmd = [_yum(), '--quiet', 'check-update']
|
||||
for args in (repo_arg, exclude_arg, branch_arg):
|
||||
if args:
|
||||
clean_cmd.extend(args)
|
||||
update_cmd.extend(args)
|
||||
|
||||
__salt__['cmd.run'](clean_cmd)
|
||||
return retcodes.get(
|
||||
__salt__['cmd.retcode'](update_cmd, ignore_retcode=True),
|
||||
False
|
||||
)
|
||||
__salt__['cmd.run'](clean_cmd, python_shell=False)
|
||||
result = __salt__['cmd.retcode'](update_cmd,
|
||||
ignore_retcode=True,
|
||||
python_shell=False)
|
||||
return retcodes.get(result, False)
|
||||
|
||||
|
||||
def clean_metadata(**kwargs):
|
||||
|
@ -1179,7 +1264,7 @@ def install(name=None,
|
|||
arch = '.' + archpart
|
||||
pkgname = namepart
|
||||
|
||||
pkgstr = '"{0}-{1}{2}"'.format(pkgname, version_num, arch)
|
||||
pkgstr = '{0}-{1}{2}'.format(pkgname, version_num, arch)
|
||||
else:
|
||||
pkgstr = pkgpath
|
||||
|
||||
|
@ -1198,39 +1283,53 @@ def install(name=None,
|
|||
else:
|
||||
downgrade.append(pkgstr)
|
||||
|
||||
yum_cmd = _yum()
|
||||
def _add_common_args(cmd):
|
||||
'''
|
||||
DRY function to add args common to all yum/dnf commands
|
||||
'''
|
||||
for args in (repo_arg, exclude_arg, branch_arg):
|
||||
if args:
|
||||
cmd.extend(args)
|
||||
if skip_verify:
|
||||
cmd.append('--nogpgcheck')
|
||||
|
||||
if targets:
|
||||
cmd = '{yum} -y {repo} {exclude} {branch} {gpgcheck} install {pkg}'.format(
|
||||
yum=yum_cmd,
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg,
|
||||
gpgcheck='--nogpgcheck' if skip_verify else '',
|
||||
pkg=' '.join(targets),
|
||||
cmd = [_yum(), '-y']
|
||||
if _yum() == 'dnf':
|
||||
cmd.extend(['--best', '--allowerasing'])
|
||||
_add_common_args(cmd)
|
||||
cmd.append('install')
|
||||
cmd.extend(targets)
|
||||
__salt__['cmd.run_all'](
|
||||
cmd,
|
||||
output_loglevel='trace',
|
||||
python_shell=False,
|
||||
redirect_stderr=True
|
||||
)
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace')
|
||||
|
||||
if downgrade:
|
||||
cmd = '{yum} -y {repo} {exclude} {branch} {gpgcheck} downgrade {pkg}'.format(
|
||||
yum=yum_cmd,
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg,
|
||||
gpgcheck='--nogpgcheck' if skip_verify else '',
|
||||
pkg=' '.join(downgrade),
|
||||
cmd = [_yum(), '-y']
|
||||
_add_common_args(cmd)
|
||||
cmd.append('downgrade')
|
||||
cmd.extend(downgrade)
|
||||
__salt__['cmd.run_all'](
|
||||
cmd,
|
||||
output_loglevel='trace',
|
||||
python_shell=False,
|
||||
redirect_stderr=True
|
||||
)
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace')
|
||||
|
||||
if to_reinstall:
|
||||
cmd = '{yum} -y {repo} {exclude} {branch} {gpgcheck} reinstall {pkg}'.format(
|
||||
yum=yum_cmd,
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg,
|
||||
gpgcheck='--nogpgcheck' if skip_verify else '',
|
||||
pkg=' '.join(six.itervalues(to_reinstall)),
|
||||
cmd = [_yum(), '-y']
|
||||
_add_common_args(cmd)
|
||||
cmd.append('reinstall')
|
||||
cmd.extend(six.itervalues(to_reinstall))
|
||||
__salt__['cmd.run_all'](
|
||||
cmd,
|
||||
output_loglevel='trace',
|
||||
python_shell=False,
|
||||
redirect_stderr=True
|
||||
)
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace')
|
||||
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
|
@ -1291,14 +1390,15 @@ def upgrade(refresh=True, skip_verify=False, **kwargs):
|
|||
refresh_db(**kwargs)
|
||||
|
||||
old = list_pkgs()
|
||||
cmd = '{yum} -q -y {repo} {exclude} {branch} {gpgcheck} upgrade'.format(
|
||||
yum=_yum(),
|
||||
repo=repo_arg,
|
||||
exclude=exclude_arg,
|
||||
branch=branch_arg,
|
||||
gpgcheck='--nogpgcheck' if skip_verify else '')
|
||||
cmd = [_yum(), '--quiet', '-y']
|
||||
for args in (repo_arg, exclude_arg, branch_arg):
|
||||
if args:
|
||||
cmd.extend(args)
|
||||
if skip_verify:
|
||||
cmd.append('--nogpgcheck')
|
||||
cmd.append('upgrade')
|
||||
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace')
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False)
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
ret = salt.utils.compare_dicts(old, new)
|
||||
|
@ -1343,8 +1443,7 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613
|
|||
targets = [x for x in pkg_params if x in old]
|
||||
if not targets:
|
||||
return {}
|
||||
quoted_targets = [_cmd_quote(target) for target in targets]
|
||||
cmd = _yum() + ' -y remove {0}'.format(' '.join(quoted_targets))
|
||||
cmd = [_yum(), '-y', 'remove'] + targets
|
||||
__salt__['cmd.run'](cmd, output_loglevel='trace')
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
|
@ -1465,8 +1564,8 @@ def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613
|
|||
ret[target]['comment'] = ('Package {0} is set to be held.'
|
||||
.format(target))
|
||||
else:
|
||||
cmd = _yum() + ' versionlock {0}'.format(target)
|
||||
out = __salt__['cmd.run_all'](cmd)
|
||||
cmd = [_yum(), 'versionlock', target]
|
||||
out = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
|
||||
if out['retcode'] == 0:
|
||||
ret[target].update(result=True)
|
||||
|
@ -1548,19 +1647,15 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06
|
|||
'result': False,
|
||||
'comment': ''}
|
||||
|
||||
search_locks = [lock for lock in current_locks
|
||||
if target in lock]
|
||||
search_locks = [x for x in current_locks if target in x]
|
||||
if search_locks:
|
||||
if 'test' in __opts__ and __opts__['test']:
|
||||
ret[target].update(result=None)
|
||||
ret[target]['comment'] = ('Package {0} is set to be unheld.'
|
||||
.format(target))
|
||||
else:
|
||||
quoted_targets = [_cmd_quote(item) for item in search_locks]
|
||||
cmd = _yum() + ' versionlock delete {0}'.format(
|
||||
' '.join(quoted_targets)
|
||||
)
|
||||
out = __salt__['cmd.run_all'](cmd)
|
||||
cmd = [_yum(), 'versionlock', 'delete'] + search_locks
|
||||
out = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
|
||||
if out['retcode'] == 0:
|
||||
ret[target].update(result=True)
|
||||
|
@ -2316,14 +2411,11 @@ def owner(*paths):
|
|||
return ''
|
||||
ret = {}
|
||||
for path in paths:
|
||||
cmd = 'rpm -qf --queryformat {0} \'{1}\''.format(
|
||||
_cmd_quote('%{{NAME}}'),
|
||||
path
|
||||
)
|
||||
ret[path] = __salt__['cmd.run_stdout'](
|
||||
cmd.format(path),
|
||||
output_loglevel='trace'
|
||||
)
|
||||
['rpm', '-qf', '--queryformat', '%{NAME}', path],
|
||||
output_loglevel='trace',
|
||||
python_shell=False
|
||||
)
|
||||
if 'not owned' in ret[path].lower():
|
||||
ret[path] = ''
|
||||
if len(ret) == 1:
|
||||
|
|
|
@ -5,6 +5,7 @@ Common
|
|||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import collections
|
||||
import logging
|
||||
|
||||
# Import salt libs
|
||||
|
@ -33,7 +34,7 @@ ARCHES = ARCHES_64 + ARCHES_32 + ARCHES_PPC + ARCHES_S390 + \
|
|||
QUERYFORMAT = '%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-%{REPOID}'
|
||||
|
||||
|
||||
def _osarch():
|
||||
def get_osarch():
|
||||
'''
|
||||
Get the os architecture using rpm --eval
|
||||
'''
|
||||
|
@ -51,37 +52,48 @@ def check_32(arch, osarch=None):
|
|||
Returns True if both the OS arch and the passed arch are 32-bit
|
||||
'''
|
||||
if osarch is None:
|
||||
osarch = _osarch()
|
||||
osarch = get_osarch()
|
||||
return all(x in ARCHES_32 for x in (osarch, arch))
|
||||
|
||||
|
||||
def pkginfo(name, version, arch, repoid):
|
||||
'''
|
||||
Build and return a pkginfo namedtuple
|
||||
'''
|
||||
pkginfo_tuple = collections.namedtuple(
|
||||
'PkgInfo',
|
||||
('name', 'version', 'arch', 'repoid')
|
||||
)
|
||||
return pkginfo_tuple(name, version, arch, repoid)
|
||||
|
||||
|
||||
def resolve_name(name, arch, osarch=None):
|
||||
'''
|
||||
Resolve the package name and arch into a unique name referred to by salt.
|
||||
For example, on a 64-bit OS, a 32-bit package will be pkgname.i386.
|
||||
'''
|
||||
if osarch is None:
|
||||
osarch = get_osarch()
|
||||
|
||||
if not check_32(arch, osarch) and arch not in (osarch, 'noarch'):
|
||||
name += '.{0}'.format(arch)
|
||||
return name
|
||||
|
||||
|
||||
def parse_pkginfo(line, osarch=None):
|
||||
'''
|
||||
A small helper to parse an rpm/repoquery command's output. Returns a
|
||||
namedtuple
|
||||
pkginfo namedtuple.
|
||||
'''
|
||||
# Importing `collections` here since this function is re-namespaced into
|
||||
# another module
|
||||
import collections
|
||||
pkginfo = collections.namedtuple(
|
||||
'PkgInfo',
|
||||
('name', 'version', 'arch', 'repoid')
|
||||
)
|
||||
|
||||
try:
|
||||
name, pkg_version, release, arch, repoid = line.split('_|-')
|
||||
name, version, release, arch, repoid = line.split('_|-')
|
||||
# Handle unpack errors (should never happen with the queryformat we are
|
||||
# using, but can't hurt to be careful).
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
if osarch is None:
|
||||
osarch = _osarch()
|
||||
|
||||
if not check_32(arch, osarch):
|
||||
if arch not in (osarch, 'noarch'):
|
||||
name += '.{0}'.format(arch)
|
||||
name = resolve_name(name, arch, osarch)
|
||||
if release:
|
||||
pkg_version += '-{0}'.format(release)
|
||||
version += '-{0}'.format(release)
|
||||
|
||||
return pkginfo(name, pkg_version, arch, repoid)
|
||||
return pkginfo(name, version, arch, repoid)
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salttesting import TestCase, skipIf
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
from salttesting.mock import (
|
||||
|
@ -25,6 +27,26 @@ from salt.modules import systemd
|
|||
systemd.__salt__ = {}
|
||||
systemd.__context__ = {}
|
||||
|
||||
_SYSTEMCTL_STATUS = {
|
||||
'sshd.service': '''\
|
||||
* sshd.service - OpenSSH Daemon
|
||||
Loaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: disabled)
|
||||
Active: inactive (dead)''',
|
||||
|
||||
'foo.service': '''\
|
||||
* foo.service
|
||||
Loaded: not-found (Reason: No such file or directory)
|
||||
Active: inactive (dead)'''
|
||||
}
|
||||
|
||||
_LIST_UNIT_FILES = '''\
|
||||
service1.service enabled
|
||||
service2.service disabled
|
||||
service3.service static
|
||||
timer1.timer enabled
|
||||
timer2.timer disabled
|
||||
timer3.timer static'''
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class SystemdTestCase(TestCase):
|
||||
|
@ -35,208 +57,129 @@ class SystemdTestCase(TestCase):
|
|||
'''
|
||||
Test to Reloads systemctl
|
||||
'''
|
||||
mock = MagicMock(side_effect=[1, 0])
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertFalse(systemd.systemctl_reload())
|
||||
|
||||
mock = MagicMock(side_effect=[
|
||||
{'stdout': 'Who knows why?',
|
||||
'stderr': '',
|
||||
'retcode': 1,
|
||||
'pid': 12345},
|
||||
{'stdout': '',
|
||||
'stderr': '',
|
||||
'retcode': 0,
|
||||
'pid': 54321},
|
||||
])
|
||||
with patch.dict(systemd.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertRaisesRegexp(
|
||||
CommandExecutionError,
|
||||
'Problem performing systemctl daemon-reload: Who knows why?',
|
||||
systemd.systemctl_reload
|
||||
)
|
||||
self.assertTrue(systemd.systemctl_reload())
|
||||
|
||||
def test_get_enabled(self):
|
||||
'''
|
||||
Test to return a list of all enabled services
|
||||
Test to return a list of all enabled services
|
||||
'''
|
||||
def sysv(name):
|
||||
if name in ['d', 'e']:
|
||||
return True
|
||||
return False
|
||||
cmd_mock = MagicMock(return_value=_LIST_UNIT_FILES)
|
||||
listdir_mock = MagicMock(return_value=['foo', 'bar', 'baz', 'README'])
|
||||
sd_mock = MagicMock(
|
||||
return_value=set(
|
||||
[x.replace('.service', '') for x in _SYSTEMCTL_STATUS]
|
||||
)
|
||||
)
|
||||
access_mock = MagicMock(
|
||||
side_effect=lambda x, y: x != os.path.join(
|
||||
systemd.INITSCRIPT_PATH,
|
||||
'README'
|
||||
)
|
||||
)
|
||||
sysv_enabled_mock = MagicMock(side_effect=lambda x: x == 'baz')
|
||||
|
||||
def sysve(name):
|
||||
if name in ['e']:
|
||||
return True
|
||||
return False
|
||||
|
||||
mock = MagicMock(return_value={"a": "enabled", "b": "enabled",
|
||||
"c": "disabled"})
|
||||
lmock = MagicMock(return_value={"d": "disabled",
|
||||
"a": "disabled",
|
||||
"b": "disabled",
|
||||
"e": "disabled"})
|
||||
with patch.object(systemd, "_sysv_is_disabled", sysve):
|
||||
with patch.object(systemd, "_service_is_sysv", sysv):
|
||||
with patch.object(systemd, '_get_all_unit_files', mock):
|
||||
with patch.object(systemd, '_get_all_units', lmock):
|
||||
self.assertListEqual(
|
||||
systemd.get_enabled(), ["a", "b", "d"])
|
||||
with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}):
|
||||
with patch.object(os, 'listdir', listdir_mock):
|
||||
with patch.object(systemd, '_get_systemd_services', sd_mock):
|
||||
with patch.object(os, 'access', side_effect=access_mock):
|
||||
with patch.object(systemd, '_sysv_enabled',
|
||||
sysv_enabled_mock):
|
||||
self.assertListEqual(
|
||||
systemd.get_enabled(),
|
||||
['baz', 'service1', 'timer1.timer']
|
||||
)
|
||||
|
||||
def test_get_disabled(self):
|
||||
'''
|
||||
Test to return a list of all disabled services
|
||||
Test to return a list of all disabled services
|
||||
'''
|
||||
mock = MagicMock(return_value={"a": "enabled", "b": "enabled",
|
||||
"c": "disabled"})
|
||||
with patch.object(systemd, '_get_all_unit_files', mock):
|
||||
mock = MagicMock(return_value={})
|
||||
with patch.object(systemd, '_get_all_legacy_init_scripts', mock):
|
||||
self.assertListEqual(systemd.get_disabled(), ["c"])
|
||||
cmd_mock = MagicMock(return_value=_LIST_UNIT_FILES)
|
||||
# 'foo' should collide with the systemd services (as returned by
|
||||
# sd_mock) and thus not be returned by _get_sysv_services(). It doesn't
|
||||
# matter that it's not part of the _LIST_UNIT_FILES output, we just
|
||||
# want to ensure that 'foo' isn't identified as a disabled initscript
|
||||
# even though below we are mocking it to show as not enabled (since
|
||||
# only 'baz' will be considered an enabled sysv service).
|
||||
listdir_mock = MagicMock(return_value=['foo', 'bar', 'baz', 'README'])
|
||||
sd_mock = MagicMock(
|
||||
return_value=set(
|
||||
[x.replace('.service', '') for x in _SYSTEMCTL_STATUS]
|
||||
)
|
||||
)
|
||||
access_mock = MagicMock(
|
||||
side_effect=lambda x, y: x != os.path.join(
|
||||
systemd.INITSCRIPT_PATH,
|
||||
'README'
|
||||
)
|
||||
)
|
||||
sysv_enabled_mock = MagicMock(side_effect=lambda x: x == 'baz')
|
||||
|
||||
with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}):
|
||||
with patch.object(os, 'listdir', listdir_mock):
|
||||
with patch.object(systemd, '_get_systemd_services', sd_mock):
|
||||
with patch.object(os, 'access', side_effect=access_mock):
|
||||
with patch.object(systemd, '_sysv_enabled',
|
||||
sysv_enabled_mock):
|
||||
self.assertListEqual(
|
||||
systemd.get_disabled(),
|
||||
['bar', 'service2', 'timer2.timer']
|
||||
)
|
||||
|
||||
def test_get_all(self):
|
||||
'''
|
||||
Test to return a list of all available services
|
||||
Test to return a list of all available services
|
||||
'''
|
||||
mock = MagicMock(return_value={"a": "enabled", "b": "enabled",
|
||||
"c": "disabled"})
|
||||
with patch.object(systemd, '_get_all_units', mock):
|
||||
mock = MagicMock(return_value={"a1": "enabled", "b1": "disabled",
|
||||
"c1": "enabled"})
|
||||
with patch.object(systemd, '_get_all_unit_files', mock):
|
||||
mock = MagicMock(return_value={})
|
||||
with patch.object(systemd,
|
||||
'_get_all_legacy_init_scripts', mock):
|
||||
self.assertListEqual(systemd.get_all(),
|
||||
['a', 'a1', 'b', 'b1', 'c', 'c1'])
|
||||
listdir_mock = MagicMock(side_effect=[
|
||||
['foo.service', 'multi-user.target.wants', 'mytimer.timer'],
|
||||
['foo.service', 'multi-user.target.wants', 'bar.service'],
|
||||
['mysql', 'nginx', 'README']
|
||||
])
|
||||
access_mock = MagicMock(
|
||||
side_effect=lambda x, y: x != os.path.join(
|
||||
systemd.INITSCRIPT_PATH,
|
||||
'README'
|
||||
)
|
||||
)
|
||||
with patch.object(os, 'listdir', listdir_mock):
|
||||
with patch.object(os, 'access', side_effect=access_mock):
|
||||
self.assertListEqual(
|
||||
systemd.get_all(),
|
||||
['bar', 'foo', 'mysql', 'mytimer.timer', 'nginx']
|
||||
)
|
||||
|
||||
def test_available(self):
|
||||
'''
|
||||
Test to check that the given service is available
|
||||
Test to check that the given service is available
|
||||
'''
|
||||
mock = MagicMock(side_effect=["a", "@", "c"])
|
||||
with patch.object(systemd, '_canonical_template_unit_name', mock):
|
||||
mock = MagicMock(side_effect=[{"a": "z", "b": "z"},
|
||||
{"@": "z", "b": "z"},
|
||||
{"a": "z", "b": "z"}])
|
||||
with patch.object(systemd, 'get_all', mock):
|
||||
self.assertTrue(systemd.available("sshd"))
|
||||
|
||||
self.assertTrue(systemd.available("sshd"))
|
||||
|
||||
self.assertFalse(systemd.available("sshd"))
|
||||
mock = MagicMock(side_effect=lambda x: _SYSTEMCTL_STATUS[x])
|
||||
with patch.object(systemd, '_systemctl_status', mock):
|
||||
self.assertTrue(systemd.available('sshd.service'))
|
||||
self.assertFalse(systemd.available('foo.service'))
|
||||
|
||||
def test_missing(self):
|
||||
'''
|
||||
Test to the inverse of service.available.
|
||||
'''
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(systemd, 'available', mock):
|
||||
self.assertFalse(systemd.missing("sshd"))
|
||||
|
||||
def test_unmask(self):
|
||||
'''
|
||||
Test to unmask the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.unmask("sshd"))
|
||||
|
||||
def test_start(self):
|
||||
'''
|
||||
Test to start the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.start("sshd"))
|
||||
|
||||
def test_stop(self):
|
||||
'''
|
||||
Test to stop the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.stop("sshd"))
|
||||
|
||||
def test_restart(self):
|
||||
'''
|
||||
Test to restart the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.restart("sshd"))
|
||||
|
||||
def test_reload_(self):
|
||||
'''
|
||||
Test to Reload the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.reload_("sshd"))
|
||||
|
||||
def test_force_reload(self):
|
||||
'''
|
||||
Test to force-reload the specified service with systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.force_reload("sshd"))
|
||||
|
||||
def test_status(self):
|
||||
'''
|
||||
Test to return the status for a service via systemd
|
||||
'''
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
self.assertTrue(systemd.status("sshd"))
|
||||
|
||||
def test_enable(self):
|
||||
'''
|
||||
Test to enable the named service to start when the system boots
|
||||
'''
|
||||
exe = MagicMock(return_value='foo')
|
||||
tmock = MagicMock(return_value=True)
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
with patch.object(systemd, "_service_is_sysv", mock):
|
||||
self.assertTrue(systemd.enable("sshd"))
|
||||
with patch.object(systemd, "_get_service_exec", exe):
|
||||
with patch.object(systemd, "_service_is_sysv", tmock):
|
||||
self.assertTrue(systemd.enable("sshd"))
|
||||
|
||||
def test_disable(self):
|
||||
'''
|
||||
Test to disable the named service to not
|
||||
start when the system boots
|
||||
'''
|
||||
exe = MagicMock(return_value='foo')
|
||||
tmock = MagicMock(return_value=True)
|
||||
mock = MagicMock(return_value=False)
|
||||
with patch.object(systemd, '_untracked_custom_unit_found', mock):
|
||||
with patch.object(systemd, '_unit_file_changed', mock):
|
||||
with patch.dict(systemd.__salt__, {'cmd.retcode': mock}):
|
||||
with patch.object(systemd, "_service_is_sysv", mock):
|
||||
self.assertTrue(systemd.disable("sshd"))
|
||||
with patch.object(systemd, "_get_service_exec", exe):
|
||||
with patch.object(systemd, "_service_is_sysv", tmock):
|
||||
self.assertTrue(systemd.disable("sshd"))
|
||||
|
||||
def test_enabled(self):
|
||||
'''
|
||||
Test to return if the named service is enabled to start on boot
|
||||
'''
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(systemd, '_enabled', mock):
|
||||
self.assertTrue(systemd.enabled("sshd"))
|
||||
|
||||
def test_disabled(self):
|
||||
'''
|
||||
Test to Return if the named service is disabled to start on boot
|
||||
'''
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(systemd, '_enabled', mock):
|
||||
self.assertFalse(systemd.disabled("sshd"))
|
||||
mock = MagicMock(side_effect=lambda x: _SYSTEMCTL_STATUS[x])
|
||||
with patch.object(systemd, '_systemctl_status', mock):
|
||||
self.assertFalse(systemd.missing('sshd.service'))
|
||||
self.assertTrue(systemd.missing('foo.service'))
|
||||
|
||||
def test_show(self):
|
||||
'''
|
||||
|
|
Loading…
Add table
Reference in a new issue