Compare commits

..
2 Commits
Author SHA1 Message Date
Dave Horton 76625c7596 Feature/recent calls enhancement with sp (#50)
* write cdrs with service_provider_sid

* write call counts at the SP level
2022-09-16 13:45:33 +02:00
Paulo Tellesandp.souza 3b0f7ff6eb change node image (#48)
Co-authored-by: p.souza <p.souza@cognigy.com>
2022-09-07 13:20:13 +02:00
6 changed files with 54 additions and 51 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 node:18.6.0-alpine as base
FROM --platform=linux/amd64 node:18.8.0-alpine as base
RUN apk --update --no-cache add --virtual .builds-deps build-base python3
+14 -8
View File
@@ -1,7 +1,7 @@
const Emitter = require('events');
const sdpTransform = require('sdp-transform');
const SrsClient = require('@jambonz/siprec-client-utils');
const {makeRtpEngineOpts, makeCallCountKey} = require('./utils');
const {makeRtpEngineOpts, makeAccountCallCountKey, makeSPCallCountKey} = require('./utils');
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
const {SipError, stringifyUri, parseUri} = require('drachtio-srf');
const debug = require('debug')('jambonz:sbc-outbound');
@@ -73,7 +73,8 @@ class CallSession extends Emitter {
this.incrKey = req.srf.locals.realtimeDbHelpers.incrKey;
this.decrKey = req.srf.locals.realtimeDbHelpers.decrKey;
this.callCountKey = makeCallCountKey(req.locals.account_sid);
this.callCountKey = makeAccountCallCountKey(req.locals.account_sid);
this.callCountKeySP = makeSPCallCountKey(req.locals.service_provider_sid);
const {performLcr, lookupCarrierBySid, lookupSipGatewaysByCarrier} = this.srf.locals.dbHelpers;
this.performLcr = performLcr;
@@ -361,6 +362,7 @@ class CallSession extends Emitter {
if (!this.req.locals.account.disable_cdrs) {
this.req.locals.cdr = {
...initCdr(this.req.srf, inv),
service_provider_sid: this.req.locals.service_provider_sid,
account_sid: this.req.locals.account_sid,
trunk
};
@@ -455,13 +457,17 @@ class CallSession extends Emitter {
await other.destroy();
} catch (err) {}
this.decrKey(this.callCountKey)
.then((count) => {
this.logger.debug(`after hangup there are ${count} active calls for this account`);
debug(`after hangup there are ${count} active calls for this account`);
return;
Promise.all([
this.decrKey(this.callCountKey),
this.decrKey(this.callCountKeySP)
])
.then(([calls, callsSP]) => {
this.logger.info({calls, callsSP}, 'decremented call counts after call completion');
return [calls, callsSP];
})
.catch((err) => this.logger.error({err}, 'Error decrementing call count'));
.catch((err) => {
this.logger.error({err}, 'error decrementing call counts after call completion');
});
/* write cdr for connected call */
if (this.req.locals.cdr) {
+26 -31
View File
@@ -1,7 +1,7 @@
const debug = require('debug')('jambonz:sbc-outbound');
const parseUri = require('drachtio-srf').parseUri;
const Registrar = require('@jambonz/mw-registrar');
const {selectHostPort, makeCallCountKey} = require('./utils');
const {selectHostPort, makeAccountCallCountKey, makeSPCallCountKey} = require('./utils');
const FS_UUID_SET_NAME = 'fsUUIDs';
module.exports = (srf, logger, opts) => {
@@ -63,7 +63,6 @@ module.exports = (srf, logger, opts) => {
}
}
stats.increment('sbc.invites', ['direction:outbound']);
req.on('cancel', () => {
@@ -76,6 +75,7 @@ module.exports = (srf, logger, opts) => {
try {
req.locals.account = await lookupAccountBySid(req.locals.account_sid);
req.locals.service_provider_sid = req.locals.account.service_provider_sid;
} catch (err) {
req.locals.logger.error({err}, `Error looking up account sid ${req.locals.account_sid}`);
res.send(500);
@@ -85,26 +85,21 @@ module.exports = (srf, logger, opts) => {
};
const checkLimits = async(req, res, next) => {
const {logger, account_sid} = req.locals;
const {logger, account_sid, service_provider_sid} = req.locals;
const {writeAlerts, AlertType} = req.srf.locals;
const key = makeCallCountKey(account_sid);
const keyAccount = makeAccountCallCountKey(account_sid);
const keySP = makeSPCallCountKey(service_provider_sid);
try {
/* increment the call count */
const calls = await incrKey(key);
debug(`checkLimits: call count is now ${calls}`);
const [calls, callsSP] = await Promise.all([incrKey(keyAccount), incrKey(keySP)]);
logger.info({calls, callsSP}, 'incremented call counts');
/* decrement count if INVITE is later rejected */
res.once('end', ({status}) => {
res.once('end', async({status}) => {
if (status > 200) {
debug('checkLimits: decrementing call count due to rejection');
decrKey(key)
.then((count) => {
logger.debug({key}, `after rejection there are ${count} active calls for this account`);
debug({key}, `after rejection there are ${count} active calls for this account`);
return;
})
.catch((err) => logger.error({err}, 'checkLimits: decrKey err'));
const [calls, callsSP] = await Promise.all([decrKey(keyAccount), decrKey(keySP)]);
logger.info({calls, callsSP}, `decremented call counts after ${status} response`);
const tags = ['accepted:no', `sipStatus:${status}`];
stats.increment('sbc.originations', tags);
}
@@ -122,24 +117,24 @@ module.exports = (srf, logger, opts) => {
const capacities = await lookupAccountCapacitiesBySid(account_sid);
const limit = capacities.find((c) => c.category == 'voice_call_session');
if (!limit) {
logger.debug('checkLimits: no call limits specified');
return next();
}
const limit_sessions = limit.quantity;
if (limit) {
const limit_sessions = limit.quantity;
if (calls > limit_sessions) {
debug(`checkLimits: limits exceeded: call count ${calls}, limit ${limit_sessions}`);
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
writeAlerts({
alert_type: AlertType.CALL_LIMIT,
account_sid,
count: limit_sessions
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
res.send(503, 'Maximum Calls In Progress');
return req.srf.endSession(req);
if (calls > limit_sessions) {
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
writeAlerts({
alert_type: AlertType.ACCOUNT_CALL_LIMIT,
service_provider_sid,
account_sid,
count: limit_sessions
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
res.send(503, 'Maximum Calls In Progress');
return req.srf.endSession(req);
}
}
//TODO: check SP limits
next();
} catch (err) {
logger.error({err}, 'error checking limits error for inbound call');
+4 -2
View File
@@ -77,7 +77,8 @@ const pingMsTeamsGateways = (logger, srf) => {
});
};
const makeCallCountKey = (sid) => `${sid}:outcalls`;
const makeAccountCallCountKey = (sid) => `${sid}:outcalls:account`;
const makeSPCallCountKey = (sid) => `${sid}:outcalls:sp`;
const equalsIgnoreOrder = (a, b) => {
if (a.length !== b.length) return false;
@@ -114,7 +115,8 @@ module.exports = {
makeRtpEngineOpts,
selectHostPort,
pingMsTeamsGateways,
makeCallCountKey,
makeAccountCallCountKey,
makeSPCallCountKey,
equalsIgnoreOrder,
systemHealth,
createHealthCheckApp
+7 -7
View File
@@ -16,7 +16,7 @@
"@jambonz/rtpengine-utils": "^0.3.1",
"@jambonz/siprec-client-utils": "^0.1.4",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.1.12",
"@jambonz/time-series": "^0.2.1",
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
"drachtio-fn-b2b-sugar": "^0.0.12",
@@ -644,9 +644,9 @@
}
},
"node_modules/@jambonz/time-series": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
"integrity": "sha512-TmCG4jcI8oK3NXOc4/PbdRhhMVLEr5FOyG4IIWpNlwB0vbjAGLY3K+O5PF4fXK+UcNYnIrUcrd2C0J9z3+YBxw==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.1.tgz",
"integrity": "sha512-uAoeZ3ibS7kEOGdT+vaY8BB8hOV4q38eEaF+d5OvLQaHCrPonNiwB8tWhhXDwtYdDompfqVRUy/plNA9fyS7Vw==",
"dependencies": {
"debug": "^4.3.1",
"influx": "^5.9.3"
@@ -5598,9 +5598,9 @@
}
},
"@jambonz/time-series": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
"integrity": "sha512-TmCG4jcI8oK3NXOc4/PbdRhhMVLEr5FOyG4IIWpNlwB0vbjAGLY3K+O5PF4fXK+UcNYnIrUcrd2C0J9z3+YBxw==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.2.1.tgz",
"integrity": "sha512-uAoeZ3ibS7kEOGdT+vaY8BB8hOV4q38eEaF+d5OvLQaHCrPonNiwB8tWhhXDwtYdDompfqVRUy/plNA9fyS7Vw==",
"requires": {
"debug": "^4.3.1",
"influx": "^5.9.3"
+2 -2
View File
@@ -22,7 +22,7 @@
"description": "jambonz session border controller application for outbound calls",
"scripts": {
"start": "node app",
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONZ_HOSTING=1 JAMBONES_NETWORK_CIDR=127.0.0.1/32 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 node test/ ",
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONZ_HOSTING=1 JAMBONES_NETWORK_CIDR=127.0.0.1/32 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=info DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 node test/ ",
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
"jslint": "eslint app.js lib"
},
@@ -34,7 +34,7 @@
"@jambonz/rtpengine-utils": "^0.3.1",
"@jambonz/siprec-client-utils": "^0.1.4",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.1.12",
"@jambonz/time-series": "^0.2.1",
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
"drachtio-fn-b2b-sugar": "^0.0.12",