1#!/usr/bin/python 2 3# Copyright 2015 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import argparse 8import dbus 9import dbus.mainloop.glib 10import gobject 11import logging 12import logging.handlers 13 14 15import common 16from autotest_lib.client.common_lib.cros.tendo import n_faced_peerd_helper 17from autotest_lib.client.cros.tendo import peerd_dbus_helper 18from autotest_lib.client.cros.tendo.n_faced_peerd import manager 19from autotest_lib.client.cros.tendo.n_faced_peerd import object_manager 20 21 22class NFacedPeerd(object): 23 """An object which looks like N different instances of peerd. 24 25 There are situations where we would like to run N instances 26 of a service on the same system (e.g. testing behavior of 27 N instances of leaderd). If that service has a dependency 28 on peerd (i.e. it advertises a service), then those N instances 29 will conflict on their shared dependency on peerd. 30 31 NFacedPeerd solves this by starting N instances of peerd running 32 inside the same process. Services exposed on a particular face 33 are advertised as remote services on the other faces. 34 35 """ 36 37 def __init__(self, num_instances, ip_address): 38 """Construct an instance. 39 40 @param num_instance: int number of "instances" of peerd to start. 41 @param ip_address: string IP address to use in service records for 42 all faces. This should usually be the address of the loopback 43 interface. 44 45 """ 46 self._instances = [] 47 # This is a class that wraps around a global singleton to provide 48 # dbus-python specific functionality. This design pattern fills 49 # me with quiet horror. 50 loop = dbus.mainloop.glib.DBusGMainLoop() 51 # Construct N fake instances of peerd 52 for i in range(num_instances): 53 bus = dbus.SystemBus(private=True, mainloop=loop) 54 unique_name = n_faced_peerd_helper.get_nth_service_name(i) 55 om = object_manager.ObjectManager( 56 bus, peerd_dbus_helper.DBUS_PATH_OBJECT_MANAGER) 57 self._instances.append(manager.Manager( 58 bus, ip_address, self._on_service_modified, unique_name, om)) 59 # Now tell them all about each other 60 for instance in self._instances: 61 for other_instance in self._instances: 62 # Don't tell anyone about themselves, that would be silly. 63 if instance == other_instance: 64 continue 65 instance.add_remote_peer(other_instance.self_peer) 66 67 68 def _on_service_modified(self, updated_manager, service_id): 69 """Called on a service being modified by a manager. 70 71 We use this callback to propagate services exposed to a particular 72 instance of peerd to all other instances of peerd as a remote 73 service. Note that |service_id| could have just been deleted, 74 in which case, the lookup for the service will fail. 75 76 @param updated_manager_index: integer index of manager modifying 77 the service. 78 @param service_id: string service ID of service being modified. 79 80 """ 81 logging.debug('Service %s modified on instance %r', 82 service_id, updated_manager) 83 updated_peer = updated_manager.self_peer 84 for other_manager in self._instances: 85 if other_manager == updated_manager: 86 continue 87 other_manager.on_remote_service_modified(updated_peer, service_id) 88 89 90 def run(self): 91 """Enter the mainloop and respond to DBus queries.""" 92 # Despite going by two different names, this is actually the same 93 # mainloop we referenced earlier. Yay! 94 loop = gobject.MainLoop() 95 loop.run() 96 97 98def main(): 99 """Entry point for this daemon.""" 100 formatter = logging.Formatter( 101 'n_faced_peerd: [%(levelname)s] %(message)s') 102 handler = logging.handlers.SysLogHandler(address='/dev/log') 103 handler.setFormatter(formatter) 104 logger = logging.getLogger() 105 logger.addHandler(handler) 106 logger.setLevel(logging.DEBUG) 107 logging.info('NFacedPeerd daemon starting.') 108 parser = argparse.ArgumentParser( 109 description='Acts like N instances of peerd.') 110 parser.add_argument('num_instances', metavar='N', type=int, 111 help='Number of fake instances to start.') 112 parser.add_argument( 113 'ip_address', metavar='ip_address', type=str, 114 help='IP address to claim for all instances (e.g. "127.0.0.1").') 115 args = parser.parse_args() 116 n_faces = NFacedPeerd(args.num_instances, args.ip_address) 117 n_faces.run() 118 logging.info('NFacedPeerd daemon mainloop has exitted.') 119 120 121if __name__ == '__main__': 122 main() 123