Exemple #1
0
    def testDeleteRequest(self):
        """Check that we can efficiently destroy a single flow request."""
        session_id = rdfvalue.SessionID(flow_name="test3")

        request = rdf_flow_runner.RequestState(
            id=1,
            client_id=test_lib.TEST_CLIENT_ID,
            next_state="TestState",
            session_id=session_id)

        with queue_manager.QueueManager(token=self.token) as manager:
            manager.QueueRequest(request)
            manager.QueueResponse(
                rdf_flows.GrrMessage(session_id=session_id,
                                     request_id=1,
                                     response_id=1))

        # Check the request and responses are there.
        all_requests = list(manager.FetchRequestsAndResponses(session_id))
        self.assertEqual(len(all_requests), 1)
        self.assertEqual(all_requests[0][0], request)

        with queue_manager.QueueManager(token=self.token) as manager:
            manager.DeleteRequest(request)

        all_requests = list(manager.FetchRequestsAndResponses(session_id))
        self.assertEqual(len(all_requests), 0)
Exemple #2
0
  def _StopLegacy(self, reason=None):
    super(GenericHunt, self).Stop(reason=reason)

    started_flows = grr_collections.RDFUrnCollection(
        self.started_flows_collection_urn)

    num_terminated_flows = 0
    self.Log("Hunt stop. Terminating all the started flows.")

    # Delete hunt flows states.
    for flows_batch in collection.Batch(started_flows,
                                        self.__class__.STOP_BATCH_SIZE):
      with queue_manager.QueueManager(token=self.token) as manager:
        manager.MultiDestroyFlowStates(flows_batch)

      with data_store.DB.GetMutationPool() as mutation_pool:
        for f in flows_batch:
          flow.GRRFlow.MarkForTermination(
              f, reason="Parent hunt stopped.", mutation_pool=mutation_pool)

      num_terminated_flows += len(flows_batch)

    # Delete hunt's requests and responses to ensure no more
    # processing is going to occur.
    with queue_manager.QueueManager(token=self.token) as manager:
      manager.DestroyFlowStates(self.session_id)

    self.Log("%d flows terminated.", num_terminated_flows)
Exemple #3
0
  def testUsesFrozenTimestampWhenDeletingAndFetchingNotifications(self):
    # When used in "with" statement QueueManager uses the frozen timestamp
    # when fetching and deleting data. Test that if we have 2 managers
    # created at different times,  they will behave correctly when dealing
    # with notifications for the same session ids. I.e. older queue_manager
    # will only "see" it's own notification and younger queue_manager will
    # "see" both.
    with queue_manager.QueueManager(token=self.token) as manager1:
      manager1.QueueNotification(
          session_id=rdfvalue.SessionID(
              base="aff4:/hunts", queue=queues.HUNTS, flow_name="123456"))
      manager1.Flush()

      self._current_mock_time += 10
      with queue_manager.QueueManager(token=self.token) as manager2:
        manager2.QueueNotification(
            session_id=rdfvalue.SessionID(
                base="aff4:/hunts", queue=queues.HUNTS, flow_name="123456"))
        manager2.Flush()

        self.assertEqual(
            len(manager1.GetNotificationsForAllShards(queues.HUNTS)), 1)
        self.assertEqual(
            len(manager2.GetNotificationsForAllShards(queues.HUNTS)), 1)

        manager1.DeleteNotification(
            rdfvalue.SessionID(
                base="aff4:/hunts", queue=queues.HUNTS, flow_name="123456"))

        self.assertEqual(
            len(manager1.GetNotificationsForAllShards(queues.HUNTS)), 0)
        self.assertEqual(
            len(manager2.GetNotificationsForAllShards(queues.HUNTS)), 1)
Exemple #4
0
    def DrainTaskSchedulerQueueForClient(self, client, max_count=None):
        """Drains the client's Task Scheduler queue.

    1) Get all messages in the client queue.
    2) Sort these into a set of session_ids.
    3) Use data_store.DB.ResolvePrefix() to query all requests.
    4) Delete all responses for retransmitted messages (if needed).

    Args:
       client: The ClientURN object specifying this client.
       max_count: The maximum number of messages we will issue for the client.
         If not given, uses self.max_queue_size .

    Returns:
       The tasks respresenting the messages returned. If we can not send them,
       we can reschedule them for later.
    """
        if max_count is None:
            max_count = self.max_queue_size

        if max_count <= 0:
            return []

        client = rdf_client.ClientURN(client)

        start_time = time.time()
        # Drain the queue for this client
        new_tasks = queue_manager.QueueManager(token=self.token).QueryAndOwn(
            queue=client.Queue(),
            limit=max_count,
            lease_seconds=self.message_expiry_time)

        initial_ttl = rdf_flows.GrrMessage().task_ttl
        check_before_sending = []
        result = []
        for task in new_tasks:
            if task.task_ttl < initial_ttl - 1:
                # This message has been leased before.
                check_before_sending.append(task)
            else:
                result.append(task)

        if check_before_sending:
            with queue_manager.QueueManager(token=self.token) as manager:
                status_found = manager.MultiCheckStatus(check_before_sending)

                # All messages that don't have a status yet should be sent again.
                for task in check_before_sending:
                    if task not in status_found:
                        result.append(task)
                    else:
                        manager.DeQueueClientRequest(task)

        stats.STATS.IncrementCounter("grr_messages_sent", len(result))
        if result:
            logging.debug("Drained %d messages for %s in %s seconds.",
                          len(result), client,
                          time.time() - start_time)

        return result
Exemple #5
0
    def testNotificationRequeueing(self):
        with test_lib.ConfigOverrider({"Worker.queue_shards": 1}):
            session_id = rdfvalue.SessionID(base="aff4:/testflows",
                                            queue=queues.HUNTS,
                                            flow_name="123")
            with test_lib.FakeTime(1000):
                # Schedule a notification.
                with queue_manager.QueueManager(token=self.token) as manager:
                    manager.QueueNotification(session_id=session_id)

            with test_lib.FakeTime(1100):
                with queue_manager.QueueManager(token=self.token) as manager:
                    notifications = manager.GetNotifications(queues.HUNTS)
                    self.assertEqual(len(notifications), 1)
                    # This notification was first queued and last queued at time 1000.
                    notification = notifications[0]
                    self.assertEqual(
                        notification.timestamp.AsSecondsSinceEpoch(), 1000)
                    self.assertEqual(
                        notification.first_queued.AsSecondsSinceEpoch(), 1000)
                    # Now requeue the same notification.
                    manager.DeleteNotification(session_id)
                    manager.QueueNotification(notification)

            with test_lib.FakeTime(1200):
                with queue_manager.QueueManager(token=self.token) as manager:
                    notifications = manager.GetNotifications(queues.HUNTS)
                    self.assertEqual(len(notifications), 1)
                    notification = notifications[0]
                    # Now the last queue time is 1100, the first queue time is still 1000.
                    self.assertEqual(
                        notification.timestamp.AsSecondsSinceEpoch(), 1100)
                    self.assertEqual(
                        notification.first_queued.AsSecondsSinceEpoch(), 1000)
                    # Again requeue the same notification.
                    manager.DeleteNotification(session_id)
                    manager.QueueNotification(notification)

            expired = 1000 + queue_manager.QueueManager.notification_expiry_time
            with test_lib.FakeTime(expired):
                with queue_manager.QueueManager(token=self.token) as manager:
                    notifications = manager.GetNotifications(queues.HUNTS)
                    self.assertEqual(len(notifications), 1)
                    # Again requeue the notification, this time it should be dropped.
                    manager.DeleteNotification(session_id)
                    manager.QueueNotification(notifications[0])

                with queue_manager.QueueManager(token=self.token) as manager:
                    notifications = manager.GetNotifications(queues.HUNTS)
                    self.assertEqual(len(notifications), 0)
Exemple #6
0
    def testDestroyFlowStates(self):
        """Check that we can efficiently destroy the flow's request queues."""
        session_id = rdfvalue.SessionID(flow_name="test2")

        request = rdf_flow_runner.RequestState(
            id=1,
            client_id=test_lib.TEST_CLIENT_ID,
            next_state="TestState",
            session_id=session_id)

        with queue_manager.QueueManager(token=self.token) as manager:
            manager.QueueRequest(request)
            manager.QueueResponse(
                rdf_flows.GrrMessage(request_id=1,
                                     response_id=1,
                                     session_id=session_id))

        # Check the request and responses are there.
        all_requests = list(manager.FetchRequestsAndResponses(session_id))
        self.assertEqual(len(all_requests), 1)
        self.assertEqual(all_requests[0][0], request)

        # Read the response directly.
        responses = data_store.DB.ReadResponsesForRequestId(session_id, 1)
        self.assertEqual(len(responses), 1)
        response = responses[0]
        self.assertEqual(response.request_id, 1)
        self.assertEqual(response.response_id, 1)
        self.assertEqual(response.session_id, session_id)

        with queue_manager.QueueManager(token=self.token) as manager:
            manager.DestroyFlowStates(session_id)

        all_requests = list(manager.FetchRequestsAndResponses(session_id))
        self.assertEqual(len(all_requests), 0)

        # Check that the response is gone.
        responses = data_store.DB.ReadResponsesForRequestId(session_id, 1)
        self.assertEqual(len(responses), 0)

        # Ensure the rows are gone from the data store. Some data stores
        # don't store the queues in that way but there is no harm in
        # checking.
        self.assertEqual(
            data_store.DB.ResolveRow(session_id.Add("state/request:00000001")),
            [])

        self.assertEqual(data_store.DB.ResolveRow(session_id.Add("state")), [])
    def Run(self):
        client_id = self.SetupClient(0)
        with test_lib.FakeTime(42):
            flow_urn = flow.StartAFF4Flow(
                flow_name=processes.ListProcesses.__name__,
                client_id=client_id,
                token=self.token)

            test_process = client_test_lib.MockWindowsProcess(
                name="test_process")
            with utils.Stubber(psutil, "Process", lambda: test_process):
                mock = flow_test_lib.MockClient(client_id,
                                                None,
                                                token=self.token)
                while mock.Next():
                    pass

        replace = {flow_urn.Basename(): "W:ABCDEF"}

        manager = queue_manager.QueueManager(token=self.token)
        requests_responses = manager.FetchRequestsAndResponses(flow_urn)
        for request, responses in requests_responses:
            replace[str(request.request.task_id)] = "42"
            for response in responses:
                replace[str(response.task_id)] = "42"

        self.Check("ListFlowRequests",
                   args=flow_plugin.ApiListFlowRequestsArgs(
                       client_id=client_id.Basename(),
                       flow_id=flow_urn.Basename()),
                   replace=replace)
Exemple #8
0
  def ExpireRules(self):
    """Removes any rules with an expiration date in the past."""
    rules = self.Get(self.Schema.RULES)
    new_rules = self.Schema.RULES()
    now = time.time() * 1e6
    expired_session_ids = set()

    for rule in rules:
      if rule.expires > now:
        new_rules.Append(rule)
      else:
        for action in rule.actions:
          if action.hunt_id:
            expired_session_ids.add(action.hunt_id)

    if expired_session_ids:
      with data_store.DB.GetMutationPool() as pool:
        # Notify the worker to mark this hunt as terminated.
        manager = queue_manager.QueueManager(token=self.token)
        manager.MultiNotifyQueue([
            rdf_flows.GrrNotification(session_id=session_id)
            for session_id in expired_session_ids
        ],
                                 mutation_pool=pool)

    if len(new_rules) < len(rules):
      self.Set(self.Schema.RULES, new_rules)
      self.Flush()
Exemple #9
0
  def Next(self):
    """Grab tasks for us from the server's queue."""
    with queue_manager.QueueManager(token=self.token) as manager:
      request_tasks = manager.QueryAndOwn(
          self.client_id.Queue(), limit=1, lease_seconds=10000)

      request_tasks.extend(self._mock_task_queue)
      self._mock_task_queue[:] = []  # Clear the referenced list.

      for message in request_tasks:
        try:
          responses = self.client_mock.HandleMessage(message)
          logging.info("Called client action %s generating %s responses",
                       message.name,
                       len(responses) + 1)
        except Exception as e:  # pylint: disable=broad-except
          logging.exception("Error %s occurred in client", e)
          responses = [
              self.client_mock.GenerateStatusMessage(
                  message, 1, status="GENERIC_ERROR")
          ]

        # Now insert those on the flow state queue
        for response in responses:
          self.PushToStateQueue(manager, response)

        # Additionally schedule a task for the worker
        manager.QueueNotification(session_id=message.session_id)

      return len(request_tasks)
    def testReceiveMessageListFleetspeak(self):
        fsd = fs_frontend_tool.GRRFSServer()

        grr_client_nr = 0xab
        grr_client_id_urn = self.SetupClient(grr_client_nr)

        flow_obj = self.FlowSetup(flow_test_lib.FlowOrderTest.__name__,
                                  grr_client_id_urn)

        num_msgs = 9

        session_id = flow_obj.session_id
        messages = [
            rdf_flows.GrrMessage(request_id=1,
                                 response_id=i,
                                 session_id=session_id,
                                 payload=rdfvalue.RDFInteger(i))
            for i in range(1, num_msgs + 1)
        ]

        fs_client_id = b"\x10\x00\x00\x00\x00\x00\x00\xab"
        # fs_client_id should be equivalent to grr_client_id_urn
        self.assertEqual(
            fs_client_id,
            fleetspeak_utils.GRRIDToFleetspeakID(grr_client_id_urn.Basename()))

        message_list = rdf_flows.PackedMessageList()
        communicator.Communicator.EncodeMessageList(
            rdf_flows.MessageList(job=messages), message_list)

        fs_message = fs_common_pb2.Message(message_type="MessageList",
                                           source=fs_common_pb2.Address(
                                               client_id=fs_client_id,
                                               service_name=FS_SERVICE_NAME))
        fs_message.data.Pack(message_list.AsPrimitiveProto())
        fsd.Process(fs_message, None)

        # Make sure the task is still on the client queue
        manager = queue_manager.QueueManager(token=self.token)
        tasks_on_client_queue = manager.Query(grr_client_id_urn, 100)
        self.assertEqual(len(tasks_on_client_queue), 1)

        want_messages = [message.Copy() for message in messages]
        for want_message in want_messages:
            # This is filled in by the frontend as soon as it gets the message.
            want_message.auth_state = (
                rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED)
            want_message.source = grr_client_id_urn

        stored_messages = data_store.DB.ReadResponsesForRequestId(
            session_id, 1)

        self.assertEqual(len(stored_messages), len(want_messages))

        stored_messages.sort(key=lambda m: m.response_id)
        # Check that messages were stored correctly
        for stored_message, want_message in itertools.izip(
                stored_messages, want_messages):
            stored_message.timestamp = None
            self.assertRDFValuesEqual(stored_message, want_message)
Exemple #11
0
  def Handle(self, args, token=None):
    flow_urn = args.flow_id.ResolveClientFlowURN(args.client_id, token=token)

    # Check if this flow really exists.
    try:
      aff4.FACTORY.Open(flow_urn, aff4_type=flow.GRRFlow, mode="r", token=token)
    except aff4.InstantiationError:
      raise FlowNotFoundError()

    result = ApiListFlowRequestsResult()
    manager = queue_manager.QueueManager(token=token)
    requests_responses = manager.FetchRequestsAndResponses(flow_urn)

    stop = None
    if args.count:
      stop = args.offset + args.count

    for request, responses in itertools.islice(requests_responses, args.offset,
                                               stop):
      if request.id == 0:
        continue

      # TODO(amoser): The request_id field should be an int.
      api_request = ApiFlowRequest(
          request_id=str(request.id), request_state=request)

      if responses:
        api_request.responses = responses

      result.items.append(api_request)

    return result
Exemple #12
0
    def _HandleAFF4(self, args, token=None):
        manager = queue_manager.QueueManager(token=token)

        result = ApiListClientActionRequestsResult()
        # Passing "limit" argument explicitly, as Query returns just 1 request
        # by default.
        for task in manager.Query(args.client_id.ToClientURN(),
                                  limit=self.__class__.REQUESTS_NUM_LIMIT):
            request = ApiClientActionRequest(leased_until=task.leased_until,
                                             session_id=task.session_id,
                                             client_action=task.name)

            if args.fetch_responses:
                res = []
                for r in data_store.DB.ReadResponsesForRequestId(
                        task.session_id, task.request_id):
                    # Clear out some internal fields.
                    r.task_id = None
                    r.auth_state = None
                    r.name = None
                    res.append(r)

                request.responses = res

            result.items.append(request)

        return result
  def testInspect(self):
    """Test the inspect UI."""
    client_urn = self.SetupClient(0)
    client_id = client_urn.Basename()

    self.RequestAndGrantClientApproval(client_id)

    if data_store.RelationalDBEnabled():
      flow_id = flow.StartFlow(
          client_id=client_id, flow_cls=flow_discovery.Interrogate)
      status = rdf_flow_objects.FlowStatus(
          client_id=client_id, flow_id=flow_id, request_id=1, response_id=2)
      data_store.REL_DB.WriteFlowResponses([status])
    else:
      session_id = flow.StartAFF4Flow(
          client_id=client_urn,
          flow_name=flow_discovery.Interrogate.__name__,
          token=self.token)
      status = rdf_flows.GrrMessage(
          request_id=1,
          response_id=2,
          session_id=session_id,
          type=rdf_flows.GrrMessage.Type.STATUS,
          auth_state=rdf_flows.GrrMessage.AuthorizationState.AUTHENTICATED)
      with queue_manager.QueueManager(token=self.token) as manager:
        manager.QueueResponse(status)

    self.Open("/#/clients/%s/debug-requests" % client_id)

    # Check that the we can see both requests and responses.
    self.WaitUntil(self.IsTextPresent, "GetPlatformInfo")
    self.WaitUntil(self.IsTextPresent, "GetConfig")
    self.WaitUntil(self.IsTextPresent, "EnumerateInterfaces")

    self.WaitUntil(self.IsTextPresent, "STATUS")
Exemple #14
0
    def testNoNotificationRescheduling(self):
        """Test that no notifications are rescheduled when a flow raises."""

        with test_lib.FakeTime(10000):
            flow_obj = self.FlowSetup("RaisingTestFlow")
            session_id = flow_obj.session_id
            flow_obj.Close()

            # Send the flow some messages.
            self.SendResponse(session_id, "Hello1", request_id=1)
            self.SendResponse(session_id, "Hello2", request_id=2)
            self.SendResponse(session_id, "Hello3", request_id=3)

            worker_obj = self._TestWorker()

            # Process all messages.
            with test_lib.SuppressLogs():
                worker_obj.RunOnce()
                worker_obj.thread_pool.Join()

        delay = flow_runner.FlowRunner.notification_retry_interval
        with test_lib.FakeTime(10000 + 100 + delay):
            manager = queue_manager.QueueManager(token=self.token)
            self.assertFalse(
                manager.GetNotificationsForAllShards(session_id.Queue()))
Exemple #15
0
  def FinalizeProcessCompletedRequests(self, notification):
    # Delete kill notification as the flow got processed and is not
    # stuck.
    with queue_manager.QueueManager(token=self.token) as manager:
      manager.DeleteNotification(
          self.session_id,
          start=self.context.kill_timestamp,
          end=self.context.kill_timestamp)
      self.context.kill_timestamp = None

      # If a flow raises in one state, the remaining states will not
      # be processed. This is indistinguishable from an incomplete
      # state due to missing responses / status so we need to check
      # here if the flow is still running before rescheduling.
      if (self.IsRunning() and notification.last_status and
          (self.context.next_processed_request <= notification.last_status)):
        logging.debug("Had to reschedule a notification: %s", notification)
        # We have received a notification for a specific request but
        # could not process that request. This might be a race
        # condition in the data store so we reschedule the
        # notification in the future.
        delay = self.notification_retry_interval
        notification.ttl -= 1
        if notification.ttl:
          manager.QueueNotification(
              notification, timestamp=notification.timestamp + delay)
Exemple #16
0
    def testNotificationsAreDeletedFromAllShards(self):
        manager = queue_manager.QueueManager(token=self.token)
        manager.QueueNotification(session_id=rdfvalue.SessionID(
            base="aff4:/hunts", queue=queues.HUNTS, flow_name="42"))
        manager.Flush()
        manager.QueueNotification(session_id=rdfvalue.SessionID(
            base="aff4:/hunts", queue=queues.HUNTS, flow_name="43"))
        manager.Flush()
        # There should be two notifications in two different shards.
        shards_with_data = 0
        for _ in range(manager.num_notification_shards):
            shard_sessions = manager.GetNotifications(queues.HUNTS)
            if shard_sessions:
                shards_with_data += 1
                self.assertEqual(len(shard_sessions), 1)
        self.assertEqual(shards_with_data, 2)

        # This should still work, as we delete notifications from all shards.
        manager.DeleteNotification(
            rdfvalue.SessionID(base="aff4:/hunts",
                               queue=queues.HUNTS,
                               flow_name="43"))
        manager.DeleteNotification(
            rdfvalue.SessionID(base="aff4:/hunts",
                               queue=queues.HUNTS,
                               flow_name="42"))
        for _ in range(manager.num_notification_shards):
            shard_sessions = manager.GetNotifications(queues.HUNTS)
            self.assertFalse(shard_sessions)
Exemple #17
0
    def testDelete(self):
        """Test that we can delete tasks."""

        test_queue = rdfvalue.RDFURN("fooDelete")
        task = rdf_flows.GrrMessage(queue=test_queue,
                                    session_id="aff4:/Test",
                                    generate_task_id=True)

        with data_store.DB.GetMutationPool() as pool:
            manager = queue_manager.QueueManager(token=self.token)
            manager.Schedule([task], pool)

        # Get a lease on the task
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 1)

        self.assertEqual(tasks[0].session_id, "aff4:/Test")

        # Now delete the task
        with data_store.DB.GetMutationPool() as pool:
            manager.Delete(test_queue, tasks, mutation_pool=pool)

        # Task is now deleted.
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 0)

        # If we try to get another lease on it we should fail - even after
        # expiry time.
        self._current_mock_time += 1000
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 0)
Exemple #18
0
    def testCountsActualNumberOfCompletedResponsesWhenApplyingTheLimit(self):
        session_id = rdfvalue.SessionID(flow_name="test")

        # Now queue more requests and responses:
        with queue_manager.QueueManager(token=self.token) as manager:
            # Start with request 1 - leave request 1 un-responded to.
            for request_id in range(5):
                request = rdf_flow_runner.RequestState(
                    id=request_id,
                    client_id=test_lib.TEST_CLIENT_ID,
                    next_state="TestState",
                    session_id=session_id)

                manager.QueueRequest(request)

                # Don't queue any actual responses, just a status message with a
                # fake response_id.
                manager.QueueResponse(
                    rdf_flows.GrrMessage(
                        session_id=session_id,
                        request_id=request_id,
                        response_id=1000,
                        type=rdf_flows.GrrMessage.Type.STATUS))

        # Check that even though status message for every request indicates 1000
        # responses, only the actual response count is used to apply the limit
        # when FetchCompletedResponses is called.
        completed_response = list(
            manager.FetchCompletedResponses(session_id, limit=5))
        self.assertEqual(len(completed_response), 5)
        for i, (request, responses) in enumerate(completed_response):
            self.assertEqual(request.id, i)
            # Responses contain just the status message.
            self.assertEqual(len(responses), 1)
Exemple #19
0
    def testSchedule(self):
        """Test the ability to schedule a task."""
        test_queue = rdfvalue.RDFURN("fooSchedule")
        task = rdf_flows.GrrMessage(queue=test_queue,
                                    task_ttl=5,
                                    session_id="aff4:/Test",
                                    generate_task_id=True)
        manager = queue_manager.QueueManager(token=self.token)
        with data_store.DB.GetMutationPool() as pool:
            manager.Schedule([task], pool)

        self.assertGreater(task.task_id, 0)
        self.assertGreater(task.task_id & 0xffffffff, 0)
        self.assertEqual(
            (int(self._current_mock_time * 1000) & 0xffffffff) << 32,
            task.task_id
            & 0x1fffffff00000000)
        self.assertEqual(task.task_ttl, 5)

        stored_tasks = data_store.DB.QueueQueryTasks(test_queue, limit=100000)
        self.assertEqual(len(stored_tasks), 1)
        stored_task = stored_tasks[0]
        self.assertGreater(stored_task.leased_until, 0)
        stored_task.leased_until = None

        self.assertRDFValuesEqual(stored_task, task)

        # Get a lease on the task
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 1)
        self.assertEqual(tasks[0].task_ttl, 4)

        self.assertEqual(tasks[0].session_id, "aff4:/Test")

        # If we try to get another lease on it we should fail
        self._current_mock_time += 10
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 0)

        # However after 100 seconds this should work again
        self._current_mock_time += 110
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 1)
        self.assertEqual(tasks[0].task_ttl, 3)

        # Check now that after a few retransmits we drop the message
        for i in range(2, 0, -1):
            self._current_mock_time += 110
            tasks = manager.QueryAndOwn(test_queue, lease_seconds=100)

            self.assertEqual(len(tasks), 1)
            self.assertEqual(tasks[0].task_ttl, i)

        # The task is now gone
        self._current_mock_time += 110
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100)
        self.assertEqual(len(tasks), 0)
Exemple #20
0
    def testTaskRetransmissionsAreCorrectlyAccounted(self):
        test_queue = rdfvalue.RDFURN("fooSchedule")
        task = rdf_flows.GrrMessage(queue=test_queue,
                                    task_ttl=5,
                                    session_id="aff4:/Test",
                                    generate_task_id=True)

        manager = queue_manager.QueueManager(token=self.token)
        with data_store.DB.GetMutationPool() as pool:
            manager.Schedule([task], pool)

        # Get a lease on the task
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 1)
        self.assertEqual(tasks[0].task_ttl, 4)

        self.assertEqual(
            stats.STATS.GetMetricValue("grr_task_retransmission_count"),
            self.retransmission_metric_value)

        # Get a lease on the task 100 seconds later
        self._current_mock_time += 110
        tasks = manager.QueryAndOwn(test_queue, lease_seconds=100, limit=100)

        self.assertEqual(len(tasks), 1)
        self.assertEqual(tasks[0].task_ttl, 3)

        self.assertEqual(
            stats.STATS.GetMetricValue("grr_task_retransmission_count"),
            self.retransmission_metric_value + 1)
Exemple #21
0
    def CheckNotificationsDisappear(self, session_id):
        worker_obj = self._TestWorker()
        manager = queue_manager.QueueManager(token=self.token)
        notification = rdf_flows.GrrNotification(session_id=session_id)
        with data_store.DB.GetMutationPool() as pool:
            manager.NotifyQueue(notification, mutation_pool=pool)

        notifications = manager.GetNotifications(queues.FLOWS)

        # Check the notification is there. With multiple worker queue shards we can
        # get other notifications such as for audit event listeners, so we need to
        # filter out ours.
        notifications = [
            x for x in notifications if x.session_id == session_id
        ]
        self.assertLen(notifications, 1)

        # Process all messages
        worker_obj.RunOnce()
        worker_obj.thread_pool.Join()

        notifications = manager.GetNotifications(queues.FLOWS)
        notifications = [
            x for x in notifications if x.session_id == session_id
        ]

        # Check the notification is now gone.
        self.assertEmpty(notifications)
Exemple #22
0
    def testKillNotificationsScheduledForFlows(self):
        worker_obj = self._TestWorker()
        initial_time = rdfvalue.RDFDatetime.FromSecondsSinceEpoch(100)

        try:
            with test_lib.FakeTime(initial_time.AsSecondsSinceEpoch()):
                flow.StartAFF4Flow(flow_name=WorkerStuckableTestFlow.__name__,
                                   client_id=self.client_id,
                                   token=self.token,
                                   sync=False)

                # Process all messages
                worker_obj.RunOnce()
                # Wait until worker thread starts processing the flow.
                WorkerStuckableTestFlow.WaitUntilWorkerStartsProcessing()

                # Assert that there are no stuck notifications in the worker's
                # queue.
                with queue_manager.QueueManager(token=self.token) as manager:
                    for queue in worker_obj.queues:
                        notifications = manager.GetNotifications(queue)
                        for n in notifications:
                            self.assertFalse(n.in_progress)

        finally:
            # Release the semaphore so that worker thread unblocks and finishes
            # processing the flow.
            WorkerStuckableTestFlow.StopFlow()
            WorkerStuckableTestFlow.LetWorkerFinishProcessing()
            worker_obj.thread_pool.Join()
Exemple #23
0
    def testNoKillNotificationsScheduledForHunts(self):
        worker_obj = self._TestWorker()
        initial_time = rdfvalue.RDFDatetime.FromSecondsSinceEpoch(100)

        try:
            with test_lib.FakeTime(initial_time.AsSecondsSinceEpoch()):
                with implementation.StartHunt(
                        hunt_name=WorkerStuckableHunt.__name__,
                        client_rate=0,
                        token=self.token) as hunt:
                    hunt.GetRunner().Start()

                implementation.GRRHunt.StartClients(hunt.session_id,
                                                    [self.client_id])

                # Process all messages
                while worker_obj.RunOnce():
                    pass
                # Wait until worker thread starts processing the flow.
                WorkerStuckableHunt.WaitUntilWorkerStartsProcessing()

                # Assert that there are no stuck notifications in the worker's queue.
                with queue_manager.QueueManager(token=self.token) as manager:
                    for queue in worker_obj.queues:
                        notifications = manager.GetNotifications(queue)
                        for n in notifications:
                            self.assertFalse(n.in_progress)

        finally:
            # Release the semaphore so that worker thread unblocks and finishes
            # processing the flow.
            WorkerStuckableHunt.LetWorkerFinishProcessing()
            worker_obj.thread_pool.Join()
Exemple #24
0
  def Next(self):
    """Very simple emulator of the worker.

    We wake each flow in turn and run it.

    Returns:
      total number of flows still alive.

    Raises:
      RuntimeError: if the flow terminates with an error.
    """
    with queue_manager.QueueManager(token=self.token) as manager:
      run_sessions = []
      for queue in self.queues:
        notifications_available = manager.GetNotificationsForAllShards(queue)
        # Run all the flows until they are finished

        # Only sample one session at the time to force serialization of flows
        # after each state run.
        for notification in notifications_available[:1]:
          session_id = notification.session_id
          manager.DeleteNotification(session_id, end=notification.timestamp)
          run_sessions.append(session_id)

          # Handle well known flows here.
          flow_name = session_id.FlowName()
          if flow_name in self.well_known_flows:
            well_known_flow = self.well_known_flows[flow_name]
            with well_known_flow:
              responses = well_known_flow.FetchAndRemoveRequestsAndResponses(
                  well_known_flow.well_known_session_id)
            well_known_flow.ProcessResponses(responses, self.pool)
            continue

          with aff4.FACTORY.OpenWithLock(
              session_id, token=self.token, blocking=False) as flow_obj:

            # Run it
            runner = flow_obj.GetRunner()
            cpu_used = runner.context.client_resources.cpu_usage
            user_cpu = self.cpu_user.next()
            system_cpu = self.cpu_system.next()
            network_bytes = self.network_bytes.next()
            cpu_used.user_cpu_time += user_cpu
            cpu_used.system_cpu_time += system_cpu
            runner.context.network_bytes_sent += network_bytes
            runner.ProcessCompletedRequests(notification, self.pool)

            if (self.check_flow_errors and
                isinstance(flow_obj, flow.GRRFlow) and
                runner.context.state == rdf_flow_runner.FlowContext.State.ERROR
               ):
              logging.exception("Flow terminated in state %s with an error: %s",
                                runner.context.current_state,
                                runner.context.backtrace)
              raise RuntimeError(runner.context.backtrace)

    return run_sessions
Exemple #25
0
    def testNotFirstShardNameHasIndexSuffix(self):
        manager = queue_manager.QueueManager(token=self.token)
        while True:
            shard = manager.GetNotificationShard(queues.HUNTS)
            if (manager.notification_shard_counters[str(queues.HUNTS)] %
                    manager.num_notification_shards) == 1:
                break

        self.assertEqual(shard, queues.HUNTS.Add("1"))
Exemple #26
0
    def testFirstShardNameIsEqualToTheQueue(self):
        manager = queue_manager.QueueManager(token=self.token)
        while True:
            shard = manager.GetNotificationShard(queues.HUNTS)
            if (manager.notification_shard_counters[str(queues.HUNTS)] %
                    manager.num_notification_shards) == 0:
                break

        self.assertEqual(shard, queues.HUNTS)
Exemple #27
0
    def HandleMessageBundles(self, request_comms, response_comms):
        """Processes a queue of messages as passed from the client.

    We basically dispatch all the GrrMessages in the queue to the task scheduler
    for backend processing. We then retrieve from the TS the messages destined
    for this client.

    Args:
       request_comms: A ClientCommunication rdfvalue with messages sent by the
       client. source should be set to the client CN.

       response_comms: A ClientCommunication rdfvalue of jobs destined to this
       client.

    Returns:
       tuple of (source, message_count) where message_count is the number of
       messages received from the client with common name source.
    """
        messages, source, timestamp = self._communicator.DecodeMessages(
            request_comms)

        now = time.time()
        if messages:
            # Receive messages in line.
            self.ReceiveMessages(source, messages)

        # We send the client a maximum of self.max_queue_size messages
        required_count = max(0, self.max_queue_size - request_comms.queue_size)
        tasks = []

        message_list = rdf_flows.MessageList()
        # Only give the client messages if we are able to receive them in a
        # reasonable time.
        if time.time() - now < 10:
            tasks = self.DrainTaskSchedulerQueueForClient(
                source, required_count)
            message_list.job = tasks

        # Encode the message_list in the response_comms using the same API version
        # the client used.
        try:
            self._communicator.EncodeMessages(
                message_list,
                response_comms,
                destination=source,
                timestamp=timestamp,
                api_version=request_comms.api_version)
        except communicator.UnknownClientCert:
            # We can not encode messages to the client yet because we do not have the
            # client certificate - return them to the queue so we can try again later.
            with data_store.DB.GetMutationPool() as pool:
                queue_manager.QueueManager(token=self.token).Schedule(
                    tasks, pool)
            raise

        return source, len(messages)
Exemple #28
0
    def DeleteRuns(self, job, age=None, token=None):
        """Deletes flows initiated by the job that are older than specified."""
        if age is None:
            raise ValueError("age can't be None")

        child_flows = list(job.ListChildren(age=age))
        with queue_manager.QueueManager(token=token) as queuemanager:
            queuemanager.MultiDestroyFlowStates(child_flows)

        aff4.FACTORY.MultiDelete(child_flows, token=token)
Exemple #29
0
    def __init__(self,
                 flow_obj,
                 parent_runner=None,
                 runner_args=None,
                 token=None):
        """Constructor for the Flow Runner.

    Args:
      flow_obj: The flow object this runner will run states for.
      parent_runner: The parent runner of this runner.
      runner_args: A FlowRunnerArgs() instance containing initial values. If not
        specified, we use the runner_args from the flow_obj.
      token: An instance of access_control.ACLToken security token.
    """
        self.token = token or flow_obj.token
        self.parent_runner = parent_runner

        # If we have a parent runner, we use its queue manager.
        if parent_runner is not None:
            self.queue_manager = parent_runner.queue_manager
        else:
            # Otherwise we use a new queue manager.
            self.queue_manager = queue_manager.QueueManager(token=self.token)
            self.queue_manager.FreezeTimestamp()

        self.queued_replies = []

        self.outbound_lock = threading.Lock()
        self.flow_obj = flow_obj

        # Initialize from a new runner args proto.
        if runner_args is not None:
            self.runner_args = runner_args
            self.session_id = self.GetNewSessionID()
            self.flow_obj.urn = self.session_id

            # Flow state does not have a valid context, we need to create one.
            self.context = self.InitializeContext(runner_args)
            self.flow_obj.context = self.context
            self.context.session_id = self.session_id

        else:
            # Retrieve args from the flow object's context. The flow object is
            # responsible for storing our context, although they do not generally
            # access it directly.
            self.context = self.flow_obj.context

            self.runner_args = self.flow_obj.runner_args

        # Populate the flow object's urn with the session id.
        self.flow_obj.urn = self.session_id = self.context.session_id

        # Sent replies are cached so that they can be processed by output plugins
        # when the flow is saved.
        self.sent_replies = []
Exemple #30
0
  def DeleteOldRuns(self, job, cutoff_timestamp=None, token=None):
    """Deletes flows initiated by the job that are older than specified."""
    if cutoff_timestamp is None:
      raise ValueError("cutoff_timestamp can't be None")

    child_flows = list(job.ListChildren(age=cutoff_timestamp))
    with queue_manager.QueueManager(token=token) as queuemanager:
      queuemanager.MultiDestroyFlowStates(child_flows)

    aff4.FACTORY.MultiDelete(child_flows, token=token)
    return len(child_flows)