Home | History | Annotate | Download | only in peerd_DiscoverServices
      1 # Copyright 2014 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 collections
      6 import logging
      7 
      8 from autotest_lib.client.bin import test
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.client.common_lib.cros.tendo import peerd_config
     11 from autotest_lib.client.cros import chrooted_avahi
     12 from autotest_lib.client.cros.netprotos import interface_host
     13 from autotest_lib.client.cros.netprotos import zeroconf
     14 from autotest_lib.client.cros.tendo import peerd_dbus_helper
     15 
     16 
     17 class peerd_DiscoverServices(test.test):
     18     """Test that peerd can correctly discover services over mDNS."""
     19     version = 1
     20 
     21     FakeService = collections.namedtuple('FakeService',
     22                                          'service_id service_info port')
     23     FAKE_HOST_HOSTNAME = 'test-host'
     24     TEST_TIMEOUT_SECONDS = 30
     25     PEER_ID = '123e4567-e89b-12d3-a456-426655440000'
     26     PEER_SERBUS_VERSION = '1.12'
     27     PEER_SERVICES = [FakeService('test-service-0',
     28                                  {'some_data': 'a value',
     29                                   'other_data': 'another value',
     30                                  },
     31                                  8080),
     32                      FakeService('test-service-1',
     33                                  {'again': 'so much data',
     34                                  },
     35                                  8081),
     36                     ]
     37     SERBUS_SERVICE_NAME = '_serbus'
     38     SERBUS_PROTOCOL = '_tcp'
     39     SERBUS_PORT = 0
     40     SERBUS_TXT_DICT = {'ver': PEER_SERBUS_VERSION,
     41                        'id': PEER_ID,
     42                        'services': '.'.join([service.service_id
     43                                              for service in PEER_SERVICES])
     44                       }
     45     UNIQUE_PREFIX = 'a_unique_mdns_prefix'
     46 
     47 
     48 
     49     def initialize(self):
     50         # Make sure these are initiallized to None in case we throw
     51         # during self.initialize().
     52         self._chrooted_avahi = None
     53         self._peerd = None
     54         self._host = None
     55         self._zc_listener = None
     56         self._chrooted_avahi = chrooted_avahi.ChrootedAvahi()
     57         self._chrooted_avahi.start()
     58         # Start up a fresh copy of peerd with really verbose logging.
     59         self._peerd = peerd_dbus_helper.make_helper(
     60                 peerd_config.PeerdConfig(verbosity_level=3))
     61         # Listen on our half of the interface pair for mDNS advertisements.
     62         self._host = interface_host.InterfaceHost(
     63                 self._chrooted_avahi.unchrooted_interface_name)
     64         self._zc_listener = zeroconf.ZeroconfDaemon(self._host,
     65                                                     self.FAKE_HOST_HOSTNAME)
     66         # The queries for hostname/dns_domain are IPCs and therefore relatively
     67         # expensive.  Do them just once.
     68         hostname = self._chrooted_avahi.hostname
     69         dns_domain = self._chrooted_avahi.dns_domain
     70         if not hostname or not dns_domain:
     71             raise error.TestFail('Failed to get hostname/domain from avahi.')
     72         self._dns_domain = dns_domain
     73         self._hostname = '%s.%s' % (hostname, dns_domain)
     74 
     75 
     76     def cleanup(self):
     77         for obj in (self._chrooted_avahi,
     78                     self._host,
     79                     self._peerd):
     80             if obj is not None:
     81                 obj.close()
     82 
     83 
     84     def _has_expected_peer(self):
     85         peer = self._peerd.has_peer(self.PEER_ID)
     86         if peer is None:
     87             logging.debug('No peer found.')
     88             return False
     89         logging.debug('Found peer=%s', peer)
     90         if len(peer.services) != len(self.PEER_SERVICES):
     91             logging.debug('Found %d services, but expected %d.',
     92                           len(peer.services), len(self.PEER_SERVICES))
     93             return False
     94         for service_id, info, port in self.PEER_SERVICES:
     95             service = None
     96             for s in peer.services:
     97                 if s.service_id == service_id:
     98                     service = s
     99                     break
    100             else:
    101                 logging.debug('No service %s found.', service_id)
    102                 return False
    103             if service.service_info != info:
    104                 logging.debug('Invalid info found for service %s, '
    105                               'expected %r but got %r.', service_id,
    106                               info, service.service_info)
    107                 return False
    108             if len(service.service_ips) != 1:
    109                 logging.debug('Missing service IP for service %s.',
    110                               service_id)
    111                 return False
    112             # We're publishing records from a "peer" outside the chroot.
    113             expected_addr = (self._chrooted_avahi.MONITOR_IF_IP.addr, port)
    114             if service.service_ips[0] != expected_addr:
    115                 logging.debug('Expected service IP for service %s=%r '
    116                               'but got %r.',
    117                               service_id, expected_addr, service.service_ips[0])
    118                 return False
    119         return True
    120 
    121 
    122     def run_once(self):
    123         # Expose serbus mDNS records through our fake peer.
    124         self._zc_listener.register_service(
    125                 self.UNIQUE_PREFIX,
    126                 self.SERBUS_SERVICE_NAME,
    127                 self.SERBUS_PROTOCOL,
    128                 self.SERBUS_PORT,
    129                 ['='.join(pair) for pair in self.SERBUS_TXT_DICT.iteritems()])
    130         for service_id, info, port in self.PEER_SERVICES:
    131             self._zc_listener.register_service(
    132                     self.UNIQUE_PREFIX,
    133                     '_' + service_id,
    134                     self.SERBUS_PROTOCOL,
    135                     port,
    136                     ['='.join(pair) for pair in info.iteritems()])
    137 
    138             # Look for mDNS records through peerd
    139         self._peerd.start_monitoring([peerd_dbus_helper.TECHNOLOGY_MDNS])
    140         # Wait for advertisements of that service to appear from avahi.
    141         logging.info('Waiting for peerd to discover our services.')
    142         success, duration = self._host.run_until(self._has_expected_peer,
    143                                                  self.TEST_TIMEOUT_SECONDS)
    144         logging.debug('Took %f seconds to find our peer.', duration)
    145         if not success:
    146             raise error.TestFail('Peerd failed to publish suitable DBus '
    147                                  'proxies in time.')
    148