vici: Add a python vici command execution handler
This commit is contained in:
committed by
Martin Willi
parent
b26e142863
commit
8c089cddef
@@ -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
|
||||
Reference in New Issue
Block a user