xref: /aosp_15_r20/external/autotest/client/cros/network.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright (c) 2019 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Liimport logging
7*9c5db199SXin Lifrom six.moves import urllib
8*9c5db199SXin Liimport socket
9*9c5db199SXin Liimport time
10*9c5db199SXin Li
11*9c5db199SXin Lifrom autotest_lib.client.bin import utils
12*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
13*9c5db199SXin Li
14*9c5db199SXin Li
15*9c5db199SXin Lidef CheckThatInterfaceCanAccessDestination(host,
16*9c5db199SXin Li                                           interface,
17*9c5db199SXin Li                                           families=[socket.AF_UNSPEC]):
18*9c5db199SXin Li    """
19*9c5db199SXin Li    Checks that we can access a host using a specific interface.
20*9c5db199SXin Li
21*9c5db199SXin Li    @param host: Destination host
22*9c5db199SXin Li    @param interface: Name of the network interface to be used
23*9c5db199SXin Li    @raises: error.TestFail if the interface cannot access the specified host.
24*9c5db199SXin Li
25*9c5db199SXin Li    """
26*9c5db199SXin Li    logging.debug('Check connection to %s', host)
27*9c5db199SXin Li    # addrinfo records: (family, type, proto, canonname, (addr, port))
28*9c5db199SXin Li    server_addresses = []
29*9c5db199SXin Li    for family in families:
30*9c5db199SXin Li        try:
31*9c5db199SXin Li            records = socket.getaddrinfo(host, 80, family)
32*9c5db199SXin Li        except:
33*9c5db199SXin Li            # Just ignore this family.
34*9c5db199SXin Li            continue
35*9c5db199SXin Li        server_addresses.extend(record[4][0] for record in records)
36*9c5db199SXin Li
37*9c5db199SXin Li    found_route = False
38*9c5db199SXin Li    failing_addresses = []
39*9c5db199SXin Li    for address in set(server_addresses):
40*9c5db199SXin Li        # Routes may not always be up by this point. Note that routes for v4 or
41*9c5db199SXin Li        # v6 may come up before the other, so we simply do this poll for all
42*9c5db199SXin Li        # addresses.
43*9c5db199SXin Li        try:
44*9c5db199SXin Li            utils.poll_for_condition(condition=lambda: utils.ping(
45*9c5db199SXin Li                    address, interface=interface, tries=2, timeout=3) == 0,
46*9c5db199SXin Li                                     exception=Exception('No route to %s' %
47*9c5db199SXin Li                                                         address),
48*9c5db199SXin Li                                     timeout=2)
49*9c5db199SXin Li        except Exception as e:
50*9c5db199SXin Li            logging.info(e)
51*9c5db199SXin Li            failing_addresses.append(address)
52*9c5db199SXin Li        else:
53*9c5db199SXin Li            found_route = True
54*9c5db199SXin Li
55*9c5db199SXin Li    if not found_route:
56*9c5db199SXin Li        raise error.TestFail('Interface %s cannot connect to %s' % (interface,
57*9c5db199SXin Li                             failing_addresses))
58*9c5db199SXin Li
59*9c5db199SXin Li
60*9c5db199SXin LiFETCH_URL_PATTERN_FOR_TEST = \
61*9c5db199SXin Li    'http://testing-chargen.appspot.com/download?size=%d'
62*9c5db199SXin Li
63*9c5db199SXin Lidef FetchUrl(url_pattern, bytes_to_fetch=10, fetch_timeout=10):
64*9c5db199SXin Li    """
65*9c5db199SXin Li    Fetches a specified number of bytes from a URL.
66*9c5db199SXin Li
67*9c5db199SXin Li    @param url_pattern: URL pattern for fetching a specified number of bytes.
68*9c5db199SXin Li            %d in the pattern is to be filled in with the number of bytes to
69*9c5db199SXin Li            fetch.
70*9c5db199SXin Li    @param bytes_to_fetch: Number of bytes to fetch.
71*9c5db199SXin Li    @param fetch_timeout: Number of seconds to wait for the fetch to complete
72*9c5db199SXin Li            before it times out.
73*9c5db199SXin Li    @return: The time in seconds spent for fetching the specified number of
74*9c5db199SXin Li            bytes.
75*9c5db199SXin Li    @raises: error.TestError if one of the following happens:
76*9c5db199SXin Li            - The fetch takes no time.
77*9c5db199SXin Li            - The number of bytes fetched differs from the specified
78*9c5db199SXin Li              number.
79*9c5db199SXin Li
80*9c5db199SXin Li    """
81*9c5db199SXin Li    # Limit the amount of bytes to read at a time.
82*9c5db199SXin Li    _MAX_FETCH_READ_BYTES = 1024 * 1024
83*9c5db199SXin Li
84*9c5db199SXin Li    url = url_pattern % bytes_to_fetch
85*9c5db199SXin Li    logging.info('FetchUrl %s', url)
86*9c5db199SXin Li    start_time = time.time()
87*9c5db199SXin Li    result = urllib.request.urlopen(url, timeout=fetch_timeout)
88*9c5db199SXin Li    bytes_fetched = 0
89*9c5db199SXin Li    while bytes_fetched < bytes_to_fetch:
90*9c5db199SXin Li        bytes_left = bytes_to_fetch - bytes_fetched
91*9c5db199SXin Li        bytes_to_read = min(bytes_left, _MAX_FETCH_READ_BYTES)
92*9c5db199SXin Li        bytes_read = len(result.read(bytes_to_read))
93*9c5db199SXin Li        bytes_fetched += bytes_read
94*9c5db199SXin Li        if bytes_read != bytes_to_read:
95*9c5db199SXin Li            raise error.TestError('FetchUrl tried to read %d bytes, but got '
96*9c5db199SXin Li                                  '%d bytes instead.' %
97*9c5db199SXin Li                                  (bytes_to_read, bytes_read))
98*9c5db199SXin Li        fetch_time = time.time() - start_time
99*9c5db199SXin Li        if fetch_time > fetch_timeout:
100*9c5db199SXin Li            raise error.TestError('FetchUrl exceeded timeout.')
101*9c5db199SXin Li
102*9c5db199SXin Li    return fetch_time
103