MT#18411 provisioning scripts rework

- reworked /bin/* provisioning scripts to use NGCP::API::Client
    - completely reworked ngcp-fraud-* ngcp-credit-warning scripts

Change-Id: I416847c5c1d3e7dda5695d700a03182fa47f5b24
changes/52/5052/11
Kirill Solomko 9 years ago
parent 82a1be5c3e
commit 3d65648d9f

@ -1,125 +1,92 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use NGCP::API::Client;
use Pod::Usage;
use IO::Socket::SSL;
use Readonly;
Readonly my @required => qw();
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0,
admin => 0
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"help|h" => \&usage,
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
) or pod2usage(2);
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $url = $urlbase . '/api/domains/';
my $res = do_request($ua, $url);
if($res->is_success) {
print "API up\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
sub do_request {
my $ua = shift;
my $url = shift;
my $req = HTTP::Request->new('GET', $url);
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
sub main {
check_params();
my $client = new NGCP::API::Client;
my $uri = '/api/domains/';
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
my $rc = 0;
if (defined $res->as_hash->{total_count}) {
print "API is up\n";
} else {
print "API is down\n";
$rc = 1;
}
return $ua->request($req);
exit $rc;
return;
}
main();
exit 0;
__END__
=head1 NAME
ngcp-api_ping - check NGCP API status
=head1 SYNOPSIS
ngcp-api_ping [options]
=head1 OPTIONS
=over 8
=item B<-help>
Print a brief help message and exits.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
=item B<-host>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
Print a brief help message.
=item B<-verbose>
See debug information. Default false.
Show additional debug information. Default false.
=back
=head1 DESCRIPTION
=head1 USAGE
B<This program> will check NGCP API.
ngcp-api_ping [options]
=head1 USAGE
=head1 DESCRIPTION
ngcp-api-check -host 1.2.3.4
B<This program> checks if NGCP API is running.
=head1 REQUIRED ARGUMENTS
@ -127,18 +94,20 @@ None
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is fine otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-api_ping relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the ngcp-panel Debian package.
=head1 INCOMPATIBILITIES
@ -153,17 +122,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2016 Sipwise GmbH, Austria
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
=head1 LICENSE AND COPYRIGHT
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,99 +1,70 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(billing_profile_id contact_id);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
billing_id => 1,
contact_id => 1,
type => 'sipaccount',
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0
type => 'sipaccount',
status => 'active',
verbose => 0,
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"billing_id=i",
"help|h" => \&usage,
"billing_profile_id=i",
"contact_id=i",
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"verbose",
"type=s",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); }
) or pod2usage(2);
"verbose",
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $data = {
billing_profile_id => $opts->{billing_id},
contact_id => $opts->{contact_id},
type => $opts->{type},
status => 'active',
};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $res = do_request($ua, $urlbase, $data);
if($res->is_success) {
print $res->status_line . ' ' . $res->header('Location'). "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
sub do_request {
my $ua = shift;
my $urlbase = shift;
my $data = shift;
sub main {
check_params();
my $req = HTTP::Request->new('POST', $urlbase.'/api/customers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json($data));
return $ua->request($req);
my $uri = '/api/customers/';
my %data = map { $_ => $opts->{$_} }
qw(billing_profile_id contact_id type status);
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("POST", $uri, \%data);
print $res->result."\n";
return;
}
main();
exit 0;
__END__
=head1 NAME
ngcp-create_customer - create a customer on NGCP
=head1 NAME
=head1 SYNOPSIS
ngcp-create_customer [options]
ngcp-create_customer - create a customer
=head1 OPTIONS
@ -101,31 +72,15 @@ ngcp-create_customer [options]
=item B<-help>
Print a brief help message and exits.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
Print a brief help message.
=item B<-host>
=item B<-billing_profile_id>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-billing_id>
The billing profile id. Defaults to 1.
Billing profile id.
=item B<-contact_id>
The contact id. Defaults to 1.
Contact id.
=item B<-type>
@ -134,36 +89,46 @@ Customer can be one of the "sipaccount" or "pbxaccount" type. Defaults to
=item B<-verbose>
See debug information. Default false.
Show additional debug information. Default false.
=back
=head1 DESCRIPTION
=head1 USAGE
B<This program> will create a subscriber at NGCP.
ngcp-create_customer [options]
=head1 USAGE
ngcp-create_customer --billing_profile_id 1 --contact_id 4 --type sipaccount
ngcp-create_customer -host 1.2.3.4 -billing_id 1 -profile_id 4 -type sipaccount
=head1 DESCRIPTION
B<This program> creates a customer on the NGCP platform.
=head1 REQUIRED ARGUMENTS
TODO
=over 8
=item B<-billing_profile_id>
=item B<-contact_id>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-create_customer relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -178,17 +143,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,104 +1,72 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(domain);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
domain => $ARGV[0],
reseller_id => 1,
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0,
skip_xmpp => 0,
skip_sip => 0,
verbose => 0,
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"help|h" => \&usage,
"domain=s",
"reseller_id=i",
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"skip_xmpp",
"skip_sip",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "No domain") unless ($#ARGV == 0);
) or usage();
sub main {
my $domain = shift;
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $data = {
domain => $domain,
reseller_id => $opts->{reseller_id},
($opts->{skip_xmpp} ? (_skip_xmpp_reload => 1) : ()),
($opts->{skip_sip} ? (_skip_sip_reload => 1) : ()),
};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $res = do_request($ua, $urlbase, $data);
if($res->is_success) {
print $res->status_line . ' ' . $res->header('Location') . "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub do_request {
my $ua = shift;
my $urlbase = shift;
my $data = shift;
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
my $req = HTTP::Request->new('POST', $urlbase."/api/domains/");
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json($data));
sub main {
check_params();
my $uri = '/api/domains/';
my %data = map { $_ => $opts->{$_} } qw(reseller_id domain);
map { $data{"_".$_."_reload"} = $opts->{$_} } qw(skip_xmpp skip_sip);
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("POST", $uri, \%data);
print $res->result."\n";
return $ua->request($req);
return;
}
main($ARGV[0]);
main();
__END__
=head1 NAME
exit 0;
ngcp-create_domain - create a domain on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-create_domain [options] domain
ngcp-create_domain - create an NGCP domain
=head1 OPTIONS
@ -106,31 +74,11 @@ ngcp-create_domain [options] domain
=item B<-help>
Print a brief help message and exits.
Print a brief help message.
=item B<-reseller_id>
The reseller id to assign this domain to. Defaults to 1.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
=item B<-host>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-verbose>
See debug information. Default false.
Reseller id. Default 1.
=item B<-skip_sip>
@ -142,34 +90,46 @@ until the respective service is restarted or properly notified. Default false.
Skip reloading XMPP services. If true, changes will not be effective immedeately
until the respective service is restarted or properly notified. Default false.
=back
=item B<-verbose>
=head1 DESCRIPTION
Show additional debug information. Default false.
B<This program> will create a domain at NGCP.
=back
=head1 USAGE
ngcp-create_domain -host 1.2.3.4 -reseller_id 4 test.example.org
ngcp-create_domain [options]
ngcp-create_domain --domain example.org
=head1 DESCRIPTION
B<This program> creates a domain on the NGCP platform.
=head1 REQUIRED ARGUMENTS
B<domain> to be created
=over 8
=item B<-domain>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-create_domain relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -184,17 +144,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,152 +1,87 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(customer_id username domain password cc ac sn);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
admin => 0,
verbose => 0,
admin => 0
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"help|h" => \&usage,
"customer_id=i",
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
"username|u=s",
"domain|d=s",
"password|p=s",
"domain|d=s",
"admin|s=i",
"cc|c=i",
"ac|a=i",
"sn|n=i",
"account_id|v=i",
"webpassword|w=s"
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "Missing parameters: customer_id")
unless defined $opts->{customer_id};
die pod2usage(-exitval => 1, -message => "Missing parameters") unless
defined $opts->{username} and defined $opts->{domain}
and defined $opts->{password};
die pod2usage(-exitval => 1, -message => "Missing parameters") unless
(defined $opts->{cc} and defined $opts->{ac} and defined $opts->{sn})
or ( not defined $opts->{cc} and not defined $opts->{ac}
and not defined $opts->{sn});
"webpassword|w=s",
"verbose",
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $domain_id = get_domain_id($ua, $urlbase, $opts->{domain});
my $res = do_request($ua, $urlbase, get_data($domain_id));
if($res->is_success) {
print $res->status_line . ' ' . $res->header('Location') . "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub get_data {
my $domain_id = shift;
my $data = {
administrative => $opts->{admin} || 0,
domain_id => $domain_id,
customer_id => $opts->{customer_id},
username => $opts->{username},
password => $opts->{password},
webpassword => $opts->{webpassword},
primary_number => {
cc => $opts->{cc},
ac => $opts->{ac},
sn => $opts->{sn}
},
};
return $data;
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
sub get_domain_id {
my $ua = shift;
my $urlbase = shift;
my $domain = shift;
my $url = $urlbase."/api/domains/?domain=".$domain;
my $req = HTTP::Request->new('GET', $url);
my $domain_id;
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
if ($collection->{total_count} == 1) {
$domain_id = $collection->{_embedded}->{'ngcp:domains'}->{id};
}
else {
pod2usage(-exitval => 3, -message => "Domain not found");
}
}
else {
die $res->status_line, "\n";
sub main {
check_params();
my $uri = '/api/subscribers/';
my %data = map { $_ => $opts->{$_} }
qw(customer_id username password webpassword);
$data{primary_number} = join '', @{$opts}{qw(cc ac sn)};
$data{administrative} = $opts->{admin};
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
# domain_id
my $dom = $client->request("GET", "/api/domains/?domain=".$opts->{domain});
if ($dom->as_hash->{total_count} == 1) {
$data{domain_id} = $dom->as_hash->{_embedded}->{'ngcp:domains'}->{id};
} else {
usage("Domain not found");
}
return $domain_id;
}
my $res = $client->request("POST", $uri, \%data);
print $res->result."\n";
sub do_request {
my $ua = shift;
my $urlbase = shift;
my $data = shift;
my $req = HTTP::Request->new('POST', $urlbase.'/api/subscribers/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json($data));
return $ua->request($req);
return;
}
main();
__END__
=head1 NAME
exit 0;
ngcp-create_subscriber - create a subscriber on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-create_subscriber [options]
ngcp-create_subscriber - create a subscriber
=head1 OPTIONS
@ -154,99 +89,97 @@ ngcp-create_subscriber [options]
=item B<-help>
Print a brief help message and exits.
Print a brief help message.
=item B<-customer_id>
The customer id to assign this subscriber to.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
=item B<-host>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-account_id|v>
the unique ID of an existing account, the script will create a new one
if unspecified.
An existing customer id to assign this subscriber to.
=item B<-username|u>
new SIP username.
A SIP username.
=item B<-domain|d>
existing domain for subscriber.
An existing domain for the new subscriber.
=item B<-password|p>
unencrypted SIP password for subscriber.
An unencrypted SIP password for the new subscriber.
=item B<-webpassword|w>
unencrypted web password for subscriber.
An unencrypted web password for the new subscriber.
=item B<-cc|c>
country code of subscriber number.
A country code part of the subscriber's number.
=item B<-ac|a>
area code of subscriber number.
An area code part of the subscriber's number.
=item B<-number|n>
local part of subscriber number.
A local number part of the subscriber's number.
=item B<-admin|s>
whether or not to set the administrative flag for the subscriber.
defaults to 0 (no).
Set the administrative flag for the new subscriber.
Defaults to 0 (no).
=item B<-verbose>
See debug information. Default false.
Show additional debug information. Default false.
=back
=head1 DESCRIPTION
=head1 USAGE
B<This program> will create a subscriber at NGCP.
ngcp-create_subscriber [options]
=head1 USAGE
ngcp-create_subscriber --customer_id 1 --username 4311101 --domain sipwise.com --password SecurePassword1 --cc 43 --ac 111 --sn 01
=head1 DESCRIPTION
ngcp-create_subscriber -host 1.2.3.4 -customer_id 4 -d test.example.org -u test
-p passw12_ -c 34 -a 11 -n 12345 -s 0
B<This program> creates a subscriber on the NGCP platform.
=head1 REQUIRED ARGUMENTS
TODO
=over 8
=item B<-customer_id>
=item B<-username>
=item B<-domain>
=item B<-password>
=item B<-cc>
=item B<-ac>
=item B<-sn>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-create_subscriber relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
ngcp-create_customer relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -261,17 +194,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,61 +1,232 @@
#!/usr/bin/perl -w
#!/usr/bin/perl
use strict;
use warnings;
use Email::Sender::Simple qw(sendmail);
use English;
use Getopt::Long;
use Pod::Usage;
use NGCP::API::Client;
use Readonly;
use XML::Simple;
use Sipwise::DB;
use Sipwise::Provisioning::Config;
use Template;
use Email::Sender::Simple qw();
use Email::Simple;
use Email::Simple::Creator;
use Email::Sender::Transport::Sendmail qw();
Readonly my @required => qw();
Readonly my $config_file => '/etc/ngcp-panel/provisioning.conf';
my $opts = {
verbose => 0,
};
my $config;
GetOptions( $opts,
"help|h" => \&usage,
"verbose",
) or usage();
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
sub load_config {
$config = XML::Simple->new()->XMLin($config_file, ForceArray => 0)
or die "Cannot load config: $config_file: $ERRNO";
return;
}
sub send_email {
my ($cwarning, $contracts) = @_;
my $MTA = '/usr/sbin/sendmail -oi -t';
my $template = get_email_template() || return;
sub main;
my $vars = { domain => $cwarning->{domain},
threshold => $cwarning->{threshold},
adminmail => $config->{adminmail} };
foreach my $data (@{$contracts}) {
$vars->{contracts} .= sprintf <<EOF, @{$data}{qw(id cash_balance)};
contract_id: %s cash_balance: %s
EOF
}
main;
my $tt = Template->new();
map { my $out;
$tt->process(\$template->{$_}, $vars, \$out);
$out and $template->{$_} = $out;
} keys %{$template};
my $transport = Email::Sender::Transport::Sendmail->new;
my $email = Email::Simple->create(
header => [
To => ref $cwarning->{recipients} eq 'ARRAY'
? join(', ', @{$cwarning->{recipients}})
: $cwarning->{recipients},
From => $template->{from_email},
Subject => $template->{subject},
],
body => $template->{body},
);
Email::Sender::Simple->send($email, { transport => $transport })
or die sprintf "Cannot send credit warning notification to %s: $ERRNO",
$email->header('To');
return;
}
sub get_data {
my ($uri, $link) = @_;
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
die $res->result unless $res->is_success;
my $res_hash = $res->as_hash;
return [] unless $res_hash->{total_count} && $res_hash->{total_count} > 0;
my $data = $res_hash->{_embedded}{'ngcp:'.$link};
return ref $data eq 'ARRAY' ? $data : [ $data ];
}
sub get_email_template {
my $templates_data = get_data('/api/emailtemplates/', 'emailtemplates');
foreach my $template (@{$templates_data}) {
next unless $template->{name} eq 'credit_warning_default_email';
return $template;
}
return;
}
sub main {
my $xs = new XML::Simple;
my $conf = Sipwise::Provisioning::Config->new()->get_config();
my $db = new Sipwise::DB ( $$conf{billingdb} );
$$conf{credit_warnings} = [ $$conf{credit_warnings} ]
if defined eval { %{$$conf{credit_warnings}} };
foreach my $domcfg (@{$$conf{credit_warnings}}) {
my $contracts = $db->sql_get_all_arrayref("
SELECT a.contract_id,a.cash_balance,a.cash_balance_interval,GROUP_CONCAT(c.username,'\@',d.domain) AS subscribers
FROM contract_balances a, contracts b,voip_subscribers c, domains d
WHERE a.start = DATE_FORMAT(now(), '%Y-%m-01 00:00:00')
AND a.contract_id = b.id
AND b.status != 'terminated'
AND a.contract_id = c.contract_id
AND c.domain_id = d.id
AND d.domain = ?
AND c.status != 'terminated'
AND a.cash_balance < ?
GROUP BY contract_id
", $$domcfg{domain}, $$domcfg{threshold});
if(@$contracts) {
$$domcfg{recipients} = [ $$domcfg{recipients} ]
unless defined eval { @{$$domcfg{recipients}} };
my $mailtxt;
$mailtxt .= "Credit threshold warning for: $$domcfg{domain}\nThe following contracts are below the configured threshold of $$domcfg{threshold} cent:\n\n";
$mailtxt .= "account_id\tcash_balance\tcash_balance_interval\tsubscribers\n";
for(@$contracts) {
$mailtxt .= "$$_{contract_id}\t$$_{cash_balance}\t$$_{cash_balance_interval}\t$$_{subscribers}\n";
}
sendmail ( Email::Simple->create(
header => [
To => join(', ', @{$$domcfg{recipients}}),
From => $$conf{adminmail},
Subject => 'Sipwise NGCP credit threshold notification',
],
body => $mailtxt,
));
check_params();
load_config();
my $cwarnings = ref $config->{credit_warnings} eq 'ARRAY'
? $config->{credit_warnings}
: [ $config->{credit_warnings} ];
foreach my $cwarning (@{$cwarnings}) {
unless ($cwarning->{recipients}) {
die "No recipients defined for domain: $cwarning->{domain}";
}
unless ($cwarning->{domain}) {
die "Missing domain in a credit warning check";
}
my @contracts;
my $balances =
get_data(sprintf('/api/customerbalances/?domain=%s',
$cwarning->{domain}),
'customerbalances');
foreach my $balance (@{$balances}) {
next if $balance->{cash_balance} >= $cwarning->{threshold};
push @contracts,
{ map { $_ => $balance->{$_} } qw(id cash_balance) };
}
if (@contracts) {
send_email($cwarning, \@contracts);
}
}
}
return;
}
main();
exit 0;
__END__
=head1 NAME
ngcp-credit-warning - checks for contract balances above credit warning thresholds
=head1 OPTIONS
=over 8
=item B<-help>
Print a brief help message.
=item B<-verbose>
Show additional debug information. Default false.
=back
=head1 USAGE
ngcp-credit-warning [options]
=head1 DESCRIPTION
B<This program> checks for contract balances above credit warning thresholds and sends email notifications about the incidents
=head1 REQUIRED ARGUMENTS
None
=head1 EXIT STATUS
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 SEE ALSO
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-credit-warning relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
No known at this time.
=head1 BUGS AND LIMITATIONS
Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.
=head1 AUTHOR
Kirill Solomko <ksolomko@sipwise.com>
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2016 Sipwise GmbH, Austria
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,126 +1,82 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use URI;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(domain);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
reseller_id => 1,
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
domain => $ARGV[0],
skip_xmpp => 0,
skip_sip => 0,
verbose => 0,
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h",
"reseller_id=i",
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"skip_sip",
"help|h" => \&usage,
"domain=s",
"skip_xmpp",
"skip_sip",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "No domain") unless ($#ARGV == 0);
) or usage();
sub main {
my $domain = shift;
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $domain_url = get_domain_url($ua, $urlbase, $domain);
my $res = do_request($ua, $domain_url);
if($res->is_success) {
print $res->status_line . "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub get_domain_url {
my $ua = shift;
my $urlbase = shift;
my $domain = shift;
my $url = $urlbase."/api/domains/?domain=".$domain;
my $req = HTTP::Request->new('GET', $url);
my $domain_url;
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
if ($collection->{total_count} == 1) {
$domain_url = $urlbase .
$collection->{_links}->{'ngcp:domains'}->{href};
}
else {
pod2usage(-exitval => 3, -message => "Domain not found");
}
}
else {
die $res->as_string;
}
return $domain_url;
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
sub do_request {
my $ua = shift;
my $url = shift;
my $full_url = URI->new($url);
$full_url->query_form(
$full_url->query_form,
($opts->{skip_xmpp} ? (_skip_xmpp_reload => 1) : ()),
($opts->{skip_sip} ? (_skip_sip_reload => 1) : ()),
);
my $req = HTTP::Request->new('DELETE', $full_url);
return $ua->request($req);
sub main {
check_params();
my $uri = '/api/domains/';
my %data = map { "_".$_."_reload" => $opts->{$_} } qw(skip_xmpp skip_sip);
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
# domain_id
my $dom = $client->request("GET", "/api/domains/?domain=".$opts->{domain});
if ($dom->as_hash->{total_count} == 1) {
my $dom_id = $dom->as_hash->{_embedded}->{'ngcp:domains'}->{id};
usage("Domain not found") unless $dom_id;
$uri .= $dom_id;
} else {
usage("Domain not found");
}
my $res = $client->request("DELETE", $uri, \%data);
print $res->result."\n";
return;
}
main($ARGV[0]);
main();
__END__
=head1 NAME
exit 0;
ngcp-delete_domain - delete a domain on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-delete_domain [options] domain
ngcp-delete_domain - delete an NGCP domain
=head1 OPTIONS
@ -128,31 +84,7 @@ ngcp-delete_domain [options] domain
=item B<-help>
Print a brief help message and exits.
=item B<-reseller_id>
The reseller id to assign this domain to. Defaults to 1.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
=item B<-host>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-verbose>
See debug information. Default false.
Print a brief help message.
=item B<-skip_sip>
@ -164,34 +96,46 @@ until the respective service is restarted or properly notified. Default false.
Skip reloading XMPP services. If true, changes will not be effective immedeately
until the respective service is restarted or properly notified. Default false.
=back
=item B<-verbose>
=head1 DESCRIPTION
Show additional debug information. Default false.
B<This program> will delete a domain at NGCP.
=back
=head1 USAGE
ngcp-delete_domain -host 1.2.3.4 -reseller_id 4 test.example.org
ngcp-delete_domain [options]
ngcp-delete_domain --domain example.org
=head1 DESCRIPTION
B<This program> deletes a domain on the NGCP platform.
=head1 REQUIRED ARGUMENTS
B<domain> to be deleted
=over 8
=item B<-domain>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-delete_domain relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -206,17 +150,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,154 +1,259 @@
#!/usr/bin/perl
use strict;
use warnings;
use English;
use Getopt::Long;
use Pod::Usage;
use NGCP::API::Client;
use Readonly;
use XML::Simple;
use Template;
use Email::Sender::Simple qw();
use Email::Simple;
use Email::Simple::Creator;
use Email::Sender::Transport::Sendmail qw();
use Email::Sender::Simple qw(sendmail);
use Sipwise::Provisioning::Billing;
my %LOCK = (
none => undef,
foreign => 1,
outgoing => 2,
incoming => 3,
global => 4,
0 => 'none',
1 => 'foreign',
2 => 'outgoing',
3 => 'incoming',
4 => 'global',
);
my $conf = Sipwise::Provisioning::Config->new()->get_config();
my $o = Sipwise::Provisioning::Billing->new();
my $db = $o->{database};
# We select only those billing profile hits which exceed the fraud limit and
# have an action attached to keep the number of result rows small.
# However, we select all account hits and manually check them later, as they
# can potentially override billing profile hits.
my $a = $db->sql_get_all_arrayref(<<"!");
SELECT bpinfo.type, bpinfo.id,
IF (bpinfo.fraud_use_reseller_rates > 0, SUM(cdr.source_reseller_cost),
SUM(cdr.source_customer_cost)) as interval_cost,
bpinfo.fraud_interval_limit, bpinfo.fraud_interval_lock, bpinfo.fraud_interval_notify
FROM (
SELECT contracts.id, bp.fraud_use_reseller_rates,
CASE WHEN cfp.fraud_interval_limit > 0 THEN 'account_limit'
ELSE 'profile_limit' END as type,
IF (cfp.fraud_interval_limit > 0, cfp.fraud_interval_limit, bp.fraud_interval_limit) as fraud_interval_limit,
IF (cfp.fraud_interval_limit > 0, cfp.fraud_interval_lock, bp.fraud_interval_lock) as fraud_interval_lock,
IF (cfp.fraud_interval_limit > 0, cfp.fraud_interval_notify, bp.fraud_interval_notify) as fraud_interval_notify
FROM billing.contracts
JOIN billing.billing_profiles bp
ON (bp.id =
(SELECT m.billing_profile_id
FROM billing.billing_mappings m
USE INDEX (contractid_idx)
JOIN products pr ON pr.id = m.product_id
WHERE ((m.start_date IS NULL) OR (m.start_date <= NOW()))
AND ((m.end_date IS NULL) OR (m.end_date >= NOW()))
AND (m.contract_id = contracts.id)
AND (pr.class IN ('sipaccount', 'pbxaccount') OR pr.class IS NULL)
ORDER BY m.start_date DESC LIMIT 1
)
)
LEFT JOIN billing.contract_fraud_preferences cfp ON cfp.contract_id = contracts.id
WHERE (contracts.status = 'active')
AND (cfp.fraud_interval_limit > 0 OR bp.fraud_interval_limit > 0)
) as bpinfo
JOIN accounting.cdr ON cdr.source_account_id = bpinfo.id
WHERE cdr.start_time BETWEEN UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00'))
AND UNIX_TIMESTAMP(DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01 00:00:00'))-1
GROUP BY bpinfo.id
HAVING interval_cost >= fraud_interval_limit
AND (fraud_interval_lock > 0 OR fraud_interval_notify <> '')
!
my $x = {};
for my $e (@{ $a }) {
if(exists $x->{$e->{id}}) {
if($x->{$e->{id}}->{type} eq 'profile_limit' && $e->{type} eq 'account_limit') {
# if account limit hits and it has lock and/or notify, mark for action
if(defined $e->{fraud_interval_limit} and
int($e->{interval_cost}) >= int($e->{fraud_interval_limit}) and
($e->{fraud_interval_lock} || $e->{fraud_interval_notify})) {
$x->{$e->{id}} = $e;
} else {
# we have account fraud prefs set, but either the limit is not reached
# or no actions are necessary, let it slip through, overriding
# billing profile fraud settings
delete $x->{$e->{id}};
}
}
} else {
# if account or billing profile limit hits and it has lock and/or notify,
# mark for action
if(defined $e->{fraud_interval_limit} and
int($e->{interval_cost}) >= int($e->{fraud_interval_limit}) and
($e->{fraud_interval_lock} || $e->{fraud_interval_notify})) {
$x->{$e->{id}} = $e;
Readonly my @required => qw();
Readonly my $config_file => '/etc/ngcp-panel/provisioning.conf';
Readonly my $interval => 'month';
my $opts = {
verbose => 0,
};
my $config;
GetOptions( $opts,
"help|h" => \&usage,
"verbose",
) or usage();
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
for my $e (values %{ $x }) {
sub load_config {
$config = XML::Simple->new()->XMLin($config_file, ForceArray => 0)
or die "Cannot load config: $config_file: $ERRNO";
return;
}
sub get_data {
my ($uri, $link) = @_;
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
die $res->result unless $res->is_success;
my $res_hash = $res->as_hash;
return [] unless $res_hash->{total_count} && $res_hash->{total_count} > 0;
my $data = $res_hash->{_embedded}{'ngcp:'.$link};
return ref $data eq 'ARRAY' ? $data : [ $data ];
}
$e->{fraud_interval_lock} and
$o->lock_voip_account({id => $e->{id}, lock => $LOCK{$e->{fraud_interval_lock}}});
sub get_email_template {
my $event = shift;
my $lock_type = $event->{interval_lock} ? 'lock' : 'warning';
my $reseller_id = $event->{reseller_id};
my $templates_data = get_data('/api/emailtemplates/', 'emailtemplates');
my $selected_template;
foreach my $template (@{$templates_data}) {
next if $template->{name} ne 'customer_fraud_'.$lock_type.'_default_email'
&& $template->{name} ne 'customer_fraud_'.$lock_type.'_email';
next if $template->{reseller_id} && $template->{reseller_id} != $reseller_id;
$selected_template = $template;
last if $template->{reseller_id};
}
return $selected_template;
}
$e->{fraud_interval_notify} or next;
sub send_email {
my ($event, $subscribers) = @_;
my $subs = $db->sql_get_all_arrayref(<<"!", $e->{id});
SELECT s.username, d.domain, s.external_id
FROM voip_subscribers s
LEFT JOIN domains d ON d.id = s.domain_id
WHERE s.contract_id = ?
AND s.status != 'terminated'
!
my $template = get_email_template($event);
my $cur = sprintf('%.2f', $e->{interval_cost} / 100);
my $max = sprintf('%.2f', $e->{fraud_interval_limit} / 100);
my $vars = { adminmail => $config->{adminmail},
customer_id => $event->{id},
interval_cost => sprintf('%.2f', $event->{interval_cost}/100),
interval_limit => sprintf('%.2f', $event->{interval_limit}/100),
type => $event->{type} eq 'profile_limit'
? 'billing profile' : 'customer',
};
my ($subject, $body);
if ($e->{fraud_interval_lock}) {
$body = "Account ID " . $e->{id} . " has been locked due to exceeding the configured" . "\n"
. "credit balance threshold ($cur >= $max ) in the "
. ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . "\n\n";
$subject = 'Account ID ' . $e->{id} . ' locked by fraud detection';
}
else {
$body = "Account ID " . $e->{id} . " is currently exceeding the configured credit balance" . "\n"
. "threshold ($cur >= $max) in the "
. ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . ",\n"
. "but has not been locked due to configuration.\n\n";
$subject = 'Account ID ' . $e->{id} . ' exceeding fraud detection limit';
foreach my $subscriber (@{$subscribers}) {
$vars->{subscribers} .= sprintf "%s\@%s %s\n",
@{$subscriber}{qw(username domain)},
$subscriber->{external_id}
? '('.$subscriber->{external_id}.')' : '';
}
if (!$subs || !@$subs) {
$body .= "There are no affected subscribers.\n";
}
else {
$body .= "Affected subscribers:\n";
for my $s (@$subs) {
$body .= "\t$s->{username}\@$s->{domain}".
($s->{external_id} ? " (external ID '$s->{external_id}')"
: '') . "\n";
}
}
sendmail ( Email::Simple->create(
my $tt = Template->new();
map { my $out;
$tt->process(\$template->{$_}, $vars, \$out);
$out and $template->{$_} = $out;
} keys %{$template};
my $transport = Email::Sender::Transport::Sendmail->new;
my $email = Email::Simple->create(
header => [
To => $e->{fraud_interval_notify},
From => $$conf{adminmail},
Subject => $subject,
To => $event->{interval_notify},
From => $template->{from_email},
Subject => $template->{subject},
],
body => $body,
));
body => $template->{body},
);
Email::Sender::Simple->send($email, { transport => $transport })
or die sprintf "Cannot send fraud auto lock notification to %s: $ERRNO",
$email->header('To');
return;
}
sub lock_customer {
my ($event, $subscribers) = @_;
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $uri = '/api/customers/'.$event->{id};
my $data = [ { op => 'replace',
path => '/status',
value => 'locked' } ];
my $res = $client->request("PATCH", $uri, $data);
if ($res->status_line =~ /200 OK/) {
foreach my $subscriber (@{$subscribers}) {
$uri = '/api/subscribers/'.$subscriber->{id};
$data = [ { op => 'replace',
path => '/status',
value => 'locked' },
{ op => 'replace',
path => '/lock',
value => $event->{interval_lock} } ];
$res = $client->request("PATCH", $uri, $data);
}
}
return;
}
sub main {
check_params();
load_config();
my $events = get_data(sprintf('/api/customerfraudevents/?interval=%s',
$interval),
'customerfraudevents');
foreach my $event (@{$events}) {
my $subscribers = get_data(sprintf('/api/subscribers/?customer_id=%d',
$event->{id}),
'subscribers');
if ($event->{interval_lock} > 0) {
lock_customer($event, $subscribers);
}
send_email($event, $subscribers);
}
return;
}
1;
main();
exit 0;
__END__
=head1 NAME
ngcp-fraud-auto-lock - checks for contract balances above fraud limit thresholds
=head1 OPTIONS
=over 8
=item B<-help>
Print a brief help message.
=item B<-verbose>
Show additional debug information. Default false.
=back
=head1 USAGE
ngcp-fraud-auto-lock [options]
=head1 DESCRIPTION
B<This program> checks for contract balances above fraud limit warning thresholds and sends email notifications about the incidents as applying actions on such contracts (e.g. lock). Interval: 'month'
=head1 REQUIRED ARGUMENTS
None
=head1 EXIT STATUS
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 SEE ALSO
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-fraud-auto-lock relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
No known at this time.
=head1 BUGS AND LIMITATIONS
Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.
=head1 AUTHOR
Kirill Solomko <ksolomko@sipwise.com>
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2016 Sipwise GmbH, Austria
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,146 +1,259 @@
#!/usr/bin/perl
use strict;
use warnings;
use English;
use Getopt::Long;
use Pod::Usage;
use NGCP::API::Client;
use Readonly;
use XML::Simple;
use Template;
use Email::Sender::Simple qw();
use Email::Simple;
use Email::Simple::Creator;
use Email::Sender::Transport::Sendmail qw();
use Email::Sender::Simple qw(sendmail);
use Sipwise::Provisioning::Billing;
my %LOCK = (
none => undef,
foreign => 1,
outgoing => 2,
incoming => 3,
global => 4,
0 => 'none',
1 => 'foreign',
2 => 'outgoing',
3 => 'incoming',
4 => 'global',
);
my $conf = Sipwise::Provisioning::Config->new()->get_config();
my $o = Sipwise::Provisioning::Billing->new();
my $db = $o->{database};
my $a = $db->sql_get_all_arrayref(<<"!");
SELECT bpinfo.type, bpinfo.id,
IF (bpinfo.fraud_use_reseller_rates > 0, SUM(cdr.source_reseller_cost),
SUM(cdr.source_customer_cost)) as daily_cost,
bpinfo.fraud_daily_limit, bpinfo.fraud_daily_lock, bpinfo.fraud_daily_notify
FROM (
SELECT contracts.id, bp.fraud_use_reseller_rates,
CASE WHEN cfp.fraud_daily_limit > 0 THEN 'account_limit'
ELSE 'profile_limit' END as type,
IF (cfp.fraud_daily_limit > 0, cfp.fraud_daily_limit, bp.fraud_daily_limit) as fraud_daily_limit,
IF (cfp.fraud_daily_limit > 0, cfp.fraud_daily_lock, bp.fraud_daily_lock) as fraud_daily_lock,
IF (cfp.fraud_daily_limit > 0, cfp.fraud_daily_notify, bp.fraud_daily_notify) as fraud_daily_notify
FROM billing.contracts
JOIN billing.billing_profiles bp
ON (bp.id =
(SELECT m.billing_profile_id
FROM billing.billing_mappings m
USE INDEX (contractid_idx)
WHERE ((m.start_date IS NULL) OR (m.start_date <= NOW()))
AND ((m.end_date IS NULL) OR (m.end_date >= NOW()))
AND (m.contract_id = contracts.id)
ORDER BY m.start_date DESC LIMIT 1
)
)
LEFT JOIN billing.contract_fraud_preferences cfp ON cfp.contract_id = contracts.id
WHERE (contracts.status = 'active')
AND (cfp.fraud_daily_limit > 0 OR bp.fraud_daily_limit > 0)
) as bpinfo
JOIN accounting.cdr ON cdr.source_account_id = bpinfo.id
WHERE cdr.start_time BETWEEN UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00'))
AND UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-%d 23:59:59'))
GROUP BY bpinfo.id
HAVING daily_cost >= fraud_daily_limit
!
my $x = {};
for my $e (@{ $a }) {
if(exists $x->{$e->{id}}) {
if($x->{$e->{id}}->{type} eq 'profile_limit' && $e->{type} eq 'account_limit') {
# if account limit hits and it has lock and/or notify, mark for action
if(defined $e->{fraud_daily_limit} and
int($e->{daily_cost}) >= int($e->{fraud_daily_limit}) and
($e->{fraud_daily_lock} || $e->{fraud_daily_notify})) {
$x->{$e->{id}} = $e;
} else {
# we have account fraud prefs set, but either the limit is not reached
# or no actions are necessary, let it slip through, overriding
# billing profile fraud settings
delete $x->{$e->{id}};
}
}
} else {
# if account or billing profile limit hits and it has lock and/or notify,
# mark for action
if(defined $e->{fraud_daily_limit} and
int($e->{daily_cost}) >= int($e->{fraud_daily_limit}) and
($e->{fraud_daily_lock} || $e->{fraud_daily_notify})) {
$x->{$e->{id}} = $e;
Readonly my @required => qw();
Readonly my $config_file => '/etc/ngcp-panel/provisioning.conf';
Readonly my $interval => 'day';
my $opts = {
verbose => 0,
};
my $config;
GetOptions( $opts,
"help|h" => \&usage,
"verbose",
) or usage();
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
for my $e (values %{ $x }) {
$e->{fraud_daily_lock} and
$o->lock_voip_account({id => $e->{id}, lock => $LOCK{$e->{fraud_daily_lock}}});
$e->{fraud_daily_notify} or next;
my $subs = $db->sql_get_all_arrayref(<<"!", $e->{id});
SELECT s.username, d.domain, s.external_id
FROM voip_subscribers s
LEFT JOIN domains d ON d.id = s.domain_id
WHERE s.contract_id = ?
AND s.status != 'terminated'
!
my $cur = sprintf('%.2f', $e->{daily_cost} / 100);
my $max = sprintf('%.2f', $e->{fraud_daily_limit} / 100);
my ($subject, $body);
if ($e->{fraud_daily_lock}) {
$body = "Account ID " . $e->{id} . " has been locked due to exceeding the configured" . "\n"
. "daily credit balance threshold ($cur >= $max ) in the "
. ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . "\n\n";
$subject = 'Account ID ' . $e->{id} . ' locked by daily fraud detection';
sub load_config {
$config = XML::Simple->new()->XMLin($config_file, ForceArray => 0)
or die "Cannot load config: $config_file: $ERRNO";
return;
}
sub get_data {
my ($uri, $link) = @_;
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
die $res->result unless $res->is_success;
my $res_hash = $res->as_hash;
return [] unless $res_hash->{total_count} && $res_hash->{total_count} > 0;
my $data = $res_hash->{_embedded}{'ngcp:'.$link};
return ref $data eq 'ARRAY' ? $data : [ $data ];
}
sub get_email_template {
my $event = shift;
my $lock_type = $event->{interval_lock} ? 'lock' : 'warning';
my $reseller_id = $event->{reseller_id};
my $templates_data = get_data('/api/emailtemplates/', 'emailtemplates');
my $selected_template;
foreach my $template (@{$templates_data}) {
next if $template->{name} ne 'customer_fraud_'.$lock_type.'_default_email'
&& $template->{name} ne 'customer_fraud_'.$lock_type.'_email';
next if $template->{reseller_id} && $template->{reseller_id} != $reseller_id;
$selected_template = $template;
last if $template->{reseller_id};
}
else {
$body = "Account ID " . $e->{id} . " is currently exceeding the configured daily credit balance" . "\n"
. "threshold ($cur >= $max) in the "
. ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . ",\n"
. "but has not been locked due to configuration.\n\n";
$subject = 'Account ID ' . $e->{id} . ' exceeding daily fraud detection limit';
return $selected_template;
}
sub send_email {
my ($event, $subscribers) = @_;
my $template = get_email_template($event);
my $vars = { adminmail => $config->{adminmail},
customer_id => $event->{id},
interval_cost => sprintf('%.2f', $event->{interval_cost}/100),
interval_limit => sprintf('%.2f', $event->{interval_limit}/100),
type => $event->{type} eq 'profile_limit'
? 'billing profile' : 'customer',
};
foreach my $subscriber (@{$subscribers}) {
$vars->{subscribers} .= sprintf "%s\@%s %s\n",
@{$subscriber}{qw(username domain)},
$subscriber->{external_id}
? '('.$subscriber->{external_id}.')' : '';
}
if (!$subs || !@$subs) {
$body .= "There are no affected subscribers.\n";
}
else {
$body .= "Affected subscribers:\n";
for my $s (@$subs) {
$body .= "\t$s->{username}\@$s->{domain}".
($s->{external_id} ? " (external ID '$s->{external_id}')"
: '') . "\n";
}
}
sendmail ( Email::Simple->create(
my $tt = Template->new();
map { my $out;
$tt->process(\$template->{$_}, $vars, \$out);
$out and $template->{$_} = $out;
} keys %{$template};
my $transport = Email::Sender::Transport::Sendmail->new;
my $email = Email::Simple->create(
header => [
To => $e->{fraud_daily_notify},
From => $$conf{adminmail},
Subject => $subject,
To => $event->{interval_notify},
From => $template->{from_email},
Subject => $template->{subject},
],
body => $body,
));
body => $template->{body},
);
Email::Sender::Simple->send($email, { transport => $transport })
or die sprintf "Cannot send fraud daily lock notification to %s: $ERRNO",
$email->header('To');
return;
}
sub lock_customer {
my ($event, $subscribers) = @_;
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $uri = '/api/customers/'.$event->{id};
my $data = [ { op => 'replace',
path => '/status',
value => 'locked' } ];
my $res = $client->request("PATCH", $uri, $data);
if ($res->status_line =~ /200 OK/) {
foreach my $subscriber (@{$subscribers}) {
$uri = '/api/subscribers/'.$subscriber->{id};
$data = [ { op => 'replace',
path => '/status',
value => 'locked' },
{ op => 'replace',
path => '/lock',
value => $event->{interval_lock} } ];
$res = $client->request("PATCH", $uri, $data);
}
}
return;
}
sub main {
check_params();
load_config();
my $events = get_data(sprintf('/api/customerfraudevents/?interval=%s',
$interval),
'customerfraudevents');
foreach my $event (@{$events}) {
my $subscribers = get_data(sprintf('/api/subscribers/?customer_id=%d',
$event->{id}),
'subscribers');
if ($event->{interval_lock} > 0) {
lock_customer($event, $subscribers);
}
send_email($event, $subscribers);
}
return;
}
1;
main();
exit 0;
__END__
=head1 NAME
ngcp-fraud-daily-lock - checks for contract balances above fraud limit thresholds
=head1 OPTIONS
=over 8
=item B<-help>
Print a brief help message.
=item B<-verbose>
Show additional debug information. Default false.
=back
=head1 USAGE
ngcp-fraud-daily-lock [options]
=head1 DESCRIPTION
B<This program> checks for contract balances above fraud limit warning thresholds and sends email notifications about the incidents as applying actions on such contracts (e.g. lock). Interval: 'day'
=head1 REQUIRED ARGUMENTS
None
=head1 EXIT STATUS
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 SEE ALSO
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-fraud-daily-lock relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
No known at this time.
=head1 BUGS AND LIMITATIONS
Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.
=head1 AUTHOR
Kirill Solomko <ksolomko@sipwise.com>
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2016 Sipwise GmbH, Austria
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,135 +1,65 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use Data::Dumper;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(customer_id);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0,
admin => 0
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"help|h" => \&usage,
"customer_id=i",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
"username|u=s",
"domain|d=s",
"account_id|i=i"
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "Missing parameters") unless
(defined $opts->{account_id} and not defined $opts->{username} and
not defined $opts->{domain}) or
(defined $opts->{username} and defined $opts->{domain} and
not defined $opts->{account_id});
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $customer_url;
if ($opts->{account_id}) {
$customer_url = $urlbase . '/api/customers/'.$opts->{account_id};
}
else {
$customer_url= get_customer_url($ua, $urlbase);
}
my $res = do_request($ua, $customer_url);
if($res->is_success) {
# use no indentation/linebreaks, for syslog logging
$Data::Dumper::Indent = 1;
# don't print useless variable names
$Data::Dumper::Terse = 1;
# sort hash keys, so parameters always have the same order
$Data::Dumper::Sortkeys = 1;
my $collection = JSON::from_json($res->decoded_content);
print "VoIP account information:\n", Dumper $collection;
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub get_customer_url {
my $ua = shift;
my $urlbase = shift;
my $url = $urlbase."/api/subscribers/?domain=".$opts->{domain}.
'&username='.$opts->{username};
my $req = HTTP::Request->new('GET', $url);
my $customer_url;
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
if ($collection->{total_count} == 1) {
my $lnks = $collection->{_embedded}->{'ngcp:subscribers'}->{_links};
$customer_url = $urlbase . $lnks->{'ngcp:customers'}->{href};
}
else {
pod2usage(-exitval => 3, -message => "customer not found");
}
}
else {
die $res->as_string;
}
return $customer_url;
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
sub do_request {
my $ua = shift;
my $url = shift;
my $req = HTTP::Request->new('GET', $url);
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
sub main {
check_params();
my $uri = '/api/customers/';
$uri .= $opts->{customer_id} ? $opts->{customer_id} : '';
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
$res->is_success ? print $res->decoded_content."\n"
: print $res->result."\n";
return $ua->request($req);
return;
}
main();
__END__
=head1 NAME
exit 0;
ngcp-get_customer - show info of a customer on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-get_customer [options]
ngcp-get_customer - retreives an NGCP Customer
=head1 OPTIONS
@ -137,70 +67,52 @@ ngcp-get_customer [options]
=item B<-help>
Print a brief help message and exits.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
=item B<-host>
Print a brief help message.
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-customer_id>
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-account_id|i>
the unique ID of an existing account.
=item B<-username|u>
SIP username.
=item B<-domain|d>
existing domain for subscriber.
Customer id
=item B<-verbose>
See debug information. Default false.
Show additional debug information. Default false.
=back
=head1 DESCRIPTION
=head1 USAGE
B<This program> will show info of a customer at NGCP.
ngcp-get_customer [options]
=head1 USAGE
ngcp-get_customer --customer_id 11
ngcp-get_customer -host 1.2.3.4 -d test.example.org -u test
=head1 DESCRIPTION
ngcp-get_customer -host 1.2.3.4 -i 45
B<This program> retreives a customer from the NGCP platform.
=head1 REQUIRED ARGUMENTS
B<domain> and B<username> or B<account_id>
=over 8
=item B<-customer_id>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-get_customer relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -215,17 +127,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,99 +1,147 @@
#!/usr/bin/perl
use strict;
use warnings;
use English;
use Getopt::Long;
use Pod::Usage;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(id);
my $opts = {
verbose => 0,
};
GetOptions( $opts,
"help|h" => \&usage,
"id=i",
"verbose",
) or usage();
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
use Data::Dumper;
use Sipwise::Provisioning::Voip;
use Sipwise::Provisioning::Config;
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
my %CONFIG = (admin => 'cmd');
sub main {
check_params();
my $uri = '/api/soundsets/';
$uri .= $opts->{id} ? $opts->{id} : '';
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $res = $client->request("GET", $uri);
$res->is_success ? print $res->content."\n"
: print $res->result."\n";
return;
}
my $config = Sipwise::Provisioning::Config->new()->get_config();
main();
unless ($CONFIG{password} = $config->{acl}->{$CONFIG{admin}}->{password}) {
die "Error: No provisioning password found for user $CONFIG{admin}\n";
}
exit 0;
sub main;
sub usage;
sub call_prov;
__END__
my $prov = Sipwise::Provisioning::Voip->new();
=head1 NAME
main;
ngcp-sound_set - retreives an NGCP Sound Set
sub main {
# use no indentation/linebreaks, for syslog logging
$Data::Dumper::Indent = 1;
# don't print useless variable names
$Data::Dumper::Terse = 1;
# sort hash keys, so parameters always have the same order
$Data::Dumper::Sortkeys = 1;
my $return;
print "Fetching all sound sets\n";
$return = call_prov('get_sound_sets');
print "Sound Sets:\n", Dumper $return;
print "Creating global sound set\n";
$return = call_prov('create_sound_set', {
reseller_id => 1,
name => 'api sound set',
description => 'sound set created via api',
});
print "Created Sound Set:\n", Dumper $return;
my $set_id = $return;
print "Fetching new sound set\n";
$return = call_prov('get_sound_set', { id => $set_id });
print "Sound Set:\n", Dumper $return;
print "Updating global sound set\n";
$return = call_prov('update_sound_set', {
id => $set_id,
name => 'updated sound set',
description => 'updated sound set created via api',
account_id => 23,
});
print "Updated Sound Set:\n", Dumper $return;
print "Fetching new sound set\n";
$return = call_prov('get_sound_set', { id => $set_id });
print "Sound Set:\n", Dumper $return;
print "Delete new sound set\n";
$return = call_prov('delete_sound_set', { id => $set_id });
print "Sound Set Deleted:\n", Dumper $return;
exit;
}
=head1 OPTIONS
=over 8
sub call_prov {
# scalar, scalar, hash-ref
my ($function, $parameter) = @_;
my $result;
eval {
$result = $prov->handle_request( $function,
{
authentication => {
type => 'system',
username => $CONFIG{admin},
password => $CONFIG{password},
},
parameters => $parameter,
});
};
if($@) {
if(ref $@ eq 'SOAP::Fault') {
die "Voip\::$function failed: ". $@->faultstring;
} else {
die "Voip\::$function failed: $@";
}
}
=item B<-help>
return $result;
}
Print a brief help message.
=item B<-id>
Sound set id
=item B<-verbose>
Show additional debug information. Default false.
=back
=head1 USAGE
ngcp-sound_set [options]
ngcp-sound_set --id 11
=head1 DESCRIPTION
B<This program> retreives a sound set from the NGCP platform.
=head1 REQUIRED ARGUMENTS
=over 8
=item B<-id>
=back
=head1 EXIT STATUS
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 SEE ALSO
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-sound_set relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
No known at this time.
=head1 BUGS AND LIMITATIONS
Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.
=head1 AUTHOR
Victor Seva <vseva@sipwise.com>
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2016 Sipwise GmbH, Austria
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,143 +1,66 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(customer_id);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0,
admin => 0
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h" => sub { pod2usage(-exitval =>0); },
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"help|h" => \&usage,
"customer_id=i",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
"username|u=s",
"domain|d=s",
"account_id|i=i"
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "Missing parameters") unless
(defined $opts->{account_id} and not defined $opts->{username} and
not defined $opts->{domain}) or
(defined $opts->{username} and defined $opts->{domain} and
not defined $opts->{account_id});
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $customer_url;
if ($opts->{account_id}) {
$customer_url = $urlbase . '/api/customers/'.$opts->{account_id};
}
else {
$customer_url= get_customer_url($ua, $urlbase);
}
my $res = do_request($ua, $customer_url);
if($res->is_success) {
print $res->status_line . "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub get_customer_url {
my $ua = shift;
my $urlbase = shift;
my $url = $urlbase."/api/subscribers/?domain=".$opts->{domain}.
'&username='.$opts->{username};
my $req = HTTP::Request->new('GET', $url);
my $customer_url;
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
if ($collection->{total_count} == 1) {
$customer_url = $urlbase .
$collection->{_links}->{'ngcp:customers'}->{href};
}
else {
pod2usage(-exitval => 3, -message => "customer not found");
}
}
else {
die $res->as_string;
}
return $customer_url;
sub usage {
my $missing = shift;
pod2usage(-exitval => $missing ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $missing ? "Missing parameters: $missing" : '',
);
return;
}
sub do_request {
my $ua = shift;
my $url = shift;
my $data = {
status => 'terminated',
contact_id => 0,
billing_profile_id => 0
};
my $req = HTTP::Request->new('GET', $url);
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
$data->{contact_id} = $collection->{contact_id};
$data->{billing_profile_id} = $collection->{billing_profile_id};
}
else {
die $res->as_string;
}
$req = HTTP::Request->new('PUT', $url);
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=minimal');
$req->content(JSON::to_json($data));
sub main {
check_params();
my $uri = '/api/customers/'.$opts->{customer_id};
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $data = [ { op => 'replace',
path => '/status',
value => 'terminated' } ];
my $res = $client->request("PATCH", $uri, $data);
print $res->result."\n";
return $ua->request($req);
return;
}
main();
__END__
=head1 NAME
exit 0;
ngcp-terminate_customer - terminates a customer on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-terminate_customer [options]
ngcp-terminate_customer - terminate an NGCP Customer
=head1 OPTIONS
@ -145,70 +68,52 @@ ngcp-terminate_customer [options]
=item B<-help>
Print a brief help message and exits.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
=item B<-auth_pwd>
Authentication password . Defaults to 'administrator'.
Print a brief help message.
=item B<-host>
=item B<-customer_id>
Host where the send queries. Defaults to '127.0.0.1'.
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-account_id|i>
the unique ID of an existing account.
=item B<-username|u>
SIP username.
=item B<-domain|d>
existing domain for subscriber.
Customer id
=item B<-verbose>
See debug information. Default false.
Show additional debug information. Default false.
=back
=head1 DESCRIPTION
=head1 USAGE
B<This program> will terminate a customer at NGCP.
ngcp-terminate_customer [options]
=head1 USAGE
ngcp-terminate_customer --customer_id 11
ngcp-terminate_customer -host 1.2.3.4 -d test.example.org -u test
=head1 DESCRIPTION
ngcp-terminate_customer -host 1.2.3.4 -i 45
B<This program> terminates a customer on the NGCP platform.
=head1 REQUIRED ARGUMENTS
B<domain> and B<username> or B<account_id>
=over 8
=item B<-customer_id>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-terminate_customer relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -223,17 +128,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

@ -1,147 +1,81 @@
#!/usr/bin/perl
use strict;
use warnings;
use Config::Tiny;
use English;
use Getopt::Long;
use JSON qw();
use LWP::UserAgent;
use Pod::Usage;
use IO::Socket::SSL;
use NGCP::API::Client;
use Readonly;
Readonly my @required => qw(username domain);
my $config = Config::Tiny->read('/etc/default/ngcp-api');
my $opts = {
host => '127.0.0.1',
port => 1443,
auth_user => 'administrator',
auth_pwd => 'administrator',
verbose => 0,
admin => 0
};
if ($config) {
$opts->{host} = $config->{_}->{NGCP_API_IP};
$opts->{port} = $config->{_}->{NGCP_API_PORT};
$opts->{sslverify} = $config->{_}->{NGCP_API_SSLVERIFY} || 'yes';
}
GetOptions( $opts,
"help|h",
"host=s",
"port=i",
"auth_user=s",
"auth_pwd=s",
"help|h" => \&usage,
"username=s",
"domain=s",
"verbose",
"man" => sub { pod2usage(-exitval => 0, -verbose => 2); },
"username|u=s",
"domain|d=s",
"account_id|i=i"
) or pod2usage(2);
die pod2usage(-exitval => 1, -message => "Missing parameters") unless
(defined $opts->{account_id} and not defined $opts->{username} and
not defined $opts->{domain}) or
(defined $opts->{username} and defined $opts->{domain} and
not defined $opts->{account_id});
) or usage();
sub main {
my $urlbase = 'https://'.$opts->{host}.':'.$opts->{port};
my $ua = LWP::UserAgent->new();
if($opts->{sslverify} eq 'no') {
$ua->ssl_opts(
verify_hostname => 0,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
);
}
$ua->credentials($opts->{host}.':'.$opts->{port}, 'api_admin_http',
$opts->{auth_user}, $opts->{auth_pwd});
# debug!!
if($opts->{verbose}) {
$ua->show_progress(1);
$ua->add_handler("request_send", sub { shift->dump; return });
$ua->add_handler("response_done", sub { shift->dump; return });
}
my $subscriber_url;
if ($opts->{account_id}) {
$subscriber_url = $urlbase . '/api/customers/'.$opts->{account_id};
}
else {
$subscriber_url= get_subscriber_url($ua, $urlbase);
}
my $res = do_request($ua, $subscriber_url);
if($res->is_success) {
print $res->status_line . "\n";
} else {
die $res->as_string;
sub check_params {
my @missing;
foreach my $param (@required) {
push @missing, $param unless $opts->{$param};
}
usage(join(' ', @missing)) if scalar @missing;
return;
}
sub get_subscriber_url {
my $ua = shift;
my $urlbase = shift;
my $url = $urlbase."/api/subscribers/?domain=".$opts->{domain}.
'&username='.$opts->{username};
my $req = HTTP::Request->new('GET', $url);
my $subscriber_url;
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
if ($collection->{total_count} == 1) {
$subscriber_url = $urlbase .
$collection->{_links}->{'ngcp:subscribers'}->{href};
}
else {
pod2usage(-exitval => 3, -message => "subscriber not found");
}
}
else {
die $res->as_string;
}
return $subscriber_url;
sub usage {
my $msg = shift;
pod2usage(-exitval => $msg ? 1 : 0,
-verbose => 99,
-sections => [ qw(NAME OPTIONS USAGE) ],
-message => $msg
? $msg =~ /not found/i
? $msg
: "Missing parameters: $msg"
: '',
);
return;
}
sub do_request {
my $ua = shift;
my $url = shift;
my $data = {
status => 'terminated',
domain_id => 0,
customer_id => 0,
username => '',
password => ''
};
my $req = HTTP::Request->new('GET', $url);
my $res = $ua->request($req);
if($res->is_success) {
my $collection = JSON::from_json($res->decoded_content);
$data->{domain_id} = $collection->{domain_id};
$data->{customer_id} = $collection->{customer_id};
$data->{username} = $collection->{username};
$data->{password} = $collection->{password};
}
else {
die $res->as_string;
sub main {
check_params();
my $uri = sprintf '/api/subscribers/?username=%s&domain=%s',
@{$opts}{qw(username domain)};
my $client = new NGCP::API::Client;
$client->set_verbose($opts->{verbose});
my $sub = $client->request("GET", $uri);
my $sub_id;
if ($sub->as_hash->{total_count} == 1) {
$sub_id = $sub->as_hash->{_embedded}->{'ngcp:subscribers'}->{id};
usage("Wrong subscriber id found") unless $sub_id =~ /^\d$/;
} else {
usage("Subscriber not found");
}
$req = HTTP::Request->new('PUT', $url);
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=minimal');
$req->content(JSON::to_json($data));
$uri = '/api/subscribers/'.$sub_id;
my $data = [ { op => 'replace',
path => '/status',
value => 'terminated' } ];
my $res = $client->request("PATCH", $uri, $data);
print $res->result."\n";
return $ua->request($req);
return;
}
main();
__END__
=head1 NAME
exit 0;
ngcp-terminate_subscriber - terminates a subscriber on NGCP
__END__
=head1 SYNOPSIS
=head1 NAME
ngcp-terminate_subscriber [options]
ngcp-terminate_subscriber - terminate an NGCP Subscriber
=head1 OPTIONS
@ -149,70 +83,58 @@ ngcp-terminate_subscriber [options]
=item B<-help>
Print a brief help message and exits.
=item B<-auth_user>
Authentication username . Defaults to 'administrator'.
Print a brief help message
=item B<-auth_pwd>
=item B<-username>
Authentication password . Defaults to 'administrator'.
Subscriber username
=item B<-host>
=item B<-domain>
Host where the send queries. Defaults to '127.0.0.1'.
Subscriber domain
=item B<-port>
Port where the send queries. Defaults to 1443.
=item B<-account_id|i>
the unique ID of an existing account.
=item B<-username|u>
SIP username.
=item B<-verbose>
=item B<-domain|d>
Show additional debug information. Default false.
existing domain for subscriber.
=back
=item B<-verbose>
=head1 USAGE
See debug information. Default false.
ngcp-terminate_subscriber [options]
=back
ngcp-terminate_subscriber --username 431110001 --domain example.org
=head1 DESCRIPTION
B<This program> will terminate a subscriber at NGCP.
B<This program> terminates a subscriber on the NGCP platform.
=head1 USAGE
=head1 REQUIRED ARGUMENTS
ngcp-terminate_subscriber -host 1.2.3.4 -d test.example.org -u test
=over 8
ngcp-terminate_subscriber -host 1.2.3.4 -i 45
=item B<-username>
=head1 REQUIRED ARGUMENTS
=item B<-domain>
B<domain> and B<username> or B<account_id>
=back
=head1 EXIT STATUS
Exit code 0 means that everything should have went fine otherwise error.
Exit code 0 means everything is ok otherwise 1.
=head1 CONFIGURATION
=head1 DIAGNOSTICS
=head1 CONFIGURATION
=head1 SEE ALSO
/etc/default/ngcp-api for default values
NGCP::API::Client
=head1 DEPENDENCIES
ngcp-terminate_subscriber relies on a bunch of Perl modules, all of them specified as
dependencies through the ngcp-ossbss-clients-perl Debian package.
dependencies through the Debian package.
=head1 INCOMPATIBILITIES
@ -227,17 +149,21 @@ Development Team <support@sipwise.com>.
Victor Seva <vseva@sipwise.com>
=head1 LICENSE
=head1 LICENSE AND COPYRIGHT
Copyright (c) 2015 Sipwise GmbH, Austria.
All rights reserved. You may not copy, distribute
or modify without prior written permission from
Sipwise GmbH, Austria.
Copyright (C) 2016 Sipwise GmbH, Austria
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Copyright (c) 2015 Sipwise GmbH, Austria.
You should have received a copy of the licence terms together with the
software.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

Loading…
Cancel
Save