Beispiel #1
0
	def test_get_will_reuse_existent_connector(self):
		c = SMPPServerStatsCollector()
		self.assertTrue('test_get_will_reuse_existent_connector' not in c.connectors)

		i1 = c.get(cid = 'test_get_will_reuse_existent_connector')
		i2 = c.get(cid = 'test_get_will_reuse_existent_connector')
		self.assertEqual(i1, i2)
Beispiel #2
0
    def setUp(self):
        SMPPClientTestCases.setUp(self)

        # Re-init stats singleton collector
        created_at = SMPPServerStatsCollector().get(cid=self.smpps_config.id).get('created_at')
        SMPPServerStatsCollector().get(cid=self.smpps_config.id).init()
        SMPPServerStatsCollector().get(cid=self.smpps_config.id).set('created_at', created_at)
Beispiel #3
0
	def test_exceptions(self):
		stats = SMPPServerStatsCollector().get(cid = 'test_exceptions')
		self.assertRaises(KeyNotFound, stats.get, 'anything')
		self.assertRaises(KeyNotFound, stats.set, 'anything', 22)
		self.assertRaises(KeyNotFound, stats.inc, 'anything')

		stats.set('created_at', datetime.now())
		self.assertRaises(KeyNotIncrementable, stats.inc, 'created_at')
Beispiel #4
0
    def setUp(self):
        SMPPClientTestCases.setUp(self)

        # Provision a user and default route into RouterPB
        # Add throughput limit on user
        self.foo = User('u1', Group('test'), 'username', 'password')
        self.foo.mt_credential.setQuota('smpps_throughput', 2)
        self.c1 = SmppClientConnector(id_generator())
        self.defaultroute = DefaultRoute(self.c1)
        self.provision_user_defaultroute(user=self.foo, defaultroute=self.defaultroute)

        self.stats = SMPPServerStatsCollector().get(cid=self.smpps_config.id)
Beispiel #5
0
	def test_stats_inc(self):
		stats = SMPPServerStatsCollector().get(cid = 'test_stats_inc')
		self.assertEqual(stats.get('bound_tx_count'), 0)

		stats.inc('bound_tx_count')
		self.assertEqual(stats.get('bound_tx_count'), 1)

		stats.inc('bound_tx_count', 5)
		self.assertEqual(stats.get('bound_tx_count'), 6)
Beispiel #6
0
    def test_stats(self):
        stats = SMPPServerStatsCollector().get(cid='test_stats')

        self.assertEqual(
            stats._stats, {
                'bind_rx_count': 0,
                'bind_trx_count': 0,
                'bind_tx_count': 0,
                'bound_rx_count': 0,
                'bound_trx_count': 0,
                'bound_tx_count': 0,
                'connect_count': 0,
                'connected_count': 0,
                'created_at': 0,
                'data_sm_count': 0,
                'deliver_sm_count': 0,
                'disconnect_count': 0,
                'elink_count': 0,
                'last_received_elink_at': 0,
                'last_received_pdu_at': 0,
                'last_sent_pdu_at': 0,
                'other_submit_error_count': 0,
                'submit_sm_count': 0,
                'submit_sm_request_count': 0,
                'throttling_error_count': 0,
                'unbind_count': 0,
                'interceptor_count': 0,
                'interceptor_error_count': 0,
            })
Beispiel #7
0
	def test_is_singleton(self):
		i1 = SMPPServerStatsCollector()
		i2 = SMPPServerStatsCollector()
		self.assertEqual(i1, i2)

		i1.get(cid = 'testing').set('bind_rx_count', 100)

		self.assertEqual(i1.get(cid = 'testing').get('bind_rx_count'),
						 i2.get(cid = 'testing').get('bind_rx_count'),
						 )
Beispiel #8
0
    def test_02_enquire_link(self):
        self.smppc_config.enquireLinkTimerSecs = 1
        stats = SMPPServerStatsCollector().get(cid=self.smpps_config.id)

        self.assertEqual(stats.get('last_received_elink_at'), 0)

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Wait some secs in order to receive some elinks
        yield waitFor(6)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

        self.assertTrue(type(stats.get('last_received_elink_at')) == datetime)
Beispiel #9
0
    def test_02_enquire_link(self):
        self.smppc_config.enquireLinkTimerSecs = 1
        stats = SMPPServerStatsCollector().get(cid = self.smpps_config.id)

        self.assertEqual(stats.get('last_received_elink_at'), 0)

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Wait some secs in order to receive some elinks
        yield waitFor(6)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

        self.assertTrue(type(stats.get('last_received_elink_at')) == datetime)
Beispiel #10
0
    def smppsapi(self, arg, opts):
        """As of Jasmin's 0.6 version, there can be only one SMPPs API, the smpp server id
        is set for later evolution to handle multiple APIs, this is why the id is hard coded
        to 'smpps_01'.
        """
        sc = SMPPServerStatsCollector()
        headers = ["#Item", "Value"]

        table = []
        for k, v in sc.get('smpps_01')._stats.iteritems():
            row = []
            row.append('#%s' % k)
            if k[-3:] == '_at':
                row.append(formatDateTime(v))
            else:
                row.append(v)

            table.append(row)

        self.protocol.sendData(tabulate(table, headers, tablefmt="plain", numalign="left").encode('ascii'))
Beispiel #11
0
    def smppsapi(self, arg, opts):
        """As of Jasmin's 0.6 version, there can be only one SMPPs API, the smpp server id
        is set for later evolution to handle multiple APIs, this is why the id is hard coded
        to 'smpps_01'.
        """
        sc = SMPPServerStatsCollector()
        headers = ["#Item", "Value"]

        table = []
        for k, v in sc.get('smpps_01').getStats().iteritems():
            row = []
            row.append('#%s' % k)
            if k[-3:] == '_at':
                row.append(formatDateTime(v))
            else:
                row.append(v)

            table.append(row)

        self.protocol.sendData(tabulate(table, headers, tablefmt="plain", numalign="left").encode('ascii'))
Beispiel #12
0
    def setUp(self):
        SMPPClientTestCases.setUp(self)

        # Provision a user and default route into RouterPB
        # Add throughput limit on user
        self.foo = User('u1', Group('test'), 'username', 'password')
        self.foo.mt_credential.setQuota('smpps_throughput', 2)
        self.c1 = SmppClientConnector(id_generator())
        self.defaultroute = DefaultRoute(self.c1)
        self.provision_user_defaultroute(user = self.foo, defaultroute = self.defaultroute)

        self.stats = SMPPServerStatsCollector().get(cid = self.smpps_config.id)
Beispiel #13
0
    def __init__(self,
                 config,
                 auth_portal,
                 RouterPB=None,
                 SMPPClientManagerPB=None,
                 interceptorpb_client=None):
        self.config = config
        # A dict of protocol instances for each of the current connections,
        # indexed by system_id
        self.bound_connections = {}
        self._auth_portal = auth_portal
        self.RouterPB = RouterPB
        self.SMPPClientManagerPB = SMPPClientManagerPB
        self.interceptorpb_client = interceptorpb_client

        # Setup statistics collector
        self.stats = SMPPServerStatsCollector().get(cid=self.config.id)
        self.stats.set('created_at', datetime.now())

        # Set up a dedicated logger
        self.log = logging.getLogger(LOG_CATEGORY_SERVER_BASE +
                                     ".%s" % config.id)
        if len(self.log.handlers) != 1:
            self.log.setLevel(config.log_level)
            if 'stdout' in self.config.log_file:
                handler = logging.StreamHandler(sys.stdout)
            else:
                handler = TimedRotatingFileHandler(
                    filename=self.config.log_file, when=self.config.log_rotate)
            formatter = logging.Formatter(config.log_format,
                                          config.log_date_format)
            handler.setFormatter(formatter)
            self.log.addHandler(handler)
            self.log.propagate = False

        self.msgHandler = self.submit_sm_event_interceptor
Beispiel #14
0
    def setUp(self):
        if hasattr(self, 'ipb_client'):
            yield SMPPClientTestCases.setUp(self, interceptorpb_client=self.ipb_client)
        else:
            yield SMPPClientTestCases.setUp(self)

        # Connect to RouterPB
        yield self.connect('127.0.0.1', self.pbPort)
        # Provision mt interceptor
        self.mt_interceptor = MTInterceptorScript(self.script)
        yield self.mtinterceptor_add(DefaultInterceptor(self.mt_interceptor), 0)
        # Disconnect from RouterPB
        self.disconnect()

        # Get stats singletons
        self.stats_smpps = SMPPServerStatsCollector().get(cid=self.smpps_config.id)
Beispiel #15
0
	def test_stats(self):
		stats = SMPPServerStatsCollector().get(cid = 'test_stats')

		self.assertEqual(stats._stats, {
			'created_at': 0,
			'last_received_pdu_at': 0,
			'last_sent_pdu_at': 0,
			'last_received_elink_at': 0,
			'connected_count': 0,
			'connect_count': 0,
			'disconnect_count': 0,
			'bound_trx_count': 0,
			'bound_rx_count': 0,
			'bound_tx_count': 0,
			'bind_trx_count': 0,
			'bind_rx_count': 0,
			'bind_tx_count': 0,
			'unbind_count': 0,
		})
Beispiel #16
0
    def test_01_smpps_basic(self):
        "A simple test of _some_ stats parameters"
        stats = SMPPServerStatsCollector().get(cid=self.smpps_config.id)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertEqual(stats.get('last_received_pdu_at'), 0)
        self.assertEqual(stats.get('last_sent_pdu_at'), 0)
        self.assertEqual(stats.get('connected_count'), 0)
        self.assertEqual(stats.get('connect_count'), 0)
        self.assertEqual(stats.get('disconnect_count'), 0)
        self.assertEqual(stats.get('bound_trx_count'), 0)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 0)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 0)

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState,
                         SMPPSessionStates.BOUND_TRX)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertTrue(type(stats.get('last_received_pdu_at')) == datetime)
        self.assertTrue(type(stats.get('last_sent_pdu_at')) == datetime)
        self.assertEqual(stats.get('connected_count'), 1)
        self.assertEqual(stats.get('connect_count'), 1)
        self.assertEqual(stats.get('disconnect_count'), 0)
        self.assertEqual(stats.get('bound_trx_count'), 1)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 1)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 0)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState,
                         SMPPSessionStates.UNBOUND)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertTrue(type(stats.get('last_received_pdu_at')) == datetime)
        self.assertTrue(type(stats.get('last_sent_pdu_at')) == datetime)
        self.assertEqual(stats.get('connected_count'), 0)
        self.assertEqual(stats.get('connect_count'), 1)
        self.assertEqual(stats.get('disconnect_count'), 1)
        self.assertEqual(stats.get('bound_trx_count'), 0)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 1)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 1)
Beispiel #17
0
	def test_get_will_create_connector(self):
		c = SMPPServerStatsCollector()
		self.assertTrue('test_get_will_create_connector' not in c.connectors)

		c.get(cid = 'test_get_will_create_connector')
		self.assertTrue('test_get_will_create_connector' in c.connectors)
Beispiel #18
0
    def test_01_smpps_basic(self):
        "A simple test of _some_ stats parameters"
        stats = SMPPServerStatsCollector().get(cid = self.smpps_config.id)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertEqual(stats.get('last_received_pdu_at'), 0)
        self.assertEqual(stats.get('last_sent_pdu_at'), 0)
        self.assertEqual(stats.get('connected_count'), 0)
        self.assertEqual(stats.get('connect_count'), 0)
        self.assertEqual(stats.get('disconnect_count'), 0)
        self.assertEqual(stats.get('bound_trx_count'), 0)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 0)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 0)

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertTrue(type(stats.get('last_received_pdu_at')) == datetime)
        self.assertTrue(type(stats.get('last_sent_pdu_at')) == datetime)
        self.assertEqual(stats.get('connected_count'), 1)
        self.assertEqual(stats.get('connect_count'), 1)
        self.assertEqual(stats.get('disconnect_count'), 0)
        self.assertEqual(stats.get('bound_trx_count'), 1)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 1)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 0)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

        self.assertTrue(type(stats.get('created_at')) == datetime)
        self.assertTrue(type(stats.get('last_received_pdu_at')) == datetime)
        self.assertTrue(type(stats.get('last_sent_pdu_at')) == datetime)
        self.assertEqual(stats.get('connected_count'), 0)
        self.assertEqual(stats.get('connect_count'), 1)
        self.assertEqual(stats.get('disconnect_count'), 1)
        self.assertEqual(stats.get('bound_trx_count'), 0)
        self.assertEqual(stats.get('bound_rx_count'), 0)
        self.assertEqual(stats.get('bound_tx_count'), 0)
        self.assertEqual(stats.get('bind_trx_count'), 1)
        self.assertEqual(stats.get('bind_rx_count'), 0)
        self.assertEqual(stats.get('bind_tx_count'), 0)
        self.assertEqual(stats.get('unbind_count'), 1)
Beispiel #19
0
class SmppsStatsTestCases(SMPPClientTestCases):
    def setUp(self):
        SMPPClientTestCases.setUp(self)

        # Provision a user and default route into RouterPB
        # Add throughput limit on user
        self.foo = User('u1', Group('test'), 'username', 'password')
        self.foo.mt_credential.setQuota('smpps_throughput', 2)
        self.c1 = SmppClientConnector(id_generator())
        self.defaultroute = DefaultRoute(self.c1)
        self.provision_user_defaultroute(user=self.foo, defaultroute=self.defaultroute)

        self.stats = SMPPServerStatsCollector().get(cid=self.smpps_config.id)

    @defer.inlineCallbacks
    def test_elink_count(self):
        self.smppc_config.enquireLinkTimerSecs = 1

        # Save the 'before' value
        _elink_count = self.stats.get('elink_count')

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Wait
        yield waitFor(5)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

        # Asserts
        self.assertEqual(_elink_count + 4, self.stats.get('elink_count'))

    @defer.inlineCallbacks
    def test_throttling_error_count(self):
        """In this test it is demonstrated the
        difference between submit_sm_request_count and submit_sm_count:
        * submit_sm_request_count: is the number of submit_sm requested
        * submit_sm_count: is number of submit_sm accepted (replied with ESME_ROK)
        """

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _submit_sm_request_count = self.stats.get('submit_sm_request_count')
        _throttling_error_count = self.stats.get('throttling_error_count')
        _other_submit_error_count = self.stats.get('other_submit_error_count')
        _submit_sm_count = self.stats.get('submit_sm_count')

        # SMPPClient > SMPPServer
        for _ in range(50):
            yield self.smppc_factory.lastProto.sendDataRequest(self.SubmitSmPDU)
            yield waitFor(0.1)

        # Assert after
        self.assertEqual(self.stats.get('submit_sm_request_count'), _submit_sm_request_count + 50)
        self.assertEqual(self.stats.get('other_submit_error_count'), _other_submit_error_count)
        self.assertLess(self.stats.get('throttling_error_count'), _submit_sm_request_count + 50)
        self.assertGreater(self.stats.get('throttling_error_count'), 0)
        self.assertGreater(self.stats.get('submit_sm_count'), _submit_sm_count)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_other_submit_error_count(self):
        """Send a submit_sm wile bound_rx: will get a resp with ESME_RINVBNDSTS
        Will also ensure
        """

        self.smppc_config.bindOperation = 'receiver'

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_RX)

        # Save the 'before' value
        _other_submit_error_count = self.stats.get('other_submit_error_count')
        _throttling_error_count = self.stats.get('throttling_error_count')

        # SMPPClient > SMPPServer
        yield self.smppc_factory.lastProto.sendDataRequest(self.SubmitSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('other_submit_error_count'), _other_submit_error_count + 1)
        self.assertEqual(self.stats.get('throttling_error_count'), _throttling_error_count)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_deliver_sm_count(self):
        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _deliver_sm_count = self.stats.get('deliver_sm_count')

        # SMPPServer > SMPPClient
        yield self.smpps_factory.lastProto.sendDataRequest(self.DeliverSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('deliver_sm_count'), _deliver_sm_count + 1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_data_sm_count(self):
        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _data_sm_count = self.stats.get('data_sm_count')

        # SMPPServer > SMPPClient
        yield self.smpps_factory.lastProto.sendDataRequest(self.DataSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('data_sm_count'), _data_sm_count + 1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)
Beispiel #20
0
class SMPPServerFactory(_SMPPServerFactory):
    protocol = SMPPServerProtocol

    def __init__(self,
                 config,
                 auth_portal,
                 RouterPB=None,
                 SMPPClientManagerPB=None,
                 interceptorpb_client=None):
        self.config = config
        # A dict of protocol instances for each of the current connections,
        # indexed by system_id
        self.bound_connections = {}
        self._auth_portal = auth_portal
        self.RouterPB = RouterPB
        self.SMPPClientManagerPB = SMPPClientManagerPB
        self.interceptorpb_client = interceptorpb_client

        # Setup statistics collector
        self.stats = SMPPServerStatsCollector().get(cid=self.config.id)
        self.stats.set('created_at', datetime.now())

        # Set up a dedicated logger
        self.log = logging.getLogger(LOG_CATEGORY_SERVER_BASE +
                                     ".%s" % config.id)
        if len(self.log.handlers) != 1:
            self.log.setLevel(config.log_level)
            if 'stdout' in self.config.log_file:
                handler = logging.StreamHandler(sys.stdout)
            else:
                handler = TimedRotatingFileHandler(
                    filename=self.config.log_file, when=self.config.log_rotate)
            formatter = logging.Formatter(config.log_format,
                                          config.log_date_format)
            handler.setFormatter(formatter)
            self.log.addHandler(handler)
            self.log.propagate = False

        self.msgHandler = self.submit_sm_event_interceptor

    def addInterceptorPBClient(self, interceptorpb_client):
        self.interceptorpb_client = interceptorpb_client

        self.log.info('Added Interceptor to SMPPServerFactory')

    def submit_sm_event_interceptor(self, system_id, *args):
        """Intercept submit_sm before handing it to self.submit_sm_event
        """

        self.log.debug('Intercepting submit_sm event for system_id: %s',
                       system_id)

        # Args validation
        if len(args) != 2:
            self.log.error('(submit_sm_event/%s) Invalid args: %s', system_id,
                           args)
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[1], PDURequest):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a PDURequest: %s',
                system_id, args[1])
            raise SubmitSmInvalidArgsError()
        if args[1].id != CommandId.submit_sm:
            self.log.error(
                '(submit_sm_event/%s) Received a non submit_sm command id: %s',
                system_id, args[1].id)
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[0], SMPPServerProtocol):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a SMPPServerProtocol: %s',
                system_id, args[0])
            raise SubmitSmInvalidArgsError()

        proto = args[0]
        user = proto.user
        SubmitSmPDU = args[1]

        # Update CnxStatus
        user.getCnxStatus().smpps['submit_sm_request_count'] += 1

        # Basic validation
        if len(SubmitSmPDU.params['destination_addr']
               ) < 1 or SubmitSmPDU.params['destination_addr'] is None:
            self.log.error(
                '(submit_sm_event/%s) SubmitSmPDU have no defined destination_addr',
                system_id)
            raise SubmitSmWithoutDestinationAddrError()

        # Make Credential validation
        v = SmppsCredentialValidator('Send', user, SubmitSmPDU)
        v.validate()

        # Update SubmitSmPDU by default values from user MtMessagingCredential
        SubmitSmPDU = v.updatePDUWithUserDefaults(SubmitSmPDU)

        if self.RouterPB is None:
            self.log.error(
                '(submit_sm_event_interceptor/%s) RouterPB not set: submit_sm will not be routed',
                system_id)
            return

        # Prepare for interception then routing
        routable = RoutableSubmitSm(SubmitSmPDU, user)

        # Interception inline
        # @TODO: make Interception in a thread, just like httpapi interception
        interceptor = self.RouterPB.getMTInterceptionTable().getInterceptorFor(
            routable)
        if interceptor is not None:
            self.log.debug(
                "RouterPB selected %s interceptor for this SubmitSmPDU",
                interceptor)
            if self.interceptorpb_client is None:
                self.stats.inc('interceptor_error_count')
                self.log.error("InterceptorPB not set !")
                raise InterceptorNotSetError('InterceptorPB not set !')
            if not self.interceptorpb_client.isConnected:
                self.stats.inc('interceptor_error_count')
                self.log.error("InterceptorPB not connected !")
                raise InterceptorNotConnectedError(
                    'InterceptorPB not connected !')

            script = interceptor.getScript()
            self.log.debug("Interceptor script loaded: %s", script)

            # Run !
            d = self.interceptorpb_client.run_script(script, routable)
            d.addCallback(self.submit_sm_post_interception,
                          system_id=system_id,
                          proto=proto)
            d.addErrback(self.submit_sm_post_interception)
            return d
        else:
            return self.submit_sm_post_interception(routable=routable,
                                                    system_id=system_id,
                                                    proto=proto)

    def submit_sm_post_interception(self, *args, **kw):
        """This event handler will deliver the submit_sm to the right smppc connector.
        Note that Jasmin deliver submit_sm messages like this:
        - from httpapi to smppc (handled in jasmin.protocols.http.server)
        - from smpps to smppc (this event handler)

        Note: This event handler MUST behave exactly like jasmin.protocols.http.server.Send.render
        """

        try:
            # Init message id & status
            message_id = None
            status = None

            # Post interception:
            if len(args) == 1:
                if isinstance(args[0], bool) and not args[0]:
                    self.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Failed running interception script, got a False return.'
                    )
                    raise InterceptorRunError(
                        'Failed running interception script, check log for details'
                    )
                elif isinstance(args[0], dict) and args[0]['smpp_status'] > 0:
                    self.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Interceptor script returned %s smpp_status error.',
                        args[0]['smpp_status'])
                    raise SubmitSmInterceptionError(
                        code=args[0]['smpp_status'])
                elif isinstance(args[0], dict) and args[0]['smpp_status'] == 0:
                    self.stats.inc('interceptor_count')
                    self.log.info(
                        'Interceptor script returned %s success smpp_status.',
                        args[0]['smpp_status'])
                    # Do we have a message_id returned from interceptor ?
                    if 'message_id' in args[0]['extra']:
                        message_id = str(args[0]['extra']['message_id'])
                    raise SubmitSmInterceptionSuccess()
                elif isinstance(args[0], (str, bytes)):
                    self.stats.inc('interceptor_count')
                    routable = pickle.loads(args[0])
                else:
                    self.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Failed running interception script, got the following return: %s',
                        args[0])
                    raise InterceptorRunError(
                        'Failed running interception script, got the following return: %s'
                        % args[0])
            else:
                routable = kw['routable']

            system_id = kw['system_id']
            proto = kw['proto']

            self.log.debug(
                'Handling submit_sm_post_interception event for system_id: %s',
                system_id)

            # Get the route
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.log.error(
                    "No route matched from user %s for SubmitSmPDU: %s",
                    routable.user, routable.pdu)
                raise SubmitSmRouteNotFoundError()

            # Get connector from selected route
            self.log.debug("RouterPB selected %s route for this SubmitSmPDU",
                           route)
            routedConnector = route.getConnector()
            # Is it a failover route ? then check for a bound connector, otherwise don't route
            # The failover route requires at least one connector to be up, no message enqueuing will
            # occur otherwise.
            if repr(route) == 'FailoverMTRoute':
                self.log.debug(
                    'Selected route is a failover, will ensure connector is bound:'
                )
                while True:
                    c = self.SMPPClientManagerPB.perspective_connector_details(
                        routedConnector.cid)
                    if c:
                        self.log.debug('Connector [%s] is: %s',
                                       routedConnector.cid, c['session_state'])
                    else:
                        self.log.debug('Connector [%s] is not found',
                                       routedConnector.cid)

                    if c and c['session_state'][:6] == 'BOUND_':
                        # Choose this connector
                        break
                    else:
                        # Check next connector, None if no more connectors are available
                        routedConnector = route.getConnector()
                        if routedConnector is None:
                            break

            if routedConnector is None:
                self.log.error(
                    "Failover route has no bound connector to handle SubmitSmPDU: %s",
                    routable.pdu)
                raise SubmitSmRoutingError()

            # QoS throttling
            if (routable.user.mt_credential.getQuota('smpps_throughput') and
                    routable.user.mt_credential.getQuota('smpps_throughput') >=
                    0 and
                    routable.user.getCnxStatus().smpps['qos_last_submit_sm_at']
                    != 0):
                qos_throughput_second = 1 / float(
                    routable.user.mt_credential.getQuota('smpps_throughput'))
                qos_throughput_ysecond_td = timedelta(
                    microseconds=qos_throughput_second * 1000000)
                qos_delay = datetime.now() - routable.user.getCnxStatus(
                ).smpps['qos_last_submit_sm_at']
                if qos_delay < qos_throughput_ysecond_td:
                    self.log.error(
                        "QoS: submit_sm_event is faster (%s) than fixed throughput (%s) for user (%s), rejecting message.",
                        qos_delay, qos_throughput_ysecond_td, routable.user)

                    raise SubmitSmThroughputExceededError()
            routable.user.getCnxStatus(
            ).smpps['qos_last_submit_sm_at'] = datetime.now()

            # Pre-sending submit_sm: Billing processing
            bill = route.getBillFor(routable.user)
            self.log.debug(
                "SubmitSmBill [bid:%s] [ttlamounts:%s] generated for this SubmitSmPDU",
                bill.bid, bill.getTotalAmounts())
            charging_requirements = []
            u_balance = routable.user.mt_credential.getQuota('balance')
            u_subsm_count = routable.user.mt_credential.getQuota(
                'submit_sm_count')
            if u_balance is not None and bill.getTotalAmounts() > 0:
                # Ensure user have enough balance to pay submit_sm and submit_sm_resp
                charging_requirements.append({
                    'condition':
                    bill.getTotalAmounts() <= u_balance,
                    'error_message':
                    'Not enough balance (%s) for charging: %s' %
                    (u_balance, bill.getTotalAmounts())
                })
            if u_subsm_count is not None:
                # Ensure user have enough submit_sm_count to to cover the bill action (decrement_submit_sm_count)
                charging_requirements.append({
                    'condition':
                    bill.getAction('decrement_submit_sm_count') <=
                    u_subsm_count,
                    'error_message':
                    'Not enough submit_sm_count (%s) for charging: %s' %
                    (u_subsm_count,
                     bill.getAction('decrement_submit_sm_count'))
                })

            if self.RouterPB.chargeUserForSubmitSms(
                    routable.user, bill,
                    requirements=charging_requirements) is None:
                self.log.error(
                    'Charging user %s failed, [bid:%s] [ttlamounts:%s] (check router log)',
                    routable.user, bill.bid, bill.getTotalAmounts())
                raise SubmitSmChargingError()

            # Get priority value from SubmitSmPDU to pass to SMPPClientManagerPB.perspective_submit_sm()
            priority = 0
            if routable.pdu.params['priority_flag'] is not None:
                priority = routable.pdu.params['priority_flag']._value_

            if self.SMPPClientManagerPB is None:
                self.log.error(
                    '(submit_sm_event/%s) SMPPClientManagerPB not set: submit_sm will not be submitted',
                    system_id)
                return

            ########################################################
            # Send SubmitSmPDU through smpp client manager PB server
            self.log.debug(
                "Connector '%s' is set to be a route for this SubmitSmPDU",
                routedConnector.cid)
            c = self.SMPPClientManagerPB.perspective_submit_sm(
                uid=routable.user.uid,
                cid=routedConnector.cid,
                SubmitSmPDU=routable.pdu,
                submit_sm_bill=bill,
                priority=priority,
                pickled=False,
                source_connector=proto)

            if not hasattr(c, 'result'):
                self.log.error(
                    'Failed to send SubmitSmPDU to [cid:%s], got: %s',
                    routedConnector.cid, c)
                raise SubmitSmRoutingError()

            # Build final response
            if not c.result:
                self.log.error('Failed to send SubmitSmPDU to [cid:%s]',
                               routedConnector.cid)
                raise SubmitSmRoutingError()

            # Otherwise, message_id is defined on ESME_ROK
            message_id = c.result
        except (SubmitSmInterceptionError, SubmitSmInterceptionSuccess,
                InterceptorRunError, SubmitSmRouteNotFoundError,
                SubmitSmThroughputExceededError, SubmitSmChargingError,
                SubmitSmRoutingError) as e:
            # Known exception handling
            status = e.status
        except Exception as e:
            # Unknown exception handling
            self.log.critical('Got an unknown exception: %s', e)
            status = CommandStatus.ESME_RUNKNOWNERR
        else:
            self.log.debug('SubmitSmPDU sent to [cid:%s], result = %s',
                           routedConnector.cid, message_id)

            # Do not log text for privacy reasons
            # Added in #691
            if self.config.log_privacy:
                logged_content = '** %s byte content **' % len(
                    routable.pdu.params['short_message'])
            else:
                if isinstance(routable.pdu.params['short_message'], bytes):
                    logged_content = '%r' % re.sub(
                        rb'[^\x20-\x7E]+', b'.',
                        routable.pdu.params['short_message'])
                else:
                    logged_content = '%r' % re.sub(
                        r'[^\x20-\x7E]+', '.',
                        routable.pdu.params['short_message'])

            self.log.info(
                'SMS-MT [uid:%s] [cid:%s] [msgid:%s] [prio:%s] [from:%s] [to:%s] [content:%s]',
                routable.user.uid, routedConnector.cid, message_id, priority,
                routable.pdu.params['source_addr'],
                routable.pdu.params['destination_addr'], logged_content)
            status = CommandStatus.ESME_ROK
        finally:
            if message_id is not None:
                return DataHandlerResponse(status=status,
                                           message_id=message_id)
            elif status is not None:
                return DataHandlerResponse(status=status)

    def buildProtocol(self, addr):
        """Provision protocol with the dedicated logger
        """
        proto = _SMPPServerFactory.buildProtocol(self, addr)

        # Setup logger
        proto.log = self.log

        return proto

    def addBoundConnection(self, connection, user):
        """
        Overloading _SMPPServerFactory to remove dependency with config.systems
        Jasmin removed systems from config as everything about credentials is
        managed through User object
        """
        system_id = connection.system_id
        self.log.debug('Adding SMPP binding for %s', system_id)
        if system_id not in self.bound_connections:
            self.bound_connections[system_id] = SMPPBindManager(user)
        self.bound_connections[system_id].addBinding(connection)
        bind_type = connection.bind_type
        self.log.info("Added %s bind for '%s'. Active binds: %s.", bind_type,
                      system_id, self.getBoundConnectionCountsStr(system_id))

    def removeConnection(self, connection):
        """
        Overloading _SMPPServerFactory to remove dependency with config.systems
        Jasmin removed systems from config as everything about credentials is
        managed through User object
        """
        if connection.system_id is None:
            self.log.debug("SMPP connection attempt failed without binding.")
        else:
            system_id = connection.system_id
            bind_type = connection.bind_type
            self.bound_connections[system_id].removeBinding(connection)
            self.log.info("Dropped %s bind for '%s'. Active binds: %s.",
                          bind_type, system_id,
                          self.getBoundConnectionCountsStr(system_id))
            # If this is the last binding for this service then remove the BindManager
            if self.bound_connections[system_id].getBindingCount() == 0:
                self.bound_connections.pop(system_id)

    def canOpenNewConnection(self, user, bind_type):
        """
        Overloading _SMPPServerFactory to remove dependency with config.systems
        Jasmin removed systems from config as everything about credentials is
        managed through User object
        This method will check for authorization and quotas before allowing a new
        connection
        """
        # Can bind ?
        if not user.smpps_credential.getAuthorization('bind'):
            self.log.warning(
                'New bind rejected for username: "******", reason: authorization failure.',
                user.username)
            return False
        # Still didnt reach max_bindings ?
        elif user.smpps_credential.getQuota('max_bindings') is not None:
            bind_count = user.getCnxStatus(
            ).smpps['bound_connections_count']['bind_transmitter']
            bind_count += user.getCnxStatus(
            ).smpps['bound_connections_count']['bind_receiver']
            bind_count += user.getCnxStatus(
            ).smpps['bound_connections_count']['bind_transceiver']
            if bind_count >= user.smpps_credential.getQuota('max_bindings'):
                self.log.warning(
                    'New bind rejected for username: "******", reason: max_bindings limit reached.',
                    user.username)
                return False

        return True

    def unbindAndRemoveGateway(self, user, ban=True):
        """
        Overloading _SMPPServerFactory to remove dependency with config.systems
        Jasmin removed systems from config as everything about credentials is
        managed through User object.
        It's also adding a 'ban' parameter to optionally remove binding authorization
        for user.
        """
        if ban:
            user.smpps_credential.setAuthorization('bind', False)

        d = self.unbindGateway(user.username)
        return d
Beispiel #21
0
	def test_stats_set(self):
		stats = SMPPServerStatsCollector().get(cid = 'test_stats_set')
		self.assertEqual(stats.get('bind_rx_count'), 0)

		stats.set('bind_rx_count', 2)
		self.assertEqual(stats.get('bind_rx_count'), 2)
Beispiel #22
0
class SmppsStatsTestCases(SMPPClientTestCases):

    def setUp(self):
        SMPPClientTestCases.setUp(self)

        # Provision a user and default route into RouterPB
        # Add throughput limit on user
        self.foo = User('u1', Group('test'), 'username', 'password')
        self.foo.mt_credential.setQuota('smpps_throughput', 2)
        self.c1 = SmppClientConnector(id_generator())
        self.defaultroute = DefaultRoute(self.c1)
        self.provision_user_defaultroute(user = self.foo, defaultroute = self.defaultroute)

        self.stats = SMPPServerStatsCollector().get(cid = self.smpps_config.id)

    @defer.inlineCallbacks
    def test_elink_count(self):
        self.smppc_config.enquireLinkTimerSecs = 1

        # Save the 'before' value
        _elink_count = self.stats.get('elink_count')

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Wait
        yield waitFor(5)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

        # Asserts
        self.assertEqual(_elink_count+4, self.stats.get('elink_count'))

    @defer.inlineCallbacks
    def test_throttling_error_count(self):
        """In this test it is demonstrated the
        difference between submit_sm_request_count and submit_sm_count:
        * submit_sm_request_count: is the number of submit_sm requested
        * submit_sm_count: is number of submit_sm accepted (replied with ESME_ROK)
        """

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _submit_sm_request_count = self.stats.get('submit_sm_request_count')
        _throttling_error_count = self.stats.get('throttling_error_count')
        _other_submit_error_count = self.stats.get('other_submit_error_count')
        _submit_sm_count = self.stats.get('submit_sm_count')

        # SMPPClient > SMPPServer
        for _ in range(50):
            yield self.smppc_factory.lastProto.sendDataRequest(self.SubmitSmPDU)
            yield waitFor(0.1)

        # Assert after
        self.assertEqual(self.stats.get('submit_sm_request_count'), _submit_sm_request_count+50)
        self.assertEqual(self.stats.get('other_submit_error_count'), _other_submit_error_count)
        self.assertLess(self.stats.get('throttling_error_count'), _submit_sm_request_count+50)
        self.assertGreater(self.stats.get('throttling_error_count'), 0)
        self.assertGreater(self.stats.get('submit_sm_count'), _submit_sm_count)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_other_submit_error_count(self):
        """Send a submit_sm wile bound_rx: will get a resp with ESME_RINVBNDSTS
        Will also ensure
        """

        self.smppc_config.bindOperation = 'receiver'

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_RX)

        # Save the 'before' value
        _other_submit_error_count = self.stats.get('other_submit_error_count')
        _throttling_error_count = self.stats.get('throttling_error_count')

        # SMPPClient > SMPPServer
        yield self.smppc_factory.lastProto.sendDataRequest(self.SubmitSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('other_submit_error_count'), _other_submit_error_count+1)
        self.assertEqual(self.stats.get('throttling_error_count'), _throttling_error_count)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_deliver_sm_count(self):
        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _deliver_sm_count = self.stats.get('deliver_sm_count')

        # SMPPServer > SMPPClient
        yield self.smpps_factory.lastProto.sendDataRequest(self.DeliverSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('deliver_sm_count'), _deliver_sm_count+1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)

    @defer.inlineCallbacks
    def test_data_sm_count(self):
        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.BOUND_TRX)

        # Save the 'before' value
        _data_sm_count = self.stats.get('data_sm_count')

        # SMPPServer > SMPPClient
        yield self.smpps_factory.lastProto.sendDataRequest(self.DataSmPDU)

        # Assert after
        self.assertEqual(self.stats.get('data_sm_count'), _data_sm_count+1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState, SMPPSessionStates.UNBOUND)