Home | History | Annotate | Download | only in n_faced_peerd
      1 # Copyright 2015 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 dbus
      7 import dbus.bus
      8 import dbus.service
      9 import logging
     10 import uuid
     11 
     12 
     13 from autotest_lib.client.cros import dbus_util
     14 from autotest_lib.client.cros.tendo import peerd_dbus_helper
     15 from autotest_lib.client.cros.tendo.n_faced_peerd import dbus_property_exposer
     16 from autotest_lib.client.cros.tendo.n_faced_peerd import peer
     17 from autotest_lib.client.cros.tendo.n_faced_peerd import service
     18 
     19 # A tuple of a bus name that sent us an ExposeService message, and an
     20 # object responsible for watching for the death of that bus name's
     21 # DBus connection.
     22 SenderWatch = collections.namedtuple('SenderWatch', ['sender', 'watch'])
     23 
     24 
     25 IGNORED_MONITORING_TOKEN_VALUE = 'This is a monitoring token.'
     26 class InvalidMonitoringTokenException(Exception):
     27     """Self explanatory."""
     28 
     29 
     30 class Manager(dbus_property_exposer.DBusPropertyExposer):
     31     """Represents an instance of org.chromium.peerd.Manager."""
     32 
     33     def __init__(self, bus, ip_address, on_service_modified, unique_name,
     34                  object_manager):
     35         """Construct an instance of Manager.
     36 
     37         @param bus: dbus.Bus object to export this object on.
     38         @param ip_address: string IP address (e.g. '127.0.01').
     39         @param on_service_modified: callback that takes a Manager instance and
     40                 a service ID.  We'll call this whenever we Expose/Remove a
     41                 service via the DBus API.
     42         @param unique_name: string DBus well known name to claim on DBus.
     43         @param object_manager: an instance of ObjectManager.
     44 
     45         """
     46         super(Manager, self).__init__(bus,
     47                                       peerd_dbus_helper.DBUS_PATH_MANAGER,
     48                                       peerd_dbus_helper.DBUS_INTERFACE_MANAGER)
     49         self._bus = bus
     50         self._object_manager = object_manager
     51         self._peer_counter = 0
     52         self._peers = dict()
     53         self._ip_address = ip_address
     54         self._on_service_modified = on_service_modified
     55         # A map from service_ids to dbus.bus.NameOwnerWatch objects.
     56         self._watches = dict()
     57         self.self_peer = peer.Peer(self._bus,
     58                                    peerd_dbus_helper.DBUS_PATH_SELF,
     59                                    uuid.uuid4(),
     60                                    self._object_manager,
     61                                    is_self=True)
     62         # TODO(wiley) Expose monitored technologies property
     63         self._object_manager.claim_interface(
     64                 peerd_dbus_helper.DBUS_PATH_MANAGER,
     65                 peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
     66                 self.property_getter)
     67         if (self._bus.request_name(unique_name) !=
     68                 dbus.bus.REQUEST_NAME_REPLY_PRIMARY_OWNER):
     69             raise Exception('Failed to claim name %s' % unique_name)
     70 
     71 
     72     def _on_name_owner_changed(self, service_id, owner):
     73         """Callback that removes a service when the owner disconnects from DBus.
     74 
     75         @param service_id: string service_id of service to remove.
     76         @param owner: dbus.String identifier of service owner.
     77 
     78         """
     79         owner = dbus_util.dbus2primitive(owner)
     80         logging.debug('Name owner for service=%s changed to "%s".',
     81                       service_id, owner)
     82         if not owner:
     83             self.RemoveExposedService(service_id)
     84 
     85 
     86     def close(self):
     87         """Release resources held by this object and child objects."""
     88         # TODO(wiley) call close on self and remote peers.
     89         raise NotImplementedError('Manager.close() does not work properly')
     90 
     91 
     92     def add_remote_peer(self, remote_peer):
     93         """Add a remote peer to this object.
     94 
     95         For any given face of NFacedPeerd, the other N - 1 faces are treated
     96         as "remote peers" that we instantly discover changes on.
     97 
     98         @param remote_peer: Peer object.  Should be the |self_peer| of another
     99                 instance of Manager.
    100 
    101         """
    102         logging.info('Adding remote peer %s', remote_peer.uuid)
    103         self._peer_counter += 1
    104         peer_path = '%s%d' % (peerd_dbus_helper.PEER_PATH_PREFIX,
    105                               self._peer_counter)
    106         self._peers[remote_peer.uuid] = peer.Peer(
    107                 self._bus, peer_path, remote_peer.uuid, self._object_manager)
    108 
    109 
    110     def on_remote_service_modified(self, remote_peer, service_id):
    111         """Cause this face to update its view of a remote peer.
    112 
    113         @param remote_peer: Peer object.  Should be the |self_peer| of another
    114                 instance of Manager.
    115         @param service_id: string service ID of remote service that has changed.
    116                 Note that this service may have been removed.
    117 
    118         """
    119         local_peer = self._peers[remote_peer.uuid]
    120         remote_service = remote_peer.services.get(service_id)
    121         if remote_service is not None:
    122             logging.info('Exposing remote service: %s', service_id)
    123             local_peer.update_service(remote_service.service_id,
    124                                       remote_service.service_info,
    125                                       remote_service.ip_info)
    126         else:
    127             logging.info('Removing remote service: %s', service_id)
    128             local_peer.remove_service(service_id)
    129 
    130 
    131     @dbus.service.method(
    132             dbus_interface=peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
    133             in_signature='sa{ss}a{sv}', out_signature='',
    134             sender_keyword='sender')
    135     def ExposeService(self, service_id, service_info, options, sender=None):
    136         """Implementation of org.chromium.peerd.Manager.ExposeService().
    137 
    138         @param service_id: see DBus API documentation.
    139         @param service_info: see DBus API documentation.
    140         @param options: see DBus API documentation.
    141         @param sender: string DBus connection ID of caller.  Must match
    142                 |sender_keyword| value in decorator.
    143 
    144         """
    145         if (service_id in self._watches and
    146                 sender != self._watches[service_id].sender):
    147             logging.error('Asked to advertise a duplicate service by %s. '
    148                           'Expected %s.',
    149                           sender, self._watches[service_id].sender)
    150             raise Exception('Will not advertise duplicate services.')
    151         logging.info('Exposing service %s', service_id)
    152         port = options.get('mdns', dict()).get('port', 0)
    153         self.self_peer.update_service(
    154                 service_id, service_info,
    155                 service.IpInfo(addr=self._ip_address, port=port))
    156         if service_id not in self._watches:
    157             cb = lambda owner: self._on_name_owner_changed(service_id, owner)
    158             watch = dbus.bus.NameOwnerWatch(self._bus, sender, cb)
    159             self._watches[service_id] = SenderWatch(sender, watch)
    160         self._on_service_modified(self, service_id)
    161 
    162 
    163     @dbus.service.method(
    164             dbus_interface=peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
    165             in_signature='s', out_signature='')
    166     def RemoveExposedService(self, service_id):
    167         """Implementation of org.chromium.peerd.Manager.RemoveExposedService().
    168 
    169         @param service_id: see DBus API documentation.
    170 
    171         """
    172         logging.info('Removing service %s', service_id)
    173         self._watches.pop(service_id, None)
    174         self.self_peer.remove_service(service_id)
    175         self._on_service_modified(self, service_id)
    176 
    177 
    178     @dbus.service.method(
    179             dbus_interface=peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
    180             in_signature='asa{sv}', out_signature='s')
    181     def StartMonitoring(self, technologies, options):
    182         """Fake implementation of org.chromium.peerd.Manager.StartMonitoring().
    183 
    184         We do not monitor anything in NFacedPeerd.
    185 
    186         @param technologies: See DBus API documentation.
    187         @param options: See DBus API documentation.
    188 
    189         """
    190         return IGNORED_MONITORING_TOKEN_VALUE
    191 
    192 
    193     @dbus.service.method(
    194             dbus_interface=peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
    195             in_signature='s', out_signature='')
    196     def StopMonitoring(self, monitoring_token):
    197         """Fake implementation of org.chromium.peerd.Manager.StopMonitoring().
    198 
    199         We do not monitor anything in NFacedPeerd
    200 
    201         @param monitoring_token: See DBus API documentation.
    202 
    203         """
    204         if monitoring_token != IGNORED_MONITORING_TOKEN_VALUE:
    205             raise InvalidMonitoringTokenException()
    206 
    207 
    208     @dbus.service.method(
    209             dbus_interface=peerd_dbus_helper.DBUS_INTERFACE_MANAGER,
    210             in_signature='', out_signature='s')
    211     def Ping(self):
    212         """Implementation of org.chromium.peerd.Manager.Ping()."""
    213         return 'Hello world!'
    214