Home | History | Annotate | Download | only in p2p_ServeFiles
      1 # Copyright (c) 2013 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 
      5 import logging
      6 import os
      7 import sys
      8 import tempfile
      9 import time
     10 
     11 from autotest_lib.client.bin import test
     12 from autotest_lib.client.common_lib import error, utils
     13 from autotest_lib.client.cros import p2p_utils
     14 from autotest_lib.client.cros.netprotos import cros_p2p, zeroconf
     15 
     16 
     17 class p2p_ServeFiles(test.test):
     18     """The P2P Server class tester.
     19 
     20     This class runs the p2p service (p2p-server and p2p-http-server) and checks
     21     that the DUT is serving the shared files on the network.
     22     """
     23     version = 1
     24 
     25     def setup(self):
     26         self.job.setup_dep(['lansim'])
     27 
     28 
     29     def initialize(self):
     30         dep = 'lansim'
     31         dep_dir = os.path.join(self.autodir, 'deps', dep)
     32         logging.info('lansim is at %s', dep_dir)
     33         self.job.install_pkg(dep, 'dep', dep_dir)
     34 
     35         # Import the lansim modules installed on lansim/build/
     36         sys.path.append(os.path.join(dep_dir, 'build'))
     37 
     38         self._p2p = p2p_utils.P2PServerOverTap()
     39         self._sim = None
     40 
     41 
     42     def cleanup(self):
     43         # Work around problem described in the chromium:364583 bug.
     44         time.sleep(1)
     45         self._join_simulator()
     46         self._p2p.cleanup()
     47 
     48 
     49     def _join_simulator(self):
     50         """Stops the simulator and logs any exception generated there."""
     51         if not self._sim:
     52             return
     53         self._sim.stop()
     54         self._sim.join()
     55         if self._sim.error:
     56             logging.error('SimulatorThread exception: %r', self._sim.error)
     57             logging.error(self._sim.traceback)
     58 
     59 
     60     def _dut_ready(self, p2pcli):
     61         # Lookup the DUT on the mDNS network.
     62         peers = p2pcli.get_peers()
     63         if not peers:
     64             return False
     65         peer_name, hostname, ips, port = peers[0]
     66         # Get the files shared by the DUT.
     67         files = p2pcli.get_peer_files(peer_name)
     68         if not files:
     69             return False
     70         return peer_name, hostname, ips, port, files
     71 
     72 
     73     def _p2p_fetch(self, host, port, filename):
     74         """Fetch a file from a p2p-http-server.
     75 
     76         @return: A str with the contents of the responde if the request
     77         succeeds or an integer value with the error code returned by curl
     78         otherwise.
     79         """
     80         fd, tempfn = tempfile.mkstemp(prefix='p2p-fetch')
     81         ret = utils.run(
     82                 'curl', args=['http://%s:%s/%s' % (host, port, filename)],
     83                 timeout=20., ignore_timeout=False, ignore_status=True,
     84                 stdout_tee=open(tempfn, 'w'), stderr_tee=sys.stdout)
     85         with os.fdopen(fd) as f:
     86             output = f.read()
     87         os.unlink(tempfn)
     88 
     89         if ret is None:
     90             return None
     91         if ret.exit_status != 0:
     92             return ret.exit_status
     93         return output
     94 
     95 
     96     def run_once(self):
     97         from lansim import simulator, host
     98 
     99         # Setup the environment where avahi-daemon runs during the test.
    100         try:
    101             self._p2p.setup(dumpdir=self.job.resultdir)
    102         except:
    103             logging.exception('Failed to start tested services.')
    104             raise
    105 
    106         # Share a file on the DUT.
    107         content = open('/dev/urandom').read(16*1024)
    108         with open(os.path.join(p2p_utils.P2P_SHARE_PATH, 'file.p2p'), 'w') as f:
    109             f.write(content)
    110 
    111         self._sim = simulator.SimulatorThread(self._p2p.tap)
    112         # Create a single fake peer that will be sending the multicast requests.
    113         peer = host.SimpleHost(self._sim, '94:EB:2C:00:00:61', '169.254.10.55')
    114 
    115         # Run a userspace implementation of avahi + p2p-client on the fake
    116         # host. This will use the P2P services exported by the DUT.
    117         zero = zeroconf.ZeroconfDaemon(peer, 'peer')
    118         p2pcli = cros_p2p.CrosP2PClient(zero)
    119 
    120         self._sim.start()
    121 
    122         # Force a request from the client before waiting for the DUT's response.
    123         self._sim.run_on_simulator(lambda: p2pcli.start_query())
    124 
    125         # Wait up to 30 seconds until the DUT is ready sharing the files.
    126         res = self._sim.wait_for_condition(lambda: self._dut_ready(p2pcli),
    127                                            timeout=30.)
    128         self._sim.run_on_simulator(lambda: p2pcli.stop_query())
    129 
    130         if not res:
    131             raise error.TestFail('The DUT failed to announce the shared files '
    132                                  'after 30 seconds.')
    133 
    134         # DUT's p2p-http-server is running on hostname:port.
    135         peer_name, hostname, ips, port, files = res
    136 
    137         if len(files) != 1 or files[0] != ('file', len(content)) or (
    138                 len(ips) != 1) or ips[0] != self._p2p.tap.addr:
    139             logging.error('peer_name = %r', peer_name)
    140             logging.error('hostname = %r', hostname)
    141             logging.error('ips = %r', ips)
    142             logging.error('port = %r', port)
    143             logging.error('files = %r', files)
    144             raise error.TestFail('The DUT announced an erroneous file.')
    145 
    146         # Check we can't download directly from localhost.
    147         for host_ip in (ips[0], '127.0.0.1'):
    148             ret = self._p2p_fetch(host_ip, port, 'file')
    149             if ret != 7: # curl's exit code 7 is "Failed to connect to host."
    150                 logging.error('curl returned: %s', repr(ret)[:100])
    151                 raise error.TestFail(
    152                         "The DUT didn't block a request from localhost using "
    153                         "the address %s." % host_ip)
    154 
    155         # Check we can download if the connection comes from a peer on the
    156         # network. To achieve this, we forward the tester's TCP traffic through
    157         # a fake host on lansim.
    158         self._sim.run_on_simulator(lambda: peer.tcp_forward(1234, ips[0], port))
    159 
    160         ret = self._p2p_fetch(peer.ip_addr, 1234, 'file')
    161         if ret != content:
    162             logging.error('curl returned: %s', repr(ret)[:100])
    163             raise error.TestFail(
    164                     "The DUT didn't serve the file request from %s " %
    165                     peer.id_addr)
    166