Beispiel #1
0
    def test_run_unknown_user(self, mock_getpwnam):
        mock_getpwnam.side_effect = KeyError("foo")

        self.manager.config.script_users = "foo"

        self.store.add_graph(123, "filename", "foo")
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                    123: {
                        "error": u"UnknownUserError: Unknown user 'foo'",
                        "script-hash": b"",
                        "values": []},
                    },
                  "type": "custom-graph"}])
            mock_getpwnam.assert_called_with("foo")

        return result.addCallback(check)
Beispiel #2
0
    def test_run_dissallowed_user(self):
        uid = os.getuid()
        info = pwd.getpwuid(uid)
        username = info.pw_name
        self.manager.config.script_users = "foo"

        self.store.add_graph(123, "filename", username)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                    123: {
                        "error": (u"ProhibitedUserError: Custom graph cannot "
                                  u"be run as user %s") % (username,),
                        "script-hash": b"",
                        "values": []},
                    },
                  "type": "custom-graph"}])

        return result.addCallback(check)
Beispiel #3
0
    def test_run_timeout(self):
        filename = self.makeFile("some content")
        self.store.add_graph(123, filename, None)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]
        protocol = spawn[0]
        protocol.makeConnection(DummyProcess())
        self.assertEqual(spawn[1], filename)

        self.manager.reactor.advance(110)
        protocol.processEnded(Failure(ProcessDone(0)))

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                    123: {
                        "error": u"Process exceeded the 10 seconds limit",
                        "script-hash": b"9893532233caff98cd083a116b013c0b",
                        "values": []},
                    },
                  "type": "custom-graph"}])

        return result.addCallback(check)
Beispiel #4
0
    def test_run_user(self, mock_getpwnam):
        filename = self.makeFile("some content")
        self.store.add_graph(123, filename, "bar")
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory

        class pwnam(object):
            pw_uid = 1234
            pw_gid = 5678
            pw_dir = self.makeFile()

        mock_getpwnam.return_value = pwnam

        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]
        self.assertEqual(spawn[1], filename)
        self.assertEqual(spawn[2], (filename,))
        self.assertEqual(spawn[3], {})
        self.assertEqual(spawn[4], "/")
        self.assertEqual(spawn[5], 1234)
        self.assertEqual(spawn[6], 5678)
        mock_getpwnam.assert_called_with("bar")

        self._exit_process_protocol(spawn[0], b"spam")

        return result
Beispiel #5
0
    def test_run_unknown_user_with_unicode(self):
        """
        Using a non-existent user containing unicode characters fails with the
        appropriate error message.
        """
        username = u"non-existent-f\N{LATIN SMALL LETTER E WITH ACUTE}e"

        self.manager.config.script_users = "ALL"

        filename = self.makeFile("some content")
        self.store.add_graph(123, filename, username)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                    123: {
                        "error":
                            u"UnknownUserError: Unknown user '%s'" % username,
                        "script-hash": b"9893532233caff98cd083a116b013c0b",
                        "values": []}},
                  "type": "custom-graph"}])

        return result.addCallback(check)
    def test_unknown_error(self):
        """
        When a completely unknown error comes back from the process protocol,
        the operation fails and the formatted failure is included in the
        response message.
        """
        factory = StubProcessFactory()

        self.manager.add(ScriptExecutionPlugin(process_factory=factory))

        result = self._send_script(sys.executable, "print 'hi'")

        self._verify_script(factory.spawns[0][1], sys.executable, "print 'hi'")
        self.assertMessages(
            self.broker_service.message_store.get_pending_messages(), [])

        failure = Failure(RuntimeError("Oh noes!"))
        factory.spawns[0][0].result_deferred.errback(failure)

        def got_result(r):
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{
                    "type": "operation-result",
                    "operation-id": 123,
                    "status": FAILED,
                    "result-text": str(failure)
                }])

        result.addCallback(got_result)
        return result
Beispiel #7
0
    def test_run_with_script_removed(self):
        """
        If a script is removed while a data point is being retrieved, the data
        point is discarded and no data is sent at all.
        """
        uid = os.getuid()
        info = pwd.getpwuid(uid)
        username = info.pw_name
        self.manager.dispatch_message(
            {"type": "custom-graph-add",
                     "interpreter": "/bin/sh",
                     "code": "echo 1.0",
                     "username": username,
                     "graph-id": 123})

        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]

        self.manager.dispatch_message(
            {"type": "custom-graph-remove",
                     "graph-id": 123})

        self._exit_process_protocol(spawn[0], b"1.0")

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"api": b"3.2", "data": {}, "timestamp": 0, "type":
                  "custom-graph"}])
        return result.addCallback(check)
    def _run_script(self, username, uid, gid, path):
        expected_uid = uid if uid != os.getuid() else None
        expected_gid = gid if gid != os.getgid() else None

        factory = StubProcessFactory()
        self.plugin.process_factory = factory

        # ignore the call to chown!
        patch_chown = mock.patch("os.chown")
        mock_chown = patch_chown.start()

        result = self.plugin.run_script("/bin/sh", "echo hi", user=username)

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]
        self.assertEqual(spawn[4], path)
        self.assertEqual(spawn[5], expected_uid)
        self.assertEqual(spawn[6], expected_gid)

        protocol = spawn[0]
        protocol.childDataReceived(1, b"foobar")
        for fd in (0, 1, 2):
            protocol.childConnectionLost(fd)
        protocol.processEnded(Failure(ProcessDone(0)))

        def check(result):
            mock_chown.assert_called_with()
            self.assertEqual(result, "foobar")

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

        return result.addErrback(check).addBoth(cleanup)
Beispiel #9
0
    def test_run_no_output_error(self):
        filename = self.makeFile("some_content")
        self.store.add_graph(123, filename, None)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]
        self.assertEqual(spawn[1], filename)

        self._exit_process_protocol(spawn[0], b"")

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                    123: {
                        "error": (u"NoOutputError: Script did not output "
                                  u"any value"),
                        "values": [],
                        "script-hash": b"baab6c16d9143523b7865d46896e4596",
                        },
                    },
                  "type": "custom-graph"}])
        return result.addCallback(check)
    def test_success(self):
        """
        When a C{execute-script} message is received from the server, the
        specified script will be run and an operation-result will be sent back
        to the server.
        """
        # Let's use a stub process factory, because otherwise we don't have
        # access to the deferred.
        factory = StubProcessFactory()

        self.manager.add(ScriptExecutionPlugin(process_factory=factory))

        result = self._send_script(sys.executable, "print 'hi'")

        self._verify_script(factory.spawns[0][1], sys.executable, "print 'hi'")
        self.assertMessages(
            self.broker_service.message_store.get_pending_messages(), [])

        # Now let's simulate the completion of the process
        factory.spawns[0][0].childDataReceived(1, b"hi!\n")
        factory.spawns[0][0].processEnded(Failure(ProcessDone(0)))

        def got_result(r):
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{
                    "type": "operation-result",
                    "operation-id": 123,
                    "status": SUCCEEDED,
                    "result-text": u"hi!\n"
                }])

        result.addCallback(got_result)
        return result
    def test_timeout(self):
        """
        If a L{ProcessTimeLimitReachedError} is fired back, the
        operation-result should have a failed status.
        """
        factory = StubProcessFactory()
        self.manager.add(ScriptExecutionPlugin(process_factory=factory))

        result = self._send_script(sys.executable, "bar", time_limit=30)
        self._verify_script(factory.spawns[0][1], sys.executable, "bar")

        protocol = factory.spawns[0][0]
        protocol.makeConnection(DummyProcess())
        protocol.childDataReceived(2, b"ONOEZ")
        self.manager.reactor.advance(31)
        protocol.processEnded(Failure(ProcessDone(0)))

        def got_result(r):
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{
                    "type": "operation-result",
                    "operation-id": 123,
                    "status": FAILED,
                    "result-text": u"ONOEZ",
                    "result-code": 102
                }])

        result.addCallback(got_result)
        return result
Beispiel #12
0
    def test_multiple_errors(self):
        filename1 = self.makeFile("some_content")
        self.store.add_graph(123, filename1, None)
        filename2 = self.makeFile("some_content")
        self.store.add_graph(124, filename2, None)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 2)
        spawn = factory.spawns[0]
        self._exit_process_protocol(spawn[0], b"foo")
        spawn = factory.spawns[1]
        self._exit_process_protocol(spawn[0], b"")

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"data": {
                      123: {
                          "error": (u"InvalidFormatError: Failed to convert "
                                    u"to number: 'foo'"),
                          "script-hash": b"baab6c16d9143523b7865d46896e4596",
                          "values": []},
                      124: {
                          "error": (u"NoOutputError: Script did not output "
                                    u"any value"),
                          "script-hash": b"baab6c16d9143523b7865d46896e4596",
                          "values": []},
                      },
                  "type": "custom-graph"}])
        return result.addCallback(check)
Beispiel #13
0
 def setUp(self):
     super(ShutdownManagerTest, self).setUp()
     self.broker_service.message_store.set_accepted_types(
         ["shutdown", "operation-result"])
     self.broker_service.pinger.start()
     self.process_factory = StubProcessFactory()
     self.plugin = ShutdownManager(process_factory=self.process_factory)
     self.manager.add(self.plugin)
 def test_time_limit_canceled_after_success(self):
     """
     The timeout call is cancelled after the script terminates.
     """
     factory = StubProcessFactory()
     self.plugin.process_factory = factory
     self.plugin.run_script("/bin/sh", "", time_limit=500)
     protocol = factory.spawns[0][0]
     transport = DummyProcess()
     protocol.makeConnection(transport)
     protocol.childDataReceived(1, b"hi\n")
     protocol.processEnded(Failure(ProcessDone(0)))
     self.manager.reactor.advance(501)
     self.assertEqual(transport.signals, [])
Beispiel #15
0
    def test_run_without_graph(self):
        """
        If no graph is available, C{CustomGraphPlugin.run} doesn't even call
        C{call_if_accepted} on the broker and return immediately an empty list
        of results.
        """
        self.graph_manager.registry.broker.call_if_accepted = (
            lambda *args: 1 / 0)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        return result.addCallback(self.assertEqual, [])
Beispiel #16
0
    def test_run_removed_file(self):
        """
        If run is called on a script file that has been removed, it doesn't try
        to run it, but report it with an empty hash value.
        """
        self.store.add_graph(123, "/nonexistent", None)
        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        self.graph_manager.exchange()
        self.assertMessages(
            self.broker_service.message_store.get_pending_messages(),
            [{"data": {123: {"error": u"", "script-hash": b"", "values": []}},
              "type": "custom-graph"}])
Beispiel #17
0
    def test_run_with_script_updated(self):
        """
        If a script is updated while a data point is being retrieved, the data
        point is discarded and no value is sent, but the new script is
        mentioned.
        """
        uid = os.getuid()
        info = pwd.getpwuid(uid)
        username = info.pw_name
        self.manager.dispatch_message(
            {"type": "custom-graph-add",
                     "interpreter": "/bin/sh",
             "code": "echo 1.0",
                     "username": username,
                     "graph-id": 123})

        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]

        self.manager.dispatch_message(
            {"type": "custom-graph-add",
                     "interpreter": "/bin/sh",
                     "code": "echo 2.0",
                     "username": username,
                     "graph-id": 123})

        self._exit_process_protocol(spawn[0], b"1.0")

        def check(ignore):
            self.graph_manager.exchange()
            self.assertMessages(
                self.broker_service.message_store.get_pending_messages(),
                [{"api": b"3.2",
                  "data": {123: {"error": u"",
                                 "script-hash":
                                     b"991e15a81929c79fe1d243b2afd99c62",
                                 "values": []}},
                  "timestamp": 0,
                  "type": "custom-graph"}])

        return result.addCallback(check)
    def test_user_with_attachments(self):
        uid = os.getuid()
        info = pwd.getpwuid(uid)
        username = info.pw_name
        gid = info.pw_gid

        patch_chown = mock.patch("os.chown")
        mock_chown = patch_chown.start()

        factory = StubProcessFactory()
        self.plugin.process_factory = factory

        result = self.plugin.run_script("/bin/sh",
                                        "echo hi",
                                        user=username,
                                        attachments={u"file 1": "some data"})

        self.assertEqual(len(factory.spawns), 1)
        spawn = factory.spawns[0]
        self.assertIn("LANDSCAPE_ATTACHMENTS", spawn[3])
        attachment_dir = spawn[3]["LANDSCAPE_ATTACHMENTS"]
        self.assertEqual(stat.S_IMODE(os.stat(attachment_dir).st_mode), 0o700)
        filename = os.path.join(attachment_dir, "file 1")
        self.assertEqual(stat.S_IMODE(os.stat(filename).st_mode), 0o600)

        protocol = spawn[0]
        protocol.childDataReceived(1, b"foobar")
        for fd in (0, 1, 2):
            protocol.childConnectionLost(fd)
        protocol.processEnded(Failure(ProcessDone(0)))

        def check(data):
            self.assertEqual(data, "foobar")
            self.assertFalse(os.path.exists(attachment_dir))
            mock_chown.assert_has_calls(
                [mock.call(mock.ANY, uid, gid) for x in range(3)])

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

        return result.addCallback(check).addBoth(cleanup)
    def test_limit_time_accumulates_data(self):
        """
        Data from processes that time out should still be accumulated and
        available from the exception object that is raised.
        """
        factory = StubProcessFactory()
        self.plugin.process_factory = factory
        result = self.plugin.run_script("/bin/sh", "", time_limit=500)
        protocol = factory.spawns[0][0]
        protocol.makeConnection(DummyProcess())
        protocol.childDataReceived(1, b"hi\n")
        self.manager.reactor.advance(501)
        protocol.processEnded(Failure(ProcessDone(0)))

        def got_error(f):
            self.assertTrue(f.check(ProcessTimeLimitReachedError))
            self.assertEqual(f.value.data, "hi\n")

        result.addErrback(got_error)
        return result
    def test_cancel_doesnt_blow_after_success(self):
        """
        When the process ends successfully and is immediately followed by the
        timeout, the output should still be in the failure and nothing bad will
        happen!
        [regression test: killing of the already-dead process would blow up.]
        """
        factory = StubProcessFactory()
        self.plugin.process_factory = factory
        result = self.plugin.run_script("/bin/sh", "", time_limit=500)
        protocol = factory.spawns[0][0]
        protocol.makeConnection(DummyProcess())
        protocol.childDataReceived(1, b"hi")
        protocol.processEnded(Failure(ProcessDone(0)))
        self.manager.reactor.advance(501)

        def got_result(output):
            self.assertEqual(output, "hi")

        result.addCallback(got_result)
        return result
    def test_limit_size(self):
        """Data returned from the command is limited."""
        factory = StubProcessFactory()
        self.plugin.process_factory = factory
        self.plugin.size_limit = 100
        result = self.plugin.run_script("/bin/sh", "")

        # Ultimately we assert that the resulting output is limited to
        # 100 bytes and indicates its truncation.
        result.addCallback(self.assertEqual,
                           ("x" * 79) + "\n**OUTPUT TRUNCATED**")

        protocol = factory.spawns[0][0]

        # Push 200 bytes of output, so we trigger truncation.
        protocol.childDataReceived(1, b"x" * 200)

        for fd in (0, 1, 2):
            protocol.childConnectionLost(fd)
        protocol.processEnded(Failure(ProcessDone(0)))

        return result
Beispiel #22
0
    def test_command_output_ends_with_truncation(self):
        """After truncation, no further output is recorded."""
        factory = StubProcessFactory()
        self.plugin.process_factory = factory
        self.manager.config.script_output_limit = 1
        result = self.plugin.run_script("/bin/sh", "")

        # Ultimately we assert that the resulting output is limited to
        # 1024 bytes and indicates its truncation.
        result.addCallback(self.assertEqual,
                           ("x" * (1024 - 21)) + "\n**OUTPUT TRUNCATED**")
        protocol = factory.spawns[0][0]

        # Push 1024 bytes of output, so we trigger truncation.
        protocol.childDataReceived(1, b"x" * 1024)
        # Push 1024 bytes more
        protocol.childDataReceived(1, b"x" * 1024)

        for fd in (0, 1, 2):
            protocol.childConnectionLost(fd)
        protocol.processEnded(Failure(ProcessDone(0)))

        return result
Beispiel #23
0
    def test_run_not_accepted_types(self):
        """
        If "custom-graph" is not an accepted message-type anymore,
        C{CustomGraphPlugin.run} shouldn't even run the graph scripts.
        """
        self.broker_service.message_store.set_accepted_types([])

        uid = os.getuid()
        info = pwd.getpwuid(uid)
        username = info.pw_name
        self.manager.dispatch_message(
            {"type": "custom-graph-add",
                     "interpreter": "/bin/sh",
                     "code": "echo 1.0",
                     "username": username,
                     "graph-id": 123})

        factory = StubProcessFactory()
        self.graph_manager.process_factory = factory
        result = self.graph_manager.run()

        self.assertEqual(len(factory.spawns), 0)

        return result.addCallback(self.assertIdentical, None)