def assert_routing_entries(self, routing_table, expected_entries):
     self.assertEqual(
         sorted(routing_table.entries()),
         sorted(
             (GoConnector.parse(str(sc)), se, GoConnector.parse(str(dc)), de) for sc, se, dc, de in expected_entries
         ),
     )
Example #2
0
    def process_outbound(self, config, msg, connector_name):
        """Process an outbound message.

        Outbound messages can be from:

        * conversations
        * routers
        * the opt-out worker
        * the billing worker

        And may go to:

        * routers
        * transports
        * the billing worker
        """
        log.debug("Processing outbound: %s" % (msg,))
        msg_mdh = self.get_metadata_helper(msg)
        msg_mdh.set_user_account(config.user_account_key)

        connector_type = self.connector_type(connector_name)
        src_conn = self.acquire_source(msg, connector_type, self.OUTBOUND)

        if self.billing_outbound_connector:
            if connector_type in (self.CONVERSATION, self.ROUTER):
                msg_mdh.reset_paid()
            elif connector_type == self.OPT_OUT:
                tag = yield self.tag_for_reply(msg)
                yield self.publish_outbound_to_billing(config, msg, tag)
                return
            elif connector_type == self.BILLING:
                yield self.publish_outbound_from_billing(config, msg)
                return
        else:
            if connector_type == self.OPT_OUT:
                yield self.publish_outbound_optout(config, msg)
                return

        target = self.find_target(config, msg, src_conn)
        if target is None:
            raise NoTargetError(
                "No target found for outbound message from '%s': %s" % (
                    connector_name, msg), msg)

        if self.billing_outbound_connector:
            target_conn = GoConnector.parse(target[0])
            if target_conn.ctype == target_conn.TRANSPORT_TAG:
                tag = [target_conn.tagpool, target_conn.tagname]
                yield self.publish_outbound_to_billing(config, msg, tag)
                return

        dst_connector_name, dst_endpoint = yield self.set_destination(
            msg, target, self.OUTBOUND)

        yield self.publish_outbound(msg, dst_connector_name, dst_endpoint)
Example #3
0
    def process_outbound(self, config, msg, connector_name):
        """Process an outbound message.

        Outbound messages can be from:

        * conversations
        * routers
        * the opt-out worker
        * the billing worker

        And may go to:

        * routers
        * transports
        * the billing worker
        """
        log.debug("Processing outbound: %s" % (msg, ))
        msg_mdh = self.get_metadata_helper(msg)
        msg_mdh.set_user_account(config.user_account_key)

        connector_type = self.connector_type(connector_name)
        src_conn = self.acquire_source(msg, connector_type, self.OUTBOUND)

        if self.billing_outbound_connector:
            if connector_type in (self.CONVERSATION, self.ROUTER):
                msg_mdh.reset_paid()
            elif connector_type == self.OPT_OUT:
                tag = yield self.tag_for_reply(msg)
                yield self.publish_outbound_to_billing(config, msg, tag)
                return
            elif connector_type == self.BILLING:
                yield self.publish_outbound_from_billing(config, msg)
                return
        else:
            if connector_type == self.OPT_OUT:
                yield self.publish_outbound_optout(config, msg)
                return

        target = self.find_target(config, msg, src_conn)
        if target is None:
            raise NoTargetError(
                "No target found for outbound message from '%s': %s" %
                (connector_name, msg), msg)

        if self.billing_outbound_connector:
            target_conn = GoConnector.parse(target[0])
            if target_conn.ctype == target_conn.TRANSPORT_TAG:
                tag = [target_conn.tagpool, target_conn.tagname]
                yield self.publish_outbound_to_billing(config, msg, tag)
                return

        dst_connector_name, dst_endpoint = yield self.set_destination(
            msg, target, self.OUTBOUND)

        yield self.publish_outbound(msg, dst_connector_name, dst_endpoint)
Example #4
0
 def handle_remove(self, user_api, options):
     src_conn, src_endpoint, dst_conn, dst_endpoint = options['remove']
     account = user_api.get_user_account()
     if account.routing_table is None:
         raise CommandError("No routing table found.")
     target = account.routing_table.lookup_target(src_conn, src_endpoint)
     if target is None:
         raise CommandError("No routing entry found for (%s, %s)." %
                            (src_conn, src_endpoint))
     elif target != [GoConnector.parse(dst_conn), dst_endpoint]:
         raise CommandError(
             "Existing entry (%s, %s) does not match (%s, %s)." %
             (target[0], target[1], dst_conn, dst_endpoint))
     account.routing_table.remove_entry(src_conn, src_endpoint)
     try:
         user_api.validate_routing_table(account)
     except Exception as e:
         raise CommandError(e)
     account.save()
     self.stdout.write("Routing table entry removed.\n")
 def handle_remove(self, user_api, options):
     src_conn, src_endpoint, dst_conn, dst_endpoint = options['remove']
     account = user_api.get_user_account()
     if account.routing_table is None:
         raise CommandError("No routing table found.")
     target = account.routing_table.lookup_target(src_conn, src_endpoint)
     if target is None:
         raise CommandError("No routing entry found for (%s, %s)." % (
             src_conn, src_endpoint))
     elif target != [GoConnector.parse(dst_conn), dst_endpoint]:
         raise CommandError(
             "Existing entry (%s, %s) does not match (%s, %s)." % (
                 target[0], target[1], dst_conn, dst_endpoint))
     account.routing_table.remove_entry(src_conn, src_endpoint)
     try:
         user_api.validate_routing_table(account)
     except Exception as e:
         raise CommandError(e)
     account.save()
     self.stdout.write("Routing table entry removed.\n")
 def test_lookup_source(self):
     rt = self.make_rt()
     self.assertEqual(rt.lookup_source(self.CHANNEL_2, "default2"),
                      [GoConnector.parse(self.CONV_1), "default1.1"])
     self.assertEqual(rt.lookup_source(self.CHANNEL_3, "default3"),
                      [GoConnector.parse(self.CONV_1), "default1.2"])
 def test_lookup_targets(self):
     rt = self.make_rt()
     self.assertEqual(sorted(rt.lookup_targets(self.CONV_1)), [
         ("default1.1", [GoConnector.parse(self.CHANNEL_2), "default2"]),
         ("default1.2", [GoConnector.parse(self.CHANNEL_3), "default3"]),
     ])
Example #8
0
 def test_lookup_target(self):
     rt = self.make_rt()
     self.assertEqual(rt.lookup_target(self.CONV_1, "default1.1"),
                      [GoConnector.parse(self.CHANNEL_2), "default2"])
     self.assertEqual(rt.lookup_target(self.CONV_1, "default1.2"),
                      [GoConnector.parse(self.CHANNEL_3), "default3"])
Example #9
0
 def mkconn(thing):
     if isinstance(thing, basestring):
         return GoConnector.parse(thing)
     else:
         # Assume it's a conversation/channel/router.
         return thing.get_connector()
Example #10
0
 def test_lookup_sources(self):
     rt = self.make_rt()
     self.assertEqual(sorted(rt.lookup_sources(self.CHANNEL_3)), [
         ("default3", [GoConnector.parse(self.CONV_1), "default1.2"]),
     ])
Example #11
0
    def set_destination(self, msg, target, direction, push_hops=True):
        """Parse a target `(str(go_connector), endpoint)` pair and determine
        the corresponding dispatcher connector to publish on. Set any
        appropriate Go helper_metadata required by the destination.

        Raises `UnroutableMessageError` if the parsed `GoConnector` has a
        connector type not approriate to the message direction.

        Note: `str(go_connector)` is what is stored in Go routing tables.
        """
        msg_mdh = self.get_metadata_helper(msg)
        conn = GoConnector.parse(target[0])

        if direction == self.INBOUND:
            allowed_types = (
                self.CONVERSATION, self.ROUTER, self.OPT_OUT, self.BILLING)
        else:
            allowed_types = (
                self.ROUTER, self.TRANSPORT_TAG, self.BILLING)

        if conn.ctype not in allowed_types:
            raise UnroutableMessageError(
                "Destination connector of invalid type: %s" % conn, msg)

        if conn.ctype == conn.CONVERSATION:
            msg_mdh.set_conversation_info(conn.conv_type, conn.conv_key)
            dst_connector_name = self.get_application_connector(conn.conv_type)

        elif conn.ctype == conn.ROUTER:
            msg_mdh.set_router_info(conn.router_type, conn.router_key)
            dst_connector_name = self.get_router_connector(
                conn.router_type, self.router_direction(direction))

        elif conn.ctype == conn.TRANSPORT_TAG:
            msg_mdh.set_tag([conn.tagpool, conn.tagname])
            tagpool_metadata = yield msg_mdh.get_tagpool_metadata()
            transport_name = tagpool_metadata.get('transport_name')
            if transport_name is None:
                raise UnroutableMessageError(
                    "No transport name found for tagpool %r"
                    % conn.tagpool, msg)
            if self.connector_type(transport_name) != self.TRANSPORT_TAG:
                raise UnroutableMessageError(
                    "Transport name %r found in tagpool metadata for pool"
                    " %r is invalid." % (transport_name, conn.tagpool), msg)
            dst_connector_name = transport_name
            msg['transport_name'] = transport_name

            transport_type = tagpool_metadata.get('transport_type')
            if transport_type is not None:
                msg['transport_type'] = transport_type
            else:
                log.error(
                    "No transport type found for tagpool %r while routing %s"
                    % (conn.tagpool, msg))

        elif conn.ctype == conn.OPT_OUT:
            dst_connector_name = self.opt_out_connector

        elif conn.ctype == conn.BILLING:
            if direction == self.INBOUND:
                dst_connector_name = self.billing_inbound_connector
            else:
                dst_connector_name = self.billing_outbound_connector

        else:
            raise UnroutableMessageError(
                "Serious error. Reached apparently unreachable state"
                " in which destination connector type is valid but"
                " unknown. Bad connector is: %s" % conn, msg)

        if push_hops:
            rmeta = RoutingMetadata(msg)
            rmeta.push_destination(str(conn), target[1])
        returnValue((dst_connector_name, target[1]))
Example #12
0
 def test_parse_opt_out_connector(self):
     c = GoConnector.parse("OPT_OUT")
     self.assertEqual(c.ctype, GoConnector.OPT_OUT)
Example #13
0
 def test_parse_router_connector(self):
     c = GoConnector.parse("ROUTER:rb_type_1:12345:OUTBOUND")
     self.assertEqual(c.ctype, GoConnector.ROUTER)
     self.assertEqual(c.router_type, "rb_type_1")
     self.assertEqual(c.router_key, "12345")
     self.assertEqual(c.direction, GoConnector.OUTBOUND)
Example #14
0
 def test_parse_opt_out_connector(self):
     c = GoConnector.parse("OPT_OUT")
     self.assertEqual(c.ctype, GoConnector.OPT_OUT)
Example #15
0
 def assert_connectors(self, connectors, expected_connectors):
     self.assertEqual(sorted(connectors), sorted(
         GoConnector.parse(str(conn)) for conn in expected_connectors))
Example #16
0
 def test_parse_transport_tag_connector(self):
     c = GoConnector.parse("TRANSPORT_TAG:tagpool_1:tag_1")
     self.assertEqual(c.ctype, GoConnector.TRANSPORT_TAG)
     self.assertEqual(c.tagpool, "tagpool_1")
     self.assertEqual(c.tagname, "tag_1")
Example #17
0
 def test_parse_router_connector(self):
     c = GoConnector.parse("ROUTER:rb_type_1:12345:OUTBOUND")
     self.assertEqual(c.ctype, GoConnector.ROUTER)
     self.assertEqual(c.router_type, "rb_type_1")
     self.assertEqual(c.router_key, "12345")
     self.assertEqual(c.direction, GoConnector.OUTBOUND)
Example #18
0
 def test_parse_conversation_connector(self):
     c = GoConnector.parse("CONVERSATION:conv_type_1:12345")
     self.assertEqual(c.ctype, GoConnector.CONVERSATION)
     self.assertEqual(c.conv_type, "conv_type_1")
     self.assertEqual(c.conv_key, "12345")
Example #19
0
 def test_lookup_sources(self):
     rt = self.make_rt()
     self.assertEqual(sorted(rt.lookup_sources(self.CHANNEL_3)), [
         ("default3", [GoConnector.parse(self.CONV_1), "default1.2"]),
     ])
Example #20
0
 def assert_connectors(self, connectors, expected_connectors):
     self.assertEqual(
         sorted(connectors),
         sorted(
             GoConnector.parse(str(conn)) for conn in expected_connectors))
Example #21
0
 def test_parse_conversation_connector(self):
     c = GoConnector.parse("CONVERSATION:conv_type_1:12345")
     self.assertEqual(c.ctype, GoConnector.CONVERSATION)
     self.assertEqual(c.conv_type, "conv_type_1")
     self.assertEqual(c.conv_key, "12345")
Example #22
0
 def test_lookup_source(self):
     rt = self.make_rt()
     self.assertEqual(rt.lookup_source(self.CHANNEL_2, "default2"),
                      [GoConnector.parse(self.CONV_1), "default1.1"])
     self.assertEqual(rt.lookup_source(self.CHANNEL_3, "default3"),
                      [GoConnector.parse(self.CONV_1), "default1.2"])
Example #23
0
 def test_parse_transport_tag_connector(self):
     c = GoConnector.parse("TRANSPORT_TAG:tagpool_1:tag_1")
     self.assertEqual(c.ctype, GoConnector.TRANSPORT_TAG)
     self.assertEqual(c.tagpool, "tagpool_1")
     self.assertEqual(c.tagname, "tag_1")
Example #24
0
 def test_lookup_targets(self):
     rt = self.make_rt()
     self.assertEqual(sorted(rt.lookup_targets(self.CONV_1)), [
         ("default1.1", [GoConnector.parse(self.CHANNEL_2), "default2"]),
         ("default1.2", [GoConnector.parse(self.CHANNEL_3), "default3"]),
     ])
Example #25
0
 def assert_routing_entries(self, routing_table, expected_entries):
     self.assertEqual(
         sorted(routing_table.entries()),
         sorted((GoConnector.parse(str(sc)), se, GoConnector.parse(str(dc)),
                 de) for sc, se, dc, de in expected_entries))
Example #26
0
 def mkconn(thing):
     if isinstance(thing, basestring):
         return GoConnector.parse(thing)
     else:
         # Assume it's a conversation/channel/router.
         return thing.get_connector()
Example #27
0
 def test_lookup_target(self):
     rt = self.make_rt()
     self.assertEqual(rt.lookup_target(self.CONV_1, "default1.1"),
                      [GoConnector.parse(self.CHANNEL_2), "default2"])
     self.assertEqual(rt.lookup_target(self.CONV_1, "default1.2"),
                      [GoConnector.parse(self.CHANNEL_3), "default3"])
Example #28
0
    def set_destination(self, msg, target, direction, push_hops=True):
        """Parse a target `(str(go_connector), endpoint)` pair and determine
        the corresponding dispatcher connector to publish on. Set any
        appropriate Go helper_metadata required by the destination.

        Raises `UnroutableMessageError` if the parsed `GoConnector` has a
        connector type not approriate to the message direction.

        Note: `str(go_connector)` is what is stored in Go routing tables.
        """
        msg_mdh = self.get_metadata_helper(msg)
        conn = GoConnector.parse(target[0])

        if direction == self.INBOUND:
            allowed_types = (self.CONVERSATION, self.ROUTER, self.OPT_OUT,
                             self.BILLING)
        else:
            allowed_types = (self.ROUTER, self.TRANSPORT_TAG, self.BILLING)

        if conn.ctype not in allowed_types:
            raise UnroutableMessageError(
                "Destination connector of invalid type: %s" % conn, msg)

        if conn.ctype == conn.CONVERSATION:
            msg_mdh.set_conversation_info(conn.conv_type, conn.conv_key)
            dst_connector_name = self.get_application_connector(conn.conv_type)

        elif conn.ctype == conn.ROUTER:
            msg_mdh.set_router_info(conn.router_type, conn.router_key)
            dst_connector_name = self.get_router_connector(
                conn.router_type, self.router_direction(direction))

        elif conn.ctype == conn.TRANSPORT_TAG:
            msg_mdh.set_tag([conn.tagpool, conn.tagname])
            tagpool_metadata = yield msg_mdh.get_tagpool_metadata()
            transport_name = tagpool_metadata.get('transport_name')
            if transport_name is None:
                raise UnroutableMessageError(
                    "No transport name found for tagpool %r" % conn.tagpool,
                    msg)
            if self.connector_type(transport_name) != self.TRANSPORT_TAG:
                raise UnroutableMessageError(
                    "Transport name %r found in tagpool metadata for pool"
                    " %r is invalid." % (transport_name, conn.tagpool), msg)
            dst_connector_name = transport_name
            msg['transport_name'] = transport_name

            transport_type = tagpool_metadata.get('transport_type')
            if transport_type is not None:
                msg['transport_type'] = transport_type
            else:
                log.error(
                    "No transport type found for tagpool %r while routing %s" %
                    (conn.tagpool, msg))

        elif conn.ctype == conn.OPT_OUT:
            dst_connector_name = self.opt_out_connector

        elif conn.ctype == conn.BILLING:
            if direction == self.INBOUND:
                dst_connector_name = self.billing_inbound_connector
            else:
                dst_connector_name = self.billing_outbound_connector

        else:
            raise UnroutableMessageError(
                "Serious error. Reached apparently unreachable state"
                " in which destination connector type is valid but"
                " unknown. Bad connector is: %s" % conn, msg)

        if push_hops:
            rmeta = RoutingMetadata(msg)
            rmeta.push_destination(str(conn), target[1])
        returnValue((dst_connector_name, target[1]))