From cd0fab302fdce95920c2ea46508ac1e68872836a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 31 Dec 2024 18:45:59 +0200 Subject: [PATCH 1/4] per dst subscriber capacity limit --- app/admin/equipment/gateways.rb | 4 +- app/models/gateway.rb | 7 +- db/custom_seeds/yeti_ro_role.rb | 64 + ...231160003_dst_subscriber_capacity_limit.rb | 1623 +++++++++++++++++ db/seeds/main/class4.sql | 1 + db/seeds/main/switch22.sql | 1 + db/structure.sql | 21 +- spec/factories/gateways.rb | 1 + .../gateways/export_gateways_spec.rb | 1 + spec/models/gateway_spec.rb | 1 + 10 files changed, 1717 insertions(+), 7 deletions(-) create mode 100644 db/custom_seeds/yeti_ro_role.rb create mode 100644 db/migrate/20241231160003_dst_subscriber_capacity_limit.rb diff --git a/app/admin/equipment/gateways.rb b/app/admin/equipment/gateways.rb index cf6296b28..9f14eef7f 100644 --- a/app/admin/equipment/gateways.rb +++ b/app/admin/equipment/gateways.rb @@ -26,7 +26,7 @@ [:contractor_name, proc { |row| row.contractor.try(:name) }], :is_shared, :allow_origination, :allow_termination, :sst_enabled, - :origination_capacity, :termination_capacity, + :origination_capacity, :termination_capacity, :termination_subscriber_capacity, :preserve_anonymous_from_domain, [:termination_src_numberlist_name, proc { |row| row.termination_src_numberlist.try(:name) }], [:termination_dst_numberlist_name, proc { |row| row.termination_dst_numberlist.try(:name) }], @@ -357,6 +357,7 @@ def resource_params f.input :origination_capacity f.input :termination_capacity + f.input :termination_subscriber_capacity f.input :acd_limit f.input :asr_limit f.input :short_calls_limit @@ -551,6 +552,7 @@ def resource_params row :allow_termination row :origination_capacity row :termination_capacity + row :termination_subscriber_capacity row :acd_limit row :asr_limit row :short_calls_limit diff --git a/app/models/gateway.rb b/app/models/gateway.rb index 1fbdecbec..cb2159200 100644 --- a/app/models/gateway.rb +++ b/app/models/gateway.rb @@ -90,6 +90,7 @@ # term_outbound_proxy :string # term_use_outbound_proxy :boolean default(FALSE), not null # termination_capacity :integer(2) +# termination_subscriber_capacity :integer(2) # to_rewrite_result :string # to_rewrite_rule :string # transit_headers_from_origination :string @@ -310,7 +311,11 @@ def normalize_host(value) validates :max_30x_redirects, :max_transfers, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: PG_MAX_SMALLINT, allow_nil: true, only_integer: true } - validates :origination_capacity, :termination_capacity, numericality: { greater_than: 0, less_than_or_equal_to: PG_MAX_SMALLINT, allow_nil: true, only_integer: true } + validates :origination_capacity, + :termination_capacity, + :termination_subscriber_capacity, + numericality: { greater_than: 0, less_than_or_equal_to: PG_MAX_SMALLINT, allow_nil: true, only_integer: true } + validates :port, numericality: { greater_than_or_equal_to: ApplicationRecord::L4_PORT_MIN, less_than_or_equal_to: ApplicationRecord::L4_PORT_MAX, allow_nil: true, only_integer: true } validates :fake_180_timer, numericality: { greater_than: 0, less_than_or_equal_to: PG_MAX_SMALLINT, allow_nil: true, only_integer: true } diff --git a/db/custom_seeds/yeti_ro_role.rb b/db/custom_seeds/yeti_ro_role.rb new file mode 100644 index 000000000..bbaea37c0 --- /dev/null +++ b/db/custom_seeds/yeti_ro_role.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +routing_role = 'yeti_ro' +routing_schemas = %w[ + billing + class4 + data_import + gui + lnp + logs + notifications + ratemanagement + runtime_stats + sys +] +routing_schemas_with_partitioning = ['logs'] + +cdr_role = 'cdr_ro' +cdr_schemas = %w[ + auth_log + billing + cdr + event + external_data + reports + rtp_statistics + stats +] + +cdr_schemas_with_partitioning = %w[ + auth_log + cdr + rtp_statistics +] + +SqlCaller::Yeti.transaction do + SqlCaller::Yeti.execute "DROP OWNED BY #{routing_role}" + SqlCaller::Yeti.execute "DROP ROLE IF EXISTS #{routing_role}" + SqlCaller::Yeti.execute "CREATE ROLE #{routing_role} NOLOGIN" + + for s in routing_schemas do + SqlCaller::Yeti.execute "GRANT USAGE ON SCHEMA #{s} TO #{routing_role}" + SqlCaller::Yeti.execute "GRANT SELECT ON ALL TABLES IN SCHEMA #{s} TO #{routing_role}" + end + + for s in routing_schemas_with_partitioning do + SqlCaller::Yeti.execute "ALTER DEFAULT PRIVILEGES IN SCHEMA #{s} GRANT SELECT ON TABLES TO #{routing_role}" + end +end + +Cdr::Cdr.transaction do + SqlCaller::Cdr.execute "DROP OWNED BY #{cdr_role}" + SqlCaller::Cdr.execute "DROP ROLE IF EXISTS #{cdr_role}" + SqlCaller::Cdr.execute "CREATE ROLE #{cdr_role} NOLOGIN" + + for s in cdr_schemas do + SqlCaller::Cdr.execute "GRANT USAGE ON SCHEMA #{s} TO #{cdr_role}" + SqlCaller::Cdr.execute "GRANT SELECT ON ALL TABLES IN SCHEMA #{s} TO #{cdr_role}" + end + + for s in cdr_schemas_with_partitioning do + SqlCaller::Cdr.execute "ALTER DEFAULT PRIVILEGES IN SCHEMA #{s} GRANT SELECT ON TABLES TO #{cdr_role}" + end +end diff --git a/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb b/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb new file mode 100644 index 000000000..4766a21bf --- /dev/null +++ b/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb @@ -0,0 +1,1623 @@ +class DstSubscriberCapacityLimit < ActiveRecord::Migration[7.0] + def up + execute %q{ + insert into class4.disconnect_code (id, namespace_id, code, reason) values (1513, 1, 480, 'Subscriber capacity limit'); + INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUES (8, 'Subscriber capacity limit', 1513, 2); + + alter table class4.gateways add termination_subscriber_capacity smallint; + +CREATE OR REPLACE FUNCTION switch22.process_gw(i_profile switch22.callprofile_ty, i_destination class4.destinations, i_dp class4.dialpeers, i_customer_acc billing.accounts, i_customer_gw class4.gateways, i_vendor_acc billing.accounts, i_vendor_gw class4.gateways, i_send_billing_information boolean, i_max_call_length integer, i_diversion switch22.uri_ty[], i_privacy character varying[], i_pai switch22.uri_ty[], i_ppi switch22.uri_ty) RETURNS switch22.callprofile_ty + LANGUAGE plpgsql STABLE SECURITY DEFINER COST 100000 + AS $_$ +DECLARE + i integer; + v_vendor_allowtime real; + v_route_found boolean:=false; + v_from_user varchar; + v_from_domain varchar; + v_schema varchar; + v_termination_numberlist class4.numberlists%rowtype; + v_termination_numberlist_item class4.numberlist_items%rowtype; + v_termination_numberlist_size integer; + v_aleg_append_headers_reply varchar[] not null default ARRAY[]::varchar[]; + v_bleg_append_headers_req varchar[] not null default ARRAY[]::varchar[]; + v_diversion switch22.uri_ty[] not null default ARRAY[]::switch22.uri_ty[]; + v_diversion_header switch22.uri_ty; + v_pai switch22.uri_ty; + v_allow_pai boolean:=true; + v_to_uri_params varchar[] not null default ARRAY[]::varchar[]; + v_from_uri_params varchar[] not null default ARRAY[]::varchar[]; + v_ruri_host varchar; + v_ruri_params varchar[] not null default ARRAY[]::varchar[]; + v_ruri_user_params varchar[] not null default ARRAY[]::varchar[]; + v_to_username varchar; + v_customer_transit_headers_from_origination varchar[] default ARRAY[]::varchar[]; + v_vendor_transit_headers_from_origination varchar[] default ARRAY[]::varchar[]; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ +BEGIN + /*dbg{*/ + v_start:=now(); + --RAISE NOTICE 'process_dp in: %',i_profile; + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Found dialpeer: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(i_dp,true); + /*}dbg*/ + + --RAISE NOTICE 'process_dp dst: %',i_destination; + + i_profile.destination_id:=i_destination.id; + i_profile.destination_fee:=i_destination.connect_fee::varchar; + i_profile.destination_rate_policy_id:=i_destination.rate_policy_id; + + --vendor account capacity limit; + i_profile.legb_res= ''; + if i_vendor_acc.termination_capacity is not null then + i_profile.legb_res = '2:'||i_dp.account_id::varchar||':'||i_vendor_acc.termination_capacity::varchar||':1;'; + end if; + + if i_vendor_acc.total_capacity is not null then + i_profile.legb_res = i_profile.legb_res||'7:'||i_dp.account_id::varchar||':'||i_vendor_acc.total_capacity::varchar||':1;'; + end if; + + -- dialpeer account capacity limit; + if i_dp.capacity is not null then + i_profile.legb_res = i_profile.legb_res||'6:'||i_dp.id::varchar||':'||i_dp.capacity::varchar||':1;'; + end if; + + /* */ + i_profile.dialpeer_id=i_dp.id; + i_profile.dialpeer_prefix=i_dp.prefix; + i_profile.dialpeer_next_rate=i_dp.next_rate::varchar; + i_profile.dialpeer_initial_rate=i_dp.initial_rate::varchar; + i_profile.dialpeer_initial_interval=i_dp.initial_interval; + i_profile.dialpeer_next_interval=i_dp.next_interval; + i_profile.dialpeer_fee=i_dp.connect_fee::varchar; + i_profile.dialpeer_reverse_billing=i_dp.reverse_billing; + i_profile.vendor_id=i_dp.vendor_id; + i_profile.vendor_acc_id=i_dp.account_id; + i_profile.term_gw_id=i_vendor_gw.id; + + i_profile.orig_gw_name=i_customer_gw."name"; + i_profile.orig_gw_external_id=i_customer_gw.external_id; + + i_profile.term_gw_name=i_vendor_gw."name"; + i_profile.term_gw_external_id=i_vendor_gw.external_id; + + i_profile.customer_account_name=i_customer_acc."name"; + + i_profile.routing_group_id:=i_dp.routing_group_id; + + -- TODO. store arrays in GW and not convert it there + v_customer_transit_headers_from_origination = string_to_array(COALESCE(i_customer_gw.transit_headers_from_origination,''),','); + v_vendor_transit_headers_from_origination = string_to_array(COALESCE(i_vendor_gw.transit_headers_from_origination,''),','); + + if i_send_billing_information then + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-INIT-INT:'||i_profile.dialpeer_initial_interval)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-NEXT-INT:'||i_profile.dialpeer_next_interval)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-INIT-RATE:'||i_profile.dialpeer_initial_rate)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-NEXT-RATE:'||i_profile.dialpeer_next_rate)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-CF:'||i_profile.dialpeer_fee)::varchar); + end if; + v_aleg_append_headers_reply = array_cat(v_aleg_append_headers_reply,i_customer_gw.orig_append_headers_reply); + i_profile.aleg_append_headers_reply=ARRAY_TO_STRING(v_aleg_append_headers_reply,'\r\n'); + + if i_destination.use_dp_intervals THEN + i_profile.destination_initial_interval:=i_dp.initial_interval; + i_profile.destination_next_interval:=i_dp.next_interval; + ELSE + i_profile.destination_initial_interval:=i_destination.initial_interval; + i_profile.destination_next_interval:=i_destination.next_interval; + end if; + + IF i_profile.package_counter_id IS NULL THEN + CASE i_profile.destination_rate_policy_id + WHEN 1 THEN -- fixed + i_profile.destination_next_rate:=i_destination.next_rate::varchar; + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + WHEN 2 THEN -- based on dialpeer + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + WHEN 3 THEN -- min + IF i_dp.next_rate >= i_destination.next_rate THEN + i_profile.destination_next_rate:=i_destination.next_rate::varchar; -- FIXED least + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + ELSE + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; -- DYNAMIC + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + END IF; + WHEN 4 THEN -- max + IF i_dp.next_rate < i_destination.next_rate THEN + i_profile.destination_next_rate:=i_destination.next_rate::varchar; --FIXED + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + ELSE + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; -- DYNAMIC + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + END IF; + ELSE + -- + end case; + END IF; + + + /* time limiting START */ + --SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth.account_id; + --SELECT INTO STRICT v_v_acc * FROM billing.accounts WHERE id=v_dialpeer.account_id; + + + if i_profile.time_limit is null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: customer time limit is not set, calculating',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + IF (i_customer_acc.balance-i_customer_acc.min_balance)-i_destination.connect_fee-i_destination.initial_rate/60*i_destination.initial_interval<0 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: No enough customer balance even for first billing interval. rejecting',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + i_profile.disconnect_code_id=8000; --Not enough customer balance + RETURN i_profile; + ELSIF i_destination.next_rate!=0 AND i_destination.next_interval!=0 THEN + i_profile.time_limit = (i_destination.initial_interval+ + LEAST(FLOOR(((i_customer_acc.balance-i_customer_acc.min_balance)-i_destination.connect_fee-i_destination.initial_rate/60*i_destination.initial_interval)/ + (i_destination.next_rate/60*i_destination.next_interval)),24e6)::integer*i_destination.next_interval)::integer; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: customer time limit: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.time_limit; + /*}dbg*/ + ELSE /* DST rates is 0, allowing maximum call length */ + i_profile.time_limit = COALESCE(i_customer_acc.max_call_duration, i_max_call_length)::integer; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: DST rate is 0. customer time limit set to max value: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.time_limit; + /*}dbg*/ + end IF; + end if; + + IF (i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee <0 THEN /* No enough balance, skipping this profile */ + v_vendor_allowtime:=0; + return null; + ELSIF (i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee-i_dp.initial_rate/60*i_dp.initial_interval<0 THEN /* No enough balance even for first billing interval - skipping this profile */ + return null; + ELSIF i_dp.next_rate!=0 AND i_dp.next_interval!=0 THEN /* DP rates is not zero, calculating limit */ + v_vendor_allowtime:=i_dp.initial_interval+ + LEAST(FLOOR(((i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee-i_dp.initial_rate/60*i_dp.initial_interval)/ + (i_dp.next_rate/60*i_dp.next_interval)),24e6)::integer*i_dp.next_interval; + ELSE /* DP rates is 0, allowing maximum call length */ + v_vendor_allowtime:=COALESCE(i_vendor_acc.max_call_duration, i_max_call_length); + end IF; + + i_profile.time_limit=LEAST( + COALESCE(i_customer_acc.max_call_duration, i_max_call_length)::integer, + COALESCE(i_vendor_acc.max_call_duration, i_max_call_length)::integer, + v_vendor_allowtime, + i_profile.time_limit + )::integer; + + + /* number rewriting _After_ routing */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out,i_dp.dst_rewrite_rule,i_dp.dst_rewrite_result); + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand(i_profile.src_prefix_out,i_dp.src_rewrite_rule,i_dp.src_rewrite_result); + i_profile.src_name_out=yeti_ext.regexp_replace_rand(i_profile.src_name_out,i_dp.src_name_rewrite_rule,i_dp.src_name_rewrite_result, true); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + /* + get termination gw data + */ + --SELECT into v_dst_gw * from class4.gateways WHERE id=v_dialpeer.gateway_id; + --SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth.gateway_id; + --vendor gw + if i_vendor_gw.termination_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'5:'||i_vendor_gw.id::varchar||':'||i_vendor_gw.termination_capacity::varchar||':1;'; + end if; + + /* + numberlist processing _After_ routing _IN_ termination GW + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. Before numberlist processing src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + + ----- DST Numberlist processing------------------------------------------------------------------------------------------------------- + IF i_vendor_gw.termination_dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.dst_prefix_out; + /*}dbg*/ + + select into v_termination_numberlist * from class4.numberlists where id=i_vendor_gw.termination_dst_numberlist_id; + CASE v_termination_numberlist.mode_id + when 1 then -- strict match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id and + ni.key=i_profile.dst_prefix_out + limit 1; + when 2 then -- prefix match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(i_profile.dst_prefix_out) and + length(i_profile.dst_prefix_out) between ni.number_min_length and ni.number_max_length + order by length(prefix_range(ni.key)) desc + limit 1; + when 3 then -- random + select into v_termination_numberlist_size count(*) from class4.numberlist_items where numberlist_id=i_vendor_gw.termination_dst_numberlist_id; + select into v_termination_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id order by ni.id OFFSET floor(random()*v_termination_numberlist_size) limit 1; + END CASE; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_termination_numberlist_item); + /*}dbg*/ + + IF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by key action. Skipping route. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_termination_numberlist_item.key; + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist_item.src_rewrite_rule, + v_termination_numberlist_item.src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist_item.dst_rewrite_rule, + v_termination_numberlist_item.dst_rewrite_result + ); + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by default action. Skipping route',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist.default_src_rewrite_rule, + v_termination_numberlist.default_src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist.default_dst_rewrite_rule, + v_termination_numberlist.default_dst_rewrite_result + ); + END IF; + END IF; + + + + ----- SRC Numberlist processing------------------------------------------------------------------------------------------------------- + IF i_vendor_gw.termination_src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.src_prefix_out; + /*}dbg*/ + + select into v_termination_numberlist * from class4.numberlists where id=i_vendor_gw.termination_src_numberlist_id; + CASE v_termination_numberlist.mode_id + when 1 then -- strict match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id and + ni.key=i_profile.src_prefix_out + limit 1; + when 2 then -- prefix match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id and + prefix_range(ni.key)@>prefix_range(i_profile.src_prefix_out) and + length(i_profile.src_prefix_out) between ni.number_min_length and ni.number_max_length + order by length(prefix_range(ni.key)) desc + limit 1; + when 3 then -- random + select into v_termination_numberlist_size count(*) from class4.numberlist_items where numberlist_id=i_vendor_gw.termination_src_numberlist_id; + select into v_termination_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id order by ni.id OFFSET floor(random()*v_termination_numberlist_size) limit 1; + END CASE; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_termination_numberlist_item); + /*}dbg*/ + + IF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist. Drop by key action. Skipping route. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_termination_numberlist_item.key; + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist_item.src_rewrite_rule, + v_termination_numberlist_item.src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist_item.dst_rewrite_rule, + v_termination_numberlist_item.dst_rewrite_result + ); + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by default action. Skipping route.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + RETURN null; + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist.default_src_rewrite_rule, + v_termination_numberlist.default_src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist.default_dst_rewrite_rule, + v_termination_numberlist.default_dst_rewrite_result + ); + END IF; + END IF; + + + + /* + number rewriting _After_ routing _IN_ termination GW + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out,i_vendor_gw.dst_rewrite_rule,i_vendor_gw.dst_rewrite_result); + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand(i_profile.src_prefix_out,i_vendor_gw.src_rewrite_rule,i_vendor_gw.src_rewrite_result); + i_profile.src_name_out=yeti_ext.regexp_replace_rand(i_profile.src_name_out,i_vendor_gw.src_name_rewrite_rule,i_vendor_gw.src_name_rewrite_result, true); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + -- apply capacity limit by destination number + if i_vendor_gw.termination_subscriber_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + end if; + + IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN + IF i_vendor_gw.diversion_send_mode_id = 2 AND i_vendor_gw.diversion_domain is not null AND i_vendor_gw.diversion_domain!='' THEN + /* Diversion as SIP URI */ + FOREACH v_diversion_header IN ARRAY i_diversion LOOP + v_diversion_header.u = yeti_ext.regexp_replace_rand(v_diversion_header.u, i_vendor_gw.diversion_rewrite_rule, i_vendor_gw.diversion_rewrite_result); + v_diversion_header.s = 'sip'; + v_diversion_header.h = i_vendor_gw.diversion_domain; + v_bleg_append_headers_req = array_append( + v_bleg_append_headers_req, + 'Diversion: '||switch22.build_uri(false, v_diversion_header) + ); + END LOOP; + ELSIF i_vendor_gw.diversion_send_mode_id = 3 THEN + /* Diversion as TEL URI */ + FOREACH v_diversion_header IN ARRAY i_diversion LOOP + v_diversion_header.u = yeti_ext.regexp_replace_rand(v_diversion_header.u, i_vendor_gw.diversion_rewrite_rule, i_vendor_gw.diversion_rewrite_result); + v_diversion_header.s = 'tel'; + v_bleg_append_headers_req=array_append( + v_bleg_append_headers_req, + 'Diversion: '||switch22.build_uri(false, v_diversion_header) + ); + END LOOP; + END IF; + END IF; + + CASE i_vendor_gw.privacy_mode_id + WHEN 0 THEN + -- do nothing + WHEN 1 THEN + IF cardinality(array_remove(i_privacy,'none')) > 0 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested but privacy_mode_is %. Skipping gw.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + return null; + END IF; + WHEN 2 THEN + IF 'critical' = ANY(i_privacy) THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested but privacy_mode_is %. Skipping gw.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + return null; + END IF; + WHEN 3 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. Applying privacy.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF 'id' = ANY(i_privacy) OR 'user' = ANY(i_privacy) THEN + i_profile.src_prefix_out='anonymous'; + i_profile.src_name_out='Anonymous'; + v_from_domain = 'anonymous.invalid'; + END IF; + IF 'id' = ANY(i_privacy) OR 'header' = ANY(i_privacy) THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. removing PAI/PPI headers.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + v_allow_pai = false; + END IF; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + WHEN 4 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. forwarding.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF cardinality(i_privacy)>0 THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + END IF; + WHEN 5 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. forwarding with anonymous From.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF 'id' = ANY(i_privacy) or 'user' = ANY(i_privacy) THEN + i_profile.src_prefix_out='anonymous'; + i_profile.src_name_out='Anonymous'; + v_from_domain = 'anonymous.invalid'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + END IF; + END CASE; + + IF v_allow_pai THEN + -- only if privacy mode allows to send PAI + IF i_vendor_gw.pai_send_mode_id = 1 THEN + -- TEL URI + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 2 and i_vendor_gw.pai_domain is not null and i_vendor_gw.pai_domain!='' THEN + -- SIP URL + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out, i_vendor_gw.pai_domain)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 3 and i_vendor_gw.pai_domain is not null and i_vendor_gw.pai_domain!='' THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out, i_vendor_gw.pai_domain)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 4 THEN + -- relay + FOREACH v_pai IN ARRAY i_pai LOOP + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 5 THEN + -- relay with conversion to tel URI + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'tel'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'tel'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 6 THEN + -- relay with conversion to SIP URI + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'sip'; + v_pai.h = COALESCE(v_pai.h, i_vendor_gw.pai_domain); + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'sip'; + i_ppi.h = COALESCE(i_ppi.h, i_vendor_gw.pai_domain); + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 7 THEN + -- relay with conversion to SIP URI. Force replace domain + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'sip'; + v_pai.h = i_vendor_gw.pai_domain; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'sip'; + i_ppi.h = i_vendor_gw.pai_domain; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + END IF; + + END IF; + + IF i_vendor_gw.stir_shaken_mode_id IN (1,2) THEN + IF i_profile.lega_ss_status_id >0 THEN + -- relaying valid header from customer + i_profile.legb_ss_status_id = i_profile.lega_ss_status_id; + v_customer_transit_headers_from_origination = array_append(v_customer_transit_headers_from_origination,'Identity'); + v_vendor_transit_headers_from_origination = array_append(v_vendor_transit_headers_from_origination,'Identity'); + ELSIF COALESCE(i_profile.ss_attest_id,0) > 0 AND i_vendor_gw.stir_shaken_crt_id IS NOT NULL THEN + -- insert our signature + i_profile.ss_crt_id = i_vendor_gw.stir_shaken_crt_id; + i_profile.legb_ss_status_id = i_profile.ss_attest_id; + + IF i_vendor_gw.stir_shaken_mode_id = 1 THEN + i_profile.ss_otn = i_profile.src_prefix_routing; + i_profile.ss_dtn = i_profile.dst_prefix_routing; + ELSIF i_vendor_gw.stir_shaken_mode_id = 2 THEN + i_profile.ss_otn = i_profile.src_prefix_out; + i_profile.ss_dtn = i_profile.dst_prefix_out; + END IF; + END IF; + END IF ; + + v_bleg_append_headers_req = array_cat(v_bleg_append_headers_req, i_vendor_gw.term_append_headers_req); + i_profile.append_headers_req = array_to_string(v_bleg_append_headers_req,'\r\n'); + + i_profile.aleg_append_headers_req = array_to_string(i_customer_gw.orig_append_headers_req,'\r\n'); + + i_profile.next_hop_1st_req=i_vendor_gw.auth_enabled; -- use low delay dns srv if auth enabled + i_profile.next_hop:=i_vendor_gw.term_next_hop; + i_profile.aleg_next_hop:=i_customer_gw.orig_next_hop; + -- i_profile.next_hop_for_replies:=v_dst_gw.term_next_hop_for_replies; + + i_profile.dlg_nat_handling=i_customer_gw.dialog_nat_handling; + + i_profile.call_id:=''; -- Generation by sems + + i_profile.enable_auth:=i_vendor_gw.auth_enabled; + i_profile.auth_pwd:=i_vendor_gw.auth_password; + i_profile.auth_user:=i_vendor_gw.auth_user; + i_profile.enable_aleg_auth:=false; + i_profile.auth_aleg_pwd:=''; + i_profile.auth_aleg_user:=''; + + if i_profile.enable_auth then + v_from_user=COALESCE(i_vendor_gw.auth_from_user,i_profile.src_prefix_out,''); + -- may be it already defined by privacy logic + v_from_domain=COALESCE(v_from_domain, i_vendor_gw.auth_from_domain, '$Oi'); + else + v_from_user=COALESCE(i_profile.src_prefix_out,''); + if i_vendor_gw.preserve_anonymous_from_domain and i_profile.from_domain='anonymous.invalid' then + v_from_domain='anonymous.invalid'; + else + v_from_domain=COALESCE(v_from_domain, '$Oi'); + end if; + end if; + + v_to_username = yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out, i_vendor_gw.to_rewrite_rule, i_vendor_gw.to_rewrite_result); + + if i_vendor_gw.sip_schema_id = 1 then + v_schema='sip'; + elsif i_vendor_gw.sip_schema_id = 2 then + v_schema='sips'; + elsif i_vendor_gw.sip_schema_id = 3 then + v_schema='sip'; + -- user=phone param require e.164 with + in username, but we are not forcing it + v_from_uri_params = array_append(v_from_uri_params,'user=phone'); + v_to_uri_params = array_append(v_to_uri_params,'user=phone'); + v_ruri_params = array_append(v_ruri_params,'user=phone'); + else + RAISE exception 'Unknown termination gateway % SIP schema %', i_vendor_gw.id, i_vendor_gw.sip_schema_id; + end if; + + if i_vendor_gw.send_lnp_information and i_profile.lrn is not null then + if i_profile.lrn=i_profile.dst_prefix_routing then -- number not ported, but request was successf we musr add ;npdi=yes; + v_ruri_user_params = array_append(v_ruri_user_params, 'npdi=yes'); + i_profile.lrn=nullif(i_profile.dst_prefix_routing,i_profile.lrn); -- clear lnr field if number not ported; + else -- if number ported + v_ruri_user_params = array_append(v_ruri_user_params, 'rn='||i_profile.lrn); + v_ruri_user_params = array_append(v_ruri_user_params, 'npdi=yes'); + end if; + end if; + + i_profile.registered_aor_mode_id = i_vendor_gw.registered_aor_mode_id; + if i_vendor_gw.registered_aor_mode_id > 0 then + i_profile.registered_aor_id=i_vendor_gw.id; + v_ruri_host = 'unknown.invalid'; + else + v_ruri_host = i_vendor_gw.host; + end if; + + i_profile."from" = switch22.build_uri(false, v_schema, i_profile.src_name_out, v_from_user, null, v_from_domain, null, v_from_uri_params); + + i_profile."to" = switch22.build_uri(false, v_schema, null, v_to_username, null, v_ruri_host, i_vendor_gw.port, v_to_uri_params); + i_profile.ruri = switch22.build_uri(true, v_schema, null, i_profile.dst_prefix_out, v_ruri_user_params, v_ruri_host, i_vendor_gw.port, v_ruri_params); + + i_profile.bleg_transport_protocol_id:=i_vendor_gw.transport_protocol_id; + i_profile.bleg_protocol_priority_id:=i_vendor_gw.network_protocol_priority_id; + + i_profile.aleg_media_encryption_mode_id:=i_customer_gw.media_encryption_mode_id; + i_profile.bleg_media_encryption_mode_id:=i_vendor_gw.media_encryption_mode_id; + + IF (i_vendor_gw.term_use_outbound_proxy ) THEN + i_profile.outbound_proxy:=v_schema||':'||i_vendor_gw.term_outbound_proxy; + i_profile.force_outbound_proxy:=i_vendor_gw.term_force_outbound_proxy; + i_profile.bleg_outbound_proxy_transport_protocol_id:=i_vendor_gw.term_proxy_transport_protocol_id; + ELSE + i_profile.outbound_proxy:=NULL; + i_profile.force_outbound_proxy:=false; + END IF; + + IF (i_customer_gw.orig_use_outbound_proxy ) THEN + i_profile.aleg_force_outbound_proxy:=i_customer_gw.orig_force_outbound_proxy; + i_profile.aleg_outbound_proxy=v_schema||':'||i_customer_gw.orig_outbound_proxy; + i_profile.aleg_outbound_proxy_transport_protocol_id:=i_customer_gw.orig_proxy_transport_protocol_id; + else + i_profile.aleg_force_outbound_proxy:=FALSE; + i_profile.aleg_outbound_proxy=NULL; + end if; + + i_profile.aleg_policy_id=i_customer_gw.orig_disconnect_policy_id; + i_profile.bleg_policy_id=i_vendor_gw.term_disconnect_policy_id; + + i_profile.transit_headers_a2b:=array_to_string(v_customer_transit_headers_from_origination,',')||';'||array_to_string(v_vendor_transit_headers_from_origination,','); + i_profile.transit_headers_b2a:=i_vendor_gw.transit_headers_from_termination||';'||i_customer_gw.transit_headers_from_termination; + + i_profile.sdp_filter_type_id:=0; + i_profile.sdp_filter_list:=''; + + i_profile.sdp_alines_filter_type_id:=i_vendor_gw.sdp_alines_filter_type_id; + i_profile.sdp_alines_filter_list:=i_vendor_gw.sdp_alines_filter_list; + + i_profile.enable_session_timer=i_vendor_gw.sst_enabled; + i_profile.session_expires =i_vendor_gw.sst_session_expires; + i_profile.minimum_timer:=i_vendor_gw.sst_minimum_timer; + i_profile.maximum_timer:=i_vendor_gw.sst_maximum_timer; + i_profile.session_refresh_method_id:=i_vendor_gw.session_refresh_method_id; + i_profile.accept_501_reply:=i_vendor_gw.sst_accept501; + + i_profile.enable_aleg_session_timer=i_customer_gw.sst_enabled; + i_profile.aleg_session_expires:=i_customer_gw.sst_session_expires; + i_profile.aleg_minimum_timer:=i_customer_gw.sst_minimum_timer; + i_profile.aleg_maximum_timer:=i_customer_gw.sst_maximum_timer; + i_profile.aleg_session_refresh_method_id:=i_customer_gw.session_refresh_method_id; + i_profile.aleg_accept_501_reply:=i_customer_gw.sst_accept501; + + i_profile.reply_translations:=''; + i_profile.disconnect_code_id:=NULL; + i_profile.enable_rtprelay:=i_vendor_gw.proxy_media OR i_customer_gw.proxy_media; + + i_profile.rtprelay_interface:=i_vendor_gw.rtp_interface_name; + i_profile.aleg_rtprelay_interface:=i_customer_gw.rtp_interface_name; + + i_profile.outbound_interface:=i_vendor_gw.sip_interface_name; + i_profile.aleg_outbound_interface:=i_customer_gw.sip_interface_name; + + i_profile.bleg_force_symmetric_rtp:=i_vendor_gw.force_symmetric_rtp; + i_profile.bleg_symmetric_rtp_nonstop=i_vendor_gw.symmetric_rtp_nonstop; + + i_profile.aleg_force_symmetric_rtp:=i_customer_gw.force_symmetric_rtp; + i_profile.aleg_symmetric_rtp_nonstop=i_customer_gw.symmetric_rtp_nonstop; + + i_profile.bleg_rtp_ping=i_vendor_gw.rtp_ping; + i_profile.aleg_rtp_ping=i_customer_gw.rtp_ping; + + i_profile.bleg_relay_options = i_vendor_gw.relay_options; + i_profile.aleg_relay_options = i_customer_gw.relay_options; + + + i_profile.filter_noaudio_streams = i_vendor_gw.filter_noaudio_streams OR i_customer_gw.filter_noaudio_streams; + i_profile.force_one_way_early_media = i_vendor_gw.force_one_way_early_media OR i_customer_gw.force_one_way_early_media; + i_profile.aleg_relay_reinvite = i_vendor_gw.relay_reinvite; + i_profile.bleg_relay_reinvite = i_customer_gw.relay_reinvite; + + i_profile.aleg_relay_hold = i_vendor_gw.relay_hold; + i_profile.bleg_relay_hold = i_customer_gw.relay_hold; + + i_profile.aleg_relay_prack = i_vendor_gw.relay_prack; + i_profile.bleg_relay_prack = i_customer_gw.relay_prack; + i_profile.aleg_rel100_mode_id = i_customer_gw.rel100_mode_id; + i_profile.bleg_rel100_mode_id = i_vendor_gw.rel100_mode_id; + + i_profile.rtp_relay_timestamp_aligning=i_vendor_gw.rtp_relay_timestamp_aligning OR i_customer_gw.rtp_relay_timestamp_aligning; + i_profile.allow_1xx_wo2tag=i_vendor_gw.allow_1xx_without_to_tag OR i_customer_gw.allow_1xx_without_to_tag; + + i_profile.aleg_sdp_c_location_id=i_customer_gw.sdp_c_location_id; + i_profile.bleg_sdp_c_location_id=i_vendor_gw.sdp_c_location_id; + i_profile.trusted_hdrs_gw=false; + + + + i_profile.aleg_codecs_group_id:=i_customer_gw.codec_group_id; + i_profile.bleg_codecs_group_id:=i_vendor_gw.codec_group_id; + i_profile.aleg_single_codec_in_200ok:=i_customer_gw.single_codec_in_200ok; + i_profile.bleg_single_codec_in_200ok:=i_vendor_gw.single_codec_in_200ok; + i_profile.try_avoid_transcoding = i_customer_gw.try_avoid_transcoding; + i_profile.ringing_timeout=i_vendor_gw.ringing_timeout; + i_profile.dead_rtp_time=GREATEST(i_vendor_gw.rtp_timeout,i_customer_gw.rtp_timeout); + i_profile.invite_timeout=i_vendor_gw.sip_timer_b; + i_profile.srv_failover_timeout=i_vendor_gw.dns_srv_failover_timer; + i_profile.fake_180_timer=i_vendor_gw.fake_180_timer; + i_profile.rtp_force_relay_cn=i_vendor_gw.rtp_force_relay_cn OR i_customer_gw.rtp_force_relay_cn; + i_profile.patch_ruri_next_hop=i_vendor_gw.resolve_ruri; + + i_profile.aleg_sensor_id=i_customer_gw.sensor_id; + i_profile.aleg_sensor_level_id=i_customer_gw.sensor_level_id; + i_profile.bleg_sensor_id=i_vendor_gw.sensor_id; + i_profile.bleg_sensor_level_id=i_vendor_gw.sensor_level_id; + + i_profile.aleg_dtmf_send_mode_id=i_customer_gw.dtmf_send_mode_id; + i_profile.aleg_dtmf_recv_modes=i_customer_gw.dtmf_receive_mode_id; + i_profile.bleg_dtmf_send_mode_id=i_vendor_gw.dtmf_send_mode_id; + i_profile.bleg_dtmf_recv_modes=i_vendor_gw.dtmf_receive_mode_id; + + + i_profile.aleg_rtp_filter_inband_dtmf=false; + i_profile.bleg_rtp_filter_inband_dtmf=false; + + if i_customer_gw.rx_inband_dtmf_filtering_mode_id=3 then -- enable filtering + i_profile.aleg_rtp_filter_inband_dtmf=true; + elsif i_customer_gw.rx_inband_dtmf_filtering_mode_id=1 then -- inherit + if i_vendor_gw.tx_inband_dtmf_filtering_mode_id in (1,2) then -- inherit or disable filtering + i_profile.aleg_rtp_filter_inband_dtmf=false; + elsif i_vendor_gw.tx_inband_dtmf_filtering_mode_id = 3 then -- enable filtering + i_profile.aleg_rtp_filter_inband_dtmf=true; + end if; + end if; + + + if i_vendor_gw.rx_inband_dtmf_filtering_mode_id=3 then -- enable filtering + i_profile.bleg_rtp_filter_inband_dtmf=true; + elsif i_vendor_gw.rx_inband_dtmf_filtering_mode_id=1 then -- inherit + if i_customer_gw.tx_inband_dtmf_filtering_mode_id in (1,2) then -- inherit or disable filtering + i_profile.bleg_rtp_filter_inband_dtmf=false; + elsif i_customer_gw.tx_inband_dtmf_filtering_mode_id = 3 then -- enable filtering + i_profile.bleg_rtp_filter_inband_dtmf=true; + end if; + end if; + + i_profile.aleg_rtp_acl = i_customer_gw.rtp_acl; + i_profile.bleg_rtp_acl = i_vendor_gw.rtp_acl; + + i_profile.rtprelay_force_dtmf_relay=i_vendor_gw.force_dtmf_relay; + i_profile.rtprelay_dtmf_detection=NOT i_vendor_gw.force_dtmf_relay; + i_profile.rtprelay_dtmf_filtering=NOT i_vendor_gw.force_dtmf_relay; + i_profile.bleg_max_30x_redirects = i_vendor_gw.max_30x_redirects; + i_profile.bleg_max_transfers = i_vendor_gw.max_transfers; + + + i_profile.aleg_relay_update=i_customer_gw.relay_update; + i_profile.bleg_relay_update=i_vendor_gw.relay_update; + i_profile.suppress_early_media=i_customer_gw.suppress_early_media OR i_vendor_gw.suppress_early_media; + + i_profile.bleg_radius_acc_profile_id=i_vendor_gw.radius_accounting_profile_id; + i_profile.bleg_force_cancel_routeset=i_vendor_gw.force_cancel_routeset; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Finished: % ',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(i_profile,true); + /*}dbg*/ + RETURN i_profile; +END; +$_$; + + set search_path TO switch22; + SELECT * from switch22.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + + } + end + + def down + execute %q{ + +CREATE OR REPLACE FUNCTION switch22.process_gw(i_profile switch22.callprofile_ty, i_destination class4.destinations, i_dp class4.dialpeers, i_customer_acc billing.accounts, i_customer_gw class4.gateways, i_vendor_acc billing.accounts, i_vendor_gw class4.gateways, i_send_billing_information boolean, i_max_call_length integer, i_diversion switch22.uri_ty[], i_privacy character varying[], i_pai switch22.uri_ty[], i_ppi switch22.uri_ty) RETURNS switch22.callprofile_ty + LANGUAGE plpgsql STABLE SECURITY DEFINER COST 100000 + AS $_$ +DECLARE + i integer; + v_vendor_allowtime real; + v_route_found boolean:=false; + v_from_user varchar; + v_from_domain varchar; + v_schema varchar; + v_termination_numberlist class4.numberlists%rowtype; + v_termination_numberlist_item class4.numberlist_items%rowtype; + v_termination_numberlist_size integer; + v_aleg_append_headers_reply varchar[] not null default ARRAY[]::varchar[]; + v_bleg_append_headers_req varchar[] not null default ARRAY[]::varchar[]; + v_diversion switch22.uri_ty[] not null default ARRAY[]::switch22.uri_ty[]; + v_diversion_header switch22.uri_ty; + v_pai switch22.uri_ty; + v_allow_pai boolean:=true; + v_to_uri_params varchar[] not null default ARRAY[]::varchar[]; + v_from_uri_params varchar[] not null default ARRAY[]::varchar[]; + v_ruri_host varchar; + v_ruri_params varchar[] not null default ARRAY[]::varchar[]; + v_ruri_user_params varchar[] not null default ARRAY[]::varchar[]; + v_to_username varchar; + v_customer_transit_headers_from_origination varchar[] default ARRAY[]::varchar[]; + v_vendor_transit_headers_from_origination varchar[] default ARRAY[]::varchar[]; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ +BEGIN + /*dbg{*/ + v_start:=now(); + --RAISE NOTICE 'process_dp in: %',i_profile; + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Found dialpeer: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(i_dp,true); + /*}dbg*/ + + --RAISE NOTICE 'process_dp dst: %',i_destination; + + i_profile.destination_id:=i_destination.id; + i_profile.destination_fee:=i_destination.connect_fee::varchar; + i_profile.destination_rate_policy_id:=i_destination.rate_policy_id; + + --vendor account capacity limit; + i_profile.legb_res= ''; + if i_vendor_acc.termination_capacity is not null then + i_profile.legb_res = '2:'||i_dp.account_id::varchar||':'||i_vendor_acc.termination_capacity::varchar||':1;'; + end if; + + if i_vendor_acc.total_capacity is not null then + i_profile.legb_res = i_profile.legb_res||'7:'||i_dp.account_id::varchar||':'||i_vendor_acc.total_capacity::varchar||':1;'; + end if; + + -- dialpeer account capacity limit; + if i_dp.capacity is not null then + i_profile.legb_res = i_profile.legb_res||'6:'||i_dp.id::varchar||':'||i_dp.capacity::varchar||':1;'; + end if; + + /* */ + i_profile.dialpeer_id=i_dp.id; + i_profile.dialpeer_prefix=i_dp.prefix; + i_profile.dialpeer_next_rate=i_dp.next_rate::varchar; + i_profile.dialpeer_initial_rate=i_dp.initial_rate::varchar; + i_profile.dialpeer_initial_interval=i_dp.initial_interval; + i_profile.dialpeer_next_interval=i_dp.next_interval; + i_profile.dialpeer_fee=i_dp.connect_fee::varchar; + i_profile.dialpeer_reverse_billing=i_dp.reverse_billing; + i_profile.vendor_id=i_dp.vendor_id; + i_profile.vendor_acc_id=i_dp.account_id; + i_profile.term_gw_id=i_vendor_gw.id; + + i_profile.orig_gw_name=i_customer_gw."name"; + i_profile.orig_gw_external_id=i_customer_gw.external_id; + + i_profile.term_gw_name=i_vendor_gw."name"; + i_profile.term_gw_external_id=i_vendor_gw.external_id; + + i_profile.customer_account_name=i_customer_acc."name"; + + i_profile.routing_group_id:=i_dp.routing_group_id; + + -- TODO. store arrays in GW and not convert it there + v_customer_transit_headers_from_origination = string_to_array(COALESCE(i_customer_gw.transit_headers_from_origination,''),','); + v_vendor_transit_headers_from_origination = string_to_array(COALESCE(i_vendor_gw.transit_headers_from_origination,''),','); + + if i_send_billing_information then + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-INIT-INT:'||i_profile.dialpeer_initial_interval)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-NEXT-INT:'||i_profile.dialpeer_next_interval)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-INIT-RATE:'||i_profile.dialpeer_initial_rate)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-NEXT-RATE:'||i_profile.dialpeer_next_rate)::varchar); + v_aleg_append_headers_reply=array_append(v_aleg_append_headers_reply, (E'X-VND-CF:'||i_profile.dialpeer_fee)::varchar); + end if; + v_aleg_append_headers_reply = array_cat(v_aleg_append_headers_reply,i_customer_gw.orig_append_headers_reply); + i_profile.aleg_append_headers_reply=ARRAY_TO_STRING(v_aleg_append_headers_reply,'\r\n'); + + if i_destination.use_dp_intervals THEN + i_profile.destination_initial_interval:=i_dp.initial_interval; + i_profile.destination_next_interval:=i_dp.next_interval; + ELSE + i_profile.destination_initial_interval:=i_destination.initial_interval; + i_profile.destination_next_interval:=i_destination.next_interval; + end if; + + IF i_profile.package_counter_id IS NULL THEN + CASE i_profile.destination_rate_policy_id + WHEN 1 THEN -- fixed + i_profile.destination_next_rate:=i_destination.next_rate::varchar; + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + WHEN 2 THEN -- based on dialpeer + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + WHEN 3 THEN -- min + IF i_dp.next_rate >= i_destination.next_rate THEN + i_profile.destination_next_rate:=i_destination.next_rate::varchar; -- FIXED least + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + ELSE + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; -- DYNAMIC + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + END IF; + WHEN 4 THEN -- max + IF i_dp.next_rate < i_destination.next_rate THEN + i_profile.destination_next_rate:=i_destination.next_rate::varchar; --FIXED + i_profile.destination_initial_rate:=i_destination.initial_rate::varchar; + ELSE + i_profile.destination_next_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.next_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; -- DYNAMIC + i_profile.destination_initial_rate:=(COALESCE(i_destination.dp_margin_fixed,0)+i_dp.initial_rate*(1+COALESCE(i_destination.dp_margin_percent,0)))::varchar; + END IF; + ELSE + -- + end case; + END IF; + + + /* time limiting START */ + --SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth.account_id; + --SELECT INTO STRICT v_v_acc * FROM billing.accounts WHERE id=v_dialpeer.account_id; + + + if i_profile.time_limit is null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: customer time limit is not set, calculating',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + IF (i_customer_acc.balance-i_customer_acc.min_balance)-i_destination.connect_fee-i_destination.initial_rate/60*i_destination.initial_interval<0 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: No enough customer balance even for first billing interval. rejecting',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + i_profile.disconnect_code_id=8000; --Not enough customer balance + RETURN i_profile; + ELSIF i_destination.next_rate!=0 AND i_destination.next_interval!=0 THEN + i_profile.time_limit = (i_destination.initial_interval+ + LEAST(FLOOR(((i_customer_acc.balance-i_customer_acc.min_balance)-i_destination.connect_fee-i_destination.initial_rate/60*i_destination.initial_interval)/ + (i_destination.next_rate/60*i_destination.next_interval)),24e6)::integer*i_destination.next_interval)::integer; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: customer time limit: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.time_limit; + /*}dbg*/ + ELSE /* DST rates is 0, allowing maximum call length */ + i_profile.time_limit = COALESCE(i_customer_acc.max_call_duration, i_max_call_length)::integer; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> process_gw: DST rate is 0. customer time limit set to max value: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.time_limit; + /*}dbg*/ + end IF; + end if; + + IF (i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee <0 THEN /* No enough balance, skipping this profile */ + v_vendor_allowtime:=0; + return null; + ELSIF (i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee-i_dp.initial_rate/60*i_dp.initial_interval<0 THEN /* No enough balance even for first billing interval - skipping this profile */ + return null; + ELSIF i_dp.next_rate!=0 AND i_dp.next_interval!=0 THEN /* DP rates is not zero, calculating limit */ + v_vendor_allowtime:=i_dp.initial_interval+ + LEAST(FLOOR(((i_vendor_acc.max_balance-i_vendor_acc.balance)-i_dp.connect_fee-i_dp.initial_rate/60*i_dp.initial_interval)/ + (i_dp.next_rate/60*i_dp.next_interval)),24e6)::integer*i_dp.next_interval; + ELSE /* DP rates is 0, allowing maximum call length */ + v_vendor_allowtime:=COALESCE(i_vendor_acc.max_call_duration, i_max_call_length); + end IF; + + i_profile.time_limit=LEAST( + COALESCE(i_customer_acc.max_call_duration, i_max_call_length)::integer, + COALESCE(i_vendor_acc.max_call_duration, i_max_call_length)::integer, + v_vendor_allowtime, + i_profile.time_limit + )::integer; + + + /* number rewriting _After_ routing */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out,i_dp.dst_rewrite_rule,i_dp.dst_rewrite_result); + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand(i_profile.src_prefix_out,i_dp.src_rewrite_rule,i_dp.src_rewrite_result); + i_profile.src_name_out=yeti_ext.regexp_replace_rand(i_profile.src_name_out,i_dp.src_name_rewrite_rule,i_dp.src_name_rewrite_result, true); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + /* + get termination gw data + */ + --SELECT into v_dst_gw * from class4.gateways WHERE id=v_dialpeer.gateway_id; + --SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth.gateway_id; + --vendor gw + if i_vendor_gw.termination_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'5:'||i_vendor_gw.id::varchar||':'||i_vendor_gw.termination_capacity::varchar||':1;'; + end if; + + + /* + numberlist processing _After_ routing _IN_ termination GW + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. Before numberlist processing src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + + ----- DST Numberlist processing------------------------------------------------------------------------------------------------------- + IF i_vendor_gw.termination_dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.dst_prefix_out; + /*}dbg*/ + + select into v_termination_numberlist * from class4.numberlists where id=i_vendor_gw.termination_dst_numberlist_id; + CASE v_termination_numberlist.mode_id + when 1 then -- strict match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id and + ni.key=i_profile.dst_prefix_out + limit 1; + when 2 then -- prefix match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(i_profile.dst_prefix_out) and + length(i_profile.dst_prefix_out) between ni.number_min_length and ni.number_max_length + order by length(prefix_range(ni.key)) desc + limit 1; + when 3 then -- random + select into v_termination_numberlist_size count(*) from class4.numberlist_items where numberlist_id=i_vendor_gw.termination_dst_numberlist_id; + select into v_termination_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=i_vendor_gw.termination_dst_numberlist_id order by ni.id OFFSET floor(random()*v_termination_numberlist_size) limit 1; + END CASE; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_termination_numberlist_item); + /*}dbg*/ + + IF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by key action. Skipping route. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_termination_numberlist_item.key; + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist_item.src_rewrite_rule, + v_termination_numberlist_item.src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist_item.dst_rewrite_rule, + v_termination_numberlist_item.dst_rewrite_result + ); + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by default action. Skipping route',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist.default_src_rewrite_rule, + v_termination_numberlist.default_src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist.default_dst_rewrite_rule, + v_termination_numberlist.default_dst_rewrite_result + ); + END IF; + END IF; + + + + ----- SRC Numberlist processing------------------------------------------------------------------------------------------------------- + IF i_vendor_gw.termination_src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), i_profile.src_prefix_out; + /*}dbg*/ + + select into v_termination_numberlist * from class4.numberlists where id=i_vendor_gw.termination_src_numberlist_id; + CASE v_termination_numberlist.mode_id + when 1 then -- strict match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id and + ni.key=i_profile.src_prefix_out + limit 1; + when 2 then -- prefix match + select into v_termination_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id and + prefix_range(ni.key)@>prefix_range(i_profile.src_prefix_out) and + length(i_profile.src_prefix_out) between ni.number_min_length and ni.number_max_length + order by length(prefix_range(ni.key)) desc + limit 1; + when 3 then -- random + select into v_termination_numberlist_size count(*) from class4.numberlist_items where numberlist_id=i_vendor_gw.termination_src_numberlist_id; + select into v_termination_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=i_vendor_gw.termination_src_numberlist_id order by ni.id OFFSET floor(random()*v_termination_numberlist_size) limit 1; + END CASE; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_termination_numberlist_item); + /*}dbg*/ + + IF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW SRC Numberlist. Drop by key action. Skipping route. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_termination_numberlist_item.key; + /*}dbg*/ + RETURN null; + ELSIF v_termination_numberlist_item.action_id is not null and v_termination_numberlist_item.action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist_item.src_rewrite_rule, + v_termination_numberlist_item.src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist_item.dst_rewrite_rule, + v_termination_numberlist_item.dst_rewrite_result + ); + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW DST Numberlist. Drop by default action. Skipping route.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + RETURN null; + ELSIF v_termination_numberlist_item.action_id is null and v_termination_numberlist.default_action_id=2 then + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.src_prefix_out, + v_termination_numberlist.default_src_rewrite_rule, + v_termination_numberlist.default_src_rewrite_result + ); + + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand( + i_profile.dst_prefix_out, + v_termination_numberlist.default_dst_rewrite_rule, + v_termination_numberlist.default_dst_rewrite_result + ); + END IF; + END IF; + + + + /* + number rewriting _After_ routing _IN_ termination GW + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + i_profile.dst_prefix_out=yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out,i_vendor_gw.dst_rewrite_rule,i_vendor_gw.dst_rewrite_result); + i_profile.src_prefix_out=yeti_ext.regexp_replace_rand(i_profile.src_prefix_out,i_vendor_gw.src_rewrite_rule,i_vendor_gw.src_rewrite_result); + i_profile.src_name_out=yeti_ext.regexp_replace_rand(i_profile.src_name_out,i_vendor_gw.src_name_rewrite_rule,i_vendor_gw.src_name_rewrite_result, true); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; + /*}dbg*/ + + + IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN + IF i_vendor_gw.diversion_send_mode_id = 2 AND i_vendor_gw.diversion_domain is not null AND i_vendor_gw.diversion_domain!='' THEN + /* Diversion as SIP URI */ + FOREACH v_diversion_header IN ARRAY i_diversion LOOP + v_diversion_header.u = yeti_ext.regexp_replace_rand(v_diversion_header.u, i_vendor_gw.diversion_rewrite_rule, i_vendor_gw.diversion_rewrite_result); + v_diversion_header.s = 'sip'; + v_diversion_header.h = i_vendor_gw.diversion_domain; + v_bleg_append_headers_req = array_append( + v_bleg_append_headers_req, + 'Diversion: '||switch22.build_uri(false, v_diversion_header) + ); + END LOOP; + ELSIF i_vendor_gw.diversion_send_mode_id = 3 THEN + /* Diversion as TEL URI */ + FOREACH v_diversion_header IN ARRAY i_diversion LOOP + v_diversion_header.u = yeti_ext.regexp_replace_rand(v_diversion_header.u, i_vendor_gw.diversion_rewrite_rule, i_vendor_gw.diversion_rewrite_result); + v_diversion_header.s = 'tel'; + v_bleg_append_headers_req=array_append( + v_bleg_append_headers_req, + 'Diversion: '||switch22.build_uri(false, v_diversion_header) + ); + END LOOP; + END IF; + END IF; + + CASE i_vendor_gw.privacy_mode_id + WHEN 0 THEN + -- do nothing + WHEN 1 THEN + IF cardinality(array_remove(i_privacy,'none')) > 0 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested but privacy_mode_is %. Skipping gw.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + return null; + END IF; + WHEN 2 THEN + IF 'critical' = ANY(i_privacy) THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested but privacy_mode_is %. Skipping gw.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + return null; + END IF; + WHEN 3 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. Applying privacy.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF 'id' = ANY(i_privacy) OR 'user' = ANY(i_privacy) THEN + i_profile.src_prefix_out='anonymous'; + i_profile.src_name_out='Anonymous'; + v_from_domain = 'anonymous.invalid'; + END IF; + IF 'id' = ANY(i_privacy) OR 'header' = ANY(i_privacy) THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. removing PAI/PPI headers.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + v_allow_pai = false; + END IF; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + WHEN 4 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. forwarding.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF cardinality(i_privacy)>0 THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + END IF; + WHEN 5 THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> GW Privacy % requested, privacy_mode_is %. forwarding with anonymous From.',EXTRACT(MILLISECOND from v_end-v_start), i_privacy, i_vendor_gw.privacy_mode_id; + /*}dbg*/ + IF 'id' = ANY(i_privacy) or 'user' = ANY(i_privacy) THEN + i_profile.src_prefix_out='anonymous'; + i_profile.src_name_out='Anonymous'; + v_from_domain = 'anonymous.invalid'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('Privacy: %s', array_to_string(i_privacy,';')::varchar)); + END IF; + END CASE; + + IF v_allow_pai THEN + -- only if privacy mode allows to send PAI + IF i_vendor_gw.pai_send_mode_id = 1 THEN + -- TEL URI + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 2 and i_vendor_gw.pai_domain is not null and i_vendor_gw.pai_domain!='' THEN + -- SIP URL + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out, i_vendor_gw.pai_domain)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 3 and i_vendor_gw.pai_domain is not null and i_vendor_gw.pai_domain!='' THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: ', i_profile.src_prefix_out, i_vendor_gw.pai_domain)::varchar); + ELSIF i_vendor_gw.pai_send_mode_id = 4 THEN + -- relay + FOREACH v_pai IN ARRAY i_pai LOOP + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 5 THEN + -- relay with conversion to tel URI + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'tel'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'tel'; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 6 THEN + -- relay with conversion to SIP URI + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'sip'; + v_pai.h = COALESCE(v_pai.h, i_vendor_gw.pai_domain); + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'sip'; + i_ppi.h = COALESCE(i_ppi.h, i_vendor_gw.pai_domain); + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + ELSIF i_vendor_gw.pai_send_mode_id = 7 THEN + -- relay with conversion to SIP URI. Force replace domain + FOREACH v_pai IN ARRAY i_pai LOOP + v_pai.s = 'sip'; + v_pai.h = i_vendor_gw.pai_domain; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Asserted-Identity: %s', switch22.build_uri(false, v_pai))::varchar); + END LOOP; + IF i_ppi.u is not null THEN + i_ppi.s = 'sip'; + i_ppi.h = i_vendor_gw.pai_domain; + v_bleg_append_headers_req = array_append(v_bleg_append_headers_req, format('P-Preferred-Identity: %s', switch22.build_uri(false, i_ppi))::varchar); + END IF; + END IF; + + END IF; + + IF i_vendor_gw.stir_shaken_mode_id IN (1,2) THEN + IF i_profile.lega_ss_status_id >0 THEN + -- relaying valid header from customer + i_profile.legb_ss_status_id = i_profile.lega_ss_status_id; + v_customer_transit_headers_from_origination = array_append(v_customer_transit_headers_from_origination,'Identity'); + v_vendor_transit_headers_from_origination = array_append(v_vendor_transit_headers_from_origination,'Identity'); + ELSIF COALESCE(i_profile.ss_attest_id,0) > 0 AND i_vendor_gw.stir_shaken_crt_id IS NOT NULL THEN + -- insert our signature + i_profile.ss_crt_id = i_vendor_gw.stir_shaken_crt_id; + i_profile.legb_ss_status_id = i_profile.ss_attest_id; + + IF i_vendor_gw.stir_shaken_mode_id = 1 THEN + i_profile.ss_otn = i_profile.src_prefix_routing; + i_profile.ss_dtn = i_profile.dst_prefix_routing; + ELSIF i_vendor_gw.stir_shaken_mode_id = 2 THEN + i_profile.ss_otn = i_profile.src_prefix_out; + i_profile.ss_dtn = i_profile.dst_prefix_out; + END IF; + END IF; + END IF ; + + v_bleg_append_headers_req = array_cat(v_bleg_append_headers_req, i_vendor_gw.term_append_headers_req); + i_profile.append_headers_req = array_to_string(v_bleg_append_headers_req,'\r\n'); + + i_profile.aleg_append_headers_req = array_to_string(i_customer_gw.orig_append_headers_req,'\r\n'); + + i_profile.next_hop_1st_req=i_vendor_gw.auth_enabled; -- use low delay dns srv if auth enabled + i_profile.next_hop:=i_vendor_gw.term_next_hop; + i_profile.aleg_next_hop:=i_customer_gw.orig_next_hop; + -- i_profile.next_hop_for_replies:=v_dst_gw.term_next_hop_for_replies; + + i_profile.dlg_nat_handling=i_customer_gw.dialog_nat_handling; + + i_profile.call_id:=''; -- Generation by sems + + i_profile.enable_auth:=i_vendor_gw.auth_enabled; + i_profile.auth_pwd:=i_vendor_gw.auth_password; + i_profile.auth_user:=i_vendor_gw.auth_user; + i_profile.enable_aleg_auth:=false; + i_profile.auth_aleg_pwd:=''; + i_profile.auth_aleg_user:=''; + + if i_profile.enable_auth then + v_from_user=COALESCE(i_vendor_gw.auth_from_user,i_profile.src_prefix_out,''); + -- may be it already defined by privacy logic + v_from_domain=COALESCE(v_from_domain, i_vendor_gw.auth_from_domain, '$Oi'); + else + v_from_user=COALESCE(i_profile.src_prefix_out,''); + if i_vendor_gw.preserve_anonymous_from_domain and i_profile.from_domain='anonymous.invalid' then + v_from_domain='anonymous.invalid'; + else + v_from_domain=COALESCE(v_from_domain, '$Oi'); + end if; + end if; + + v_to_username = yeti_ext.regexp_replace_rand(i_profile.dst_prefix_out, i_vendor_gw.to_rewrite_rule, i_vendor_gw.to_rewrite_result); + + if i_vendor_gw.sip_schema_id = 1 then + v_schema='sip'; + elsif i_vendor_gw.sip_schema_id = 2 then + v_schema='sips'; + elsif i_vendor_gw.sip_schema_id = 3 then + v_schema='sip'; + -- user=phone param require e.164 with + in username, but we are not forcing it + v_from_uri_params = array_append(v_from_uri_params,'user=phone'); + v_to_uri_params = array_append(v_to_uri_params,'user=phone'); + v_ruri_params = array_append(v_ruri_params,'user=phone'); + else + RAISE exception 'Unknown termination gateway % SIP schema %', i_vendor_gw.id, i_vendor_gw.sip_schema_id; + end if; + + if i_vendor_gw.send_lnp_information and i_profile.lrn is not null then + if i_profile.lrn=i_profile.dst_prefix_routing then -- number not ported, but request was successf we musr add ;npdi=yes; + v_ruri_user_params = array_append(v_ruri_user_params, 'npdi=yes'); + i_profile.lrn=nullif(i_profile.dst_prefix_routing,i_profile.lrn); -- clear lnr field if number not ported; + else -- if number ported + v_ruri_user_params = array_append(v_ruri_user_params, 'rn='||i_profile.lrn); + v_ruri_user_params = array_append(v_ruri_user_params, 'npdi=yes'); + end if; + end if; + + i_profile.registered_aor_mode_id = i_vendor_gw.registered_aor_mode_id; + if i_vendor_gw.registered_aor_mode_id > 0 then + i_profile.registered_aor_id=i_vendor_gw.id; + v_ruri_host = 'unknown.invalid'; + else + v_ruri_host = i_vendor_gw.host; + end if; + + i_profile."from" = switch22.build_uri(false, v_schema, i_profile.src_name_out, v_from_user, null, v_from_domain, null, v_from_uri_params); + + i_profile."to" = switch22.build_uri(false, v_schema, null, v_to_username, null, v_ruri_host, i_vendor_gw.port, v_to_uri_params); + i_profile.ruri = switch22.build_uri(true, v_schema, null, i_profile.dst_prefix_out, v_ruri_user_params, v_ruri_host, i_vendor_gw.port, v_ruri_params); + + i_profile.bleg_transport_protocol_id:=i_vendor_gw.transport_protocol_id; + i_profile.bleg_protocol_priority_id:=i_vendor_gw.network_protocol_priority_id; + + i_profile.aleg_media_encryption_mode_id:=i_customer_gw.media_encryption_mode_id; + i_profile.bleg_media_encryption_mode_id:=i_vendor_gw.media_encryption_mode_id; + + IF (i_vendor_gw.term_use_outbound_proxy ) THEN + i_profile.outbound_proxy:=v_schema||':'||i_vendor_gw.term_outbound_proxy; + i_profile.force_outbound_proxy:=i_vendor_gw.term_force_outbound_proxy; + i_profile.bleg_outbound_proxy_transport_protocol_id:=i_vendor_gw.term_proxy_transport_protocol_id; + ELSE + i_profile.outbound_proxy:=NULL; + i_profile.force_outbound_proxy:=false; + END IF; + + IF (i_customer_gw.orig_use_outbound_proxy ) THEN + i_profile.aleg_force_outbound_proxy:=i_customer_gw.orig_force_outbound_proxy; + i_profile.aleg_outbound_proxy=v_schema||':'||i_customer_gw.orig_outbound_proxy; + i_profile.aleg_outbound_proxy_transport_protocol_id:=i_customer_gw.orig_proxy_transport_protocol_id; + else + i_profile.aleg_force_outbound_proxy:=FALSE; + i_profile.aleg_outbound_proxy=NULL; + end if; + + i_profile.aleg_policy_id=i_customer_gw.orig_disconnect_policy_id; + i_profile.bleg_policy_id=i_vendor_gw.term_disconnect_policy_id; + + i_profile.transit_headers_a2b:=array_to_string(v_customer_transit_headers_from_origination,',')||';'||array_to_string(v_vendor_transit_headers_from_origination,','); + i_profile.transit_headers_b2a:=i_vendor_gw.transit_headers_from_termination||';'||i_customer_gw.transit_headers_from_termination; + + i_profile.sdp_filter_type_id:=0; + i_profile.sdp_filter_list:=''; + + i_profile.sdp_alines_filter_type_id:=i_vendor_gw.sdp_alines_filter_type_id; + i_profile.sdp_alines_filter_list:=i_vendor_gw.sdp_alines_filter_list; + + i_profile.enable_session_timer=i_vendor_gw.sst_enabled; + i_profile.session_expires =i_vendor_gw.sst_session_expires; + i_profile.minimum_timer:=i_vendor_gw.sst_minimum_timer; + i_profile.maximum_timer:=i_vendor_gw.sst_maximum_timer; + i_profile.session_refresh_method_id:=i_vendor_gw.session_refresh_method_id; + i_profile.accept_501_reply:=i_vendor_gw.sst_accept501; + + i_profile.enable_aleg_session_timer=i_customer_gw.sst_enabled; + i_profile.aleg_session_expires:=i_customer_gw.sst_session_expires; + i_profile.aleg_minimum_timer:=i_customer_gw.sst_minimum_timer; + i_profile.aleg_maximum_timer:=i_customer_gw.sst_maximum_timer; + i_profile.aleg_session_refresh_method_id:=i_customer_gw.session_refresh_method_id; + i_profile.aleg_accept_501_reply:=i_customer_gw.sst_accept501; + + i_profile.reply_translations:=''; + i_profile.disconnect_code_id:=NULL; + i_profile.enable_rtprelay:=i_vendor_gw.proxy_media OR i_customer_gw.proxy_media; + + i_profile.rtprelay_interface:=i_vendor_gw.rtp_interface_name; + i_profile.aleg_rtprelay_interface:=i_customer_gw.rtp_interface_name; + + i_profile.outbound_interface:=i_vendor_gw.sip_interface_name; + i_profile.aleg_outbound_interface:=i_customer_gw.sip_interface_name; + + i_profile.bleg_force_symmetric_rtp:=i_vendor_gw.force_symmetric_rtp; + i_profile.bleg_symmetric_rtp_nonstop=i_vendor_gw.symmetric_rtp_nonstop; + + i_profile.aleg_force_symmetric_rtp:=i_customer_gw.force_symmetric_rtp; + i_profile.aleg_symmetric_rtp_nonstop=i_customer_gw.symmetric_rtp_nonstop; + + i_profile.bleg_rtp_ping=i_vendor_gw.rtp_ping; + i_profile.aleg_rtp_ping=i_customer_gw.rtp_ping; + + i_profile.bleg_relay_options = i_vendor_gw.relay_options; + i_profile.aleg_relay_options = i_customer_gw.relay_options; + + + i_profile.filter_noaudio_streams = i_vendor_gw.filter_noaudio_streams OR i_customer_gw.filter_noaudio_streams; + i_profile.force_one_way_early_media = i_vendor_gw.force_one_way_early_media OR i_customer_gw.force_one_way_early_media; + i_profile.aleg_relay_reinvite = i_vendor_gw.relay_reinvite; + i_profile.bleg_relay_reinvite = i_customer_gw.relay_reinvite; + + i_profile.aleg_relay_hold = i_vendor_gw.relay_hold; + i_profile.bleg_relay_hold = i_customer_gw.relay_hold; + + i_profile.aleg_relay_prack = i_vendor_gw.relay_prack; + i_profile.bleg_relay_prack = i_customer_gw.relay_prack; + i_profile.aleg_rel100_mode_id = i_customer_gw.rel100_mode_id; + i_profile.bleg_rel100_mode_id = i_vendor_gw.rel100_mode_id; + + i_profile.rtp_relay_timestamp_aligning=i_vendor_gw.rtp_relay_timestamp_aligning OR i_customer_gw.rtp_relay_timestamp_aligning; + i_profile.allow_1xx_wo2tag=i_vendor_gw.allow_1xx_without_to_tag OR i_customer_gw.allow_1xx_without_to_tag; + + i_profile.aleg_sdp_c_location_id=i_customer_gw.sdp_c_location_id; + i_profile.bleg_sdp_c_location_id=i_vendor_gw.sdp_c_location_id; + i_profile.trusted_hdrs_gw=false; + + + + i_profile.aleg_codecs_group_id:=i_customer_gw.codec_group_id; + i_profile.bleg_codecs_group_id:=i_vendor_gw.codec_group_id; + i_profile.aleg_single_codec_in_200ok:=i_customer_gw.single_codec_in_200ok; + i_profile.bleg_single_codec_in_200ok:=i_vendor_gw.single_codec_in_200ok; + i_profile.try_avoid_transcoding = i_customer_gw.try_avoid_transcoding; + i_profile.ringing_timeout=i_vendor_gw.ringing_timeout; + i_profile.dead_rtp_time=GREATEST(i_vendor_gw.rtp_timeout,i_customer_gw.rtp_timeout); + i_profile.invite_timeout=i_vendor_gw.sip_timer_b; + i_profile.srv_failover_timeout=i_vendor_gw.dns_srv_failover_timer; + i_profile.fake_180_timer=i_vendor_gw.fake_180_timer; + i_profile.rtp_force_relay_cn=i_vendor_gw.rtp_force_relay_cn OR i_customer_gw.rtp_force_relay_cn; + i_profile.patch_ruri_next_hop=i_vendor_gw.resolve_ruri; + + i_profile.aleg_sensor_id=i_customer_gw.sensor_id; + i_profile.aleg_sensor_level_id=i_customer_gw.sensor_level_id; + i_profile.bleg_sensor_id=i_vendor_gw.sensor_id; + i_profile.bleg_sensor_level_id=i_vendor_gw.sensor_level_id; + + i_profile.aleg_dtmf_send_mode_id=i_customer_gw.dtmf_send_mode_id; + i_profile.aleg_dtmf_recv_modes=i_customer_gw.dtmf_receive_mode_id; + i_profile.bleg_dtmf_send_mode_id=i_vendor_gw.dtmf_send_mode_id; + i_profile.bleg_dtmf_recv_modes=i_vendor_gw.dtmf_receive_mode_id; + + + i_profile.aleg_rtp_filter_inband_dtmf=false; + i_profile.bleg_rtp_filter_inband_dtmf=false; + + if i_customer_gw.rx_inband_dtmf_filtering_mode_id=3 then -- enable filtering + i_profile.aleg_rtp_filter_inband_dtmf=true; + elsif i_customer_gw.rx_inband_dtmf_filtering_mode_id=1 then -- inherit + if i_vendor_gw.tx_inband_dtmf_filtering_mode_id in (1,2) then -- inherit or disable filtering + i_profile.aleg_rtp_filter_inband_dtmf=false; + elsif i_vendor_gw.tx_inband_dtmf_filtering_mode_id = 3 then -- enable filtering + i_profile.aleg_rtp_filter_inband_dtmf=true; + end if; + end if; + + + if i_vendor_gw.rx_inband_dtmf_filtering_mode_id=3 then -- enable filtering + i_profile.bleg_rtp_filter_inband_dtmf=true; + elsif i_vendor_gw.rx_inband_dtmf_filtering_mode_id=1 then -- inherit + if i_customer_gw.tx_inband_dtmf_filtering_mode_id in (1,2) then -- inherit or disable filtering + i_profile.bleg_rtp_filter_inband_dtmf=false; + elsif i_customer_gw.tx_inband_dtmf_filtering_mode_id = 3 then -- enable filtering + i_profile.bleg_rtp_filter_inband_dtmf=true; + end if; + end if; + + i_profile.aleg_rtp_acl = i_customer_gw.rtp_acl; + i_profile.bleg_rtp_acl = i_vendor_gw.rtp_acl; + + i_profile.rtprelay_force_dtmf_relay=i_vendor_gw.force_dtmf_relay; + i_profile.rtprelay_dtmf_detection=NOT i_vendor_gw.force_dtmf_relay; + i_profile.rtprelay_dtmf_filtering=NOT i_vendor_gw.force_dtmf_relay; + i_profile.bleg_max_30x_redirects = i_vendor_gw.max_30x_redirects; + i_profile.bleg_max_transfers = i_vendor_gw.max_transfers; + + + i_profile.aleg_relay_update=i_customer_gw.relay_update; + i_profile.bleg_relay_update=i_vendor_gw.relay_update; + i_profile.suppress_early_media=i_customer_gw.suppress_early_media OR i_vendor_gw.suppress_early_media; + + i_profile.bleg_radius_acc_profile_id=i_vendor_gw.radius_accounting_profile_id; + i_profile.bleg_force_cancel_routeset=i_vendor_gw.force_cancel_routeset; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. Finished: % ',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(i_profile,true); + /*}dbg*/ + RETURN i_profile; +END; +$_$; + + set search_path TO switch22; + SELECT * from switch22.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + + delete from class4.disconnect_code where id = 1513; + delete from switch22.resource_type where id = 8; + + alter table class4.gateways drop column termination_subscriber_capacity; + + + + } + end +end diff --git a/db/seeds/main/class4.sql b/db/seeds/main/class4.sql index d737d9fbd..1ea1cf0e6 100644 --- a/db/seeds/main/class4.sql +++ b/db/seeds/main/class4.sql @@ -368,6 +368,7 @@ insert into disconnect_code (id,namespace_id,code,reason) values (1509, 1, 480, insert into disconnect_code (id,namespace_id,code,reason) values (1510, 1, 480, 'Vendor gateway $id overloaded'); insert into disconnect_code (id,namespace_id,code,reason) values (1511, 1, 480, 'Dialpeer $id overloaded'); insert into disconnect_code (id,namespace_id,code,reason) values (1512, 1, 480, 'Account $id total capacity reached'); +insert into disconnect_code (id,namespace_id,code,reason) values (1513, 1, 480, 'Subscriber capacity limit'); insert into disconnect_code (id,namespace_id,code,reason) values (1600, 1, 503, 'Resource cache error'); insert into disconnect_code (id,namespace_id,code,reason) values (1601, 1, 503, 'Unknown resource overload'); diff --git a/db/seeds/main/switch22.sql b/db/seeds/main/switch22.sql index a2b4f4cfa..da2458db3 100644 --- a/db/seeds/main/switch22.sql +++ b/db/seeds/main/switch22.sql @@ -21,6 +21,7 @@ INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUE INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUES (5, 'Vendor gateway', 1510, 2); INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUES (6, 'Dialpeer', 1511, 2); INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUES (7, 'Account total capacity', 1512, 1); +INSERT INTO switch22.resource_type (id, name, internal_code_id, action_id) VALUES (8, 'Subscriber capacity limit', 1513, 2); INSERT INTO switch22.switch_interface_out (id, name, type, custom, rank, for_radius) VALUES (890, 'src_number_radius', 'varchar', false, 1050, true); diff --git a/db/structure.sql b/db/structure.sql index 81b481d30..f05a6836b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -3537,7 +3537,8 @@ CREATE TABLE class4.gateways ( to_rewrite_rule character varying, to_rewrite_result character varying, privacy_mode_id smallint DEFAULT 0 NOT NULL, - incoming_auth_allow_jwt boolean DEFAULT false NOT NULL + incoming_auth_allow_jwt boolean DEFAULT false NOT NULL, + termination_subscriber_capacity smallint ); @@ -32828,7 +32829,6 @@ BEGIN i_profile.legb_res:=i_profile.legb_res||'5:'||i_vendor_gw.id::varchar||':'||i_vendor_gw.termination_capacity::varchar||':1;'; end if; - /* numberlist processing _After_ routing _IN_ termination GW */ @@ -33005,6 +33005,10 @@ BEGIN RAISE NOTICE '% ms -> GW. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; /*}dbg*/ + -- apply capacity limit by destination number + if i_vendor_gw.termination_subscriber_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN IF i_vendor_gw.diversion_send_mode_id = 2 AND i_vendor_gw.diversion_domain is not null AND i_vendor_gw.diversion_domain!='' THEN @@ -33626,7 +33630,6 @@ BEGIN i_profile.legb_res:=i_profile.legb_res||'5:'||i_vendor_gw.id::varchar||':'||i_vendor_gw.termination_capacity::varchar||':1;'; end if; - /* numberlist processing _After_ routing _IN_ termination GW */ @@ -33803,6 +33806,10 @@ BEGIN RAISE NOTICE '% ms -> GW. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),i_profile.src_prefix_out,i_profile.dst_prefix_out; /*}dbg*/ + -- apply capacity limit by destination number + if i_vendor_gw.termination_subscriber_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN IF i_vendor_gw.diversion_send_mode_id = 2 AND i_vendor_gw.diversion_domain is not null AND i_vendor_gw.diversion_domain!='' THEN @@ -34398,7 +34405,6 @@ BEGIN i_profile.legb_res:=i_profile.legb_res||'5:'||i_vendor_gw.id::varchar||':'||i_vendor_gw.termination_capacity::varchar||':1;'; end if; - /* numberlist processing _After_ routing _IN_ termination GW */ @@ -34542,6 +34548,10 @@ BEGIN + -- apply capacity limit by destination number + if i_vendor_gw.termination_subscriber_capacity is not null then + i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN IF i_vendor_gw.diversion_send_mode_id = 2 AND i_vendor_gw.diversion_domain is not null AND i_vendor_gw.diversion_domain!='' THEN @@ -50143,6 +50153,7 @@ INSERT INTO "public"."schema_migrations" (version) VALUES ('20241213175248'), ('20241215155451'), ('20241216165130'), -('20241218154326'); +('20241218154326'), +('20241231160003'); diff --git a/spec/factories/gateways.rb b/spec/factories/gateways.rb index 57c79256f..fd109a3a5 100644 --- a/spec/factories/gateways.rb +++ b/spec/factories/gateways.rb @@ -90,6 +90,7 @@ # term_outbound_proxy :string # term_use_outbound_proxy :boolean default(FALSE), not null # termination_capacity :integer(2) +# termination_subscriber_capacity :integer(2) # to_rewrite_result :string # to_rewrite_rule :string # transit_headers_from_origination :string diff --git a/spec/features/equipment/gateways/export_gateways_spec.rb b/spec/features/equipment/gateways/export_gateways_spec.rb index 32bceb00b..8529b2f80 100644 --- a/spec/features/equipment/gateways/export_gateways_spec.rb +++ b/spec/features/equipment/gateways/export_gateways_spec.rb @@ -45,6 +45,7 @@ ['Sst enabled', item.sst_enabled.to_s], ['Origination capacity', item.origination_capacity.to_s], ['Termination capacity', item.termination_capacity.to_s], + ['Termination subscriber capacity', item.termination_subscriber_capacity.to_s], ['Acd limit', item.acd_limit.to_s], ['Asr limit', item.asr_limit.to_s], ['Short calls limit', item.short_calls_limit.to_s], diff --git a/spec/models/gateway_spec.rb b/spec/models/gateway_spec.rb index 3cf0df7e7..ca7a3460c 100644 --- a/spec/models/gateway_spec.rb +++ b/spec/models/gateway_spec.rb @@ -90,6 +90,7 @@ # term_outbound_proxy :string # term_use_outbound_proxy :boolean default(FALSE), not null # termination_capacity :integer(2) +# termination_subscriber_capacity :integer(2) # to_rewrite_result :string # to_rewrite_rule :string # transit_headers_from_origination :string From a0fa96d4e680ea5bb324e341a35660e93f47327b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 31 Dec 2024 18:58:40 +0200 Subject: [PATCH 2/4] make rubocop happy --- db/custom_seeds/yeti_ro_role.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/custom_seeds/yeti_ro_role.rb b/db/custom_seeds/yeti_ro_role.rb index bbaea37c0..42f34e773 100644 --- a/db/custom_seeds/yeti_ro_role.rb +++ b/db/custom_seeds/yeti_ro_role.rb @@ -38,12 +38,12 @@ SqlCaller::Yeti.execute "DROP ROLE IF EXISTS #{routing_role}" SqlCaller::Yeti.execute "CREATE ROLE #{routing_role} NOLOGIN" - for s in routing_schemas do + routing_schemas.each do |s| SqlCaller::Yeti.execute "GRANT USAGE ON SCHEMA #{s} TO #{routing_role}" SqlCaller::Yeti.execute "GRANT SELECT ON ALL TABLES IN SCHEMA #{s} TO #{routing_role}" end - for s in routing_schemas_with_partitioning do + routing_schemas_with_partitioning.each do |s| SqlCaller::Yeti.execute "ALTER DEFAULT PRIVILEGES IN SCHEMA #{s} GRANT SELECT ON TABLES TO #{routing_role}" end end @@ -53,12 +53,12 @@ SqlCaller::Cdr.execute "DROP ROLE IF EXISTS #{cdr_role}" SqlCaller::Cdr.execute "CREATE ROLE #{cdr_role} NOLOGIN" - for s in cdr_schemas do + cdr_schemas.each do |s| SqlCaller::Cdr.execute "GRANT USAGE ON SCHEMA #{s} TO #{cdr_role}" SqlCaller::Cdr.execute "GRANT SELECT ON ALL TABLES IN SCHEMA #{s} TO #{cdr_role}" end - for s in cdr_schemas_with_partitioning do + cdr_schemas_with_partitioning.each do |s| SqlCaller::Cdr.execute "ALTER DEFAULT PRIVILEGES IN SCHEMA #{s} GRANT SELECT ON TABLES TO #{cdr_role}" end end From 5a0ed33329439bf5c4810038032c44c3b3e28500 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 31 Dec 2024 19:52:42 +0200 Subject: [PATCH 3/4] add tests --- ...231160003_dst_subscriber_capacity_limit.rb | 2 +- db/structure.sql | 6 +-- spec/factories/accounts.rb | 6 +-- spec/factories/dialpeers.rb | 2 +- spec/factories/gateways.rb | 4 +- spec/sql/switch22/route_spec.rb | 38 +++++++++++++++++++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb b/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb index 4766a21bf..d31e04a55 100644 --- a/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb +++ b/db/migrate/20241231160003_dst_subscriber_capacity_limit.rb @@ -398,7 +398,7 @@ def up -- apply capacity limit by destination number if i_vendor_gw.termination_subscriber_capacity is not null then - i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + i_profile.legb_res = i_profile.legb_res||'8:'||i_vendor_gw.id||'_'||translate(i_profile.dst_prefix_out,': #@', '____')||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN diff --git a/db/structure.sql b/db/structure.sql index f05a6836b..0663a7929 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -33007,7 +33007,7 @@ BEGIN -- apply capacity limit by destination number if i_vendor_gw.termination_subscriber_capacity is not null then - i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + i_profile.legb_res = i_profile.legb_res||'8:'||i_vendor_gw.id||'_'||translate(i_profile.dst_prefix_out,': #@', '____')||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN @@ -33808,7 +33808,7 @@ BEGIN -- apply capacity limit by destination number if i_vendor_gw.termination_subscriber_capacity is not null then - i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + i_profile.legb_res = i_profile.legb_res||'8:'||i_vendor_gw.id||'_'||translate(i_profile.dst_prefix_out,': #@', '____')||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN @@ -34550,7 +34550,7 @@ BEGIN -- apply capacity limit by destination number if i_vendor_gw.termination_subscriber_capacity is not null then - i_profile.legb_res:=i_profile.legb_res||'8:'||i_profile.dst_prefix_out::varchar||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; + i_profile.legb_res = i_profile.legb_res||'8:'||i_vendor_gw.id||'_'||translate(i_profile.dst_prefix_out,': #@', '____')||':'||i_vendor_gw.termination_subscriber_capacity||':1;'; end if; IF cardinality(i_diversion) > 0 AND i_vendor_gw.diversion_send_mode_id > 1 THEN diff --git a/spec/factories/accounts.rb b/spec/factories/accounts.rb index 9c388d4cc..db36613e3 100644 --- a/spec/factories/accounts.rb +++ b/spec/factories/accounts.rb @@ -48,9 +48,9 @@ max_call_duration { 36_000 } min_balance { 0 } max_balance { 0 } - origination_capacity { 1 } - termination_capacity { 2 } - total_capacity { 5 } + origination_capacity { nil } + termination_capacity { nil } + total_capacity { nil } timezone_id { 1 } transient do diff --git a/spec/factories/dialpeers.rb b/spec/factories/dialpeers.rb index 660de52f8..11da91dcb 100644 --- a/spec/factories/dialpeers.rb +++ b/spec/factories/dialpeers.rb @@ -81,7 +81,7 @@ dst_rewrite_result { nil } locked { false } priority { 100 } - capacity { 1 } + capacity { nil } lcr_rate_multiplier { 1 } next_rate { 0.0 } initial_rate { 0.0 } diff --git a/spec/factories/gateways.rb b/spec/factories/gateways.rb index fd109a3a5..35cb6af86 100644 --- a/spec/factories/gateways.rb +++ b/spec/factories/gateways.rb @@ -200,11 +200,11 @@ term_outbound_proxy { nil } term_next_hop_for_replies { false } term_use_outbound_proxy { false } - termination_capacity { 1 } + termination_capacity { nil } allow_termination { true } allow_origination { true } proxy_media { true } - origination_capacity { 1 } + origination_capacity { nil } preserve_anonymous_from_domain { false } sst_enabled { false } sst_minimum_timer { 50 } diff --git a/spec/sql/switch22/route_spec.rb b/spec/sql/switch22/route_spec.rb index fba62836e..824a6c927 100644 --- a/spec/sql/switch22/route_spec.rb +++ b/spec/sql/switch22/route_spec.rb @@ -567,6 +567,8 @@ def routing_sp host: vendor_gw_host, port: vendor_gw_port, allow_termination: true, + termination_capacity: vendor_gw_termination_capacity, + termination_subscriber_capacity: vendor_gw_termination_subscriber_capacity, term_append_headers_req: vendor_gw_term_append_headers_req, diversion_send_mode_id: vendor_gw_diversion_send_mode_id, diversion_domain: vendor_gw_diversion_domain, @@ -609,6 +611,9 @@ def routing_sp let(:vendor_gw_send_lnp_information) { false } + let(:vendor_gw_termination_capacity) { nil } + let(:vendor_gw_termination_subscriber_capacity) { nil } + let!(:customer) { create(:contractor, customer: true, enabled: true) } let!(:customer_account) { create(:account, @@ -4653,6 +4658,39 @@ def routing_sp end end end + + context 'Termination gw capacity limit' do + + context 'no termination gw capacity limit' do + it 'response with ok ' do + expect(subject.size).to eq(2) + expect(subject.first[:legb_res]).to eq("") + expect(subject.second[:disconnect_code_id]).to eq(113) # last profile with route not found error + end + end + + context 'termination gw capacity limit' do + let(:vendor_gw_termination_capacity) { 10 } + + it 'response with ok ' do + expect(subject.size).to eq(2) + expect(subject.first[:legb_res]).to eq("5:#{vendor_gateway.id}:#{vendor_gw_termination_capacity}:1;") + expect(subject.second[:disconnect_code_id]).to eq(113) # last profile with route not found error + end + end + + context 'termination gw capacity limit' do + let(:vendor_gw_termination_subscriber_capacity) { 20 } + let(:uri_name) { '#380_961 234:567@' } + + it 'response with ok ' do + expect(subject.size).to eq(2) + expect(subject.first[:legb_res]).to eq("8:#{vendor_gateway.id}__380_961_234_567_:#{vendor_gw_termination_subscriber_capacity}:1;") + expect(subject.second[:disconnect_code_id]).to eq(113) # last profile with route not found error + end + end + + end end end From 1000ac66f52ffe3b4db4a24e4f866c74473b4002 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 31 Dec 2024 20:07:33 +0200 Subject: [PATCH 4/4] make rubocop happy --- spec/features/routing/dialpeers/show_dialpeer_spec.rb | 1 + spec/sql/switch22/route_spec.rb | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/features/routing/dialpeers/show_dialpeer_spec.rb b/spec/features/routing/dialpeers/show_dialpeer_spec.rb index f9520d09e..e10378d5b 100644 --- a/spec/features/routing/dialpeers/show_dialpeer_spec.rb +++ b/spec/features/routing/dialpeers/show_dialpeer_spec.rb @@ -20,6 +20,7 @@ prefix: '99912', vendor: vendor, gateway: gateway, + capacity: 100, routing_tag_ids: [routing_tags.first.id, routing_tags.third.id, nil] } end diff --git a/spec/sql/switch22/route_spec.rb b/spec/sql/switch22/route_spec.rb index 824a6c927..6737cde77 100644 --- a/spec/sql/switch22/route_spec.rb +++ b/spec/sql/switch22/route_spec.rb @@ -4660,11 +4660,10 @@ def routing_sp end context 'Termination gw capacity limit' do - context 'no termination gw capacity limit' do it 'response with ok ' do expect(subject.size).to eq(2) - expect(subject.first[:legb_res]).to eq("") + expect(subject.first[:legb_res]).to eq('') expect(subject.second[:disconnect_code_id]).to eq(113) # last profile with route not found error end end @@ -4689,7 +4688,6 @@ def routing_sp expect(subject.second[:disconnect_code_id]).to eq(113) # last profile with route not found error end end - end end end