Merge pull request #36755 from terminalmage/issue36671

systemd.py: check retcode for service availability in systemd >= 231
This commit is contained in:
Thomas S Hatch 2016-10-13 13:41:50 -06:00 committed by GitHub
commit 6b782c15e1
2 changed files with 83 additions and 12 deletions

View file

@ -75,7 +75,23 @@ def _check_available(name):
'''
Returns boolean telling whether or not the named service is available
'''
out = _systemctl_status(name).lower()
_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:
@ -287,9 +303,10 @@ def _systemctl_status(name):
contextkey = 'systemd._systemctl_status.%s' % name
if contextkey in __context__:
return __context__[contextkey]
__context__[contextkey] = __salt__['cmd.run'](
__context__[contextkey] = __salt__['cmd.run_all'](
_systemctl_cmd('status', name),
python_shell=False,
redirect_stderr=True,
ignore_retcode=True
)
return __context__[contextkey]
@ -323,7 +340,7 @@ 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()
return "'systemctl daemon-reload'" in _systemctl_status(name)['stdout'].lower()
def systemctl_reload():

View file

@ -29,15 +29,35 @@ systemd.__salt__ = {}
systemd.__context__ = {}
_SYSTEMCTL_STATUS = {
'sshd.service': '''\
'sshd.service': {
'stdout': '''\
* sshd.service - OpenSSH Daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: disabled)
Active: inactive (dead)''',
'stderr': '',
'retcode': 3,
'pid': 12345,
},
'foo.service': '''\
'foo.service': {
'stdout': '''\
* foo.service
Loaded: not-found (Reason: No such file or directory)
Active: inactive (dead)'''
Active: inactive (dead)''',
'stderr': '',
'retcode': 3,
'pid': 12345,
},
}
# This reflects systemd >= 231 behavior
_SYSTEMCTL_STATUS_GTE_231 = {
'bar.service': {
'stdout': 'Unit bar.service could not be found.',
'stderr': '',
'retcode': 4,
'pid': 12345,
},
}
_LIST_UNIT_FILES = '''\
@ -170,18 +190,52 @@ class SystemdTestCase(TestCase):
Test to check that the given service is available
'''
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'))
# systemd < 231
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 230}):
with patch.object(systemd, '_systemctl_status', mock):
self.assertTrue(systemd.available('sshd.service'))
self.assertFalse(systemd.available('foo.service'))
# systemd >= 231
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 231}):
with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
with patch.object(systemd, '_systemctl_status', mock):
self.assertTrue(systemd.available('sshd.service'))
self.assertFalse(systemd.available('bar.service'))
# systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3)
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 219}):
with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
with patch.object(systemd, '_systemctl_status', mock):
self.assertTrue(systemd.available('sshd.service'))
self.assertFalse(systemd.available('bar.service'))
def test_missing(self):
'''
Test to the inverse of service.available.
'''
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'))
# systemd < 231
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 230}):
with patch.object(systemd, '_systemctl_status', mock):
self.assertFalse(systemd.missing('sshd.service'))
self.assertTrue(systemd.missing('foo.service'))
# systemd >= 231
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 231}):
with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
with patch.object(systemd, '_systemctl_status', mock):
self.assertFalse(systemd.missing('sshd.service'))
self.assertTrue(systemd.missing('bar.service'))
# systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3)
with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 219}):
with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231):
with patch.object(systemd, '_systemctl_status', mock):
self.assertFalse(systemd.missing('sshd.service'))
self.assertTrue(systemd.missing('bar.service'))
def test_show(self):
'''