diff --git a/rigo/RigoDaemon/app.py b/rigo/RigoDaemon/app.py index 3479b69e0..26062a73c 100755 --- a/rigo/RigoDaemon/app.py +++ b/rigo/RigoDaemon/app.py @@ -436,7 +436,7 @@ class RigoDaemonService(dbus.service.Object): BUS_NAME = DbusConfig.BUS_NAME OBJECT_PATH = DbusConfig.OBJECT_PATH - API_VERSION = 6 + API_VERSION = 7 """ RigoDaemon is the dbus service Object in charge of executing @@ -2549,6 +2549,37 @@ class RigoDaemonService(dbus.service.Object): outcome.append(obj) return outcome + def _optimize_mirrors(self, repository_ids): + """ + Execute a background Repository Mirrors optimization. + This low-priority method shall reorder Repository mirrors + basing on throughput performance. + It can be run in parallel with any other Entropy Client + activity since the operation is atomic. + """ + optimized = False + for repository_id in repository_ids: + + write_output("_optimize_mirrors: %s" % ( + repository_id,), debug=True) + + try: + repository_metadata = self._entropy.reorder_mirrors( + repository_id) + except KeyError as err: + write_output("_optimize_mirrors: " + "repository update error: %s" % ( + repr(err),), debug=True) + continue + + optimized = True + mirrors = repository_metadata.get('plain_packages') + write_output("_optimize_mirrors: repository " + "'%s' updated, mirrors: %s" % ( + repository_id, mirrors), debug=True) + + GLib.idle_add(self.mirrors_optimized, repository_ids, optimized) + ### DBUS METHODS @dbus.service.method(BUS_NAME, in_signature='asb', @@ -3372,6 +3403,40 @@ class RigoDaemonService(dbus.service.Object): task.start() return True + @dbus.service.method(BUS_NAME, in_signature='as', + out_signature='b', sender_keyword='sender') + def optimize_mirrors(self, repository_ids, sender=None): + """ + Request RigoDaeon to optimize Repository Mirrors + in background. + """ + pid = self._get_caller_pid(sender) + write_output("optimize_mirrors called: " + "client pid: %s" % ( + (pid,)), debug=True) + + def _optimize(_repository_ids): + _repository_ids = [self._dbus_to_unicode(x) for \ + x in _repository_ids] + authorized = self._authorize( + pid, PolicyActions.MANAGE_CONFIGURATION) + if authorized: + th = ParallelTask( + self._optimize_mirrors, + _repository_ids) + th.name = "OptimizeMirrorsThread" + th.daemon = True + th.start() + else: + GLib.idle_add(self.mirrors_optimized, + _repository_ids, False) + + task = ParallelTask(_optimize, repository_ids) + task.name = "OptimizeMirrorsRequestThread" + task.daemon = True + task.start() + return True + ### DBUS SIGNALS @dbus.service.signal(dbus_interface=BUS_NAME, @@ -3611,6 +3676,15 @@ class RigoDaemonService(dbus.service.Object): write_output("old_repositories(): %s" % (locals(),), debug=True) + @dbus.service.signal(dbus_interface=BUS_NAME, + signature='asb') + def mirrors_optimized(self, repository_ids, optimized): + """ + Mirrors have been eventually optimized for given Repositories. + """ + write_output("mirrors_optimized() issued, args:" + " %s" % (locals(),), debug=True) + @dbus.service.signal(dbus_interface=BUS_NAME, signature='') def ping(self): diff --git a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml index effaaf7a2..4f1f11c93 100644 --- a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml +++ b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml @@ -112,5 +112,10 @@ + + + + + diff --git a/rigo/data/icons/browser-download.svg b/rigo/data/icons/browser-download.svg new file mode 100644 index 000000000..20485b467 --- /dev/null +++ b/rigo/data/icons/browser-download.svg @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rigo/rigo/controllers/daemon.py b/rigo/rigo/controllers/daemon.py index c5f696991..92eeca81d 100644 --- a/rigo/rigo/controllers/daemon.py +++ b/rigo/rigo/controllers/daemon.py @@ -69,6 +69,7 @@ class RigoServiceController(GObject.Object): SYSTEM_UPGRADE_CONTEXT_ID = "SystemUpgradeContextId" PKG_INSTALL_CONTEXT_ID = "PackageInstallContextId" REPOSITORY_SETTINGS_CONTEXT_ID = "RepositoriesSettingsContextId" + OPTIMIZE_MIRRORS_CONTEXT_ID = "MirrorsOptimizedContextId" class ServiceNotificationBox(NotificationBox): @@ -186,7 +187,8 @@ class RigoServiceController(GObject.Object): _OLD_REPOSITORIES_SIGNAL = "old_repositories" _NOTICEBOARDS_AVAILABLE_SIGNAL = "noticeboards_available" _REPOS_SETTINGS_CHANGED_SIGNAL = "repositories_settings_changed" - _SUPPORTED_APIS = [6] + _MIRRORS_OPTIMIZED_SIGNAL = "mirrors_optimized" + _SUPPORTED_APIS = [6, 7] def __init__(self, rigo_app, activity_rwsem, entropy_client, entropy_ws): @@ -541,6 +543,13 @@ class RigoServiceController(GObject.Object): self._repositories_settings_changed_signal, dbus_interface=self.DBUS_INTERFACE) + # RigoDaemon tells us that mirros have been + # optimized + self.__entropy_bus.connect_to_signal( + self._MIRRORS_OPTIMIZED_SIGNAL, + self._mirrors_optimized_signal, + dbus_interface=self.DBUS_INTERFACE) + return self.__entropy_bus ### GOBJECT EVENTS @@ -1085,6 +1094,30 @@ class RigoServiceController(GObject.Object): __name__, "_repositories_updated_signal: repositories-updated") + def _mirrors_optimized_signal(self, repository_ids, optimized): + + repository_ids = [self._dbus_to_unicode(x) for x in \ + repository_ids] + + const_debug_write( + __name__, + "_mirrors_optimized_signal: received for " + "%s, optimized: %s" % (repository_ids, optimized,)) + + if optimized: + msg = prepare_markup( + _("Congratulations, mirrors have been optimized!")) + msg_type = Gtk.MessageType.INFO + else: + msg = prepare_markup( + _("Ouch, mirrors not optimized, sorry!")) + msg_type = Gtk.MessageType.WARNING + + box = self.ServiceNotificationBox( + msg, msg_type, + context_id=self.OPTIMIZE_MIRRORS_CONTEXT_ID) + self._nc.append(box, timeout=30) + def _output_signal(self, text, header, footer, back, importance, level, count_c, count_t, percent, raw): """ @@ -1466,6 +1499,37 @@ class RigoServiceController(GObject.Object): task.daemon = True task.start() + def optimize_mirrors(self, repository_ids): + """ + Request mirror list optimization (basically + sorting per throughput) for the given + Repositories. + """ + if self.api() < 7: + # ignore request, RigoDaemon is too old + return + + def _optimize(): + accepted = dbus.Interface( + self._entropy_bus, + dbus_interface=self.DBUS_INTERFACE + ).optimize_mirrors(repository_ids) + if accepted: + msg = prepare_markup( + _("Mirrors will be optimized in background...")) + msg_type = Gtk.MessageType.INFO + else: + msg = prepare_markup( + _("Mirrors optimization not available at this time")) + msg_type = Gtk.MessageType.WARNING + box = self.ServiceNotificationBox( + msg, msg_type, + context_id=self.OPTIMIZE_MIRRORS_CONTEXT_ID) + self._nc.append(box, timeout=30) + return accepted + + return self._execute_mainloop(_optimize) + def configuration_updates(self): """ Request pending Configuration File Updates. diff --git a/rigo/rigo/ui/gtk3/controllers/applications.py b/rigo/rigo/ui/gtk3/controllers/applications.py index 6b83663bf..aa380a134 100644 --- a/rigo/rigo/ui/gtk3/controllers/applications.py +++ b/rigo/rigo/ui/gtk3/controllers/applications.py @@ -446,6 +446,14 @@ class ApplicationsViewController(GObject.Object): if sim_str: self.remove(sim_str) + def _do_optimize_mirrors(): + self._entropy.rwsem().reader_acquire() + try: + repository_ids = self._entropy.repositories() + finally: + self._entropy.rwsem().reader_release() + self._service.optimize_mirrors(repository_ids) + special_keys_map = { "in:confupdate": self._service.configuration_updates, self.SHOW_QUEUE_KEY: self._show_action_queue_items, @@ -463,6 +471,7 @@ class ApplicationsViewController(GObject.Object): "do:remove": _do_remove, "do:upgrade": self.upgrade, "do:hello": self._service.hello, + "do:optimize": _do_optimize_mirrors, } special_f = special_keys_map.get(split_text[0]) @@ -717,11 +726,25 @@ class ApplicationsViewController(GObject.Object): self._search(ApplicationsViewController.SHOW_INSTALLED_KEY, _force=True) pref = Preference( - -1, _("Show Installed Applications"), + -2, _("Show Installed Applications"), _("Browse through the currently Installed Applications."), "drive-harddisk", _show_installed) self._prefc.append(pref) + def _optimize_mirrors(): + self._entropy.rwsem().reader_acquire() + try: + repository_ids = self._entropy.repositories() + finally: + self._entropy.rwsem().reader_release() + self._service.optimize_mirrors(repository_ids) + pref = Preference( + 50, _("Optimize Download Speed"), + _("Benchmark the download mirrors to speed up Application" + " installation."), + "browser-download", _optimize_mirrors) + self._prefc.append(pref) + def _show_queue_view(widget): self._search(ApplicationsViewController.SHOW_QUEUE_KEY, _force=True)