def get_launchpad_urls(self): """Obtain mirrors' corresponding launchpad URLs""" launchpad_base = "https://launchpad.net" launchpad_url = launchpad_base + "/ubuntu/+archivemirrors" stderr.write("Getting list of launchpad URLs...") try: launchpad_html = get_html(launchpad_url) except HTMLGetError as err: stderr.write(( "%s: %s\nUnable to retrieve list of launchpad sites\n" "Reverting to latency only" % (launchpad_url, err) )) self.abort_launch = True else: stderr.write("done.\n") soup = BeautifulSoup(launchpad_html, PARSER) prev = "" for element in soup.table.descendants: try: url = element.a except AttributeError: pass else: try: url = url["href"] except TypeError: pass else: if url in self.urls: self.urls[url]["Launchpad"] = launchpad_base + prev if url.startswith("/ubuntu/+mirror/"): prev = url
def get_info(self): """Parse launchpad page HTML for mirror information Ideally, launchpadlib would be used to get mirror information, but the Launchpad API doesn't support access to archivemirror statuses.""" try: launch_html = get_html(self.launch_url) except HTMLGetError as err: stderr.write("connection to %s: %s" % (self.launch_url, err)) self.data_queue.put_nowait((self.url, None)) else: info = self.__parse_mirror_html(launch_html) if "Status" not in info: stderr.write(( "Unable to parse status info from %s" % self.launch_url )) self.data_queue.put_nowait((self.url, None)) return # Launchpad has more descriptive "unknown" status. # It's trimmed here to match statuses list if "unknown" in info["Status"]: info["Status"] = "unknown" self.data_queue.put((self.url, info))
def __get_info(self, url): """Parse launchpad page HTML for mirror information""" try: launch_html = get_html(self.urls[url]["Launchpad"]) except HTMLGetError as err: raise DataError(( "connection to %s: %s" % (self.urls[url]["Launchpad"], err) )) info = {} soup = BeautifulSoup(launch_html, PARSER) for line in soup.find('table', class_='listing sortable', id='arches').find('tbody').find_all('tr'): arches = [x.get_text() for x in line.find_all('td')] if self.codename in arches[0] and arches[1] == self.hardware: info.update({"Status": arches[2]}) for line in soup.find_all(id=re.compile('speed|organisation')): info.update({line.dt.get_text().strip(':'): line.dd.get_text()}) if "Status" not in info: raise DataError(( "Unable to parse status info from %s" % self.urls[url]["Launchpad"] )) # Launchpad has more descriptive "unknown" status. # It's trimmed here to match statuses list if "unknown" in info["Status"]: info["Status"] = "unknown" return [url, info]
def get_info(self): """Parse launchpad page HTML for mirror information Ideally, launchpadlib would be used to get mirror information, but the Launchpad API doesn't support access to archivemirror statuses.""" try: launch_html = get_html(self.launch_url) except HTMLGetError as err: stderr.write("connection to %s: %s" % (self.launch_url, err)) self.data_queue.put_nowait(self.url, None) else: info = self.__parse_mirror_html(launch_html) if "Status" not in info: stderr.write(( "Unable to parse status info from %s" % self.launch_url )) self.data_queue.put_nowait(self.url, None) return # Launchpad has more descriptive "unknown" status. # It's trimmed here to match statuses list if "unknown" in info["Status"]: info["Status"] = "unknown" self.data_queue.put((self.url, info))
def __get_info(self, url): """Parse launchpad page HTML for mirror information Ideally, launchpadlib would be used to get mirror information, but the Launchpad API doesn't support access to archivemirror statuses.""" try: launch_html = get_html(self.urls[url]["Launchpad"]) except HTMLGetError as err: raise DataError(( "connection to %s: %s" % (self.urls[url]["Launchpad"], err) )) info = {} soup = BeautifulSoup(launch_html, PARSER) # Find elements of the ids we need for line in soup.find_all(id=['arches', 'speed', 'organisation']): if line.name == 'table': # Status information lives in a table column alongside # series name and machine architecture for tr in line.find('tbody').find_all('tr'): arches = [x.get_text() for x in tr.find_all('td')] if self.codename in arches[0] and arches[1] == self.hardware: info.update({"Status": arches[2]}) else: # "Speed" lives in a dl, and we use the key -> value as such info.update({line.dt.get_text().strip(':'): line.dd.get_text()}) if "Status" not in info: raise DataError(( "Unable to parse status info from %s" % self.urls[url]["Launchpad"] )) # Launchpad has more descriptive "unknown" status. # It's trimmed here to match statuses list if "unknown" in info["Status"]: info["Status"] = "unknown" return [url, info]
def apt_select(): """Run apt-select: Ubuntu archive mirror reporting tool""" parser = get_args() args = parser.parse_args() top_number = args.top_number[0] ping_only = args.ping_only list_only = args.list_only choose = args.choose min_status = args.min_status[0].replace('-', ' ') if not ping_only and (min_status != 'unknown'): # Convert status argument to format used by Launchpad min_status = min_status[0].upper() + min_status[1:] if choose and (not top_number or top_number < 2): parser.print_usage() exit(( "error: -c/--choose option requires -t/--top-number NUMBER " "where NUMBER is greater than 1." )) try: release = check_output(["lsb_release", "-ics"]) except OSError: not_ubuntu() else: release = [s.strip() for s in release.decode('utf-8').split()] if release[0] == 'Debian': exit("Debian is not currently supported") elif release[0] != 'Ubuntu': not_ubuntu() directory = '/etc/apt/' apt_file = 'sources.list' sources_path = directory + apt_file if not path.isfile(sources_path): exit("%s must exist as file" % sources_path) mirrors_loc = "mirrors.ubuntu.com" mirrors_url = "http://%s/mirrors.txt" % mirrors_loc stderr.write("Getting list of mirrors...") try: mirrors_list = get_html(mirrors_url) except HTMLGetError as err: exit("Error getting list from %s:\n\t%s" % (mirrors_list, err)) stderr.write("done.\n") mirrors_list = mirrors_list.splitlines() codename = release[1][0].upper() + release[1][1:] hardware = check_output(["uname", "-m"]).strip().decode('utf-8') if hardware == 'x86_64': hardware = 'amd64' else: hardware = 'i386' archives = Mirrors(mirrors_list, ping_only, min_status) archives.get_rtts() if archives.got["ping"] < top_number: top_number = archives.got["ping"] if top_number == 0: exit("Cannot connect to any mirrors in %s\n." % mirrors_list) if not ping_only: archives.get_launchpad_urls() if not archives.abort_launch: # Mirrors needs a limit to stop launching threads archives.status_num = top_number stderr.write("Looking up %d status(es)\n" % top_number) archives.lookup_statuses(min_status, codename, hardware) if top_number > 1: stderr.write('\n') repo_name = "" found = False skip_gen_msg = "Skipping file generation." with open(sources_path, 'r') as sources_file: lines = sources_file.readlines() repos = [] required_repo = "main" for line in lines: fields = line.split() if confirm_mirror(fields): if (not found and (release[1] in fields[2]) and (fields[3] == required_repo)): repos += [fields[1]] found = True continue elif fields[2] == '%s-security' % (release[1]): repos += [fields[1]] break if not repos: stderr.write(( "Error finding current %s repository in %s\n%s\n" % (required_repo, sources_path, skip_gen_msg) )) else: repo_name = repos[0] rank = 0 current_key = -1 if ping_only: archives.top_list = archives.ranked[:top_number+1] for url in archives.top_list: info = archives.urls[url] host = info["Host"] if url == repo_name: host += " (current)" current_key = rank if not ping_only and not archives.abort_launch: if "Status" in info: assign_defaults(info, ("Org", "Speed"), "N/A") print(( "%(rank)d. %(mirror)s\n%(tab)sLatency: %(ms)d ms\n" "%(tab)sOrg: %(org)s\n%(tab)sStatus: %(status)s\n" "%(tab)sSpeed: %(speed)s" % { 'tab': ' ', 'rank': rank + 1, 'mirror': host, 'ms': info["Latency"], 'org': info["Organisation"], 'status': info["Status"], 'speed': info["Speed"] } )) else: print("%d. %s: %d ms" % (rank+1, info["Host"], info["Latency"])) rank += 1 if rank == top_number: break key = 0 if choose: key = ask(( "Choose a mirror (1 - %d)\n'q' to quit " % len(archives.top_list) )) while True: try: key = int(key) except ValueError: if key == 'q': exit() if (type(key) is not str) and (key >= 1) and (key <= rank): break key = ask("Invalid entry ") key -= 1 if list_only: exit() # Avoid generating duplicate sources.list if current_key == key: exit(( "%s is the currently used mirror.\n%s" % (archives.urls[repo_name]["Host"], skip_gen_msg) )) mirror = archives.top_list[key] lines = ''.join(lines) for repo in repos: lines = lines.replace(repo, mirror) work_dir = getcwd() if work_dir == directory[0:-1]: query = ( "'%(dir)s' is the current directory.\n" "Generating a new '%(apt)s' file will " "overwrite the current file.\n" "You should copy or backup '%(apt)s' before replacing it.\n" "Continue?\n[yes|no] " % { 'dir': directory, 'apt': apt_file } ) yes_or_no(query) write_file = work_dir.rstrip('/') + '/' + apt_file try: with open(write_file, 'w') as sources_file: sources_file.write(lines) except IOError as err: exit("Unable to generate sources.list:\n\t%s\n" % err) else: print("New config file saved to %s" % write_file) exit()
def apt_select(): """Run apt-select: Ubuntu archive mirror reporting tool""" parser = get_args() args = parser.parse_args() top_number = args.top_number[0] ping_only = args.ping_only list_only = args.list_only choose = args.choose min_status = args.min_status[0].replace('-', ' ') if not ping_only and (min_status != 'unknown'): # Convert status argument to format used by Launchpad min_status = min_status[0].upper() + min_status[1:] if choose and (not top_number or top_number < 2): parser.print_usage() exit(("error: -c/--choose option requires -t/--top-number NUMBER " "where NUMBER is greater than 1.")) try: release = check_output(["lsb_release", "-ics"]) except OSError: not_ubuntu() else: release = [s.strip() for s in release.decode('utf-8').split()] if release[0] == 'Debian': exit("Debian is not currently supported") elif release[0] != 'Ubuntu': not_ubuntu() directory = '/etc/apt/' apt_file = 'sources.list' sources_path = directory + apt_file if not path.isfile(sources_path): exit("%s must exist as file" % sources_path) mirrors_loc = "mirrors.ubuntu.com" mirrors_url = "http://%s/mirrors.txt" % mirrors_loc stderr.write("Getting list of mirrors...") try: mirrors_list = get_html(mirrors_url) except HTMLGetError as err: exit("Error getting list from %s:\n\t%s" % (mirrors_list, err)) stderr.write("done.\n") mirrors_list = mirrors_list.splitlines() codename = release[1][0].upper() + release[1][1:] hardware = check_output(["uname", "-m"]).strip().decode('utf-8') if hardware == 'x86_64': hardware = 'amd64' else: hardware = 'i386' archives = Mirrors(mirrors_list, ping_only, min_status) archives.get_rtts() if archives.got["ping"] < top_number: top_number = archives.got["ping"] if top_number == 0: exit("Cannot connect to any mirrors in %s\n." % mirrors_list) if not ping_only: archives.get_launchpad_urls() if not archives.abort_launch: # Mirrors needs a limit to stop launching threads archives.status_num = top_number stderr.write("Looking up %d status(es)\n" % top_number) archives.lookup_statuses(min_status, codename, hardware) if top_number > 1: stderr.write('\n') repo_name = "" found = False skip_gen_msg = "Skipping file generation." with open(sources_path, 'r') as sources_file: lines = sources_file.readlines() repos = [] required_repo = "main" for line in lines: fields = line.split() if confirm_mirror(fields): if (not found and (release[1] in fields[2]) and (fields[3] == required_repo)): repos += [fields[1]] found = True continue elif fields[2] == '%s-security' % (release[1]): repos += [fields[1]] break if not repos: stderr.write(("Error finding current %s repository in %s\n%s\n" % (required_repo, sources_path, skip_gen_msg))) else: repo_name = repos[0] rank = 0 current_key = -1 if ping_only: archives.top_list = archives.ranked[:top_number + 1] for url in archives.top_list: info = archives.urls[url] host = info["Host"] if url == repo_name: host += " (current)" current_key = rank if not ping_only and not archives.abort_launch: if "Status" in info: assign_defaults(info, ("Org", "Speed"), "N/A") print(("%(rank)d. %(mirror)s\n%(tab)sLatency: %(ms)d ms\n" "%(tab)sOrg: %(org)s\n%(tab)sStatus: %(status)s\n" "%(tab)sSpeed: %(speed)s" % { 'tab': ' ', 'rank': rank + 1, 'mirror': host, 'ms': info["Latency"], 'org': info["Organisation"], 'status': info["Status"], 'speed': info["Speed"] })) else: print("%d. %s: %d ms" % (rank + 1, info["Host"], info["Latency"])) rank += 1 if rank == top_number: break key = 0 if choose: key = ask(("Choose a mirror (1 - %d)\n'q' to quit " % len(archives.top_list))) while True: try: key = int(key) except ValueError: if key == 'q': exit() if (type(key) is not str) and (key >= 1) and (key <= rank): break key = ask("Invalid entry ") key -= 1 if list_only: exit() # Avoid generating duplicate sources.list if current_key == key: exit(("%s is the currently used mirror.\n%s" % (archives.urls[repo_name]["Host"], skip_gen_msg))) mirror = archives.top_list[key] lines = ''.join(lines) for repo in repos: lines = lines.replace(repo, mirror) work_dir = getcwd() if work_dir == directory[0:-1]: query = ("'%(dir)s' is the current directory.\n" "Generating a new '%(apt)s' file will " "overwrite the current file.\n" "You should copy or backup '%(apt)s' before replacing it.\n" "Continue?\n[yes|no] " % { 'dir': directory, 'apt': apt_file }) yes_or_no(query) write_file = work_dir.rstrip('/') + '/' + apt_file try: with open(write_file, 'w') as sources_file: sources_file.write(lines) except IOError as err: exit("Unable to generate sources.list:\n\t%s\n" % err) else: print("New config file saved to %s" % write_file) exit()