better handling of piped exit codes
This commit is contained in:
@ -17,12 +17,13 @@ class CmdPipe:
|
||||
self.readonly = readonly
|
||||
self._should_execute = True
|
||||
|
||||
def add(self, cmd, readonly=False, stderr_handler=None):
|
||||
def add(self, cmd, readonly=False, stderr_handler=None, exit_handler=None):
|
||||
"""adds a command to pipe"""
|
||||
|
||||
self.items.append({
|
||||
'cmd': cmd,
|
||||
'stderr_handler': stderr_handler
|
||||
'stderr_handler': stderr_handler,
|
||||
'exit_handler': exit_handler
|
||||
})
|
||||
|
||||
if not readonly and self.readonly:
|
||||
@ -117,10 +118,15 @@ class CmdPipe:
|
||||
if eof_count == len(selectors) and done_count == len(self.items):
|
||||
break
|
||||
|
||||
# ret = []
|
||||
#close filehandles
|
||||
last_stdout.close()
|
||||
for item in self.items:
|
||||
item['process'].stderr.close()
|
||||
# ret.append(item['process'].returncode)
|
||||
|
||||
#call exit handlers
|
||||
for item in self.items:
|
||||
if item['exit_handler'] is not None:
|
||||
item['exit_handler'](item['process'].returncode)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
@ -5,6 +5,8 @@ import subprocess
|
||||
from zfs_autobackup.CmdPipe import CmdPipe
|
||||
from zfs_autobackup.LogStub import LogStub
|
||||
|
||||
class ExecuteError(Exception):
|
||||
pass
|
||||
|
||||
class ExecuteNode(LogStub):
|
||||
"""an endpoint to execute local or remote commands via ssh"""
|
||||
@ -108,9 +110,20 @@ class ExecuteNode(LogStub):
|
||||
error_lines.append(line.rstrip())
|
||||
self._parse_stderr(line, hide_errors)
|
||||
|
||||
# exit code hanlder
|
||||
if valid_exitcodes is None:
|
||||
valid_exitcodes = [0]
|
||||
|
||||
def exit_handler(exit_code):
|
||||
if self.debug_output:
|
||||
self.debug("EXIT > {}".format(exit_code))
|
||||
|
||||
if (valid_exitcodes != []) and (exit_code not in valid_exitcodes):
|
||||
raise (ExecuteError("Command '{}' return exit code '{}' (valid codes: {})".format(" ".join(cmd), exit_code, valid_exitcodes)))
|
||||
|
||||
# add command to pipe
|
||||
encoded_cmd = self._remote_cmd(cmd)
|
||||
p.add(cmd=encoded_cmd, readonly=readonly, stderr_handler=stderr_handler)
|
||||
p.add(cmd=encoded_cmd, readonly=readonly, stderr_handler=stderr_handler, exit_handler=exit_handler)
|
||||
|
||||
# return pipe instead of executing?
|
||||
if pipe:
|
||||
@ -130,21 +143,8 @@ class ExecuteNode(LogStub):
|
||||
else:
|
||||
self.debug("CMDSKIP> {}".format(p))
|
||||
|
||||
# execute and verify exit codes
|
||||
if p.execute(stdout_handler=stdout_handler) and valid_exitcodes is not []:
|
||||
if valid_exitcodes is None:
|
||||
valid_exitcodes = [0]
|
||||
|
||||
item_nr=1
|
||||
for item in p.items:
|
||||
exit_code=item['process'].returncode
|
||||
|
||||
if self.debug_output:
|
||||
self.debug("EXIT{} > {}".format(item_nr, exit_code))
|
||||
|
||||
if exit_code not in valid_exitcodes:
|
||||
raise (subprocess.CalledProcessError(exit_code, " ".join(item['cmd'])))
|
||||
item_nr=item_nr+1
|
||||
# execute and calls handlers in CmdPipe
|
||||
p.execute(stdout_handler=stdout_handler)
|
||||
|
||||
if return_stderr:
|
||||
return output_lines, error_lines
|
||||
|
||||
@ -3,6 +3,7 @@ import subprocess
|
||||
import time
|
||||
|
||||
from zfs_autobackup.CachedProperty import CachedProperty
|
||||
from zfs_autobackup.ExecuteNode import ExecuteError
|
||||
|
||||
|
||||
class ZfsDataset:
|
||||
@ -250,7 +251,7 @@ class ZfsDataset:
|
||||
self.invalidate()
|
||||
self.force_exists = False
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
except ExecuteError:
|
||||
if not fail_exception:
|
||||
return False
|
||||
else:
|
||||
|
||||
@ -10,6 +10,7 @@ from zfs_autobackup.Thinner import Thinner
|
||||
from zfs_autobackup.CachedProperty import CachedProperty
|
||||
from zfs_autobackup.ZfsPool import ZfsPool
|
||||
from zfs_autobackup.ZfsDataset import ZfsDataset
|
||||
from zfs_autobackup.ExecuteNode import ExecuteError
|
||||
|
||||
|
||||
class ZfsNode(ExecuteNode):
|
||||
@ -81,7 +82,7 @@ class ZfsNode(ExecuteNode):
|
||||
|
||||
try:
|
||||
self.run(cmd, hide_errors=True, valid_exitcodes=[0, 1])
|
||||
except subprocess.CalledProcessError:
|
||||
except ExecuteError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user