home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / softwareproperties / gtk / DialogMirror.py < prev    next >
Encoding:
Python Source  |  2009-03-27  |  15.3 KB  |  375 lines

  1. # dialog_add.py.in - dialog to add a new repository
  2. #  
  3. #  Copyright (c) 2006 FSF Europe
  4. #              
  5. #  Authors: 
  6. #       Sebastian Heinlein <glatzor@ubuntu.com>
  7. #
  8. #  This program is free software; you can redistribute it and/or 
  9. #  modify it under the terms of the GNU General Public License as 
  10. #  published by the Free Software Foundation; either version 2 of the
  11. #  License, or (at your option) any later version.
  12. #  This program is distributed in the hope that it will be useful,
  13. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. #  GNU General Public License for more details.
  16. #  You should have received a copy of the GNU General Public License
  17. #  along with this program; if not, write to the Free Software
  18. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  19. #  USA
  20.  
  21. import os
  22. import gobject
  23. import gtk
  24. import gtk.glade
  25. from gettext import gettext as _
  26. import threading
  27. import string
  28. import re
  29. from random import randint
  30.  
  31. import dialogs
  32. from softwareproperties.MirrorTest import MirrorTest
  33.  
  34. testing = threading.Event()
  35.  
  36. (COLUMN_PROTO, COLUMN_DIR) = range(2)
  37. (COLUMN_URI, COLUMN_SEPARATOR, COLUMN_CUSTOM, COLUMN_MIRROR) = range(4)
  38.  
  39. from softwareproperties.CountryInformation import CountryInformation
  40.  
  41. def threaded(f):
  42.     ''' Thanks to Ross Burton for this piece of code '''
  43.     def wrapper(*args, **kwargs):
  44.         t = threading.Thread(target=f, args=args, kwargs=kwargs)
  45.         t.setDaemon(True)
  46.         t.start()
  47.     wrapper.__name__ = f.__name__
  48.     return wrapper
  49.  
  50. def sort_mirrors(model, iter1, iter2, data=None):
  51.       ''' sort function for the mirror list:
  52.            - at first show all custom urls
  53.            - secondly the separator
  54.            - third the official mirrors. if available
  55.              sort the countries '''
  56.       #FIXME: cmp seems to prefer ASCI chars
  57.       (url1, sep1, custom1) = model.get(iter1, 0,1,2)
  58.       (url2, sep2, custom2) = model.get(iter2, 0,1,2)
  59.       if custom1 and custom2:
  60.           return cmp(url1,url2)
  61.       elif custom1:
  62.           return -1
  63.       elif custom2:
  64.           return 1
  65.       if sep1:
  66.           return -1
  67.       elif sep2:
  68.           return 1
  69.       return cmp(url1,url2)
  70.  
  71. class DialogMirror:
  72.   def __init__(self, parent, datadir, distro, custom_mirrors):
  73.     """
  74.     Initialize the dialog that allows to choose a custom or official mirror
  75.     """
  76.     def is_separator(model, iter, data=None):
  77.         return model.get_value(iter, COLUMN_SEPARATOR)
  78.  
  79.     self.custom_mirrors = custom_mirrors
  80.  
  81.     self.country_info = CountryInformation()
  82.  
  83.     self.gladexml = gtk.glade.XML("%s/glade/dialogs.glade" %\
  84.                                   datadir)
  85.     self.gladexml.signal_autoconnect(self)
  86.     self.dialog = self.gladexml.get_widget("dialog_mirror")
  87.     self.dialog.set_transient_for(parent)
  88.     self.dialog_test = self.gladexml.get_widget("dialog_mirror_test")
  89.     self.dialog_test.set_transient_for(self.dialog)
  90.     self.distro = distro
  91.     self.treeview = self.gladexml.get_widget("treeview_mirrors")
  92.     self.button_edit = self.gladexml.get_widget("button_mirror_edit")
  93.     self.button_remove = self.gladexml.get_widget("button_mirror_remove")
  94.     self.button_choose = self.gladexml.get_widget("button_mirror_choose")
  95.     self.button_cancel = self.gladexml.get_widget("button_test_cancel")
  96.     self.label_test = self.gladexml.get_widget("label_test_mirror")
  97.     self.progressbar_test = self.gladexml.get_widget("progressbar_test_mirror")
  98.     self.combobox = self.gladexml.get_widget("combobox_mirror_proto")
  99.     self.progress = self.gladexml.get_widget("progressbar_test_mirror")
  100.     self.label_action = self.gladexml.get_widget("label_test_mirror")
  101.  
  102.     # store each proto and its dir
  103.     model_proto = gtk.ListStore(gobject.TYPE_STRING,
  104.                                 gobject.TYPE_STRING)
  105.     self.combobox.set_model(model_proto)
  106.  
  107.     self.model = gtk.TreeStore(gobject.TYPE_STRING,  # COLUMN_URI
  108.                                gobject.TYPE_BOOLEAN, # COLUMN_SEPARATOR
  109.                                gobject.TYPE_BOOLEAN, # COLUMN_CUSTOM
  110.                                gobject.TYPE_PYOBJECT)# COLUMN_MIRROR
  111.     self.treeview.set_row_separator_func(is_separator)
  112.     self.model_sort = gtk.TreeModelSort(self.model)
  113.     self.model_sort.set_default_sort_func(sort_mirrors)
  114.  
  115.     self.distro = distro
  116.  
  117.     self.treeview.set_model(self.model_sort)
  118.     # the cell renderer for the mirror uri
  119.     self.renderer_mirror = gtk.CellRendererText()
  120.     self.renderer_mirror.connect('edited', 
  121.                                  self.on_edited_custom_mirror, 
  122.                                  self.model)
  123.     # the visible column that holds the mirror uris
  124.     self.column_mirror = gtk.TreeViewColumn("URI", 
  125.                                             self.renderer_mirror, 
  126.                                             text=COLUMN_URI)
  127.     self.treeview.append_column(self.column_mirror)
  128.  
  129.     # used to find the corresponding iter of a location
  130.     map_loc = {}
  131.     patriot = None
  132.     model = self.treeview.get_model().get_model()
  133.     # at first add all custom mirrors and a separator
  134.     if len(self.custom_mirrors) > 0:
  135.         for mirror in self.custom_mirrors:
  136.             model.append(None, [mirror, False, True, None])
  137.             self.column_mirror.add_attribute(self.renderer_mirror, 
  138.                                              "editable", 
  139.                                              COLUMN_CUSTOM)
  140.         model.append(None, [None, True, False, None])
  141.     # secondly add all official mirrors
  142.     for hostname in self.distro.source_template.mirror_set.keys():
  143.         mirror = self.distro.source_template.mirror_set[hostname]
  144.         if map_loc.has_key(mirror.location):
  145.             model.append(map_loc[mirror.location],
  146.                          [hostname, False, False, mirror])
  147.         elif mirror.location != None:
  148.             parent = model.append(None, 
  149.                                   [self.country_info.get_country_name(mirror.location), False, False, None])
  150.             if mirror.location == self.country_info.code and patriot == None:
  151.                 patriot = parent
  152.             model.append(parent, [hostname, False, False, mirror]),
  153.             map_loc[mirror.location] = parent
  154.         else:
  155.             model.append(None, [hostname, False, False, mirror])
  156.     # Scroll to the local mirror set
  157.     if patriot != None:
  158.         path_sort = self.model_sort.get_path(self.model_sort.convert_child_iter_to_iter(None, patriot))
  159.         self.treeview.expand_row(path_sort, False)
  160.         self.treeview.set_cursor(path_sort)
  161.         self.treeview.scroll_to_cell(path_sort, use_align=True, row_align=0.5)
  162.  
  163.   def on_edited_custom_mirror(self, cell, path, new_text, model):
  164.     ''' Check if the new mirror uri is faild, if yes change it, if not
  165.         remove the mirror from the list '''
  166.     iter = model.get_iter(path)
  167.     iter_next = model.iter_next(iter)
  168.     if new_text != "":
  169.         model.set_value(iter, COLUMN_URI, new_text)
  170.         # Add a separator if the next mirror is a not a separator or 
  171.         # a custom one
  172.         if iter_next != None and not \
  173.            (model.get_value(iter_next, COLUMN_SEPARATOR) or \
  174.             model.get_value(iter_next, COLUMN_CUSTOM)):
  175.             model.insert(1, [None, True, False])
  176.         self.button_choose.set_sensitive(self.is_valid_mirror(new_text))
  177.     else:
  178.         model.remove(iter)
  179.         # Remove the separator if this was the last custom mirror
  180.         if model.get_value(model.get_iter_first(), COLUMN_SEPARATOR):
  181.             model.remove(model.get_iter_first())
  182.         self.treeview.set_cursor((0,))
  183.     return
  184.  
  185.   def is_valid_mirror(self, uri):
  186.     ''' Check if a given uri is a vaild one '''
  187.     if uri == None:
  188.         return False
  189.     elif re.match("^((ftp)|(http)|(file)|(rsync)|(https))://([a-z]|[A-Z]|[0-9]|:|/|\.|~)+$", uri) == None:
  190.         return False
  191.     else:
  192.         return True
  193.  
  194.   def on_treeview_mirrors_cursor_changed(self, treeview, data=None):
  195.     ''' Check if the currently selected row in the mirror list
  196.         contains a mirror and or is editable '''
  197.     (row, column) = treeview.get_cursor()
  198.     if row == None:
  199.         self.button_remove.set_sensitive(False)
  200.         self.button_edit.set_sensitive(False)
  201.         self.button_choose.set_sensitive(False)
  202.         return
  203.     model = treeview.get_model()
  204.     iter = model.get_iter(row)
  205.     # Update the list of available protocolls
  206.     mirror = model.get_value(iter, COLUMN_MIRROR)
  207.     model_protos = self.combobox.get_model()
  208.     model_protos.clear()
  209.     if mirror != None:
  210.         self.combobox.set_sensitive(True)
  211.         seen_protos = []
  212.         for repo in mirror.repositories:
  213.             # Only add a repository for a protocoll once
  214.             if repo.proto in seen_protos:
  215.                 continue
  216.             seen_protos.append(repo.proto)
  217.             model_protos.append(repo.get_info())
  218.         self.combobox.set_active(0)
  219.         self.button_choose.set_sensitive(True)
  220.     else:
  221.         # Allow to edit and remove custom mirrors
  222.         self.button_remove.set_sensitive(model.get_value(iter, COLUMN_CUSTOM))
  223.         self.button_edit.set_sensitive(model.get_value(iter, COLUMN_CUSTOM))
  224.         self.button_choose.set_sensitive(self.is_valid_mirror(model.get_value(iter, COLUMN_URI)))
  225.         self.combobox.set_sensitive(False)
  226.  
  227.   def on_button_mirror_remove_clicked(self, button, data=None):
  228.     ''' Remove the currently selected mirror '''
  229.     path, column = self.treeview.get_cursor()
  230.     iter = self.treeview.get_model().get_iter(path)
  231.     model = self.treeview.get_model().get_model()
  232.     model.remove(iter)
  233.     # Remove the separator if this was the last custom mirror
  234.     if model.get_value(model.get_iter_first(), COLUMN_SEPARATOR):
  235.         model.remove(model.get_iter_first())
  236.     self.treeview.set_cursor((0,))
  237.  
  238.   def on_button_mirror_add_clicked(self, button, data=None):
  239.     ''' Add a new mirror at the beginning of the list and start
  240.         editing '''
  241.     model = self.treeview.get_model().get_model()
  242.     model.append(None, [_("New mirror"), False, True, None])
  243.     self.treeview.grab_focus()
  244.     self.treeview.set_cursor((0,),
  245.                              focus_column=self.column_mirror, 
  246.                              start_editing=True)
  247.  
  248.   def on_button_mirror_edit_clicked(self, button, data=None):
  249.     ''' Grab the focus and start editing the currently selected mirror '''
  250.     path, column = self.treeview.get_cursor()
  251.     self.treeview.grab_focus()
  252.     self.treeview.set_cursor(path, focus_column=column, start_editing=True)
  253.  
  254.   def on_dialog_mirror_test_delete_event(self, dialog, event, data=None):
  255.     ''' If anybody wants to close the dialog, stop the test before '''
  256.     self.on_button_cancel_test_clicked(None)
  257.     return True
  258.  
  259.   def run(self):
  260.     ''' Run the chooser dialog and return the chosen mirror or None '''
  261.     res = self.dialog.run()
  262.     self.dialog.hide()
  263.  
  264.     (row, column) = self.treeview.get_cursor()
  265.     model = self.treeview.get_model()
  266.     iter = model.get_iter(row)
  267.     mirror = model.get_value(iter, COLUMN_MIRROR)
  268.  
  269.     # FIXME: we should also return the list of custom servers
  270.     if res == gtk.RESPONSE_OK:
  271.         if mirror == None:
  272.             # Return the URL of the selected custom mirror
  273.             return model.get_value(iter, COLUMN_URI)
  274.         else:
  275.             # Return an URL created from the hostname and the selected
  276.             # repository
  277.             model_proto = self.combobox.get_model()
  278.             iter_proto = model_proto.get_iter(self.combobox.get_active())
  279.             proto = model_proto.get_value(iter_proto, COLUMN_PROTO)
  280.             dir = model_proto.get_value(iter_proto, COLUMN_DIR)
  281.             return "%s://%s/%s" % (proto, mirror.hostname, dir)
  282.     else:
  283.         return None
  284.  
  285.   @threaded
  286.   def on_button_test_clicked(self, button, data=None):
  287.     ''' Perform a test to find the best mirror and select it 
  288.         afterwards in the treeview '''
  289.     class MirrorTestGtk(MirrorTest):
  290.         def __init__(self, mirrors, test_file, running, progressbar, label):
  291.             MirrorTest.__init__(self, mirrors, test_file, running)
  292.             self.progress = progressbar
  293.             self.label = label
  294.         def report_action(self, text):
  295.             gtk.gdk.threads_enter()
  296.             self.label.set_label(str("<i>%s</i>" % text))
  297.             gtk.gdk.threads_leave()
  298.         def report_progress(self, current, max, borders=(0,1), mod=(0,0)):
  299.             gtk.gdk.threads_enter()
  300.             self.progress.set_text(_("Completed %s of %s tests") % \
  301.                                    (current + mod[0], max + mod[1]))
  302.             frac = borders[0] + (borders[1] - borders[0]) / max * current
  303.             self.progress.set_fraction(frac)
  304.             gtk.gdk.threads_leave()
  305.         def run_full_test(self):
  306.             # Determinate the 5 top ping servers
  307.             results_ping = self.run_ping_test(max=5,
  308.                                               borders=(0, 0.5),
  309.                                               mod=(0,7))
  310.             # Add two random mirrors to the download test
  311.             size = len(self.mirrors)
  312.         if size > 2:
  313.                 results_ping.append([0, 0, self.mirrors[randint(1,size-1)]])
  314.                 results_ping.append([0, 0, self.mirrors[randint(1,size-1)]])
  315.             results = self.run_download_test(map(lambda r: r[2], results_ping),
  316.                                              borders=(0.5, 1),
  317.                                              mod=(MirrorTest.todo,
  318.                                                   MirrorTest.todo))
  319.             for (t, h) in results:
  320.                 print "mirror: %s - time: %s" % (h.hostname, t)
  321.             if len(results) == 0:
  322.                 return None
  323.             else:
  324.                 return results[0][1].hostname
  325.  
  326.     gtk.gdk.threads_enter()
  327.     self.button_cancel.set_sensitive(True)
  328.     self.dialog_test.show()
  329.     gtk.gdk.threads_leave()
  330.     self.running = threading.Event()
  331.     self.running.set()
  332.     pipe = os.popen("dpkg --print-architecture")
  333.     arch = pipe.read().strip()
  334.     test_file = "dists/%s/%s/binary-%s/Packages.gz" % \
  335.                  (self.distro.source_template.name,
  336.                   self.distro.source_template.components[0].name,
  337.                   arch)
  338.     test = MirrorTestGtk(self.distro.source_template.mirror_set.values(), 
  339.                          test_file,
  340.                          self.running,
  341.                          self.progress,
  342.                          self.label_action)
  343.     test.start()
  344.     rocker = test.run_full_test()
  345.     gtk.gdk.threads_enter()
  346.     testing.clear()
  347.     self.dialog_test.hide()
  348.     # Select the mirror in the list or show an error dialog
  349.     if rocker != None:
  350.         self.model_sort.foreach(self.select_mirror, rocker)
  351.     else:
  352.         dialogs.show_error_dialog(self.dialog, 
  353.                                   _("No suitable download server was found"),
  354.                                   _("Please check your Internet connection."))
  355.     gtk.gdk.threads_leave()
  356.  
  357.   def select_mirror(self, model, path, iter, mirror):
  358.     """Select and expand the path to a matching mirror in the list"""
  359.     if model.get_value(iter, COLUMN_URI) == mirror:
  360.         self.treeview.expand_to_path(path)
  361.         self.treeview.set_cursor(path)
  362.         self.treeview.scroll_to_cell(path, use_align=True, row_align=0.5)
  363.         self.treeview.grab_focus()
  364.         # breaks foreach
  365.         return True
  366.  
  367.   def on_button_cancel_test_clicked(self, button):
  368.     ''' Abort the mirror performance test '''
  369.     self.running.clear()
  370.     self.label_test.set_label("<i>%s</i>" % _("Canceling..."))
  371.     self.button_cancel.set_sensitive(False)
  372.     self.progressbar_test.set_fraction(1)
  373.