1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os, tarfile, tempfile
17
18 from twisted.python import log
19 from twisted.internet import defer
20
21 from buildslave.commands.base import Command
22
24 """
25 Upload a file from slave to build master
26 Arguments:
27
28 - ['workdir']: base directory to use
29 - ['slavesrc']: name of the slave-side file to read from
30 - ['writer']: RemoteReference to a transfer._FileWriter object
31 - ['maxsize']: max size (in bytes) of file to write
32 - ['blocksize']: max size for each data block
33 """
34 debug = False
35
37 self.workdir = args['workdir']
38 self.filename = args['slavesrc']
39 self.writer = args['writer']
40 self.remaining = args['maxsize']
41 self.blocksize = args['blocksize']
42 self.stderr = None
43 self.rc = 0
44
46 if self.debug:
47 log.msg('SlaveFileUploadCommand started')
48
49
50 self.path = os.path.join(self.builder.basedir,
51 self.workdir,
52 os.path.expanduser(self.filename))
53 try:
54 self.fp = open(self.path, 'rb')
55 if self.debug:
56 log.msg("Opened '%s' for upload" % self.path)
57 except:
58 self.fp = None
59 self.stderr = "Cannot open file '%s' for upload" % self.path
60 self.rc = 1
61 if self.debug:
62 log.msg("Cannot open file '%s' for upload" % self.path)
63
64 self.sendStatus({'header': "sending %s" % self.path})
65
66 d = defer.Deferred()
67 self._reactor.callLater(0, self._loop, d)
68 def _close_ok(res):
69 self.fp = None
70 return self.writer.callRemote("close")
71 def _close_err(f):
72 self.fp = None
73
74 d1 = self.writer.callRemote("close")
75 def eb(f2):
76 log.msg("ignoring error from remote close():")
77 log.err(f2)
78 d1.addErrback(eb)
79 d1.addBoth(lambda _ : f)
80 return d1
81 d.addCallbacks(_close_ok, _close_err)
82 d.addBoth(self.finished)
83 return d
84
85 - def _loop(self, fire_when_done):
86 d = defer.maybeDeferred(self._writeBlock)
87 def _done(finished):
88 if finished:
89 fire_when_done.callback(None)
90 else:
91 self._loop(fire_when_done)
92 def _err(why):
93 fire_when_done.errback(why)
94 d.addCallbacks(_done, _err)
95 return None
96
98 """Write a block of data to the remote writer"""
99
100 if self.interrupted or self.fp is None:
101 if self.debug:
102 log.msg('SlaveFileUploadCommand._writeBlock(): end')
103 return True
104
105 length = self.blocksize
106 if self.remaining is not None and length > self.remaining:
107 length = self.remaining
108
109 if length <= 0:
110 if self.stderr is None:
111 self.stderr = 'Maximum filesize reached, truncating file \'%s\'' \
112 % self.path
113 self.rc = 1
114 data = ''
115 else:
116 data = self.fp.read(length)
117
118 if self.debug:
119 log.msg('SlaveFileUploadCommand._writeBlock(): '+
120 'allowed=%d readlen=%d' % (length, len(data)))
121 if len(data) == 0:
122 log.msg("EOF: callRemote(close)")
123 return True
124
125 if self.remaining is not None:
126 self.remaining = self.remaining - len(data)
127 assert self.remaining >= 0
128 d = self.writer.callRemote('write', data)
129 d.addCallback(lambda res: False)
130 return d
131
133 if self.debug:
134 log.msg('interrupted')
135 if self.interrupted:
136 return
137 if self.stderr is None:
138 self.stderr = 'Upload of \'%s\' interrupted' % self.path
139 self.rc = 1
140 self.interrupted = True
141
142
144 if self.debug:
145 log.msg('finished: stderr=%r, rc=%r' % (self.stderr, self.rc))
146 if self.stderr is None:
147 self.sendStatus({'rc': self.rc})
148 else:
149 self.sendStatus({'stderr': self.stderr, 'rc': self.rc})
150 return res
151
152
154 """
155 Upload a directory from slave to build master
156 Arguments:
157
158 - ['workdir']: base directory to use
159 - ['slavesrc']: name of the slave-side directory to read from
160 - ['writer']: RemoteReference to a transfer._DirectoryWriter object
161 - ['maxsize']: max size (in bytes) of file to write
162 - ['blocksize']: max size for each data block
163 - ['compress']: one of [None, 'bz2', 'gz']
164 """
165 debug = False
166
168 self.workdir = args['workdir']
169 self.dirname = args['slavesrc']
170 self.writer = args['writer']
171 self.remaining = args['maxsize']
172 self.blocksize = args['blocksize']
173 self.compress = args['compress']
174 self.stderr = None
175 self.rc = 0
176
178 if self.debug:
179 log.msg('SlaveDirectoryUploadCommand started')
180
181 self.path = os.path.join(self.builder.basedir,
182 self.workdir,
183 os.path.expanduser(self.dirname))
184 if self.debug:
185 log.msg("path: %r" % self.path)
186
187
188 fd, self.tarname = tempfile.mkstemp()
189 fileobj = os.fdopen(fd, 'w')
190 if self.compress == 'bz2':
191 mode='w|bz2'
192 elif self.compress == 'gz':
193 mode='w|gz'
194 else:
195 mode = 'w'
196 archive = tarfile.open(name=self.tarname, mode=mode, fileobj=fileobj)
197 archive.add(self.path, '')
198 archive.close()
199 fileobj.close()
200
201
202 self.fp = open(self.tarname, 'rb')
203
204 self.sendStatus({'header': "sending %s" % self.path})
205
206 d = defer.Deferred()
207 self._reactor.callLater(0, self._loop, d)
208 def unpack(res):
209
210 d1 = self.writer.callRemote("unpack")
211 d1.addErrback(log.err)
212 d1.addCallback(lambda ignored: res)
213 return d1
214 d.addCallback(unpack)
215 d.addBoth(self.finished)
216 return d
217
219 self.fp.close()
220 os.remove(self.tarname)
221 if self.debug:
222 log.msg('finished: stderr=%r, rc=%r' % (self.stderr, self.rc))
223 if self.stderr is None:
224 self.sendStatus({'rc': self.rc})
225 else:
226 self.sendStatus({'stderr': self.stderr, 'rc': self.rc})
227 return res
228
229
231 """
232 Download a file from master to slave
233 Arguments:
234
235 - ['workdir']: base directory to use
236 - ['slavedest']: name of the slave-side file to be created
237 - ['reader']: RemoteReference to a transfer._FileReader object
238 - ['maxsize']: max size (in bytes) of file to write
239 - ['blocksize']: max size for each data block
240 - ['mode']: access mode for the new file
241 """
242 debug = False
243
245 self.workdir = args['workdir']
246 self.filename = args['slavedest']
247 self.reader = args['reader']
248 self.bytes_remaining = args['maxsize']
249 self.blocksize = args['blocksize']
250 self.mode = args['mode']
251 self.stderr = None
252 self.rc = 0
253
255 if self.debug:
256 log.msg('SlaveFileDownloadCommand starting')
257
258
259 self.path = os.path.join(self.builder.basedir,
260 self.workdir,
261 os.path.expanduser(self.filename))
262
263 dirname = os.path.dirname(self.path)
264 if not os.path.exists(dirname):
265 os.makedirs(dirname)
266
267 try:
268 self.fp = open(self.path, 'wb')
269 if self.debug:
270 log.msg("Opened '%s' for download" % self.path)
271 if self.mode is not None:
272
273
274
275
276
277
278
279 os.chmod(self.path, self.mode)
280 except IOError:
281
282 self.fp = None
283 self.stderr = "Cannot open file '%s' for download" % self.path
284 self.rc = 1
285 if self.debug:
286 log.msg("Cannot open file '%s' for download" % self.path)
287
288 d = defer.Deferred()
289 self._reactor.callLater(0, self._loop, d)
290 def _close(res):
291
292 d1 = self.reader.callRemote('close')
293 d1.addErrback(log.err)
294 d1.addCallback(lambda ignored: res)
295 return d1
296 d.addBoth(_close)
297 d.addBoth(self.finished)
298 return d
299
300 - def _loop(self, fire_when_done):
301 d = defer.maybeDeferred(self._readBlock)
302 def _done(finished):
303 if finished:
304 fire_when_done.callback(None)
305 else:
306 self._loop(fire_when_done)
307 def _err(why):
308 fire_when_done.errback(why)
309 d.addCallbacks(_done, _err)
310 return None
311
313 """Read a block of data from the remote reader."""
314
315 if self.interrupted or self.fp is None:
316 if self.debug:
317 log.msg('SlaveFileDownloadCommand._readBlock(): end')
318 return True
319
320 length = self.blocksize
321 if self.bytes_remaining is not None and length > self.bytes_remaining:
322 length = self.bytes_remaining
323
324 if length <= 0:
325 if self.stderr is None:
326 self.stderr = "Maximum filesize reached, truncating file '%s'" \
327 % self.path
328 self.rc = 1
329 return True
330 else:
331 d = self.reader.callRemote('read', length)
332 d.addCallback(self._writeData)
333 return d
334
336 if self.debug:
337 log.msg('SlaveFileDownloadCommand._readBlock(): readlen=%d' %
338 len(data))
339 if len(data) == 0:
340 return True
341
342 if self.bytes_remaining is not None:
343 self.bytes_remaining = self.bytes_remaining - len(data)
344 assert self.bytes_remaining >= 0
345 self.fp.write(data)
346 return False
347
349 if self.debug:
350 log.msg('interrupted')
351 if self.interrupted:
352 return
353 if self.stderr is None:
354 self.stderr = "Download of '%s' interrupted" % self.path
355 self.rc = 1
356 self.interrupted = True
357
358
359
361 if self.fp is not None:
362 self.fp.close()
363
364 if self.debug:
365 log.msg('finished: stderr=%r, rc=%r' % (self.stderr, self.rc))
366 if self.stderr is None:
367 self.sendStatus({'rc': self.rc})
368 else:
369 self.sendStatus({'stderr': self.stderr, 'rc': self.rc})
370 return res
371