pec2 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'