From 520b66201e8fd3be21f4c3d591f05b81ddaaf12c Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Wed, 2 Dec 2015 13:57:45 +0100 Subject: [PATCH] MT#16273 Implement X1 agent interface for REST On X1 requests, properly update the configured agents on the cluster. Currently we only throw an error if no agents are configured at all, but let it slip through if the actual requests are failing, as they might be down or failing. Change-Id: I0f4e021a5cc4ba6a30e30bb197ed20d4504797d8 --- .../Panel/Controller/API/Interceptions.pm | 18 +++- .../Panel/Controller/API/InterceptionsItem.pm | 48 +++++++++++ lib/NGCP/Panel/Role/API/Interceptions.pm | 49 +++++++---- lib/NGCP/Panel/Utils/Interception.pm | 84 +++++++++++++++++++ 4 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 lib/NGCP/Panel/Utils/Interception.pm diff --git a/lib/NGCP/Panel/Controller/API/Interceptions.pm b/lib/NGCP/Panel/Controller/API/Interceptions.pm index 6118b2ecc8..8c31e16159 100644 --- a/lib/NGCP/Panel/Controller/API/Interceptions.pm +++ b/lib/NGCP/Panel/Controller/API/Interceptions.pm @@ -10,6 +10,7 @@ use HTTP::Headers qw(); use HTTP::Status qw(:constants); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Utils::DateTime; +use NGCP::Panel::Utils::Interception; use Path::Tiny qw(path); use Safe::Isa qw($_isa); use UUID qw/generate unparse/; @@ -210,9 +211,24 @@ sub POST :Allow { $resource = $self->resnames_to_dbnames($resource); try { $item = $c->model('DB')->resultset('voip_intercept')->create($resource); + my $res = NGCP::Panel::Utils::Interception::request($c, 'POST', undef, { + liid => $resource->{liid}, + uuid => $resource->{uuid}, + number => $resource->{number}, + sip_username => $sub->username, + sip_domain => $sub->domain->domain, + delivery_host => $resource->{x2_host}, + delivery_port => $resource->{x2_port}, + delivery_user => $resource->{x2_user}, + delivery_password => $resource->{x2_password}, + cc_required => $resource->{x3_required}, + cc_delivery_host => $resource->{x3_host}, + cc_delivery_port => $resource->{x3_port}, + }); + die "Failed to populate capture agents\n" unless($res); } catch($e) { $c->log->error("failed to create interception: $e"); # TODO: user, message, trace, ... - $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create interception."); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create interception"); last; } diff --git a/lib/NGCP/Panel/Controller/API/InterceptionsItem.pm b/lib/NGCP/Panel/Controller/API/InterceptionsItem.pm index 9a9b891267..11c44e64da 100644 --- a/lib/NGCP/Panel/Controller/API/InterceptionsItem.pm +++ b/lib/NGCP/Panel/Controller/API/InterceptionsItem.pm @@ -8,6 +8,7 @@ use HTTP::Status qw(:constants); use MooseX::ClassAttribute qw(class_has); use NGCP::Panel::Utils::DateTime; use NGCP::Panel::Utils::ValidateJSON qw(); +use NGCP::Panel::Utils::Interception; use Path::Tiny qw(path); use Safe::Isa qw($_isa); BEGIN { extends 'Catalyst::Controller::ActionRole'; } @@ -107,6 +108,26 @@ sub PATCH :Allow { $item = $self->update_item($c, $item, $old_resource, $resource, $form); last unless $item; + my ($sub, $reseller) = $self->subres_from_number($c, $resource->{number}); + last unless($sub && $reseller); + + my $res = NGCP::Panel::Utils::Interception::request($c, 'PUT', $item->uuid, { + number => $resource->{number}, + sip_username => $sub->username, + sip_domain => $sub->domain->domain, + delivery_host => $resource->{x2_host}, + delivery_port => $resource->{x2_port}, + delivery_user => $resource->{x2_user}, + delivery_password => $resource->{x2_password}, + cc_required => $resource->{x3_required}, + cc_delivery_host => $resource->{x3_host}, + cc_delivery_port => $resource->{x3_port}, + }); + unless($res) { + $c->log->error("failed to update capture agents"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update capture agents"); + last; + } $guard->commit; @@ -147,6 +168,26 @@ sub PUT :Allow { $item = $self->update_item($c, $item, $old_resource, $resource, $form); last unless $item; + my ($sub, $reseller) = $self->subres_from_number($c, $resource->{number}); + last unless($sub && $reseller); + + my $res = NGCP::Panel::Utils::Interception::request($c, 'PUT', $item->uuid, { + number => $resource->{number}, + sip_username => $sub->username, + sip_domain => $sub->domain->domain, + delivery_host => $resource->{x2_host}, + delivery_port => $resource->{x2_port}, + delivery_user => $resource->{x2_user}, + delivery_password => $resource->{x2_password}, + cc_required => $resource->{x3_required}, + cc_delivery_host => $resource->{x3_host}, + cc_delivery_port => $resource->{x3_port}, + }); + unless($res) { + $c->log->error("failed to update capture agents"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update capture agents"); + last; + } $guard->commit; @@ -173,6 +214,7 @@ sub DELETE :Allow { my $guard = $c->model('DB')->txn_scope_guard; { my $item = $self->item_by_id($c, $id); + my $uuid = $item->uuid; last unless $self->resource_exists($c, interception => $item); $item->update({ deleted => 1, @@ -190,6 +232,12 @@ sub DELETE :Allow { sip_domain => undef, uuid => undef, }); + my $res = NGCP::Panel::Utils::Interception::request($c, 'DELETE', $uuid); + unless($res) { + $c->log->error("failed to update capture agents"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to update capture agents"); + last; + } $guard->commit; diff --git a/lib/NGCP/Panel/Role/API/Interceptions.pm b/lib/NGCP/Panel/Role/API/Interceptions.pm index 86b7c8aef1..6a4afe6615 100644 --- a/lib/NGCP/Panel/Role/API/Interceptions.pm +++ b/lib/NGCP/Panel/Role/API/Interceptions.pm @@ -129,29 +129,17 @@ sub update_item { resource => $resource, ); - my $num_rs = $c->model('DB')->resultset('voip_numbers')->search( - \[ 'concat(cc,ac,sn) = ?', [ {} => $resource->{number} ]] - ); - unless($num_rs->first) { - $c->log->error("invalid number '$$resource{number}'"); - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number does not exist"); - last; - } - $resource->{reseller_id} = $num_rs->first->reseller_id; - - my $sub = $num_rs->first->subscriber; - unless($sub) { - $c->log->error("invalid number '$$resource{number}', not assigned to any subscriber"); - $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number is not active"); - last; - } + my ($sub, $reseller) = $self->subres_from_number($c, $resource->{number}); + return unless($sub && $reseller); + + $resource->{reseller_id} = $reseller->id; $resource->{sip_username} = $sub->username; $resource->{sip_domain} = $sub->domain->domain; if($resource->{x3_required} && (!defined $resource->{x3_host} || !defined $resource->{x3_port})) { $c->log->error("Missing parameter 'x3_host' or 'x3_port' with 'x3_required' activated"); $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Missing parameter 'x3_host' or 'x3_port' with 'x3_required' activated"); - last; + return; } $resource->{x3_host} = $resource->{x3_port} = undef unless($resource->{x3_required}); @@ -163,5 +151,32 @@ sub update_item { return $item; } +sub subres_from_number { + my ($self, $c, $number) = @_; + my $num_rs = $c->model('DB')->resultset('voip_numbers')->search( + \[ 'concat(cc,ac,sn) = ?', [ {} => $number ]] + ); + unless($num_rs->first) { + $c->log->error("invalid number '$number'"); + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number does not exist"); + return; + } + my $sub = $num_rs->first->subscriber; + unless($sub) { + $c->log->error("invalid number '$number', not assigned to any subscriber"); + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number is not active"); + return; + } + + my $res = $num_rs->first->reseller; + unless($res) { + $c->log->error("invalid number '$number', not assigned to any reseller"); + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Number is not active"); + return; + } + + return ($sub, $res); +} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Interception.pm b/lib/NGCP/Panel/Utils/Interception.pm new file mode 100644 index 0000000000..9b8c284607 --- /dev/null +++ b/lib/NGCP/Panel/Utils/Interception.pm @@ -0,0 +1,84 @@ +package NGCP::Panel::Utils::Interception; + +use Data::Dumper; +use LWP::UserAgent; +use TryCatch; + +sub request { + my ($c, $method, $uuid, $data) = @_; + + my $a = _init($c); + return unless($a); + + return unless($method eq 'POST' || $method eq 'PUT' || $method eq 'DELETE'); + + my @agents = @{ $a->{ua} }; + my @urls = @{ $a->{url} }; + for(my $i = 0; $i < scalar(@agents); ++$i) { + my $ua = $agents[$i]; + my $url = $urls[$i]; + if($method eq 'PUT' or $method eq 'DELETE') { + return unless($uuid); + $url .= '/'.$uuid; + } + $c->log->debug("performing $method for interception at $url"); + try { + _request($c, $ua, $url, $method, $data); + } catch($e) { + # skip errors + } + } + return 1; +} + +sub _request { + my ($c, $ua, $url, $method, $data) = @_; + + my $req = HTTP::Request->new($method => $url); + if($data) { + $req->content_type('application/json'); + $req->content($jdata); + } + my $res = $ua->request($req); + if($res->is_success) { + return 1; + } else { + $c->log->error("Failed to do $method on $url: " . $res->status_line); + return; + } +} + +sub _init { + my ($c) = @_; + + my @ua = (); + my @url = (); + my @cfgs = (); + if(ref $c->config->{intercept}->{agent} eq 'HASH') { + push @cfgs, $c->config->{intercept}->{agent}; + } elsif(ref $c->config->{intercept}->{agent} eq 'ARRAY') { + @cfgs = @{ $c->config->{intercept}->{agent} }; + } + unless(@cfgs) { + $c->log->error("No intercept agents configured in ngcp_panel.conf, rejecting request"); + return; + } + foreach my $cfg(@cfgs) { + my $agent = LWP::UserAgent->new(); + $agent->agent("Sipwise NGCP X1/0.2"); + $agent->timeout(2); + if($cfg->{user} && $cfg->{pass}) { + $agent->credentials( + $cfg->{host}.":".$cfg->{port}, + $cfg->{realm}, + $cfg->{user}, + $cfg->{pass} + ); + } + push @ua, $agent; + push @url, $cfg->{schema}.'://'.$cfg->{host}.':'.$cfg->{port}.$cfg->{url}; + } + return { ua => \@ua, url => \@url }; +} + +1;