feat: complete phase2 baseline cdr quality rbac

This commit is contained in:
hectorzhao
2026-06-24 18:40:21 +08:00
parent 7057fd3c42
commit a86de6545f
63 changed files with 7853 additions and 3678 deletions
+231 -84
View File
@@ -1,37 +1,30 @@
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 source_ip = ARGV[1] or ''
local caller = ARGV[2] or ''
local callee = ARGV[3] or ''
local raw_callee = ARGV[3] or ''
local call_id = ARGV[4] or ''
local prefix = 'cfg:v:' .. active
local prefix = active and active ~= '' and ('cfg:v:' .. active) or ''
local number_info = nil
local function field(json, name)
if not json then
return nil
end
return string.match(json, '"' .. name .. '":"([^"]*)"')
end
local function number_field(json, name)
if not json then
return nil
end
return string.match(json, '"' .. name .. '":([0-9]+)')
end
local function starts_with(value, prefix_value)
return prefix_value ~= '' and string.sub(value, 1, string.len(prefix_value)) == prefix_value
end
local function normalize_callee(value)
local digits = string.gsub(value or '', '[^0-9]', '')
if string.sub(digits, 1, 4) == '0086' then
@@ -123,9 +116,49 @@ local function resolve_number(value)
return info
end
local number_info = resolve_number(callee)
local function ensure_number_info(callee_value)
if not number_info then
number_info = resolve_number(callee_value)
end
return number_info
end
local function result(decision, reason, gateway_id, version, line_group_id, policy_id, customer_id, vendor_id, vendor_gateway_id, host, port)
local function hash_value(value)
local hash = 0
for index = 1, string.len(value or '') do
hash = (hash * 31 + string.byte(value, index)) % 2147483647
end
return hash
end
local function choose_landing_caller(vendor_gateway_id)
local pool = redis.call('LRANGE', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':caller_rewrite_pool', 0, -1)
local total_weight = 0
local choices = {}
for _, item_json in ipairs(pool) do
if string.find(item_json, '"status":"ENABLED"', 1, true) or not string.find(item_json, '"status"', 1, true) then
local rewrite_caller = field(item_json, 'caller')
local weight = tonumber(number_field(item_json, 'weight') or '0') or 0
if rewrite_caller and rewrite_caller ~= '' and weight > 0 then
total_weight = total_weight + weight
choices[#choices + 1] = {rewrite_caller, total_weight}
end
end
end
if total_weight <= 0 then
return caller
end
local slot = (hash_value(call_id .. ':' .. vendor_gateway_id) % total_weight) + 1
for _, choice in ipairs(choices) do
if slot <= choice[2] then
return choice[1]
end
end
return choices[#choices][1]
end
local function result(decision, reason, gateway_id, version, line_group_id, policy_id, customer_id, vendor_id, vendor_gateway_id, host, port, real_callee, business_prefix_id, business_prefix, landing_caller, landing_callee)
local info = ensure_number_info(real_callee or raw_callee)
return {
decision,
reason,
@@ -138,51 +171,90 @@ local function result(decision, reason, gateway_id, version, line_group_id, poli
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
info.city_code,
info.city_name,
info.province_name,
info.carrier,
info.number_type,
info.province_code,
real_callee or raw_callee,
raw_callee,
business_prefix_id or 'none',
business_prefix or 'none',
landing_caller or caller,
landing_callee or info.normalized,
info.normalized ~= '' and info.normalized or 'none'
}
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')
if not active or active == '' then
return result('reject', 'CONFIG_MISSING', 'none', 'no_active_version', 'none', 'none', 'no_active_version', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
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')
local function customer_ok(gateway_id, gateway_json)
local customer_id = field(gateway_json, 'customerId')
if not customer_id then
return nil, 'CUSTOMER_MISSING', 'customer_id_missing'
end
local customer_json = redis.call('GET', prefix .. ':customer:' .. customer_id)
if not customer_json then
return nil, 'CUSTOMER_MISSING', 'customer_missing'
end
if not string.find(customer_json, '"status":"ENABLED"', 1, true) then
return nil, 'CUSTOMER_DISABLED', 'customer_disabled'
end
return customer_id, nil, nil
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')
local function match_caller(gateway_id, gateway_json)
local mode = field(gateway_json, 'callerMatchMode') or 'ANY'
if mode == 'ANY' then
return true
end
if mode == 'PREFIXES' then
local prefixes = redis.call('LRANGE', prefix .. ':customer_gateway:' .. gateway_id .. ':caller_prefixes', 0, -1)
for _, caller_prefix in ipairs(prefixes) do
if starts_with(caller, caller_prefix) then
return true
end
end
end
return false
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')
local function match_callee(gateway_id, gateway_json)
local mode = field(gateway_json, 'calleeMatchMode') or 'ANY'
if mode == 'ANY' then
return raw_callee, 'none', 'none'
end
if mode == 'BUSINESS_PREFIXES' then
local prefixes = redis.call('LRANGE', prefix .. ':customer_gateway:' .. gateway_id .. ':business_prefixes', 0, -1)
local best_prefix = ''
local best_prefix_id = 'none'
for _, item_json in ipairs(prefixes) do
local item_prefix = field(item_json, 'prefix') or ''
if starts_with(raw_callee, item_prefix) and string.len(item_prefix) > string.len(best_prefix) then
best_prefix = item_prefix
best_prefix_id = field(item_json, 'id') or 'none'
end
end
if best_prefix ~= '' then
return string.sub(raw_callee, string.len(best_prefix) + 1), best_prefix_id, best_prefix
end
end
return nil, '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)
local info = ensure_number_info(raw_callee)
if info.city_code ~= 'UNKNOWN' then
local city_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_city_codes', 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 info.province_code ~= 'UNKNOWN' then
local province_blocked = redis.call('SISMEMBER', prefix .. ':vendor_gateway:' .. vendor_gateway_id .. ':blocked_province_codes', info.province_code)
if province_blocked == 1 then
return true
end
@@ -190,7 +262,7 @@ local function region_blocked(vendor_gateway_id)
return false
end
local function first_vendor_route(line_group_id)
local function first_vendor_route(line_group_id, real_callee)
local line_group_json = redis.call('GET', prefix .. ':line_group:' .. line_group_id)
if not line_group_json then
return nil, 'LINE_GROUP_MISSING'
@@ -199,21 +271,27 @@ local function first_vendor_route(line_group_id)
return nil, 'LINE_GROUP_DISABLED'
end
number_info = resolve_number(real_callee)
local normalized_callee = number_info.normalized
local route_callee = normalized_callee ~= '' and normalized_callee or real_callee
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":"([^"]+)"')
local vendor_gateway_id = field(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'
local vendor_id = field(vendor_gateway_json, 'vendorId') or 'none'
local host = field(vendor_gateway_json, 'host') or 'none'
local port = number_field(vendor_gateway_json, 'port') or '5060'
local landing_prefix = field(vendor_gateway_json, 'landingCalleePrefix') or ''
local landing_callee = landing_prefix .. route_callee
local rewrite_caller = choose_landing_caller(vendor_gateway_id)
return {vendor_id, vendor_gateway_id, host, port, rewrite_caller, landing_callee}, 'OK'
end
end
end
@@ -226,31 +304,100 @@ local function first_vendor_route(line_group_id)
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 function legacy_policy_route(gateway_id, customer_id)
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 = field(policy_json, 'callerMode') or 'ANY'
local caller_value = field(policy_json, 'callerValue') or ''
local callee_mode = field(policy_json, 'calleeMode') or 'ANY'
local callee_value = field(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)
local caller_ok = caller_mode == 'ANY'
or (caller_mode == 'EQUALS' and caller == caller_value)
or (caller_mode == 'PREFIX' and starts_with(caller, caller_value))
local callee_ok = callee_mode == 'ANY'
or (callee_mode == 'EQUALS' and raw_callee == callee_value)
or (callee_mode == 'PREFIX' and starts_with(raw_callee, 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])
if caller_ok and callee_ok then
local policy_id = field(policy_json, 'id') or 'none'
local line_group_id = field(policy_json, 'lineGroupId') or 'none'
local vendor_route, route_reason = first_vendor_route(line_group_id, raw_callee)
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], raw_callee, 'none', 'none', vendor_route[5], vendor_route[6])
end
return result('reject', route_reason, gateway_id, active, line_group_id, policy_id, customer_id, 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
return result('reject', route_reason, gateway_id, active, line_group_id, policy_id, customer_id, 'none', 'none', 'none', 'none')
end
end
return nil
end
local candidates = redis.call('LRANGE', prefix .. ':auth:ip:' .. source_ip .. ':gateways', 0, -1)
if #candidates == 0 then
local legacy_gateway_id = redis.call('GET', prefix .. ':auth:ip:' .. source_ip)
if legacy_gateway_id then
candidates = {legacy_gateway_id}
end
end
if #candidates == 0 then
return result('reject', 'AUTH_MISSING', 'none', active, 'none', 'none', 'no_auth_ip', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
local saw_gateway = false
local saw_customer = false
local saw_caller = false
local saw_business_prefix_required = false
for _, gateway_id in ipairs(candidates) do
local gateway_json = redis.call('GET', prefix .. ':customer_gateway:' .. gateway_id)
if gateway_json and string.find(gateway_json, '"status":"ENABLED"', 1, true) then
saw_gateway = true
local customer_id, customer_reason, customer_marker = customer_ok(gateway_id, gateway_json)
if customer_id then
saw_customer = true
if match_caller(gateway_id, gateway_json) then
saw_caller = true
if (field(gateway_json, 'calleeMatchMode') or 'ANY') == 'BUSINESS_PREFIXES' then
saw_business_prefix_required = true
end
local real_callee, business_prefix_id, business_prefix = match_callee(gateway_id, gateway_json)
if real_callee then
local line_group_id = field(gateway_json, 'lineGroupId')
if line_group_id and line_group_id ~= '' then
local vendor_route, route_reason = first_vendor_route(line_group_id, real_callee)
if vendor_route then
return result('allow', 'OK', gateway_id, active, line_group_id, 'single_gateway', customer_id, vendor_route[1], vendor_route[2], vendor_route[3], vendor_route[4], real_callee, business_prefix_id, business_prefix, vendor_route[5], vendor_route[6])
end
return result('reject', route_reason, gateway_id, active, line_group_id, 'single_gateway', customer_id, 'none', 'none', 'none', 'none', real_callee, business_prefix_id, business_prefix, caller, real_callee)
end
local legacy = legacy_policy_route(gateway_id, customer_id)
if legacy then
return legacy
end
return result('reject', 'LINE_GROUP_MISSING', gateway_id, active, 'none', 'single_gateway', customer_id, 'none', 'none', 'none', 'none', real_callee, business_prefix_id, business_prefix, caller, real_callee)
end
end
else
return result('reject', customer_reason, gateway_id, active, 'none', 'none', customer_marker, 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
end
end
return result('reject', 'NO_POLICY', gateway_id, active, 'none', 'none', 'no_policy_match', 'none', 'none', 'none', 'none')
if not saw_gateway then
return result('reject', 'GATEWAY_MISSING', candidates[1] or 'none', active, 'none', 'none', 'gateway_missing', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
if not saw_customer then
return result('reject', 'CUSTOMER_MISSING', candidates[1] or 'none', active, 'none', 'none', 'customer_missing', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
if not saw_caller then
return result('reject', 'CALLER_PREFIX_NOT_MATCHED', candidates[1] or 'none', active, 'none', 'none', 'caller_prefix_not_matched', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
if saw_business_prefix_required then
return result('reject', 'BUSINESS_PREFIX_NOT_MATCHED', candidates[1] or 'none', active, 'none', 'none', 'business_prefix_not_matched', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
end
return result('reject', 'NO_POLICY', candidates[1] or 'none', active, 'none', 'none', 'no_gateway_match', 'none', 'none', 'none', 'none', raw_callee, 'none', 'none', caller, raw_callee)
+37 -7
View File
@@ -46,6 +46,8 @@ loadmodule "auth.so"
modparam("auth", "nonce_expire", 300)
modparam("auth", "disable_nonce_check", 0)
loadmodule "uac.so"
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 32)
@@ -182,11 +184,17 @@ route[S28_INVITE] {
$var(s28_callee_operator) = "UNKNOWN";
$var(s28_callee_number_type) = "UNKNOWN";
$var(s28_callee_province_code) = "UNKNOWN";
$var(s28_callee_normalized) = "";
$var(s28_real_callee) = $rU;
$var(s28_raw_callee) = $rU;
$var(s28_business_prefix_id) = "none";
$var(s28_business_prefix) = "none";
$var(s28_landing_caller) = $fU;
$var(s28_landing_callee) = $rU;
$var(s28_callee_normalized) = $rU;
$var(s28_reply_code) = 503;
$var(s28_reply_text) = "Routing Not Ready";
if (!cache_raw_query("redis:s20", "EVALSHA 229c06326b97d3d64b0b02deedec77a764a61045 1 cfg:active_version $si $fU $rU $ci", "$avp(s28_hotpath)")) {
if (!cache_raw_query("redis:s20", "EVALSHA 0b7ee595cdcad2f38a670dd8ff3553387820039e 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;
@@ -213,7 +221,13 @@ route[S28_INVITE] {
$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]);
$var(s28_real_callee) = $(avp(s28_hotpath)[17]);
$var(s28_raw_callee) = $(avp(s28_hotpath)[18]);
$var(s28_business_prefix_id) = $(avp(s28_hotpath)[19]);
$var(s28_business_prefix) = $(avp(s28_hotpath)[20]);
$var(s28_landing_caller) = $(avp(s28_hotpath)[21]);
$var(s28_landing_callee) = $(avp(s28_hotpath)[22]);
$var(s28_callee_normalized) = $(avp(s28_hotpath)[23]);
if ($var(s28_decision) != "allow") {
update_stat("s28_hotpath_reject_total", 1);
@@ -223,6 +237,12 @@ route[S28_INVITE] {
} 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 if ($var(s28_reason) == "BUSINESS_PREFIX_NOT_MATCHED") {
$var(s28_reply_code) = 404;
$var(s28_reply_text) = "Business Prefix Not Matched";
} else if ($var(s28_reason) == "CALLER_PREFIX_NOT_MATCHED") {
$var(s28_reply_code) = 403;
$var(s28_reply_text) = "Caller Prefix Not Matched";
} else {
$var(s28_reply_code) = 403;
$var(s28_reply_text) = "Forbidden";
@@ -251,7 +271,13 @@ route[S28_INVITE] {
$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) = $var(s28_real_callee);
$dlg_val(s28_raw_callee) = $var(s28_raw_callee);
$dlg_val(s28_business_prefix_id) = $var(s28_business_prefix_id);
$dlg_val(s28_business_prefix) = $var(s28_business_prefix);
$dlg_val(s28_landing_caller) = $var(s28_landing_caller);
$dlg_val(s28_landing_callee) = $var(s28_landing_callee);
$dlg_val(s28_callee_normalized) = $var(s28_callee_normalized);
$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);
@@ -272,7 +298,11 @@ route[S28_INVITE] {
record_route();
$du = "sip:" + $var(s28_vendor_host) + ":" + $var(s28_vendor_port);
$ru = "sip:" + $rU + "@" + $var(s28_vendor_host) + ":" + $var(s28_vendor_port);
$var(s28_landing_from_uri) = "sip:" + $var(s28_landing_caller) + "@" + $var(s28_vendor_host);
if ($var(s28_landing_caller) != $fU) {
uac_replace_from("", "$var(s28_landing_from_uri)");
}
$ru = "sip:" + $var(s28_landing_callee) + "@" + $var(s28_vendor_host) + ":" + $var(s28_vendor_port);
t_on_reply("S28_REPLY");
if (!t_relay()) {
@@ -297,7 +327,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 $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)")) {
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) raw_callee $dlg_val(s28_raw_callee) business_prefix_id $dlg_val(s28_business_prefix_id) business_prefix $dlg_val(s28_business_prefix) landing_caller $dlg_val(s28_landing_caller) landing_callee $dlg_val(s28_landing_callee) normalized_callee $dlg_val(s28_callee_normalized) 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 {
@@ -309,7 +339,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 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)")) {
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 $var(s28_real_callee) raw_callee $var(s28_raw_callee) business_prefix_id $var(s28_business_prefix_id) business_prefix $var(s28_business_prefix) landing_caller $var(s28_landing_caller) landing_callee $var(s28_landing_callee) normalized_callee $var(s28_callee_normalized) 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);