1# Copyright (c) 2019 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import re 7import socket 8import time 9import urllib2 10 11import common 12 13from autotest_lib.client.bin import utils 14from autotest_lib.client.common_lib import error 15 16 17def CheckInterfaceForDestination(host, expected_interface, 18 family=socket.AF_UNSPEC): 19 """ 20 Checks that routes for host go through a given interface. 21 22 The concern here is that our network setup may have gone wrong 23 and our test connections may go over some other network than 24 the one we're trying to test. So we take all the IP addresses 25 for the supplied host and make sure they go through the given 26 network interface. 27 28 @param host: Destination host 29 @param expected_interface: Expected interface name 30 @raises: error.TestFail if the routes for the given host go through 31 a different interface than the expected one. 32 33 """ 34 def _MatchesRoute(address, expected_interface): 35 """ 36 Returns whether or not |expected_interface| is used to reach |address|. 37 38 @param address: string containing an IP (v4 or v6) address. 39 @param expected_interface: string containing an interface name. 40 41 """ 42 output = utils.run('ip route get %s' % address).stdout 43 44 if re.search(r'unreachable', output): 45 return False 46 47 match = re.search(r'\sdev\s(\S+)', output) 48 if match is None: 49 return False 50 interface = match.group(1) 51 52 logging.info('interface for %s: %s', address, interface) 53 if interface != expected_interface: 54 raise error.TestFail('Target server %s uses interface %s' 55 '(%s expected).' % 56 (address, interface, expected_interface)) 57 return True 58 59 # addrinfo records: (family, type, proto, canonname, (addr, port)) 60 server_addresses = [record[4][0] 61 for record in socket.getaddrinfo(host, 80, family)] 62 for address in server_addresses: 63 # Routes may not always be up by this point. Note that routes for v4 or 64 # v6 may come up before the other, so we simply do this poll for all 65 # addresses. 66 utils.poll_for_condition( 67 condition=lambda: _MatchesRoute(address, expected_interface), 68 exception=error.TestFail('No route to %s' % address), 69 timeout=1) 70 71FETCH_URL_PATTERN_FOR_TEST = \ 72 'http://testing-chargen.appspot.com/download?size=%d' 73 74def FetchUrl(url_pattern, bytes_to_fetch=10, fetch_timeout=10): 75 """ 76 Fetches a specified number of bytes from a URL. 77 78 @param url_pattern: URL pattern for fetching a specified number of bytes. 79 %d in the pattern is to be filled in with the number of bytes to 80 fetch. 81 @param bytes_to_fetch: Number of bytes to fetch. 82 @param fetch_timeout: Number of seconds to wait for the fetch to complete 83 before it times out. 84 @return: The time in seconds spent for fetching the specified number of 85 bytes. 86 @raises: error.TestError if one of the following happens: 87 - The fetch takes no time. 88 - The number of bytes fetched differs from the specified 89 number. 90 91 """ 92 # Limit the amount of bytes to read at a time. 93 _MAX_FETCH_READ_BYTES = 1024 * 1024 94 95 url = url_pattern % bytes_to_fetch 96 logging.info('FetchUrl %s', url) 97 start_time = time.time() 98 result = urllib2.urlopen(url, timeout=fetch_timeout) 99 bytes_fetched = 0 100 while bytes_fetched < bytes_to_fetch: 101 bytes_left = bytes_to_fetch - bytes_fetched 102 bytes_to_read = min(bytes_left, _MAX_FETCH_READ_BYTES) 103 bytes_read = len(result.read(bytes_to_read)) 104 bytes_fetched += bytes_read 105 if bytes_read != bytes_to_read: 106 raise error.TestError('FetchUrl tried to read %d bytes, but got ' 107 '%d bytes instead.' % 108 (bytes_to_read, bytes_read)) 109 fetch_time = time.time() - start_time 110 if fetch_time > fetch_timeout: 111 raise error.TestError('FetchUrl exceeded timeout.') 112 113 return fetch_time 114