| # Lint as: python2, python3 |
| # Copyright (c) 2019 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| from six.moves import urllib |
| import socket |
| import time |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| |
| |
| def CheckThatInterfaceCanAccessDestination(host, |
| interface, |
| families=[socket.AF_UNSPEC]): |
| """ |
| Checks that we can access a host using a specific interface. |
| |
| @param host: Destination host |
| @param interface: Name of the network interface to be used |
| @raises: error.TestFail if the interface cannot access the specified host. |
| |
| """ |
| logging.debug('Check connection to %s', host) |
| # addrinfo records: (family, type, proto, canonname, (addr, port)) |
| server_addresses = [] |
| for family in families: |
| try: |
| records = socket.getaddrinfo(host, 80, family) |
| except: |
| # Just ignore this family. |
| continue |
| server_addresses.extend(record[4][0] for record in records) |
| |
| found_route = False |
| failing_addresses = [] |
| for address in set(server_addresses): |
| # Routes may not always be up by this point. Note that routes for v4 or |
| # v6 may come up before the other, so we simply do this poll for all |
| # addresses. |
| try: |
| utils.poll_for_condition(condition=lambda: utils.ping( |
| address, interface=interface, tries=2, timeout=3) == 0, |
| exception=Exception('No route to %s' % |
| address), |
| timeout=2) |
| except Exception as e: |
| logging.info(e) |
| failing_addresses.append(address) |
| else: |
| found_route = True |
| |
| if not found_route: |
| raise error.TestFail('Interface %s cannot connect to %s' % (interface, |
| failing_addresses)) |
| |
| |
| FETCH_URL_PATTERN_FOR_TEST = \ |
| 'http://testing-chargen.appspot.com/download?size=%d' |
| |
| def FetchUrl(url_pattern, bytes_to_fetch=10, fetch_timeout=10): |
| """ |
| Fetches a specified number of bytes from a URL. |
| |
| @param url_pattern: URL pattern for fetching a specified number of bytes. |
| %d in the pattern is to be filled in with the number of bytes to |
| fetch. |
| @param bytes_to_fetch: Number of bytes to fetch. |
| @param fetch_timeout: Number of seconds to wait for the fetch to complete |
| before it times out. |
| @return: The time in seconds spent for fetching the specified number of |
| bytes. |
| @raises: error.TestError if one of the following happens: |
| - The fetch takes no time. |
| - The number of bytes fetched differs from the specified |
| number. |
| |
| """ |
| # Limit the amount of bytes to read at a time. |
| _MAX_FETCH_READ_BYTES = 1024 * 1024 |
| |
| url = url_pattern % bytes_to_fetch |
| logging.info('FetchUrl %s', url) |
| start_time = time.time() |
| result = urllib.request.urlopen(url, timeout=fetch_timeout) |
| bytes_fetched = 0 |
| while bytes_fetched < bytes_to_fetch: |
| bytes_left = bytes_to_fetch - bytes_fetched |
| bytes_to_read = min(bytes_left, _MAX_FETCH_READ_BYTES) |
| bytes_read = len(result.read(bytes_to_read)) |
| bytes_fetched += bytes_read |
| if bytes_read != bytes_to_read: |
| raise error.TestError('FetchUrl tried to read %d bytes, but got ' |
| '%d bytes instead.' % |
| (bytes_to_read, bytes_read)) |
| fetch_time = time.time() - start_time |
| if fetch_time > fetch_timeout: |
| raise error.TestError('FetchUrl exceeded timeout.') |
| |
| return fetch_time |