diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index 03590475b2..ce25890328 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -6,6 +6,9 @@ not all of the options may have been provided yet. For a complete reference, see the `Parallels Desktop Reference Guide `_. +This module requires the prlctl binary to be installed to run most functions. +To run parallels.prlsrvctl, the prlsrvctl binary is required. + What has not been implemented yet can be accessed through ``parallels.prlctl`` and ``parallels.prlsrvctl`` (note the preceding double dash ``--`` as necessary): @@ -29,7 +32,7 @@ import shlex import salt.utils.locales import salt.utils.path import salt.utils.yaml -from salt.exceptions import SaltInvocationError +from salt.exceptions import SaltInvocationError, CommandExecutionError # Import 3rd party libs from salt.ext import six @@ -43,17 +46,6 @@ log = logging.getLogger(__name__) GUID_REGEX = re.compile(r'{?([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})}?', re.I) -def __virtual__(): - ''' - Load this module if prlctl is available - ''' - if not salt.utils.path.which('prlctl'): - return (False, 'prlctl utility not available') - if not salt.utils.path.which('prlsrvctl'): - return (False, 'prlsrvctl utility not available') - return __virtualname__ - - def _normalize_args(args): ''' Return args as a list of strings @@ -111,6 +103,9 @@ def prlsrvctl(sub_cmd, args=None, runas=None): salt '*' parallels.prlsrvctl usb list runas=macdev salt -- '*' parallels.prlsrvctl set '--mem-limit auto' runas=macdev ''' + if not salt.utils.path.which('prlsrvctl'): + raise CommandExecutionError('prlsrvctl utility not available') + # Construct command cmd = ['prlsrvctl', sub_cmd] if args: @@ -141,6 +136,9 @@ def prlctl(sub_cmd, args=None, runas=None): salt '*' parallels.prlctl exec 'macvm uname' runas=macdev salt -- '*' parallels.prlctl capture 'macvm --file macvm.display.png' runas=macdev ''' + if not salt.utils.path.which('prlctl'): + raise CommandExecutionError('prlctl utility not available') + # Construct command cmd = ['prlctl', sub_cmd] if args: diff --git a/tests/unit/modules/test_parallels.py b/tests/unit/modules/test_parallels.py index 302a7e8930..953092316a 100644 --- a/tests/unit/modules/test_parallels.py +++ b/tests/unit/modules/test_parallels.py @@ -6,7 +6,7 @@ import textwrap # Import Salt Libs import salt.modules.parallels as parallels -from salt.exceptions import SaltInvocationError +from salt.exceptions import SaltInvocationError, CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin @@ -25,27 +25,6 @@ class ParallelsTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {parallels: {}} - def test___virtual__(self): - ''' - Test parallels.__virtual__ - ''' - mock_true = MagicMock(return_value=True) - mock_false = MagicMock(return_value=False) - - # Validate false return - with patch('salt.utils.path.which', mock_false): - ret = parallels.__virtual__() - self.assertTrue(isinstance(ret, tuple)) - self.assertEqual(len(ret), 2) - self.assertFalse(ret[0]) - self.assertTrue(isinstance(ret[1], six.string_types)) - - # Validate true return - with patch('salt.utils.path.which', mock_true): - ret = parallels.__virtual__() - self.assertTrue(ret) - self.assertEqual(ret, 'parallels') - def test__normalize_args(self): ''' Test parallels._normalize_args @@ -94,26 +73,34 @@ class ParallelsTestCase(TestCase, LoaderModuleMockMixin): ''' runas = 'macdev' - # Validate 'prlsrvctl info' - info_cmd = ['prlsrvctl', 'info'] - info_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': info_fcn}): - parallels.prlsrvctl('info', runas=runas) - info_fcn.assert_called_once_with(info_cmd, runas=runas) + # Test missing prlsrvctl binary + with patch('salt.utils.path.which', MagicMock(return_value=False)): + with self.assertRaises(CommandExecutionError): + parallels.prlsrvctl('info', runas=runas) - # Validate 'prlsrvctl usb list' - usb_cmd = ['prlsrvctl', 'usb', 'list'] - usb_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': usb_fcn}): - parallels.prlsrvctl('usb', 'list', runas=runas) - usb_fcn.assert_called_once_with(usb_cmd, runas=runas) + # Simulate the existence of prlsrvctl + with patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/prlsrvctl")): - # Validate 'prlsrvctl set "--mem-limit auto"' - set_cmd = ['prlsrvctl', 'set', '--mem-limit', 'auto'] - set_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': set_fcn}): - parallels.prlsrvctl('set', '--mem-limit auto', runas=runas) - set_fcn.assert_called_once_with(set_cmd, runas=runas) + # Validate 'prlsrvctl info' + info_cmd = ['prlsrvctl', 'info'] + info_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': info_fcn}): + parallels.prlsrvctl('info', runas=runas) + info_fcn.assert_called_once_with(info_cmd, runas=runas) + + # Validate 'prlsrvctl usb list' + usb_cmd = ['prlsrvctl', 'usb', 'list'] + usb_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': usb_fcn}): + parallels.prlsrvctl('usb', 'list', runas=runas) + usb_fcn.assert_called_once_with(usb_cmd, runas=runas) + + # Validate 'prlsrvctl set "--mem-limit auto"' + set_cmd = ['prlsrvctl', 'set', '--mem-limit', 'auto'] + set_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': set_fcn}): + parallels.prlsrvctl('set', '--mem-limit auto', runas=runas) + set_fcn.assert_called_once_with(set_cmd, runas=runas) def test_prlctl(self): ''' @@ -121,26 +108,34 @@ class ParallelsTestCase(TestCase, LoaderModuleMockMixin): ''' runas = 'macdev' - # Validate 'prlctl user list' - user_cmd = ['prlctl', 'user', 'list'] - user_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': user_fcn}): - parallels.prlctl('user', 'list', runas=runas) - user_fcn.assert_called_once_with(user_cmd, runas=runas) + # Test missing prlctl binary + with patch('salt.utils.path.which', MagicMock(return_value=False)): + with self.assertRaises(CommandExecutionError): + parallels.prlctl('info', runas=runas) - # Validate 'prlctl exec "macvm uname"' - exec_cmd = ['prlctl', 'exec', 'macvm', 'uname'] - exec_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': exec_fcn}): - parallels.prlctl('exec', 'macvm uname', runas=runas) - exec_fcn.assert_called_once_with(exec_cmd, runas=runas) + # Simulate the existence of prlctl + with patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/prlctl")): - # Validate 'prlctl capture "macvm --file macvm.display.png"' - cap_cmd = ['prlctl', 'capture', 'macvm', '--file', 'macvm.display.png'] - cap_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': cap_fcn}): - parallels.prlctl('capture', 'macvm --file macvm.display.png', runas=runas) - cap_fcn.assert_called_once_with(cap_cmd, runas=runas) + # Validate 'prlctl user list' + user_cmd = ['prlctl', 'user', 'list'] + user_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': user_fcn}): + parallels.prlctl('user', 'list', runas=runas) + user_fcn.assert_called_once_with(user_cmd, runas=runas) + + # Validate 'prlctl exec "macvm uname"' + exec_cmd = ['prlctl', 'exec', 'macvm', 'uname'] + exec_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': exec_fcn}): + parallels.prlctl('exec', 'macvm uname', runas=runas) + exec_fcn.assert_called_once_with(exec_cmd, runas=runas) + + # Validate 'prlctl capture "macvm --file macvm.display.png"' + cap_cmd = ['prlctl', 'capture', 'macvm', '--file', 'macvm.display.png'] + cap_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': cap_fcn}): + parallels.prlctl('capture', 'macvm --file macvm.display.png', runas=runas) + cap_fcn.assert_called_once_with(cap_cmd, runas=runas) def test_list_vms(self): '''