コード例 #1
0
ファイル: core.py プロジェクト: johnjohnbear/Rammbock
 def _init_caches(self):
     self._protocol_in_progress = None
     self._protocols = {}
     self._servers = _NamedCache('server')
     self._clients = _NamedCache('client')
     self._message_stack = []
     self._header_values = {}
     self._field_values = {}
     self._message_sequence = MessageSequence()
     self._message_templates = {}
コード例 #2
0
ファイル: core.py プロジェクト: mengzr/Rammbock
 def _init_caches(self):
     self._protocol_in_progress = None
     self._protocols = {}
     self._servers = _NamedCache('server', "No servers defined!")
     self._clients = _NamedCache('client', "No clients defined!")
     self._message_stack = []
     self._header_values = {}
     self._field_values = {}
     self._message_sequence = MessageSequence()
     self._message_templates = {}
コード例 #3
0
ファイル: core.py プロジェクト: johnjohnbear/Rammbock
class RammbockCore(object):

    ROBOT_LIBRARY_SCOPE = 'GLOBAL'

    def __init__(self):
        self._init_caches()

    def _init_caches(self):
        self._protocol_in_progress = None
        self._protocols = {}
        self._servers = _NamedCache('server')
        self._clients = _NamedCache('client')
        self._message_stack = []
        self._header_values = {}
        self._field_values = {}
        self._message_sequence = MessageSequence()
        self._message_templates = {}

    @property
    def _current_container(self):
        return self._message_stack[-1]

    def reset_rammbock(self):
        """Closes all connections, deletes all servers, clients, and protocols.

        You should call this method before exiting your test run. This will
        close all the connections and the ports will therefore be available for
        reuse faster.
        """
        for client in self._clients:
            client.close()
        for server in self._servers:
            server.close()
        self._init_caches()

    def clear_message_streams(self):
        """ Resets streams and sockets of incoming messages.

        You can use this method to reuse the same connections for several
        consecutive test cases.
        """
        for client in self._clients:
            client.empty()
        for server in self._servers:
            server.empty()

    def new_protocol(self, protocol_name):
        """Start defining a new protocol template.

        All messages sent and received from a connection that uses a protocol
        have to conform to this protocol template.
        """
        if self._protocol_in_progress:
            raise Exception(
                'Can not start a new protocol definition in middle of old.')
        if protocol_name in self._protocols:
            raise Exception('Protocol %s already defined' % protocol_name)
        self._init_new_message_stack(Protocol(protocol_name))
        self._protocol_in_progress = True

    def end_protocol(self):
        """End protocol definition."""
        protocol = self._get_message_template()
        self._protocols[protocol.name] = protocol
        self._protocol_in_progress = False

    def start_udp_server(self,
                         ip,
                         port,
                         name=None,
                         timeout=None,
                         protocol=None):
        """Starts a new UDP server to given `ip` and `port`.

        Server can be given a `name`, default `timeout` and a `protocol`.

        Examples:
        | Start UDP server | 10.10.10.2 | 53 |
        | Start UDP server | 10.10.10.2 | 53 | Server1 |
        | Start UDP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start UDP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(UDPServer, ip, port, name, timeout, protocol)

    def start_tcp_server(self,
                         ip,
                         port,
                         name=None,
                         timeout=None,
                         protocol=None):
        """Starts a new TCP server to given `ip` and `port`.

        Server can be given a `name`, default `timeout` and a `protocol`.
        Notice that you have to use `Accept Connection` keyword for server to
        receive connections.

        Examples:
        | Start TCP server | 10.10.10.2 | 53 |
        | Start TCP server | 10.10.10.2 | 53 | Server1 |
        | Start TCP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(TCPServer, ip, port, name, timeout, protocol)

    def start_sctp_server(self,
                          ip,
                          port,
                          name=None,
                          timeout=None,
                          protocol=None):
        """Starts a new STCP server to given `ip` and `port`.
        pysctp (https://github.com/philpraxis/pysctp) need to be installed your system.
        Server can be given a `name`, default `timeout` and a `protocol`.
        Notice that you have to use `Accept Connection` keyword for server to
        receive connections.

        Examples:
        | Start STCP server | 10.10.10.2 | 53 |
        | Start STCP server | 10.10.10.2 | 53 | Server1 |
        | Start STCP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start STCP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(SCTPServer, ip, port, name, timeout, protocol)

    def _start_server(self,
                      server_class,
                      ip,
                      port,
                      name=None,
                      timeout=None,
                      protocol=None):
        protocol = self._get_protocol(protocol)
        server = server_class(ip=ip,
                              port=port,
                              timeout=timeout,
                              protocol=protocol)
        return self._servers.add(server, name)

    def start_udp_client(self,
                         ip=None,
                         port=None,
                         name=None,
                         timeout=None,
                         protocol=None):
        """Starts a new UDP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start UDP client |
        | Start UDP client | name=Client1 | protocol=GTPV2 |
        | Start UDP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start UDP client | timeout=5 |
        """
        self._start_client(UDPClient, ip, port, name, timeout, protocol)

    def start_tcp_client(self,
                         ip=None,
                         port=None,
                         name=None,
                         timeout=None,
                         protocol=None):
        """Starts a new TCP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start TCP client |
        | Start TCP client | name=Client1 | protocol=GTPV2 |
        | Start TCP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP client | timeout=5 |
        """
        self._start_client(TCPClient, ip, port, name, timeout, protocol)

    def start_sctp_client(self,
                          ip=None,
                          port=None,
                          name=None,
                          timeout=None,
                          protocol=None):
        """Starts a new SCTP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start TCP client |
        | Start TCP client | name=Client1 | protocol=GTPV2 |
        | Start TCP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP client | timeout=5 |
        """
        self._start_client(SCTPClient, ip, port, name, timeout, protocol)

    def _start_client(self,
                      client_class,
                      ip=None,
                      port=None,
                      name=None,
                      timeout=None,
                      protocol=None):
        protocol = self._get_protocol(protocol)
        client = client_class(timeout=timeout, protocol=protocol)
        if ip or port:
            client.set_own_ip_and_port(ip=ip, port=port)
        return self._clients.add(client, name)

    def _get_protocol(self, protocol):
        try:
            protocol = self._protocols[protocol] if protocol else None
        except KeyError:
            raise Exception("No protocol '%s' defined!" % protocol)
        return protocol

    def get_client_protocol(self, name=None):
        """Returns name of the protocol client uses or empty if client does not
        use a protocol.
        """
        return self._clients.get(name).protocol_name or ''

    def accept_connection(self, name=None, alias=None):
        """Accepts a connection to server identified by `name` or the latest
        server if `name` is empty.

        If given an `alias`, the connection is named and can be later referenced
        with that name.

        Examples:
        | Accept connection |
        | Accept connection | Server1 | my_connection |
        """
        server = self._servers.get(name)
        server.accept_connection(alias)

    def connect(self, host, port, name=None):
        """Connects a client to given `host` and `port`. If client `name` is not
        given then connects the latest client.

        Examples:
        | Connect | 127.0.0.1 | 8080 |
        | Connect | 127.0.0.1 | 8080 | Client1 |
        """
        client = self._clients.get(name)
        client.connect_to(host, port)

    def _register_send(self, sender, label, name, connection=None):
        self._message_sequence.send(name, sender.get_own_address(),
                                    sender.get_peer_address(alias=connection),
                                    sender.protocol_name, label)

    def _register_receive(self,
                          receiver,
                          label,
                          name,
                          error='',
                          connection=None):
        self._message_sequence.receive(
            name, receiver.get_own_address(),
            receiver.get_peer_address(alias=connection),
            receiver.protocol_name, label, error)

    def client_sends_binary(self, message, name=None, label=None):
        """Send raw binary `message`.

        If client `name` is not given, uses the latest client. Optional message
        `label` is shown on logs.

        Examples:
        | Client sends binary | Hello! |
        | Client sends binary | ${some binary} | Client1 | label=DebugMessage |
        """
        client, name = self._clients.get_with_name(name)
        client.send(message)
        self._register_send(client, label, name)

    # FIXME: support "send to" somehow. A new keyword?
    def server_sends_binary(self,
                            message,
                            name=None,
                            connection=None,
                            label=None):
        """Send raw binary `message`.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | Server sends binary | Hello! |
        | Server sends binary | ${some binary} | Server1 | label=DebugMessage |
        | Server sends binary | ${some binary} | connection=my_connection |
        """
        server, name = self._servers.get_with_name(name)
        server.send(message, alias=connection)
        self._register_send(server, label, name, connection=connection)

    def client_receives_binary(self, name=None, timeout=None, label=None):
        """Receive raw binary message.

        If client `name` is not given, uses the latest client. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} = | Client receives binary |
        | ${binary} = | Client receives binary | Client1 | timeout=5 |
        """
        client, name = self._clients.get_with_name(name)
        msg = client.receive(timeout=timeout)
        self._register_receive(client, label, name)
        return msg

    def server_receives_binary(self,
                               name=None,
                               timeout=None,
                               connection=None,
                               label=None):
        """Receive raw binary message.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} = | Server receives binary |
        | ${binary} = | Server receives binary | Server1 | connection=my_connection | timeout=5 |
        """
        return self.server_receives_binary_from(name,
                                                timeout,
                                                connection=connection,
                                                label=label)[0]

    def server_receives_binary_from(self,
                                    name=None,
                                    timeout=None,
                                    connection=None,
                                    label=None):
        """Receive raw binary message. Returns message, ip, and port.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} | ${ip} | ${port} = | Server receives binary from |
        | ${binary} | ${ip} | ${port} = | Server receives binary from | Server1 | connection=my_connection | timeout=5 |
        """
        server, name = self._servers.get_with_name(name)
        msg, ip, port = server.receive_from(timeout=timeout, alias=connection)
        self._register_receive(server, label, name, connection=connection)
        return msg, ip, port

    def _init_new_message_stack(self,
                                message,
                                fields=None,
                                header_fields=None):
        self._field_values = fields if fields else {}
        self._header_values = header_fields if header_fields else {}
        self._message_stack = [message]

    def new_message(self, message_name, protocol=None, *parameters):
        """Define a new message template with `message_name`.

        `protocol` has to be defined earlier with `Start Protocol Description`.
        Optional parameters are default values for message header separated with
        colon.

        Examples:
        | New message | MyMessage | MyProtocol | header_field:value |
        """
        proto = self._get_protocol(protocol)
        if not proto:
            raise Exception(
                "Protocol not defined! Please define a protocol before creating a message!"
            )
        if self._protocol_in_progress:
            raise Exception(
                "Protocol definition in progress. Please finish it before starting to define a message."
            )
        configs, fields, header_fields = self._parse_parameters(parameters)
        self._raise_error_if_configs_or_fields(configs, fields, 'New message')
        self._init_new_message_stack(
            MessageTemplate(message_name, proto, header_fields))

    def _raise_error_if_configs_or_fields(self, configs, fields, function):
        if configs or fields:
            raise AssertionError('Cannot set configs or pdu fields in %s' %
                                 function)

    def save_template(self, name):
        """Save a message template for later use with `Load template`.
        """
        template = self._get_message_template()
        template.set_as_saved()
        self._message_templates[name] = (template, self._field_values)

    def load_template(self, name, *parameters):
        """Load a message template saved with `Save template`.
        Optional parameters are default values for message header separated with
        colon.

        Examples:
        | Load Template | MyMessage | header_field:value |
        """
        configs, fields, header_fields = self._parse_parameters(parameters)
        self._raise_error_if_configs_or_fields(configs, fields,
                                               'Load template')
        template, fields = self._message_templates[name]
        self._init_new_message_stack(template, fields, header_fields)

    def get_message(self, *parameters):
        """Get encoded message.

        * Send Message -keywords are convenience methods, that will call this to
        get the message object and then send it. Optional parameters are message
        field values separated with colon.

        Examples:
        | ${msg} = | Get message |
        | ${msg} = | Get message | field_name:value |
        """
        _, message_fields, header_fields = self._get_parameters_with_defaults(
            parameters)
        return self._encode_message(message_fields, header_fields)

    def _encode_message(self, message_fields, header_fields):
        msg = self._get_message_template().encode(message_fields,
                                                  header_fields)
        logger.debug('%s' % repr(msg))
        return msg

    def _get_message_template(self):
        if len(self._message_stack) != 1:
            raise Exception(
                'Message definition not complete. %s not completed.' %
                self._current_container.name)
        return self._message_stack[0]

    def client_sends_message(self, *parameters):
        """Send a message defined with `New Message`.

        Optional parameters are client `name` separated with equals and message
        field values separated with colon. Protocol header values can be set
        with syntax header:header_field_name:value.

        Examples:
        | Client sends message |
        | Client sends message | field_name:value | field_name2:value |
        | Client sends message | name=Client1 | header:message_code:0x32 |
        """
        self._send_message(self.client_sends_binary, parameters)

    # FIXME: support "send to" somehow. A new keyword?
    def server_sends_message(self, *parameters):
        """Send a message defined with `New Message`.

        Optional parameters are server `name` and possible `connection` alias
        separated with equals and message field values separated with colon.
        Protocol header values can be set with syntax header:header_field_name:value.

        Examples:
        | Server sends message |
        | Server sends message | field_name:value | field_name2:value |
        | Server sends message | name=Server1 | connection=my_connection | header:message_code:0x32 |
        """
        self._send_message(self.server_sends_binary, parameters)

    def _send_message(self, callback, parameters):
        configs, message_fields, header_fields = self._get_parameters_with_defaults(
            parameters)
        msg = self._encode_message(message_fields, header_fields)
        callback(msg._raw, label=self._current_container.name, **configs)

    def client_receives_message(self, *parameters):
        """Receive a message with template defined using `New Message` and
        validate field values.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are client `name` and possible `timeout`
        separated with equals and message field values for validation separated
        with colon.

        Examples:
        | ${msg} = | Client receives message |
        | ${msg} = | Client receives message | name=Client1 | timeout=5 |
        | ${msg} = | Client receives message | message_field:(0|1) |
        """
        with self._receive(self._clients, *parameters) as (msg, message_fields,
                                                           header_fields):
            self._validate_message(msg, message_fields, header_fields)
            return msg

    def client_receives_without_validation(self, *parameters):
        """Receive a message with template defined using `New Message`.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are client `name` and possible `timeout`
        separated with equals.

        Examples:
        | ${msg} = | Client receives without validation |
        | ${msg} = | Client receives without validation | name=Client1 | timeout=5 |
        """
        with self._receive(self._clients, *parameters) as (msg, _, _):
            return msg

    def server_receives_message(self, *parameters):
        """Receive a message with template defined using `New Message` and
        validate field values.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are server `name`, `connection` alias and
        possible `timeout` separated with equals and message field values for
        validation separated with colon.

        Examples:
        | ${msg} = | Server receives message |
        | ${msg} = | Server receives message | name=Server1 | alias=my_connection | timeout=5 |
        | ${msg} = | Server receives message | message_field:(0|1) |
        """
        with self._receive(self._servers, *parameters) as (msg, message_fields,
                                                           header_fields):
            self._validate_message(msg, message_fields, header_fields)
            return msg

    def server_receives_without_validation(self, *parameters):
        """Receive a message with template defined using `New Message`.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are server `name` and possible `timeout`
        separated with equals.

        Examples:
        | ${msg} = | Server receives without validation |
        | ${msg} = | Server receives without validation | name=Server1 | alias=my_connection | timeout=5 |
        """
        with self._receive(self._servers, *parameters) as (msg, _, _):
            return msg

    def validate_message(self, msg, *parameters):
        """Validates given message using template defined with `New Message` and
        field values given as optional arguments.

        Examples:
        | Validate message | ${msg} |
        | Validate message | ${msg} | status:0 |
        """
        _, message_fields, header_fields = self._get_parameters_with_defaults(
            parameters)
        self._validate_message(msg, message_fields, header_fields)

    def _validate_message(self, msg, message_fields, header_fields):
        errors = self._get_message_template().validate(msg, message_fields,
                                                       header_fields)
        if errors:
            logger.info("Validation failed for %s" % repr(msg))
            logger.info('\n'.join(errors))
            raise AssertionError(errors[0])

    @contextmanager
    def _receive(self, nodes, *parameters):
        configs, message_fields, header_fields = self._get_parameters_with_defaults(
            parameters)
        node, name = nodes.get_with_name(configs.pop('name', None))
        msg = node.get_message(self._get_message_template(), **configs)
        try:
            yield msg, message_fields, header_fields
            self._register_receive(node, self._current_container.name, name)
            logger.debug("Received %s" % repr(msg))
        except AssertionError, e:
            self._register_receive(node,
                                   self._current_container.name,
                                   name,
                                   error=e.args[0])
            raise e
コード例 #4
0
ファイル: core.py プロジェクト: mengzr/Rammbock
class RammbockCore(object):

    ROBOT_LIBRARY_SCOPE = 'GLOBAL'

    def __init__(self):
        self._init_caches()

    def _init_caches(self):
        self._protocol_in_progress = None
        self._protocols = {}
        self._servers = _NamedCache('server', "No servers defined!")
        self._clients = _NamedCache('client', "No clients defined!")
        self._message_stack = []
        self._header_values = {}
        self._field_values = {}
        self._message_sequence = MessageSequence()
        self._message_templates = {}

    @property
    def _current_container(self):
        return self._message_stack[-1]

    def reset_rammbock(self):
        """Closes all connections, deletes all servers, clients, and protocols.

        You should call this method before exiting your test run. This will
        close all the connections and the ports will therefore be available for
        reuse faster.
        """
        for client in self._clients:
            client.close()
        for server in self._servers:
            server.close()
        self._init_caches()

    def clear_message_streams(self):
        """ Resets streams and sockets of incoming messages.

        You can use this method to reuse the same connections for several
        consecutive test cases.
        """
        for client in self._clients:
            client.empty()
        for server in self._servers:
            server.empty()

    def new_protocol(self, protocol_name):
        """Start defining a new protocol template.

        All messages sent and received from a connection that uses a protocol
        have to conform to this protocol template.
        """
        if self._protocol_in_progress:
            raise Exception('Can not start a new protocol definition in middle of old.')
        if protocol_name in self._protocols:
            raise Exception('Protocol %s already defined' % protocol_name)
        self._init_new_message_stack(Protocol(protocol_name))
        self._protocol_in_progress = True

    def end_protocol(self):
        """End protocol definition."""
        protocol = self._get_message_template()
        self._protocols[protocol.name] = protocol
        self._protocol_in_progress = False

    def start_udp_server(self, ip, port, name=None, timeout=None, protocol=None):
        """Starts a new UDP server to given `ip` and `port`.

        Server can be given a `name`, default `timeout` and a `protocol`.

        Examples:
        | Start UDP server | 10.10.10.2 | 53 |
        | Start UDP server | 10.10.10.2 | 53 | Server1 |
        | Start UDP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start UDP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(UDPServer, ip, port, name, timeout, protocol)

    def start_tcp_server(self, ip, port, name=None, timeout=None, protocol=None):
        """Starts a new TCP server to given `ip` and `port`.

        Server can be given a `name`, default `timeout` and a `protocol`.
        Notice that you have to use `Accept Connection` keyword for server to
        receive connections.

        Examples:
        | Start TCP server | 10.10.10.2 | 53 |
        | Start TCP server | 10.10.10.2 | 53 | Server1 |
        | Start TCP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(TCPServer, ip, port, name, timeout, protocol)

    def start_sctp_server(self, ip, port, name=None, timeout=None, protocol=None):
        """Starts a new STCP server to given `ip` and `port`.
        pysctp (https://github.com/philpraxis/pysctp) need to be installed your system.
        Server can be given a `name`, default `timeout` and a `protocol`.
        Notice that you have to use `Accept Connection` keyword for server to
        receive connections.

        Examples:
        | Start STCP server | 10.10.10.2 | 53 |
        | Start STCP server | 10.10.10.2 | 53 | Server1 |
        | Start STCP server | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start STCP server | 10.10.10.2 | 53 | timeout=5 |
        """
        self._start_server(SCTPServer, ip, port, name, timeout, protocol)

    def _start_server(self, server_class, ip, port, name=None, timeout=None, protocol=None):
        protocol = self._get_protocol(protocol)
        server = server_class(ip=ip, port=port, timeout=timeout, protocol=protocol)
        return self._servers.add(server, name)

    def start_udp_client(self, ip=None, port=None, name=None, timeout=None, protocol=None):
        """Starts a new UDP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start UDP client |
        | Start UDP client | name=Client1 | protocol=GTPV2 |
        | Start UDP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start UDP client | timeout=5 |
        """
        self._start_client(UDPClient, ip, port, name, timeout, protocol)

    def start_tcp_client(self, ip=None, port=None, name=None, timeout=None, protocol=None):
        """Starts a new TCP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start TCP client |
        | Start TCP client | name=Client1 | protocol=GTPV2 |
        | Start TCP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP client | timeout=5 |
        """
        self._start_client(TCPClient, ip, port, name, timeout, protocol)

    def start_sctp_client(self, ip=None, port=None, name=None, timeout=None, protocol=None):
        """Starts a new SCTP client.

        Client can be optionally given `ip` and `port` to bind to, as well as
        `name`, default `timeout` and a `protocol`. You should use `Connect`
        keyword to connect client to a host.

        Examples:
        | Start TCP client |
        | Start TCP client | name=Client1 | protocol=GTPV2 |
        | Start TCP client | 10.10.10.2 | 53 | name=Server1 | protocol=GTPV2 |
        | Start TCP client | timeout=5 |
        """
        self._start_client(SCTPClient, ip, port, name, timeout, protocol)

    def _start_client(self, client_class, ip=None, port=None, name=None, timeout=None, protocol=None):
        protocol = self._get_protocol(protocol)
        client = client_class(timeout=timeout, protocol=protocol)
        if ip or port:
            client.set_own_ip_and_port(ip=ip, port=port)
        return self._clients.add(client, name)

    def _get_protocol(self, protocol):
        try:
            protocol = self._protocols[protocol] if protocol else None
        except KeyError:
            raise Exception("No protocol '%s' defined!" % protocol)
        return protocol

    def get_client_protocol(self, name=None):
        """Returns name of the protocol client uses or empty if client does not
        use a protocol.
        """
        return self._clients.get(name).protocol_name or ''

    def accept_connection(self, name=None, alias=None):
        """Accepts a connection to server identified by `name` or the latest
        server if `name` is empty.

        If given an `alias`, the connection is named and can be later referenced
        with that name.

        Examples:
        | Accept connection |
        | Accept connection | Server1 | my_connection |
        """
        server = self._servers.get(name)
        server.accept_connection(alias)

    def connect(self, host, port, name=None):
        """Connects a client to given `host` and `port`. If client `name` is not
        given then connects the latest client.

        Examples:
        | Connect | 127.0.0.1 | 8080 |
        | Connect | 127.0.0.1 | 8080 | Client1 |
        """
        client = self._clients.get(name)
        client.connect_to(host, port)

    def _register_send(self, sender, label, name, connection=None):
        self._message_sequence.send(name, sender.get_own_address(), sender.get_peer_address(alias=connection),
                                    sender.protocol_name, label)

    def _register_receive(self, receiver, label, name, error='', connection=None):
        self._message_sequence.receive(name, receiver.get_own_address(), receiver.get_peer_address(alias=connection),
                                       receiver.protocol_name, label, error)

    def client_sends_binary(self, message, name=None, label=None):
        """Send raw binary `message`.

        If client `name` is not given, uses the latest client. Optional message
        `label` is shown on logs.

        Examples:
        | Client sends binary | Hello! |
        | Client sends binary | ${some binary} | Client1 | label=DebugMessage |
        """
        client, name = self._clients.get_with_name(name)
        client.send(message)
        self._register_send(client, label, name)

    # FIXME: support "send to" somehow. A new keyword?
    def server_sends_binary(self, message, name=None, connection=None, label=None):
        """Send raw binary `message`.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | Server sends binary | Hello! |
        | Server sends binary | ${some binary} | Server1 | label=DebugMessage |
        | Server sends binary | ${some binary} | connection=my_connection |
        """
        server, name = self._servers.get_with_name(name)
        server.send(message, alias=connection)
        self._register_send(server, label, name, connection=connection)

    def client_receives_binary(self, name=None, timeout=None, label=None):
        """Receive raw binary message.

        If client `name` is not given, uses the latest client. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} = | Client receives binary |
        | ${binary} = | Client receives binary | Client1 | timeout=5 |
        """
        client, name = self._clients.get_with_name(name)
        msg = client.receive(timeout=timeout)
        self._register_receive(client, label, name)
        return msg

    def server_receives_binary(self, name=None, timeout=None, connection=None, label=None):
        """Receive raw binary message.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} = | Server receives binary |
        | ${binary} = | Server receives binary | Server1 | connection=my_connection | timeout=5 |
        """
        return self.server_receives_binary_from(name, timeout, connection=connection, label=label)[0]

    def server_receives_binary_from(self, name=None, timeout=None, connection=None, label=None):
        """Receive raw binary message. Returns message, ip, and port.

        If server `name` is not given, uses the latest server. Optional message
        `label` is shown on logs.

        Examples:
        | ${binary} | ${ip} | ${port} = | Server receives binary from |
        | ${binary} | ${ip} | ${port} = | Server receives binary from | Server1 | connection=my_connection | timeout=5 |
        """
        server, name = self._servers.get_with_name(name)
        msg, ip, port = server.receive_from(timeout=timeout, alias=connection)
        self._register_receive(server, label, name, connection=connection)
        return msg, ip, port

    def _init_new_message_stack(self, message, fields=None, header_fields=None):
        self._field_values = fields if fields else {}
        self._header_values = header_fields if header_fields else {}
        self._message_stack = [message]

    def new_message(self, message_name, protocol=None, *parameters):
        """Define a new message template with `message_name`.

        `protocol` has to be defined earlier with `Start Protocol Description`.
        Optional parameters are default values for message header separated with
        colon.

        Examples:
        | New message | MyMessage | MyProtocol | header_field:value |
        """
        proto = self._get_protocol(protocol)
        if not proto:
            raise Exception("Protocol not defined! Please define a protocol before creating a message!")
        if self._protocol_in_progress:
            raise Exception("Protocol definition in progress. Please finish it before starting to define a message.")
        configs, fields, header_fields = self._parse_parameters(parameters)
        self._raise_error_if_configs_or_fields(configs, fields, 'New message')
        self._init_new_message_stack(MessageTemplate(message_name, proto, header_fields))

    def _raise_error_if_configs_or_fields(self, configs, fields, function):
        if configs or fields:
            raise AssertionError('Cannot set configs or pdu fields in %s' % function)

    def save_template(self, name):
        """Save a message template for later use with `Load template`.
        """
        template = self._get_message_template()
        template.set_as_saved()
        self._message_templates[name] = (template, self._field_values)

    def load_template(self, name, *parameters):
        """Load a message template saved with `Save template`.
        Optional parameters are default values for message header separated with
        colon.

        Examples:
        | Load Template | MyMessage | header_field:value |
        """
        template, fields, header_fields = self._set_templates_fields_and_header_fields(name, parameters)
        self._init_new_message_stack(template, fields, header_fields)

    def load_copy_of_template(self, name, *parameters):
        """Load a copy of message template saved with `Save template` when originally saved values need to be preserved
        from test to test.
        Optional parameters are default values for message header separated with
        colon.

        Examples:
        | Load Copy Of Template | MyMessage | header_field:value |
        """
        template, fields, header_fields = self._set_templates_fields_and_header_fields(name, parameters)
        copy_of_template = copy.deepcopy(template)
        copy_of_fields = copy.deepcopy(fields)
        self._init_new_message_stack(copy_of_template, copy_of_fields, header_fields)

    def _set_templates_fields_and_header_fields(self, name, parameters):
        configs, fields, header_fields = self._parse_parameters(parameters)
        self._raise_error_if_configs_or_fields(configs, fields, 'Load template')
        template, fields = self._message_templates[name]
        return template, fields, header_fields

    def get_message(self, *parameters):
        """Get encoded message.

        * Send Message -keywords are convenience methods, that will call this to
        get the message object and then send it. Optional parameters are message
        field values separated with colon.

        Examples:
        | ${msg} = | Get message |
        | ${msg} = | Get message | field_name:value |
        """
        _, message_fields, header_fields = self._get_parameters_with_defaults(parameters)
        return self._encode_message(message_fields, header_fields)

    def _encode_message(self, message_fields, header_fields):
        msg = self._get_message_template().encode(message_fields, header_fields)
        logger.debug('%s' % repr(msg))
        return msg

    def _get_message_template(self):
        if len(self._message_stack) != 1:
            raise Exception('Message definition not complete. %s not completed.' % self._current_container.name)
        return self._message_stack[0]

    def client_sends_message(self, *parameters):
        """Send a message defined with `New Message`.

        Optional parameters are client `name` separated with equals and message
        field values separated with colon. Protocol header values can be set
        with syntax header:header_field_name:value.

        Examples:
        | Client sends message |
        | Client sends message | field_name:value | field_name2:value |
        | Client sends message | name=Client1 | header:message_code:0x32 |
        """
        self._send_message(self.client_sends_binary, parameters)

    # FIXME: support "send to" somehow. A new keyword?
    def server_sends_message(self, *parameters):
        """Send a message defined with `New Message`.

        Optional parameters are server `name` and possible `connection` alias
        separated with equals and message field values separated with colon.
        Protocol header values can be set with syntax header:header_field_name:value.

        Examples:
        | Server sends message |
        | Server sends message | field_name:value | field_name2:value |
        | Server sends message | name=Server1 | connection=my_connection | header:message_code:0x32 |
        """
        self._send_message(self.server_sends_binary, parameters)

    def _send_message(self, callback, parameters):
        configs, message_fields, header_fields = self._get_parameters_with_defaults(parameters)
        msg = self._encode_message(message_fields, header_fields)
        callback(msg._raw, label=self._current_container.name, **configs)

    def client_receives_message(self, *parameters):
        """Receive a message with template defined using `New Message` and
        validate field values.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are client `name` and possible `timeout`
        separated with equals and message field values for validation separated
        with colon.

        Examples:
        | ${msg} = | Client receives message |
        | ${msg} = | Client receives message | name=Client1 | timeout=5 |
        | ${msg} = | Client receives message | message_field:(0|1) |
        """
        with self._receive(self._clients, *parameters) as (msg, message_fields, header_fields):
            self._validate_message(msg, message_fields, header_fields)
            return msg

    def client_receives_without_validation(self, *parameters):
        """Receive a message with template defined using `New Message`.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are client `name` and possible `timeout`
        separated with equals.

        Examples:
        | ${msg} = | Client receives without validation |
        | ${msg} = | Client receives without validation | name=Client1 | timeout=5 |
        """
        with self._receive(self._clients, *parameters) as (msg, _, _):
            return msg

    def server_receives_message(self, *parameters):
        """Receive a message with template defined using `New Message` and
        validate field values.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are server `name`, `connection` alias and
        possible `timeout` separated with equals and message field values for
        validation separated with colon.

        Examples:
        | ${msg} = | Server receives message |
        | ${msg} = | Server receives message | name=Server1 | alias=my_connection | timeout=5 |
        | ${msg} = | Server receives message | message_field:(0|1) |
        """
        with self._receive(self._servers, *parameters) as (msg, message_fields, header_fields):
            self._validate_message(msg, message_fields, header_fields)
            return msg

    def server_receives_without_validation(self, *parameters):
        """Receive a message with template defined using `New Message`.

        Message template has to be defined with `New Message` before calling
        this. Optional parameters are server `name` and possible `timeout`
        separated with equals.

        Examples:
        | ${msg} = | Server receives without validation |
        | ${msg} = | Server receives without validation | name=Server1 | alias=my_connection | timeout=5 |
        """
        with self._receive(self._servers, *parameters) as (msg, _, _):
            return msg

    def validate_message(self, msg, *parameters):
        """Validates given message using template defined with `New Message` and
        field values given as optional arguments.

        Examples:
        | Validate message | ${msg} |
        | Validate message | ${msg} | status:0 |
        """
        _, message_fields, header_fields = self._get_parameters_with_defaults(parameters)
        self._validate_message(msg, message_fields, header_fields)

    def _validate_message(self, msg, message_fields, header_fields):
        errors = self._get_message_template().validate(msg, message_fields, header_fields)
        if errors:
            logger.info("Validation failed for %s" % repr(msg))
            logger.info('\n'.join(errors))
            raise AssertionError(errors[0])

    @contextmanager
    def _receive(self, nodes, *parameters):
        configs, message_fields, header_fields = self._get_parameters_with_defaults(parameters)
        node, name = nodes.get_with_name(configs.pop('name', None))
        msg = node.get_message(self._get_message_template(), **configs)
        try:
            yield msg, message_fields, header_fields
            self._register_receive(node, self._current_container.name, name)
            logger.debug("Received %s" % repr(msg))
        except AssertionError, e:
            self._register_receive(node, self._current_container.name, name, error=e.args[0])
            raise e