1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22 from __future__ import with_statement
23 import os
24 import urllib2
25 import time
26 from entropy.const import etpConst
27 from entropy.output import TextInterface, darkblue, darkred, purple, blue, \
28 brown, darkgreen, red, bold
29 from entropy.exceptions import *
30 from entropy.i18n import _
31 from entropy.misc import TimeScheduled, ParallelTask
32 from entropy.core import SystemSettings
33
35
36 - def __init__(self, url, path_to_save, checksum = True,
37 show_speed = True, resume = True,
38 abort_check_func = None, disallow_redirect = False,
39 thread_stop_func = None, speed_limit = None,
40 OutputInterface = None):
41
42 self.__system_settings = SystemSettings()
43 if speed_limit == None:
44 speed_limit = self.__system_settings['repositories']['transfer_limit']
45
46 self.progress = None
47 import entropy.tools as entropyTools
48 import socket
49 self.entropyTools, self.socket = entropyTools, socket
50 self.__th_id = 0
51 self.__resume = resume
52 self.__url = self.__encode_url(url)
53 self.__path_to_save = path_to_save
54 self.__checksum = checksum
55 self.__show_speed = show_speed
56 self.__abort_check_func = abort_check_func
57 self.__thread_stop_func = thread_stop_func
58 self.__disallow_redirect = disallow_redirect
59 self.__speedlimit = speed_limit
60 self.__existed_before = False
61 self.localfile = None
62
63
64 self.__datatransfer = 0
65 self.__resumed = False
66
67 uname = os.uname()
68 self.user_agent = "Entropy/%s (compatible; %s; %s: %s %s %s)" % (
69 etpConst['entropyversion'],
70 "Entropy",
71 os.path.basename(self.__url),
72 uname[0],
73 uname[4],
74 uname[2],
75 )
76 self.__extra_header_data = {}
77 self.__Output = OutputInterface
78 if self.__Output == None:
79 self.__Output = TextInterface()
80 elif not hasattr(self.__Output,'updateProgress'):
81 mytxt = _("Output interface passed doesn't have the updateProgress method")
82 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
83 elif not callable(self.__Output.updateProgress):
84 mytxt = _("Output interface passed doesn't have the updateProgress method")
85 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
86
88
89 self.__resumed = False
90 self.__buffersize = 8192
91 self.__status = None
92 self.__remotefile = None
93 self.__downloadedsize = 0
94 self.__average = 0
95 self.__remotesize = 0
96 self.__oldaverage = 0.0
97
98 self.__startingposition = 0
99 self.__datatransfer = 0
100 self.__time_remaining = "(infinite)"
101 self.__time_remaining_secs = 0
102 self.__elapsed = 0.0
103 self.__updatestep = 0.2
104 self.__transferpollingtime = float(1)/4
105 self.__existed_before = False
106 if os.path.lexists(self.__path_to_save):
107 self.__existed_before = True
108 self.__setup_resume_support()
109 self._setup_proxy()
110
112
113
114
115
116 if isinstance(self.localfile, file):
117 try:
118 self.localfile.flush()
119 self.localfile.close()
120 except (IOError, OSError,):
121 pass
122
123
124 if os.path.isfile(self.__path_to_save) and \
125 os.access(self.__path_to_save,os.W_OK) and self.__resume:
126
127 self.localfile = open(self.__path_to_save,"awb")
128 self.localfile.seek(0,2)
129 self.__startingposition = int(self.localfile.tell())
130 self.__resumed = True
131
132 elif os.path.lexists(self.__path_to_save) and not \
133 self.entropyTools.is_valid_path(self.__path_to_save):
134 try:
135 os.remove(self.__path_to_save)
136 except OSError:
137 pass
138 self.localfile = open(self.__path_to_save,"wb")
139
141
142 mydict = {}
143 proxy_data = self.__system_settings['system']['proxy']
144 if proxy_data['ftp']:
145 mydict['ftp'] = proxy_data['ftp']
146 if proxy_data['http']:
147 mydict['http'] = proxy_data['http']
148 if mydict:
149 mydict['username'] = proxy_data['username']
150 mydict['password'] = proxy_data['password']
151 self.entropyTools.add_proxy_opener(urllib2, mydict)
152 else:
153
154 urllib2._opener = None
155
157 import urllib
158 url = os.path.join(os.path.dirname(url),
159 urllib.quote(os.path.basename(url)))
160 return url
161
164
166
167 self._init_vars()
168 self.speedUpdater = TimeScheduled(
169 self.__transferpollingtime,
170 self.__update_speed,
171 )
172 self.speedUpdater.start()
173
174 self.socket.setdefaulttimeout(20)
175
176 if self.__url.startswith("http://"):
177 headers = { 'User-Agent' : self.user_agent }
178 req = urllib2.Request(self.__url, self.__extra_header_data, headers)
179 else:
180 req = self.__url
181
182 u_agent_error = False
183 while 1:
184
185 try:
186 self.__remotefile = urllib2.urlopen(req)
187 except KeyboardInterrupt:
188 self.__close(True)
189 raise
190 except urllib2.HTTPError, e:
191 if (e.code == 405) and not u_agent_error:
192
193 req = self.__url
194 u_agent_error = True
195 continue
196 self.__close(True)
197 self.__status = "-3"
198 return self.__status
199 except:
200 self.__close(True)
201 self.__status = "-3"
202 return self.__status
203 break
204
205 try:
206 self.__remotesize = int(self.__remotefile.headers.get(
207 "content-length"))
208 self.__remotefile.close()
209 except KeyboardInterrupt:
210 self.__close(True)
211 raise
212 except:
213 pass
214
215
216 try:
217 request = self.__url
218 if ((self.__startingposition > 0) and (self.__remotesize > 0)) \
219 and (self.__startingposition < self.__remotesize):
220
221 try:
222 request = urllib2.Request(
223 self.__url,
224 headers = {
225 "Range" : "bytes=" + \
226 str(self.__startingposition) + "-" + \
227 str(self.__remotesize)
228 }
229 )
230 except KeyboardInterrupt:
231 self.__close(True)
232 raise
233 except:
234 pass
235 elif (self.__startingposition == self.__remotesize):
236
237 self.__close(False)
238 return self.__prepare_return()
239 else:
240 self.localfile = open(self.__path_to_save,"wb")
241 self.__remotefile = urllib2.urlopen(request)
242 except KeyboardInterrupt:
243 self.__close(True)
244 raise
245 except:
246 self.__close(True)
247 self.__status = "-3"
248 return self.__status
249
250 if self.__remotesize > 0:
251 self.__remotesize = float(int(self.__remotesize))/1024
252
253 if self.__disallow_redirect and \
254 (self.__url != self.__remotefile.geturl()):
255
256 self.__close(True)
257 self.__status = "-3"
258 return self.__status
259
260 while 1:
261 try:
262 rsx = self.__remotefile.read(self.__buffersize)
263 if rsx == '': break
264 if self.__abort_check_func != None:
265 self.__abort_check_func()
266 if self.__thread_stop_func != None:
267 self.__thread_stop_func()
268 except KeyboardInterrupt:
269 self.__close(True)
270 raise
271 except self.socket.timeout:
272 self.__close(True)
273 self.__status = "-4"
274 return self.__status
275 except:
276
277 self.__close(True)
278 self.__status = "-3"
279 return self.__status
280 self.__commit(rsx)
281 if self.__show_speed:
282 self.handle_statistics(self.__th_id, self.__downloadedsize,
283 self.__remotesize, self.__average, self.__oldaverage,
284 self.__updatestep, self.__show_speed, self.__datatransfer,
285 self.__time_remaining, self.__time_remaining_secs
286 )
287 self.updateProgress()
288 self.__oldaverage = self.__average
289 if self.__speedlimit:
290 while self.__datatransfer > self.__speedlimit*1024:
291 time.sleep(0.1)
292 if self.__show_speed:
293 self.updateProgress()
294 self.__oldaverage = self.__average
295
296
297 self.__close(False)
298 return self.__prepare_return()
299
300
302 if self.__checksum:
303 self.__status = self.entropyTools.md5sum(self.__path_to_save)
304 return self.__status
305 self.__status = "-2"
306 return self.__status
307
309
310 self.localfile.write(mybuffer)
311
312 self.__downloadedsize = self.localfile.tell()
313 kbytecount = float(self.__downloadedsize)/1024
314 self.__average = int((kbytecount/self.__remotesize)*100)
315
317 try:
318 if isinstance(self.localfile, file):
319 self.localfile.flush()
320 self.localfile.close()
321 except IOError:
322 pass
323 if (not self.__existed_before) and errored:
324 try:
325 os.remove(self.__path_to_save)
326 except OSError:
327 pass
328 try:
329 self.__remotefile.close()
330 except:
331 pass
332 self.speedUpdater.kill()
333 self.socket.setdefaulttimeout(2)
334
336 self.__elapsed += self.__transferpollingtime
337
338 x_delta = self.__downloadedsize - self.__startingposition
339 self.__datatransfer = x_delta / self.__elapsed
340 if self.__datatransfer < 0:
341 self.__datatransfer = 0
342 try:
343 rounded_remote = int(round(self.__remotesize*1024,0))
344 rounded_downloaded = int(round(self.__downloadedsize,0))
345 x_delta = rounded_remote - rounded_downloaded
346 tx_round = int(round(x_delta/self.__datatransfer,0))
347 self.__time_remaining_secs = tx_round
348 self.__time_remaining = self.entropyTools.convert_seconds_to_fancy_output(self.__time_remaining_secs)
349 except:
350 self.__time_remaining = "(%s)" % (_("infinite"),)
351
353 return self.__datatransfer
354
356 return self.__resumed
357
358 - def handle_statistics(self, th_id, downloaded_size, total_size,
359 average, old_average, update_step, show_speed, data_transfer,
360 time_remaining, time_remaining_secs):
362
364
365 mytxt = _("[F]")
366 eta_txt = _("ETA")
367 sec_txt = _("sec")
368
369 currentText = darkred(" %s: " % (mytxt,)) + \
370 darkgreen(str(round(float(self.__downloadedsize)/1024,1))) + "/" + \
371 red(str(round(self.__remotesize,1))) + " kB"
372
373 barsize = 10
374 bartext = "["
375 curbarsize = 1
376 if self.__average > self.__oldaverage+self.__updatestep:
377 averagesize = (self.__average*barsize)/100
378 while averagesize > 0:
379 curbarsize += 1
380 bartext += "="
381 averagesize -= 1
382 bartext += ">"
383 diffbarsize = barsize-curbarsize
384 while diffbarsize > 0:
385 bartext += " "
386 diffbarsize -= 1
387 if self.__show_speed:
388 bartext += "] => %s" % (self.entropyTools.bytes_into_human(self.__datatransfer),)
389 bartext += "/%s : %s: %s" % (sec_txt,eta_txt,self.__time_remaining,)
390 else:
391 bartext += "]"
392 average = str(self.__average)
393 if len(average) < 2:
394 average = " "+average
395 currentText += " <-> "+average+"% "+bartext
396 self.__Output.updateProgress(currentText, back = True)
397
399
400 import entropy.tools as entropyTools
401 - def __init__(self, url_path_list, checksum = True,
402 show_speed = True, resume = True,
403 abort_check_func = None, disallow_redirect = False,
404 OutputInterface = None, UrlFetcherClass = None):
405 """
406 @param url_path_list list [(url,path_to_save,),...]
407 @param checksum bool return checksum data
408 @param show_speed bool show transfer speed on the output
409 @param resume bool enable resume support
410 @param abort_check_func callable function that could
411 raise exception and stop transfer
412 @param disallow_redirect bool disable automatic HTTP redirect
413 @param OutputInterface TextInterface instance used to
414 print instance output through a common interface
415 @param UrlFetcherClass, urlFetcher instance/interface used
416 """
417 self.__system_settings = SystemSettings()
418 self.__url_path_list = url_path_list
419 self.__resume = resume
420 self.__checksum = checksum
421 self.__show_speed = show_speed
422 self.__abort_check_func = abort_check_func
423 self.__disallow_redirect = disallow_redirect
424
425
426 self.__data_transfer = 0
427 self.__average = 0
428 self.__old_average = 0
429 self.__time_remaining_sec = 0
430
431 self.__Output = OutputInterface
432 if self.__Output == None:
433 self.__Output = TextInterface()
434 elif not hasattr(self.__Output,'updateProgress'):
435 mytxt = _("Output interface passed doesn't have the updateProgress method")
436 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
437 elif not callable(self.__Output.updateProgress):
438 mytxt = _("Output interface passed doesn't have the updateProgress method")
439 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
440
441 self.__url_fetcher = UrlFetcherClass
442 if self.__url_fetcher == None:
443 self.__url_fetcher = UrlFetcher
444
445
449
451 self.__progress_data = {}
452 self.__thread_pool = {}
453 self.__download_statuses = {}
454 self.__show_progress = False
455 self.__stop_threads = False
456 self.__first_refreshes = 50
457 self.__data_transfer = 0
458 self.__average = 0
459 self.__old_average = 0
460 self.__time_remaining_sec = 0
461
463 self._init_vars()
464
465 th_id = 0
466 speed_limit = 0
467 dsl = self.__system_settings['repositories']['transfer_limit']
468 if isinstance(dsl,int) and self.__url_path_list:
469 speed_limit = dsl/len(self.__url_path_list)
470
471 for url, path_to_save in self.__url_path_list:
472 th_id += 1
473 downloader = self.__url_fetcher(url, path_to_save,
474 checksum = self.__checksum, show_speed = self.__show_speed,
475 resume = self.__resume, abort_check_func = self.__abort_check_func,
476 disallow_redirect = self.__disallow_redirect,
477 thread_stop_func = self.__handle_threads_stop,
478 speed_limit = speed_limit,
479 OutputInterface = self.__Output
480 )
481 downloader.set_id(th_id)
482 downloader.updateProgress = self.updateProgress
483 downloader.handle_statistics = self.handle_statistics
484
485 def do_download(ds, th_id, downloader):
486 ds[th_id] = downloader.download()
487
488 t = ParallelTask(do_download, self.__download_statuses, th_id, downloader)
489 self.__thread_pool[th_id] = t
490 t.start()
491 self.show_download_files_info()
492 self.__show_progress = True
493 while len(self.__url_path_list) != len(self.__download_statuses):
494 try:
495 time.sleep(0.5)
496 except (SystemExit, KeyboardInterrupt,):
497 self.__stop_threads = True
498 raise
499
500 return self.__download_statuses
501
503 return self.__data_transfer
504
506 return self.__average
507
509 return self.__time_remaining_sec
510
512 count = 0
513 pl = self.__url_path_list[:]
514 self.__Output.updateProgress(
515 "%s: %s %s" % (
516 darkblue(_("Aggregated download")),
517 darkred(str(len(pl))),
518 darkblue(_("items")),
519 ),
520 importance = 0,
521 type = "info",
522 header = purple(" ## ")
523 )
524 for url, save_path in pl:
525 count += 1
526 fname = os.path.basename(url)
527 uri = self.entropyTools.spliturl(url)[1]
528 self.__Output.updateProgress(
529 "[%s] %s => %s" % (
530 darkblue(str(count)),
531 darkgreen(uri),
532 blue(fname),
533 ),
534 importance = 0,
535 type = "info",
536 header = brown(" # ")
537 )
538
539 - def handle_statistics(self, th_id, downloaded_size, total_size,
540 average, old_average, update_step, show_speed, data_transfer,
541 time_remaining, time_remaining_secs):
542 data = {
543 'th_id': th_id,
544 'downloaded_size': downloaded_size,
545 'total_size': total_size,
546 'average': average,
547 'old_average': old_average,
548 'update_step': update_step,
549 'show_speed': show_speed,
550 'data_transfer': data_transfer,
551 'time_remaining': time_remaining,
552 'time_remaining_secs': time_remaining_secs,
553 }
554 self.__progress_data[th_id] = data
555
557
558 eta_txt = _("ETA")
559 sec_txt = _("sec")
560 downloaded_size = 0
561 total_size = 0
562 time_remaining = 0
563 data_transfer = 0
564 update_step = 0
565 average = 100
566 pd = self.__progress_data.copy()
567 pdlen = len(pd)
568
569
570 for th_id in sorted(pd):
571 data = pd.get(th_id)
572 downloaded_size += data.get('downloaded_size',0)
573 total_size += data.get('total_size',0)
574 data_transfer += data.get('data_transfer',0)
575 tr = data.get('time_remaining_secs',0)
576 if tr > 0: time_remaining += tr
577 update_step += data.get('update_step',0)
578
579
580
581 if total_size > 0:
582 average = int(float(downloaded_size/1024)/total_size * 100)
583 self.__data_transfer = data_transfer
584 self.__average = average
585 update_step = update_step/pdlen
586 self.__time_remaining_sec = time_remaining
587 time_remaining = self.entropyTools.convert_seconds_to_fancy_output(time_remaining)
588
589 if ((average > self.__old_average+update_step) or \
590 (self.__first_refreshes > 0)) and self.__show_progress:
591
592 self.__first_refreshes -= 1
593 currentText = darkgreen(str(round(float(downloaded_size)/1024,1))) + "/" + \
594 red(str(round(total_size,1))) + " kB"
595
596 barsize = 10
597 bartext = "["
598 curbarsize = 1
599 averagesize = (average*barsize)/100
600 while averagesize > 0:
601 curbarsize += 1
602 bartext += "="
603 averagesize -= 1
604 bartext += ">"
605 diffbarsize = barsize-curbarsize
606 while diffbarsize > 0:
607 bartext += " "
608 diffbarsize -= 1
609 if self.__show_speed:
610 bartext += "] => %s" % (self.entropyTools.bytes_into_human(data_transfer),)
611 bartext += "/%s : %s: %s" % (sec_txt,eta_txt,time_remaining,)
612 else:
613 bartext += "]"
614 myavg = str(average)
615 if len(myavg) < 2:
616 myavg = " "+myavg
617 currentText += " <-> "+myavg+"% "+bartext+" "
618 self.__Output.updateProgress(currentText, back = True)
619
620 self.__old_average = average
621
622
624
625
626 - def __init__(self, ftpuri, OutputInterface, verbose = True):
627
628 if not hasattr(OutputInterface,'updateProgress'):
629 mytxt = _("OutputInterface does not have an updateProgress method")
630 raise IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (OutputInterface,mytxt,))
631 elif not callable(OutputInterface.updateProgress):
632 mytxt = _("OutputInterface does not have an updateProgress method")
633 raise IncorrectParameter("IncorrectParameter: %s, (! %s !)" % (OutputInterface,mytxt,))
634
635 import socket, ftplib
636 import entropy.tools as entropyTools
637 self.socket, self.ftplib, self.entropyTools = socket, ftplib, entropyTools
638 self.Entropy = OutputInterface
639 self.__verbose = verbose
640 self.__init_vars()
641 self.socket.setdefaulttimeout(60)
642 self.__ftpuri = ftpuri
643 self.__speed_updater = None
644 self.__currentdir = '.'
645 self.__ftphost = self.entropyTools.extract_ftp_host_from_uri(self.__ftpuri)
646 self.__ftpuser, self.__ftppassword, self.__ftpport, self.__ftpdir = self.entropyTools.extract_ftp_data(ftpuri)
647
648 count = 10
649 while 1:
650 count -= 1
651 try:
652 self.__ftpconn = self.ftplib.FTP(self.__ftphost)
653 break
654 except (self.socket.gaierror,), e:
655 raise ConnectionError('ConnectionError: %s' % (e,))
656 except (self.socket.error,), e:
657 if not count:
658 raise ConnectionError('ConnectionError: %s' % (e,))
659 continue
660 except:
661 if not count: raise
662 continue
663
664 if self.__verbose:
665 mytxt = _("connecting with user")
666 self.Entropy.updateProgress(
667 "[ftp:%s] %s: %s" % (darkgreen(self.__ftphost),mytxt,blue(self.__ftpuser),),
668 importance = 1,
669 type = "info",
670 header = darkgreen(" * ")
671 )
672 try:
673 self.__ftpconn.login(self.__ftpuser,self.__ftppassword)
674 except self.ftplib.error_perm, e:
675 raise FtpError('FtpError: %s' % (e,))
676 if self.__verbose:
677 mytxt = _("switching to")
678 self.Entropy.updateProgress(
679 "[ftp:%s] %s: %s" % (darkgreen(self.__ftphost),mytxt,blue(self.__ftpdir),),
680 importance = 1,
681 type = "info",
682 header = darkgreen(" * ")
683 )
684 self.set_cwd(self.__ftpdir, dodir = True)
685
687 self.__oldprogress = 0.0
688 self.__filesize = 0
689 self.__filekbcount = 0
690 self.__transfersize = 0
691 self.__startingposition = 0
692 self.__elapsed = 0.0
693 self.__time_remaining_secs = 0
694 self.__time_remaining = "(%s)" % (_("infinite"),)
695 self.__transferpollingtime = float(1)/4
696
698 return self.set_cwd(self.__ftpdir)
699
700
702
703 self.socket.setdefaulttimeout(60)
704 counter = 10
705 while 1:
706 counter -= 1
707 try:
708 self.__ftpconn = self.ftplib.FTP(self.__ftphost)
709 break
710 except:
711 if not counter:
712 raise
713 continue
714 if self.__verbose:
715 mytxt = _("reconnecting with user")
716 self.Entropy.updateProgress(
717 "[ftp:%s] %s: %s" % (darkgreen(self.__ftphost),mytxt,blue(self.__ftpuser),),
718 importance = 1,
719 type = "info",
720 header = darkgreen(" * ")
721 )
722 self.__ftpconn.login(self.__ftpuser,self.__ftppassword)
723 if self.__verbose:
724 mytxt = _("switching to")
725 self.Entropy.updateProgress(
726 "[ftp:%s] %s: %s" % (darkgreen(self.__ftphost),mytxt,blue(self.__ftpdir),),
727 importance = 1,
728 type = "info",
729 header = darkgreen(" * ")
730 )
731 self.set_cwd(self.__currentdir)
732
734 return self.__ftphost
735
737 return self.__ftpport
738
741
743 pwd = self.__ftpconn.pwd()
744 return pwd
745
746 - def set_cwd(self, mydir, dodir = False):
747 try:
748 return self._set_cwd(mydir, dodir)
749 except self.ftplib.error_perm, e:
750 raise FtpError('FtpError: %s' % (e,))
751
752 - def _set_cwd(self, mydir, dodir = False):
753 if self.__verbose:
754 mytxt = _("switching to")
755 self.Entropy.updateProgress(
756 "[ftp:%s] %s: %s" % (darkgreen(self.__ftphost),mytxt,blue(mydir),),
757 importance = 1,
758 type = "info",
759 header = darkgreen(" * ")
760 )
761 try:
762 self.__ftpconn.cwd(mydir)
763 except self.ftplib.error_perm, e:
764 if e[0][:3] == '550' and dodir:
765 self.recursive_mkdir(mydir)
766 self.__ftpconn.cwd(mydir)
767 else:
768 raise
769 self.__currentdir = self.get_cwd()
770
773
775 return self.__ftpconn.voidcmd("SITE CHMOD "+str(chmodvalue)+" "+str(file))
776
778 rc = self.__ftpconn.sendcmd("mdtm "+path)
779 return rc.split()[-1]
780
782 return self.__ftpconn.sendcmd(cmd)
783
785 return [x.split("/")[-1] for x in self.__ftpconn.nlst()]
786
788 xx = []
789 def cb(x):
790 if x == filename: xx.append(x)
791 self.__ftpconn.retrlines('NLST',cb)
792 if xx: return True
793 return False
794
796 try:
797 rc = self.__ftpconn.delete(file)
798 except self.ftplib.error_perm, e:
799 if e[0][:3] == '550':
800 return True
801 return False
802 if rc.startswith("250"):
803 return True
804 return False
805
807 mydirs = [x for x in mypath.split("/") if x]
808 mycurpath = ""
809 for mydir in mydirs:
810 mycurpath = os.path.join(mycurpath,mydir)
811 if not self.is_file_available(mycurpath):
812 try:
813 self.mkdir(mycurpath)
814 except self.ftplib.error_perm, e:
815 if e[0].lower().find("permission denied") != -1:
816 raise
817 elif e[0][:3] != '550':
818 raise
819
820 - def mkdir(self,directory):
821 return self.__ftpconn.mkd(directory)
822
824
825
826 def advanced_stor(cmd, fp, callback=None):
827 ''' Store a file in binary mode. Our version supports a callback function'''
828 self.__ftpconn.voidcmd('TYPE I')
829 conn = self.__ftpconn.transfercmd(cmd)
830 while 1:
831 buf = fp.readline()
832 if not buf: break
833 conn.sendall(buf)
834 if callback: callback(buf)
835 conn.close()
836
837
838
839 try:
840 rc = self.__ftpconn.voidresp()
841 except:
842 self.reconnect_host()
843 return "226"
844 return rc
845
846 def up_file_up_progress(buf):
847 self.updateProgress(len(buf))
848
849 tries = 0
850 while tries < 10:
851
852 tries += 1
853 filename = os.path.basename(file)
854 self.__init_vars()
855 self.__start_speed_counter()
856 try:
857
858 with open(file,"r") as f:
859
860 self.__filesize = round(float(self.entropyTools.get_file_size(file))/1024,1)
861 self.__filekbcount = 0
862
863 if self.is_file_available(filename+".tmp"):
864 self.delete_file(filename+".tmp")
865
866 if ascii:
867 rc = self.__ftpconn.storlines("STOR "+filename+".tmp",f)
868 else:
869 rc = advanced_stor("STOR "+filename+".tmp", f, callback = up_file_up_progress)
870
871
872 self.rename_file(filename+".tmp",filename)
873
874 if rc.find("226") != -1:
875 return True
876 return False
877
878 except Exception, e:
879
880 self.entropyTools.print_traceback()
881 mytxt = red("%s: %s, %s... #%s") % (
882 _("Upload issue"),
883 e,
884 _("retrying"),
885 tries+1,
886 )
887 self.Entropy.updateProgress(
888 mytxt,
889 importance = 1,
890 type = "warning",
891 header = " "
892 )
893 self.reconnect_host()
894 if self.is_file_available(filename):
895 self.delete_file(filename)
896 if self.is_file_available(filename+".tmp"):
897 self.delete_file(filename+".tmp")
898
899 finally:
900 self.__stop_speed_counter()
901
903
904 def df_up(buf):
905
906 f.write(buf)
907
908 self.__filekbcount += float(len(buf))/1024
909
910 cnt = round(self.__filekbcount,1)
911 mytxt = _("Download status")
912 currentText = brown(" <-> %s: " % (mytxt,)) + darkgreen(str(cnt)) + "/" + \
913 red(str(self.__filesize)) + " kB"
914 self.Entropy.updateProgress(
915 currentText,
916 importance = 0,
917 type = "info",
918 back = True,
919 count = (cnt, self.__filesize),
920 percent = True
921 )
922
923 tries = 10
924 while tries:
925 tries -= 1
926
927 self.__init_vars()
928 self.__start_speed_counter()
929 try:
930
931
932 if self.is_file_available(filename):
933 self.__filekbcount = 0
934
935 self.__filesize = self.get_file_size_compat(filename)
936 if (self.__filesize):
937 self.__filesize = round(float(int(self.__filesize))/1024,1)
938 if (self.__filesize == 0):
939 self.__filesize = 1
940 else:
941 self.__filesize = 0
942 if not ascii:
943 f = open(downloaddir+"/"+filename,"wb")
944 rc = self.__ftpconn.retrbinary('RETR '+filename, df_up, 1024)
945 else:
946 f = open(downloaddir+"/"+filename,"w")
947 rc = self.__ftpconn.retrlines('RETR '+filename, f.write)
948 f.flush()
949 f.close()
950 if rc.find("226") != -1:
951 return True
952 return False
953
954 except Exception, e:
955
956 self.entropyTools.print_traceback()
957 mytxt = red("%s: %s, %s... #%s") % (
958 _("Download issue"),
959 e,
960 _("retrying"),
961 tries+1,
962 )
963 self.Entropy.updateProgress(
964 mytxt,
965 importance = 1,
966 type = "warning",
967 header = " "
968 )
969 self.reconnect_host()
970
971 finally:
972 self.__stop_speed_counter()
973
974
976 rc = self.__ftpconn.rename(fromfile,tofile)
977 return rc
978
980
981 try:
982 rc_data = self.__ftpconn.sendcmd("SITE MD5 %s" % (filename,))
983 except self.ftplib.error_perm:
984 return None
985 try:
986 return rc_data.split("\n")[0].split("\t")[0].split("-")[1]
987 except (IndexError, TypeError,):
988 return None
989
991 return self.__ftpconn.size(filename)
992
994 try:
995 data = [x.split() for x in self.__ftpconn.sendcmd("stat %s" % (filename,)).split("\n")]
996 except self.ftplib.error_temp:
997 return ""
998 for item in data:
999 if item[-1] == filename:
1000 return item[4]
1001 return ""
1002
1004 mybuffer = []
1005 def bufferizer(buf):
1006 mybuffer.append(buf)
1007 self.__ftpconn.dir(bufferizer)
1008 return mybuffer
1009
1011 try:
1012 self.__ftpconn.quit()
1013 except (EOFError,AttributeError,self.socket.timeout,self.ftplib.error_reply,):
1014
1015
1016
1017 pass
1018
1020 self.__speed_updater = TimeScheduled(
1021 self.__transferpollingtime,
1022 self.__update_speed,
1023 )
1024 self.__speed_updater.start()
1025
1027 if self.__speed_updater != None:
1028 self.__speed_updater.kill()
1029
1031 self.__elapsed += self.__transferpollingtime
1032
1033 self.__datatransfer = (self.__transfersize-self.__startingposition) / self.__elapsed
1034 if self.__datatransfer < 0:
1035 self.__datatransfer = 0
1036 try:
1037 self.__time_remaining_secs = int(round((int(round(self.__filesize*1024,0))-int(round(self.__transfersize,0)))/self.__datatransfer,0))
1038 self.__time_remaining = self.entropyTools.convert_seconds_to_fancy_output(self.__time_remaining_secs)
1039 except:
1040 self.__time_remaining = "(%s)" % (_("infinite"),)
1041
1043
1044 self.__filekbcount += float(buf_len)/1024
1045 self.__transfersize += buf_len
1046
1047 myUploadPercentage = 100.0
1048 if self.__filesize >= 1:
1049 myUploadPercentage = round((round(self.__filekbcount,1)/self.__filesize)*100,1)
1050 currentprogress = myUploadPercentage
1051 myUploadSize = round(self.__filekbcount,1)
1052 if (currentprogress > self.__oldprogress+1.0) and \
1053 (myUploadPercentage < 100.1) and \
1054 (myUploadSize <= self.__filesize):
1055
1056 myUploadPercentage = str(myUploadPercentage)+"%"
1057
1058 mytxt = _("Transfer status")
1059 currentText = brown(" <-> %s: " % (mytxt,)) + \
1060 darkgreen(str(myUploadSize)) + "/" + red(str(self.__filesize)) + " kB " + \
1061 brown("[") + str(myUploadPercentage) + brown("]") + " " + self.__time_remaining + \
1062 " " + self.entropyTools.bytes_into_human(self.__datatransfer) + "/"+_("sec")
1063
1064
1065 self.Entropy.updateProgress(currentText, back = True)
1066 self.__oldprogress = currentprogress
1067
1068
1070
1071 import entropy.tools as entropyTools
1072 - def __init__(self, ftp_interface, entropy_interface, uris, files_to_upload,
1073 download = False, remove = False, ftp_basedir = None, local_basedir = None,
1074 critical_files = [], use_handlers = False, handlers_data = {}, repo = None):
1075
1076 self.FtpInterface = ftp_interface
1077 self.Entropy = entropy_interface
1078 if not isinstance(uris,list):
1079 raise InvalidDataType("InvalidDataType: %s" % (_("uris must be a list instance"),))
1080 if not isinstance(files_to_upload,(list,dict)):
1081 raise InvalidDataType("InvalidDataType: %s" % (
1082 _("files_to_upload must be a list or dict instance"),
1083 )
1084 )
1085 self.uris = uris
1086 if isinstance(files_to_upload,list):
1087 self.myfiles = files_to_upload[:]
1088 else:
1089 self.myfiles = sorted([x for x in files_to_upload])
1090 self.download = download
1091 self.remove = remove
1092 self.repo = repo
1093 if self.repo == None:
1094 self.repo = self.Entropy.default_repository
1095 self.use_handlers = use_handlers
1096 if self.remove:
1097 self.download = False
1098 self.use_handlers = False
1099 if not ftp_basedir:
1100
1101 branch = self.Entropy.SystemSettings['repositories']['branch']
1102 my_path = os.path.join(self.Entropy.get_remote_database_relative_path(repo), branch)
1103 self.ftp_basedir = unicode(my_path)
1104 else:
1105 self.ftp_basedir = unicode(ftp_basedir)
1106 if not local_basedir:
1107
1108 self.local_basedir = os.path.dirname(self.Entropy.get_local_database_file(self.repo))
1109 else:
1110 self.local_basedir = unicode(local_basedir)
1111 self.critical_files = critical_files
1112 self.handlers_data = handlers_data.copy()
1113
1114 - def handler_verify_upload(self, local_filepath, uri, counter, maxcount,
1115 tries, remote_md5 = None):
1116
1117 crippled_uri = self.entropyTools.extract_ftp_host_from_uri(uri)
1118
1119 self.Entropy.updateProgress(
1120 "[%s|#%s|(%s/%s)] %s: %s" % (
1121 blue(crippled_uri),
1122 darkgreen(str(tries)),
1123 blue(str(counter)),
1124 bold(str(maxcount)),
1125 darkgreen(_("verifying upload (if supported)")),
1126 blue(os.path.basename(local_filepath)),
1127 ),
1128 importance = 0,
1129 type = "info",
1130 header = red(" @@ "),
1131 back = True
1132 )
1133
1134 valid_remote_md5 = True
1135
1136 if isinstance(remote_md5, basestring):
1137 valid_md5 = self.entropyTools.is_valid_md5(remote_md5)
1138 ckres = False
1139 if valid_md5:
1140 ckres = self.entropyTools.compare_md5(local_filepath,
1141 remote_md5)
1142 if ckres:
1143 self.Entropy.updateProgress(
1144 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1145 blue(crippled_uri),
1146 darkgreen(str(tries)),
1147 blue(str(counter)),
1148 bold(str(maxcount)),
1149 blue(_("digest verification")),
1150 os.path.basename(local_filepath),
1151 darkgreen(_("so far, so good!")),
1152 ),
1153 importance = 0,
1154 type = "info",
1155 header = red(" @@ ")
1156 )
1157 return True
1158
1159 elif not valid_md5:
1160
1161 self.Entropy.updateProgress(
1162 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1163 blue(crippled_uri),
1164 darkgreen(str(tries)),
1165 blue(str(counter)),
1166 bold(str(maxcount)),
1167 blue(_("digest verification")),
1168 os.path.basename(local_filepath),
1169 bold(_("malformed md5 provided to function")),
1170 ),
1171 importance = 0,
1172 type = "warning",
1173 header = brown(" @@ ")
1174 )
1175 else:
1176 self.Entropy.updateProgress(
1177 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1178 blue(crippled_uri),
1179 darkgreen(str(tries)),
1180 blue(str(counter)),
1181 bold(str(maxcount)),
1182 blue(_("digest verification")),
1183 os.path.basename(local_filepath),
1184 bold(_("remote md5 is invalid")),
1185 ),
1186 importance = 0,
1187 type = "warning",
1188 header = brown(" @@ ")
1189 )
1190 valid_remote_md5 = False
1191
1192 if not self.use_handlers:
1193
1194 return valid_remote_md5
1195
1196 checksum = self.Entropy.get_remote_package_checksum(
1197 self.repo,
1198 os.path.basename(local_filepath),
1199 self.handlers_data['branch']
1200 )
1201 if checksum == None:
1202 self.Entropy.updateProgress(
1203 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1204 blue(crippled_uri),
1205 darkgreen(str(tries)),
1206 blue(str(counter)),
1207 bold(str(maxcount)),
1208 blue(_("digest verification")),
1209 os.path.basename(local_filepath),
1210 darkred(_("not supported")),
1211 ),
1212 importance = 0,
1213 type = "info",
1214 header = red(" @@ ")
1215 )
1216 return valid_remote_md5
1217 elif isinstance(checksum, bool) and not checksum:
1218 self.Entropy.updateProgress(
1219 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1220 blue(crippled_uri),
1221 darkgreen(str(tries)),
1222 blue(str(counter)),
1223 bold(str(maxcount)),
1224 blue(_("digest verification")),
1225 os.path.basename(local_filepath),
1226 bold(_("file not found")),
1227 ),
1228 importance = 0,
1229 type = "warning",
1230 header = brown(" @@ ")
1231 )
1232 return False
1233 elif self.entropyTools.is_valid_md5(checksum):
1234
1235 ckres = self.entropyTools.compare_md5(local_filepath,checksum)
1236 if ckres:
1237 self.Entropy.updateProgress(
1238 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1239 blue(crippled_uri),
1240 darkgreen(str(tries)),
1241 blue(str(counter)),
1242 bold(str(maxcount)),
1243 blue(_("digest verification")),
1244 os.path.basename(local_filepath),
1245 darkgreen(_("so far, so good!")),
1246 ),
1247 importance = 0,
1248 type = "info",
1249 header = red(" @@ ")
1250 )
1251 return True
1252 else:
1253 self.Entropy.updateProgress(
1254 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1255 blue(crippled_uri),
1256 darkgreen(str(tries)),
1257 blue(str(counter)),
1258 bold(str(maxcount)),
1259 blue(_("digest verification")),
1260 os.path.basename(local_filepath),
1261 darkred(_("invalid checksum")),
1262 ),
1263 importance = 0,
1264 type = "warning",
1265 header = brown(" @@ ")
1266 )
1267 return False
1268 else:
1269 self.Entropy.updateProgress(
1270 "[%s|#%s|(%s/%s)] %s: %s: %s" % (
1271 blue(crippled_uri),
1272 darkgreen(str(tries)),
1273 blue(str(counter)),
1274 bold(str(maxcount)),
1275 blue(_("digest verification")),
1276 os.path.basename(local_filepath),
1277 darkred(_("unknown data returned")),
1278 ),
1279 importance = 0,
1280 type = "warning",
1281 header = brown(" @@ ")
1282 )
1283 return valid_remote_md5
1284
1286
1287 broken_uris = set()
1288 fine_uris = set()
1289 errors = False
1290 action = 'upload'
1291 if self.download:
1292 action = 'download'
1293 elif self.remove:
1294 action = 'remove'
1295
1296 for uri in self.uris:
1297
1298 crippled_uri = self.entropyTools.extract_ftp_host_from_uri(uri)
1299 self.Entropy.updateProgress(
1300 "[%s|%s] %s..." % (
1301 blue(crippled_uri),
1302 brown(action),
1303 blue(_("connecting to mirror")),
1304 ),
1305 importance = 0,
1306 type = "info",
1307 header = blue(" @@ ")
1308 )
1309 try:
1310 ftp = self.FtpInterface(uri, self.Entropy)
1311 except ConnectionError:
1312 self.entropyTools.print_traceback()
1313 return True,fine_uris,broken_uris
1314 branch = self.Entropy.SystemSettings['repositories']['branch']
1315 my_path = os.path.join(self.Entropy.get_remote_database_relative_path(self.repo), branch)
1316 self.Entropy.updateProgress(
1317 "[%s|%s] %s %s..." % (
1318 blue(crippled_uri),
1319 brown(action),
1320 blue(_("changing directory to")),
1321 darkgreen(my_path),
1322 ),
1323 importance = 0,
1324 type = "info",
1325 header = blue(" @@ ")
1326 )
1327
1328 ftp.set_cwd(self.ftp_basedir, dodir = True)
1329 maxcount = len(self.myfiles)
1330 counter = 0
1331
1332 for mypath in self.myfiles:
1333
1334 ftp.set_basedir()
1335 ftp.set_cwd(self.ftp_basedir, dodir = True)
1336
1337 mycwd = None
1338 if isinstance(mypath,tuple):
1339 if len(mypath) < 2: continue
1340 mycwd = mypath[0]
1341 mypath = mypath[1]
1342 ftp.set_cwd(mycwd, dodir = True)
1343
1344 syncer = ftp.upload_file
1345 myargs = [mypath]
1346 if self.download:
1347 syncer = ftp.download_file
1348 myargs = [os.path.basename(mypath),self.local_basedir]
1349 elif self.remove:
1350 syncer = ftp.delete_file
1351
1352 counter += 1
1353 tries = 0
1354 done = False
1355 lastrc = None
1356 while tries < 5:
1357 tries += 1
1358 self.Entropy.updateProgress(
1359 "[%s|#%s|(%s/%s)] %s: %s" % (
1360 blue(crippled_uri),
1361 darkgreen(str(tries)),
1362 blue(str(counter)),
1363 bold(str(maxcount)),
1364 blue(action+"ing"),
1365 red(os.path.basename(mypath)),
1366 ),
1367 importance = 0,
1368 type = "info",
1369 header = red(" @@ ")
1370 )
1371 rc = syncer(*myargs)
1372 if rc and not self.download:
1373
1374
1375 remote_md5 = ftp.get_file_md5(os.path.basename(mypath))
1376 rc = self.handler_verify_upload(mypath, uri,
1377 counter, maxcount, tries, remote_md5 = remote_md5)
1378 if rc:
1379 self.Entropy.updateProgress(
1380 "[%s|#%s|(%s/%s)] %s %s: %s" % (
1381 blue(crippled_uri),
1382 darkgreen(str(tries)),
1383 blue(str(counter)),
1384 bold(str(maxcount)),
1385 blue(action),
1386 _("successful"),
1387 red(os.path.basename(mypath)),
1388 ),
1389 importance = 0,
1390 type = "info",
1391 header = darkgreen(" @@ ")
1392 )
1393 done = True
1394 break
1395 else:
1396 self.Entropy.updateProgress(
1397 "[%s|#%s|(%s/%s)] %s %s: %s" % (
1398 blue(crippled_uri),
1399 darkgreen(str(tries)),
1400 blue(str(counter)),
1401 bold(str(maxcount)),
1402 blue(action),
1403 brown(_("failed, retrying")),
1404 red(os.path.basename(mypath)),
1405 ),
1406 importance = 0,
1407 type = "warning",
1408 header = brown(" @@ ")
1409 )
1410 lastrc = rc
1411 continue
1412
1413 if not done:
1414
1415 self.Entropy.updateProgress(
1416 "[%s|(%s/%s)] %s %s: %s - %s: %s" % (
1417 blue(crippled_uri),
1418 blue(str(counter)),
1419 bold(str(maxcount)),
1420 blue(action),
1421 darkred("failed, giving up"),
1422 red(os.path.basename(mypath)),
1423 _("error"),
1424 lastrc,
1425 ),
1426 importance = 1,
1427 type = "error",
1428 header = darkred(" !!! ")
1429 )
1430
1431 if mypath not in self.critical_files:
1432 self.Entropy.updateProgress(
1433 "[%s|(%s/%s)] %s: %s, %s..." % (
1434 blue(crippled_uri),
1435 blue(str(counter)),
1436 bold(str(maxcount)),
1437 blue(_("not critical")),
1438 os.path.basename(mypath),
1439 blue(_("continuing")),
1440 ),
1441 importance = 1,
1442 type = "warning",
1443 header = brown(" @@ ")
1444 )
1445 continue
1446
1447 ftp.close()
1448 errors = True
1449 broken_uris.add((uri,lastrc))
1450
1451 break
1452
1453
1454 ftp.close()
1455 fine_uris.add(uri)
1456
1457 return errors,fine_uris,broken_uris
1458