Example #1
0
    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
Example #2
0
    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))
Example #3
0
    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]
Example #4
0
    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
Example #5
0
    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))
Example #6
0
    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]
Example #7
0
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()
Example #8
0
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()