pec2 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,108 +0,0 @@
1
- # Copyright (c) 2009-2012, Andrew McNabb
2
- # Copyright (c) 2003-2008, Brent N. Chun
3
-
4
- import fcntl
5
- import string
6
- import sys
7
-
8
- HOST_FORMAT = 'Host format is [user@]host[:port] [user]'
9
-
10
-
11
- def read_host_files(paths, default_user=None, default_port=None):
12
- """Reads the given host files.
13
-
14
- Returns a list of (host, port, user) triples.
15
- """
16
- hosts = []
17
- if paths:
18
- for path in paths:
19
- hosts.extend(read_host_file(path, default_user=default_user))
20
- return hosts
21
-
22
-
23
- def read_host_file(path, default_user=None, default_port=None):
24
- """Reads the given host file.
25
-
26
- Lines are of the form: host[:port] [login].
27
- Returns a list of (host, port, user) triples.
28
- """
29
- lines = []
30
- f = open(path)
31
- for line in f:
32
- lines.append(line.strip())
33
- f.close()
34
-
35
- hosts = []
36
- for line in lines:
37
- # Skip blank lines or lines starting with #
38
- line = line.strip()
39
- if not line or line.startswith('#'):
40
- continue
41
- host, port, user = parse_host_entry(line, default_user, default_port)
42
- if host:
43
- hosts.append((host, port, user))
44
- return hosts
45
-
46
-
47
- # TODO: deprecate the second host field and standardize on the
48
- # [user@]host[:port] format.
49
- def parse_host_entry(line, default_user, default_port):
50
- """Parses a single host entry.
51
-
52
- This may take either the of the form [user@]host[:port] or
53
- host[:port][ user].
54
-
55
- Returns a (host, port, user) triple.
56
- """
57
- fields = line.split()
58
- if len(fields) > 2:
59
- sys.stderr.write('Bad line: "%s". Format should be'
60
- ' [user@]host[:port] [user]\n' % line)
61
- return None, None, None
62
- host_field = fields[0]
63
- host, port, user = parse_host(host_field, default_port=default_port)
64
- if len(fields) == 2:
65
- if user is None:
66
- user = fields[1]
67
- else:
68
- sys.stderr.write('User specified twice in line: "%s"\n' % line)
69
- return None, None, None
70
- if user is None:
71
- user = default_user
72
- return host, port, user
73
-
74
-
75
- def parse_host_string(host_string, default_user=None, default_port=None):
76
- """Parses a whitespace-delimited string of "[user@]host[:port]" entries.
77
-
78
- Returns a list of (host, port, user) triples.
79
- """
80
- hosts = []
81
- entries = host_string.split()
82
- for entry in entries:
83
- hosts.append(parse_host(entry, default_user, default_port))
84
- return hosts
85
-
86
-
87
- def parse_host(host, default_user=None, default_port=None):
88
- """Parses host entries of the form "[user@]host[:port]".
89
-
90
- Returns a (host, port, user) triple.
91
- """
92
- # TODO: when we stop supporting Python 2.4, switch to using str.partition.
93
- user = default_user
94
- port = default_port
95
- if '@' in host:
96
- user, host = host.split('@', 1)
97
- if ':' in host:
98
- host, port = host.rsplit(':', 1)
99
- return (host, port, user)
100
-
101
-
102
- def set_cloexec(filelike):
103
- """Sets the underlying filedescriptor to automatically close on exec.
104
-
105
- If set_cloexec is called for all open files, then subprocess.Popen does
106
- not require the close_fds option.
107
- """
108
- fcntl.fcntl(filelike.fileno(), fcntl.FD_CLOEXEC, 1)
data/exe/psshlib/task.py DELETED
@@ -1,288 +0,0 @@
1
- # Copyright (c) 2009-2012, Andrew McNabb
2
-
3
- from errno import EINTR
4
- from subprocess import Popen, PIPE
5
- import os
6
- import signal
7
- import sys
8
- import time
9
- import traceback
10
-
11
- from psshlib import askpass_client
12
- from psshlib import color
13
-
14
- BUFFER_SIZE = 1 << 16
15
-
16
- try:
17
- bytes
18
- except NameError:
19
- bytes = str
20
-
21
-
22
- class Task(object):
23
- """Starts a process and manages its input and output.
24
-
25
- Upon completion, the `exitstatus` attribute is set to the exit status
26
- of the process.
27
- """
28
- def __init__(self, host, port, user, cmd, opts, stdin=None):
29
- self.exitstatus = None
30
-
31
- self.host = host
32
- self.pretty_host = host
33
- self.port = port
34
- self.cmd = cmd
35
-
36
- if user != opts.user:
37
- self.pretty_host = '@'.join((user, self.pretty_host))
38
- if port:
39
- self.pretty_host = ':'.join((self.pretty_host, port))
40
-
41
- self.proc = None
42
- self.writer = None
43
- self.timestamp = None
44
- self.failures = []
45
- self.killed = False
46
- self.inputbuffer = stdin
47
- self.byteswritten = 0
48
- self.outputbuffer = bytes()
49
- self.errorbuffer = bytes()
50
-
51
- self.stdin = None
52
- self.stdout = None
53
- self.stderr = None
54
- self.outfile = None
55
- self.errfile = None
56
-
57
- # Set options.
58
- self.verbose = opts.verbose
59
- try:
60
- self.print_out = bool(opts.print_out)
61
- except AttributeError:
62
- self.print_out = False
63
- try:
64
- self.inline = bool(opts.inline)
65
- except AttributeError:
66
- self.inline = False
67
- try:
68
- self.inline_stdout = bool(opts.inline_stdout)
69
- except AttributeError:
70
- self.inline_stdout = False
71
-
72
- def start(self, nodenum, iomap, writer, askpass_socket=None):
73
- """Starts the process and registers files with the IOMap."""
74
- self.writer = writer
75
-
76
- if writer:
77
- self.outfile, self.errfile = writer.open_files(self.pretty_host)
78
-
79
- # Set up the environment.
80
- environ = dict(os.environ)
81
- environ['PSSH_NODENUM'] = str(nodenum)
82
- environ['PSSH_HOST'] = self.host
83
- # Disable the GNOME pop-up password dialog and allow ssh to use
84
- # askpass.py to get a provided password. If the module file is
85
- # askpass.pyc, we replace the extension.
86
- environ['SSH_ASKPASS'] = askpass_client.executable_path()
87
- if askpass_socket:
88
- environ['PSSH_ASKPASS_SOCKET'] = askpass_socket
89
- if self.verbose:
90
- environ['PSSH_ASKPASS_VERBOSE'] = '1'
91
- # Work around a mis-feature in ssh where it won't call SSH_ASKPASS
92
- # if DISPLAY is unset.
93
- if 'DISPLAY' not in environ:
94
- environ['DISPLAY'] = 'pssh-gibberish'
95
-
96
- # Create the subprocess. Since we carefully call set_cloexec() on
97
- # all open files, we specify close_fds=False.
98
- self.proc = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
99
- close_fds=False, preexec_fn=os.setsid, env=environ)
100
- self.timestamp = time.time()
101
- if self.inputbuffer:
102
- self.stdin = self.proc.stdin
103
- iomap.register_write(self.stdin.fileno(), self.handle_stdin)
104
- else:
105
- self.proc.stdin.close()
106
- self.stdout = self.proc.stdout
107
- iomap.register_read(self.stdout.fileno(), self.handle_stdout)
108
- self.stderr = self.proc.stderr
109
- iomap.register_read(self.stderr.fileno(), self.handle_stderr)
110
-
111
- def _kill(self):
112
- """Signals the process to terminate."""
113
- if self.proc:
114
- try:
115
- os.kill(-self.proc.pid, signal.SIGKILL)
116
- except OSError:
117
- # If the kill fails, then just assume the process is dead.
118
- pass
119
- self.killed = True
120
-
121
- def timedout(self):
122
- """Kills the process and registers a timeout error."""
123
- if not self.killed:
124
- self._kill()
125
- self.failures.append('Timed out')
126
-
127
- def interrupted(self):
128
- """Kills the process and registers an keyboard interrupt error."""
129
- if not self.killed:
130
- self._kill()
131
- self.failures.append('Interrupted')
132
-
133
- def cancel(self):
134
- """Stops a task that has not started."""
135
- self.failures.append('Cancelled')
136
-
137
- def elapsed(self):
138
- """Finds the time in seconds since the process was started."""
139
- return time.time() - self.timestamp
140
-
141
- def running(self):
142
- """Finds if the process has terminated and saves the return code."""
143
- if self.stdin or self.stdout or self.stderr:
144
- return True
145
- if self.proc:
146
- self.exitstatus = self.proc.poll()
147
- if self.exitstatus is None:
148
- if self.killed:
149
- # Set the exitstatus to what it would be if we waited.
150
- self.exitstatus = -signal.SIGKILL
151
- return False
152
- else:
153
- return True
154
- else:
155
- if self.exitstatus < 0:
156
- message = 'Killed by signal %s' % (-self.exitstatus)
157
- self.failures.append(message)
158
- elif self.exitstatus > 0:
159
- message = 'Exited with error code %s' % self.exitstatus
160
- self.failures.append(message)
161
- self.proc = None
162
- return False
163
-
164
- def handle_stdin(self, fd, iomap):
165
- """Called when the process's standard input is ready for writing."""
166
- try:
167
- start = self.byteswritten
168
- if start < len(self.inputbuffer):
169
- chunk = self.inputbuffer[start:start+BUFFER_SIZE]
170
- self.byteswritten = start + os.write(fd, chunk)
171
- else:
172
- self.close_stdin(iomap)
173
- except (OSError, IOError):
174
- _, e, _ = sys.exc_info()
175
- if e.errno != EINTR:
176
- self.close_stdin(iomap)
177
- self.log_exception(e)
178
-
179
- def close_stdin(self, iomap):
180
- if self.stdin:
181
- iomap.unregister(self.stdin.fileno())
182
- self.stdin.close()
183
- self.stdin = None
184
-
185
- def handle_stdout(self, fd, iomap):
186
- """Called when the process's standard output is ready for reading."""
187
- try:
188
- buf = os.read(fd, BUFFER_SIZE)
189
- if buf:
190
- if self.inline or self.inline_stdout:
191
- self.outputbuffer += buf
192
- if self.outfile:
193
- self.writer.write(self.outfile, buf)
194
- if self.print_out:
195
- sys.stdout.write('%s: %s' % (self.host, buf))
196
- if buf[-1] != '\n':
197
- sys.stdout.write('\n')
198
- else:
199
- self.close_stdout(iomap)
200
- except (OSError, IOError):
201
- _, e, _ = sys.exc_info()
202
- if e.errno != EINTR:
203
- self.close_stdout(iomap)
204
- self.log_exception(e)
205
-
206
- def close_stdout(self, iomap):
207
- if self.stdout:
208
- iomap.unregister(self.stdout.fileno())
209
- self.stdout.close()
210
- self.stdout = None
211
- if self.outfile:
212
- self.writer.close(self.outfile)
213
- self.outfile = None
214
-
215
- def handle_stderr(self, fd, iomap):
216
- """Called when the process's standard error is ready for reading."""
217
- try:
218
- buf = os.read(fd, BUFFER_SIZE)
219
- if buf:
220
- if self.inline:
221
- self.errorbuffer += buf
222
- if self.errfile:
223
- self.writer.write(self.errfile, buf)
224
- else:
225
- self.close_stderr(iomap)
226
- except (OSError, IOError):
227
- _, e, _ = sys.exc_info()
228
- if e.errno != EINTR:
229
- self.close_stderr(iomap)
230
- self.log_exception(e)
231
-
232
- def close_stderr(self, iomap):
233
- if self.stderr:
234
- iomap.unregister(self.stderr.fileno())
235
- self.stderr.close()
236
- self.stderr = None
237
- if self.errfile:
238
- self.writer.close(self.errfile)
239
- self.errfile = None
240
-
241
- def log_exception(self, e):
242
- """Saves a record of the most recent exception for error reporting."""
243
- if self.verbose:
244
- exc_type, exc_value, exc_traceback = sys.exc_info()
245
- exc = ("Exception: %s, %s, %s" %
246
- (exc_type, exc_value, traceback.format_tb(exc_traceback)))
247
- else:
248
- exc = str(e)
249
- self.failures.append(exc)
250
-
251
- def report(self, n):
252
- """Pretty prints a status report after the Task completes."""
253
- error = ', '.join(self.failures)
254
- tstamp = time.asctime().split()[3] # Current time
255
- if color.has_colors(sys.stdout):
256
- progress = color.c("[%s]" % color.B(n))
257
- success = color.g("[%s]" % color.B("SUCCESS"))
258
- failure = color.r("[%s]" % color.B("FAILURE"))
259
- stderr = color.r("Stderr: ")
260
- error = color.r(color.B(error))
261
- else:
262
- progress = "[%s]" % n
263
- success = "[SUCCESS]"
264
- failure = "[FAILURE]"
265
- stderr = "Stderr: "
266
- host = self.pretty_host
267
- if self.failures:
268
- print(' '.join((progress, tstamp, failure, host, error)))
269
- else:
270
- print(' '.join((progress, tstamp, success, host)))
271
- # NOTE: The extra flushes are to ensure that the data is output in
272
- # the correct order with the C implementation of io.
273
- if self.outputbuffer:
274
- sys.stdout.flush()
275
- try:
276
- sys.stdout.buffer.write(self.outputbuffer)
277
- sys.stdout.flush()
278
- except AttributeError:
279
- sys.stdout.write(self.outputbuffer)
280
- if self.errorbuffer:
281
- sys.stdout.write(stderr)
282
- # Flush the TextIOWrapper before writing to the binary buffer.
283
- sys.stdout.flush()
284
- try:
285
- sys.stdout.buffer.write(self.errorbuffer)
286
- except AttributeError:
287
- sys.stdout.write(self.errorbuffer)
288
-
@@ -1 +0,0 @@
1
- VERSION = '2.3.1'