• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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