blob: 33c6481f118e1965129675733d3193481af5b000 [file] [log] [blame]
Derek Beckett8affba02020-10-20 14:24:18 -07001# Lint as: python2, python3
Alex Khouderchah7d763ab2019-07-25 12:22:20 -07002# Copyright (c) 2019 The Chromium OS Authors. All rights reserved.
David Rochberg1068c202011-05-18 15:42:03 -04003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Ben Chan89216c62013-05-07 15:55:15 -07006import logging
Alex Khouderchah7d763ab2019-07-25 12:22:20 -07007import re
Derek Beckett8affba02020-10-20 14:24:18 -07008from six.moves import urllib
Ben Chan89216c62013-05-07 15:55:15 -07009import socket
10import time
David Rochberg65a34fc2011-12-29 17:39:13 -050011
Ben Chan89216c62013-05-07 15:55:15 -070012import common
13
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070014from autotest_lib.client.bin import utils
Ben Chan89216c62013-05-07 15:55:15 -070015from autotest_lib.client.common_lib import error
David Rochbergc1b32402011-05-24 16:51:48 -040016
David Rochberg6eef6cc2011-10-31 10:19:06 -040017
Alex Khouderchah49a623e2019-10-07 14:49:32 -070018def CheckInterfaceForDestination(host, expected_interface,
19 family=socket.AF_UNSPEC):
Thieu Le0f2a00d2013-05-30 13:43:55 -070020 """
21 Checks that routes for host go through a given interface.
Ben Chan89216c62013-05-07 15:55:15 -070022
23 The concern here is that our network setup may have gone wrong
24 and our test connections may go over some other network than
25 the one we're trying to test. So we take all the IP addresses
26 for the supplied host and make sure they go through the given
27 network interface.
28
Thieu Le0f2a00d2013-05-30 13:43:55 -070029 @param host: Destination host
30 @param expected_interface: Expected interface name
31 @raises: error.TestFail if the routes for the given host go through
Ben Chan89216c62013-05-07 15:55:15 -070032 a different interface than the expected one.
33
34 """
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070035 def _MatchesRoute(address, expected_interface):
36 """
37 Returns whether or not |expected_interface| is used to reach |address|.
Ben Chan89216c62013-05-07 15:55:15 -070038
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070039 @param address: string containing an IP (v4 or v6) address.
40 @param expected_interface: string containing an interface name.
Ben Chanbfd52842017-07-28 14:17:08 -070041
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070042 """
43 output = utils.run('ip route get %s' % address).stdout
Eric Caruso7407ed72017-07-17 11:13:59 -070044
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070045 if re.search(r'unreachable', output):
46 return False
47
48 match = re.search(r'\sdev\s(\S+)', output)
49 if match is None:
50 return False
51 interface = match.group(1)
52
Ben Chan89216c62013-05-07 15:55:15 -070053 logging.info('interface for %s: %s', address, interface)
54 if interface != expected_interface:
55 raise error.TestFail('Target server %s uses interface %s'
56 '(%s expected).' %
57 (address, interface, expected_interface))
Alex Khouderchahf404c112019-09-23 08:53:40 -070058 return True
Ben Chan89216c62013-05-07 15:55:15 -070059
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070060 # addrinfo records: (family, type, proto, canonname, (addr, port))
61 server_addresses = [record[4][0]
Alex Khouderchah49a623e2019-10-07 14:49:32 -070062 for record in socket.getaddrinfo(host, 80, family)]
Alex Khouderchah7d763ab2019-07-25 12:22:20 -070063 for address in server_addresses:
64 # Routes may not always be up by this point. Note that routes for v4 or
65 # v6 may come up before the other, so we simply do this poll for all
66 # addresses.
67 utils.poll_for_condition(
68 condition=lambda: _MatchesRoute(address, expected_interface),
69 exception=error.TestFail('No route to %s' % address),
70 timeout=1)
Ben Chan89216c62013-05-07 15:55:15 -070071
72FETCH_URL_PATTERN_FOR_TEST = \
73 'http://testing-chargen.appspot.com/download?size=%d'
74
75def FetchUrl(url_pattern, bytes_to_fetch=10, fetch_timeout=10):
Thieu Le0f2a00d2013-05-30 13:43:55 -070076 """
77 Fetches a specified number of bytes from a URL.
Ben Chan89216c62013-05-07 15:55:15 -070078
Thieu Le0f2a00d2013-05-30 13:43:55 -070079 @param url_pattern: URL pattern for fetching a specified number of bytes.
Ben Chan89216c62013-05-07 15:55:15 -070080 %d in the pattern is to be filled in with the number of bytes to
81 fetch.
Thieu Le0f2a00d2013-05-30 13:43:55 -070082 @param bytes_to_fetch: Number of bytes to fetch.
83 @param fetch_timeout: Number of seconds to wait for the fetch to complete
Ben Chan89216c62013-05-07 15:55:15 -070084 before it times out.
Thieu Le0f2a00d2013-05-30 13:43:55 -070085 @return: The time in seconds spent for fetching the specified number of
86 bytes.
87 @raises: error.TestError if one of the following happens:
Ben Chan89216c62013-05-07 15:55:15 -070088 - The fetch takes no time.
Thieu Le0f2a00d2013-05-30 13:43:55 -070089 - The number of bytes fetched differs from the specified
90 number.
Ben Chan89216c62013-05-07 15:55:15 -070091
92 """
Thieu Le6369a1d2014-04-23 14:31:36 -070093 # Limit the amount of bytes to read at a time.
94 _MAX_FETCH_READ_BYTES = 1024 * 1024
95
Ben Chan89216c62013-05-07 15:55:15 -070096 url = url_pattern % bytes_to_fetch
97 logging.info('FetchUrl %s', url)
98 start_time = time.time()
Derek Beckett8affba02020-10-20 14:24:18 -070099 result = urllib.request.urlopen(url, timeout=fetch_timeout)
Thieu Le6369a1d2014-04-23 14:31:36 -0700100 bytes_fetched = 0
101 while bytes_fetched < bytes_to_fetch:
102 bytes_left = bytes_to_fetch - bytes_fetched
103 bytes_to_read = min(bytes_left, _MAX_FETCH_READ_BYTES)
104 bytes_read = len(result.read(bytes_to_read))
105 bytes_fetched += bytes_read
106 if bytes_read != bytes_to_read:
107 raise error.TestError('FetchUrl tried to read %d bytes, but got '
108 '%d bytes instead.' %
109 (bytes_to_read, bytes_read))
110 fetch_time = time.time() - start_time
111 if fetch_time > fetch_timeout:
112 raise error.TestError('FetchUrl exceeded timeout.')
Ben Chan89216c62013-05-07 15:55:15 -0700113
114 return fetch_time