diff --git a/.gitignore b/.gitignore index 7c57342f9..9bacf8628 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ swagger/yeti.json vendor/bundle vendor/bundler db/schema_cache.yml +db/cdr_schema_cache.yml .byebug_history config/database.yml config/pkgr.yml diff --git a/Gemfile b/Gemfile index accc22cd1..057dc6051 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ gem 'pg' gem 'pg_advisory_lock', git: 'https://github.com/didww/pg_advisory_lock.git' gem 'pg_sql_caller', git: 'https://github.com/didww/pg_sql_caller.git' gem 'rack', '2.1.4' # https://github.com/rack/rack/issues/1628 -gem 'rails', '~> 6.1.4.1' +gem 'rails', '~> 6.1.4.4' gem 'responders' # Authentication diff --git a/Gemfile.lock b/Gemfile.lock index 4ff0edab3..2ac7b290d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -95,40 +95,40 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + actioncable (6.1.4.4) + actionpack (= 6.1.4.4) + activesupport (= 6.1.4.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionmailbox (6.1.4.4) + actionpack (= 6.1.4.4) + activejob (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) mail (>= 2.7.1) - actionmailer (6.1.4.1) - actionpack (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionmailer (6.1.4.4) + actionpack (= 6.1.4.4) + actionview (= 6.1.4.4) + activejob (= 6.1.4.4) + activesupport (= 6.1.4.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) + actionpack (6.1.4.4) + actionview (= 6.1.4.4) + activesupport (= 6.1.4.4) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.1) - actionpack (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + actiontext (6.1.4.4) + actionpack (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) nokogiri (>= 1.8.5) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + actionview (6.1.4.4) + activesupport (= 6.1.4.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -159,8 +159,8 @@ GEM kaminari (~> 1.0, >= 1.2.1) railties (>= 5.2, < 6.2) ransack (~> 2.1, >= 2.1.1) - activejob (6.1.4.1) - activesupport (= 6.1.4.1) + activejob (6.1.4.4) + activesupport (= 6.1.4.4) globalid (>= 0.3.6) activeldap (5.1.1) activemodel (> 4.0.0) @@ -168,25 +168,25 @@ GEM gettext gettext_i18n_rails locale - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) + activemodel (6.1.4.4) + activesupport (= 6.1.4.4) activemodel-serializers-xml (1.0.2) activemodel (> 5.x) activesupport (> 5.x) builder (~> 3.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) + activerecord (6.1.4.4) + activemodel (= 6.1.4.4) + activesupport (= 6.1.4.4) activerecord-import (1.0.4) activerecord (>= 3.2) - activestorage (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activesupport (= 6.1.4.1) + activestorage (6.1.4.4) + actionpack (= 6.1.4.4) + activejob (= 6.1.4.4) + activerecord (= 6.1.4.4) + activesupport (= 6.1.4.4) marcel (~> 1.0.0) mini_mime (>= 1.1.0) - activesupport (6.1.4.1) + activesupport (6.1.4.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -308,13 +308,13 @@ GEM text (>= 1.3.0) gettext_i18n_rails (1.8.0) fast_gettext (>= 0.9.0) - globalid (0.5.2) + globalid (1.0.0) activesupport (>= 5.0) has_scope (0.8.0) actionpack (>= 5.2) activesupport (>= 5.2) hashdiff (0.3.8) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) inherited_resources (1.13.0) actionpack (>= 5.2, < 6.2) @@ -355,16 +355,16 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) locale (2.1.2) - loofah (2.12.0) + loofah (2.13.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (1.0.1) + marcel (1.0.2) method_source (1.0.0) - mini_mime (1.1.1) + mini_mime (1.1.2) mini_portile2 (2.6.1) - minitest (5.14.4) + minitest (5.15.0) msgpack (1.4.2) multipart-post (2.1.1) mustache (1.0.5) @@ -396,33 +396,33 @@ GEM pundit (1.1.0) activesupport (>= 3.0.0) raabro (1.4.0) - racc (1.5.2) + racc (1.6.0) rack (2.1.4) rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.1.4.1) - actioncable (= 6.1.4.1) - actionmailbox (= 6.1.4.1) - actionmailer (= 6.1.4.1) - actionpack (= 6.1.4.1) - actiontext (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activemodel (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + rails (6.1.4.4) + actioncable (= 6.1.4.4) + actionmailbox (= 6.1.4.4) + actionmailer (= 6.1.4.4) + actionpack (= 6.1.4.4) + actiontext (= 6.1.4.4) + actionview (= 6.1.4.4) + activejob (= 6.1.4.4) + activemodel (= 6.1.4.4) + activerecord (= 6.1.4.4) + activestorage (= 6.1.4.4) + activesupport (= 6.1.4.4) bundler (>= 1.15.0) - railties (= 6.1.4.1) + railties (= 6.1.4.4) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.4.2) loofah (~> 2.3) - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + railties (6.1.4.4) + actionpack (= 6.1.4.4) + activesupport (= 6.1.4.4) method_source rake (>= 0.13) thor (~> 1.0) @@ -533,9 +533,9 @@ GEM sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) syslog-logger (1.6.8) text (1.3.1) @@ -576,7 +576,7 @@ GEM rails (>= 3.2.16) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.5.1) zip-zip (0.3) rubyzip (>= 1.0.0) @@ -641,7 +641,7 @@ DEPENDENCIES puma_worker_killer pundit rack (= 2.1.4) - rails (~> 6.1.4.1) + rails (~> 6.1.4.4) rails-html-sanitizer (~> 1.0) ransack responders diff --git a/app/admin/equipment/lnp_databases.rb b/app/admin/equipment/lnp_databases.rb index 687fffd5b..68fb1e3ac 100644 --- a/app/admin/equipment/lnp_databases.rb +++ b/app/admin/equipment/lnp_databases.rb @@ -48,6 +48,7 @@ actions column :name column :type, :database_type_name, sortable: :database_type + column :cache_ttl column :created_at end @@ -78,6 +79,7 @@ attributes_table do row :id row :name + row :cache_ttl row :created_at end end @@ -93,6 +95,7 @@ row :username row :token when Lnp::Database::CONST::TYPE_SIP_REDIRECT + row :format row :host row :port row :timeout @@ -118,7 +121,7 @@ end permit_params do - attrs = %i[name database_type] + attrs = %i[name database_type cache_ttl] database_attrs = [:type] database_type = params[:lnp_database].try!(:[], :database_type) || params[:lnp_database].try!(:[], :database_attributes).try!(:[], :type) @@ -127,7 +130,7 @@ when Lnp::Database::CONST::TYPE_THINQ database_attrs += %i[host port username token timeout] when Lnp::Database::CONST::TYPE_SIP_REDIRECT - database_attrs += %i[host port timeout] + database_attrs += %i[host port timeout format_id] when Lnp::Database::CONST::TYPE_CSV database_attrs += [:csv_file_path] when Lnp::Database::CONST::TYPE_ALCAZAR @@ -145,6 +148,7 @@ f.inputs name: 'Main' do f.input :name + f.input :cache_ttl end database = f.object.database || f.object.database_type.constantize.new @@ -159,6 +163,7 @@ o.input :token o.input :timeout when Lnp::Database::CONST::TYPE_SIP_REDIRECT + o.input :format, as: :select, include_blank: false o.input :host o.input :port o.input :timeout diff --git a/app/admin/routing/routing_plan_lnp_rules.rb b/app/admin/routing/routing_plan_lnp_rules.rb index 641dbaa8d..481da03e2 100644 --- a/app/admin/routing/routing_plan_lnp_rules.rb +++ b/app/admin/routing/routing_plan_lnp_rules.rb @@ -15,7 +15,8 @@ permit_params :routing_plan_id, :database_id, :dst_prefix, :req_dst_rewrite_rule, :req_dst_rewrite_result, - :lrn_rewrite_rule, :lrn_rewrite_result + :lrn_rewrite_rule, :lrn_rewrite_result, + :drop_call_on_error, :rewrite_call_destination includes :routing_plan, :database @@ -30,6 +31,8 @@ column :database column :lrn_rewrite_rule column :lrn_rewrite_result + column :drop_call_on_error + column :rewrite_call_destination column :created_at end @@ -49,19 +52,23 @@ row :database row :lrn_rewrite_rule row :lrn_rewrite_result + row :drop_call_on_error + row :rewrite_call_destination row :created_at end end form do |f| f.inputs do - f.input :routing_plan + f.input :routing_plan, input_html: { class: 'chosen' } f.input :dst_prefix f.input :req_dst_rewrite_rule f.input :req_dst_rewrite_result - f.input :database + f.input :database, input_html: { class: 'chosen' } f.input :lrn_rewrite_rule f.input :lrn_rewrite_result + f.input :drop_call_on_error + f.input :rewrite_call_destination end actions end diff --git a/app/models/lnp/database.rb b/app/models/lnp/database.rb index 675502b39..dad766b63 100644 --- a/app/models/lnp/database.rb +++ b/app/models/lnp/database.rb @@ -5,6 +5,7 @@ # Table name: class4.lnp_databases # # id :integer(2) not null, primary key +# cache_ttl :integer(4) default(10800), not null # database_type(One of Lnp::DatabaseThinq, Lnp::DatabaseSipRedirect, Lnp::DatabaseCsv) :string # name :string not null # created_at :datetime @@ -65,6 +66,7 @@ def database_attributes=(attrs) end validates :database, presence: true + validates :cache_ttl, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: PG_MAX_INT, allow_nil: false, only_integer: true } validates :name, presence: true, uniqueness: true validates :database_id, uniqueness: { scope: :database_type } diff --git a/app/models/lnp/database_sip_redirect.rb b/app/models/lnp/database_sip_redirect.rb index 19ef74d42..eaa9f407a 100644 --- a/app/models/lnp/database_sip_redirect.rb +++ b/app/models/lnp/database_sip_redirect.rb @@ -4,16 +4,22 @@ # # Table name: class4.lnp_databases_30x_redirect # -# id :integer(2) not null, primary key -# host :string not null -# port :integer(4) -# timeout :integer(2) default(300), not null +# id :integer(2) not null, primary key +# host :string not null +# port :integer(4) +# timeout :integer(2) default(300), not null +# format_id :integer(2) default(1), not null +# +# Foreign Keys +# +# lnp_databases_30x_redirect_format_id_fkey (format_id => lnp_databases_30x_redirect_formats.id) # class Lnp::DatabaseSipRedirect < ApplicationRecord self.table_name = 'class4.lnp_databases_30x_redirect' has_one :lnp_database, as: :database, class_name: 'Lnp::Database' + belongs_to :format, class_name: 'Lnp::DatabaseSipRedirectFormat', foreign_key: :format_id validates :host, presence: true validates :timeout, allow_nil: true, numericality: { @@ -21,4 +27,5 @@ class Lnp::DatabaseSipRedirect < ApplicationRecord less_than_or_equal_to: PG_MAX_SMALLINT, only_integer: true } + validates :format_id, presence: true end diff --git a/app/models/lnp/database_sip_redirect_format.rb b/app/models/lnp/database_sip_redirect_format.rb new file mode 100644 index 000000000..2804bbe19 --- /dev/null +++ b/app/models/lnp/database_sip_redirect_format.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: class4.lnp_databases_30x_redirect_formats +# +# id :integer(2) not null, primary key +# name :string not null +# +# Indexes +# +# lnp_databases_30x_redirect_formats_name_key (name) UNIQUE +# +class Lnp::DatabaseSipRedirectFormat < ApplicationRecord + self.table_name = 'class4.lnp_databases_30x_redirect_formats' +end diff --git a/app/models/lnp/routing_plan_lnp_rule.rb b/app/models/lnp/routing_plan_lnp_rule.rb index 7ed40fc3c..764cd6416 100644 --- a/app/models/lnp/routing_plan_lnp_rule.rb +++ b/app/models/lnp/routing_plan_lnp_rule.rb @@ -4,15 +4,17 @@ # # Table name: class4.routing_plan_lnp_rules # -# id :integer(4) not null, primary key -# dst_prefix :string default(""), not null -# lrn_rewrite_result :string -# lrn_rewrite_rule :string -# req_dst_rewrite_result :string -# req_dst_rewrite_rule :string -# created_at :datetime -# database_id :integer(2) not null -# routing_plan_id :integer(4) not null +# id :integer(4) not null, primary key +# drop_call_on_error :boolean default(FALSE), not null +# dst_prefix :string default(""), not null +# lrn_rewrite_result :string +# lrn_rewrite_rule :string +# req_dst_rewrite_result :string +# req_dst_rewrite_rule :string +# rewrite_call_destination :boolean default(FALSE), not null +# created_at :datetime +# database_id :integer(2) not null +# routing_plan_id :integer(4) not null # # Indexes # diff --git a/config/locales/en.yml b/config/locales/en.yml index 254f64dfb..d303b025f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -88,6 +88,11 @@ en: numberlist_item: number_min_length: "Affects only prefix match mode" number_max_length: "Affects only prefix match mode" + lnp_database: + cache_ttl: "LNP response cache ttl in seconds. Use 0 to disable caching" + lnp_routing_plan_lnp_rule: + drop_call_on_error: "Drop call if LNP resolve failed. When disabled routing will fallback to use destination number" + rewrite_call_destination: "use LRN as new call destination" attributes: display_tag_action_value: 'Tag Action Value' diff --git a/db/migrate/20211217132047_lnp_redirect.rb b/db/migrate/20211217132047_lnp_redirect.rb new file mode 100644 index 000000000..f737178fc --- /dev/null +++ b/db/migrate/20211217132047_lnp_redirect.rb @@ -0,0 +1,4857 @@ +class LnpRedirect < ActiveRecord::Migration[6.1] + def up + execute %q{ + create table class4.lnp_databases_30x_redirect_formats( + id smallint primary key, + name varchar unique not null + ); + insert into class4.lnp_databases_30x_redirect_formats(id, name) values (1,'Contact URI username rn parameter'); + insert into class4.lnp_databases_30x_redirect_formats(id, name) values (2,'Contact URI username'); + + alter table class4.lnp_databases_30x_redirect + add format_id smallint not null default 1 references class4.lnp_databases_30x_redirect_formats(id); + + alter table class4.lnp_databases + add cache_ttl integer not null default 10800; + + alter table class4.routing_plan_lnp_rules + add drop_call_on_error boolean not null default false, + add rewrite_call_destination boolean not null default false; + + alter table class4.lnp_databases_alcazar drop column database_id; + alter table class4.lnp_databases_coure_anq drop column database_id; + + alter table class4.lnp_cache alter column expires_at set default now(); + update class4.lnp_cache set expires_at=now(); + alter table class4.lnp_cache alter column expires_at set not null; + +CREATE OR REPLACE FUNCTION lnp.cache_lnp_data(i_database_id smallint, i_dst character varying, i_lrn character varying, i_tag character varying, i_data character varying) + RETURNS void + LANGUAGE plpgsql + COST 10 +AS $function$ + declare + v_ttl integer; + v_expire timestamptz; + BEGIN + select into v_ttl cache_ttl from class4.lnp_databases where id=i_database_id; + if v_ttl is not null and v_ttl > 0 then + v_expire=now()+v_ttl*'1 seconds'::interval; + begin + insert into class4.lnp_cache(dst,lrn,created_at,updated_at,expires_at,database_id,data,tag) values( i_dst, i_lrn, now(),now(),v_expire,i_database_id,i_data,i_tag); + exception + when unique_violation then + update class4.lnp_cache set lrn=i_lrn, updated_at=now(), expires_at=v_expire, data=i_data, tag=i_tag WHERE dst=i_dst and database_id=i_database_id; + end; + end if; + END; + $function$; + + +CREATE or replace FUNCTION switch20.route( + i_node_id integer, + i_pop_id integer, + i_protocol_id smallint, + i_remote_ip inet, + i_remote_port integer, + i_local_ip inet, + i_local_port integer, + i_from_dsp character varying, + i_from_name character varying, + i_from_domain character varying, + i_from_port integer, + i_to_name character varying, + i_to_domain character varying, + i_to_port integer, + i_contact_name character varying, + i_contact_domain character varying, + i_contact_port integer, + i_uri_name character varying, + i_uri_domain character varying, + i_auth_id integer, + i_identity_data json, + i_x_yeti_auth character varying, + i_diversion character varying, + i_x_orig_ip inet, + i_x_orig_port integer, + i_x_orig_protocol_id smallint, + i_pai character varying, + i_ppi character varying, + i_privacy character varying, + i_rpid character varying, + i_rpid_privacy character varying +) RETURNS SETOF switch20.callprofile_ty + LANGUAGE plpgsql SECURITY DEFINER ROWS 10 + AS $_$ + DECLARE + v_ret switch20.callprofile_ty; + i integer; + v_ip inet; + v_remote_ip inet; + v_remote_port INTEGER; + v_transport_protocol_id smallint; + v_customer_auth_normalized class4.customers_auth_normalized; + v_destination class4.destinations%rowtype; + v_dialpeer record; + v_rateplan class4.rateplans%rowtype; + v_dst_gw class4.gateways%rowtype; + v_orig_gw class4.gateways%rowtype; + v_rp class4.routing_plans%rowtype; + v_customer_allowtime real; + v_vendor_allowtime real; + v_sorting_id integer; + v_customer_acc integer; + v_route_found boolean:=false; + v_c_acc billing.accounts%rowtype; + v_v_acc billing.accounts%rowtype; + v_network sys.network_prefixes%rowtype; + v_src_network sys.network_prefixes%rowtype; + routedata record; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ + v_rate NUMERIC; + v_now timestamp; + v_x_yeti_auth varchar; + -- v_uri_domain varchar; + v_rate_limit float:='Infinity'::float; + v_destination_rate_limit float:='Infinity'::float; + v_test_vendor_id integer; + v_random float; + v_max_call_length integer; + v_routing_key varchar; + v_lnp_key varchar; + v_lnp_rule class4.routing_plan_lnp_rules%rowtype; + v_numberlist record; + v_numberlist_item record; + v_call_tags smallint[]:='{}'::smallint[]; + v_area_direction class4.routing_tag_detection_rules%rowtype; + v_numberlist_size integer; + v_lua_context switch20.lua_call_context; + v_identity_data switch20.identity_data_ty[]; + + BEGIN + /*dbg{*/ + v_start:=now(); + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Execution start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + perform id from sys.load_balancers where signalling_ip=host(i_remote_ip)::varchar; + IF FOUND and i_x_orig_ip IS not NULL AND i_x_orig_port IS not NULL THEN + v_remote_ip:=i_x_orig_ip; + v_remote_port:=i_x_orig_port; + v_transport_protocol_id=i_x_orig_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from x-headers',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + else + v_remote_ip:=i_remote_ip; + v_remote_port:=i_remote_port; + v_transport_protocol_id:=i_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from switch leg info',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + end if; + + v_now:=now(); + v_ret:=switch20.new_profile(); + v_ret.cache_time = 10; + + v_ret.diversion_in:=i_diversion; + v_ret.diversion_out:=i_diversion; -- FIXME + + v_ret.auth_orig_protocol_id =v_transport_protocol_id; + v_ret.auth_orig_ip = v_remote_ip; + v_ret.auth_orig_port = v_remote_port; + + v_ret.src_name_in:=i_from_dsp; + v_ret.src_name_out:=v_ret.src_name_in; + + v_ret.src_prefix_in:=i_from_name; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + v_ret.dst_prefix_in:=i_uri_name; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + v_ret.ruri_domain=i_uri_domain; + v_ret.from_domain=i_from_domain; + v_ret.to_domain=i_to_domain; + + v_ret.pai_in=i_pai; + v_ret.ppi_in=i_ppi; + v_ret.privacy_in=i_privacy; + v_ret.rpid_in=i_rpid; + v_ret.rpid_privacy_in=i_rpid_privacy; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. lookup started',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_x_yeti_auth:=COALESCE(i_x_yeti_auth,''); + -- v_uri_domain:=COALESCE(i_uri_domain,''); + + if i_auth_id is null then + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null, + ca.require_incoming_auth + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.require_incoming_auth then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Incoming auth required. Respond 401',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.aleg_auth_required=true; + RETURN NEXT v_ret; + RETURN; + end IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + else + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer and + ca.require_incoming_auth and gateway_id = i_auth_id + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + end IF; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_customer_auth_normalized, true); + /*}dbg*/ + + -- redefine call SRC/DST numbers + + IF v_customer_auth_normalized.src_name_field_id=1 THEN /* default - from uri display name */ + v_ret.src_name_in:=i_from_dsp; + END IF; + v_ret.src_name_out:=v_ret.src_name_in; + + IF v_customer_auth_normalized.src_number_field_id=1 THEN /* default - from uri userpart */ + v_ret.src_prefix_in:=i_from_name; + ELSIF v_customer_auth_normalized.src_number_field_id=2 THEN /* From uri Display name */ + v_ret.src_prefix_in:=i_from_dsp; + END IF; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + IF v_customer_auth_normalized.dst_number_field_id=1 THEN /* default - RURI userpart*/ + v_ret.dst_prefix_in:=i_uri_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=2 THEN /* TO URI userpart */ + v_ret.dst_prefix_in:=i_to_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=3 THEN /* Top-Most Diversion header userpart */ + v_ret.dst_prefix_in:=i_diversion; + END IF; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + select into v_identity_data array_agg(d) from json_populate_recordset(null::switch20.identity_data_ty, i_identity_data) d; + + -- feel customer data ;-) + v_ret.dump_level_id:=v_customer_auth_normalized.dump_level_id; + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_customer_auth_normalized.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + v_ret.orig_gw_id:=v_customer_auth_normalized.gateway_id; + + v_ret.radius_auth_profile_id=v_customer_auth_normalized.radius_auth_profile_id; + v_ret.aleg_radius_acc_profile_id=v_customer_auth_normalized.radius_accounting_profile_id; + v_ret.record_audio=v_customer_auth_normalized.enable_audio_recording; + + v_ret.customer_acc_check_balance=v_customer_auth_normalized.check_account_balance; + + SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + v_destination_rate_limit=coalesce(v_c_acc.destination_rate_limit::float,'Infinity'::float); + + if v_customer_auth_normalized.check_account_balance AND v_c_acc.balance<=v_c_acc.min_balance then + v_ret.disconnect_code_id=8000; --No enough customer balance + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + + SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth_normalized.gateway_id; + if not v_orig_gw.enabled then + v_ret.disconnect_code_id=8005; -- Origination gateway is disabled + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.resources:=''; + if v_customer_auth_normalized.capacity is not null then + v_ret.resources:=v_ret.resources||'3:'||v_customer_auth_normalized.customers_auth_id||':'||v_customer_auth_normalized.capacity::varchar||':1;'; + end if; + + if v_c_acc.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'1:'||v_c_acc.id::varchar||':'||v_c_acc.origination_capacity::varchar||':1;'; + end if; + + if v_c_acc.total_capacity is not null then + v_ret.resources:=v_ret.resources||'7:'||v_c_acc.id::varchar||':'||v_c_acc.total_capacity::varchar||':1;'; + end if; + + if v_orig_gw.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'4:'||v_orig_gw.id::varchar||':'||v_orig_gw.origination_capacity::varchar||':1;'; + end if; + + -- Tag processing CA + v_call_tags=yeti_ext.tag_action(v_customer_auth_normalized.tag_action_id, v_call_tags, v_customer_auth_normalized.tag_action_value); + + /* + number rewriting _Before_ routing + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand(v_ret.dst_prefix_out,v_customer_auth_normalized.dst_rewrite_rule,v_customer_auth_normalized.dst_rewrite_result); + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand(v_ret.src_prefix_out,v_customer_auth_normalized.src_rewrite_rule,v_customer_auth_normalized.src_rewrite_result); + v_ret.src_name_out=yeti_ext.regexp_replace_rand(v_ret.src_name_out,v_customer_auth_normalized.src_name_rewrite_rule,v_customer_auth_normalized.src_name_rewrite_result, true); + + -- if v_ret.radius_auth_profile_id is not null then + v_ret.src_number_radius:=i_from_name; + v_ret.dst_number_radius:=i_uri_name; + v_ret.src_number_radius=yeti_ext.regexp_replace_rand( + v_ret.src_number_radius, + v_customer_auth_normalized.src_number_radius_rewrite_rule, + v_customer_auth_normalized.src_number_radius_rewrite_result + ); + + v_ret.dst_number_radius=yeti_ext.regexp_replace_rand( + v_ret.dst_number_radius, + v_customer_auth_normalized.dst_number_radius_rewrite_rule, + v_customer_auth_normalized.dst_number_radius_rewrite_result + ); + v_ret.customer_auth_name=v_customer_auth_normalized."name"; + v_ret.customer_name=(select "name" from public.contractors where id=v_customer_auth_normalized.customer_id limit 1); + -- end if; +/** + if v_customer_auth_normalized.lua_script_id is not null then + v_lua_context.src_name_in = v_ret.src_name_in; + v_lua_context.src_number_in = v_ret.src_prefix_in; + v_lua_context.dst_number_in = v_ret.dst_prefix_in; + v_lua_context.src_name_out = v_ret.src_name_out; + v_lua_context.src_number_out = v_ret.src_prefix_out; + v_lua_context.dst_number_out = v_ret.dst_prefix_out; + -- v_lua_context.src_name_routing + -- v_lua_context.src_number_routing + -- v_lua_context.dst_number_routing + -- #arrays + -- v_lua_context.diversion_in + -- v_lua_context.diversion_routing + -- v_lua_context.diversion_out + select into v_lua_context switch20.lua_exec(v_customer_auth_normalized.lua_script_id, v_lua_context); + v_ret.src_name_out = v_lua_context.src_name_out; + v_ret.src_prefix_out = v_lua_context.src_number_out; + v_ret.dst_prefix_out = v_lua_context.dst_number_out; + end if; +**/ + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + + ----- Numberlist processing------------------------------------------------------------------------------------------------------- + if v_customer_auth_normalized.dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.dst_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and ni.key=v_ret.dst_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * + from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.dst_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.dst_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + if v_customer_auth_normalized.src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist processing. Lookup by key: %s',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.src_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and ni.key=v_ret.src_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.src_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.src_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + -- setting numbers used for routing & billing + v_ret.src_prefix_routing=v_ret.src_prefix_out; + v_ret.dst_prefix_routing=v_ret.dst_prefix_out; + v_routing_key=v_ret.dst_prefix_out; + + -- Areas and Tag detection------------------------------------------- + v_ret.src_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.src_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_area_id; + /*}dbg*/ + + v_ret.dst_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.dst_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_area_id; + /*}dbg*/ + + + select into v_area_direction * from class4.routing_tag_detection_rules + where + (src_area_id is null OR src_area_id = v_ret.src_area_id) AND + (dst_area_id is null OR dst_area_id = v_ret.dst_area_id) AND + prefix_range(src_prefix) @> prefix_range(v_ret.src_prefix_routing) AND + prefix_range(dst_prefix) @> prefix_range(v_ret.dst_prefix_routing) AND + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id ) > 0 + order by + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id) desc, + length(prefix_range(src_prefix)) desc, + length(prefix_range(dst_prefix)) desc, + src_area_id is null, + dst_area_id is null + limit 1; + if found then + v_call_tags=yeti_ext.tag_action(v_area_direction.tag_action_id, v_call_tags, v_area_direction.tag_action_value); + end if; + + v_ret.routing_tag_ids:=v_call_tags; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing tag detected: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.routing_tag_ids; + /*}dbg*/ + ---------------------------------------------------------------------- + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; + + v_routing_key=v_ret.dst_prefix_routing; + SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; + if v_rp.sorting_id=5 then -- route testing + v_test_vendor_id=regexp_replace(v_routing_key,'(.*)\*(.*)','\1')::integer; + v_routing_key=regexp_replace(v_routing_key,'(.*)\*(.*)','\2'); + v_ret.dst_prefix_out=v_routing_key; + v_ret.dst_prefix_routing=v_routing_key; + end if; + + if v_rp.use_lnp then + select into v_lnp_rule rules.* + from class4.routing_plan_lnp_rules rules + WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id + order by length(prefix_range(rules.dst_prefix)) desc limit 1; + if found then + v_ret.lnp_database_id=v_lnp_rule.database_id; + v_lnp_key=v_ret.dst_prefix_routing; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Need LNP lookup, LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + v_lnp_key=yeti_ext.regexp_replace_rand(v_lnp_key,v_lnp_rule.req_dst_rewrite_rule,v_lnp_rule.req_dst_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP key translation. LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + -- try cache + select into v_ret.lrn lrn from class4.lnp_cache where dst=v_lnp_key AND database_id=v_lnp_rule.database_id and expires_at>v_now; + if found then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Data found in cache, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from cache + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; + else + v_ret.lrn=switch20.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); + if v_ret.lrn is null then -- fail + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + if v_lnp_rule.drop_call_on_error then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8003; --No response from LNP DB + RETURN NEXT v_ret; + RETURN; + end if; + else + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Success, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from LNP DB + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; + end if; + end if; + end if; + end if; + + + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. search start. Routing key: %. Routing tags: %, Rate limit: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_ret.routing_tag_ids, v_destination_rate_limit; + /*}dbg*/ + v_src_network:=switch20.detect_network(v_ret.src_prefix_routing); + v_ret.src_network_id=v_src_network.network_id; + v_ret.src_country_id=v_src_network.country_id; + + v_network:=switch20.detect_network(v_ret.dst_prefix_routing); + v_ret.dst_network_id=v_network.network_id; + v_ret.dst_country_id=v_network.country_id; + + IF v_rp.validate_dst_number_network AND v_ret.dst_network_id is null THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Network detection. DST network validation enabled and DST network was not found. Rejecting calls',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + v_ret.disconnect_code_id=8007; --No network detected for DST number + RETURN NEXT v_ret; + RETURN; + END IF; + + + IF v_rp.validate_dst_number_format AND NOT (v_routing_key ~ '^[0-9]+$') THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dst number format is not valid. DST number: %s',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key; + /*}dbg*/ + + v_ret.disconnect_code_id=8008; --Invalid number format + RETURN NEXT v_ret; + RETURN; + END IF; + + SELECT into v_destination d.*/*,switch.tracelog(d.*)*/ + FROM class4.destinations d + JOIN class4.rate_plan_groups rpg ON d.rate_group_id=rpg.rate_group_id + WHERE + prefix_range(prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between d.dst_number_min_length and d.dst_number_max_length + AND rpg.rateplan_id=v_customer_auth_normalized.rateplan_id + AND enabled + AND valid_from <= v_now + AND valid_till >= v_now + AND yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags, d.routing_tag_mode_id)>0 + ORDER BY length(prefix_range(prefix)) DESC, yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags) desc + limit 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. Destination not found',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=111; --Cant find destination prefix + RETURN NEXT v_ret; + RETURN; + END IF; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_destination, true); + /*}dbg*/ + + v_ret.destination_id:=v_destination.id; + v_ret.destination_prefix=v_destination.prefix; + v_ret.destination_initial_interval:=v_destination.initial_interval; + v_ret.destination_fee:=v_destination.connect_fee::varchar; + v_ret.destination_next_interval:=v_destination.next_interval; + v_ret.destination_rate_policy_id:=v_destination.rate_policy_id; + v_ret.destination_reverse_billing:=v_destination.reverse_billing; + IF v_destination.reject_calls THEN + v_ret.disconnect_code_id=112; --Rejected by destination + RETURN NEXT v_ret; + RETURN; + END IF; + + if v_destination.next_rate::float>v_destination_rate_limit then + v_ret.disconnect_code_id=8006; -- No destination with appropriate rate found + RETURN NEXT v_ret; + RETURN; + end if; + + select into v_rateplan * from class4.rateplans where id=v_customer_auth_normalized.rateplan_id; + if COALESCE(v_destination.profit_control_mode_id,v_rateplan.profit_control_mode_id)=2 then -- per call + v_rate_limit=v_destination.next_rate::float; + end if; + + + /* + FIND dialpeers logic. Queries must use prefix index for best performance + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. search start. Routing key: %. Rate limit: %. Routing tag: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_rate_limit, v_ret.routing_tag_ids; + /*}dbg*/ + CASE v_rp.sorting_id + WHEN'1' THEN -- LCR,Prio, ACD&ASR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + t_dp.next_rate as dp_next_rate, + t_dp.lcr_rate_multiplier AS dp_lcr_rate_multiplier, + t_dp.priority AS dp_priority, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_next_rate<=v_rate_limit + AND dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY dp_next_rate*dp_lcr_rate_multiplier, dp_priority DESC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN '2' THEN --LCR, no prio, No ACD&ASR control + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + -- (t_vendor_gateway.*)::class4.gateways as s1_vendor_gateway, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN '3' THEN --Prio, LCR, ACD&ASR control + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'4' THEN -- LCRD, Prio, ACD&ACR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + ((t_dp.next_rate - first_value(t_dp.next_rate) OVER(ORDER BY t_dp.next_rate ASC)) > v_rp.rate_delta_max)::INTEGER *(t_dp.next_rate + t_dp.priority) - t_dp.priority AS r2, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate <= v_rate_limit + and dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY r2 ASC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN'5' THEN -- Route test + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'6' THEN -- QD.Static,LCR,ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + left join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'7' THEN -- QD.Static, No ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + + ELSE + RAISE NOTICE 'BUG: unknown sorting_id'; + END CASE; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dialpeer search done',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=113; --No routes + RETURN NEXT v_ret; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DONE.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN; + END; + $_$; + + set search_path TO switch20; + SELECT * from switch20.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + +CREATE or replace FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_id smallint, i_remote_ip inet, i_remote_port integer, i_local_ip inet, i_local_port integer, i_from_dsp character varying, i_from_name character varying, i_from_domain character varying, i_from_port integer, i_to_name character varying, i_to_domain character varying, i_to_port integer, i_contact_name character varying, i_contact_domain character varying, i_contact_port integer, i_uri_name character varying, i_uri_domain character varying, i_auth_id integer, i_x_yeti_auth character varying, i_diversion character varying, i_x_orig_ip inet, i_x_orig_port integer, i_x_orig_protocol_id smallint, i_pai character varying, i_ppi character varying, i_privacy character varying, i_rpid character varying, i_rpid_privacy character varying) RETURNS SETOF switch19.callprofile_ty + LANGUAGE plpgsql SECURITY DEFINER ROWS 10 + AS $_$ + DECLARE + v_ret switch19.callprofile_ty; + i integer; + v_ip inet; + v_remote_ip inet; + v_remote_port INTEGER; + v_transport_protocol_id smallint; + v_customer_auth_normalized class4.customers_auth_normalized; + v_destination class4.destinations%rowtype; + v_dialpeer record; + v_rateplan class4.rateplans%rowtype; + v_dst_gw class4.gateways%rowtype; + v_orig_gw class4.gateways%rowtype; + v_rp class4.routing_plans%rowtype; + v_customer_allowtime real; + v_vendor_allowtime real; + v_sorting_id integer; + v_customer_acc integer; + v_route_found boolean:=false; + v_c_acc billing.accounts%rowtype; + v_v_acc billing.accounts%rowtype; + v_network sys.network_prefixes%rowtype; + v_src_network sys.network_prefixes%rowtype; + routedata record; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ + v_rate NUMERIC; + v_now timestamp; + v_x_yeti_auth varchar; + -- v_uri_domain varchar; + v_rate_limit float:='Infinity'::float; + v_destination_rate_limit float:='Infinity'::float; + v_test_vendor_id integer; + v_random float; + v_max_call_length integer; + v_routing_key varchar; + v_lnp_key varchar; + v_lnp_rule class4.routing_plan_lnp_rules%rowtype; + v_numberlist record; + v_numberlist_item record; + v_call_tags smallint[]:='{}'::smallint[]; + v_area_direction class4.routing_tag_detection_rules%rowtype; + v_numberlist_size integer; + v_lua_context switch19.lua_call_context; + + BEGIN + /*dbg{*/ + v_start:=now(); + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Execution start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + perform id from sys.load_balancers where signalling_ip=host(i_remote_ip)::varchar; + IF FOUND and i_x_orig_ip IS not NULL AND i_x_orig_port IS not NULL THEN + v_remote_ip:=i_x_orig_ip; + v_remote_port:=i_x_orig_port; + v_transport_protocol_id=i_x_orig_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from x-headers',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + else + v_remote_ip:=i_remote_ip; + v_remote_port:=i_remote_port; + v_transport_protocol_id:=i_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from switch leg info',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + end if; + + v_now:=now(); + v_ret:=switch19.new_profile(); + v_ret.cache_time = 10; + + v_ret.diversion_in:=i_diversion; + v_ret.diversion_out:=i_diversion; -- FIXME + + v_ret.auth_orig_protocol_id =v_transport_protocol_id; + v_ret.auth_orig_ip = v_remote_ip; + v_ret.auth_orig_port = v_remote_port; + + v_ret.src_name_in:=i_from_dsp; + v_ret.src_name_out:=v_ret.src_name_in; + + v_ret.src_prefix_in:=i_from_name; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + v_ret.dst_prefix_in:=i_uri_name; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + v_ret.ruri_domain=i_uri_domain; + v_ret.from_domain=i_from_domain; + v_ret.to_domain=i_to_domain; + + v_ret.pai_in=i_pai; + v_ret.ppi_in=i_ppi; + v_ret.privacy_in=i_privacy; + v_ret.rpid_in=i_rpid; + v_ret.rpid_privacy_in=i_rpid_privacy; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. lookup started',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_x_yeti_auth:=COALESCE(i_x_yeti_auth,''); + -- v_uri_domain:=COALESCE(i_uri_domain,''); + + if i_auth_id is null then + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null, + ca.require_incoming_auth + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.require_incoming_auth then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Incoming auth required. Respond 401',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.aleg_auth_required=true; + RETURN NEXT v_ret; + RETURN; + end IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + else + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer and + ca.require_incoming_auth and gateway_id = i_auth_id + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + end IF; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_customer_auth_normalized, true); + /*}dbg*/ + + -- redefine call SRC/DST numbers + + IF v_customer_auth_normalized.src_name_field_id=1 THEN /* default - from uri display name */ + v_ret.src_name_in:=i_from_dsp; + END IF; + v_ret.src_name_out:=v_ret.src_name_in; + + IF v_customer_auth_normalized.src_number_field_id=1 THEN /* default - from uri userpart */ + v_ret.src_prefix_in:=i_from_name; + ELSIF v_customer_auth_normalized.src_number_field_id=2 THEN /* From uri Display name */ + v_ret.src_prefix_in:=i_from_dsp; + END IF; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + IF v_customer_auth_normalized.dst_number_field_id=1 THEN /* default - RURI userpart*/ + v_ret.dst_prefix_in:=i_uri_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=2 THEN /* TO URI userpart */ + v_ret.dst_prefix_in:=i_to_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=3 THEN /* Top-Most Diversion header userpart */ + v_ret.dst_prefix_in:=i_diversion; + END IF; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + -- feel customer data ;-) + v_ret.dump_level_id:=v_customer_auth_normalized.dump_level_id; + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_customer_auth_normalized.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + v_ret.orig_gw_id:=v_customer_auth_normalized.gateway_id; + + v_ret.radius_auth_profile_id=v_customer_auth_normalized.radius_auth_profile_id; + v_ret.aleg_radius_acc_profile_id=v_customer_auth_normalized.radius_accounting_profile_id; + v_ret.record_audio=v_customer_auth_normalized.enable_audio_recording; + + v_ret.customer_acc_check_balance=v_customer_auth_normalized.check_account_balance; + + SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + v_destination_rate_limit=coalesce(v_c_acc.destination_rate_limit::float,'Infinity'::float); + + if v_customer_auth_normalized.check_account_balance AND v_c_acc.balance<=v_c_acc.min_balance then + v_ret.disconnect_code_id=8000; --No enough customer balance + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + + SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth_normalized.gateway_id; + if not v_orig_gw.enabled then + v_ret.disconnect_code_id=8005; -- Origination gateway is disabled + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.resources:=''; + if v_customer_auth_normalized.capacity is not null then + v_ret.resources:=v_ret.resources||'3:'||v_customer_auth_normalized.customers_auth_id||':'||v_customer_auth_normalized.capacity::varchar||':1;'; + end if; + + if v_c_acc.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'1:'||v_c_acc.id::varchar||':'||v_c_acc.origination_capacity::varchar||':1;'; + end if; + + if v_c_acc.total_capacity is not null then + v_ret.resources:=v_ret.resources||'7:'||v_c_acc.id::varchar||':'||v_c_acc.total_capacity::varchar||':1;'; + end if; + + if v_orig_gw.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'4:'||v_orig_gw.id::varchar||':'||v_orig_gw.origination_capacity::varchar||':1;'; + end if; + + -- Tag processing CA + v_call_tags=yeti_ext.tag_action(v_customer_auth_normalized.tag_action_id, v_call_tags, v_customer_auth_normalized.tag_action_value); + + /* + number rewriting _Before_ routing + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand(v_ret.dst_prefix_out,v_customer_auth_normalized.dst_rewrite_rule,v_customer_auth_normalized.dst_rewrite_result); + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand(v_ret.src_prefix_out,v_customer_auth_normalized.src_rewrite_rule,v_customer_auth_normalized.src_rewrite_result); + v_ret.src_name_out=yeti_ext.regexp_replace_rand(v_ret.src_name_out,v_customer_auth_normalized.src_name_rewrite_rule,v_customer_auth_normalized.src_name_rewrite_result, true); + + -- if v_ret.radius_auth_profile_id is not null then + v_ret.src_number_radius:=i_from_name; + v_ret.dst_number_radius:=i_uri_name; + v_ret.src_number_radius=yeti_ext.regexp_replace_rand( + v_ret.src_number_radius, + v_customer_auth_normalized.src_number_radius_rewrite_rule, + v_customer_auth_normalized.src_number_radius_rewrite_result + ); + + v_ret.dst_number_radius=yeti_ext.regexp_replace_rand( + v_ret.dst_number_radius, + v_customer_auth_normalized.dst_number_radius_rewrite_rule, + v_customer_auth_normalized.dst_number_radius_rewrite_result + ); + v_ret.customer_auth_name=v_customer_auth_normalized."name"; + v_ret.customer_name=(select "name" from public.contractors where id=v_customer_auth_normalized.customer_id limit 1); + -- end if; +/* + if v_customer_auth_normalized.lua_script_id is not null then + v_lua_context.src_name_in = v_ret.src_name_in; + v_lua_context.src_number_in = v_ret.src_prefix_in; + v_lua_context.dst_number_in = v_ret.dst_prefix_in; + v_lua_context.src_name_out = v_ret.src_name_out; + v_lua_context.src_number_out = v_ret.src_prefix_out; + v_lua_context.dst_number_out = v_ret.dst_prefix_out; + -- v_lua_context.src_name_routing + -- v_lua_context.src_number_routing + -- v_lua_context.dst_number_routing + -- #arrays + -- v_lua_context.diversion_in + -- v_lua_context.diversion_routing + -- v_lua_context.diversion_out + v_lua_context = switch19.lua_exec(v_customer_auth_normalized.lua_script_id, v_lua_context); + v_ret.src_name_out = v_lua_context.src_name_out; + v_ret.src_prefix_out = v_lua_context.src_number_out; + v_ret.dst_prefix_out = v_lua_context.dst_number_out; + end if; +*/ + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + + ----- Numberlist processing------------------------------------------------------------------------------------------------------- + if v_customer_auth_normalized.dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.dst_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and ni.key=v_ret.dst_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * + from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.dst_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.dst_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + if v_customer_auth_normalized.src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist processing. Lookup by key: %s',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.src_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and ni.key=v_ret.src_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.src_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.src_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + -- setting numbers used for routing & billing + v_ret.src_prefix_routing=v_ret.src_prefix_out; + v_ret.dst_prefix_routing=v_ret.dst_prefix_out; + v_routing_key=v_ret.dst_prefix_out; + + -- Areas and Tag detection------------------------------------------- + v_ret.src_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.src_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_area_id; + /*}dbg*/ + + v_ret.dst_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.dst_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_area_id; + /*}dbg*/ + + + select into v_area_direction * from class4.routing_tag_detection_rules + where + (src_area_id is null OR src_area_id = v_ret.src_area_id) AND + (dst_area_id is null OR dst_area_id = v_ret.dst_area_id) AND + prefix_range(src_prefix) @> prefix_range(v_ret.src_prefix_routing) AND + prefix_range(dst_prefix) @> prefix_range(v_ret.dst_prefix_routing) AND + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id ) > 0 + order by + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id) desc, + length(prefix_range(src_prefix)) desc, + length(prefix_range(dst_prefix)) desc, + src_area_id is null, + dst_area_id is null + limit 1; + if found then + v_call_tags=yeti_ext.tag_action(v_area_direction.tag_action_id, v_call_tags, v_area_direction.tag_action_value); + end if; + + v_ret.routing_tag_ids:=v_call_tags; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing tag detected: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.routing_tag_ids; + /*}dbg*/ + ---------------------------------------------------------------------- + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; + + v_routing_key=v_ret.dst_prefix_routing; + SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; + if v_rp.sorting_id=5 then -- route testing + v_test_vendor_id=regexp_replace(v_routing_key,'(.*)\*(.*)','\1')::integer; + v_routing_key=regexp_replace(v_routing_key,'(.*)\*(.*)','\2'); + v_ret.dst_prefix_out=v_routing_key; + v_ret.dst_prefix_routing=v_routing_key; + end if; + + if v_rp.use_lnp then + select into v_lnp_rule rules.* + from class4.routing_plan_lnp_rules rules + WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id + order by length(prefix_range(rules.dst_prefix)) desc limit 1; + if found then + v_ret.lnp_database_id=v_lnp_rule.database_id; + v_lnp_key=v_ret.dst_prefix_routing; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Need LNP lookup, LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + v_lnp_key=yeti_ext.regexp_replace_rand(v_lnp_key,v_lnp_rule.req_dst_rewrite_rule,v_lnp_rule.req_dst_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP key translation. LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + -- try cache + select into v_ret.lrn lrn from class4.lnp_cache where dst=v_lnp_key AND database_id=v_lnp_rule.database_id and expires_at>v_now; + if found then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Data found in cache, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from cache + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; + else + v_ret.lrn=switch19.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); + if v_ret.lrn is null then -- fail + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + if v_lnp_rule.drop_call_on_error then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8003; --No response from LNP DB + RETURN NEXT v_ret; + RETURN; + end if; + else + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Success, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from LNP DB + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; + end if; + end if; + end if; + end if; + + + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. search start. Routing key: %. Routing tags: %, Rate limit: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_ret.routing_tag_ids, v_destination_rate_limit; + /*}dbg*/ + v_src_network:=switch19.detect_network(v_ret.src_prefix_routing); + v_ret.src_network_id=v_src_network.network_id; + v_ret.src_country_id=v_src_network.country_id; + + v_network:=switch19.detect_network(v_ret.dst_prefix_routing); + v_ret.dst_network_id=v_network.network_id; + v_ret.dst_country_id=v_network.country_id; + + IF v_rp.validate_dst_number_network AND v_ret.dst_network_id is null THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Network detection. DST network validation enabled and DST network was not found. Rejecting calls',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + v_ret.disconnect_code_id=8007; --No network detected for DST number + RETURN NEXT v_ret; + RETURN; + END IF; + + + IF v_rp.validate_dst_number_format AND NOT (v_routing_key ~ '^[0-9]+$') THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dst number format is not valid. DST number: %s',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key; + /*}dbg*/ + + v_ret.disconnect_code_id=8008; --Invalid number format + RETURN NEXT v_ret; + RETURN; + END IF; + + SELECT into v_destination d.*/*,switch.tracelog(d.*)*/ + FROM class4.destinations d + JOIN class4.rate_plan_groups rpg ON d.rate_group_id=rpg.rate_group_id + WHERE + prefix_range(prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between d.dst_number_min_length and d.dst_number_max_length + AND rpg.rateplan_id=v_customer_auth_normalized.rateplan_id + AND enabled + AND valid_from <= v_now + AND valid_till >= v_now + AND yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags, d.routing_tag_mode_id)>0 + ORDER BY length(prefix_range(prefix)) DESC, yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags) desc + limit 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. Destination not found',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=111; --Cant find destination prefix + RETURN NEXT v_ret; + RETURN; + END IF; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_destination, true); + /*}dbg*/ + + v_ret.destination_id:=v_destination.id; + v_ret.destination_prefix=v_destination.prefix; + v_ret.destination_initial_interval:=v_destination.initial_interval; + v_ret.destination_fee:=v_destination.connect_fee::varchar; + v_ret.destination_next_interval:=v_destination.next_interval; + v_ret.destination_rate_policy_id:=v_destination.rate_policy_id; + v_ret.destination_reverse_billing:=v_destination.reverse_billing; + IF v_destination.reject_calls THEN + v_ret.disconnect_code_id=112; --Rejected by destination + RETURN NEXT v_ret; + RETURN; + END IF; + + if v_destination.next_rate::float>v_destination_rate_limit then + v_ret.disconnect_code_id=8006; -- No destination with appropriate rate found + RETURN NEXT v_ret; + RETURN; + end if; + + select into v_rateplan * from class4.rateplans where id=v_customer_auth_normalized.rateplan_id; + if COALESCE(v_destination.profit_control_mode_id,v_rateplan.profit_control_mode_id)=2 then -- per call + v_rate_limit=v_destination.next_rate::float; + end if; + + + /* + FIND dialpeers logic. Queries must use prefix index for best performance + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. search start. Routing key: %. Rate limit: %. Routing tag: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_rate_limit, v_ret.routing_tag_ids; + /*}dbg*/ + CASE v_rp.sorting_id + WHEN'1' THEN -- LCR,Prio, ACD&ASR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + t_dp.next_rate as dp_next_rate, + t_dp.lcr_rate_multiplier AS dp_lcr_rate_multiplier, + t_dp.priority AS dp_priority, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_next_rate<=v_rate_limit + AND dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY dp_next_rate*dp_lcr_rate_multiplier, dp_priority DESC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN '2' THEN --LCR, no prio, No ACD&ASR control + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + -- (t_vendor_gateway.*)::class4.gateways as s1_vendor_gateway, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN '3' THEN --Prio, LCR, ACD&ASR control + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'4' THEN -- LCRD, Prio, ACD&ACR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + ((t_dp.next_rate - first_value(t_dp.next_rate) OVER(ORDER BY t_dp.next_rate ASC)) > v_rp.rate_delta_max)::INTEGER *(t_dp.next_rate + t_dp.priority) - t_dp.priority AS r2, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate <= v_rate_limit + and dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY r2 ASC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN'5' THEN -- Route test + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'6' THEN -- QD.Static,LCR,ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + left join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'7' THEN -- QD.Static, No ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + + ELSE + RAISE NOTICE 'BUG: unknown sorting_id'; + END CASE; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dialpeer search done',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=113; --No routes + RETURN NEXT v_ret; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DONE.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN; + END; + $_$; + + set search_path TO switch19; + SELECT * from switch19.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + } + end + + def down + execute %q{ + alter table class4.routing_plan_lnp_rules + drop column drop_call_on_error, + drop column rewrite_call_destination; + + alter table class4.lnp_databases_30x_redirect + drop column format_id; + + alter table class4.lnp_databases + drop column cache_ttl; + + drop table class4.lnp_databases_30x_redirect_formats; + + alter table class4.lnp_databases_alcazar add database_id integer; + alter table class4.lnp_databases_coure_anq add database_id integer; + + alter table class4.lnp_cache alter column expires_at drop default; + alter table class4.lnp_cache alter column expires_at drop not null; + +CREATE OR REPLACE FUNCTION lnp.cache_lnp_data(i_database_id smallint, i_dst character varying, i_lrn character varying, i_tag character varying, i_data character varying) + RETURNS void + LANGUAGE plpgsql + COST 10 +AS $function$ + declare + v_ttl integer; + v_expire timestamptz; + BEGIN + select into v_ttl lnp_cache_ttl from sys.guiconfig; + v_expire=now()+v_ttl*'1 minute'::interval; + begin + insert into class4.lnp_cache(dst,lrn,created_at,updated_at,expires_at,database_id,data,tag) values( i_dst, i_lrn, now(),now(),v_expire,i_database_id,i_data,i_tag); + Exception + when unique_violation then + update class4.lnp_cache set lrn=i_lrn, updated_at=now(), expires_at=v_expire, data=i_data, tag=i_tag WHERE dst=i_dst and database_id=i_database_id; + end; + END; + $function$; + +CREATE or replace FUNCTION switch20.route( + i_node_id integer, + i_pop_id integer, + i_protocol_id smallint, + i_remote_ip inet, + i_remote_port integer, + i_local_ip inet, + i_local_port integer, + i_from_dsp character varying, + i_from_name character varying, + i_from_domain character varying, + i_from_port integer, + i_to_name character varying, + i_to_domain character varying, + i_to_port integer, + i_contact_name character varying, + i_contact_domain character varying, + i_contact_port integer, + i_uri_name character varying, + i_uri_domain character varying, + i_auth_id integer, + i_identity_data json, + i_x_yeti_auth character varying, + i_diversion character varying, + i_x_orig_ip inet, + i_x_orig_port integer, + i_x_orig_protocol_id smallint, + i_pai character varying, + i_ppi character varying, + i_privacy character varying, + i_rpid character varying, + i_rpid_privacy character varying +) RETURNS SETOF switch20.callprofile_ty + LANGUAGE plpgsql SECURITY DEFINER ROWS 10 + AS $_$ + DECLARE + v_ret switch20.callprofile_ty; + i integer; + v_ip inet; + v_remote_ip inet; + v_remote_port INTEGER; + v_transport_protocol_id smallint; + v_customer_auth_normalized class4.customers_auth_normalized; + v_destination class4.destinations%rowtype; + v_dialpeer record; + v_rateplan class4.rateplans%rowtype; + v_dst_gw class4.gateways%rowtype; + v_orig_gw class4.gateways%rowtype; + v_rp class4.routing_plans%rowtype; + v_customer_allowtime real; + v_vendor_allowtime real; + v_sorting_id integer; + v_customer_acc integer; + v_route_found boolean:=false; + v_c_acc billing.accounts%rowtype; + v_v_acc billing.accounts%rowtype; + v_network sys.network_prefixes%rowtype; + v_src_network sys.network_prefixes%rowtype; + routedata record; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ + v_rate NUMERIC; + v_now timestamp; + v_x_yeti_auth varchar; + -- v_uri_domain varchar; + v_rate_limit float:='Infinity'::float; + v_destination_rate_limit float:='Infinity'::float; + v_test_vendor_id integer; + v_random float; + v_max_call_length integer; + v_routing_key varchar; + v_lnp_key varchar; + v_drop_call_if_lnp_fail boolean; + v_lnp_rule class4.routing_plan_lnp_rules%rowtype; + v_numberlist record; + v_numberlist_item record; + v_call_tags smallint[]:='{}'::smallint[]; + v_area_direction class4.routing_tag_detection_rules%rowtype; + v_numberlist_size integer; + v_lua_context switch20.lua_call_context; + v_identity_data switch20.identity_data_ty[]; + + BEGIN + /*dbg{*/ + v_start:=now(); + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Execution start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + perform id from sys.load_balancers where signalling_ip=host(i_remote_ip)::varchar; + IF FOUND and i_x_orig_ip IS not NULL AND i_x_orig_port IS not NULL THEN + v_remote_ip:=i_x_orig_ip; + v_remote_port:=i_x_orig_port; + v_transport_protocol_id=i_x_orig_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from x-headers',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + else + v_remote_ip:=i_remote_ip; + v_remote_port:=i_remote_port; + v_transport_protocol_id:=i_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from switch leg info',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + end if; + + v_now:=now(); + v_ret:=switch20.new_profile(); + v_ret.cache_time = 10; + + v_ret.diversion_in:=i_diversion; + v_ret.diversion_out:=i_diversion; -- FIXME + + v_ret.auth_orig_protocol_id =v_transport_protocol_id; + v_ret.auth_orig_ip = v_remote_ip; + v_ret.auth_orig_port = v_remote_port; + + v_ret.src_name_in:=i_from_dsp; + v_ret.src_name_out:=v_ret.src_name_in; + + v_ret.src_prefix_in:=i_from_name; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + v_ret.dst_prefix_in:=i_uri_name; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + v_ret.ruri_domain=i_uri_domain; + v_ret.from_domain=i_from_domain; + v_ret.to_domain=i_to_domain; + + v_ret.pai_in=i_pai; + v_ret.ppi_in=i_ppi; + v_ret.privacy_in=i_privacy; + v_ret.rpid_in=i_rpid; + v_ret.rpid_privacy_in=i_rpid_privacy; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. lookup started',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_x_yeti_auth:=COALESCE(i_x_yeti_auth,''); + -- v_uri_domain:=COALESCE(i_uri_domain,''); + + if i_auth_id is null then + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null, + ca.require_incoming_auth + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.require_incoming_auth then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Incoming auth required. Respond 401',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.aleg_auth_required=true; + RETURN NEXT v_ret; + RETURN; + end IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + else + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer and + ca.require_incoming_auth and gateway_id = i_auth_id + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + end IF; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_customer_auth_normalized, true); + /*}dbg*/ + + -- redefine call SRC/DST numbers + + IF v_customer_auth_normalized.src_name_field_id=1 THEN /* default - from uri display name */ + v_ret.src_name_in:=i_from_dsp; + END IF; + v_ret.src_name_out:=v_ret.src_name_in; + + IF v_customer_auth_normalized.src_number_field_id=1 THEN /* default - from uri userpart */ + v_ret.src_prefix_in:=i_from_name; + ELSIF v_customer_auth_normalized.src_number_field_id=2 THEN /* From uri Display name */ + v_ret.src_prefix_in:=i_from_dsp; + END IF; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + IF v_customer_auth_normalized.dst_number_field_id=1 THEN /* default - RURI userpart*/ + v_ret.dst_prefix_in:=i_uri_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=2 THEN /* TO URI userpart */ + v_ret.dst_prefix_in:=i_to_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=3 THEN /* Top-Most Diversion header userpart */ + v_ret.dst_prefix_in:=i_diversion; + END IF; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + select into v_identity_data array_agg(d) from json_populate_recordset(null::switch20.identity_data_ty, i_identity_data) d; + + -- feel customer data ;-) + v_ret.dump_level_id:=v_customer_auth_normalized.dump_level_id; + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_customer_auth_normalized.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + v_ret.orig_gw_id:=v_customer_auth_normalized.gateway_id; + + v_ret.radius_auth_profile_id=v_customer_auth_normalized.radius_auth_profile_id; + v_ret.aleg_radius_acc_profile_id=v_customer_auth_normalized.radius_accounting_profile_id; + v_ret.record_audio=v_customer_auth_normalized.enable_audio_recording; + + v_ret.customer_acc_check_balance=v_customer_auth_normalized.check_account_balance; + + SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + v_destination_rate_limit=coalesce(v_c_acc.destination_rate_limit::float,'Infinity'::float); + + if v_customer_auth_normalized.check_account_balance AND v_c_acc.balance<=v_c_acc.min_balance then + v_ret.disconnect_code_id=8000; --No enough customer balance + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + + SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth_normalized.gateway_id; + if not v_orig_gw.enabled then + v_ret.disconnect_code_id=8005; -- Origination gateway is disabled + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.resources:=''; + if v_customer_auth_normalized.capacity is not null then + v_ret.resources:=v_ret.resources||'3:'||v_customer_auth_normalized.customers_auth_id||':'||v_customer_auth_normalized.capacity::varchar||':1;'; + end if; + + if v_c_acc.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'1:'||v_c_acc.id::varchar||':'||v_c_acc.origination_capacity::varchar||':1;'; + end if; + + if v_c_acc.total_capacity is not null then + v_ret.resources:=v_ret.resources||'7:'||v_c_acc.id::varchar||':'||v_c_acc.total_capacity::varchar||':1;'; + end if; + + if v_orig_gw.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'4:'||v_orig_gw.id::varchar||':'||v_orig_gw.origination_capacity::varchar||':1;'; + end if; + + -- Tag processing CA + v_call_tags=yeti_ext.tag_action(v_customer_auth_normalized.tag_action_id, v_call_tags, v_customer_auth_normalized.tag_action_value); + + /* + number rewriting _Before_ routing + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand(v_ret.dst_prefix_out,v_customer_auth_normalized.dst_rewrite_rule,v_customer_auth_normalized.dst_rewrite_result); + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand(v_ret.src_prefix_out,v_customer_auth_normalized.src_rewrite_rule,v_customer_auth_normalized.src_rewrite_result); + v_ret.src_name_out=yeti_ext.regexp_replace_rand(v_ret.src_name_out,v_customer_auth_normalized.src_name_rewrite_rule,v_customer_auth_normalized.src_name_rewrite_result, true); + + -- if v_ret.radius_auth_profile_id is not null then + v_ret.src_number_radius:=i_from_name; + v_ret.dst_number_radius:=i_uri_name; + v_ret.src_number_radius=yeti_ext.regexp_replace_rand( + v_ret.src_number_radius, + v_customer_auth_normalized.src_number_radius_rewrite_rule, + v_customer_auth_normalized.src_number_radius_rewrite_result + ); + + v_ret.dst_number_radius=yeti_ext.regexp_replace_rand( + v_ret.dst_number_radius, + v_customer_auth_normalized.dst_number_radius_rewrite_rule, + v_customer_auth_normalized.dst_number_radius_rewrite_result + ); + v_ret.customer_auth_name=v_customer_auth_normalized."name"; + v_ret.customer_name=(select "name" from public.contractors where id=v_customer_auth_normalized.customer_id limit 1); + -- end if; +/** + if v_customer_auth_normalized.lua_script_id is not null then + v_lua_context.src_name_in = v_ret.src_name_in; + v_lua_context.src_number_in = v_ret.src_prefix_in; + v_lua_context.dst_number_in = v_ret.dst_prefix_in; + v_lua_context.src_name_out = v_ret.src_name_out; + v_lua_context.src_number_out = v_ret.src_prefix_out; + v_lua_context.dst_number_out = v_ret.dst_prefix_out; + -- v_lua_context.src_name_routing + -- v_lua_context.src_number_routing + -- v_lua_context.dst_number_routing + -- #arrays + -- v_lua_context.diversion_in + -- v_lua_context.diversion_routing + -- v_lua_context.diversion_out + select into v_lua_context switch20.lua_exec(v_customer_auth_normalized.lua_script_id, v_lua_context); + v_ret.src_name_out = v_lua_context.src_name_out; + v_ret.src_prefix_out = v_lua_context.src_number_out; + v_ret.dst_prefix_out = v_lua_context.dst_number_out; + end if; +**/ + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + + ----- Numberlist processing------------------------------------------------------------------------------------------------------- + if v_customer_auth_normalized.dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.dst_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and ni.key=v_ret.dst_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * + from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.dst_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.dst_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + if v_customer_auth_normalized.src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist processing. Lookup by key: %s',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.src_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and ni.key=v_ret.src_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.src_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.src_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + -- setting numbers used for routing & billing + v_ret.src_prefix_routing=v_ret.src_prefix_out; + v_ret.dst_prefix_routing=v_ret.dst_prefix_out; + v_routing_key=v_ret.dst_prefix_out; + + -- Areas and Tag detection------------------------------------------- + v_ret.src_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.src_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_area_id; + /*}dbg*/ + + v_ret.dst_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.dst_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_area_id; + /*}dbg*/ + + + select into v_area_direction * from class4.routing_tag_detection_rules + where + (src_area_id is null OR src_area_id = v_ret.src_area_id) AND + (dst_area_id is null OR dst_area_id = v_ret.dst_area_id) AND + prefix_range(src_prefix) @> prefix_range(v_ret.src_prefix_routing) AND + prefix_range(dst_prefix) @> prefix_range(v_ret.dst_prefix_routing) AND + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id ) > 0 + order by + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id) desc, + length(prefix_range(src_prefix)) desc, + length(prefix_range(dst_prefix)) desc, + src_area_id is null, + dst_area_id is null + limit 1; + if found then + v_call_tags=yeti_ext.tag_action(v_area_direction.tag_action_id, v_call_tags, v_area_direction.tag_action_value); + end if; + + v_ret.routing_tag_ids:=v_call_tags; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing tag detected: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.routing_tag_ids; + /*}dbg*/ + ---------------------------------------------------------------------- + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + + v_routing_key=v_ret.dst_prefix_routing; + SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; + if v_rp.sorting_id=5 then -- route testing + v_test_vendor_id=regexp_replace(v_routing_key,'(.*)\*(.*)','\1')::integer; + v_routing_key=regexp_replace(v_routing_key,'(.*)\*(.*)','\2'); + v_ret.dst_prefix_out=v_routing_key; + v_ret.dst_prefix_routing=v_routing_key; + end if; + + if v_rp.use_lnp then + select into v_lnp_rule rules.* + from class4.routing_plan_lnp_rules rules + WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id + order by length(prefix_range(rules.dst_prefix)) limit 1; + if found then + v_ret.lnp_database_id=v_lnp_rule.database_id; + v_lnp_key=v_ret.dst_prefix_routing; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Need LNP lookup, LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + v_lnp_key=yeti_ext.regexp_replace_rand(v_lnp_key,v_lnp_rule.req_dst_rewrite_rule,v_lnp_rule.req_dst_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP key translation. LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + -- try cache + select into v_ret.lrn lrn from class4.lnp_cache where dst=v_lnp_key AND database_id=v_lnp_rule.database_id and expires_at>v_now; + if found then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Data found in cache, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from cache + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + else + v_ret.lrn=switch20.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); + if v_ret.lrn is null then -- fail + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + if v_drop_call_if_lnp_fail then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8003; --No response from LNP DB + RETURN NEXT v_ret; + RETURN; + end if; + else + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Success, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from LNP DB + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + end if; + end if; + end if; + end if; + + + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. search start. Routing key: %. Routing tags: %, Rate limit: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_ret.routing_tag_ids, v_destination_rate_limit; + /*}dbg*/ + v_src_network:=switch20.detect_network(v_ret.src_prefix_routing); + v_ret.src_network_id=v_src_network.network_id; + v_ret.src_country_id=v_src_network.country_id; + + v_network:=switch20.detect_network(v_ret.dst_prefix_routing); + v_ret.dst_network_id=v_network.network_id; + v_ret.dst_country_id=v_network.country_id; + + IF v_rp.validate_dst_number_network AND v_ret.dst_network_id is null THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Network detection. DST network validation enabled and DST network was not found. Rejecting calls',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + v_ret.disconnect_code_id=8007; --No network detected for DST number + RETURN NEXT v_ret; + RETURN; + END IF; + + + IF v_rp.validate_dst_number_format AND NOT (v_routing_key ~ '^[0-9]+$') THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dst number format is not valid. DST number: %s',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key; + /*}dbg*/ + + v_ret.disconnect_code_id=8008; --Invalid number format + RETURN NEXT v_ret; + RETURN; + END IF; + + SELECT into v_destination d.*/*,switch.tracelog(d.*)*/ + FROM class4.destinations d + JOIN class4.rate_plan_groups rpg ON d.rate_group_id=rpg.rate_group_id + WHERE + prefix_range(prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between d.dst_number_min_length and d.dst_number_max_length + AND rpg.rateplan_id=v_customer_auth_normalized.rateplan_id + AND enabled + AND valid_from <= v_now + AND valid_till >= v_now + AND yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags, d.routing_tag_mode_id)>0 + ORDER BY length(prefix_range(prefix)) DESC, yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags) desc + limit 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. Destination not found',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=111; --Cant find destination prefix + RETURN NEXT v_ret; + RETURN; + END IF; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_destination, true); + /*}dbg*/ + + v_ret.destination_id:=v_destination.id; + v_ret.destination_prefix=v_destination.prefix; + v_ret.destination_initial_interval:=v_destination.initial_interval; + v_ret.destination_fee:=v_destination.connect_fee::varchar; + v_ret.destination_next_interval:=v_destination.next_interval; + v_ret.destination_rate_policy_id:=v_destination.rate_policy_id; + v_ret.destination_reverse_billing:=v_destination.reverse_billing; + IF v_destination.reject_calls THEN + v_ret.disconnect_code_id=112; --Rejected by destination + RETURN NEXT v_ret; + RETURN; + END IF; + + if v_destination.next_rate::float>v_destination_rate_limit then + v_ret.disconnect_code_id=8006; -- No destination with appropriate rate found + RETURN NEXT v_ret; + RETURN; + end if; + + select into v_rateplan * from class4.rateplans where id=v_customer_auth_normalized.rateplan_id; + if COALESCE(v_destination.profit_control_mode_id,v_rateplan.profit_control_mode_id)=2 then -- per call + v_rate_limit=v_destination.next_rate::float; + end if; + + + /* + FIND dialpeers logic. Queries must use prefix index for best performance + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. search start. Routing key: %. Rate limit: %. Routing tag: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_rate_limit, v_ret.routing_tag_ids; + /*}dbg*/ + CASE v_rp.sorting_id + WHEN'1' THEN -- LCR,Prio, ACD&ASR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + t_dp.next_rate as dp_next_rate, + t_dp.lcr_rate_multiplier AS dp_lcr_rate_multiplier, + t_dp.priority AS dp_priority, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_next_rate<=v_rate_limit + AND dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY dp_next_rate*dp_lcr_rate_multiplier, dp_priority DESC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN '2' THEN --LCR, no prio, No ACD&ASR control + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + -- (t_vendor_gateway.*)::class4.gateways as s1_vendor_gateway, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN '3' THEN --Prio, LCR, ACD&ASR control + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'4' THEN -- LCRD, Prio, ACD&ACR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + ((t_dp.next_rate - first_value(t_dp.next_rate) OVER(ORDER BY t_dp.next_rate ASC)) > v_rp.rate_delta_max)::INTEGER *(t_dp.next_rate + t_dp.priority) - t_dp.priority AS r2, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate <= v_rate_limit + and dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY r2 ASC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN'5' THEN -- Route test + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'6' THEN -- QD.Static,LCR,ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + left join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'7' THEN -- QD.Static, No ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + + ELSE + RAISE NOTICE 'BUG: unknown sorting_id'; + END CASE; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dialpeer search done',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=113; --No routes + RETURN NEXT v_ret; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DONE.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN; + END; + $_$; + + set search_path TO switch20; + SELECT * from switch20.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + +CREATE or replace FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_id smallint, i_remote_ip inet, i_remote_port integer, i_local_ip inet, i_local_port integer, i_from_dsp character varying, i_from_name character varying, i_from_domain character varying, i_from_port integer, i_to_name character varying, i_to_domain character varying, i_to_port integer, i_contact_name character varying, i_contact_domain character varying, i_contact_port integer, i_uri_name character varying, i_uri_domain character varying, i_auth_id integer, i_x_yeti_auth character varying, i_diversion character varying, i_x_orig_ip inet, i_x_orig_port integer, i_x_orig_protocol_id smallint, i_pai character varying, i_ppi character varying, i_privacy character varying, i_rpid character varying, i_rpid_privacy character varying) RETURNS SETOF switch19.callprofile_ty + LANGUAGE plpgsql SECURITY DEFINER ROWS 10 + AS $_$ + DECLARE + v_ret switch19.callprofile_ty; + i integer; + v_ip inet; + v_remote_ip inet; + v_remote_port INTEGER; + v_transport_protocol_id smallint; + v_customer_auth_normalized class4.customers_auth_normalized; + v_destination class4.destinations%rowtype; + v_dialpeer record; + v_rateplan class4.rateplans%rowtype; + v_dst_gw class4.gateways%rowtype; + v_orig_gw class4.gateways%rowtype; + v_rp class4.routing_plans%rowtype; + v_customer_allowtime real; + v_vendor_allowtime real; + v_sorting_id integer; + v_customer_acc integer; + v_route_found boolean:=false; + v_c_acc billing.accounts%rowtype; + v_v_acc billing.accounts%rowtype; + v_network sys.network_prefixes%rowtype; + v_src_network sys.network_prefixes%rowtype; + routedata record; + /*dbg{*/ + v_start timestamp; + v_end timestamp; + /*}dbg*/ + v_rate NUMERIC; + v_now timestamp; + v_x_yeti_auth varchar; + -- v_uri_domain varchar; + v_rate_limit float:='Infinity'::float; + v_destination_rate_limit float:='Infinity'::float; + v_test_vendor_id integer; + v_random float; + v_max_call_length integer; + v_routing_key varchar; + v_lnp_key varchar; + v_drop_call_if_lnp_fail boolean; + v_lnp_rule class4.routing_plan_lnp_rules%rowtype; + v_numberlist record; + v_numberlist_item record; + v_call_tags smallint[]:='{}'::smallint[]; + v_area_direction class4.routing_tag_detection_rules%rowtype; + v_numberlist_size integer; + v_lua_context switch19.lua_call_context; + + BEGIN + /*dbg{*/ + v_start:=now(); + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Execution start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + perform id from sys.load_balancers where signalling_ip=host(i_remote_ip)::varchar; + IF FOUND and i_x_orig_ip IS not NULL AND i_x_orig_port IS not NULL THEN + v_remote_ip:=i_x_orig_ip; + v_remote_port:=i_x_orig_port; + v_transport_protocol_id=i_x_orig_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from x-headers',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + else + v_remote_ip:=i_remote_ip; + v_remote_port:=i_remote_port; + v_transport_protocol_id:=i_protocol_id; + /*dbg{*/RAISE NOTICE '% ms -> Got originator address "%:%, proto: %" from switch leg info',EXTRACT(MILLISECOND from v_end-v_start), v_remote_ip,v_remote_port, v_transport_protocol_id;/*}dbg*/ + end if; + + v_now:=now(); + v_ret:=switch19.new_profile(); + v_ret.cache_time = 10; + + v_ret.diversion_in:=i_diversion; + v_ret.diversion_out:=i_diversion; -- FIXME + + v_ret.auth_orig_protocol_id =v_transport_protocol_id; + v_ret.auth_orig_ip = v_remote_ip; + v_ret.auth_orig_port = v_remote_port; + + v_ret.src_name_in:=i_from_dsp; + v_ret.src_name_out:=v_ret.src_name_in; + + v_ret.src_prefix_in:=i_from_name; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + v_ret.dst_prefix_in:=i_uri_name; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + v_ret.ruri_domain=i_uri_domain; + v_ret.from_domain=i_from_domain; + v_ret.to_domain=i_to_domain; + + v_ret.pai_in=i_pai; + v_ret.ppi_in=i_ppi; + v_ret.privacy_in=i_privacy; + v_ret.rpid_in=i_rpid; + v_ret.rpid_privacy_in=i_rpid_privacy; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. lookup started',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_x_yeti_auth:=COALESCE(i_x_yeti_auth,''); + -- v_uri_domain:=COALESCE(i_uri_domain,''); + + if i_auth_id is null then + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null, + ca.require_incoming_auth + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.require_incoming_auth then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Incoming auth required. Respond 401',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.aleg_auth_required=true; + RETURN NEXT v_ret; + RETURN; + end IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + else + SELECT into v_customer_auth_normalized ca.* + from class4.customers_auth_normalized ca + JOIN public.contractors c ON c.id=ca.customer_id + WHERE ca.enabled AND + ca.ip>>=v_remote_ip AND + prefix_range(ca.dst_prefix)@>prefix_range(v_ret.dst_prefix_in) AND + prefix_range(ca.src_prefix)@>prefix_range(v_ret.src_prefix_in) AND + (ca.pop_id=i_pop_id or ca.pop_id is null) and + COALESCE(ca.x_yeti_auth,'')=v_x_yeti_auth AND + COALESCE(nullif(ca.uri_domain,'')=i_uri_domain,true) AND + COALESCE(nullif(ca.to_domain,'')=i_to_domain,true) AND + COALESCE(nullif(ca.from_domain,'')=i_from_domain,true) AND + (ca.transport_protocol_id is null or ca.transport_protocol_id=v_transport_protocol_id) AND + length(v_ret.dst_prefix_in) between ca.dst_number_min_length and ca.dst_number_max_length and + length(v_ret.src_prefix_in) between ca.src_number_min_length and ca.src_number_max_length and + c.enabled and c.customer and + ca.require_incoming_auth and gateway_id = i_auth_id + ORDER BY + masklen(ca.ip) DESC, + ca.transport_protocol_id is null, + length(prefix_range(ca.dst_prefix)) DESC, + length(prefix_range(ca.src_prefix)) DESC, + ca.pop_id is null, + ca.uri_domain is null, + ca.to_domain is null, + ca.from_domain is null + LIMIT 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 110.Cant find customer or customer locked',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=110; --Cant find customer or customer locked + RETURN NEXT v_ret; + RETURN; + END IF; + if v_customer_auth_normalized.reject_calls then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. disconnection with 8004. Reject by customers auth',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8004; -- call rejected by authorization + + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_ret.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + SELECT INTO STRICT v_ret.customer_acc_external_id external_id FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + + RETURN NEXT v_ret; + RETURN; + end if; + end IF; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_customer_auth_normalized, true); + /*}dbg*/ + + -- redefine call SRC/DST numbers + + IF v_customer_auth_normalized.src_name_field_id=1 THEN /* default - from uri display name */ + v_ret.src_name_in:=i_from_dsp; + END IF; + v_ret.src_name_out:=v_ret.src_name_in; + + IF v_customer_auth_normalized.src_number_field_id=1 THEN /* default - from uri userpart */ + v_ret.src_prefix_in:=i_from_name; + ELSIF v_customer_auth_normalized.src_number_field_id=2 THEN /* From uri Display name */ + v_ret.src_prefix_in:=i_from_dsp; + END IF; + v_ret.src_prefix_out:=v_ret.src_prefix_in; + + IF v_customer_auth_normalized.dst_number_field_id=1 THEN /* default - RURI userpart*/ + v_ret.dst_prefix_in:=i_uri_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=2 THEN /* TO URI userpart */ + v_ret.dst_prefix_in:=i_to_name; + ELSIF v_customer_auth_normalized.dst_number_field_id=3 THEN /* Top-Most Diversion header userpart */ + v_ret.dst_prefix_in:=i_diversion; + END IF; + v_ret.dst_prefix_out:=v_ret.dst_prefix_in; + + + -- feel customer data ;-) + v_ret.dump_level_id:=v_customer_auth_normalized.dump_level_id; + v_ret.customer_auth_id:=v_customer_auth_normalized.customers_auth_id; + v_ret.customer_auth_external_id:=v_customer_auth_normalized.external_id; + + v_ret.customer_id:=v_customer_auth_normalized.customer_id; + select into strict v_ret.customer_external_id external_id from public.contractors where id=v_customer_auth_normalized.customer_id; + + v_ret.rateplan_id:=v_customer_auth_normalized.rateplan_id; + v_ret.routing_plan_id:=v_customer_auth_normalized.routing_plan_id; + v_ret.customer_acc_id:=v_customer_auth_normalized.account_id; + v_ret.orig_gw_id:=v_customer_auth_normalized.gateway_id; + + v_ret.radius_auth_profile_id=v_customer_auth_normalized.radius_auth_profile_id; + v_ret.aleg_radius_acc_profile_id=v_customer_auth_normalized.radius_accounting_profile_id; + v_ret.record_audio=v_customer_auth_normalized.enable_audio_recording; + + v_ret.customer_acc_check_balance=v_customer_auth_normalized.check_account_balance; + + SELECT INTO STRICT v_c_acc * FROM billing.accounts WHERE id=v_customer_auth_normalized.account_id; + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + v_destination_rate_limit=coalesce(v_c_acc.destination_rate_limit::float,'Infinity'::float); + + if v_customer_auth_normalized.check_account_balance AND v_c_acc.balance<=v_c_acc.min_balance then + v_ret.disconnect_code_id=8000; --No enough customer balance + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.customer_acc_external_id=v_c_acc.external_id; + v_ret.customer_acc_vat=v_c_acc.vat; + + SELECT into v_orig_gw * from class4.gateways WHERE id=v_customer_auth_normalized.gateway_id; + if not v_orig_gw.enabled then + v_ret.disconnect_code_id=8005; -- Origination gateway is disabled + RETURN NEXT v_ret; + RETURN; + end if; + + v_ret.resources:=''; + if v_customer_auth_normalized.capacity is not null then + v_ret.resources:=v_ret.resources||'3:'||v_customer_auth_normalized.customers_auth_id||':'||v_customer_auth_normalized.capacity::varchar||':1;'; + end if; + + if v_c_acc.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'1:'||v_c_acc.id::varchar||':'||v_c_acc.origination_capacity::varchar||':1;'; + end if; + + if v_c_acc.total_capacity is not null then + v_ret.resources:=v_ret.resources||'7:'||v_c_acc.id::varchar||':'||v_c_acc.total_capacity::varchar||':1;'; + end if; + + if v_orig_gw.origination_capacity is not null then + v_ret.resources:=v_ret.resources||'4:'||v_orig_gw.id::varchar||':'||v_orig_gw.origination_capacity::varchar||':1;'; + end if; + + -- Tag processing CA + v_call_tags=yeti_ext.tag_action(v_customer_auth_normalized.tag_action_id, v_call_tags, v_customer_auth_normalized.tag_action_value); + + /* + number rewriting _Before_ routing + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. Before rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand(v_ret.dst_prefix_out,v_customer_auth_normalized.dst_rewrite_rule,v_customer_auth_normalized.dst_rewrite_result); + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand(v_ret.src_prefix_out,v_customer_auth_normalized.src_rewrite_rule,v_customer_auth_normalized.src_rewrite_result); + v_ret.src_name_out=yeti_ext.regexp_replace_rand(v_ret.src_name_out,v_customer_auth_normalized.src_name_rewrite_rule,v_customer_auth_normalized.src_name_rewrite_result, true); + + -- if v_ret.radius_auth_profile_id is not null then + v_ret.src_number_radius:=i_from_name; + v_ret.dst_number_radius:=i_uri_name; + v_ret.src_number_radius=yeti_ext.regexp_replace_rand( + v_ret.src_number_radius, + v_customer_auth_normalized.src_number_radius_rewrite_rule, + v_customer_auth_normalized.src_number_radius_rewrite_result + ); + + v_ret.dst_number_radius=yeti_ext.regexp_replace_rand( + v_ret.dst_number_radius, + v_customer_auth_normalized.dst_number_radius_rewrite_rule, + v_customer_auth_normalized.dst_number_radius_rewrite_result + ); + v_ret.customer_auth_name=v_customer_auth_normalized."name"; + v_ret.customer_name=(select "name" from public.contractors where id=v_customer_auth_normalized.customer_id limit 1); + -- end if; +/* + if v_customer_auth_normalized.lua_script_id is not null then + v_lua_context.src_name_in = v_ret.src_name_in; + v_lua_context.src_number_in = v_ret.src_prefix_in; + v_lua_context.dst_number_in = v_ret.dst_prefix_in; + v_lua_context.src_name_out = v_ret.src_name_out; + v_lua_context.src_number_out = v_ret.src_prefix_out; + v_lua_context.dst_number_out = v_ret.dst_prefix_out; + -- v_lua_context.src_name_routing + -- v_lua_context.src_number_routing + -- v_lua_context.dst_number_routing + -- #arrays + -- v_lua_context.diversion_in + -- v_lua_context.diversion_routing + -- v_lua_context.diversion_out + v_lua_context = switch19.lua_exec(v_customer_auth_normalized.lua_script_id, v_lua_context); + v_ret.src_name_out = v_lua_context.src_name_out; + v_ret.src_prefix_out = v_lua_context.src_number_out; + v_ret.dst_prefix_out = v_lua_context.dst_number_out; + end if; +*/ + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> AUTH. After rewrite src_prefix: % , dst_prefix: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.src_prefix_out,v_ret.dst_prefix_out; + /*}dbg*/ + + ----- Numberlist processing------------------------------------------------------------------------------------------------------- + if v_customer_auth_normalized.dst_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist processing. Lookup by key: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.dst_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and ni.key=v_ret.dst_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * + from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.dst_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.dst_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.dst_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + -- drop by default + v_ret.disconnect_code_id=8001; --destination blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + if v_customer_auth_normalized.src_numberlist_id is not null then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist processing. Lookup by key: %s',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_prefix_out; + /*}dbg*/ + select into v_numberlist * from class4.numberlists where id=v_customer_auth_normalized.src_numberlist_id; + CASE v_numberlist.mode_id + when 1 then -- strict match + select into v_numberlist_item * from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and ni.key=v_ret.src_prefix_out limit 1; + when 2 then -- prefix match + select into v_numberlist_item * from class4.numberlist_items ni + where + ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id and + prefix_range(ni.key)@>prefix_range(v_ret.src_prefix_out) and + length(v_ret.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_numberlist_size count(*) from class4.numberlist_items where numberlist_id=v_customer_auth_normalized.src_numberlist_id; + select into v_numberlist_item * + from class4.numberlist_items ni + where ni.numberlist_id=v_customer_auth_normalized.src_numberlist_id order by ni.id OFFSET floor(random()*v_numberlist_size) limit 1; + end case; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. key found: %',EXTRACT(MILLISECOND from v_end-v_start), row_to_json(v_numberlist_item); + /*}dbg*/ + IF v_numberlist_item.action_id is not null and v_numberlist_item.action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by key action. Key: %',EXTRACT(MILLISECOND from v_end-v_start), v_numberlist_item.key; + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is not null and v_numberlist_item.action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist_item.src_rewrite_rule, + v_numberlist_item.src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist_item.dst_rewrite_rule, + v_numberlist_item.dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist_item.tag_action_id, v_call_tags, v_numberlist_item.tag_action_value); + -- pass call NOP. + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=1 then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Numberlist. Drop by default action',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8002; --source blacklisted + RETURN NEXT v_ret; + RETURN; + elsif v_numberlist_item.action_id is null and v_numberlist.default_action_id=2 then + v_ret.src_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.src_prefix_out, + v_numberlist.default_src_rewrite_rule, + v_numberlist.default_src_rewrite_result + ); + v_ret.dst_prefix_out=yeti_ext.regexp_replace_rand( + v_ret.dst_prefix_out, + v_numberlist.default_dst_rewrite_rule, + v_numberlist.default_dst_rewrite_result + ); + v_call_tags=yeti_ext.tag_action(v_numberlist.tag_action_id, v_call_tags, v_numberlist.tag_action_value); + -- pass by default + end if; + end if; + + -- setting numbers used for routing & billing + v_ret.src_prefix_routing=v_ret.src_prefix_out; + v_ret.dst_prefix_routing=v_ret.dst_prefix_out; + v_routing_key=v_ret.dst_prefix_out; + + -- Areas and Tag detection------------------------------------------- + v_ret.src_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.src_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> SRC Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.src_area_id; + /*}dbg*/ + + v_ret.dst_area_id:=( + select area_id from class4.area_prefixes where prefix_range(prefix)@>prefix_range(v_ret.dst_prefix_routing) + order by length(prefix_range(prefix)) desc limit 1 + ); + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST Area found: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.dst_area_id; + /*}dbg*/ + + + select into v_area_direction * from class4.routing_tag_detection_rules + where + (src_area_id is null OR src_area_id = v_ret.src_area_id) AND + (dst_area_id is null OR dst_area_id = v_ret.dst_area_id) AND + prefix_range(src_prefix) @> prefix_range(v_ret.src_prefix_routing) AND + prefix_range(dst_prefix) @> prefix_range(v_ret.dst_prefix_routing) AND + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id ) > 0 + order by + yeti_ext.tag_compare(routing_tag_ids, v_call_tags, routing_tag_mode_id) desc, + length(prefix_range(src_prefix)) desc, + length(prefix_range(dst_prefix)) desc, + src_area_id is null, + dst_area_id is null + limit 1; + if found then + v_call_tags=yeti_ext.tag_action(v_area_direction.tag_action_id, v_call_tags, v_area_direction.tag_action_value); + end if; + + v_ret.routing_tag_ids:=v_call_tags; + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing tag detected: %',EXTRACT(MILLISECOND from v_end-v_start), v_ret.routing_tag_ids; + /*}dbg*/ + ---------------------------------------------------------------------- + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + + v_routing_key=v_ret.dst_prefix_routing; + SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; + if v_rp.sorting_id=5 then -- route testing + v_test_vendor_id=regexp_replace(v_routing_key,'(.*)\*(.*)','\1')::integer; + v_routing_key=regexp_replace(v_routing_key,'(.*)\*(.*)','\2'); + v_ret.dst_prefix_out=v_routing_key; + v_ret.dst_prefix_routing=v_routing_key; + end if; + + if v_rp.use_lnp then + select into v_lnp_rule rules.* + from class4.routing_plan_lnp_rules rules + WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id + order by length(prefix_range(rules.dst_prefix)) limit 1; + if found then + v_ret.lnp_database_id=v_lnp_rule.database_id; + v_lnp_key=v_ret.dst_prefix_routing; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Need LNP lookup, LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + v_lnp_key=yeti_ext.regexp_replace_rand(v_lnp_key,v_lnp_rule.req_dst_rewrite_rule,v_lnp_rule.req_dst_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP key translation. LNP key: %',EXTRACT(MILLISECOND from v_end-v_start),v_lnp_key; + /*}dbg*/ + -- try cache + select into v_ret.lrn lrn from class4.lnp_cache where dst=v_lnp_key AND database_id=v_lnp_rule.database_id and expires_at>v_now; + if found then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Data found in cache, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from cache + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + else + v_ret.lrn=switch19.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); + if v_ret.lrn is null then -- fail + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + if v_drop_call_if_lnp_fail then + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=8003; --No response from LNP DB + RETURN NEXT v_ret; + RETURN; + end if; + else + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Success, lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + -- TRANSLATING response from LNP DB + v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; + /*}dbg*/ + v_routing_key=v_ret.lrn; + end if; + end if; + end if; + end if; + + + + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. search start. Routing key: %. Routing tags: %, Rate limit: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_ret.routing_tag_ids, v_destination_rate_limit; + /*}dbg*/ + v_src_network:=switch19.detect_network(v_ret.src_prefix_routing); + v_ret.src_network_id=v_src_network.network_id; + v_ret.src_country_id=v_src_network.country_id; + + v_network:=switch19.detect_network(v_ret.dst_prefix_routing); + v_ret.dst_network_id=v_network.network_id; + v_ret.dst_country_id=v_network.country_id; + + IF v_rp.validate_dst_number_network AND v_ret.dst_network_id is null THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Network detection. DST network validation enabled and DST network was not found. Rejecting calls',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + + v_ret.disconnect_code_id=8007; --No network detected for DST number + RETURN NEXT v_ret; + RETURN; + END IF; + + + IF v_rp.validate_dst_number_format AND NOT (v_routing_key ~ '^[0-9]+$') THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dst number format is not valid. DST number: %s',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key; + /*}dbg*/ + + v_ret.disconnect_code_id=8008; --Invalid number format + RETURN NEXT v_ret; + RETURN; + END IF; + + SELECT into v_destination d.*/*,switch.tracelog(d.*)*/ + FROM class4.destinations d + JOIN class4.rate_plan_groups rpg ON d.rate_group_id=rpg.rate_group_id + WHERE + prefix_range(prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between d.dst_number_min_length and d.dst_number_max_length + AND rpg.rateplan_id=v_customer_auth_normalized.rateplan_id + AND enabled + AND valid_from <= v_now + AND valid_till >= v_now + AND yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags, d.routing_tag_mode_id)>0 + ORDER BY length(prefix_range(prefix)) DESC, yeti_ext.tag_compare(d.routing_tag_ids, v_call_tags) desc + limit 1; + IF NOT FOUND THEN + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. Destination not found',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=111; --Cant find destination prefix + RETURN NEXT v_ret; + RETURN; + END IF; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DST. found: %',EXTRACT(MILLISECOND from v_end-v_start),row_to_json(v_destination, true); + /*}dbg*/ + + v_ret.destination_id:=v_destination.id; + v_ret.destination_prefix=v_destination.prefix; + v_ret.destination_initial_interval:=v_destination.initial_interval; + v_ret.destination_fee:=v_destination.connect_fee::varchar; + v_ret.destination_next_interval:=v_destination.next_interval; + v_ret.destination_rate_policy_id:=v_destination.rate_policy_id; + v_ret.destination_reverse_billing:=v_destination.reverse_billing; + IF v_destination.reject_calls THEN + v_ret.disconnect_code_id=112; --Rejected by destination + RETURN NEXT v_ret; + RETURN; + END IF; + + if v_destination.next_rate::float>v_destination_rate_limit then + v_ret.disconnect_code_id=8006; -- No destination with appropriate rate found + RETURN NEXT v_ret; + RETURN; + end if; + + select into v_rateplan * from class4.rateplans where id=v_customer_auth_normalized.rateplan_id; + if COALESCE(v_destination.profit_control_mode_id,v_rateplan.profit_control_mode_id)=2 then -- per call + v_rate_limit=v_destination.next_rate::float; + end if; + + + /* + FIND dialpeers logic. Queries must use prefix index for best performance + */ + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DP. search start. Routing key: %. Rate limit: %. Routing tag: %',EXTRACT(MILLISECOND from v_end-v_start), v_routing_key, v_rate_limit, v_ret.routing_tag_ids; + /*}dbg*/ + CASE v_rp.sorting_id + WHEN'1' THEN -- LCR,Prio, ACD&ASR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + t_dp.next_rate as dp_next_rate, + t_dp.lcr_rate_multiplier AS dp_lcr_rate_multiplier, + t_dp.priority AS dp_priority, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_next_rate<=v_rate_limit + AND dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY dp_next_rate*dp_lcr_rate_multiplier, dp_priority DESC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN '2' THEN --LCR, no prio, No ACD&ASR control + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + -- (t_vendor_gateway.*)::class4.gateways as s1_vendor_gateway, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + AND dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN '3' THEN --Prio, LCR, ACD&ASR control + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'4' THEN -- LCRD, Prio, ACD&ACR control + FOR routedata IN ( + WITH step1 AS( + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + ((t_dp.next_rate - first_value(t_dp.next_rate) OVER(ORDER BY t_dp.next_rate ASC)) > v_rp.rate_delta_max)::INTEGER *(t_dp.next_rate + t_dp.priority) - t_dp.priority AS r2, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id = t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + from step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_next_rate <= v_rate_limit + and dp_enabled + and not dp_locked --ACD&ASR control for DP + ORDER BY r2 ASC + LIMIT v_rp.max_rerouting_attempts + ) LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + end LOOP; + WHEN'5' THEN -- Route test + FOR routedata IN ( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and dp_enabled + and dp_next_rate<=v_rate_limit + ORDER BY dp_metric_priority DESC, dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'6' THEN -- QD.Static,LCR,ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.locked as dp_locked, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + left join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + and not dp_locked + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + WHEN'7' THEN -- QD.Static, No ACD&ACR control + v_random:=random(); + FOR routedata in( + WITH step1 AS( -- filtering + SELECT + (t_dp.*)::class4.dialpeers as s1_dialpeer, + (t_vendor_account.*)::billing.accounts as s1_vendor_account, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY + length(prefix_range(t_dp.prefix)) desc, + yeti_ext.tag_compare(t_dp.routing_tag_ids, v_call_tags, t_dp.routing_tag_mode_id) desc, + t_dp.exclusive_route desc -- in case when we have two identical prefixes with different exclusive flag value, we should lift up exclusive route, otherwise it will be filtered at WHERE r=1 and exclusive_rank=1 + ) as r, + rank() OVER ( + ORDER BY t_dp.exclusive_route desc -- force top rank for exclusive route + ) as exclusive_rank, + rank() OVER ( + PARTITION BY t_dp.vendor_id, t_dp.routeset_discriminator_id + ORDER BY length(prefix_range(coalesce(rpsr.prefix,''))) desc + ) as r2, + t_dp.priority as dp_metric_priority, + t_dp.next_rate*t_dp.lcr_rate_multiplier as dp_metric, + t_dp.next_rate as dp_next_rate, + t_dp.enabled as dp_enabled, + t_dp.force_hit_rate as dp_force_hit_rate, + rpsr.priority as rpsr_priority, + rpsr.weight as rpsr_weight + FROM class4.dialpeers t_dp + JOIN billing.accounts t_vendor_account ON t_dp.account_id=t_vendor_account.id + join public.contractors t_vendor on t_dp.vendor_id=t_vendor.id + JOIN class4.routing_plan_groups t_rpg ON t_dp.routing_group_id=t_rpg.routing_group_id + join class4.routing_plan_static_routes rpsr + ON rpsr.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and rpsr.vendor_id=t_dp.vendor_id + AND prefix_range(rpsr.prefix)@>prefix_range(v_routing_key) + WHERE + prefix_range(t_dp.prefix)@>prefix_range(v_routing_key) + AND length(v_routing_key) between t_dp.dst_number_min_length and t_dp.dst_number_max_length + AND t_rpg.routing_plan_id=v_customer_auth_normalized.routing_plan_id + and t_dp.valid_from<=v_now + and t_dp.valid_till>=v_now + AND t_vendor_account.balance 0 + ) + SELECT s1_dialpeer as s2_dialpeer, + s1_vendor_account as s2_vendor_account + FROM step1 + WHERE + r=1 + and exclusive_rank=1 + and r2=1 + and dp_next_rate<=v_rate_limit + and dp_enabled + ORDER BY + coalesce(v_random<=dp_force_hit_rate,false) desc, + rpsr_priority, + yeti_ext.rank_dns_srv(rpsr_weight) over ( partition by rpsr_priority order by rpsr_weight), + dp_metric + LIMIT v_rp.max_rerouting_attempts + )LOOP + RETURN QUERY + /*rel{*/SELECT * from process_dp_release(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}rel*/ + /*dbg{*/SELECT * from process_dp_debug(v_ret,v_destination,routedata.s2_dialpeer,v_c_acc,v_orig_gw,routedata.s2_vendor_account,i_pop_id,v_customer_auth_normalized.send_billing_information,v_max_call_length);/*}dbg*/ + END LOOP; + + ELSE + RAISE NOTICE 'BUG: unknown sorting_id'; + END CASE; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> Dialpeer search done',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + v_ret.disconnect_code_id=113; --No routes + RETURN NEXT v_ret; + /*dbg{*/ + v_end:=clock_timestamp(); + RAISE NOTICE '% ms -> DONE.',EXTRACT(MILLISECOND from v_end-v_start); + /*}dbg*/ + RETURN; + END; + $_$; + + set search_path TO switch19; + SELECT * from switch19.preprocess_all(); + set search_path TO gui, public, switch, billing, class4, runtime_stats, sys, logs, data_import; + + } + end + +end diff --git a/db/seeds/main/class4.sql b/db/seeds/main/class4.sql index 47999f879..159dd9950 100644 --- a/db/seeds/main/class4.sql +++ b/db/seeds/main/class4.sql @@ -982,3 +982,6 @@ insert into class4.customers_auth_src_name_fields(id,name) values(1,'From header insert into class4.customers_auth_dst_number_fields(id,name) values(1,'R-URI userpart'); insert into class4.customers_auth_dst_number_fields(id,name) values(2,'To URI userpart'); insert into class4.customers_auth_dst_number_fields(id,name) values(3,'Top Diversion header userpart'); + +insert into class4.lnp_databases_30x_redirect_formats(id, name) values (1,'Contact URI username rn parameter'); +insert into class4.lnp_databases_30x_redirect_formats(id, name) values (2,'Contact URI username'); diff --git a/db/structure.sql b/db/structure.sql index a93ca384d..16d1c3856 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1780,14 +1780,16 @@ CREATE FUNCTION lnp.cache_lnp_data(i_database_id smallint, i_dst character varyi v_ttl integer; v_expire timestamptz; BEGIN - select into v_ttl lnp_cache_ttl from sys.guiconfig; - v_expire=now()+v_ttl*'1 minute'::interval; - begin - insert into class4.lnp_cache(dst,lrn,created_at,updated_at,expires_at,database_id,data,tag) values( i_dst, i_lrn, now(),now(),v_expire,i_database_id,i_data,i_tag); - Exception - when unique_violation then - update class4.lnp_cache set lrn=i_lrn, updated_at=now(), expires_at=v_expire, data=i_data, tag=i_tag WHERE dst=i_dst and database_id=i_database_id; - end; + select into v_ttl cache_ttl from class4.lnp_databases where id=i_database_id; + if v_ttl is not null and v_ttl > 0 then + v_expire=now()+v_ttl*'1 seconds'::interval; + begin + insert into class4.lnp_cache(dst,lrn,created_at,updated_at,expires_at,database_id,data,tag) values( i_dst, i_lrn, now(),now(),v_expire,i_database_id,i_data,i_tag); + exception + when unique_violation then + update class4.lnp_cache set lrn=i_lrn, updated_at=now(), expires_at=v_expire, data=i_data, tag=i_tag WHERE dst=i_dst and database_id=i_database_id; + end; + end if; END; $$; @@ -11232,7 +11234,6 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -11779,7 +11780,7 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -11794,7 +11795,7 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -11821,6 +11822,11 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch19.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail @@ -11828,7 +11834,7 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then /*dbg{*/ v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); @@ -11849,6 +11855,11 @@ CREATE FUNCTION switch19.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -12401,7 +12412,6 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -12948,7 +12958,7 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -12963,7 +12973,7 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -12990,6 +13000,11 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch19.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail @@ -12997,7 +13012,7 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then /*dbg{*/ v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); @@ -13018,6 +13033,11 @@ CREATE FUNCTION switch19.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -13567,7 +13587,6 @@ CREATE FUNCTION switch19.route_release(i_node_id integer, i_pop_id integer, i_pr v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -14047,7 +14066,7 @@ CREATE FUNCTION switch19.route_release(i_node_id integer, i_pop_id integer, i_pr - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -14062,7 +14081,7 @@ CREATE FUNCTION switch19.route_release(i_node_id integer, i_pop_id integer, i_pr select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -14077,11 +14096,16 @@ CREATE FUNCTION switch19.route_release(i_node_id integer, i_pop_id integer, i_pr v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch19.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then v_ret.disconnect_code_id=8003; --No response from LNP DB RETURN NEXT v_ret; @@ -14093,6 +14117,11 @@ CREATE FUNCTION switch19.route_release(i_node_id integer, i_pop_id integer, i_pr v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -17577,7 +17606,6 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -18126,7 +18154,7 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -18141,7 +18169,7 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -18168,6 +18196,11 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch20.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail @@ -18175,7 +18208,7 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then /*dbg{*/ v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); @@ -18196,6 +18229,11 @@ CREATE FUNCTION switch20.route(i_node_id integer, i_pop_id integer, i_protocol_i RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -18748,7 +18786,6 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -19297,7 +19334,7 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> Routing plan search start',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -19312,7 +19349,7 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -19339,6 +19376,11 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch20.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail @@ -19346,7 +19388,7 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Query failed',EXTRACT(MILLISECOND from v_end-v_start); /*}dbg*/ - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then /*dbg{*/ v_end:=clock_timestamp(); RAISE NOTICE '% ms -> LNP. Dropping call',EXTRACT(MILLISECOND from v_end-v_start); @@ -19367,6 +19409,11 @@ CREATE FUNCTION switch20.route_debug(i_node_id integer, i_pop_id integer, i_prot RAISE NOTICE '% ms -> LNP. Translation. lrn: %',EXTRACT(MILLISECOND from v_end-v_start),v_ret.lrn; /*}dbg*/ v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -19916,7 +19963,6 @@ CREATE FUNCTION switch20.route_release(i_node_id integer, i_pop_id integer, i_pr v_max_call_length integer; v_routing_key varchar; v_lnp_key varchar; - v_drop_call_if_lnp_fail boolean; v_lnp_rule class4.routing_plan_lnp_rules%rowtype; v_numberlist record; v_numberlist_item record; @@ -20398,7 +20444,7 @@ CREATE FUNCTION switch20.route_release(i_node_id integer, i_pop_id integer, i_pr - select into v_max_call_length,v_drop_call_if_lnp_fail max_call_duration,drop_call_if_lnp_fail from sys.guiconfig limit 1; + select into v_max_call_length max_call_duration from sys.guiconfig limit 1; v_routing_key=v_ret.dst_prefix_routing; SELECT INTO v_rp * from class4.routing_plans WHERE id=v_customer_auth_normalized.routing_plan_id; @@ -20413,7 +20459,7 @@ CREATE FUNCTION switch20.route_release(i_node_id integer, i_pop_id integer, i_pr select into v_lnp_rule rules.* from class4.routing_plan_lnp_rules rules WHERE prefix_range(rules.dst_prefix)@>prefix_range(v_ret.dst_prefix_routing) and rules.routing_plan_id=v_rp.id - order by length(prefix_range(rules.dst_prefix)) limit 1; + order by length(prefix_range(rules.dst_prefix)) desc limit 1; if found then v_ret.lnp_database_id=v_lnp_rule.database_id; v_lnp_key=v_ret.dst_prefix_routing; @@ -20428,11 +20474,16 @@ CREATE FUNCTION switch20.route_release(i_node_id integer, i_pop_id integer, i_pr v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; else v_ret.lrn=switch20.lnp_resolve(v_ret.lnp_database_id,v_lnp_key); if v_ret.lrn is null then -- fail - if v_drop_call_if_lnp_fail then + if v_lnp_rule.drop_call_on_error then v_ret.disconnect_code_id=8003; --No response from LNP DB RETURN NEXT v_ret; @@ -20444,6 +20495,11 @@ CREATE FUNCTION switch20.route_release(i_node_id integer, i_pop_id integer, i_pr v_ret.lrn=yeti_ext.regexp_replace_rand(v_ret.lrn,v_lnp_rule.lrn_rewrite_rule,v_lnp_rule.lrn_rewrite_result); v_routing_key=v_ret.lrn; + if v_lnp_rule.rewrite_call_destination then + v_ret.dst_prefix_out=v_ret.lrn; + v_ret.dst_prefix_routing=v_ret.lrn; + -- TODO shouldn't we perform tag detection again there? Call destination changed. + end if; end if; end if; end if; @@ -22308,7 +22364,7 @@ CREATE TABLE class4.lnp_cache ( lrn character varying NOT NULL, created_at timestamp with time zone, updated_at timestamp with time zone, - expires_at timestamp with time zone, + expires_at timestamp with time zone DEFAULT now() NOT NULL, database_id smallint, data character varying, tag character varying @@ -22343,7 +22399,8 @@ CREATE TABLE class4.lnp_databases ( name character varying NOT NULL, created_at timestamp with time zone, database_type character varying, - database_id smallint NOT NULL + database_id smallint NOT NULL, + cache_ttl integer DEFAULT 10800 NOT NULL ); @@ -22362,7 +22419,18 @@ CREATE TABLE class4.lnp_databases_30x_redirect ( id smallint NOT NULL, host character varying NOT NULL, port integer, - timeout smallint DEFAULT 300 NOT NULL + timeout smallint DEFAULT 300 NOT NULL, + format_id smallint DEFAULT 1 NOT NULL +); + + +-- +-- Name: lnp_databases_30x_redirect_formats; Type: TABLE; Schema: class4; Owner: - +-- + +CREATE TABLE class4.lnp_databases_30x_redirect_formats ( + id smallint NOT NULL, + name character varying NOT NULL ); @@ -22395,8 +22463,7 @@ CREATE TABLE class4.lnp_databases_alcazar ( host character varying NOT NULL, port integer, timeout smallint DEFAULT 300 NOT NULL, - key character varying NOT NULL, - database_id integer + key character varying NOT NULL ); @@ -22426,7 +22493,6 @@ ALTER SEQUENCE class4.lnp_databases_alcazar_id_seq OWNED BY class4.lnp_databases CREATE TABLE class4.lnp_databases_coure_anq ( id smallint NOT NULL, - database_id integer, base_url character varying NOT NULL, timeout smallint DEFAULT 300 NOT NULL, username character varying NOT NULL, @@ -23049,7 +23115,9 @@ CREATE TABLE class4.routing_plan_lnp_rules ( lrn_rewrite_rule character varying, lrn_rewrite_result character varying, req_dst_rewrite_rule character varying, - req_dst_rewrite_result character varying + req_dst_rewrite_result character varying, + drop_call_on_error boolean DEFAULT false NOT NULL, + rewrite_call_destination boolean DEFAULT false NOT NULL ); @@ -27300,6 +27368,22 @@ ALTER TABLE ONLY class4.lnp_cache ADD CONSTRAINT lnp_cache_pkey PRIMARY KEY (id); +-- +-- Name: lnp_databases_30x_redirect_formats lnp_databases_30x_redirect_formats_name_key; Type: CONSTRAINT; Schema: class4; Owner: - +-- + +ALTER TABLE ONLY class4.lnp_databases_30x_redirect_formats + ADD CONSTRAINT lnp_databases_30x_redirect_formats_name_key UNIQUE (name); + + +-- +-- Name: lnp_databases_30x_redirect_formats lnp_databases_30x_redirect_formats_pkey; Type: CONSTRAINT; Schema: class4; Owner: - +-- + +ALTER TABLE ONLY class4.lnp_databases_30x_redirect_formats + ADD CONSTRAINT lnp_databases_30x_redirect_formats_pkey PRIMARY KEY (id); + + -- -- Name: lnp_databases_30x_redirect lnp_databases_30x_redirect_pkey; Type: CONSTRAINT; Schema: class4; Owner: - -- @@ -29491,6 +29575,14 @@ ALTER TABLE ONLY class4.lnp_cache ADD CONSTRAINT lnp_cache_database_id_fkey FOREIGN KEY (database_id) REFERENCES class4.lnp_databases(id); +-- +-- Name: lnp_databases_30x_redirect lnp_databases_30x_redirect_format_id_fkey; Type: FK CONSTRAINT; Schema: class4; Owner: - +-- + +ALTER TABLE ONLY class4.lnp_databases_30x_redirect + ADD CONSTRAINT lnp_databases_30x_redirect_format_id_fkey FOREIGN KEY (format_id) REFERENCES class4.lnp_databases_30x_redirect_formats(id); + + -- -- Name: numberlist_items numberlist_items_action_id_fkey; Type: FK CONSTRAINT; Schema: class4; Owner: - -- @@ -29967,6 +30059,7 @@ INSERT INTO "public"."schema_migrations" (version) VALUES ('20211004152514'), ('20211005184247'), ('20211130113417'), -('20211130142405'); +('20211130142405'), +('20211217132047'); diff --git a/spec/factories/lnp/lnp_databases.rb b/spec/factories/lnp/lnp_databases.rb index c9ff9cff5..7fe1227ad 100644 --- a/spec/factories/lnp/lnp_databases.rb +++ b/spec/factories/lnp/lnp_databases.rb @@ -14,6 +14,7 @@ FactoryBot.define do factory :lnp_database, class: Lnp::Database do sequence(:name) { |n| "LNP Database #{n}" } + cache_ttl { 120 } trait :thinq do database_attributes do @@ -26,5 +27,51 @@ } end end + + trait :sip_redirect do + database_attributes do + { + type: Lnp::Database::CONST::TYPE_SIP_REDIRECT, + host: 'sip.example.com', + port: 6050, + timeout: 300, + format_id: 1 + } + end + end + + trait :csv do + database_attributes do + { + type: Lnp::Database::CONST::TYPE_CSV, + csv_file_path: '/tmp/lnp.csv' + } + end + end + + trait :alcazar do + database_attributes do + { + type: Lnp::Database::CONST::TYPE_ALCAZAR, + host: 'rspec.example.com', + port: 1239, + timeout: 600, + key: 'lnp-key' + } + end + end + + trait :coure_anq do + database_attributes do + { + type: Lnp::Database::CONST::TYPE_COURE_ANQ, + base_url: 'http://lnp.rspec.example.com/api', + username: 'rspec.test.name', + password: 'rspec.test.password', + country_code: 'UA', + operators_map: '{"operator1": "tag1", "operator2": "tag2"}' + } + end + end end end diff --git a/spec/factories/routing/lnp_cache.rb b/spec/factories/routing/lnp_cache.rb index 1e6d2eec7..8f09037d9 100644 --- a/spec/factories/routing/lnp_cache.rb +++ b/spec/factories/routing/lnp_cache.rb @@ -19,5 +19,6 @@ factory :lnp_cache, class: Lnp::Cache do dst { 'dst' } lrn { 'lrn' } + expires_at { Time.now.utc + 200_000 } end end diff --git a/spec/features/routing/routing_plan_lnp_rules/new_routing_plan_lnp_rule_spec.rb b/spec/features/routing/routing_plan_lnp_rules/new_routing_plan_lnp_rule_spec.rb index fe53033bf..c3d3fe314 100644 --- a/spec/features/routing/routing_plan_lnp_rules/new_routing_plan_lnp_rule_spec.rb +++ b/spec/features/routing/routing_plan_lnp_rules/new_routing_plan_lnp_rule_spec.rb @@ -15,8 +15,8 @@ FactoryBot.create(:lnp_database, :thinq) visit new_lnp_routing_plan_lnp_rule_path - aa_form.select_value 'Routing plan', routing_plan.name - aa_form.select_value 'Database', lnp_database.name + aa_form.select_chosen 'Routing plan', routing_plan.name + aa_form.select_chosen 'Database', lnp_database.name end it 'creates record' do @@ -30,7 +30,9 @@ req_dst_rewrite_rule: '', req_dst_rewrite_result: '', lrn_rewrite_rule: '', - lrn_rewrite_result: '' + lrn_rewrite_result: '', + drop_call_on_error: false, + rewrite_call_destination: false ) end diff --git a/spec/models/lnp/database_sip_redirect_spec.rb b/spec/models/lnp/database_sip_redirect_spec.rb index dbcdc587c..363d3ae2a 100644 --- a/spec/models/lnp/database_sip_redirect_spec.rb +++ b/spec/models/lnp/database_sip_redirect_spec.rb @@ -4,10 +4,15 @@ # # Table name: class4.lnp_databases_30x_redirect # -# id :integer(2) not null, primary key -# host :string not null -# port :integer(4) -# timeout :integer(2) default(300), not null +# id :integer(2) not null, primary key +# host :string not null +# port :integer(4) +# timeout :integer(2) default(300), not null +# format_id :integer(2) default(1), not null +# +# Foreign Keys +# +# lnp_databases_30x_redirect_format_id_fkey (format_id => lnp_databases_30x_redirect_formats.id) # RSpec.describe Lnp::DatabaseSipRedirect, type: :model do diff --git a/spec/models/lnp/database_spec.rb b/spec/models/lnp/database_spec.rb index fd0ac06e5..661968cfc 100644 --- a/spec/models/lnp/database_spec.rb +++ b/spec/models/lnp/database_spec.rb @@ -5,6 +5,7 @@ # Table name: class4.lnp_databases # # id :integer(2) not null, primary key +# cache_ttl :integer(4) default(10800), not null # database_type(One of Lnp::DatabaseThinq, Lnp::DatabaseSipRedirect, Lnp::DatabaseCsv) :string # name :string not null # created_at :datetime diff --git a/spec/sql/lnp/cache_lnp_data_spec.rb b/spec/sql/lnp/cache_lnp_data_spec.rb new file mode 100644 index 000000000..1bfdbf5bc --- /dev/null +++ b/spec/sql/lnp/cache_lnp_data_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe 'lnp.cache_lnp_data' do + subject do + yeti_select_all(sql, *sql_params) + end + + let(:sql) { 'SELECT * FROM lnp.cache_lnp_data(?::smallint,?::varchar,?::varchar,?::varchar,?::varchar)' } + let(:sql_params) do + [db.id, dst, lrn, tag, data] + end + + let!(:dst) { '1111-dst' } + let!(:lrn) { 'lrn-2222' } + let!(:tag) { 'lrn-tag' } + let!(:data) { 'lrn-data' } + + context 'no caching' do + let! (:db) { create(:lnp_database, :thinq, cache_ttl: 0) } + + it 'no caching' do + expect { subject }.not_to change { Lnp::Cache.count } + end + end + + context 'caching' do + let! (:db) { create(:lnp_database, :thinq, cache_ttl: 600) } + + it 'caching' do + expect { subject }.to change { Lnp::Cache.count }.by(1) + end + end +end diff --git a/spec/sql/lnp/load_lnp_databases_spec.rb b/spec/sql/lnp/load_lnp_databases_spec.rb new file mode 100644 index 000000000..ae9141adb --- /dev/null +++ b/spec/sql/lnp/load_lnp_databases_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +RSpec.describe 'lnp.load_lnp_databases' do + subject do + yeti_select_all(sql) + end + + let(:sql) { 'SELECT * FROM lnp.load_lnp_databases()' } + + let!(:lnp_databases) do + [ + create(:lnp_database, :thinq), + create(:lnp_database, :sip_redirect), + create(:lnp_database, :csv), + create(:lnp_database, :alcazar), + create(:lnp_database, :coure_anq) + ] + end + + it 'responds with correct rows' do + expect(subject).to match_array( + lnp_databases.map do |db| + { + id: db.id, + name: db.name, + database_type: db.database_type, + parameters: db.database.to_json.to_s + } + end + ) + end +end diff --git a/spec/sql/switch19/route_spec.rb b/spec/sql/switch19/route_spec.rb new file mode 100644 index 000000000..855e8ecc5 --- /dev/null +++ b/spec/sql/switch19/route_spec.rb @@ -0,0 +1,364 @@ +# frozen_string_literal: true + +RSpec.describe '#routing logic switch19' do + subject do + CallSql::Yeti.select_all_serialized( + "SELECT * from switch19.route_debug( + ?::integer, + ?::integer, + ?::smallint, + ?::inet, + ?::integer, + ?::inet, + ?::integer, + ?::varchar, + ?::varchar, + ?::varchar, + ?::integer, + ?::varchar, + ?::varchar, + ?::integer, + ?::varchar, + ?::varchar, + ?::integer, + ?::varchar, + ?::varchar, + ?::integer, /* i_auth_id */ + ?::varchar, + ?::varchar, + ?::inet, + ?::integer, + ?::smallint, + ?::varchar, + ?::varchar, + ?::varchar, + ?::varchar, + ?::varchar + )", + node_id, + pop_id, + protocol_id, + remote_ip, + remote_port, + local_ip, + local_port, + from_dsp, + from_name, + from_domain, + from_port, + to_name, + to_domain, + to_port, + contact_name, + contact_domain, + contact_port, + uri_name, + uri_domain, + auth_id, + x_yeti_auth, + diversion, + x_orig_ip, + x_orig_port, + x_orig_protocol_id, + pai, + ppi, + privacy, + rpid, + rpid_privacy + ) + end + + let(:node_id) { 1 } + let(:pop_id) { 12 } + let(:protocol_id) { 1 } + let(:remote_ip) { '1.1.1.1' } + let(:remote_port) { 5060 } + let(:local_ip) { '2.2.2.2' } + let(:local_port) { 5060 } + let(:from_dsp) { 'from display name' } + let(:from_name) { 'from_username' } + let(:from_domain) { 'from-domain' } + let(:from_port) { 5060 } + let(:to_name) { 'to_username' } + let(:to_domain) { 'to-domain' } + let(:to_port) { 5060 } + let(:contact_name) { 'contact-username' } + let(:contact_domain) { 'contact-domain' } + let(:contact_port) { 5060 } + let(:uri_name) { 'uri-name' } + let(:uri_domain) { 'uri-domain' } + let(:auth_id) { nil } + let(:identity) { '[]' } + let(:x_yeti_auth) { nil } + let(:diversion) { 'test' } + let(:x_orig_ip) { '3.3.3.3' } + let(:x_orig_port) { 6050 } + let(:x_orig_protocol_id) { 2 } + let(:pai) { 'pai' } + let(:ppi) { 'ppi' } + let(:privacy) { 'privacy' } + let(:rpid) { 'rpid' } + let(:rpid_privacy) { 'rpid-privacy' } + + context 'Use X-SRC-IP if originator is trusted load balancer' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + + FactoryBot.create(:customers_auth, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + it 'return 404 ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + end + end + + context 'Use remote IP if originator is not trusted load balancer' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '5.5.5.5') + + FactoryBot.create(:customers_auth, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + it 'return 404 ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be_nil + expect(subject.first[:disconnect_code_id]).to eq(110) # #Cant find customer or customer locked + end + end + + context 'Authentification' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + @ca = FactoryBot.create(:customers_auth, :with_incoming_auth, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + context 'No auth data' do + it 'Reject with 401' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be_nil + expect(subject.first[:aleg_auth_required]).to eq(true) + end + end + + context 'Authorized' do + let(:auth_id) { @ca.gateway_id } + + it 'Pass auth ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to eq(@ca.id) + expect(subject.first[:aleg_auth_required]).to be_nil + expect(subject.first[:disconnect_code_id]).to eq(8000) # No enough customer balance + end + end + end + + context 'Authentification+Reject' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + @ca = FactoryBot.create(:customers_auth, :with_incoming_auth, :with_reject, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + context 'No auth data' do + it 'Reject with 401' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be_nil + expect(subject.first[:aleg_auth_required]).to eq(true) + end + end + + context 'Authorized' do + let(:auth_id) { CustomersAuth.take!.gateway_id } + + it 'Pass auth ' do + expect(subject.size).to eq(1) + expect(subject.first[:aleg_auth_required]).to be_nil + expect(subject.first[:disconnect_code_id]).to eq(8004) # Reject by customer auth + + expect(subject.first[:customer_auth_id]).to eq(@ca.id) + expect(subject.first[:customer_auth_external_id]).to eq(@ca.external_id) + expect(subject.first[:customer_id]).to eq(@ca.customer_id) + expect(subject.first[:customer_external_id]).to eq(@ca.customer.external_id) + expect(subject.first[:customer_acc_id]).to eq(@ca.account_id) + expect(subject.first[:customer_acc_external_id]).to eq(@ca.account.external_id) + expect(subject.first[:rateplan_id]).to eq(@ca.rateplan_id) + expect(subject.first[:routing_plan_id]).to eq(@ca.routing_plan_id) + end + end + end + + context 'Reject calls without Authentification' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + @ca = FactoryBot.create(:customers_auth, :with_reject, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + it 'Reject ' do + expect(subject.size).to eq(1) + expect(subject.first[:aleg_auth_required]).to be_nil + expect(subject.first[:disconnect_code_id]).to eq(8004) # Reject by customer auth + + expect(subject.first[:customer_auth_id]).to eq(@ca.id) + expect(subject.first[:customer_auth_external_id]).to eq(@ca.external_id) + expect(subject.first[:customer_id]).to eq(@ca.customer_id) + expect(subject.first[:customer_external_id]).to eq(@ca.customer.external_id) + expect(subject.first[:customer_acc_id]).to eq(@ca.account_id) + expect(subject.first[:customer_acc_external_id]).to eq(@ca.account.external_id) + expect(subject.first[:rateplan_id]).to eq(@ca.rateplan_id) + expect(subject.first[:routing_plan_id]).to eq(@ca.routing_plan_id) + end + end + + context 'Auhtorized but customer has no enough balance' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + + @ca = FactoryBot.create(:customers_auth, + ip: '3.3.3.3') + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + it 'reject ' do + expect(subject.size).to eq(1) + expect(subject.first[:aleg_auth_required]).to be_nil + expect(subject.first[:disconnect_code_id]).to eq(8000) # No enough customer balance + + expect(subject.first[:customer_auth_id]).to eq(@ca.id) + expect(subject.first[:customer_auth_external_id]).to eq(@ca.external_id) + expect(subject.first[:customer_id]).to eq(@ca.customer_id) + expect(subject.first[:customer_external_id]).to eq(@ca.customer.external_id) + expect(subject.first[:customer_acc_id]).to eq(@ca.account_id) + expect(subject.first[:customer_acc_external_id]).to eq(@ca.account.external_id) + expect(subject.first[:rateplan_id]).to eq(@ca.rateplan_id) + expect(subject.first[:routing_plan_id]).to eq(@ca.routing_plan_id) + end + end + + context 'Authorized, Balance checking disabled' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + + FactoryBot.create(:customers_auth, + ip: '3.3.3.3', + check_account_balance: false) + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + + it 'reject ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # Can't find destination prefix + end + end + + context 'Authorized, Balance checking disabled, LNP' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + + FactoryBot.create(:customers_auth, + ip: '3.3.3.3', + check_account_balance: false, + routing_plan_id: routing_plan.id) + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + let!(:routing_plan) { create(:routing_plan, use_lnp: true) } + let!(:lnp_rule) { create(:lnp_routing_plan_lnp_rule, routing_plan_id: routing_plan.id, drop_call_on_error: drop_call_on_error, rewrite_call_destination: rewrite_call_destination) } + + context 'Authorized, Balance checking disabled, LNP Error' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { false } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(8003) # no LNP Error + expect(subject.first[:lrn]).to eq(nil) # No LRN + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP Error failover' do + let(:drop_call_on_error) { false } + let(:rewrite_call_destination) { false } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq(nil) # No LRN + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP cache without redirect' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { false } + let!(:lnp_cache) { create(:lnp_cache, database_id: lnp_rule.database_id, dst: 'uri-name', lrn: 'lrn111') } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq('lrn111') # LRN from cache + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP cache with redirect' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { true } + let!(:lnp_cache) { create(:lnp_cache, database_id: lnp_rule.database_id, dst: 'uri-name', lrn: 'lrn111') } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq('lrn111') # LRN from cache + expect(subject.first[:dst_prefix_out]).to eq('lrn111') # Destination rewrited + expect(subject.first[:dst_prefix_routing]).to eq('lrn111') # Destination rewrited + end + end + end +end diff --git a/spec/routing/route_spec.rb b/spec/sql/switch20/route_spec.rb similarity index 70% rename from spec/routing/route_spec.rb rename to spec/sql/switch20/route_spec.rb index e5f4ccd06..d06047e8e 100644 --- a/spec/routing/route_spec.rb +++ b/spec/sql/switch20/route_spec.rb @@ -3,7 +3,7 @@ RSpec.describe '#routing logic' do subject do CallSql::Yeti.select_all_serialized( - "SELECT * from #{ApplicationRecord::ROUTING_SCHEMA}.route_debug( + "SELECT * from switch20.route_debug( ?::integer, ?::integer, ?::smallint, @@ -264,7 +264,7 @@ end end - context 'Auhtorized, Balance checking disabled' do + context 'Authorized, Balance checking disabled' do before do FactoryBot.create(:system_load_balancer, signalling_ip: '1.1.1.1') @@ -284,4 +284,83 @@ expect(subject.first[:disconnect_code_id]).to eq(111) # Can't find destination prefix end end + + context 'Authorized, Balance checking disabled, LNP' do + before do + FactoryBot.create(:system_load_balancer, + signalling_ip: '1.1.1.1') + + FactoryBot.create(:customers_auth, + ip: '3.3.3.3', + check_account_balance: false, + routing_plan_id: routing_plan.id) + end + + let(:remote_ip) { '1.1.1.1' } + let(:x_orig_ip) { '3.3.3.3' } + let!(:routing_plan) { create(:routing_plan, use_lnp: true) } + let!(:lnp_rule) { create(:lnp_routing_plan_lnp_rule, routing_plan_id: routing_plan.id, drop_call_on_error: drop_call_on_error, rewrite_call_destination: rewrite_call_destination) } + + context 'Authorized, Balance checking disabled, LNP Error' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { false } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(8003) # no LNP Error + expect(subject.first[:lrn]).to eq(nil) # No LRN + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP Error failover' do + let(:drop_call_on_error) { false } + let(:rewrite_call_destination) { false } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq(nil) # No LRN + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP cache without redirect' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { false } + let!(:lnp_cache) { create(:lnp_cache, database_id: lnp_rule.database_id, dst: 'uri-name', lrn: 'lrn111') } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq('lrn111') # LRN from cache + expect(subject.first[:dst_prefix_out]).to eq('uri-name') # Original destination + expect(subject.first[:dst_prefix_routing]).to eq('uri-name') # Original destination + end + end + + context 'Authorized, Balance checking disabled, LNP cache with redirect' do + let(:drop_call_on_error) { true } + let(:rewrite_call_destination) { true } + let!(:lnp_cache) { create(:lnp_cache, database_id: lnp_rule.database_id, dst: 'uri-name', lrn: 'lrn111') } + + it 'LNP fail ' do + expect(subject.size).to eq(1) + expect(subject.first[:customer_auth_id]).to be + expect(subject.first[:customer_id]).to be + expect(subject.first[:disconnect_code_id]).to eq(111) # no destination + expect(subject.first[:lrn]).to eq('lrn111') # LRN from cache + expect(subject.first[:dst_prefix_out]).to eq('lrn111') # Destination rewrited + expect(subject.first[:dst_prefix_routing]).to eq('lrn111') # Destination rewrited + end + end + end end