feat: add number library routing and cdr location support
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
local active = redis.call('GET', KEYS[1])
|
||||
if not active or active == '' then
|
||||
return {
|
||||
'reject',
|
||||
'CONFIG_MISSING',
|
||||
'none',
|
||||
'none',
|
||||
'none',
|
||||
'none',
|
||||
'no_active_version',
|
||||
'none',
|
||||
'none',
|
||||
'none',
|
||||
'none',
|
||||
'UNKNOWN',
|
||||
'UNKNOWN',
|
||||
'UNKNOWN',
|
||||
'UNKNOWN',
|
||||
'UNKNOWN',
|
||||
'UNKNOWN',
|
||||
''
|
||||
}
|
||||
end
|
||||
|
||||
local source_ip = ARGV[1]
|
||||
local caller = ARGV[2] or ''
|
||||
local callee = ARGV[3] or ''
|
||||
|
||||
local prefix = 'cfg:v:' .. active
|
||||
|
||||
local function field(json, name)
|
||||
return string.match(json, '"' .. name .. '":"([^"]*)"')
|
||||
end
|
||||
|
||||
local function normalize_callee(value)
|
||||
local digits = string.gsub(value or '', '[^0-9]', '')
|
||||
if string.sub(digits, 1, 4) == '0086' then
|
||||
digits = string.sub(digits, 5)
|
||||
elseif string.sub(digits, 1, 2) == '86' and string.len(digits) == 13 then
|
||||
digits = string.sub(digits, 3)
|
||||
end
|
||||
return digits
|
||||
end
|
||||
|
||||
local function unknown_number(callee_digits)
|
||||
return {
|
||||
city_code = 'UNKNOWN',
|
||||
city_name = 'UNKNOWN',
|
||||
province_code = 'UNKNOWN',
|
||||
province_name = 'UNKNOWN',
|
||||
carrier = 'UNKNOWN',
|
||||
number_type = 'UNKNOWN',
|
||||
normalized = callee_digits or ''
|
||||
}
|
||||
end
|
||||
|
||||
local function with_geo_city(info)
|
||||
if info.city_code == 'UNKNOWN' or info.province_code ~= 'UNKNOWN' then
|
||||
return info
|
||||
end
|
||||
local city_json = redis.call('GET', prefix .. ':geo_city:' .. info.city_code)
|
||||
if city_json then
|
||||
info.province_code = field(city_json, 'provinceCode') or info.province_code
|
||||
info.province_name = field(city_json, 'provinceName') or info.province_name
|
||||
info.city_name = field(city_json, 'cityName') or info.city_name
|
||||
end
|
||||
return info
|
||||
end
|
||||
|
||||
local function resolve_carrier(digits, fallback)
|
||||
if string.len(digits) >= 4 then
|
||||
local prefix4_json = redis.call('GET', prefix .. ':carrier_prefix:' .. string.sub(digits, 1, 4))
|
||||
if prefix4_json then
|
||||
return field(prefix4_json, 'carrier') or fallback
|
||||
end
|
||||
end
|
||||
if string.len(digits) >= 3 then
|
||||
local prefix3_json = redis.call('GET', prefix .. ':carrier_prefix:' .. string.sub(digits, 1, 3))
|
||||
if prefix3_json then
|
||||
return field(prefix3_json, 'carrier') or fallback
|
||||
end
|
||||
end
|
||||
return fallback
|
||||
end
|
||||
|
||||
local function resolve_number(value)
|
||||
local digits = normalize_callee(value)
|
||||
local info = unknown_number(digits)
|
||||
|
||||
if string.match(digits, '^1%d%d%d%d%d%d%d%d%d%d$') then
|
||||
info.number_type = 'MOBILE'
|
||||
local segment_json = redis.call('GET', prefix .. ':phone_segment:' .. string.sub(digits, 1, 7))
|
||||
if segment_json then
|
||||
info.city_code = field(segment_json, 'cityCode') or info.city_code
|
||||
info.city_name = field(segment_json, 'cityName') or info.city_name
|
||||
info.province_code = field(segment_json, 'provinceCode') or info.province_code
|
||||
info.province_name = field(segment_json, 'provinceName') or info.province_name
|
||||
info.carrier = field(segment_json, 'carrier') or info.carrier
|
||||
end
|
||||
info.carrier = resolve_carrier(digits, info.carrier)
|
||||
return with_geo_city(info)
|
||||
end
|
||||
|
||||
if string.sub(digits, 1, 1) == '0' and string.len(digits) >= 3 then
|
||||
info.number_type = 'LANDLINE'
|
||||
local area_json = nil
|
||||
if string.len(digits) >= 4 then
|
||||
area_json = redis.call('GET', prefix .. ':area_code:' .. string.sub(digits, 1, 4))
|
||||
end
|
||||
if not area_json and string.len(digits) >= 3 then
|
||||
area_json = redis.call('GET', prefix .. ':area_code:' .. string.sub(digits, 1, 3))
|
||||
end
|
||||
if area_json then
|
||||
info.city_code = field(area_json, 'cityCode') or info.city_code
|
||||
info.city_name = field(area_json, 'cityName') or info.city_name
|
||||
info.province_code = field(area_json, 'provinceCode') or info.province_code
|
||||
info.province_name = field(area_json, 'provinceName') or info.province_name
|
||||
end
|
||||
return with_geo_city(info)
|
||||
end
|
||||
|
||||
info.carrier = resolve_carrier(digits, info.carrier)
|
||||
return info
|
||||
end
|
||||
|
||||
local number_info = resolve_number(callee)
|
||||
|
||||
local function result(decision, reason, gateway_id, version, line_group_id, policy_id, customer_id, vendor_id, vendor_gateway_id, host, port)
|
||||
return {
|
||||
decision,
|
||||
reason,
|
||||
gateway_id,
|
||||
version,
|
||||
line_group_id,
|
||||
policy_id,
|
||||
customer_id,
|
||||
vendor_id,
|
||||
vendor_gateway_id,
|
||||
host,
|
||||
port,
|
||||
number_info.city_code,
|
||||
number_info.city_name,
|
||||
number_info.province_name,
|
||||
number_info.carrier,
|
||||
number_info.number_type,
|
||||
number_info.province_code,
|
||||
number_info.normalized
|
||||
}
|
||||
end
|
||||
|
||||
local gateway_id = redis.call('GET', prefix .. ':auth:ip:' .. source_ip)
|
||||
if not gateway_id then
|
||||
return result('reject', 'AUTH_MISSING', 'none', active, 'none', 'none', 'no_auth_ip', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
|
||||
local gateway_json = redis.call('GET', prefix .. ':customer_gateway:' .. gateway_id)
|
||||
if not gateway_json then
|
||||
return result('reject', 'GATEWAY_MISSING', gateway_id, active, 'none', 'none', 'gateway_missing', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
if not string.find(gateway_json, '"status":"ENABLED"', 1, true) then
|
||||
return result('reject', 'GATEWAY_DISABLED', gateway_id, active, 'none', 'none', 'gateway_disabled', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
|
||||
local customer_id = string.match(gateway_json, '"customerId":"([^"]+)"')
|
||||
if not customer_id then
|
||||
return result('reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_id_missing', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
|
||||
local customer_json = redis.call('GET', prefix .. ':customer:' .. customer_id)
|
||||
if not customer_json then
|
||||
return result('reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_missing', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
if not string.find(customer_json, '"status":"ENABLED"', 1, true) then
|
||||
return result('reject', 'CUSTOMER_DISABLED', gateway_id, active, 'none', 'none', 'customer_disabled', 'none', 'none', 'none', 'none')
|
||||
end
|
||||
|
||||
local function region_blocked(vendor_gateway_id)
|
||||
if number_info.city_code ~= 'UNKNOWN' then
|
||||
local city_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_city_codes', number_info.city_code)
|
||||
if city_blocked == 1 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if number_info.province_code ~= 'UNKNOWN' then
|
||||
local province_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_province_codes', number_info.province_code)
|
||||
if province_blocked == 1 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function first_vendor_route(line_group_id)
|
||||
local line_group_json = redis.call('GET', prefix .. ':line_group:' .. line_group_id)
|
||||
if not line_group_json then
|
||||
return nil, 'LINE_GROUP_MISSING'
|
||||
end
|
||||
if not string.find(line_group_json, '"status":"ENABLED"', 1, true) then
|
||||
return nil, 'LINE_GROUP_DISABLED'
|
||||
end
|
||||
|
||||
local items = redis.call('LRANGE', prefix .. ':line_group:' .. line_group_id .. ':items', 0, -1)
|
||||
local skipped_region = false
|
||||
for _, item_json in ipairs(items) do
|
||||
if string.find(item_json, '"status":"ENABLED"', 1, true) then
|
||||
local vendor_gateway_id = string.match(item_json, '"vendorGatewayId":"([^"]+)"')
|
||||
if vendor_gateway_id then
|
||||
local vendor_gateway_json = redis.call('GET', prefix .. ':vendor_gateway:' .. vendor_gateway_id)
|
||||
if vendor_gateway_json and string.find(vendor_gateway_json, '"status":"ENABLED"', 1, true) then
|
||||
if region_blocked(vendor_gateway_id) then
|
||||
skipped_region = true
|
||||
else
|
||||
local vendor_id = string.match(vendor_gateway_json, '"vendorId":"([^"]+)"') or 'none'
|
||||
local host = string.match(vendor_gateway_json, '"host":"([^"]+)"') or 'none'
|
||||
local port = string.match(vendor_gateway_json, '"port":([0-9]+)') or '5060'
|
||||
return {vendor_id, vendor_gateway_id, host, port}, 'OK'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if skipped_region then
|
||||
return nil, 'NO_VENDOR_ROUTE_REGION_BLOCKED'
|
||||
end
|
||||
return nil, 'NO_VENDOR_ROUTE'
|
||||
end
|
||||
|
||||
local policies = redis.call('LRANGE', prefix .. ':customer_gateway:' .. gateway_id .. ':policies', 0, -1)
|
||||
for _, policy_json in ipairs(policies) do
|
||||
if string.find(policy_json, '"status":"ENABLED"', 1, true) then
|
||||
local caller_mode = string.match(policy_json, '"callerMode":"([^"]+)"') or 'ANY'
|
||||
local caller_value = string.match(policy_json, '"callerValue":"([^"]*)"') or ''
|
||||
local callee_mode = string.match(policy_json, '"calleeMode":"([^"]+)"') or 'ANY'
|
||||
local callee_value = string.match(policy_json, '"calleeValue":"([^"]*)"') or ''
|
||||
|
||||
local caller_ok = caller_mode == 'ANY'
|
||||
or (caller_mode == 'EQUALS' and caller == caller_value)
|
||||
or (caller_mode == 'PREFIX' and string.sub(caller, 1, string.len(caller_value)) == caller_value)
|
||||
local callee_ok = callee_mode == 'ANY'
|
||||
or (callee_mode == 'EQUALS' and callee == callee_value)
|
||||
or (callee_mode == 'PREFIX' and string.sub(callee, 1, string.len(callee_value)) == callee_value)
|
||||
|
||||
if caller_ok and callee_ok then
|
||||
local policy_id = string.match(policy_json, '"id":"([^"]+)"') or 'none'
|
||||
local line_group_id = string.match(policy_json, '"lineGroupId":"([^"]+)"') or 'none'
|
||||
local vendor_route, route_reason = first_vendor_route(line_group_id)
|
||||
if vendor_route then
|
||||
return result('allow', 'OK', gateway_id, active, line_group_id, policy_id, customer_id, vendor_route[1], vendor_route[2], vendor_route[3], vendor_route[4])
|
||||
end
|
||||
return result('reject', route_reason, gateway_id, active, line_group_id, policy_id, customer_id, 'none', 'none', 'none', 'none')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result('reject', 'NO_POLICY', gateway_id, active, 'none', 'none', 'no_policy_match', 'none', 'none', 'none', 'none')
|
||||
@@ -18,11 +18,16 @@ loadmodule "signaling.so"
|
||||
loadmodule "sl.so"
|
||||
loadmodule "tm.so"
|
||||
modparam("tm", "fr_timeout", 5)
|
||||
modparam("tm", "fr_inv_timeout", 30)
|
||||
modparam("tm", "fr_inv_timeout", 95)
|
||||
|
||||
loadmodule "rr.so"
|
||||
modparam("rr", "append_fromtag", 1)
|
||||
|
||||
loadmodule "dialog.so"
|
||||
modparam("dialog", "dlg_match_mode", 1)
|
||||
modparam("dialog", "default_timeout", 7200)
|
||||
modparam("dialog", "delete_delay", 10)
|
||||
|
||||
loadmodule "maxfwd.so"
|
||||
loadmodule "sipmsgops.so"
|
||||
loadmodule "textops.so"
|
||||
@@ -167,10 +172,21 @@ route[S28_INVITE] {
|
||||
$var(s28_line_group_id) = "none";
|
||||
$var(s28_policy_id) = "none";
|
||||
$var(s28_customer_id) = "none";
|
||||
$var(s28_vendor_id) = "none";
|
||||
$var(s28_vendor_gateway_id) = "none";
|
||||
$var(s28_vendor_host) = "none";
|
||||
$var(s28_vendor_port) = "5060";
|
||||
$var(s28_callee_city_code) = "UNKNOWN";
|
||||
$var(s28_callee_city_name) = "UNKNOWN";
|
||||
$var(s28_callee_province_name) = "UNKNOWN";
|
||||
$var(s28_callee_operator) = "UNKNOWN";
|
||||
$var(s28_callee_number_type) = "UNKNOWN";
|
||||
$var(s28_callee_province_code) = "UNKNOWN";
|
||||
$var(s28_callee_normalized) = "";
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "Routing Not Ready";
|
||||
|
||||
if (!cache_raw_query("redis:s20", "EVALSHA cfdc02cbe5528d37fba617947c09e3380770c918 1 cfg:active_version $si $fU $rU $ci", "$avp(s28_hotpath)")) {
|
||||
if (!cache_raw_query("redis:s20", "EVALSHA 229c06326b97d3d64b0b02deedec77a764a61045 1 cfg:active_version $si $fU $rU $ci", "$avp(s28_hotpath)")) {
|
||||
xlog("L_ERR", "S28 Redis hotpath unavailable source=$si callid=$ci\n");
|
||||
$var(s28_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s28_reply_code) = 503;
|
||||
@@ -187,13 +203,24 @@ route[S28_INVITE] {
|
||||
$var(s28_line_group_id) = $(avp(s28_hotpath)[4]);
|
||||
$var(s28_policy_id) = $(avp(s28_hotpath)[5]);
|
||||
$var(s28_customer_id) = $(avp(s28_hotpath)[6]);
|
||||
$var(s28_vendor_id) = $(avp(s28_hotpath)[7]);
|
||||
$var(s28_vendor_gateway_id) = $(avp(s28_hotpath)[8]);
|
||||
$var(s28_vendor_host) = $(avp(s28_hotpath)[9]);
|
||||
$var(s28_vendor_port) = $(avp(s28_hotpath)[10]);
|
||||
$var(s28_callee_city_code) = $(avp(s28_hotpath)[11]);
|
||||
$var(s28_callee_city_name) = $(avp(s28_hotpath)[12]);
|
||||
$var(s28_callee_province_name) = $(avp(s28_hotpath)[13]);
|
||||
$var(s28_callee_operator) = $(avp(s28_hotpath)[14]);
|
||||
$var(s28_callee_number_type) = $(avp(s28_hotpath)[15]);
|
||||
$var(s28_callee_province_code) = $(avp(s28_hotpath)[16]);
|
||||
$var(s28_callee_normalized) = $(avp(s28_hotpath)[17]);
|
||||
|
||||
if ($var(s28_decision) != "allow") {
|
||||
update_stat("s28_hotpath_reject_total", 1);
|
||||
if ($var(s28_reason) == "CONFIG_MISSING") {
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "Config Missing";
|
||||
} else if ($var(s28_reason) == "NO_POLICY") {
|
||||
} else if ($var(s28_reason) == "NO_POLICY" || $var(s28_reason) == "NO_VENDOR_ROUTE" || $var(s28_reason) == "NO_VENDOR_ROUTE_REGION_BLOCKED" || $var(s28_reason) == "LINE_GROUP_MISSING" || $var(s28_reason) == "LINE_GROUP_DISABLED") {
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "No Route Policy";
|
||||
} else {
|
||||
@@ -206,7 +233,30 @@ route[S28_INVITE] {
|
||||
}
|
||||
|
||||
update_stat("s28_hotpath_allow_total", 1);
|
||||
xlog("L_INFO", "S28 hotpath allow source=$si callid=$ci customer=$var(s28_customer_id) gateway=$var(s28_gateway_id) policy=$var(s28_policy_id) line_group=$var(s28_line_group_id) version=$var(s28_config_version)\n");
|
||||
xlog("L_INFO", "S28 hotpath allow source=$si callid=$ci customer=$var(s28_customer_id) gateway=$var(s28_gateway_id) policy=$var(s28_policy_id) line_group=$var(s28_line_group_id) vendor=$var(s28_vendor_id) vendor_gateway=$var(s28_vendor_gateway_id) dst=$var(s28_vendor_host):$var(s28_vendor_port) version=$var(s28_config_version)\n");
|
||||
|
||||
if (!create_dialog()) {
|
||||
xlog("L_ERR", "S28 create_dialog failed callid=$ci\n");
|
||||
$var(s28_reason) = "DIALOG_CREATE_FAILED";
|
||||
$var(s28_reply_code) = 500;
|
||||
$var(s28_reply_text) = "Dialog Error";
|
||||
route(S28_CDR_FAILURE);
|
||||
send_reply($var(s28_reply_code), $var(s28_reply_text));
|
||||
exit;
|
||||
}
|
||||
$dlg_val(s28_customer_id) = $var(s28_customer_id);
|
||||
$dlg_val(s28_gateway_id) = $var(s28_gateway_id);
|
||||
$dlg_val(s28_policy_id) = $var(s28_policy_id);
|
||||
$dlg_val(s28_line_group_id) = $var(s28_line_group_id);
|
||||
$dlg_val(s28_vendor_id) = $var(s28_vendor_id);
|
||||
$dlg_val(s28_vendor_gateway_id) = $var(s28_vendor_gateway_id);
|
||||
$dlg_val(s28_config_version) = $var(s28_config_version);
|
||||
$dlg_val(s28_callee) = $rU;
|
||||
$dlg_val(s28_callee_city_code) = $var(s28_callee_city_code);
|
||||
$dlg_val(s28_callee_city_name) = $var(s28_callee_city_name);
|
||||
$dlg_val(s28_callee_province_name) = $var(s28_callee_province_name);
|
||||
$dlg_val(s28_callee_operator) = $var(s28_callee_operator);
|
||||
$dlg_val(s28_callee_number_type) = $var(s28_callee_number_type);
|
||||
|
||||
if (has_body("application/sdp")) {
|
||||
if (!rtpengine_offer("replace-origin replace-session-connection record-call=on")) {
|
||||
@@ -221,8 +271,8 @@ route[S28_INVITE] {
|
||||
}
|
||||
|
||||
record_route();
|
||||
$du = "sip:100.93.185.30:50620";
|
||||
$ru = "sip:" + $rU + "@100.93.185.30:50620";
|
||||
$du = "sip:" + $var(s28_vendor_host) + ":" + $var(s28_vendor_port);
|
||||
$ru = "sip:" + $rU + "@" + $var(s28_vendor_host) + ":" + $var(s28_vendor_port);
|
||||
t_on_reply("S28_REPLY");
|
||||
|
||||
if (!t_relay()) {
|
||||
@@ -247,7 +297,7 @@ onreply_route[S28_REPLY] {
|
||||
route[S28_CDR_SUCCESS] {
|
||||
$var(s28_event_id) = "s28-ok-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
$var(s28_idempotency_key) = $ci + ":s28-ok:" + $Ts;
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU customer_id cus_s28_t customer_gateway_id cgw_s28_t_ip customer_gateway_policy_id cgp_s28_t_default vendor_id ven_s28_t vendor_gateway_id vgw_s28_t_uas line_group_id llg_s28_t started_at $Ts answered_at $Ts ended_at $Ts duration_sec 6 sip_code 200 hangup_reason NORMAL_CLEARING recording_key none config_version $var(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $dlg_val(s28_callee) callee_city_code $dlg_val(s28_callee_city_code) callee_city_name $dlg_val(s28_callee_city_name) callee_province_name $dlg_val(s28_callee_province_name) callee_operator $dlg_val(s28_callee_operator) callee_number_type $dlg_val(s28_callee_number_type) customer_id $dlg_val(s28_customer_id) customer_gateway_id $dlg_val(s28_gateway_id) customer_gateway_policy_id $dlg_val(s28_policy_id) vendor_id $dlg_val(s28_vendor_id) vendor_gateway_id $dlg_val(s28_vendor_gateway_id) line_group_id $dlg_val(s28_line_group_id) started_at $Ts answered_at $Ts ended_at $Ts duration_sec 6 sip_code 200 hangup_reason NORMAL_CLEARING recording_key none config_version $dlg_val(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
update_stat("s28_cdr_xadd_total", 1);
|
||||
xlog("L_INFO", "S28 success CDR XADD ok redis_id=$avp(s28_cdr_id) event_id=$var(s28_event_id) callid=$ci\n");
|
||||
} else {
|
||||
@@ -259,7 +309,7 @@ route[S28_CDR_SUCCESS] {
|
||||
route[S28_CDR_FAILURE] {
|
||||
$var(s28_event_id) = "s28-fail-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
$var(s28_idempotency_key) = $ci + ":s28-fail:" + $Ts;
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU customer_id $var(s28_customer_id) customer_gateway_id $var(s28_gateway_id) customer_gateway_policy_id $var(s28_policy_id) vendor_id none vendor_gateway_id none line_group_id $var(s28_line_group_id) started_at $Ts answered_at none ended_at $Ts duration_sec 0 sip_code $var(s28_reply_code) hangup_reason $var(s28_reason) recording_key none config_version $var(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU callee_city_code $var(s28_callee_city_code) callee_city_name $var(s28_callee_city_name) callee_province_name $var(s28_callee_province_name) callee_operator $var(s28_callee_operator) callee_number_type $var(s28_callee_number_type) customer_id $var(s28_customer_id) customer_gateway_id $var(s28_gateway_id) customer_gateway_policy_id $var(s28_policy_id) vendor_id none vendor_gateway_id none line_group_id $var(s28_line_group_id) started_at $Ts answered_at none ended_at $Ts duration_sec 0 sip_code $var(s28_reply_code) hangup_reason $var(s28_reason) recording_key none config_version $var(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
update_stat("s28_cdr_xadd_total", 1);
|
||||
} else {
|
||||
update_stat("s28_cdr_xadd_error_total", 1);
|
||||
|
||||
Reference in New Issue
Block a user