File: //proc/self/root/usr/lib/python2.7/site-packages/salt/modules/systemd_service.py
# -*- coding: utf-8 -*-
'''
Provides the service module for systemd
.. versionadded:: 0.10.0
.. important::
If you feel that Salt should be using this module to manage services on a
minion, and it is using a different module (or gives an error similar to
*'service.start' is not available*), see :ref:`here
<module-provider-override>`.
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import errno
import glob
import logging
import os
import fnmatch
import re
import shlex
import time
# Import Salt libs
import salt.utils.files
import salt.utils.itertools
import salt.utils.path
import salt.utils.stringutils
import salt.utils.systemd
from salt.exceptions import CommandExecutionError
# Import 3rd-party libs
from salt.ext import six
log = logging.getLogger(__name__)
__func_alias__ = {
'reload_': 'reload',
'unmask_': 'unmask',
}
SYSTEM_CONFIG_PATHS = ('/lib/systemd/system', '/usr/lib/systemd/system')
LOCAL_CONFIG_PATH = '/etc/systemd/system'
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__):
return __virtualname__
return (
False,
'The systemd execution module failed to load: only available on Linux '
'systems which have been booted with systemd.'
)
def _root(path, root):
'''
Relocate an absolute path to a new root directory.
'''
if root:
return os.path.join(root, os.path.relpath(path, os.path.sep))
else:
return path
def _canonical_unit_name(name):
'''
Build a canonical unit name treating unit names without one
of the valid suffixes as a service.
'''
if not isinstance(name, six.string_types):
name = six.text_type(name)
if any(name.endswith(suffix) for suffix in VALID_UNIT_TYPES):
return name
return '%s.service' % name
def _check_available(name):
'''
Returns boolean telling whether or not the named service is available
'''
_status = _systemctl_status(name)
sd_version = salt.utils.systemd.version(__context__)
if sd_version is not None and sd_version >= 231:
# systemd 231 changed the output of "systemctl status" for unknown
# services, and also made it return an exit status of 4. If we are on
# a new enough version, check the retcode, otherwise fall back to
# parsing the "systemctl status" output.
# See: https://github.com/systemd/systemd/pull/3385
# Also: https://github.com/systemd/systemd/commit/3dced37
return 0 <= _status['retcode'] < 4
out = _status['stdout'].lower()
if 'could not be found' in out:
# Catch cases where the systemd version is < 231 but the return code
# and output changes have been backported (e.g. RHEL 7.3).
return False
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:
raise CommandExecutionError(
'Failed to get information on unit \'%s\'' % name
)
return ret
def _check_for_unit_changes(name):
'''
Check for modified/updated unit files, and run a daemon-reload if any are
found.
'''
contextkey = 'systemd._check_for_unit_changes.{0}'.format(name)
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 _check_unmask(name, unmask, unmask_runtime, root=None):
'''
Common code for conditionally removing masks before making changes to a
service's state.
'''
if unmask:
unmask_(name, runtime=False, root=root)
if unmask_runtime:
unmask_(name, runtime=True, root=root)
def _clear_context():
'''
Remove context
'''
# 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.'):
__context__.pop(key)
except AttributeError:
continue
def _default_runlevel():
'''
Try to figure out the default runlevel. It is kept in
/etc/init/rc-sysinit.conf, but can be overridden with entries
in /etc/inittab, or via the kernel command-line at boot
'''
# Try to get the "main" default. If this fails, throw up our
# hands and just guess "2", because things are horribly broken
try:
with salt.utils.files.fopen('/etc/init/rc-sysinit.conf') as fp_:
for line in fp_:
line = salt.utils.stringutils.to_unicode(line)
if line.startswith('env DEFAULT_RUNLEVEL'):
runlevel = line.split('=')[-1].strip()
except Exception: # pylint: disable=broad-except
return '2'
# Look for an optional "legacy" override in /etc/inittab
try:
with salt.utils.files.fopen('/etc/inittab') as fp_:
for line in fp_:
line = salt.utils.stringutils.to_unicode(line)
if not line.startswith('#') and 'initdefault' in line:
runlevel = line.split(':')[1]
except Exception: # pylint: disable=broad-except
pass
# The default runlevel can also be set via the kernel command-line.
# Kinky.
try:
valid_strings = set(
('0', '1', '2', '3', '4', '5', '6', 's', 'S', '-s', 'single'))
with salt.utils.files.fopen('/proc/cmdline') as fp_:
for line in fp_:
line = salt.utils.stringutils.to_unicode(line)
for arg in line.strip().split():
if arg in valid_strings:
runlevel = arg
break
except Exception: # pylint: disable=broad-except
pass
return runlevel
def _get_systemd_services(root):
'''
Use os.listdir() to get all the unit files
'''
ret = set()
for path in SYSTEM_CONFIG_PATHS + (LOCAL_CONFIG_PATH,):
# Make sure user has access to the path, and if the path is a
# link it's likely that another entry in SYSTEM_CONFIG_PATHS
# or LOCAL_CONFIG_PATH points to it, so we can ignore it.
path = _root(path, root)
if os.access(path, os.R_OK) and not os.path.islink(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)
return ret
def _get_sysv_services(root, systemd_services=None):
'''
Use os.listdir() and os.access() to get all the initscripts
'''
initscript_path = _root(INITSCRIPT_PATH, root)
try:
sysv_services = os.listdir(initscript_path)
except OSError as exc:
if exc.errno == errno.ENOENT:
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 []
if systemd_services is None:
systemd_services = _get_systemd_services(root)
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 _get_service_exec():
'''
Returns the path to the sysv service manager (either update-rc.d or
chkconfig)
'''
contextkey = 'systemd._get_service_exec'
if contextkey not in __context__:
executables = ('update-rc.d', 'chkconfig')
for executable in executables:
service_exec = salt.utils.path.which(executable)
if service_exec is not None:
break
else:
raise CommandExecutionError(
'Unable to find sysv service manager (tried {0})'.format(
', '.join(executables)
)
)
__context__[contextkey] = service_exec
return __context__[contextkey]
def _runlevel():
'''
Return the current runlevel
'''
contextkey = 'systemd._runlevel'
if contextkey in __context__:
return __context__[contextkey]
out = __salt__['cmd.run']('runlevel', python_shell=False, ignore_retcode=True)
try:
ret = out.split()[1]
except IndexError:
# The runlevel is unknown, return the default
ret = _default_runlevel()
__context__[contextkey] = ret
return ret
def _strip_scope(msg):
'''
Strip unnecessary message about running the command with --scope from
stderr so that we can raise an exception with the remaining stderr text.
'''
ret = []
for line in msg.splitlines():
if not line.endswith('.scope'):
ret.append(line)
return '\n'.join(ret).strip()
def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False,
root=None):
'''
Build a systemctl command line. Treat unit names without one
of the valid suffixes as a service.
'''
ret = []
if systemd_scope \
and salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
ret.extend(['systemd-run', '--scope'])
ret.append('systemctl')
if no_block:
ret.append('--no-block')
if root:
ret.extend(['--root', root])
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 _systemctl_status(name):
'''
Helper function which leverages __context__ to keep from running 'systemctl
status' more than once.
'''
contextkey = 'systemd._systemctl_status.%s' % name
if contextkey in __context__:
return __context__[contextkey]
__context__[contextkey] = __salt__['cmd.run_all'](
_systemctl_cmd('status', name),
python_shell=False,
redirect_stderr=True,
ignore_retcode=True
)
return __context__[contextkey]
def _sysv_enabled(name, root):
'''
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.
'''
# Find exact match (disambiguate matches like "S01anacron" for cron)
rc = _root('/etc/rc{}.d/S*{}'.format(_runlevel(), name), root)
for match in glob.glob(rc):
if re.match(r'S\d{,2}%s' % name, os.path.basename(match)):
return True
return False
def _untracked_custom_unit_found(name, root=None):
'''
If the passed service name is not available, but a unit file exist in
/etc/systemd/system, return True. Otherwise, return False.
'''
system = _root('/etc/systemd/system', root)
unit_path = os.path.join(system, _canonical_unit_name(name))
return os.access(unit_path, os.R_OK) and not _check_available(name)
def _unit_file_changed(name):
'''
Returns True if systemctl reports that the unit file has changed, otherwise
returns False.
'''
status = _systemctl_status(name)['stdout'].lower()
return "'systemctl daemon-reload'" in status
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_running():
'''
Return a list of all running services, so far as systemd is concerned
CLI Example:
.. code-block:: bash
salt '*' service.get_running
'''
ret = set()
# Get running systemd units
out = __salt__['cmd.run'](
_systemctl_cmd('--full --no-legend --no-pager'),
python_shell=False,
ignore_retcode=True)
for line in salt.utils.itertools.split(out, '\n'):
try:
comps = line.strip().split()
fullname = comps[0]
if len(comps) > 3:
active_state = comps[3]
except ValueError as exc:
log.error(exc)
continue
else:
if active_state != 'running':
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)
return sorted(ret)
def get_enabled(root=None):
'''
Return a list of all enabled services
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.get_enabled
'''
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',
root=root),
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 != 'enabled':
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 enabled
ret.update(set(
[x for x in _get_sysv_services(root) if _sysv_enabled(x, root)]
))
return sorted(ret)
def get_disabled(root=None):
'''
Return a list of all disabled services
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.get_disabled
'''
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',
root=root),
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(root) if not _sysv_enabled(x, root)]
))
return sorted(ret)
def get_static(root=None):
'''
.. versionadded:: 2015.8.5
Return a list of all static services
root
Enable/disable/mask unit files in the specified root directory
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',
root=root),
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)
def get_all(root=None):
'''
Return a list of all available services
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.get_all
'''
ret = _get_systemd_services(root)
ret.update(set(_get_sysv_services(root, systemd_services=ret)))
return sorted(ret)
def available(name):
'''
.. versionadded:: 0.10.4
Check that the given service is available taking into account template
units.
CLI Example:
.. code-block:: bash
salt '*' service.available sshd
'''
_check_for_unit_changes(name)
return _check_available(name)
def missing(name):
'''
.. 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:
.. code-block:: bash
salt '*' service.missing sshd
'''
return not available(name)
def unmask_(name, runtime=False, root=None):
'''
.. versionadded:: 2015.5.0
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Unmask the specified service with systemd
runtime : False
Set to ``True`` to unmask this service only until the next reboot
.. versionadded:: 2017.7.0
In previous versions, this function would remove whichever mask was
identified by running ``systemctl is-enabled`` on the service.
However, since it is possible to both have both indefinite and
runtime masks on a service simultaneously, this function now
removes a runtime mask only when this argument is set to ``True``,
and otherwise removes an indefinite mask.
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.unmask foo
salt '*' service.unmask foo runtime=True
'''
_check_for_unit_changes(name)
if not masked(name, runtime, root=root):
log.debug('Service \'%s\' is not %smasked',
name, 'runtime-' if runtime else '')
return True
cmd = 'unmask --runtime' if runtime else 'unmask'
out = __salt__['cmd.run_all'](
_systemctl_cmd(cmd, name, systemd_scope=True, root=root),
python_shell=False,
redirect_stderr=True)
if out['retcode'] != 0:
raise CommandExecutionError('Failed to unmask service \'%s\'' % name)
return True
def mask(name, runtime=False, root=None):
'''
.. versionadded:: 2015.5.0
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Mask the specified service with systemd
runtime : False
Set to ``True`` to mask this service only until the next reboot
.. versionadded:: 2015.8.5
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.mask foo
salt '*' service.mask foo runtime=True
'''
_check_for_unit_changes(name)
cmd = 'mask --runtime' if runtime else 'mask'
out = __salt__['cmd.run_all'](
_systemctl_cmd(cmd, name, systemd_scope=True, root=root),
python_shell=False,
redirect_stderr=True)
if out['retcode'] != 0:
raise CommandExecutionError(
'Failed to mask service \'%s\'' % name,
info=out['stdout']
)
return True
def masked(name, runtime=False, root=None):
'''
.. 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.
.. versionchanged:: 2017.7.0
This function now returns a boolean telling the user whether a mask
specified by the new ``runtime`` argument is set. If ``runtime`` is
``False``, this function will return ``True`` if an indefinite mask is
set for the named service (otherwise ``False`` will be returned). If
``runtime`` is ``False``, this function will return ``True`` if a
runtime mask is set, otherwise ``False``.
Check whether or not a service is masked
runtime : False
Set to ``True`` to check for a runtime mask
.. versionadded:: 2017.7.0
In previous versions, this function would simply return the output
of ``systemctl is-enabled`` when the service was found to be
masked. However, since it is possible to both have both indefinite
and runtime masks on a service simultaneously, this function now
only checks for runtime masks if this argument is set to ``True``.
Otherwise, it will check for an indefinite mask.
root
Enable/disable/mask unit files in the specified root directory
CLI Examples:
.. code-block:: bash
salt '*' service.masked foo
salt '*' service.masked foo runtime=True
'''
_check_for_unit_changes(name)
root_dir = _root('/run' if runtime else '/etc', root)
link_path = os.path.join(root_dir,
'systemd',
'system',
_canonical_unit_name(name))
try:
return os.readlink(link_path) == '/dev/null'
except OSError as exc:
if exc.errno == errno.ENOENT:
log.trace(
'Path %s does not exist. This is normal if service \'%s\' is '
'not masked or does not exist.', link_path, name
)
elif exc.errno == errno.EINVAL:
log.error(
'Failed to check mask status for service %s. Path %s is a '
'file, not a symlink. This could be caused by changes in '
'systemd and is probably a bug in Salt. Please report this '
'to the developers.', name, link_path
)
return False
def start(name, no_block=False, unmask=False, unmask_runtime=False):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Start the specified service with systemd
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
unmask : False
Set to ``True`` to remove an indefinite mask before attempting to start
the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
starting. This behavior is no longer the default.
unmask_runtime : False
Set to ``True`` to remove a runtime mask before attempting to start the
service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
starting. This behavior is no longer the default.
CLI Example:
.. code-block:: bash
salt '*' service.start <service name>
'''
_check_for_unit_changes(name)
_check_unmask(name, unmask, unmask_runtime)
ret = __salt__['cmd.run_all'](
_systemctl_cmd('start', name, systemd_scope=True, no_block=no_block),
python_shell=False)
if ret['retcode'] != 0:
# Instead of returning a bool, raise an exception so that we can
# include the error message in the return data. This helps give more
# information to the user in instances where the service is masked.
raise CommandExecutionError(_strip_scope(ret['stderr']))
return True
def stop(name, no_block=False):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Stop the specified service with systemd
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
CLI Example:
.. code-block:: bash
salt '*' service.stop <service name>
'''
_check_for_unit_changes(name)
# Using cmd.run_all instead of cmd.retcode here to make unit tests easier
return __salt__['cmd.run_all'](
_systemctl_cmd('stop', name, systemd_scope=True, no_block=no_block),
python_shell=False)['retcode'] == 0
def restart(name, no_block=False, unmask=False, unmask_runtime=False):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Restart the specified service with systemd
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
unmask : False
Set to ``True`` to remove an indefinite mask before attempting to
restart the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
restarting. This behavior is no longer the default.
unmask_runtime : False
Set to ``True`` to remove a runtime mask before attempting to restart
the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
restarting. This behavior is no longer the default.
CLI Example:
.. code-block:: bash
salt '*' service.restart <service name>
'''
_check_for_unit_changes(name)
_check_unmask(name, unmask, unmask_runtime)
ret = __salt__['cmd.run_all'](
_systemctl_cmd('restart', name, systemd_scope=True, no_block=no_block),
python_shell=False)
if ret['retcode'] != 0:
# Instead of returning a bool, raise an exception so that we can
# include the error message in the return data. This helps give more
# information to the user in instances where the service is masked.
raise CommandExecutionError(_strip_scope(ret['stderr']))
return True
def reload_(name, no_block=False, unmask=False, unmask_runtime=False):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Reload the specified service with systemd
no_block : False
Set to ``True`` to reload the service using ``--no-block``.
.. versionadded:: 2017.7.0
unmask : False
Set to ``True`` to remove an indefinite mask before attempting to
reload the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
reloading. This behavior is no longer the default.
unmask_runtime : False
Set to ``True`` to remove a runtime mask before attempting to reload
the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
reloading. This behavior is no longer the default.
CLI Example:
.. code-block:: bash
salt '*' service.reload <service name>
'''
_check_for_unit_changes(name)
_check_unmask(name, unmask, unmask_runtime)
ret = __salt__['cmd.run_all'](
_systemctl_cmd('reload', name, systemd_scope=True, no_block=no_block),
python_shell=False)
if ret['retcode'] != 0:
# Instead of returning a bool, raise an exception so that we can
# include the error message in the return data. This helps give more
# information to the user in instances where the service is masked.
raise CommandExecutionError(_strip_scope(ret['stderr']))
return True
def force_reload(name, no_block=True, unmask=False, unmask_runtime=False):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. versionadded:: 0.12.0
Force-reload the specified service with systemd
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
unmask : False
Set to ``True`` to remove an indefinite mask before attempting to
force-reload the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
force-reloading. This behavior is no longer the default.
unmask_runtime : False
Set to ``True`` to remove a runtime mask before attempting to
force-reload the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
force-reloading. This behavior is no longer the default.
CLI Example:
.. code-block:: bash
salt '*' service.force_reload <service name>
'''
_check_for_unit_changes(name)
_check_unmask(name, unmask, unmask_runtime)
ret = __salt__['cmd.run_all'](
_systemctl_cmd('force-reload', name,
systemd_scope=True, no_block=no_block),
python_shell=False)
if ret['retcode'] != 0:
# Instead of returning a bool, raise an exception so that we can
# include the error message in the return data. This helps give more
# information to the user in instances where the service is masked.
raise CommandExecutionError(_strip_scope(ret['stderr']))
return True
# The unused sig argument is required to maintain consistency with the API
# established by Salt's service management states.
def status(name, sig=None, wait=3): # pylint: disable=unused-argument
'''
Check whether or not a service is active.
If the name contains globbing, a dict mapping service names to True/False
values is returned.
.. versionchanged:: 2018.3.0
The service name can now be a glob (e.g. ``salt*``)
name
The name of the service to check
sig
Not implemented, but required to be accepted as it is passed by service
states
wait : 3
If the service is in the process of changing states (i.e. it is in
either the ``activating`` or ``deactivating`` state), wait up to this
amount of seconds (checking again periodically) before determining
whether the service is active.
.. versionadded:: 2019.2.3
CLI Example:
.. code-block:: bash
salt '*' service.status <service name> [service signature]
'''
def _get_status(service):
ret = __salt__['cmd.run_all'](_systemctl_cmd('is-active', service),
python_shell=False,
ignore_retcode=True,
redirect_stderr=True)
return ret['retcode'] == 0, ret['stdout']
contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name))
if contains_globbing:
services = fnmatch.filter(get_all(), name)
else:
services = [name]
ret = {}
for service in services:
_check_for_unit_changes(service)
ret[service], _message = _get_status(service)
if not ret[service]:
# Check if the service is in the process of activating/deactivating
start_time = time.time()
# match both 'activating' and 'deactivating'
while 'activating' in _message \
and (time.time() - start_time <= wait):
time.sleep(0.5)
ret[service], _message = _get_status(service)
if ret[service]:
break
if contains_globbing:
return ret
return ret[name]
# **kwargs is required to maintain consistency with the API established by
# Salt's service management states.
def enable(name, no_block=False, unmask=False, unmask_runtime=False,
root=None, **kwargs): # pylint: disable=unused-argument
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Enable the named service to start when the system boots
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
unmask : False
Set to ``True`` to remove an indefinite mask before attempting to
enable the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
enabling. This behavior is no longer the default.
unmask_runtime : False
Set to ``True`` to remove a runtime mask before attempting to enable
the service.
.. versionadded:: 2017.7.0
In previous releases, Salt would simply unmask a service before
enabling. This behavior is no longer the default.
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.enable <service name>
'''
_check_for_unit_changes(name)
_check_unmask(name, unmask, unmask_runtime, root)
if name in _get_sysv_services(root):
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
service_exec = _get_service_exec()
if service_exec.endswith('/update-rc.d'):
cmd.extend([service_exec, '-f', name, 'defaults', '99'])
elif service_exec.endswith('/chkconfig'):
cmd.extend([service_exec, name, 'on'])
return __salt__['cmd.retcode'](cmd,
python_shell=False,
ignore_retcode=True) == 0
ret = __salt__['cmd.run_all'](
_systemctl_cmd('enable', name, systemd_scope=True, no_block=no_block,
root=root),
python_shell=False,
ignore_retcode=True)
if ret['retcode'] != 0:
# Instead of returning a bool, raise an exception so that we can
# include the error message in the return data. This helps give more
# information to the user in instances where the service is masked.
raise CommandExecutionError(_strip_scope(ret['stderr']))
return True
# The unused kwargs argument is required to maintain consistency with the API
# established by Salt's service management states.
def disable(name, no_block=False, root=None, **kwargs): # pylint: disable=unused-argument
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands run by this function from the ``salt-minion`` daemon's
control group. This is done to avoid a race condition in cases where
the ``salt-minion`` service is restarted while a service is being
modified. If desired, usage of `systemd-run(1)`_ can be suppressed by
setting a :mod:`config option <salt.modules.config.get>` called
``systemd.scope``, with a value of ``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
Disable the named service to not start when the system boots
no_block : False
Set to ``True`` to start the service using ``--no-block``.
.. versionadded:: 2017.7.0
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.disable <service name>
'''
_check_for_unit_changes(name)
if name in _get_sysv_services(root):
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
service_exec = _get_service_exec()
if service_exec.endswith('/update-rc.d'):
cmd.extend([service_exec, '-f', name, 'remove'])
elif service_exec.endswith('/chkconfig'):
cmd.extend([service_exec, name, 'off'])
return __salt__['cmd.retcode'](cmd,
python_shell=False,
ignore_retcode=True) == 0
# Using cmd.run_all instead of cmd.retcode here to make unit tests easier
return __salt__['cmd.run_all'](
_systemctl_cmd('disable', name, systemd_scope=True, no_block=no_block,
root=root),
python_shell=False,
ignore_retcode=True)['retcode'] == 0
# The unused kwargs argument is required to maintain consistency with the API
# established by Salt's service management states.
def enabled(name, root=None, **kwargs): # pylint: disable=unused-argument
'''
Return if the named service is enabled to start on boot
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.enabled <service 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, root=root),
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.
local_config_path = _root(LOCAL_CONFIG_PATH, '/')
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
elif name in _get_sysv_services(root):
return _sysv_enabled(name, root)
return False
def disabled(name, root=None):
'''
Return if the named service is disabled from starting on boot
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
.. code-block:: bash
salt '*' service.disabled <service name>
'''
return not enabled(name, root=root)
def show(name, root=None):
'''
.. versionadded:: 2014.7.0
Show properties of one or more units/jobs or the manager
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
salt '*' service.show <service name>
'''
ret = {}
out = __salt__['cmd.run'](_systemctl_cmd('show', name, root=root),
python_shell=False)
for line in salt.utils.itertools.split(out, '\n'):
comps = line.split('=')
name = comps[0]
value = '='.join(comps[1:])
if value.startswith('{'):
value = value.replace('{', '').replace('}', '')
ret[name] = {}
for item in value.split(' ; '):
comps = item.split('=')
ret[name][comps[0].strip()] = comps[1].strip()
elif name in ('Before', 'After', 'Wants'):
ret[name] = value.split()
else:
ret[name] = value
return ret
def execs(root=None):
'''
.. versionadded:: 2014.7.0
Return a list of all files specified as ``ExecStart`` for all services.
root
Enable/disable/mask unit files in the specified root directory
CLI Example:
salt '*' service.execs
'''
ret = {}
for service in get_all(root=root):
data = show(service, root=root)
if 'ExecStart' not in data:
continue
ret[service] = data['ExecStart']['path']
return ret