TT#76783 - Implemend ALE RPS devices auto provisioning

* Implement checking/creation of server, profile
	   and prepare request for device creation on ALE
	   RPS
	 * Prepare request for device deletion on ALE RPS
	 * Changes in Panel to support provisioning via
	   the ALE RPS

Change-Id: I24b62c03b64c56fcbcabea71428d0b70b46706e6
changes/62/38462/2
Flaviu Mates 7 years ago
parent 93ef5083b2
commit 16365fb256

@ -269,11 +269,12 @@ has_field 'bootstrap_method' => (
{ label => 'Grandstream', value => 'redirect_grandstream' },
{ label => 'Sipwise Redirect', value => 'redirect_sipwise' },
{ label => 'Sipwise Profile', value => 'profile_sipwise' },
{ label => 'ALE', value => 'redirect_ale' },
],
default => 'http',
element_attr => {
rel => ['tooltip'],
title => ['Method to configure the provisioning server on the phone. One of http, redirect_panasonic, redirect_yealink, redirect_polycom, redirect_snom, redirect_grandstream, redirect_sipwise, profile_sipwise.'],
title => ['Method to configure the provisioning server on the phone. One of http, redirect_panasonic, redirect_yealink, redirect_polycom, redirect_snom, redirect_grandstream, redirect_sipwise, profile_sipwise, redirect_ale.'],
javascript => ' onchange="bootstrapDynamicFields(this.options[this.selectedIndex].value);" ',
},
);
@ -283,7 +284,7 @@ has_field 'bootstrap_uri' => (
label => 'Bootstrap URI',
default => '',
#we don't show this field for polycom, because polycom doesn't support now any possibility to configure provisioning url via API
wrapper_class => [qw/ngcp-devicetype ngcp-devicetype-phone ngcp-bootstrap-config ngcp-bootstrap-config-http ngcp-bootstrap-config-redirect_panasonic ngcp-bootstrap-config-redirect_yealink ngcp-bootstrap-config-redirect_snom ngcp-bootstrap-config-redirect_grandstream/],
wrapper_class => [qw/ngcp-devicetype ngcp-devicetype-phone ngcp-bootstrap-config ngcp-bootstrap-config-http ngcp-bootstrap-config-redirect_panasonic ngcp-bootstrap-config-redirect_yealink ngcp-bootstrap-config-redirect_snom ngcp-bootstrap-config-redirect_grandstream ngcp-bootstrap-config-redirect_ale/],
element_attr => {
rel => ['tooltip'],
title => ['Custom provisioning server URI.'],
@ -497,6 +498,28 @@ has_field 'bootstrap_config_profile_sipwise_password' => (
title => ['Password used to configure device on Sipwise EDS.'],
},
);
has_field 'bootstrap_config_redirect_ale_user' => (
type => 'Text',
required => 0,
label => 'ALE username',
default => '',
wrapper_class => [qw/ngcp-devicetype ngcp-devicetype-phone ngcp-bootstrap-config ngcp-bootstrap-config-redirect_ale/],
element_attr => {
rel => ['tooltip'],
title => ['Username used to configure bootstrap url on ALE redirect server. Obtained from ALE.'],
},
);
has_field 'bootstrap_config_redirect_ale_password' => (
type => 'Text',
required => 0,
label => 'ALE password',
default => '',
wrapper_class => [qw/ngcp-devicetype ngcp-devicetype-phone ngcp-bootstrap-config ngcp-bootstrap-config-redirect_ale/],
element_attr => {
rel => ['tooltip'],
title => ['Password used to configure bootstrap url on ALE redirect server. Obtained from ALE.'],
},
);
has_field 'save' => (
type => 'Submit',
@ -508,7 +531,7 @@ has_field 'save' => (
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/vendor model type extensions_num connectable_models linerange linerange_add bootstrap_method bootstrap_uri bootstrap_config_http_sync_method bootstrap_config_http_sync_uri bootstrap_config_http_sync_params bootstrap_config_redirect_panasonic_user bootstrap_config_redirect_panasonic_password bootstrap_config_redirect_yealink_user bootstrap_config_redirect_yealink_password bootstrap_config_redirect_polycom_user bootstrap_config_redirect_polycom_password bootstrap_config_redirect_polycom_profile bootstrap_config_redirect_grandstream_cid bootstrap_config_redirect_grandstream_key bootstrap_config_redirect_sipwise_user bootstrap_config_redirect_sipwise_password bootstrap_config_profile_sipwise_user bootstrap_config_profile_sipwise_password front_image front_thumbnail mac_image/],
render_list => [qw/vendor model type extensions_num connectable_models linerange linerange_add bootstrap_method bootstrap_uri bootstrap_config_http_sync_method bootstrap_config_http_sync_uri bootstrap_config_http_sync_params bootstrap_config_redirect_panasonic_user bootstrap_config_redirect_panasonic_password bootstrap_config_redirect_yealink_user bootstrap_config_redirect_yealink_password bootstrap_config_redirect_polycom_user bootstrap_config_redirect_polycom_password bootstrap_config_redirect_polycom_profile bootstrap_config_redirect_grandstream_cid bootstrap_config_redirect_grandstream_key bootstrap_config_redirect_sipwise_user bootstrap_config_redirect_sipwise_password bootstrap_config_profile_sipwise_user bootstrap_config_profile_sipwise_password bootstrap_config_redirect_ale_user bootstrap_config_redirect_ale_password front_image front_thumbnail mac_image/],
);
has_block 'actions' => (

@ -29,7 +29,7 @@ has_field 'save' => (
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/reseller vendor model type extensions_num connectable_models linerange linerange_add bootstrap_method bootstrap_uri bootstrap_config_http_sync_method bootstrap_config_http_sync_uri bootstrap_config_http_sync_params bootstrap_config_redirect_panasonic_user bootstrap_config_redirect_panasonic_password bootstrap_config_redirect_yealink_user bootstrap_config_redirect_yealink_password bootstrap_config_redirect_polycom_user bootstrap_config_redirect_polycom_password bootstrap_config_redirect_polycom_profile bootstrap_config_redirect_snom_user bootstrap_config_redirect_snom_password bootstrap_config_redirect_grandstream_cid bootstrap_config_redirect_grandstream_key front_image front_thumbnail mac_image/],
render_list => [qw/reseller vendor model type extensions_num connectable_models linerange linerange_add bootstrap_method bootstrap_uri bootstrap_config_http_sync_method bootstrap_config_http_sync_uri bootstrap_config_http_sync_params bootstrap_config_redirect_panasonic_user bootstrap_config_redirect_panasonic_password bootstrap_config_redirect_yealink_user bootstrap_config_redirect_yealink_password bootstrap_config_redirect_polycom_user bootstrap_config_redirect_polycom_password bootstrap_config_redirect_polycom_profile bootstrap_config_redirect_snom_user bootstrap_config_redirect_snom_password bootstrap_config_redirect_grandstream_cid bootstrap_config_redirect_grandstream_key bootstrap_config_redirect_ale_user bootstrap_config_redirect_ale_password front_image front_thumbnail mac_image/],
);
has_block 'actions' => (

@ -13,6 +13,7 @@ use NGCP::Panel::Utils::DeviceBootstrap::Snom;
use NGCP::Panel::Utils::DeviceBootstrap::Grandstream;
use NGCP::Panel::Utils::DeviceBootstrap::SipwiseRedirect;
use NGCP::Panel::Utils::DeviceBootstrap::SipwiseProfile;
use NGCP::Panel::Utils::DeviceBootstrap::ALE;
my $redirect_processor;
@ -116,6 +117,8 @@ sub get_redirect_processor{
$redirect_processor = NGCP::Panel::Utils::DeviceBootstrap::SipwiseRedirect->new( params => $params );
}elsif('profile_sipwise' eq $bootstrap_method){
$redirect_processor = NGCP::Panel::Utils::DeviceBootstrap::SipwiseProfile->new( params => $params );
}elsif('redirect_ale' eq $bootstrap_method){
$redirect_processor = NGCP::Panel::Utils::DeviceBootstrap::ALE->new( params => $params );
}elsif('http' eq $bootstrap_method){
#$ret = panasonic_bootstrap_register($params);
}

@ -0,0 +1,197 @@
package NGCP::Panel::Utils::DeviceBootstrap::ALE;
use strict;
use warnings;
use URI::Escape;
use Moo;
use Types::Standard qw(Str);
use JSON qw/encode_json decode_json/;
use MIME::Base64;
use Digest::MD5 qw/md5_hex/;
extends 'NGCP::Panel::Utils::DeviceBootstrap::VendorREST';
sub rpc_server_params{
my $self = shift;
my $cfg = {
proto => 'https',
host => 'api.rps.ce.al-enterprise.com',
path => 'api',
};
$self->{rpc_server_params} = $cfg;
return $self->{rpc_server_params};
}
sub rest_prepare_request {
my ($self, $action) = @_;
my $c = $self->params->{c};
my $ret;
my $new_mac = $self->content_params->{mac};
my $old_mac = $self->content_params->{mac_old};
my $param_servername = $self->content_params->{server_name};
my $param_uri = $self->content_params->{uri};
my $token;
$self->{rpc_server_params} //= $self->rpc_server_params;
my $cfg = $self->{rpc_server_params};
$c->log->debug("ALE prepare request for action $action");
# first, generate token
my $url = "$$cfg{proto}://$$cfg{host}/$$cfg{path}/bp_user/generate/token";
$c->log->debug("ALE generate token '$url'");
my $req = HTTP::Request->new(GET => $url);
$req->header(':api_user_name' => $self->params->{credentials}->{user});
$req->header(':api_password' => $self->params->{credentials}->{password});
my $res = $self->_ua->request($req);
my $data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("Token generation successful, data: " . $res->decoded_content);
$token = $data->{data}->{token};
} else {
$c->log->error("Token generation failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
if ($action eq 'register_content') {
# fetch server
$url = "$$cfg{proto}://$$cfg{host}/$$cfg{path}/servers";
$c->log->debug("ALE check server '$url'");
$req = HTTP::Request->new(GET => $url);
$req->header(token => $token);
$res = $self->_ua->request($req);
my $server_id;
$data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("ALE check server query successful, data: " . $res->decoded_content);
my ($server) = grep {$_->{server_url} eq $param_uri} @{$data->{data}->{server_list}};
if ($server) {
$server_id = $server->{server_id};
}
else {
#server does not exist, create it
$c->log->debug("ALE create server '$url'");
$req = HTTP::Request->new(POST => $url);
$req->header(token => $token);
$req->content_type('application/json');
$req->content(encode_json({
server_name => $param_servername,
server_url => $param_uri,
})
);
$res = $self->_ua->request($req);
$data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("ALE create server query successful, data: " . $res->decoded_content);
$server_id = $data->{data}->{server_id};
}
else{
$c->log->error("ALE create server query failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
}
} else {
$c->log->error("ALE check server query failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
# fetch profile
$url = "$$cfg{proto}://$$cfg{host}/$$cfg{path}/profiles";
$c->log->debug("ALE check server '$url'");
$req = HTTP::Request->new(GET => $url);
$req->header(token => $token);
$res = $self->_ua->request($req);
$data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("ALE check profile query successful, data: " . $res->decoded_content);
my $profile_id;
my ($profile) = grep {$_->{server_id} == $server_id} @{$data->{data}->{profile_list}};
if ($profile) {
$profile_id = $profile->{profile_id};
}
else {
#profile does not exist, create it
$c->log->debug("ALE create profile '$url'");
$req = HTTP::Request->new(POST => $url);
$req->header(token => $token);
$req->content_type('application/json');
$req->content(encode_json({
profile_name => $param_servername,
server_id => $server_id,
})
);
$res = $self->_ua->request($req);
$data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("ALE create profile query successful, data: " . $res->decoded_content);
$profile_id = $data->{data}->{profile_id};
}
else{
$c->log->error("ALE create profile query failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
}
$ret = {
method =>'POST',
url => "$$cfg{proto}://$$cfg{host}/$$cfg{path}/devices",
body => { macs => [{mac =>$new_mac}], profile_id => $profile_id},
token => $token,
};
} else {
$c->log->error("ALE check profile query failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
} elsif ($action eq 'unregister_content') {
# we've to fetch the id first before constructing the delete request
$url = "$$cfg{proto}://$$cfg{host}/$$cfg{path}/devices";
$c->log->debug("ALE check devices '$url'");
$req = HTTP::Request->new(GET => $url);
$req->header(token => $token);
$res = $self->_ua->request($req);
$data = decode_json($res->decoded_content);
if ($res->is_success && $data->{success}) {
$c->log->debug("ALE check devices query successful, data: " . $res->decoded_content);
my $device_id;
my ($device) = grep {$_->{mac} == $old_mac} @{$data->{data}->{device_list}};
if ($device) {
$device_id = $device->{device_id};
}
$c->log->debug("ALE unregister query successful, data: " . $res->decoded_content);
$data = decode_json($res->decoded_content);
$ret = {
method =>'DELETE',
url => "$$cfg{proto}://$$cfg{host}/$$cfg{path}/devices/$device_id",
body => undef,
token => $token,
};
} else {
$c->log->error("ALE unregister query failed (" . $res->status_line . "): " . $res->decoded_content);
return;
}
}
return $ret;
}
around 'process_bootstrap_uri' => sub {
my($orig_method, $self, $uri) = @_;
$uri = $self->$orig_method($uri);
$self->content_params->{uri} = $uri;
$self->bootstrap_uri_server_name($uri);
return $self->content_params->{uri};
};
sub bootstrap_uri_server_name{
my($self,$uri) = @_;
$uri ||= $self->content_params->{uri};
#http://stackoverflow.com/questions/4826403/hash-algorithm-with-alphanumeric-output-of-20-characters-max
$self->content_params->{server_name} ||= substr(md5_hex($uri),0,20);
return $self->content_params->{server_name};
}
1;

@ -23,7 +23,12 @@ sub redirect_server_call {
}
my $req = HTTP::Request->new($data->{method} => $data->{url});
$req->header(%{$self->get_basic_authorization($self->params->{credentials})});
if ($data->{token}) {
$req->header(token => $data->{token});
}
else {
$req->header(%{$self->get_basic_authorization($self->params->{credentials})});
}
if (defined $data->{body}) {
$req->content(encode_json($data->{body}));
$req->content_type('application/json');

Loading…
Cancel
Save