mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #45774 from twangboy/mac_add_service_util
Fix __virtual__ issue in mac_system.py
This commit is contained in:
commit
12ecfdee93
5 changed files with 518 additions and 105 deletions
|
@ -29,14 +29,11 @@ def __virtual__():
|
|||
'''
|
||||
Only work on Mac OS
|
||||
'''
|
||||
if salt.utils.platform.is_darwin() \
|
||||
and _LooseVersion(__grains__['osrelease']) >= _LooseVersion('10.9'):
|
||||
return True
|
||||
return (
|
||||
False,
|
||||
'The assistive module cannot be loaded: must be run on '
|
||||
'macOS 10.9 or newer.'
|
||||
)
|
||||
if not salt.utils.platform.is_darwin():
|
||||
return False, 'Must be run on macOS'
|
||||
if not _LooseVersion(__grains__['osrelease']) >= salt.utils.stringutils.to_str('10.9'):
|
||||
return False, 'Must be run on macOS 10.9 or newer'
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def install(app_id, enable=True):
|
||||
|
|
|
@ -8,14 +8,13 @@ from __future__ import absolute_import, unicode_literals, print_function
|
|||
# Import python libs
|
||||
import os
|
||||
import re
|
||||
import plistlib
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils.decorators as decorators
|
||||
import salt.utils.files
|
||||
import salt.utils.path
|
||||
import salt.utils.platform
|
||||
import salt.utils.stringutils
|
||||
import salt.utils.mac_utils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
|
||||
|
@ -53,73 +52,6 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def _launchd_paths():
|
||||
'''
|
||||
Paths where launchd services can be found
|
||||
'''
|
||||
return [
|
||||
'/Library/LaunchAgents',
|
||||
'/Library/LaunchDaemons',
|
||||
'/System/Library/LaunchAgents',
|
||||
'/System/Library/LaunchDaemons',
|
||||
]
|
||||
|
||||
|
||||
@decorators.memoize
|
||||
def _available_services():
|
||||
'''
|
||||
Return a dictionary of all available services on the system
|
||||
'''
|
||||
available_services = dict()
|
||||
for launch_dir in _launchd_paths():
|
||||
for root, dirs, files in salt.utils.path.os_walk(launch_dir):
|
||||
for file_name in files:
|
||||
|
||||
# Must be a plist file
|
||||
if not file_name.endswith('.plist'):
|
||||
continue
|
||||
|
||||
# Follow symbolic links of files in _launchd_paths
|
||||
file_path = os.path.join(root, file_name)
|
||||
true_path = os.path.realpath(file_path)
|
||||
|
||||
# ignore broken symlinks
|
||||
if not os.path.exists(true_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
# This assumes most of the plist files
|
||||
# will be already in XML format
|
||||
with salt.utils.files.fopen(file_path):
|
||||
plist = plistlib.readPlist(true_path)
|
||||
|
||||
except Exception:
|
||||
# If plistlib is unable to read the file we'll need to use
|
||||
# the system provided plutil program to do the conversion
|
||||
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
|
||||
true_path)
|
||||
plist_xml = __salt__['cmd.run'](cmd, output_loglevel='quiet')
|
||||
if six.PY2:
|
||||
plist = plistlib.readPlistFromString(plist_xml)
|
||||
else:
|
||||
plist = plistlib.readPlistFromBytes(
|
||||
salt.utils.stringutils.to_bytes(plist_xml))
|
||||
|
||||
try:
|
||||
available_services[plist.Label.lower()] = {
|
||||
'file_name': file_name,
|
||||
'file_path': true_path,
|
||||
'plist': plist}
|
||||
except AttributeError:
|
||||
# Handle malformed plist files
|
||||
available_services[os.path.basename(file_name).lower()] = {
|
||||
'file_name': file_name,
|
||||
'file_path': true_path,
|
||||
'plist': plist}
|
||||
|
||||
return available_services
|
||||
|
||||
|
||||
def _get_service(name):
|
||||
'''
|
||||
Get information about a service. If the service is not found, raise an
|
||||
|
@ -130,7 +62,7 @@ def _get_service(name):
|
|||
:return: The service information for the service, otherwise an Error
|
||||
:rtype: dict
|
||||
'''
|
||||
services = _available_services()
|
||||
services = salt.utils.mac_utils.available_services()
|
||||
name = name.lower()
|
||||
|
||||
if name in services:
|
||||
|
@ -195,26 +127,7 @@ def launchctl(sub_cmd, *args, **kwargs):
|
|||
|
||||
salt '*' service.launchctl debug org.cups.cupsd
|
||||
'''
|
||||
# Get return type
|
||||
return_stdout = kwargs.pop('return_stdout', False)
|
||||
|
||||
# Construct command
|
||||
cmd = ['launchctl', sub_cmd]
|
||||
cmd.extend(args)
|
||||
|
||||
# Run command
|
||||
kwargs['python_shell'] = False
|
||||
ret = __salt__['cmd.run_all'](cmd, **kwargs)
|
||||
|
||||
# Raise an error or return successful result
|
||||
if ret['retcode']:
|
||||
out = 'Failed to {0} service:\n'.format(sub_cmd)
|
||||
out += 'stdout: {0}\n'.format(ret['stdout'])
|
||||
out += 'stderr: {0}\n'.format(ret['stderr'])
|
||||
out += 'retcode: {0}\n'.format(ret['retcode'])
|
||||
raise CommandExecutionError(out)
|
||||
else:
|
||||
return ret['stdout'] if return_stdout else True
|
||||
return salt.utils.mac_utils.launchctl(sub_cmd, *args, **kwargs)
|
||||
|
||||
|
||||
def list_(name=None, runas=None):
|
||||
|
@ -541,7 +454,7 @@ def get_all(runas=None):
|
|||
enabled = get_enabled(runas=runas)
|
||||
|
||||
# Get list of all services
|
||||
available = list(_available_services().keys())
|
||||
available = list(salt.utils.mac_utils.available_services().keys())
|
||||
|
||||
# Return composite list
|
||||
return sorted(set(enabled + available))
|
||||
|
|
|
@ -8,6 +8,7 @@ System module for sleeping, restarting, and shutting down the system on Mac OS X
|
|||
Using this module will enable ``atrun`` on the system if it is disabled.
|
||||
'''
|
||||
from __future__ import absolute_import, unicode_literals, print_function
|
||||
import os
|
||||
|
||||
# Import python libs
|
||||
try: # python 3
|
||||
|
@ -18,9 +19,10 @@ except ImportError: # python 2
|
|||
import getpass
|
||||
|
||||
# Import salt libs
|
||||
from salt.ext import six
|
||||
import salt.utils.mac_utils
|
||||
import salt.utils.platform
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import SaltInvocationError, CommandExecutionError
|
||||
|
||||
__virtualname__ = 'system'
|
||||
|
||||
|
@ -47,15 +49,71 @@ def _atrun_enabled():
|
|||
'''
|
||||
Check to see if atrun is enabled on the system
|
||||
'''
|
||||
return __salt__['service.enabled']('com.apple.atrun')
|
||||
name = 'com.apple.atrun'
|
||||
services = salt.utils.mac_utils.available_services()
|
||||
label = None
|
||||
|
||||
if name in services:
|
||||
label = services[name]['plist']['Label']
|
||||
else:
|
||||
for service in six.itervalues(services):
|
||||
if service['file_path'].lower() == name:
|
||||
# Match on full path
|
||||
label = service['plist']['Label']
|
||||
break
|
||||
basename, ext = os.path.splitext(service['file_name'])
|
||||
if basename.lower() == name:
|
||||
# Match on basename
|
||||
label = service['plist']['Label']
|
||||
break
|
||||
|
||||
if not label:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Collect information on service: will raise an error if it fails
|
||||
salt.utils.mac_utils.launchctl('list',
|
||||
label,
|
||||
return_stdout=True,
|
||||
output_loglevel='quiet')
|
||||
return True
|
||||
except CommandExecutionError:
|
||||
return False
|
||||
|
||||
|
||||
def _enable_atrun():
|
||||
'''
|
||||
Enable and start the atrun daemon
|
||||
'''
|
||||
__salt__['service.enable']('com.apple.atrun')
|
||||
__salt__['service.start']('com.apple.atrun')
|
||||
name = 'com.apple.atrun'
|
||||
services = salt.utils.mac_utils.available_services()
|
||||
label = None
|
||||
path = None
|
||||
|
||||
if name in services:
|
||||
label = services[name]['plist']['Label']
|
||||
path = services[name]['file_path']
|
||||
else:
|
||||
for service in six.itervalues(services):
|
||||
if service['file_path'].lower() == name:
|
||||
# Match on full path
|
||||
label = service['plist']['Label']
|
||||
path = service['file_path']
|
||||
break
|
||||
basename, ext = os.path.splitext(service['file_name'])
|
||||
if basename.lower() == name:
|
||||
# Match on basename
|
||||
label = service['plist']['Label']
|
||||
path = service['file_path']
|
||||
break
|
||||
|
||||
if not label:
|
||||
return False
|
||||
|
||||
salt.utils.mac_utils.launchctl('enable',
|
||||
'system/{0}'.format(label),
|
||||
output_loglevel='quiet')
|
||||
salt.utils.mac_utils.launchctl('load', path, output_loglevel='quiet')
|
||||
return _atrun_enabled()
|
||||
|
||||
|
||||
|
|
|
@ -9,15 +9,19 @@ from __future__ import absolute_import, unicode_literals
|
|||
import logging
|
||||
import subprocess
|
||||
import os
|
||||
import plistlib
|
||||
import time
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.modules.cmdmod
|
||||
import salt.utils.args
|
||||
import salt.utils.decorators as decorators
|
||||
import salt.utils.files
|
||||
import salt.utils.path
|
||||
import salt.utils.platform
|
||||
import salt.utils.stringutils
|
||||
import salt.utils.timed_subprocess
|
||||
import salt.grains.extra
|
||||
from salt.ext import six
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError,\
|
||||
TimedProcTimeoutError
|
||||
|
||||
|
@ -227,3 +231,131 @@ def confirm_updated(value, check_fun, normalize_ret=False, wait=5):
|
|||
return True
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
|
||||
def launchctl(sub_cmd, *args, **kwargs):
|
||||
'''
|
||||
Run a launchctl command and raise an error if it fails
|
||||
|
||||
Args: additional args are passed to launchctl
|
||||
sub_cmd (str): Sub command supplied to launchctl
|
||||
|
||||
Kwargs: passed to ``cmd.run_all``
|
||||
return_stdout (bool): A keyword argument. If true return the stdout of
|
||||
the launchctl command
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if successful
|
||||
str: The stdout of the launchctl command if requested
|
||||
|
||||
Raises:
|
||||
CommandExecutionError: If command fails
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
import salt.utils.mac_service
|
||||
salt.utils.mac_service.launchctl('debug', 'org.cups.cupsd')
|
||||
'''
|
||||
# Get return type
|
||||
return_stdout = kwargs.pop('return_stdout', False)
|
||||
|
||||
# Construct command
|
||||
cmd = ['launchctl', sub_cmd]
|
||||
cmd.extend(args)
|
||||
|
||||
# Run command
|
||||
kwargs['python_shell'] = False
|
||||
ret = salt.modules.cmdmod.run_all(cmd, **kwargs)
|
||||
|
||||
# Raise an error or return successful result
|
||||
if ret['retcode']:
|
||||
out = 'Failed to {0} service:\n'.format(sub_cmd)
|
||||
out += 'stdout: {0}\n'.format(ret['stdout'])
|
||||
out += 'stderr: {0}\n'.format(ret['stderr'])
|
||||
out += 'retcode: {0}'.format(ret['retcode'])
|
||||
raise CommandExecutionError(out)
|
||||
else:
|
||||
return ret['stdout'] if return_stdout else True
|
||||
|
||||
|
||||
def _available_services():
|
||||
'''
|
||||
This is a helper function needed for testing. We are using the memoziation
|
||||
decorator on the `available_services` function, which causes the function
|
||||
to run once and then return the results of the first run on subsequent
|
||||
calls. This causes problems when trying to test the functionality of the
|
||||
`available_services` function.
|
||||
'''
|
||||
launchd_paths = [
|
||||
'/Library/LaunchAgents',
|
||||
'/Library/LaunchDaemons',
|
||||
'/System/Library/LaunchAgents',
|
||||
'/System/Library/LaunchDaemons',
|
||||
]
|
||||
_available_services = dict()
|
||||
for launch_dir in launchd_paths:
|
||||
for root, dirs, files in salt.utils.path.os_walk(launch_dir):
|
||||
for file_name in files:
|
||||
|
||||
# Must be a plist file
|
||||
if not file_name.endswith('.plist'):
|
||||
continue
|
||||
|
||||
# Follow symbolic links of files in _launchd_paths
|
||||
file_path = os.path.join(root, file_name)
|
||||
true_path = os.path.realpath(file_path)
|
||||
|
||||
# ignore broken symlinks
|
||||
if not os.path.exists(true_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
# This assumes most of the plist files
|
||||
# will be already in XML format
|
||||
plist = plistlib.readPlist(true_path)
|
||||
|
||||
except Exception:
|
||||
# If plistlib is unable to read the file we'll need to use
|
||||
# the system provided plutil program to do the conversion
|
||||
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
|
||||
true_path)
|
||||
plist_xml = salt.modules.cmdmod.run(cmd, output_loglevel='quiet')
|
||||
if six.PY2:
|
||||
plist = plistlib.readPlistFromString(plist_xml)
|
||||
else:
|
||||
plist = plistlib.loads(
|
||||
salt.utils.stringutils.to_bytes(plist_xml))
|
||||
|
||||
try:
|
||||
_available_services[plist.Label.lower()] = {
|
||||
'file_name': file_name,
|
||||
'file_path': true_path,
|
||||
'plist': plist}
|
||||
except AttributeError:
|
||||
# Handle malformed plist files
|
||||
_available_services[os.path.basename(file_name).lower()] = {
|
||||
'file_name': file_name,
|
||||
'file_path': true_path,
|
||||
'plist': plist}
|
||||
|
||||
return _available_services
|
||||
|
||||
|
||||
@decorators.memoize
|
||||
def available_services():
|
||||
'''
|
||||
Return a dictionary of all available services on the system
|
||||
|
||||
Returns:
|
||||
dict: All available services
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
import salt.utils.mac_service
|
||||
salt.utils.mac_service.available_services()
|
||||
'''
|
||||
return _available_services()
|
||||
|
|
|
@ -5,10 +5,11 @@ mac_utils tests
|
|||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON
|
||||
from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON, call
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.mac_utils as mac_utils
|
||||
|
@ -16,6 +17,7 @@ from salt.exceptions import SaltInvocationError, CommandExecutionError
|
|||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext.six.moves import range
|
||||
from salt.ext import six
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
|
@ -165,3 +167,314 @@ class MacUtilsTestCase(TestCase):
|
|||
'''
|
||||
self.assertEqual(mac_utils.validate_enabled(False),
|
||||
'off')
|
||||
|
||||
def test_launchctl(self):
|
||||
'''
|
||||
test launchctl function
|
||||
'''
|
||||
mock_cmd = MagicMock(return_value={'retcode': 0,
|
||||
'stdout': 'success',
|
||||
'stderr': 'none'})
|
||||
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
|
||||
ret = mac_utils.launchctl('enable', 'org.salt.minion')
|
||||
m_run_all.assert_called_with(
|
||||
['launchctl', 'enable', 'org.salt.minion'],
|
||||
python_shell=False)
|
||||
self.assertEqual(ret, True)
|
||||
|
||||
def test_launchctl_return_stdout(self):
|
||||
'''
|
||||
test launchctl function and return stdout
|
||||
'''
|
||||
mock_cmd = MagicMock(return_value={'retcode': 0,
|
||||
'stdout': 'success',
|
||||
'stderr': 'none'})
|
||||
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
|
||||
ret = mac_utils.launchctl('enable',
|
||||
'org.salt.minion',
|
||||
return_stdout=True)
|
||||
m_run_all.assert_called_with(['launchctl', 'enable', 'org.salt.minion'],
|
||||
python_shell=False)
|
||||
self.assertEqual(ret, 'success')
|
||||
|
||||
def test_launchctl_error(self):
|
||||
'''
|
||||
test launchctl function returning an error
|
||||
'''
|
||||
mock_cmd = MagicMock(return_value={'retcode': 1,
|
||||
'stdout': 'failure',
|
||||
'stderr': 'test failure'})
|
||||
error = 'Failed to enable service:\n' \
|
||||
'stdout: failure\n' \
|
||||
'stderr: test failure\n' \
|
||||
'retcode: 1'
|
||||
with patch('salt.modules.cmdmod.run_all', mock_cmd) as m_run_all:
|
||||
try:
|
||||
mac_utils.launchctl('enable', 'org.salt.minion')
|
||||
except CommandExecutionError as exc:
|
||||
self.assertEqual(exc.message, error)
|
||||
m_run_all.assert_called_with(['launchctl', 'enable', 'org.salt.minion'],
|
||||
python_shell=False)
|
||||
|
||||
@patch('salt.utils.path.os_walk')
|
||||
@patch('os.path.exists')
|
||||
@patch('plistlib.readPlist')
|
||||
def test_available_services(self, mock_read_plist, mock_exists, mock_os_walk):
|
||||
'''
|
||||
test available_services
|
||||
'''
|
||||
mock_os_walk.side_effect = [
|
||||
[('/Library/LaunchAgents', [], ['com.apple.lla1.plist', 'com.apple.lla2.plist'])],
|
||||
[('/Library/LaunchDaemons', [], ['com.apple.lld1.plist', 'com.apple.lld2.plist'])],
|
||||
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
|
||||
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
|
||||
]
|
||||
|
||||
mock_read_plist.side_effect = [
|
||||
MagicMock(Label='com.apple.lla1'),
|
||||
MagicMock(Label='com.apple.lla2'),
|
||||
MagicMock(Label='com.apple.lld1'),
|
||||
MagicMock(Label='com.apple.lld2'),
|
||||
MagicMock(Label='com.apple.slla1'),
|
||||
MagicMock(Label='com.apple.slla2'),
|
||||
MagicMock(Label='com.apple.slld1'),
|
||||
MagicMock(Label='com.apple.slld2'),
|
||||
]
|
||||
|
||||
mock_exists.return_value = True
|
||||
ret = mac_utils._available_services()
|
||||
|
||||
# Make sure it's a dict with 8 items
|
||||
self.assertTrue(isinstance(ret, dict))
|
||||
self.assertEqual(len(ret), 8)
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_name'],
|
||||
'com.apple.lla1.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/Library/LaunchAgents', 'com.apple.lla1.plist')))
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_name'],
|
||||
'com.apple.slld2.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/System/Library/LaunchDaemons', 'com.apple.slld2.plist')))
|
||||
|
||||
@patch('salt.utils.path.os_walk')
|
||||
@patch('os.path.exists')
|
||||
@patch('plistlib.readPlist')
|
||||
def test_available_services_broken_symlink(self, mock_read_plist, mock_exists, mock_os_walk):
|
||||
'''
|
||||
test available_services
|
||||
'''
|
||||
mock_os_walk.side_effect = [
|
||||
[('/Library/LaunchAgents', [], ['com.apple.lla1.plist', 'com.apple.lla2.plist'])],
|
||||
[('/Library/LaunchDaemons', [], ['com.apple.lld1.plist', 'com.apple.lld2.plist'])],
|
||||
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
|
||||
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
|
||||
]
|
||||
|
||||
mock_read_plist.side_effect = [
|
||||
MagicMock(Label='com.apple.lla1'),
|
||||
MagicMock(Label='com.apple.lla2'),
|
||||
MagicMock(Label='com.apple.lld1'),
|
||||
MagicMock(Label='com.apple.lld2'),
|
||||
MagicMock(Label='com.apple.slld1'),
|
||||
MagicMock(Label='com.apple.slld2'),
|
||||
]
|
||||
|
||||
mock_exists.side_effect = [True, True, True, True, False, False, True, True]
|
||||
ret = mac_utils._available_services()
|
||||
|
||||
# Make sure it's a dict with 6 items
|
||||
self.assertTrue(isinstance(ret, dict))
|
||||
self.assertEqual(len(ret), 6)
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_name'],
|
||||
'com.apple.lla1.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/Library/LaunchAgents', 'com.apple.lla1.plist')))
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_name'],
|
||||
'com.apple.slld2.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/System/Library/LaunchDaemons', 'com.apple.slld2.plist')))
|
||||
|
||||
@patch('salt.utils.path.os_walk')
|
||||
@patch('os.path.exists')
|
||||
@patch('plistlib.readPlist')
|
||||
@patch('salt.modules.cmdmod.run')
|
||||
@patch('plistlib.readPlistFromString' if six.PY2 else 'plistlib.loads')
|
||||
def test_available_services_non_xml(self,
|
||||
mock_read_plist_from_string,
|
||||
mock_run,
|
||||
mock_read_plist,
|
||||
mock_exists,
|
||||
mock_os_walk):
|
||||
'''
|
||||
test available_services
|
||||
'''
|
||||
mock_os_walk.side_effect = [
|
||||
[('/Library/LaunchAgents', [], ['com.apple.lla1.plist', 'com.apple.lla2.plist'])],
|
||||
[('/Library/LaunchDaemons', [], ['com.apple.lld1.plist', 'com.apple.lld2.plist'])],
|
||||
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
|
||||
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
|
||||
]
|
||||
mock_exists.return_value = True
|
||||
mock_read_plist.side_effect = Exception()
|
||||
mock_run.return_value = '<some xml>'
|
||||
mock_read_plist_from_string.side_effect = [
|
||||
MagicMock(Label='com.apple.lla1'),
|
||||
MagicMock(Label='com.apple.lla2'),
|
||||
MagicMock(Label='com.apple.lld1'),
|
||||
MagicMock(Label='com.apple.lld2'),
|
||||
MagicMock(Label='com.apple.slla1'),
|
||||
MagicMock(Label='com.apple.slla2'),
|
||||
MagicMock(Label='com.apple.slld1'),
|
||||
MagicMock(Label='com.apple.slld2'),
|
||||
]
|
||||
|
||||
ret = mac_utils._available_services()
|
||||
|
||||
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'
|
||||
calls = [
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
]
|
||||
mock_run.assert_has_calls(calls)
|
||||
|
||||
# Make sure it's a dict with 8 items
|
||||
self.assertTrue(isinstance(ret, dict))
|
||||
self.assertEqual(len(ret), 8)
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_name'],
|
||||
'com.apple.lla1.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/Library/LaunchAgents', 'com.apple.lla1.plist')))
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_name'],
|
||||
'com.apple.slld2.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/System/Library/LaunchDaemons', 'com.apple.slld2.plist')))
|
||||
|
||||
@patch('salt.utils.path.os_walk')
|
||||
@patch('os.path.exists')
|
||||
@patch('plistlib.readPlist')
|
||||
@patch('salt.modules.cmdmod.run')
|
||||
@patch('plistlib.readPlistFromString' if six.PY2 else 'plistlib.loads')
|
||||
def test_available_services_non_xml_malformed_plist(self,
|
||||
mock_read_plist_from_string,
|
||||
mock_run,
|
||||
mock_read_plist,
|
||||
mock_exists,
|
||||
mock_os_walk):
|
||||
'''
|
||||
test available_services
|
||||
'''
|
||||
mock_os_walk.side_effect = [
|
||||
[('/Library/LaunchAgents', [], ['com.apple.lla1.plist', 'com.apple.lla2.plist'])],
|
||||
[('/Library/LaunchDaemons', [], ['com.apple.lld1.plist', 'com.apple.lld2.plist'])],
|
||||
[('/System/Library/LaunchAgents', [], ['com.apple.slla1.plist', 'com.apple.slla2.plist'])],
|
||||
[('/System/Library/LaunchDaemons', [], ['com.apple.slld1.plist', 'com.apple.slld2.plist'])],
|
||||
]
|
||||
mock_exists.return_value = True
|
||||
mock_read_plist.side_effect = Exception()
|
||||
mock_run.return_value = '<some xml>'
|
||||
mock_read_plist_from_string.return_value = 'malformedness'
|
||||
|
||||
ret = mac_utils._available_services()
|
||||
|
||||
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'
|
||||
calls = [
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchAgents', 'com.apple.lla1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchAgents', 'com.apple.lla2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchDaemons', 'com.apple.lld1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/Library/LaunchDaemons', 'com.apple.lld2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchAgents', 'com.apple.slla1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchAgents', 'com.apple.slla2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchDaemons', 'com.apple.slld1.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
call(cmd.format(os.path.realpath(os.path.join(
|
||||
'/System/Library/LaunchDaemons', 'com.apple.slld2.plist'))),
|
||||
output_loglevel='quiet'),
|
||||
]
|
||||
mock_run.assert_has_calls(calls)
|
||||
|
||||
# Make sure it's a dict with 8 items
|
||||
self.assertTrue(isinstance(ret, dict))
|
||||
self.assertEqual(len(ret), 8)
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1.plist']['file_name'],
|
||||
'com.apple.lla1.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.lla1.plist']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/Library/LaunchAgents', 'com.apple.lla1.plist')))
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2.plist']['file_name'],
|
||||
'com.apple.slld2.plist')
|
||||
|
||||
self.assertEqual(
|
||||
ret['com.apple.slld2.plist']['file_path'],
|
||||
os.path.realpath(
|
||||
os.path.join('/System/Library/LaunchDaemons', 'com.apple.slld2.plist')))
|
||||
|
|
Loading…
Add table
Reference in a new issue