blob: 22b4aae4a71858d06bc04ca8255236cf695e1110 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import dbus
import dbus.mainloop.glib
import gobject
import logging
import logging.handlers
import common
from autotest_lib.client.common_lib.cros.tendo import n_faced_peerd_helper
from autotest_lib.client.cros.tendo import peerd_dbus_helper
from autotest_lib.client.cros.tendo.n_faced_peerd import manager
from autotest_lib.client.cros.tendo.n_faced_peerd import object_manager
class NFacedPeerd(object):
"""An object which looks like N different instances of peerd.
There are situations where we would like to run N instances
of a service on the same system (e.g. testing behavior of
N instances of leaderd). If that service has a dependency
on peerd (i.e. it advertises a service), then those N instances
will conflict on their shared dependency on peerd.
NFacedPeerd solves this by starting N instances of peerd running
inside the same process. Services exposed on a particular face
are advertised as remote services on the other faces.
def __init__(self, num_instances, ip_address):
"""Construct an instance.
@param num_instance: int number of "instances" of peerd to start.
@param ip_address: string IP address to use in service records for
all faces. This should usually be the address of the loopback
self._instances = []
# This is a class that wraps around a global singleton to provide
# dbus-python specific functionality. This design pattern fills
# me with quiet horror.
loop = dbus.mainloop.glib.DBusGMainLoop()
# Construct N fake instances of peerd
for i in range(num_instances):
bus = dbus.SystemBus(private=True, mainloop=loop)
unique_name = n_faced_peerd_helper.get_nth_service_name(i)
om = object_manager.ObjectManager(
bus, peerd_dbus_helper.DBUS_PATH_OBJECT_MANAGER)
bus, ip_address, self._on_service_modified, unique_name, om))
# Now tell them all about each other
for instance in self._instances:
for other_instance in self._instances:
# Don't tell anyone about themselves, that would be silly.
if instance == other_instance:
def _on_service_modified(self, updated_manager, service_id):
"""Called on a service being modified by a manager.
We use this callback to propagate services exposed to a particular
instance of peerd to all other instances of peerd as a remote
service. Note that |service_id| could have just been deleted,
in which case, the lookup for the service will fail.
@param updated_manager_index: integer index of manager modifying
the service.
@param service_id: string service ID of service being modified.
logging.debug('Service %s modified on instance %r',
service_id, updated_manager)
updated_peer = updated_manager.self_peer
for other_manager in self._instances:
if other_manager == updated_manager:
other_manager.on_remote_service_modified(updated_peer, service_id)
def run(self):
"""Enter the mainloop and respond to DBus queries."""
# Despite going by two different names, this is actually the same
# mainloop we referenced earlier. Yay!
loop = gobject.MainLoop()
def main():
"""Entry point for this daemon."""
formatter = logging.Formatter(
'n_faced_peerd: [%(levelname)s] %(message)s')
handler = logging.handlers.SysLogHandler(address='/dev/log')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)'NFacedPeerd daemon starting.')
parser = argparse.ArgumentParser(
description='Acts like N instances of peerd.')
parser.add_argument('num_instances', metavar='N', type=int,
help='Number of fake instances to start.')
'ip_address', metavar='ip_address', type=str,
help='IP address to claim for all instances (e.g. "").')
args = parser.parse_args()
n_faces = NFacedPeerd(args.num_instances, args.ip_address)'NFacedPeerd daemon mainloop has exitted.')
if __name__ == '__main__':