diff --git a/client/equo.py b/client/equo.py index 2f1805d19..083053f32 100644 --- a/client/equo.py +++ b/client/equo.py @@ -71,6 +71,7 @@ help_opts = [ (2, 'disable', 1, _('disable given repository')), (2, 'add ', 1, _('add repository (pass repository string)')), (2, 'remove ', 1, _('remove repository')), + (2, 'mirrorsort ', 0, _('reorder mirrors basing on response time')), (1, 'notice [repos]', 1, _('repository notice board reader')), (1, 'status', 2, _('show respositories status')), None, diff --git a/client/text_repositories.py b/client/text_repositories.py index 6de5696e8..e80326924 100644 --- a/client/text_repositories.py +++ b/client/text_repositories.py @@ -83,6 +83,8 @@ def repositories(options): rc = _add_repository(entropy_client, myopts) elif repo_opt == "remove": rc = _remove_repository(entropy_client, myopts) + elif repo_opt == "mirrorsort": + rc = _mirror_sort(entropy_client, myopts) else: rc = -10 @@ -201,6 +203,20 @@ def _disable_repositories(entropy_client, repos): teal(repo), blue(_("repository disabled")),)) return 0 +def _mirror_sort(entropy_client, repo_ids): + + for repo_id in repo_ids: + try: + entropy_client.reorder_mirrors(repo_id, dry_run = etpUi['pretend']) + except KeyError: + print_warning("[%s] %s" % ( + purple(repo_id), blue(_("repository not available")),)) + continue + print_info("[%s] %s" % ( + teal(repo_id), blue(_("mirrors sorted successfully")),)) + return 0 + + def _show_repository_info(entropy_client, reponame): repo_number = 0 diff --git a/docs/man/equo.pod b/docs/man/equo.pod index bae616127..32d1f7768 100644 --- a/docs/man/equo.pod +++ b/docs/man/equo.pod @@ -86,6 +86,10 @@ add repository (pass repository string) remove repository +=item B> + +reorder mirrors basing on response time + =back =item B diff --git a/docs/man/man1/equo.1 b/docs/man/man1/equo.1 index 091bab4b5..b69db8502 100644 --- a/docs/man/man1/equo.1 +++ b/docs/man/man1/equo.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "EQUO 1" -.TH EQUO 1 "2010-05-22" "perl v5.10.1" "Entropy" +.TH EQUO 1 "2010-06-30" "perl v5.10.1" "Entropy" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -196,6 +196,9 @@ add repository (pass repository string) .IP "\fBremove " 4 .IX Item "remove " remove repository +.IP "\fBmirrorsort " 4 +.IX Item "mirrorsort " +reorder mirrors basing on response time .RE .RS 4 .RE diff --git a/libraries/entropy/client/interfaces/methods.py b/libraries/entropy/client/interfaces/methods.py index 719e63357..c8b9c17ce 100644 --- a/libraries/entropy/client/interfaces/methods.py +++ b/libraries/entropy/client/interfaces/methods.py @@ -33,7 +33,8 @@ from entropy.cache import EntropyCacher from entropy.client.interfaces.db import ClientEntropyRepositoryPlugin, \ InstalledPackagesRepository, AvailablePackagesRepository, GenericRepository from entropy.client.mirrors import StatusInterface -from entropy.output import purple, bold, red, blue, darkgreen, darkred, brown +from entropy.output import purple, bold, red, blue, darkgreen, darkred, brown, \ + teal from entropy.db.exceptions import IntegrityError, OperationalError, \ DatabaseError @@ -711,7 +712,8 @@ class RepositoryMixin: return "%s%s%s_%sh%sm%ss" % (ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second) - comp_dbname = "%s%s.%s.bz2" % (etpConst['dbbackupprefix'], dbname, get_ts(),) + comp_dbname = "%s%s.%s.bz2" % (etpConst['dbbackupprefix'], + dbname, get_ts(),) comp_dbpath = os.path.join(backup_dir, comp_dbname) if not silent: mytxt = "%s: %s ..." % ( @@ -1307,6 +1309,83 @@ class MiscMixin: return licenses + def reorder_mirrors(self, repository_id, dry_run = False): + """ + Reorder mirror list for given repository using ping statistics. + + @param repository_id: repository identifier + @type repository_id: string + @keyword dry_run: do not actually change repository mirrors order + @type dry_run: bool + @raise KeyError: if repository_id is not available + @return: new repository metadata + @rtype: dict + """ + repo_data = None + avail_data = self._settings['repositories']['available'] + excluded_data = self._settings['repositories']['excluded'] + + if repository_id in avail_data: + repo_data = avail_data[repository_id] + elif repository_id in excluded_data: + repo_data = excluded_data[repository_id] + + if repo_data is None: + raise KeyError("repository_id not found") + + pkg_mirrors = repo_data['plain_packages'] + mirror_stats = {} + tmp_fd, tmp_path = tempfile.mkstemp() + os.close(tmp_fd) + retries = 3 + + for mirror in pkg_mirrors: + + url_data = entropy.tools.spliturl(mirror) + mytxt = "%s: %s" % ( + blue(_("Checking response time of")), + purple(url_data.hostname), + ) + self.output( + mytxt, + importance = 1, + level = "info", + header = purple(" @@ "), + back = True + ) + + start_time = time.time() + for idx in range(retries): + fetcher = self.urlFetcher(mirror, tmp_path, resume = False, + show_speed = False) + fetcher.download() + end_time = time.time() + + result_time = (end_time - start_time)/retries + mirror_stats[mirror] = result_time + + mytxt = "%s: %s, %s" % ( + blue(_("Mirror response time")), + purple(url_data.hostname), + teal(str(result_time)), + ) + self.output( + mytxt, + importance = 1, + level = "info", + header = brown(" @@ ") + ) + + os.remove(tmp_path) + + # calculate new order + new_pkg_mirrors = sorted(mirror_stats.keys(), + key = lambda x: mirror_stats[x], reverse = True) + repo_data['plain_packages'] = new_pkg_mirrors + self.remove_repository(repository_id) + self.add_repository(repo_data) + return repo_data + def set_branch(self, branch): """ Set new Entropy branch. This is NOT thread-safe.