diff --git a/ipalib/errors.py b/ipalib/errors.py index 6dd6eb01f..7191ff405 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -124,6 +124,14 @@ def _(text): return text +class SubprocessError(StandardError): + def __init__(self, returncode, argv): + self.returncode = returncode + self.argv = argv + StandardError.__init__(self, + 'return code %d from %r' % (returncode, argv) + ) + class HandledError(StandardError): """ Base class for errors that can be raised across a remote procedure call. diff --git a/ipalib/plugable.py b/ipalib/plugable.py index e6b5c1ac8..f3b35d30b 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -367,15 +367,22 @@ class Plugin(ReadOnly): assert not hasattr(self, name) setattr(self, name, getattr(api, name)) - def call(self, *args): + def call(self, executable, *args): """ - Call an external command via ``subprocess.call``. + Call ``executable`` with ``args`` using subprocess.call(). - Returns the exit status of the call. + If the call exits with a non-zero exit status, + `ipalib.errors.SubprocessError` is raised, from which you can retrieve + the exit code by checking the SubprocessError.returncode attribute. + + This method does *not* return what ``executable`` sent to stdout... for + that, use `Plugin.callread()`. """ - if hasattr(self, 'log'): - self.log.debug('Calling %r', args) - return subprocess.call(args) + argv = (executable,) + args + self.debug('Calling %r', argv) + returncode = subprocess.call(argv) + if returncode != 0: + raise errors.SubprocessError(returncode, argv) def __repr__(self): """ diff --git a/tests/test_ipalib/test_plugable.py b/tests/test_ipalib/test_plugable.py index 21c712809..b381675b6 100644 --- a/tests/test_ipalib/test_plugable.py +++ b/tests/test_ipalib/test_plugable.py @@ -452,6 +452,18 @@ class test_Plugin(ClassChecker): o.finalize() assert o.__islocked__() + def test_call(self): + """ + Test the `ipalib.plugable.Plugin.call` method. + """ + o = self.cls() + o.call('/bin/true') is None + e = raises(errors.SubprocessError, o.call, '/bin/false') + assert str(e) == 'return code %d from %r' % (1, ('/bin/false',)) + assert e.returncode == 1 + assert e.argv == ('/bin/false',) + + class test_PluginProxy(ClassChecker): """