Derek Beckett | 8affba0 | 2020-10-20 14:24:18 -0700 | [diff] [blame^] | 1 | # Lint as: python2, python3 |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 2 | # Copyright (c) 2019 The Chromium OS Authors. All rights reserved. |
David Rochberg | 1068c20 | 2011-05-18 15:42:03 -0400 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 6 | import logging |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 7 | import re |
Derek Beckett | 8affba0 | 2020-10-20 14:24:18 -0700 | [diff] [blame^] | 8 | from six.moves import urllib |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 9 | import socket |
| 10 | import time |
David Rochberg | 65a34fc | 2011-12-29 17:39:13 -0500 | [diff] [blame] | 11 | |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 12 | import common |
| 13 | |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 14 | from autotest_lib.client.bin import utils |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 15 | from autotest_lib.client.common_lib import error |
David Rochberg | c1b3240 | 2011-05-24 16:51:48 -0400 | [diff] [blame] | 16 | |
David Rochberg | 6eef6cc | 2011-10-31 10:19:06 -0400 | [diff] [blame] | 17 | |
Alex Khouderchah | 49a623e | 2019-10-07 14:49:32 -0700 | [diff] [blame] | 18 | def CheckInterfaceForDestination(host, expected_interface, |
| 19 | family=socket.AF_UNSPEC): |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 20 | """ |
| 21 | Checks that routes for host go through a given interface. |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 22 | |
| 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 Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 29 | @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 Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 32 | a different interface than the expected one. |
| 33 | |
| 34 | """ |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 35 | def _MatchesRoute(address, expected_interface): |
| 36 | """ |
| 37 | Returns whether or not |expected_interface| is used to reach |address|. |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 38 | |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 39 | @param address: string containing an IP (v4 or v6) address. |
| 40 | @param expected_interface: string containing an interface name. |
Ben Chan | bfd5284 | 2017-07-28 14:17:08 -0700 | [diff] [blame] | 41 | |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 42 | """ |
| 43 | output = utils.run('ip route get %s' % address).stdout |
Eric Caruso | 7407ed7 | 2017-07-17 11:13:59 -0700 | [diff] [blame] | 44 | |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 45 | 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 Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 53 | 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 Khouderchah | f404c11 | 2019-09-23 08:53:40 -0700 | [diff] [blame] | 58 | return True |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 59 | |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 60 | # addrinfo records: (family, type, proto, canonname, (addr, port)) |
| 61 | server_addresses = [record[4][0] |
Alex Khouderchah | 49a623e | 2019-10-07 14:49:32 -0700 | [diff] [blame] | 62 | for record in socket.getaddrinfo(host, 80, family)] |
Alex Khouderchah | 7d763ab | 2019-07-25 12:22:20 -0700 | [diff] [blame] | 63 | 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 Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 71 | |
| 72 | FETCH_URL_PATTERN_FOR_TEST = \ |
| 73 | 'http://testing-chargen.appspot.com/download?size=%d' |
| 74 | |
| 75 | def FetchUrl(url_pattern, bytes_to_fetch=10, fetch_timeout=10): |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 76 | """ |
| 77 | Fetches a specified number of bytes from a URL. |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 78 | |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 79 | @param url_pattern: URL pattern for fetching a specified number of bytes. |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 80 | %d in the pattern is to be filled in with the number of bytes to |
| 81 | fetch. |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 82 | @param bytes_to_fetch: Number of bytes to fetch. |
| 83 | @param fetch_timeout: Number of seconds to wait for the fetch to complete |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 84 | before it times out. |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 85 | @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 Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 88 | - The fetch takes no time. |
Thieu Le | 0f2a00d | 2013-05-30 13:43:55 -0700 | [diff] [blame] | 89 | - The number of bytes fetched differs from the specified |
| 90 | number. |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 91 | |
| 92 | """ |
Thieu Le | 6369a1d | 2014-04-23 14:31:36 -0700 | [diff] [blame] | 93 | # Limit the amount of bytes to read at a time. |
| 94 | _MAX_FETCH_READ_BYTES = 1024 * 1024 |
| 95 | |
Ben Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 96 | url = url_pattern % bytes_to_fetch |
| 97 | logging.info('FetchUrl %s', url) |
| 98 | start_time = time.time() |
Derek Beckett | 8affba0 | 2020-10-20 14:24:18 -0700 | [diff] [blame^] | 99 | result = urllib.request.urlopen(url, timeout=fetch_timeout) |
Thieu Le | 6369a1d | 2014-04-23 14:31:36 -0700 | [diff] [blame] | 100 | 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 Chan | 89216c6 | 2013-05-07 15:55:15 -0700 | [diff] [blame] | 113 | |
| 114 | return fetch_time |