/usr/share/pyshared/qm/host.py is in qmtest 2.4.1-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | ########################################################################
#
# File: host.py
# Author: Mark Mitchell
# Date: 2005-06-03
#
# Contents:
# Host
#
# Copyright (c) 2005 by CodeSourcery, LLC. All rights reserved.
#
########################################################################
########################################################################
# Imports
#######################################################################
from qm.executable import RedirectedExecutable
from qm.extension import Extension
import os.path
########################################################################
# Classes
#######################################################################
class Host(Extension):
"""A 'Host' is a logical machine.
Each logical machine has a default directory. When a file is
uploaded to or downloaded from the machine, and a relative path
is specified, the patch is relative to the default directory.
Similarly, when a program is run on the remote machine, its
initial working directory is the default directory.
The interface presented by 'Host' is a lowest common
denominator. The objective is not to expose all the functionality
of any host; rather it is to provide an interface that can be used
on many hosts."""
kind = "host"
class Executable(RedirectedExecutable):
"""An 'Executable' is a simple redirected executable.
The standard error and standard output streams are combined
into a single stream.
The standard input is not closed before
invoking the program because SSH hangs if its standard input
is closed before it is invoked. For example, running:
ssh machine echo hi <&-
will hang with some versions of SSH."""
def _StderrPipe(self):
return None
def __init__(self, arguments = None, **args):
if arguments: args.update(arguments)
super(Host, self).__init__(**args)
def Run(self, path, arguments, environment = None, timeout = -1,
relative = False):
"""Run a program on the remote host.
'path' -- The name of the program to run, on the remote host.
If 'relative' is true, or if 'path' is not an absolute path
but does contain at least one directory separator, then 'path'
is interpreted relative to the default directory. Otherwise,
'path' is used unmodified.
'arguments' -- The sequence of arguments that should be passed
to the program.
'environment' -- If not 'None', a dictionary of pairs of
strings to add to the environment of the running program.
'timeout' -- The number of seconds the program is permitted
to execute. After the 'timeout' expires, the program will be
terminated. However, in some cases (such as when using 'rsh')
it will be the local side of the connection that is closed.
The remote side of the connection may or may not continue to
operate, depending on the vagaries of the remote operating
system.
returns -- A pair '(status, output)'. The 'status' is the
exit status returned by the program, or 'None' if the exit
status is not available. The 'output' is a string giving the
combined standard output and standard error output from the
program."""
# Compute the full environment for the child.
if environment is not None:
new_environment = os.environ.copy()
new_environment.update(environment)
environment = new_environment
executable = self.Executable(timeout)
if relative:
path = os.path.join(os.curdir, path)
status = executable.Run([path] + arguments, environment)
return (status, executable.stdout)
def UploadFile(self, local_file, remote_file = None):
"""Copy 'local_file' to 'remote_file'.
'local_file' -- The name of the file on the local machine.
'remote_file' -- The name of the file on the remote machine.
The 'remote_file' must be a relative path. It is interpreted
relative to the default directory. If 'None', the
'remote_file' is placed in the default directory using the
basename of the 'local_file'.
If the 'local_file' and 'remote_file' are the same, then this
function succeeds, but takes no action."""
raise NotImplementedError
def DownloadFile(self, remote_file, local_file):
"""Copy 'remote_file' to 'local_file'.
'remote_file' -- The name of the file on the remote machine.
The 'remote_file' must be a relative path. It is interpreted
relative to the default directory.
'local_file' -- The name of the file on the local machine. If
'None', the 'local_file' is placed in the current directory
using the basename of the 'remote_file'.
If the 'local_file' and 'remote_file' are the same, then this
function succeeds, but takes no action."""
raise NotImplementedError
def UploadAndRun(self, path, arguments, environment = None,
timeout = -1):
"""Run a program on the remote host.
'path' -- The name of the program to run, as a path on the
local machine.
'arguments' -- As for 'Run'.
'environment' -- As for 'Run'.
'timeout' -- As for 'Run'.
returns -- As for 'Run'.
The program is uploaded to the default directory on the remote
host, run, and then deleted."""
self.UploadFile(path)
basename = os.path.basename(path)
result = self.Run(basename,
arguments,
environment,
timeout,
relative = True)
self.DeleteFile(basename)
return result
def DeleteFile(self, remote_file):
"""Delete the 'remote_file'.
'remote_file' -- A relative path to the file to be deleted."""
raise NotImplementedError
|