vici: Add a python vici command execution handler

This commit is contained in:
Björn Schuberg
2015-03-18 13:59:13 +01:00
committed by Martin Willi
parent b26e142863
commit 8c089cddef
2 changed files with 134 additions and 1 deletions
@@ -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."""
"""Encountered an unexpected byte sequence or missing element type."""
class SessionException(Exception):
"""Session request exception."""
@@ -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