Example #1
0
 def setUp(self):
     yield super(UnitAgentTestBase, self).setUp()
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type("dummy")
     self.change_environment(
         PATH=get_cli_environ_path(),
         JUJU_UNIT_NAME="mysql/0")
Example #2
0
    def start(self, provider_type=None):
        """Starts the unit deployer."""
        # Find out what provided the machine, and how to deploy units.
        if provider_type is None:
            settings = GlobalSettingsStateManager(self.client)
            provider_type = yield settings.get_provider_type()
        self.deploy_factory = get_deploy_factory(provider_type)

        if not os.path.exists(self.charms_directory):
            os.makedirs(self.charms_directory)
Example #3
0
    def setUp(self):
        yield super(StatusTestBase, self).setUp()
        settings = GlobalSettingsStateManager(self.client)
        yield settings.set_provider_type("dummy")
        self.log = self.capture_logging()

        self.environment = self.config.get_default()
        self.provider = self.environment.get_machine_provider()
        self.machine_count = 0
        self.output = StringIO()
Example #4
0
def get_unit_address(client):
    settings = GlobalSettingsStateManager(client)
    provider_type = yield settings.get_provider_type()
    if provider_type == "ec2":
        returnValue(EC2UnitAddress())
    elif provider_type == "local":
        returnValue(LocalUnitAddress())
    elif provider_type == "orchestra":
        returnValue(OrchestraUnitAddress())
    elif provider_type == "dummy":
        returnValue(DummyUnitAddress())

    raise JujuError("Unknown provider type: %r, unit addresses unknown." %
                    provider_type)
Example #5
0
def get_unit_address(client):
    settings = GlobalSettingsStateManager(client)
    provider_type = yield settings.get_provider_type()
    if provider_type == "ec2":
        returnValue(EC2UnitAddress())
    elif provider_type == "local":
        returnValue(LocalUnitAddress())
    elif provider_type == "orchestra":
        returnValue(OrchestraUnitAddress())
    elif provider_type == "dummy":
        returnValue(DummyUnitAddress())

    raise JujuError(
        "Unknown provider type: %r, unit addresses unknown." % provider_type)
Example #6
0
    def setUp(self):
        yield super(StatusTestBase, self).setUp()
        settings = GlobalSettingsStateManager(self.client)
        yield settings.set_provider_type("dummy")
        self.log = self.capture_logging()

        config = {
            "environments": {
                "firstenv": {
                    "type": "dummy",
                    "admin-secret": "homer"}}}
        self.write_config(yaml.dump(config))
        self.config.load()

        self.environment = self.config.get_default()
        self.provider = self.environment.get_machine_provider()

        self.output = StringIO()
        self.agents = []
Example #7
0
    def test_initialize(self):
        yield self.layout.initialize()

        yield self.assert_existence_and_acl("/charms")
        yield self.assert_existence_and_acl("/services")
        yield self.assert_existence_and_acl("/units")
        yield self.assert_existence_and_acl("/machines")
        yield self.assert_existence_and_acl("/relations")
        yield self.assert_existence_and_acl("/initialized")

        # To check that the constraints landed correctly, we need the
        # environment config to have been sent, or we won't be able to
        # get a provider to help us construct the appropriate objects.
        yield self.push_default_config(with_constraints=False)

        esm = EnvironmentStateManager(self.client)
        env_constraints = yield esm.get_constraints()
        self.assertEquals(env_constraints, {
            "provider-type": "dummy",
            "ubuntu-series": None,
            "arch": "arm",
            "cpu": None,
            "mem": 512})

        machine_state_manager = MachineStateManager(self.client)
        machine_state = yield machine_state_manager.get_machine_state(0)
        machine_constraints = yield machine_state.get_constraints()
        self.assertTrue(machine_constraints.complete)
        self.assertEquals(machine_constraints, {
            "provider-type": "dummy",
            "ubuntu-series": "cranky",
            "arch": "arm",
            "cpu": None,
            "mem": 512})
        instance_id = yield machine_state.get_instance_id()
        self.assertEqual(instance_id, "i-abcdef")

        settings_manager = GlobalSettingsStateManager(self.client)
        self.assertEqual((yield settings_manager.get_provider_type()), "dummy")
        self.assertEqual(
            self.log.getvalue().strip(),
            "Initializing zookeeper hierarchy")
Example #8
0
    def test_initialize(self):
        yield self.layout.initialize()

        yield self.assert_existence_and_acl("/charms")
        yield self.assert_existence_and_acl("/services")
        yield self.assert_existence_and_acl("/units")
        yield self.assert_existence_and_acl("/machines")
        yield self.assert_existence_and_acl("/relations")
        yield self.assert_existence_and_acl("/initialized")

        machine_state_manager = MachineStateManager(self.client)
        machine_state = yield machine_state_manager.get_machine_state(0)
        self.assertTrue(machine_state)
        instance_id = yield machine_state.get_instance_id()
        self.assertEqual(instance_id, "i-abcdef")

        settings_manager = GlobalSettingsStateManager(self.client)
        self.assertEqual((yield settings_manager.get_provider_type()), "dummy")
        self.assertEqual(self.log.getvalue().strip(),
                         "Initializing zookeeper hierarchy")
Example #9
0
    def test_initialize(self):
        yield self.layout.initialize()

        yield self.assert_existence_and_acl("/charms")
        yield self.assert_existence_and_acl("/services")
        yield self.assert_existence_and_acl("/units")
        yield self.assert_existence_and_acl("/machines")
        yield self.assert_existence_and_acl("/relations")
        yield self.assert_existence_and_acl("/initialized")

        machine_state_manager = MachineStateManager(self.client)
        machine_state = yield machine_state_manager.get_machine_state(0)
        self.assertTrue(machine_state)
        instance_id = yield machine_state.get_instance_id()
        self.assertEqual(instance_id, "i-abcdef")

        settings_manager = GlobalSettingsStateManager(self.client)
        self.assertEqual((yield settings_manager.get_provider_type()), "dummy")
        self.assertEqual(
            self.log.getvalue().strip(),
            "Initializing zookeeper hierarchy")
Example #10
0
    def setUp(self):
        yield super(StatusTestBase, self).setUp()
        settings = GlobalSettingsStateManager(self.client)
        yield settings.set_provider_type("dummy")
        self.log = self.capture_logging()

        config = {
            "environments": {
                "firstenv": {
                    "type": "dummy",
                    "admin-secret": "homer"
                }
            }
        }
        self.write_config(yaml.dump(config))
        self.config.load()

        self.environment = self.config.get_default()
        self.provider = self.environment.get_machine_provider()

        self.output = StringIO()
        self.agents = []
Example #11
0
    def start(self):
        """Start the machine agent.

        Creates state directories on the machine, retrieves the machine state,
        and enables watch on assigned units.
        """
        # Initialize directory paths.
        if not os.path.exists(self.charms_directory):
            os.makedirs(self.charms_directory)

        if not os.path.exists(self.units_directory):
            os.makedirs(self.units_directory)

        if not os.path.exists(self.unit_state_directory):
            os.makedirs(self.unit_state_directory)

        # Get state managers we'll be utilizing.
        self.service_state_manager = ServiceStateManager(self.client)
        self.charm_state_manager = CharmStateManager(self.client)

        # Retrieve the machine state for the machine we represent.
        machine_manager = MachineStateManager(self.client)
        self.machine_state = yield machine_manager.get_machine_state(
            self.get_machine_id())

        # Watch assigned units for the machine.
        if self.get_watch_enabled():
            self.machine_state.watch_assigned_units(self.watch_service_units)

        # Find out what provided the machine, and how to deploy units.
        settings = GlobalSettingsStateManager(self.client)
        self.provider_type = yield settings.get_provider_type()
        self.deploy_factory = get_deploy_factory(self.provider_type)

        # Connect the machine agent, broadcasting presence to the world.
        yield self.machine_state.connect_agent()
        log.info(
            "Machine agent started id:%s deploy:%r provider:%r" %
            (self.get_machine_id(), self.deploy_factory, self.provider_type))
Example #12
0
    def start(self):
        """Start the machine agent.

        Creates state directories on the machine, retrieves the machine state,
        and enables watch on assigned units.
        """
        # Initialize directory paths.
        if not os.path.exists(self.charms_directory):
            os.makedirs(self.charms_directory)

        if not os.path.exists(self.units_directory):
            os.makedirs(self.units_directory)

        if not os.path.exists(self.unit_state_directory):
            os.makedirs(self.unit_state_directory)

        # Get state managers we'll be utilizing.
        self.service_state_manager = ServiceStateManager(self.client)
        self.charm_state_manager = CharmStateManager(self.client)

        # Retrieve the machine state for the machine we represent.
        machine_manager = MachineStateManager(self.client)
        self.machine_state = yield machine_manager.get_machine_state(
            self.get_machine_id())

        # Watch assigned units for the machine.
        if self.get_watch_enabled():
            self.machine_state.watch_assigned_units(
                self.watch_service_units)

        # Find out what provided the machine, and how to deploy units.
        settings = GlobalSettingsStateManager(self.client)
        self.provider_type = yield settings.get_provider_type()
        self.deploy_factory = get_deploy_factory(self.provider_type)

        # Connect the machine agent, broadcasting presence to the world.
        yield self.machine_state.connect_agent()
        log.info("Machine agent started id:%s deploy:%r provider:%r" % (
            self.get_machine_id(), self.deploy_factory, self.provider_type))
Example #13
0
 def setUp(self):
     yield super(GlobalSettingsTest, self).setUp()
     self.manager = GlobalSettingsStateManager(self.client)
Example #14
0
class GlobalSettingsTest(StateTestBase):

    @inlineCallbacks
    def setUp(self):
        yield super(GlobalSettingsTest, self).setUp()
        self.manager = GlobalSettingsStateManager(self.client)

    @inlineCallbacks
    def test_get_set_provider_type(self):
        """Debug logging is off by default."""
        self.assertEqual((yield self.manager.get_provider_type()), None)
        yield self.manager.set_provider_type("ec2")
        self.assertEqual((yield self.manager.get_provider_type()), "ec2")
        content, stat = yield self.client.get("/settings")
        self.assertEqual(yaml.load(content),
                         {"provider-type": "ec2"})

    @inlineCallbacks
    def test_get_debug_log_enabled_no_settings_default(self):
        """Debug logging is off by default."""
        value = yield self.manager.is_debug_log_enabled()
        self.assertFalse(value)

    @inlineCallbacks
    def test_set_debug_log(self):
        """Debug logging can be (dis)enabled via the runtime manager."""
        yield self.manager.set_debug_log(True)
        value = yield self.manager.is_debug_log_enabled()
        self.assertTrue(value)
        yield self.manager.set_debug_log(False)
        value = yield self.manager.is_debug_log_enabled()
        self.assertFalse(value)

    @inlineCallbacks
    def test_watcher(self):
        """Use the watch facility of the settings manager to observer changes.
        """
        results = []
        callbacks = [Deferred() for i in range(5)]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)

        yield self.manager.set_debug_log(True)
        yield self.manager.watch_settings_changes(watch)
        self.assertTrue(results)

        yield self.manager.set_debug_log(False)
        yield self.manager.set_debug_log(True)
        yield callbacks[2]
        self.assertEqual(len(results), 3)

        self.assertEqual(
            map(lambda x: isinstance(x, bool) and x or x.type_name, results),
            [True, "changed", "changed"])
        data, stat = yield self.client.get(SETTINGS_PATH)
        self.assertEqual(
            (yield self.manager.is_debug_log_enabled()),
            True)

    @inlineCallbacks
    def test_watcher_start_stop(self):
        """Setings watcher observes changes till stopped.

        Additionally watching can be enabled on a setting node that doesn't
        exist yet.

        XXX For reasons unknown this fails under coverage outside of the test,
        at least for me (k.), but not for others.
        """
        results = []
        callbacks = [Deferred() for i in range(5)]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)

        watcher = yield self.manager.watch_settings_changes(watch)
        yield self.client.create(SETTINGS_PATH, "x")
        value = yield callbacks[0]
        self.assertEqual(value.type_name, "created")

        data = dict(x=1, y=2, z=3, moose=u"moon")
        yield self.client.set(
            SETTINGS_PATH, yaml.safe_dump(data))
        value = yield callbacks[1]
        self.assertEqual(value.type_name, "changed")

        watcher.stop()

        yield self.client.set(SETTINGS_PATH, "z")
        # Give a chance for things to go bad.
        yield self.sleep(0.1)
        self.assertFalse(callbacks[2].called)

    @inlineCallbacks
    def test_watcher_stops_on_callback_exception(self):
        """If a callback has an exception the watcher is stopped."""
        results = []
        callbacks = [Deferred(), Deferred()]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)
            raise AttributeError("foobar")

        def on_error(error):
            results.append(True)

        yield self.client.create(SETTINGS_PATH, "z")
        watcher = yield self.manager.watch_settings_changes(
            watch, on_error)
        yield callbacks[0]

        # The callback error should have disconnected the system.
        yield self.client.set(SETTINGS_PATH, "x")

        # Give a chance for things to go bad.
        yield self.sleep(0.1)

        # Verify nothing did go bad.
        self.assertFalse(watcher.is_running)
        self.assertFalse(callbacks[1].called)
        self.assertIdentical(results[1], True)
Example #15
0
 def start_global_settings_watch(self):
     """Start watching the runtime state for configuration changes."""
     self.global_settings_state = GlobalSettingsStateManager(self.client)
     return self.global_settings_state.watch_settings_changes(
         self.on_global_settings_change)
Example #16
0
class BaseAgent(object, service.Service):

    name = "juju-agent-unknown"
    client = None

    # Flag when enabling persistent topology watches, testing aid.
    _watch_enabled = True

    # Distributed debug log handler
    _debug_log_handler = None

    @classmethod
    def run(cls):
        """Runs the agent as a unix daemon.

        Main entry point for starting an agent, parses cli options, and setups
        a daemon using twistd as per options.
        """
        parser = argparse.ArgumentParser()
        cls.setup_options(parser)
        config = parser.parse_args(namespace=TwistedOptionNamespace())
        runner = AgentRunner(config)
        agent = cls()
        agent.configure(config)
        runner.application = agent.as_app()
        runner.run()

    @classmethod
    def setup_options(cls, parser):
        """Configure the argparse cli parser for the agent."""
        return cls.setup_default_options(parser)

    @classmethod
    def setup_default_options(cls, parser):
        """Setup default twistd daemon and agent options.

        This method is intended as a utility for subclasses.

        @param parser an argparse instance.
        @type C{argparse.ArgumentParser}
        """
        setup_twistd_options(parser, cls)
        setup_default_agent_options(parser, cls)

    def as_app(self):
        """
        Return the agent as a C{twisted.application.service.Application}
        """
        app = service.Application(self.name)
        self.setServiceParent(app)
        return app

    def configure(self, options):
        """Configure the agent to handle its cli options.

        Invoked called before the service is started.

        @param options
        @type C{TwistedOptionNamespace} an argparse namespace corresponding
              to a dict.
        """
        if not options.get("zookeeper_servers"):
            raise NoConnection("No zookeeper connection configured.")

        if not os.path.exists(options.get("juju_directory", "")):
            raise JujuError(
                "Invalid juju-directory %r, does not exist." % (
                    options.get("juju_directory")))

        if options["session_file"] is None:
            raise JujuError("No session file specified")

        self.config = options

    @inlineCallbacks
    def _kill_existing_session(self):
        try:
            # We might have died suddenly, in which case the session may
            # still be alive. If this is the case, shoot it in the head, so
            # it doesn't interfere with our attempts to recreate our state.
            # (We need to be able to recreate our state *anyway*, and it's
            # much simpler to force ourselves to recreate it every time than
            # it is to mess around partially recreating partial state.)
            client_id = load_client_id(self.config["session_file"])
            if client_id is None:
                return
            temp_client = yield ZookeeperClient().connect(
                self.config["zookeeper_servers"], client_id=client_id)
            yield temp_client.close()
        except zookeeper.ZooKeeperException:
            # We don't really care what went wrong; just that we're not able
            # to connect using the old session, and therefore we should be ok
            # to start a fresh one without transient state hanging around.
            pass

    @inlineCallbacks
    def connect(self):
        """Return an authenticated connection to the juju zookeeper."""
        yield self._kill_existing_session()
        self.client = yield ManagedClient().connect(
            self.config["zookeeper_servers"])
        save_client_id(
            self.config["session_file"], self.client.client_id)

        principals = self.config.get("principals", ())
        for principal in principals:
            self.client.add_auth("digest", principal)

        # bug work around to keep auth fast
        if principals:
            yield self.client.exists("/")

        returnValue(self.client)

    def start(self):
        """Callback invoked on the agent's startup.

        The agent will already be connected to zookeeper. Subclasses are
        responsible for implementing.
        """
        raise NotImplementedError

    def stop(self):
        """Callback invoked on when the agent is shutting down."""
        pass

    # Twisted IService implementation, used for delegates to maintain naming
    # conventions.
    @inlineCallbacks
    def startService(self):
        yield self.connect()

        # Start the global settings watch prior to starting the agent.
        # Allows for debug log to be enabled early.
        if self.get_watch_enabled():
            yield self.start_global_settings_watch()

        yield self.start()

    @inlineCallbacks
    def stopService(self):
        try:
            yield self.stop()
        finally:
            if self.client and self.client.connected:
                self.client.close()
            session_file = self.config["session_file"]
            if os.path.exists(session_file):
                os.unlink(session_file)

    def set_watch_enabled(self, flag):
        """Set boolean flag for whether this agent should watching zookeeper.

        This is mainly used for testing, to allow for setting up the
        various data scenarios, before enabling an agent watch which will
        be observing state.
        """
        self._watch_enabled = bool(flag)

    def get_watch_enabled(self):
        """Returns a boolean if the agent should be settings state watches.

        The meaning of this flag is typically agent specific, as each
        agent has separate watches they'd like to establish on agent specific
        state within zookeeper. In general if this flag is False, the agent
        should refrain from establishing a watch on startup. This flag is
        typically used by tests to isolate and test the watch behavior
        independent of the agent startup, via construction of test data.
        """
        return self._watch_enabled

    def start_global_settings_watch(self):
        """Start watching the runtime state for configuration changes."""
        self.global_settings_state = GlobalSettingsStateManager(self.client)
        return self.global_settings_state.watch_settings_changes(
            self.on_global_settings_change)

    @inlineCallbacks
    def on_global_settings_change(self, change):
        """On global settings change, take action.
        """
        if (yield self.global_settings_state.is_debug_log_enabled()):
            yield self.start_debug_log()
        else:
            self.stop_debug_log()

    @inlineCallbacks
    def start_debug_log(self):
        """Enable the distributed debug log handler.
        """
        if self._debug_log_handler is not None:
            returnValue(None)
        context_name = self.get_agent_name()
        self._debug_log_handler = ZookeeperHandler(
            self.client, context_name)
        yield self._debug_log_handler.open()
        log_root = logging.getLogger()
        log_root.addHandler(self._debug_log_handler)

    def stop_debug_log(self):
        """Disable any configured debug log handler.
        """
        if self._debug_log_handler is None:
            return
        handler, self._debug_log_handler = self._debug_log_handler, None
        log_root = logging.getLogger()
        log_root.removeHandler(handler)

    def get_agent_name(self):
        """Return the agent's name and context such that it can be identified.

        Subclasses should override this to provide additional context and
        unique naming.
        """
        return self.__class__.__name__
Example #17
0
 def start_global_settings_watch(self):
     """Start watching the runtime state for configuration changes."""
     self.global_settings_state = GlobalSettingsStateManager(self.client)
     return self.global_settings_state.watch_settings_changes(
         self.on_global_settings_change)
Example #18
0
 def setUp(self):
     yield super(UnitAgentTestBase, self).setUp()
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type("dummy")
     self.makeDir(path=os.path.join(self.juju_directory, "charms"))
Example #19
0
 def get_address_for(self, provider_type):
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type(provider_type)
     address = yield get_unit_address(self.client)
     returnValue(address)
Example #20
0
 def setUp(self):
     yield super(UnitAgentTestBase, self).setUp()
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type("dummy")
     self.change_environment(PATH=get_cli_environ_path(),
                             JUJU_UNIT_NAME="mysql/0")
Example #21
0
class BaseAgent(object, service.Service):

    name = "juju-agent-unknown"
    client = None

    # Flag when enabling persistent topology watches, testing aid.
    _watch_enabled = True

    # Distributed debug log handler
    _debug_log_handler = None

    @classmethod
    def run(cls):
        """Runs the agent as a unix daemon.

        Main entry point for starting an agent, parses cli options, and setups
        a daemon using twistd as per options.
        """
        parser = argparse.ArgumentParser()
        cls.setup_options(parser)
        config = parser.parse_args(namespace=TwistedOptionNamespace())
        runner = AgentRunner(config)
        agent = cls()
        agent.configure(config)
        runner.application = agent.as_app()
        runner.run()

    @classmethod
    def setup_options(cls, parser):
        """Configure the argparse cli parser for the agent."""
        return cls.setup_default_options(parser)

    @classmethod
    def setup_default_options(cls, parser):
        """Setup default twistd daemon and agent options.

        This method is intended as a utility for subclasses.

        @param parser an argparse instance.
        @type C{argparse.ArgumentParser}
        """
        setup_twistd_options(parser, cls)
        setup_default_agent_options(parser, cls)

    def as_app(self):
        """
        Return the agent as a C{twisted.application.service.Application}
        """
        app = service.Application(self.name)
        self.setServiceParent(app)
        return app

    def configure(self, options):
        """Configure the agent to handle its cli options.

        Invoked called before the service is started.

        @param options
        @type C{TwistedOptionNamespace} an argparse namespace corresponding
              to a dict.
        """
        if not options.get("zookeeper_servers"):
            raise NoConnection("No zookeeper connection configured.")

        if not os.path.exists(options.get("juju_directory", "")):
            raise JujuError("Invalid juju-directory %r, does not exist." %
                            (options.get("juju_directory")))

        self.config = options

    @inlineCallbacks
    def connect(self):
        """Return an authenticated connection to the juju zookeeper."""
        hosts = self.config["zookeeper_servers"]
        self.client = yield ZookeeperClient().connect(hosts)

        principals = self.config.get("principals", ())
        for principal in principals:
            self.client.add_auth("digest", principal)

        # bug work around to keep auth fast
        if principals:
            yield self.client.exists("/")

        returnValue(self.client)

    def start(self):
        """Callback invoked on the agent's startup.

        The agent will already be connected to zookeeper. Subclasses are
        responsible for implementing.
        """
        raise NotImplementedError

    def stop(self):
        """Callback invoked on when the agent is shutting down."""
        pass

    # Twisted IService implementation, used for delegates to maintain naming
    # conventions.
    @inlineCallbacks
    def startService(self):
        yield self.connect()
        yield self.start()

        if self.get_watch_enabled():
            yield self.start_global_settings_watch()

    @inlineCallbacks
    def stopService(self):
        try:
            yield self.stop()
        finally:
            if self.client and self.client.connected:
                self.client.close()

    def set_watch_enabled(self, flag):
        """Set boolean flag for whether this agent should watching zookeeper.

        This is mainly used for testing, to allow for setting up the
        various data scenarios, before enabling an agent watch which will
        be observing state.
        """
        self._watch_enabled = bool(flag)

    def get_watch_enabled(self):
        """Returns a boolean if the agent should be settings state watches.

        The meaning of this flag is typically agent specific, as each
        agent has separate watches they'd like to establish on agent specific
        state within zookeeper. In general if this flag is False, the agent
        should refrain from establishing a watch on startup. This flag is
        typically used by tests to isolate and test the watch behavior
        independent of the agent startup, via construction of test data.
        """
        return self._watch_enabled

    def start_global_settings_watch(self):
        """Start watching the runtime state for configuration changes."""
        self.global_settings_state = GlobalSettingsStateManager(self.client)
        return self.global_settings_state.watch_settings_changes(
            self.on_global_settings_change)

    @inlineCallbacks
    def on_global_settings_change(self, change):
        """On global settings change, take action.
        """
        if (yield self.global_settings_state.is_debug_log_enabled()):
            yield self.start_debug_log()
        else:
            self.stop_debug_log()

    @inlineCallbacks
    def start_debug_log(self):
        """Enable the distributed debug log handler.
        """
        if self._debug_log_handler is not None:
            returnValue(None)
        context_name = self.get_agent_name()
        self._debug_log_handler = ZookeeperHandler(self.client, context_name)
        yield self._debug_log_handler.open()
        log_root = logging.getLogger()
        log_root.addHandler(self._debug_log_handler)

    def stop_debug_log(self):
        """Disable any configured debug log handler.
        """
        if self._debug_log_handler is None:
            return
        handler, self._debug_log_handler = self._debug_log_handler, None
        log_root = logging.getLogger()
        log_root.removeHandler(handler)

    def get_agent_name(self):
        """Return the agent's name and context such that it can be identified.

        Subclasses should override this to provide additional context and
        unique naming.
        """
        return self.__class__.__name__
Example #22
0
class GlobalSettingsTest(StateTestBase):
    @inlineCallbacks
    def setUp(self):
        yield super(GlobalSettingsTest, self).setUp()
        self.manager = GlobalSettingsStateManager(self.client)

    @inlineCallbacks
    def test_get_set_provider_type(self):
        """Debug logging is off by default."""
        self.assertEqual((yield self.manager.get_provider_type()), None)
        yield self.manager.set_provider_type("ec2")
        self.assertEqual((yield self.manager.get_provider_type()), "ec2")
        content, stat = yield self.client.get("/settings")
        self.assertEqual(yaml.load(content), {"provider-type": "ec2"})

    @inlineCallbacks
    def test_get_debug_log_enabled_no_settings_default(self):
        """Debug logging is off by default."""
        value = yield self.manager.is_debug_log_enabled()
        self.assertFalse(value)

    @inlineCallbacks
    def test_set_debug_log(self):
        """Debug logging can be (dis)enabled via the runtime manager."""
        yield self.manager.set_debug_log(True)
        value = yield self.manager.is_debug_log_enabled()
        self.assertTrue(value)
        yield self.manager.set_debug_log(False)
        value = yield self.manager.is_debug_log_enabled()
        self.assertFalse(value)

    @inlineCallbacks
    def test_watcher(self):
        """Use the watch facility of the settings manager to observer changes.
        """
        results = []
        callbacks = [Deferred() for i in range(5)]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)

        yield self.manager.set_debug_log(True)
        yield self.manager.watch_settings_changes(watch)
        self.assertTrue(results)

        yield self.manager.set_debug_log(False)
        yield self.manager.set_debug_log(True)
        yield callbacks[2]
        self.assertEqual(len(results), 3)

        self.assertEqual(
            map(lambda x: isinstance(x, bool) and x or x.type_name, results),
            [True, "changed", "changed"])
        data, stat = yield self.client.get(SETTINGS_PATH)
        self.assertEqual((yield self.manager.is_debug_log_enabled()), True)

    @inlineCallbacks
    def test_watcher_start_stop(self):
        """Setings watcher observes changes till stopped.

        Additionally watching can be enabled on a setting node that doesn't
        exist yet.

        XXX For reasons unknown this fails under coverage outside of the test,
        at least for me (k.), but not for others.
        """
        results = []
        callbacks = [Deferred() for i in range(5)]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)

        watcher = yield self.manager.watch_settings_changes(watch)
        yield self.client.create(SETTINGS_PATH, "x")
        value = yield callbacks[0]
        self.assertEqual(value.type_name, "created")

        data = dict(x=1, y=2, z=3, moose=u"moon")
        yield self.client.set(SETTINGS_PATH, yaml.safe_dump(data))
        value = yield callbacks[1]
        self.assertEqual(value.type_name, "changed")

        watcher.stop()

        yield self.client.set(SETTINGS_PATH, "z")
        # Give a chance for things to go bad.
        yield self.sleep(0.1)
        self.assertFalse(callbacks[2].called)

    @inlineCallbacks
    def test_watcher_stops_on_callback_exception(self):
        """If a callback has an exception the watcher is stopped."""
        results = []
        callbacks = [Deferred(), Deferred()]

        def watch(content):
            results.append(content)
            callbacks[len(results) - 1].callback(content)
            raise AttributeError("foobar")

        def on_error(error):
            results.append(True)

        yield self.client.create(SETTINGS_PATH, "z")
        watcher = yield self.manager.watch_settings_changes(watch, on_error)
        yield callbacks[0]

        # The callback error should have disconnected the system.
        yield self.client.set(SETTINGS_PATH, "x")

        # Give a chance for things to go bad.
        yield self.sleep(0.1)

        # Verify nothing did go bad.
        self.assertFalse(watcher.is_running)
        self.assertFalse(callbacks[1].called)
        self.assertIdentical(results[1], True)
Example #23
0
def debug_log(config, environment, log, options):
    provider = environment.get_machine_provider()
    client = yield provider.connect()

    log.info("Enabling distributed debug log.")

    settings_manager = GlobalSettingsStateManager(client)
    yield settings_manager.set_debug_log(True)

    if not options.limit:
        log.info("Tailing logs - Ctrl-C to stop.")

    iterator = LogIterator(client, replay=options.replay)

    # Setup the logging output with the user specified file.
    if options.output == "-":
        log_file = sys.stdout
    else:
        log_file = open(options.output, "a")
    handler = logging.StreamHandler(log_file)

    log_level = logging.getLevelName(options.level)
    handler.setLevel(log_level)

    formatter = logging.Formatter(
        "%(asctime)s %(context)s: %(name)s %(levelname)s: %(message)s")
    handler.setFormatter(formatter)

    def match(data):
        local_name = data["context"].split(":")[-1]

        if options.exclude:
            for exclude in options.exclude:
                if fnmatch(local_name, exclude) or \
                   fnmatch(data["context"], exclude) or \
                   fnmatch(data["name"], exclude):
                    return False

        if options.include:
            for include in options.include:
                if fnmatch(local_name, include) or \
                   fnmatch(data["context"], include) or \
                   fnmatch(data["name"], include):
                    return True
            return False

        return True

    count = 0
    try:
        while True:
            entry = yield iterator.next()
            if not match(entry):
                continue
            # json doesn't distinguish lists v. tuples but python string
            # formatting doesn't accept lists.
            entry["args"] = tuple(entry["args"])
            record = logging.makeLogRecord(entry)
            if entry["levelno"] < handler.level:
                continue
            handler.handle(record)
            count += 1
            if options.limit is not None and count == options.limit:
                break
    finally:
        yield settings_manager.set_debug_log(False)
        client.close()
Example #24
0
 def setUp(self):
     yield super(GlobalSettingsTest, self).setUp()
     self.manager = GlobalSettingsStateManager(self.client)
Example #25
0
 def get_address_for(self, provider_type):
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type(provider_type)
     address = yield get_unit_address(self.client)
     returnValue(address)
Example #26
0
 def setUp(self):
     yield super(UnitAgentTestBase, self).setUp()
     settings = GlobalSettingsStateManager(self.client)
     yield settings.set_provider_type("dummy")
     self.makeDir(path=os.path.join(self.juju_directory, "charms"))