示例#1
0
 def register(self, client):
     super(KeystoneToken, self).register(client)
     self._persist_filename = os.path.join(self.registry.config.data_path,
                                           "keystone.bpickle")
     self._persist = Persist(filename=self._persist_filename)
     self.registry.reactor.call_every(self.registry.config.flush_interval,
                                      self.flush)
    def run_script(self,
                   shell,
                   code,
                   user=None,
                   time_limit=None,
                   attachments=None,
                   server_supplied_env=None):
        """
        Run a script based on a shell and the code.

        A file will be written with #!<shell> as the first line, as executable,
        and run as the given user.

        XXX: Handle the 'reboot' and 'killall landscape-client' commands
        gracefully.

        @param shell: The interpreter to use.
        @param code: The code to run.
        @param user: The username to run the process as.
        @param time_limit: The number of seconds to allow the process to run
            before killing it and failing the returned Deferred with a
            L{ProcessTimeLimitReachedError}.
        @param attachments: C{dict} of filename/data attached to the script.

        @return: A deferred that will fire with the data printed by the process
            or fail with a L{ProcessTimeLimitReachedError}.
        """
        if not os.path.exists(shell.split()[0]):
            return fail(UnknownInterpreterError(shell))

        uid, gid, path = get_user_info(user)

        fd, filename = tempfile.mkstemp()
        script_file = os.fdopen(fd, "w")
        self.write_script_file(script_file, filename, shell, code, uid, gid)

        env = {"PATH": UBUNTU_PATH, "USER": user or "", "HOME": path or ""}
        if server_supplied_env:
            env.update(server_supplied_env)
        old_umask = os.umask(0022)

        if attachments:
            persist = Persist(filename=os.path.join(
                self.registry.config.data_path, "broker.bpickle"))
            persist = persist.root_at("registration")
            computer_id = persist.get("secure-id")
            d = self._save_attachments(attachments, uid, gid, computer_id)
        else:
            d = succeed(None)

        def prepare_script(attachment_dir):

            if attachment_dir is not None:
                env["LANDSCAPE_ATTACHMENTS"] = attachment_dir

            return self._run_script(filename, uid, gid, path, env, time_limit)

        d.addCallback(prepare_script)
        return d.addBoth(self._cleanup, filename, env, old_umask)
示例#3
0
class KeystoneToken(DataWatcher):
    """
    A plugin which pulls the admin_token from the keystone configuration file
    and sends it to the landscape server.
    """
    message_type = "keystone-token"
    message_key = "data"
    run_interval = 60 * 15
    scope = "openstack"

    def __init__(self, keystone_config_file=KEYSTONE_CONFIG_FILE):
        self._keystone_config_file = keystone_config_file

    def register(self, client):
        super(KeystoneToken, self).register(client)
        self._persist_filename = os.path.join(self.registry.config.data_path,
                                              "keystone.bpickle")
        self._persist = Persist(filename=self._persist_filename)
        self.registry.reactor.call_every(self.registry.config.flush_interval,
                                         self.flush)

    def _reset(self):
        """
        Reset the persist.
        """
        self._persist.remove("data")

    def flush(self):
        self._persist.save(self._persist_filename)

    def get_data(self):
        """
        Return the Keystone administrative token.
        """
        if not os.path.exists(self._keystone_config_file):
            return None

        config = ConfigParser()
        if _PY3:
            # We need to use the surrogateescape error handler as the
            # admin_token my contain arbitrary bytes. The ConfigParser in
            # Python 2 on the other hand does not support read_string.
            config_str = read_binary_file(
                self._keystone_config_file).decode("utf-8", "surrogateescape")
            config.read_string(config_str)
        else:
            config.read(self._keystone_config_file)
        try:
            admin_token = config.get("DEFAULT", "admin_token")
        except NoOptionError:
            logging.error("KeystoneToken: No admin_token found in %s"
                          % (self._keystone_config_file))
            return None
        # There is no support for surrogateescape in Python 2, but we actually
        # have bytes in this case anyway.
        if _PY3:
            admin_token = admin_token.encode("utf-8", "surrogateescape")

        return admin_token
 def test_that_resynchronize_clears_message_blackhole(self):
     """
     When a resynchronisation event occurs the block on new messages
     being stored is lifted.
     """
     self.reactor.fire("resynchronize-clients", [])
     persist = Persist(filename=self.persist_filename)
     self.assertFalse(persist.get("blackhole-messages"))
示例#5
0
    def test_flush_saves_persist(self):
        """
        The L{Monitor.flush} method saves any changes made to the persist
        database.
        """
        self.monitor.persist.set("a", 1)
        self.monitor.flush()

        persist = Persist()
        persist.load(self.monitor.persist_filename)
        self.assertEqual(persist.get("a"), 1)
示例#6
0
    def test_accumulate_skipped_step(self):
        """
        step:    0              5              10             15
               --|--+--+--+--+--|--+--+--+--+--|--+--+--+--+--|--
        value:   0                                   4
        """
        persist = Persist()
        accumulate = Accumulator(persist, 5)

        self.assertEqual(persist.get("key"), None)
        step_data = accumulate(12, 4, "key")
        self.assertEqual(step_data, None)
        self.assertEqual(persist.get("key"), (12, 8))
示例#7
0
    def test_accumulate(self):
        """
        step:    0              5
               --|--+--+--+--+--|--
        value:   0              4
        """
        persist = Persist()
        accumulate = Accumulator(persist, 5)

        self.assertEqual(persist.get("key"), None)
        step_data = accumulate(5, 4, "key")
        self.assertEqual(step_data, (5, 4))
        self.assertEqual(persist.get("key"), (5, 0))
示例#8
0
    def test_flush_after_exchange(self):
        """
        The L{Monitor.exchange} method flushes the monitor after
        C{exchange} on all plugins has been called.
        """
        plugin = BrokerClientPlugin()
        plugin.exchange = lambda: self.monitor.persist.set("a", 1)
        self.monitor.add(plugin)
        self.monitor.exchange()

        persist = Persist()
        persist.load(self.monitor.persist_filename)
        self.assertEqual(persist.get("a"), 1)
示例#9
0
    def test_accumulate_with_first_value_on_step_boundary(self):
        """
        step:    0              5
               --|--+--+--+--+--|--
        value:   14
        """
        persist = Persist()
        accumulate = Accumulator(persist, 5)

        self.assertEqual(persist.get("key"), None)
        step_data = accumulate(0, 14, "key")
        self.assertEqual(step_data, None)
        self.assertEqual(persist.get("key"), (0, 0))
示例#10
0
def get_versioned_persist(service):
    """Get a L{Persist} database with upgrade rules applied.

    Load a L{Persist} database for the given C{service} and upgrade or
    mark as current, as necessary.
    """
    persist = Persist(filename=service.persist_filename)
    upgrade_manager = UPGRADE_MANAGERS[service.service_name]
    if os.path.exists(service.persist_filename):
        upgrade_manager.apply(persist)
    else:
        upgrade_manager.initialize(persist)
    persist.save(service.persist_filename)
    return persist
示例#11
0
    def test_commit(self):
        """
        The Message Store can be told to save its persistent data to disk on
        demand.
        """
        filename = self.makeFile()
        store = MessageStore(Persist(filename=filename), self.temp_dir)
        store.set_accepted_types(["foo", "bar"])

        self.assertFalse(os.path.exists(filename))
        store.commit()
        self.assertTrue(os.path.exists(filename))

        store = MessageStore(Persist(filename=filename), self.temp_dir)
        self.assertEqual(set(store.get_accepted_types()), set(["foo", "bar"]))
示例#12
0
    def test_accumulate_non_zero_accumulated_value(self):
        """
        step:    5              10             15
               --|--+--+--+--+--|--+--+--+--+--|--
        value:         4                 3
        """
        persist = Persist()
        accumulate = Accumulator(persist, 5)

        # Persist data that would have been stored when
        # accumulate(7, 4, "key") was called.
        persist.set("key", (7, 8))
        step_data = accumulate(13, 3, "key")
        self.assertEqual(step_data, (10, float((2 * 4) + (3 * 3)) / 5))
        self.assertEqual(persist.get("key"), (13, 9))
示例#13
0
    def test_accumulate_within_step_with_nonzero_start_accumulated_value(self):
        """
        step:    0              5
               --|--+--+--+--+--|--
        value:   0     3     4
        """
        persist = Persist()
        accumulate = Accumulator(persist, 5)

        # Persist data that would have been stored when
        # accumulate(2, 3, "key") was called.
        persist.set("key", (2, 6))
        step_data = accumulate(4, 4, "key")
        self.assertEqual(step_data, None)
        self.assertEqual(persist.get("key"), (4, 14))
 def handler(message):
     Persist(filename=self.persist_filename)
     store = MessageStore(self.persist, self.config.message_store_path)
     self.assertEqual(store.get_server_sequence(),
                      self.message_counter)
     self.message_counter += 1
     handled.append(True)
示例#15
0
def is_registered(config):
    """Return whether the client is already registered."""
    persist_filename = os.path.join(
        config.data_path, "{}.bpickle".format(BrokerService.service_name))
    persist = Persist(filename=persist_filename)
    identity = Identity(config, persist)
    return bool(identity.secure_id)
示例#16
0
class KeystoneToken(DataWatcher):
    """
    A plugin which pulls the admin_token from the keystone configuration file
    and sends it to the landscape server.
    """
    message_type = "keystone-token"
    message_key = "data"
    run_interval = 60 * 15
    scope = "openstack"

    def __init__(self, keystone_config_file=KEYSTONE_CONFIG_FILE):
        self._keystone_config_file = keystone_config_file

    def register(self, client):
        super(KeystoneToken, self).register(client)
        self._persist_filename = os.path.join(self.registry.config.data_path,
                                              "keystone.bpickle")
        self._persist = Persist(filename=self._persist_filename)
        self.registry.reactor.call_every(self.registry.config.flush_interval,
                                         self.flush)

    def _reset(self):
        """
        Reset the persist.
        """
        self._persist.remove("data")

    def flush(self):
        self._persist.save(self._persist_filename)

    def get_data(self):
        """
        Return the Keystone administrative token.
        """
        if not os.path.exists(self._keystone_config_file):
            return None

        config = ConfigParser()
        config.read(self._keystone_config_file)
        try:
            admin_token = config.get("DEFAULT", "admin_token")
        except NoOptionError:
            logging.error("KeystoneToken: No admin_token found in %s" %
                          (self._keystone_config_file))
            return None
        return admin_token
示例#17
0
    def setUp(self):
        super(UserChangesTest, self).setUp()
        self.persist = Persist()
        self.shadow_file = self.makeFile("""\
jdoe:$1$xFlQvTqe$cBtrNEDOIKMy/BuJoUdeG0:13348:0:99999:7:::
psmith:!:13348:0:99999:7:::
sbarnes:$1$q7sz09uw$q.A3526M/SHu8vUb.Jo1A/:13349:0:99999:7:::
""")
class IdentityTest(LandscapeTest):

    helpers = [BrokerConfigurationHelper]

    def setUp(self):
        super(IdentityTest, self).setUp()
        self.persist = Persist(filename=self.makePersistFile())
        self.identity = Identity(self.config, self.persist)

    def check_persist_property(self, attr, persist_name):
        value = "VALUE"
        self.assertEqual(getattr(self.identity, attr), None,
                         "%r attribute should default to None, not %r" %
                         (attr, getattr(self.identity, attr)))
        setattr(self.identity, attr, value)
        self.assertEqual(getattr(self.identity, attr), value,
                         "%r attribute should be %r, not %r" %
                         (attr, value, getattr(self.identity, attr)))
        self.assertEqual(
            self.persist.get(persist_name), value,
            "%r not set to %r in persist" % (persist_name, value))

    def check_config_property(self, attr):
        value = "VALUE"
        setattr(self.config, attr, value)
        self.assertEqual(getattr(self.identity, attr), value,
                         "%r attribute should be %r, not %r" %
                         (attr, value, getattr(self.identity, attr)))

    def test_secure_id(self):
        self.check_persist_property("secure_id",
                                    "registration.secure-id")

    def test_secure_id_as_unicode(self):
        """secure-id is expected to be retrieved as unicode."""
        self.identity.secure_id = b"spam"
        self.assertEqual(self.identity.secure_id, "spam")

    def test_insecure_id(self):
        self.check_persist_property("insecure_id",
                                    "registration.insecure-id")

    def test_computer_title(self):
        self.check_config_property("computer_title")

    def test_account_name(self):
        self.check_config_property("account_name")

    def test_registration_key(self):
        self.check_config_property("registration_key")

    def test_client_tags(self):
        self.check_config_property("tags")

    def test_access_group(self):
        self.check_config_property("access_group")
示例#19
0
 def create_store(self):
     persist = Persist(filename=self.persist_filename)
     store = MessageStore(persist, self.temp_dir, 20)
     store.set_accepted_types(["empty", "data", "resynchronize"])
     store.add_schema(Message("empty", {}))
     store.add_schema(Message("empty2", {}))
     store.add_schema(Message("data", {"data": Bytes()}))
     store.add_schema(Message("unaccepted", {"data": Bytes()}))
     store.add_schema(Message("resynchronize", {}))
     return store
示例#20
0
 def set_up(self, test_case):
     super(MonitorHelper, self).set_up(test_case)
     persist = Persist()
     persist_filename = test_case.makePersistFile()
     test_case.config = MonitorConfiguration()
     test_case.config.load(["-c", test_case.config_filename])
     test_case.reactor = FakeReactor()
     test_case.monitor = Monitor(test_case.reactor, test_case.config,
                                 persist, persist_filename)
     test_case.monitor.broker = test_case.remote
     test_case.mstore = test_case.broker_service.message_store
    def test_run_with_attachment_ids(self):
        """
        The most recent protocol for script message doesn't include the
        attachment body inside the message itself, but instead gives an
        attachment ID, and the plugin fetches the files separately.
        """
        self.manager.config.url = "https://localhost/message-system"
        persist = Persist(
            filename=os.path.join(self.config.data_path, "broker.bpickle"))
        registration_persist = persist.root_at("registration")
        registration_persist.set("secure-id", "secure_id")
        persist.save()

        patch_fetch = mock.patch(
            "landscape.client.manager.scriptexecution.fetch_async")
        mock_fetch = patch_fetch.start()
        mock_fetch.return_value = succeed(b"some other data")

        headers = {
            "User-Agent": "landscape-client/%s" % VERSION,
            "Content-Type": "application/octet-stream",
            "X-Computer-ID": "secure_id"
        }

        result = self.plugin.run_script(
            u"/bin/sh",
            u"ls $LANDSCAPE_ATTACHMENTS && cat $LANDSCAPE_ATTACHMENTS/file1",
            attachments={u"file1": 14})

        def check(result):
            self.assertEqual(result, "file1\nsome other data")
            mock_fetch.assert_called_with("https://localhost/attachment/14",
                                          headers=headers,
                                          cainfo=None)

        def cleanup(result):
            patch_fetch.stop()
            # We have to return the Failure or result to get a working test.
            return result

        return result.addCallback(check).addBoth(cleanup)
    def test_run_with_attachment_ids_and_ssl(self):
        """
        When fetching attachments, L{ScriptExecution} passes the optional ssl
        certificate file if the configuration specifies it.
        """
        self.manager.config.url = "https://localhost/message-system"
        self.manager.config.ssl_public_key = "/some/key"
        persist = Persist(
            filename=os.path.join(self.config.data_path, "broker.bpickle"))
        registration_persist = persist.root_at("registration")
        registration_persist.set("secure-id", "secure_id")
        persist.save()

        patch_fetch = mock.patch(
            "landscape.client.manager.scriptexecution.fetch_async")
        mock_fetch = patch_fetch.start()
        mock_fetch.return_value = succeed(b"some other data")

        headers = {
            "User-Agent": "landscape-client/%s" % VERSION,
            "Content-Type": "application/octet-stream",
            "X-Computer-ID": "secure_id"
        }

        result = self.plugin.run_script(
            u"/bin/sh",
            u"ls $LANDSCAPE_ATTACHMENTS && cat $LANDSCAPE_ATTACHMENTS/file1",
            attachments={u"file1": 14})

        def check(result):
            self.assertEqual(result, "file1\nsome other data")
            mock_fetch.assert_called_with("https://localhost/attachment/14",
                                          headers=headers,
                                          cainfo="/some/key")

        def cleanup(result):
            patch_fetch.stop()
            return result

        return result.addCallback(check).addBoth(cleanup)
    def test_fetch_attachment_failure(self, mock_fetch):
        """
        If the plugin fails to retrieve the attachments with a
        L{HTTPCodeError}, a specific error code is shown.
        """
        self.manager.config.url = "https://localhost/message-system"
        persist = Persist(
            filename=os.path.join(self.config.data_path, "broker.bpickle"))
        registration_persist = persist.root_at("registration")
        registration_persist.set("secure-id", "secure_id")
        persist.save()
        headers = {
            "User-Agent": "landscape-client/%s" % VERSION,
            "Content-Type": "application/octet-stream",
            "X-Computer-ID": "secure_id"
        }

        mock_fetch.return_value = fail(HTTPCodeError(404, "Not found"))

        self.manager.add(ScriptExecutionPlugin())
        result = self._send_script("/bin/sh",
                                   "echo hi",
                                   attachments={u"file1": 14})

        def got_result(ignored):
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{
                    "type": "operation-result",
                    "operation-id": 123,
                    "result-text": "Server returned HTTP code 404",
                    "result-code": FETCH_ATTACHMENTS_FAILED_RESULT,
                    "status": FAILED
                }])
            mock_fetch.assert_called_with("https://localhost/attachment/14",
                                          headers=headers,
                                          cainfo=None)

        return result.addCallback(got_result)
 def test_reset_exchange_token_on_failure(self):
     """
     If an exchange fails we set the value of the next exchange token to
     C{None}, so we can authenticate ourselves even if we couldn't receive
     a valid token.
     """
     self.mstore.set_exchange_token("abcd-efgh")
     self.mstore.commit()
     self.transport.exchange = lambda *args, **kwargs: None
     self.exchanger.exchange()
     # Check that the change was persisted
     persist = Persist(filename=self.persist_filename)
     store = MessageStore(persist, self.config.message_store_path)
     self.assertIs(None, store.get_exchange_token())
示例#25
0
 def set_up(self, test_case):
     super(ExchangeHelper, self).set_up(test_case)
     test_case.persist_filename = test_case.makePersistFile()
     test_case.persist = Persist(filename=test_case.persist_filename)
     test_case.mstore = get_default_message_store(
         test_case.persist, test_case.config.message_store_path)
     test_case.identity = Identity(test_case.config, test_case.persist)
     test_case.transport = FakeTransport(None, test_case.config.url,
                                         test_case.config.ssl_public_key)
     test_case.reactor = FakeReactor()
     test_case.exchange_store = ExchangeStore(
         test_case.config.exchange_store_path)
     test_case.exchanger = MessageExchange(
         test_case.reactor, test_case.mstore, test_case.transport,
         test_case.identity, test_case.exchange_store, test_case.config)
示例#26
0
    def test_creating_loads_persist(self):
        """
        If C{persist_filename} exists, it is loaded by the constructor.
        """
        filename = self.makeFile()

        persist = Persist()
        persist.set("a", "Hi there!")
        persist.save(filename)

        monitor = Monitor(self.reactor,
                          self.config,
                          persist=Persist(),
                          persist_filename=filename)
        self.assertEqual(monitor.persist.get("a"), "Hi there!")
 def setUp(self):
     super(IdentityTest, self).setUp()
     self.persist = Persist(filename=self.makePersistFile())
     self.identity = Identity(self.config, self.persist)
示例#28
0
 def build_persist(self, *args, **kwargs):
     return Persist(*args, **kwargs)
示例#29
0
 def build_persist(self, *args, **kwargs):
     return RootedPersist(Persist(), "root.path", *args, **kwargs)
示例#30
0
 def build_persist(self, *args, **kwargs):
     return Persist(PickleBackend(), *args, **kwargs)