def on_rx(self, message): with self._lock: log.debug("on_rx: %s" % message) session_id = message['session_id'] fqdn = message['fqdn'] log.info("AgentRpcMessenger.on_rx: %s/%s" % (fqdn, session_id)) if message['type'] == 'SESSION_CREATE': if fqdn in self._sessions: old_session_id = self._sessions[fqdn] self._abort_session(fqdn, "new session created", old_session_id, session_id) else: self._sessions[fqdn] = session_id elif message['type'] == 'SESSION_TERMINATE': # An agent has timed out or restarted, we're being told its session is now dead if message['fqdn'] in self._sessions: self._abort_session(fqdn, "session terminated", message['session_id']) elif message['type'] == 'SESSION_TERMINATE_ALL': # The http_agent service has restarted, all sessions are now over for fqdn, session in self._sessions.items(): self._abort_session(fqdn, "all sessions terminated", session) else: rpc_response = message['body'] if rpc_response['type'] != 'ACTION_COMPLETE': log.error("Unexpected type '%s'" % rpc_response['type']) return if fqdn in self._sessions and self._sessions[fqdn] != session_id: log.info("AgentRpcMessenger.on_rx: cancelling session %s/%s (replaced by %s)" % (fqdn, self._sessions[fqdn], session_id)) self._abort_session(fqdn, "session cancelled", self._sessions[fqdn]) HttpAgentRpc().reset_session(fqdn, AgentRpcMessenger.PLUGIN_NAME, session_id) elif fqdn in self._sessions: log.info("AgentRpcMessenger.on_rx: good session %s/%s" % (fqdn, session_id)) # Find this RPC and complete it try: rpc = self._session_rpcs[session_id][rpc_response['id']] except KeyError: if rpc_response['id'] in self._cancelled_rpcs: log.debug("Response received from a cancelled RPC (id: %s)", rpc_response['id']) else: log.error("Response received from UNKNOWN RPC of (id: %s)", rpc_response['id']) else: del self._session_rpcs[session_id][rpc_response['id']] rpc.exception = rpc_response['exception'] rpc.result = rpc_response['result'] rpc.subprocesses = rpc_response['subprocesses'] log.info("AgentRpcMessenger.on_rx: completing rpc %s" % rpc.id) rpc.complete.set() else: log.info("AgentRpcMessenger.on_rx: unknown session %s/%s" % (fqdn, session_id)) # A session I never heard of? HttpAgentRpc().reset_session(fqdn, AgentRpcMessenger.PLUGIN_NAME, session_id)
def run(self): try: HttpAgentRpc().reset_plugin_sessions(AgentRpcMessenger.PLUGIN_NAME) except RpcTimeout: # Assume this means that the http_agent service isn't running: this # is acceptable, as our goal of there not being any sessions is # already the case. log.warning("Unable to reset %s sessions" % AgentRpcMessenger.PLUGIN_NAME) self._action_runner_rx_queue.serve(session_callback = self.on_rx) log.info("AgentRpcMessenger.complete")
def test_revoked_cert(self): """Check that I'm bounced if my certificate is revoked""" # Initially should be able to to operations like open a session self._open_session() HttpAgentRpc().remove_host(self.host.fqdn) # After revokation any access should be bounced response = self._post([]) self.assertEqual(response.status_code, 403) response = self._get() self.assertEqual(response.status_code, 403)
def on_message(self, message): with self._processing_lock: fqdn = message["fqdn"] assert message["plugin"] == self._plugin_name if message["type"] != "DATA": # We are session aware in that we check sequence numbers etc, but # we don't actually require any actions on SESSION_CREATE or # SESSION_TERMINATE. assert message["type"] in ("SESSION_CREATE", "SESSION_TERMINATE") return try: host = ManagedHost.objects.get(fqdn=fqdn) except ManagedHost.DoesNotExist: log.error("Received agent message for non-existent host %s" % fqdn) return log.debug("Received agent message for %s/%s/%s" % (fqdn, message["plugin"], message["session_id"])) existing_session = self._sessions.get(host.id, None) if existing_session is None: if message["session_seq"] == 0: # No existing session, start a new one log.info("New session") self._sessions[host.id] = Session( message["session_id"], self._create_plugin_instance(host)) else: # Partway through a session, reset it log.info( "Nonzero counter for new (to me) session, resetting") HttpAgentRpc().reset_session(fqdn, self._plugin_name, message["session_id"]) return elif existing_session.id != message["session_id"]: if message["session_seq"] == 0: # Existing session to be replaced with this one log.info("Replacing session %s/%s with %s/%s" % (self._plugin_name, existing_session.id, self._plugin_name, message["session_id"])) self._sessions[host.id] = Session( message["session_id"], self._create_plugin_instance(host)) else: # Existing session is dead, new session is not at zero, must send a reset log.info( "Nonzero counter for new (to me) replacement session, resetting" ) del self._sessions[host.id] HttpAgentRpc().reset_session(fqdn, self._plugin_name, message["session_id"]) return else: if message["session_seq"] == existing_session.seq + 1: # Continuation of session pass else: # Got out of sequence, reset it log.info( "Out of sequence message (seq %s, expected %s), resetting" % (message["session_seq"], existing_session.seq + 1)) del self._sessions[host.id] HttpAgentRpc().reset_session(fqdn, self._plugin_name, message["session_id"]) return session = self._sessions.get(host.id, None) try: if message["session_seq"] == 0: session.plugin.do_agent_session_start(message["body"]) else: session.seq += 1 session.plugin.do_agent_session_continue(message["body"]) except Exception: exc_info = sys.exc_info() backtrace = "\n".join( traceback.format_exception(*(exc_info or sys.exc_info()))) log.error("Exception in agent session for %s from %s: %s" % (self._plugin_name, host, backtrace)) log.error("Data: %s" % message["body"]) HttpAgentRpc().reset_session(fqdn, self._plugin_name, message["session_id"])