forked from third-party-mirrors/zfs_autobackup
script mode wip
This commit is contained in:
parent
81d0bee7ae
commit
86706ca24f
@ -157,7 +157,15 @@ class TestExecuteNode(unittest2.TestCase):
|
||||
self.assertEqual(nodeb.run(cmd=["cat", ExecuteNode.PIPE, "pwd"], cwd="/tmp/space test"), ["/tmp/space test"])
|
||||
|
||||
|
||||
def test_script(self):
|
||||
|
||||
def stdout_handler(line):
|
||||
print("handle: " + line)
|
||||
|
||||
nodea=ExecuteNode(debug_output=True, ssh_to="localhost")
|
||||
|
||||
cmd_pipe=nodea.script(lines=["echo line1", "echo line 2"])
|
||||
cmd_pipe.execute(stdout_handler)
|
||||
|
||||
|
||||
|
||||
|
@ -92,17 +92,18 @@ class ExecuteNode(LogStub):
|
||||
return_stderr=False, pipe=False, return_all=False, cwd=None):
|
||||
"""run a command on the node , checks output and parses/handle output and returns it
|
||||
|
||||
Takes care of proper quoting/escaping/ssh and logging of stdout/err/exit codes.
|
||||
|
||||
Either uses a local shell (sh -c) or remote shell (ssh) to execute the command.
|
||||
Therefore the command can have stuff like actual pipes in it, if you dont want to use pipe=True to pipe stuff.
|
||||
|
||||
:param cmd: the actual command, should be a list, where the first item is the command
|
||||
and the rest are parameters. use ExecuteNode.PIPE to add an unescaped |
|
||||
(if you want to use system piping instead of python piping)
|
||||
:param pipe: return CmdPipe instead of executing it.
|
||||
:param pipe: return CmdPipe instead of executing it. (pipe this into anoter run() command via inp=...)
|
||||
:param inp: Can be None, a string or a CmdPipe that was previously returned.
|
||||
:param tab_split: split tabbed files in output into a list
|
||||
:param valid_exitcodes: list of valid exit codes for this command (checks exit code of both sides of a pipe)
|
||||
Use [] to accept all exit codes. Default [0]
|
||||
:param valid_exitcodes: list of valid exit codes for this command. Use [] to accept all exit codes. Default [0]
|
||||
:param readonly: make this True if the command doesn't make any changes and is safe to execute in testmode
|
||||
:param hide_errors: don't show stderr output as error, instead show it as debugging output (use to hide expected errors)
|
||||
:param return_stderr: return both stdout and stderr as a tuple. (normally only returns stdout)
|
||||
@ -176,3 +177,76 @@ class ExecuteNode(LogStub):
|
||||
return output_lines, error_lines
|
||||
else:
|
||||
return output_lines
|
||||
|
||||
def script(self, lines, inp=None, valid_exitcodes=None, readonly=False, hide_errors=False):
|
||||
"""Run a multiline script on the node.
|
||||
|
||||
This is much more low level than run() and allows for finer grained control.
|
||||
|
||||
Either uses a local shell (sh -c) or remote shell (ssh) to execute the command.
|
||||
It will always return a CmdPipe that you should call execute on, or pipe to another script. (via inp=...)
|
||||
You need to do your own escaping/quoting.
|
||||
It will do logging of stderr and exit codes, but you should
|
||||
specify your stdout handler when calling CmdPipe.execute.
|
||||
Also specify the optional stderr/exit code handlers if you need them.
|
||||
Handlers are called for each line.
|
||||
It wont collect lines internally like run() does, so streams of data can be of unlimited size.
|
||||
|
||||
:param lines: list of lines of the actual script.
|
||||
:param inp: Can be None, a string or a CmdPipe that was previously returned.
|
||||
:param readonly: make this True if the command doesn't make any changes and is safe to execute in testmode
|
||||
:param valid_exitcodes: list of valid exit codes for this command. Use [] to accept all exit codes. Default [0]
|
||||
:param hide_errors: don't show stderr output as error, instead show it as debugging output (use to hide expected errors)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# create new pipe?
|
||||
if not isinstance(inp, CmdPipe):
|
||||
cmd_pipe = CmdPipe(self.readonly, inp)
|
||||
else:
|
||||
# add stuff to existing pipe
|
||||
cmd_pipe = inp
|
||||
|
||||
|
||||
def stderr_handler(line):
|
||||
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):
|
||||
self.error("Script returned exit code {} (valid codes: {})".format(cmd_item, exit_code, valid_exitcodes))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
#build command
|
||||
cmd=[]
|
||||
|
||||
#add remote shell
|
||||
if not self.is_local():
|
||||
#note: dont escape this part (executed directly without shell)
|
||||
cmd.append("ssh")
|
||||
|
||||
if self.ssh_config is not None:
|
||||
cmd.append(["-F", self.ssh_config])
|
||||
|
||||
cmd.append(self.ssh_to)
|
||||
|
||||
# convert to script
|
||||
cmd.append("\n".join(lines))
|
||||
|
||||
# add shell command and handlers to pipe
|
||||
cmd_item=CmdItem(cmd=cmd, readonly=readonly, stderr_handler=stderr_handler, exit_handler=exit_handler, shell=self.is_local())
|
||||
cmd_pipe.add(cmd_item)
|
||||
|
||||
self.debug("SCRIPT > {}".format(cmd_pipe))
|
||||
|
||||
return cmd_pipe
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user