| # Copyright (c) 2013 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 |
| import os |
| import sys |
| import tempfile |
| import time |
| |
| from autotest_lib.client.bin import test |
| from autotest_lib.client.common_lib import error, utils |
| from autotest_lib.client.cros import p2p_utils |
| from autotest_lib.client.cros.netprotos import cros_p2p, zeroconf |
| |
| |
| class p2p_ServeFiles(test.test): |
| """The P2P Server class tester. |
| |
| This class runs the p2p service (p2p-server and p2p-http-server) and checks |
| that the DUT is serving the shared files on the network. |
| """ |
| version = 1 |
| |
| def setup(self): |
| self.job.setup_dep(['lansim']) |
| |
| |
| def initialize(self): |
| dep = 'lansim' |
| dep_dir = os.path.join(self.autodir, 'deps', dep) |
| logging.info('lansim is at %s', dep_dir) |
| self.job.install_pkg(dep, 'dep', dep_dir) |
| |
| # Import the lansim modules installed on lansim/build/ |
| sys.path.append(os.path.join(dep_dir, 'build')) |
| |
| self._p2p = p2p_utils.P2PServerOverTap() |
| self._sim = None |
| |
| |
| def cleanup(self): |
| # Work around problem described in the chromium:364583 bug. |
| time.sleep(1) |
| self._join_simulator() |
| self._p2p.cleanup() |
| |
| |
| def _join_simulator(self): |
| """Stops the simulator and logs any exception generated there.""" |
| if not self._sim: |
| return |
| self._sim.stop() |
| self._sim.join() |
| if self._sim.error: |
| logging.error('SimulatorThread exception: %r', self._sim.error) |
| logging.error(self._sim.traceback) |
| |
| |
| def _dut_ready(self, p2pcli): |
| # Lookup the DUT on the mDNS network. |
| peers = p2pcli.get_peers() |
| if not peers: |
| return False |
| peer_name, hostname, ips, port = peers[0] |
| # Get the files shared by the DUT. |
| files = p2pcli.get_peer_files(peer_name) |
| if not files: |
| return False |
| return peer_name, hostname, ips, port, files |
| |
| |
| def _p2p_fetch(self, host, port, filename): |
| """Fetch a file from a p2p-http-server. |
| |
| @return: A str with the contents of the responde if the request |
| succeeds or an integer value with the error code returned by curl |
| otherwise. |
| """ |
| fd, tempfn = tempfile.mkstemp(prefix='p2p-fetch') |
| ret = utils.run( |
| 'curl', args=['http://%s:%s/%s' % (host, port, filename)], |
| timeout=20., ignore_timeout=False, ignore_status=True, |
| stdout_tee=open(tempfn, 'w'), stderr_tee=sys.stdout) |
| with os.fdopen(fd) as f: |
| output = f.read() |
| os.unlink(tempfn) |
| |
| if ret is None: |
| return None |
| if ret.exit_status != 0: |
| return ret.exit_status |
| return output |
| |
| |
| def run_once(self): |
| from lansim import simulator, host |
| |
| # Setup the environment where avahi-daemon runs during the test. |
| try: |
| self._p2p.setup(dumpdir=self.job.resultdir) |
| except: |
| logging.exception('Failed to start tested services.') |
| raise |
| |
| # Share a file on the DUT. |
| content = open('/dev/urandom').read(16*1024) |
| with open(os.path.join(p2p_utils.P2P_SHARE_PATH, 'file.p2p'), 'w') as f: |
| f.write(content) |
| |
| self._sim = simulator.SimulatorThread(self._p2p.tap) |
| # Create a single fake peer that will be sending the multicast requests. |
| peer = host.SimpleHost(self._sim, '94:EB:2C:00:00:61', '169.254.10.55') |
| |
| # Run a userspace implementation of avahi + p2p-client on the fake |
| # host. This will use the P2P services exported by the DUT. |
| zero = zeroconf.ZeroconfDaemon(peer, 'peer') |
| p2pcli = cros_p2p.CrosP2PClient(zero) |
| |
| self._sim.start() |
| |
| # Force a request from the client before waiting for the DUT's response. |
| self._sim.run_on_simulator(lambda: p2pcli.start_query()) |
| |
| # Wait up to 30 seconds until the DUT is ready sharing the files. |
| res = self._sim.wait_for_condition(lambda: self._dut_ready(p2pcli), |
| timeout=30.) |
| self._sim.run_on_simulator(lambda: p2pcli.stop_query()) |
| |
| if not res: |
| raise error.TestFail('The DUT failed to announce the shared files ' |
| 'after 30 seconds.') |
| |
| # DUT's p2p-http-server is running on hostname:port. |
| peer_name, hostname, ips, port, files = res |
| |
| if len(files) != 1 or files[0] != ('file', len(content)) or ( |
| len(ips) != 1) or ips[0] != self._p2p.tap.addr: |
| logging.error('peer_name = %r', peer_name) |
| logging.error('hostname = %r', hostname) |
| logging.error('ips = %r', ips) |
| logging.error('port = %r', port) |
| logging.error('files = %r', files) |
| raise error.TestFail('The DUT announced an erroneous file.') |
| |
| # Check we can't download directly from localhost. |
| for host_ip in (ips[0], '127.0.0.1'): |
| ret = self._p2p_fetch(host_ip, port, 'file') |
| if ret != 7: # curl's exit code 7 is "Failed to connect to host." |
| logging.error('curl returned: %s', repr(ret)[:100]) |
| raise error.TestFail( |
| "The DUT didn't block a request from localhost using " |
| "the address %s." % host_ip) |
| |
| # Check we can download if the connection comes from a peer on the |
| # network. To achieve this, we forward the tester's TCP traffic through |
| # a fake host on lansim. |
| self._sim.run_on_simulator(lambda: peer.tcp_forward(1234, ips[0], port)) |
| |
| ret = self._p2p_fetch(peer.ip_addr, 1234, 'file') |
| if ret != content: |
| logging.error('curl returned: %s', repr(ret)[:100]) |
| raise error.TestFail( |
| "The DUT didn't serve the file request from %s " % |
| peer.id_addr) |