From: Björn Schuberg Date: Sat, 14 Feb 2015 14:53:25 +0000 (+0100) Subject: vici: Add a python vici command execution handler X-Git-Tag: 5.3.0rc1~28^2~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8c089cddef38217cf555aa8183d3621830bb6044;p=thirdparty%2Fstrongswan.git vici: Add a python vici command execution handler --- diff --git a/src/libcharon/plugins/vici/python/vici/exception.py b/src/libcharon/plugins/vici/python/vici/exception.py index 25d73490b4..89d76ab809 100644 --- a/src/libcharon/plugins/vici/python/vici/exception.py +++ b/src/libcharon/plugins/vici/python/vici/exception.py @@ -1,4 +1,7 @@ """Exception types that may be thrown by this library.""" class DeserializationException(Exception): - """Encountered an unexpected byte sequence or missing element type.""" \ No newline at end of file + """Encountered an unexpected byte sequence or missing element type.""" + +class SessionException(Exception): + """Session request exception.""" \ No newline at end of file diff --git a/src/libcharon/plugins/vici/python/vici/session.py b/src/libcharon/plugins/vici/python/vici/session.py new file mode 100644 index 0000000000..ef708feafb --- /dev/null +++ b/src/libcharon/plugins/vici/python/vici/session.py @@ -0,0 +1,130 @@ +import collections + +from .exception import SessionException +from .protocol import Packet, Message + + +class SessionHandler(object): + """Handles client command execution requests over vici.""" + + def __init__(self, transport): + self.transport = transport + self.log_events = collections.deque() + + def _communicate(self, packet): + """Send packet over transport and parse response. + + :param packet: packet to send + :type packet: :py:class:`vici.protocol.Packet` + :return: parsed packet in a tuple with message type and payload + :rtype: :py:class:`collections.namedtuple` + """ + self.transport.send(packet) + return self._read() + + def request(self, command, message=None): + """Send command request with an optional message. + + :param command: command to send + :type command: str + :param message: message (optional) + :type message: str + :return: command result + :rtype: dict + """ + if message is not None: + message = Message.serialize(message) + packet = Packet.request(command, message) + response = self._communicate(packet) + + if response.response_type != Packet.CMD_RESPONSE: + raise SessionException( + "Unexpected response type {type}, " + "expected '{response}' (CMD_RESPONSE)".format( + type=response.response_type, + response=Packet.CMD_RESPONSE + ) + ) + + return Message.deserialize(response.payload) + + def streamed_request(self, command, event_stream_type, message=None): + """Send command request and collect and return all emitted events. + + :param command: command to send + :type command: str + :param event_stream_type: event type emitted on command execution + :type event_stream_type: str + :param message: message (optional) + :type message: str + :return: a pair of the command result and a list of emitted events + :rtype: tuple + """ + result = [] + + if message is not None: + message = Message.serialize(message) + + # subscribe to event stream + packet = Packet.register_event(event_stream_type) + response = self._communicate(packet) + + if response.response_type != Packet.EVENT_CONFIRM: + raise SessionException( + "Unexpected response type {type}, " + "expected '{confirm}' (EVENT_CONFIRM)".format( + type=response.response_type, + confirm=Packet.EVENT_CONFIRM, + ) + ) + + # issue command, and read any event messages + packet = Packet.request(command, message) + self.transport.send(packet) + response = self._read() + while response.response_type == Packet.EVENT: + result.append(Message.deserialize(response.payload)) + response = self._read() + + if response.response_type == Packet.CMD_RESPONSE: + response_message = Message.deserialize(response.payload) + else: + raise SessionException( + "Unexpected response type {type}, " + "expected '{response}' (CMD_RESPONSE)".format( + type=response.response_type, + response=Packet.CMD_RESPONSE + ) + ) + + # unsubscribe from event stream + packet = Packet.unregister_event(event_stream_type) + response = self._communicate(packet) + if response.response_type != Packet.EVENT_CONFIRM: + raise SessionException( + "Unexpected response type {type}, " + "expected '{confirm}' (EVENT_CONFIRM)".format( + type=response.response_type, + confirm=Packet.EVENT_CONFIRM, + ) + ) + + return (response_message, result) + + def _read(self): + """Get next packet from transport. + + :return: parsed packet in a tuple with message type and payload + :rtype: :py:class:`collections.namedtuple` + """ + raw_response = self.transport.receive() + response = Packet.parse(raw_response) + + # FIXME + if response.response_type == Packet.EVENT and response.event_type == "log": + # queue up any debug log messages, and get next + self.log_events.append(response) + # do something? + self._read() + else: + return response