Skip to content

Commit

Permalink
Merge pull request #161 from zoufou/v0.6-beta
Browse files Browse the repository at this point in the history
v0.6b17
  • Loading branch information
farirat committed May 22, 2015
2 parents 59caabe + 76ffd18 commit 744635d
Show file tree
Hide file tree
Showing 34 changed files with 1,131 additions and 253 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ python:
# Command to install dependencies
install:
- python setup.py sdist
- sudo pip install dist/jasmin-0.6b16.tar.gz
- sudo pip install dist/jasmin-0.6b17.tar.gz
# Commands to run tests:
script:
# Add jasmind to system autostartup:
Expand Down
2 changes: 1 addition & 1 deletion jasmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

MAJOR = 0
MINOR = 6
PATCH = 16
PATCH = 17
META = 'b'

def get_version():
Expand Down
8 changes: 6 additions & 2 deletions jasmin/managers/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ def __init__(self, config_file = None):
ConfigFile.__init__(self, config_file)

self.publish_submit_sm_resp = self._getbool('sm-listener',
'publish_submit_sm_resp',
True)
'publish_submit_sm_resp',
False)

self.smpp_receipt_on_success_submit_sm_resp = self._getbool('sm-listener',
'smpp_receipt_on_success_submit_sm_resp',
False)

self.log_level = logging.getLevelName(self._get('sm-listener',
'log_level',
Expand Down
27 changes: 15 additions & 12 deletions jasmin/managers/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,18 +353,21 @@ def submit_sm_resp_event(self, r, amqpMessage):
registered_delivery,
system_id))

content = DLRContentForSmpps(str(r.response.status),
msgid,
system_id,
source_addr,
destination_addr,
sub_date)

routing_key = 'dlr_thrower.smpps'
self.log.debug("Publishing DLRContentForSmpps[%s] with routing_key[%s]" % (msgid, routing_key))
yield self.amqpBroker.publish(exchange='messaging',
routing_key=routing_key,
content=content)
if (r.response.status != CommandStatus.ESME_ROK or
(r.response.status == CommandStatus.ESME_ROK and self.config.smpp_receipt_on_success_submit_sm_resp)):
# Send back a receipt (by throwing deliver_sm or data_sm)
content = DLRContentForSmpps(str(r.response.status),
msgid,
system_id,
source_addr,
destination_addr,
sub_date)

routing_key = 'dlr_thrower.smpps'
self.log.debug("Publishing DLRContentForSmpps[%s] with routing_key[%s]" % (msgid, routing_key))
yield self.amqpBroker.publish(exchange='messaging',
routing_key=routing_key,
content=content)

# Map received submit_sm_resp's message_id to the msg for later rceipt handling
self.log.debug('Mapping smpp msgid: %s to queue msgid: %s, expiring in %s' % (
Expand Down
23 changes: 0 additions & 23 deletions jasmin/managers/test/test_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,6 @@ def test_connector_config(self):
class ClientConnectorSubmitSmTestCases(SMSCSimulatorRecorder):
receivedSubmitSmResp = None

def submit_sm_callback(self, message):
self.receivedSubmitSmResp = pickle.loads(message.content.body)

@defer.inlineCallbacks
def setUp(self):
yield SMSCSimulatorRecorder.setUp(self)
Expand Down Expand Up @@ -575,13 +572,6 @@ def test_submitSm(self):
else:
time.sleep(0.2)

# Listen on the submit.sm.resp queue
routing_key_submit_sm_resp = 'submit.sm.resp.%s' % self.defaultConfig.id
consumerTag = 'test_submitSm'
yield self.amqpBroker.chan.basic_consume(queue=routing_key_submit_sm_resp, no_ack=True, consumer_tag=consumerTag)
queue = yield self.amqpBroker.client.queue(consumerTag)
queue.get().addCallback(self.submit_sm_callback)

# Send submit_sm
assertionKey = str(randint(10000, 99999999999))
SentSubmitSmPDU = copy.copy(self.SubmitSmPDU)
Expand Down Expand Up @@ -611,9 +601,6 @@ def test_submitSm(self):
self.assertEqual(ReceivedSubmitSmPDU.params['short_message'], assertionKey)
# @todo: Should be a real uuid pattern testing
self.assertApproximates(len(msgid), 35, 5)
# Submit_sm_resp was returned
self.assertTrue(self.receivedSubmitSmResp is not None)
self.assertIsInstance(self.receivedSubmitSmResp, SubmitSMResp)

@defer.inlineCallbacks
def test_submitSm_priority(self):
Expand Down Expand Up @@ -889,9 +876,6 @@ def test_submitSm_validity(self):
class LoggingTestCases(SMSCSimulatorRecorder):
receivedSubmitSmResp = None

def submit_sm_callback(self, message):
self.receivedSubmitSmResp = pickle.loads(message.content.body)

@defer.inlineCallbacks
def setUp(self):
yield SMSCSimulatorRecorder.setUp(self)
Expand All @@ -917,13 +901,6 @@ def send_long_submit_sm(self, long_content_split):
else:
time.sleep(0.2)

# Listen on the submit.sm.resp queue
routing_key_submit_sm_resp = 'submit.sm.resp.%s' % self.defaultConfig.id
consumerTag = 'test_submitSm'
yield self.amqpBroker.chan.basic_consume(queue=routing_key_submit_sm_resp, no_ack=True, consumer_tag=consumerTag)
queue = yield self.amqpBroker.client.queue(consumerTag)
queue.get().addCallback(self.submit_sm_callback)

# Build a long submit_sm
assertionKey = str(randint(10, 99)) * 100 + 'EOF' # 203 chars
config = SMPPClientConfig(id='defaultId')
Expand Down
16 changes: 0 additions & 16 deletions jasmin/protocols/cli/jcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,6 @@ def do_load(self, arg, opts):
help = "Show smpp connector stats using it's CID"),
make_option(None, '--smppcs', action="store_true",
help = "Show all smpp connectors stats"),
make_option(None, '--moroute', type="string", metavar="ORDER",
help = "Show MO Route stats using it's ORDER"),
make_option(None, '--moroutes', action="store_true",
help = "Show all MO Routes stats"),
make_option(None, '--mtroute', type="string", metavar="ORDER",
help = "Show MT Route stats using it's ORDER"),
make_option(None, '--mtroutes', action="store_true",
help = "Show all MT Routes stats"),
make_option(None, '--httpapi', action="store_true",
help = "Show HTTP API stats"),
make_option(None, '--smppsapi', action="store_true",
Expand All @@ -376,14 +368,6 @@ def do_stats(self, arg, opts = None):
self.managers['stats'].smppc(arg, opts)
elif opts.smppcs:
self.managers['stats'].smppcs(arg, opts)
elif opts.moroute:
self.managers['stats'].moroute(arg, opts)
elif opts.moroutes:
self.managers['stats'].moroutes(arg, opts)
elif opts.mtroute:
self.managers['stats'].mtroute(arg, opts)
elif opts.mtroutes:
self.managers['stats'].mtroutes(arg, opts)
elif opts.httpapi:
self.managers['stats'].httpapi(arg, opts)
elif opts.smppsapi:
Expand Down
49 changes: 35 additions & 14 deletions jasmin/protocols/cli/statsm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pickle
from jasmin.protocols.cli.managers import Manager
from jasmin.protocols.smpp.stats import SMPPClientStatsCollector
from jasmin.protocols.smpp.stats import SMPPClientStatsCollector, SMPPServerStatsCollector
from jasmin.protocols.http.stats import HttpAPIStatsCollector
from .usersm import UserExist
from .smppccm import ConnectorExist
from tabulate import tabulate
Expand Down Expand Up @@ -106,21 +107,41 @@ def smppcs(self, arg, opts):

self.protocol.sendData(tabulate(table, headers, tablefmt = "plain", numalign = "left").encode('ascii'), prompt = False)
self.protocol.sendData('Total connectors: %s' % (len(table)))

def httpapi(self, arg, opts):
sc = HttpAPIStatsCollector()
headers = ["#Item", "Value"]

def moroute(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')

def moroutes(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')
table = []
for k, v in sc.get()._stats.iteritems():
row = []
row.append('#%s' % k)
if k[-3:] == '_at':
row.append(formatDateTime(v))
else:
row.append(v)

def mtroute(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')
table.append(row)

def mtroutes(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')

def httpapi(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')
self.protocol.sendData(tabulate(table, headers, tablefmt = "plain", numalign = "left").encode('ascii'))

def smppsapi(self, arg, opts):
return self.protocol.sendData('Not implemented yet.')
"""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'))
56 changes: 28 additions & 28 deletions jasmin/protocols/cli/test/test_statsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,38 +30,38 @@ def test_smppcs(self):
commands = [{'command': 'stats --smppcs', 'expect': expectedList}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_moroute(self):
order = '20'

commands = [{'command': 'stats --moroute=%s' % order, 'expect': r'????'}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_moroutes(self):
commands = [{'command': 'stats --moroutes', 'expect': r'????'}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_mtroute(self):
order = '20'

commands = [{'command': 'stats --mtroute=%s' % order, 'expect': r'????'}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_mtroutes(self):
commands = [{'command': 'stats --mtroutes', 'expect': r'????'}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_httpapi(self):
commands = [{'command': 'stats --httpapi', 'expect': r'????'}]
expectedList = ['#Item Value',
'#server_error_count 0',
'#last_request_at ND',
'#throughput_error_count 0',
'#success_count 0',
'#route_error_count 0',
'#request_count 0',
'#auth_error_count 0',
'#created_at ND',
'#last_success_at ND',
'#charging_error_count 0']
commands = [{'command': 'stats --httpapi', 'expect': expectedList}]
return self._test(r'jcli : ', commands)

@unittest.skip("Work in progress #123")
def test_smppsapi(self):
commands = [{'command': 'stats --smppsapi', 'expect': r'????'}]
expectedList = ['#Item Value',
'#disconnect_count 0',
'#bound_tx_count 0',
'#bind_rx_count 0',
'#last_received_pdu_at ND',
'#last_received_elink_at ND',
'#connected_count 0',
'#bound_trx_count 0',
'#unbind_count 0',
'#bind_tx_count 0',
'#bound_rx_count 0',
'#bind_trx_count 0',
'#created_at ND',
'#connect_count 0',
'#last_sent_pdu_at ND']
commands = [{'command': 'stats --smppsapi', 'expect': expectedList}]
return self._test(r'jcli : ', commands)

class UserStatsTestCases(UserTestCases):
Expand Down
36 changes: 26 additions & 10 deletions jasmin/protocols/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,26 @@
from jasmin.vendor.smpp.pdu.pdu_types import RegisteredDeliveryReceipt, RegisteredDelivery
from jasmin.protocols.smpp.operations import SMPPOperationFactory
from jasmin.routing.Routables import RoutableSubmitSm
from jasmin.protocols.http.errors import (AuthenticationError,
ServerError,
RouteNotFoundError,
ChargingError,
ThroughputExceededError,
)
from jasmin.protocols.http.validation import UrlArgsValidator, HttpAPICredentialValidator
from .errors import (AuthenticationError,
ServerError,
RouteNotFoundError,
ChargingError,
ThroughputExceededError,
)
from .validation import UrlArgsValidator, HttpAPICredentialValidator
from .stats import HttpAPIStatsCollector

LOG_CATEGORY = "jasmin-http-api"

class Send(Resource):
def __init__(self, HTTPApiConfig, RouterPB, SMPPClientManagerPB, log):
def __init__(self, HTTPApiConfig, RouterPB, SMPPClientManagerPB, stats, log):
Resource.__init__(self)

self.SMPPClientManagerPB = SMPPClientManagerPB
self.RouterPB = RouterPB
self.log = log

self.stats = stats

# opFactory is initiated with a dummy SMPPClientConfig used for building SubmitSm only
self.opFactory = SMPPOperationFactory(long_content_max_parts = HTTPApiConfig.long_content_max_parts,
long_content_split = HTTPApiConfig.long_content_split)
Expand All @@ -44,6 +46,9 @@ def render(self, request):
request.getClientIP()))
response = {'return': None, 'status': 200}

self.stats.inc('request_count')
self.stats.set('last_request_at', datetime.now())

# updated_request will be filled with default values where request will never get modified
# updated_request is used for sending the SMS, request is just kept as an original request object
updated_request = request
Expand Down Expand Up @@ -98,6 +103,7 @@ def render(self, request):
# Authentication
user = self.RouterPB.authenticateUser(username = updated_request.args['username'][0], password = updated_request.args['password'][0])
if user is None:
self.stats.inc('auth_error_count')
self.log.debug("Authentication failure for username:%s and password:%s" % (updated_request.args['username'][0], updated_request.args['password'][0]))
self.log.error("Authentication failure for username:%s" % updated_request.args['username'][0])
raise AuthenticationError('Authentication failure for username:%s' % updated_request.args['username'][0])
Expand Down Expand Up @@ -128,6 +134,7 @@ def render(self, request):
routable = RoutableSubmitSm(SubmitSmPDU, user)
route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
if route is None:
self.stats.inc('route_error_count')
self.log.error("No route matched from user %s for SubmitSmPDU: %s" % (user, SubmitSmPDU))
raise RouteNotFoundError("No route found")

Expand Down Expand Up @@ -181,6 +188,7 @@ def render(self, request):
qos_throughput_ysecond_td = timedelta( microseconds = qos_throughput_second * 1000000)
qos_delay = datetime.now() - user.CnxStatus.httpapi['qos_last_submit_sm_at']
if qos_delay < qos_throughput_ysecond_td:
self.stats.inc('throughput_error_count')
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,
Expand Down Expand Up @@ -216,6 +224,7 @@ def render(self, request):
(u_subsm_count, bill.getAction('decrement_submit_sm_count'))})

if self.RouterPB.chargeUserForSubmitSms(user, bill, submit_sm_count, charging_requirements) is None:
self.stats.inc('charging_error_count')
self.log.error('Charging user %s failed, [bid:%s] [ttlamounts:%s] SubmitSmPDU (x%s)' %
(user, bill.bid, bill.getTotalAmounts(), submit_sm_count))
raise ChargingError('Cannot charge submit_sm, check RouterPB log file for details')
Expand All @@ -234,9 +243,12 @@ def render(self, request):

# Build final response
if not c.result:
self.stats.inc('server_error_count')
self.log.error('Failed to send SubmitSmPDU to [cid:%s]' % routedConnector.cid)
raise ServerError('Cannot send submit_sm, check SMPPClientManagerPB log file for details')
else:
self.stats.inc('success_count')
self.stats.set('last_success_at', datetime.now())
self.log.debug('SubmitSmPDU sent to [cid:%s], result = %s' % (routedConnector.cid, c.result))
response = {'return': c.result, 'status': 200}
except Exception, e:
Expand Down Expand Up @@ -272,6 +284,10 @@ def __init__(self, RouterPB, SMPPClientManagerPB, config):
self.SMPPClientManagerPB = SMPPClientManagerPB
self.RouterPB = RouterPB

# Setup stats collector
self.stats = HttpAPIStatsCollector().get()
self.stats.set('created_at', datetime.now())

# Set up a dedicated logger
self.log = logging.getLogger(LOG_CATEGORY)
if len(self.log.handlers) != 1:
Expand All @@ -284,4 +300,4 @@ def __init__(self, RouterPB, SMPPClientManagerPB, config):

# Set http url routings
self.log.debug("Setting http url routing for /send")
self.putChild('send', Send(self.config, self.RouterPB, self.SMPPClientManagerPB, self.log))
self.putChild('send', Send(self.config, self.RouterPB, self.SMPPClientManagerPB, self.stats, self.log))

0 comments on commit 744635d

Please sign in to comment.