use strict;
use warnings;
{
package My::Serializer::Custom;
use Moo;
extends 'Role::REST::Client::Serializer';
sub _set_serializer {
my $s = Data::Serializer::Raw->new(
serializer => 'XML::Simple',
options => { RootName => 'object' } );
return $s;
}
has '+serializer' => ( default => \&_set_serializer, );
1;
}
package NGCP::Panel::Utils::DialogicImg;
use Moo;
use Types::Standard qw(Int HashRef);
use HTTP::Tiny;
with 'Role::REST::Client';
has '+type' => ( default => 'application/xml', is => 'rw' );
# has '+serializer_options' => (default => sub {
# my $s = Data::Serializer::Raw->new(
# serializer => 'XML::Simple',
# options => { RootName => 'object' } );
# return { serializer => $s };
# });
has '+serializer_class' =>
( is => 'rw', default => sub {'My::Serializer::Custom'} );
has '+clientattrs' => ( default => sub {
return {timeout => 5};
});
has 'appid' => ( is => 'rw', isa => Int, default => 0 );
has 'pids' => (
is => 'rw',
isa => HashRef,
default => sub {
return {
root => 10_000,
bn2020 => 10_001,
};
} );
has 'classinfo' => ( is => 'ro', isa => HashRef, default => sub{
return {
bn2020 => { # dummy, not used by _create_generic
name => 'Node',
parent => 'root',
revalidate => 0,
},
network => {
name => 'NetworkInterfaces',
parent => 'bn2020',
revalidate => 0,
},
interface_collection => {
name => 'NetworkLogicalInterfaces',
parent => 'network',
revalidate => 0,
},
interface => {
name => 'NetworkLogicalInterface',
parent => 'interface_collection',
revalidate => 0,
},
ip_address => {
name => 'NetworkInterface',
parent => 'interface',
revalidate => 1,
},
facility => {
name => 'Facility',
parent => 'bn2020',
revalidate => 0,
},
packet_facility_collection => {
name => 'PacketFacilities',
parent => 'facility',
revalidate => 0,
},
packet_facility => {
name => 'PacketFacility',
parent => 'packet_facility_collection',
revalidate => 0,
},
signaling => {
name => 'Signaling',
parent => 'bn2020',
revalidate => 0,
},
sip => {
name => 'SIP',
parent => 'signaling',
revalidate => 0,
},
sip_ip => {
name => 'SIPIP',
parent => 'sip',
revalidate => 0,
},
profile_collection => {
name => 'Profiles',
parent => 'root',
revalidate => 0,
},
ip_profile_collection => {
name => 'IPProfiles',
parent => 'profile_collection',
revalidate => 0,
},
ip_profile => {
name => 'IPProfile',
parent => 'ip_profile_collection',
revalidate => 1,
},
# ...
vocoder_profile => {
name => 'VocoderProfile',
parent => 'ip_profile',
revalidate => 1,
},
sip_profile_collection => {
name => 'SIPProfiles',
parent => 'profile_collection',
revalidate => 0,
},
sip_profile => {
name => 'SIPSGP',
parent => 'sip_profile_collection',
revalidate => 0,
},
external_network_elements => {
name => 'ExternalNetworkElements',
parent => 'root',
revalidate => 0,
},
external_gateway_collection => {
name => 'ExternalGateways',
parent => 'external_network_elements',
revalidate => 0,
},
external_gateway => {
name => 'ExternalGateway',
parent => 'external_gateway_collection',
revalidate => 1,
},
routing_configuration => {
name => 'RoutingConfiguration',
parent => 'root',
revalidate => 0,
},
channel_group_collection => {
name => 'ChannelGroups',
parent => 'routing_configuration',
revalidate => 0,
},
channel_group => {
name => 'ChannelGroup',
parent => 'channel_group_collection',
revalidate => 0,
},
route_table_collection => {
name => 'RoutingTables',
parent => 'routing_configuration',
revalidate => 0,
},
route_table => {
name => 'RouteTable',
parent => 'route_table_collection',
revalidate => 1,
},
route_element => {
name => 'RouteElement',
parent => 'route_table',
revalidate => 1,
},
cg_network_element => {
name => 'NetworkElement',
parent => 'channel_group',
revalidate => 0,
},
node_association => {
name => 'NodeAssociation',
parent => 'cg_network_element',
revalidate => 0,
},
};
});
# returns appid or 0
sub login {
my ( $self, $username, $password ) = @_;
my $resp = $self->post(
'/oamp/user_management/users/logged_in?appid=0',
{ user => { name => $username, password => $password } } );
my $code = $resp->code;
my $data = $resp->data;
if ( $code == 200 ) {
$self->appid( $data->{appid} );
return $self->appid;
}
return 0;
}
sub obtain_lock {
my ($self) = @_;
$self->set_header( 'Content-Length' => '0', );
my $resp = $self->delete(
'/oamp/user_management/locks?appid=' . $self->appid );
$resp = $self->post( '/oamp/user_management/locks?appid=' . $self->appid,
{} );
return $resp;
}
###### CREATE methods ######
sub create_bn2020 {
my ($self) = @_;
my $data = $self->objects->{bn2020};
my $resp = $self->post(
'/oamp/configuration/objects/Node/NULL?pid=10000&appid='
. $self->appid,
$data,
);
if ( $resp->code == 200 ) {
$self->pids->{bn2020} = $resp->data->{oid};
}
return $resp;
}
sub create_network {
my ($self) = @_;
return $self->_create_generic(undef, 'network');
}
sub create_interface_collection {
my ($self) = @_;
return $self->_create_generic(undef, 'interface_collection');
}
sub create_interface {
my ($self) = @_;
my $resp = $self->_create_generic(undef, 'interface');
if ( $resp->code == 200 ) {
if ( $resp->data->{property}{Interface}{value} eq "Control" ) {
$self->pids->{interface_control} = $resp->data->{oid};
} elsif ( $resp->data->{property}{Interface}{value} eq "Data A" ) {
$self->pids->{interface_dataa} = $resp->data->{oid};
}
}
return $resp;
}
sub create_ip_address {
my ( $self, $options ) = @_;
if ( defined $options->{NIIPAddress} && !defined $options->{NIIPGateway} )
{
$options->{NIIPGateway} = $options->{NIIPAddress} =~ s/\.[0-9]+$/.1/r;
}
return $self->_create_generic($options, 'ip_address');
}
sub create_facility {
my ($self) = @_;
return $self->_create_generic(undef, 'facility');
}
sub create_packet_facility_collection {
my ($self) = @_;
return $self->_create_generic(undef, 'packet_facility_collection');
}
sub create_packet_facility {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'packet_facility');
}
sub create_signaling {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'signaling');
}
sub create_sip {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'sip');
}
sub create_sip_ip {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'sip_ip');
}
sub create_profile_collection {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'profile_collection');
}
sub create_ip_profile_collection {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'ip_profile_collection');
}
sub create_ip_profile {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'ip_profile');
}
sub create_vocoder_profile {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'vocoder_profile');
}
sub create_sip_profile_collection {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'sip_profile_collection');
}
sub create_sip_profile {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'sip_profile');
}
sub create_external_network_elements {
my ( $self ) = @_;
return $self->_create_generic(undef, 'external_network_elements');
}
sub create_external_gateway_collection {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'external_gateway_collection');
}
sub create_external_gateway {
my ( $self, $options ) = @_;
return $self->_create_generic($options, 'external_gateway');
}
sub create_routing_configuration {
my ($self) = @_;
return $self->_create_generic(undef, 'routing_configuration');
}
sub create_channel_group_collection {
my ($self) = @_;
return $self->_create_generic(undef, 'channel_group_collection');
}
sub create_channel_group {
my ($self, $options) = @_;
return $self->_create_generic($options, 'channel_group');
}
sub create_route_table_collection {
my ($self, $options) = @_;
return $self->_create_generic($options, 'route_table_collection');
}
sub create_route_table {
my ($self, $options) = @_;
return $self->_create_generic($options, 'route_table');
}
sub create_route_element {
my ($self, $options) = @_;
return $self->_create_generic($options, 'route_element');
}
sub create_cg_network_element {
my ($self, $options) = @_;
return $self->_create_generic($options, 'cg_network_element');
}
sub create_node_association {
my ($self, $options) = @_;
return $self->_create_generic($options, 'node_association');
}
sub _create_generic {
my ($self, $options, $class) = @_;
my $classinfo = $self->classinfo->{$class};
my $pid = $self->pids->{$classinfo->{parent}};
unless ($pid) {
warn "$class: no valid pid available\n";
return; # TODO wrong format
}
my $classname = $classinfo->{name};
my $appid = $self->appid;
my $resp = $self->get(
"/oamp/configuration/objects/$classname/NULL?detaillevel=4&pid=$pid&appid=$appid",
);
if ( $resp->code != 200 ) {
warn "$class: Failed to fetch resource\n";
return $resp;
}
if ($classinfo->{revalidate}) {
my $validation_data = $self->_build_validation_data( $resp->data, $pid, $options );
$resp = $self->get(
"/oamp/configuration/objects/$classname/NULL",
$validation_data,
);
if ( $resp->code != 200 ) {
warn "$class: Failed to fetch resource second time (revalidate)\n";
return $resp;
}
}
my $new_data = $self->_build_response_data( $resp->data, $pid, $options );
$resp = $self->post(
"/oamp/configuration/objects/$classname/NULL?pid=$pid&appid=$appid",
$new_data,
);
if ( $resp->code == 200 ) {
$self->pids->{$class} = $resp->data->{oid};
}
return $resp;
}
# log: 0: none, 1: short, 2: everything
sub create_all_sipsip {
my ($self, $settings, $log) = @_;
$self->_create_indent;
my $resp = $self->create_bn2020;
my $schedule = [
{name => 'network', options => undef},
{name => 'interface_collection', options => undef},
{name => 'interface', options => undef},
{name => 'ip_address', options => {
NIIPAddress => $settings->{ip1},
NIIPPhy => 'Services',
}},
{name => 'interface', options => undef},
{name => 'ip_address', options => {
NIIPAddress => $settings->{ip2},
NIIPPhy => 'Media 0',
}},
{name => 'facility', options => undef},
{name => 'packet_facility_collection', options => undef},
{name => 'packet_facility', options => {
ChannelCount => 50,
}},
{name => 'signaling', options => undef},
{name => 'sip', options => undef},
{name => 'sip_ip', options => {
IPAddress => $settings->{ip1},
}},
{name => 'profile_collection', options => undef},
{name => 'ip_profile_collection', options => undef},
{name => 'ip_profile', options => {
DigitRelay => 'DTMF Packetized',
Name => 'ngcp_in_profile',
}},
{name => 'vocoder_profile', options => {
PayloadType => 'G711 ulaw',
}},
{name => 'vocoder_profile', options => {
PayloadType => 'G711 alaw',
}},
{name => 'sip_profile_collection', options => undef},
{name => 'sip_profile', options => undef},
#{run => 'download_profiles'},
{name => 'external_network_elements', options => undef},
{name => 'external_gateway_collection', options => undef},
{name => 'external_gateway', options => {
Name => 'Phone1',
IPAddress => $settings->{ip_client},
IPAddress4 => $settings->{ip_client},
}},
{name => 'routing_configuration', options => undef},
{name => 'channel_group_collection', options => undef},
{name => 'route_table_collection', options => undef},
{name => 'route_table', options => {
Name => 'ngcp_route_table',
}},
{name => 'channel_group', options => {
SignalingType => 'SIP',
InRouteTable => 'ngcp_route_table - ID: 5',
InIPProfile => 'ngcp_in_profile',
InIPProfileId => '1',
OutIPProfile => 'ngcp_in_profile', # separate one for out?
SupportA2F => 'True',
}},
{name => 'cg_network_element', options => undef},
{name => 'node_association', options => undef},
{name => 'route_element', options => {
StringType => 'Channel Group',
InChannelGroup => 'ChannelGroup0',
RouteActionType => 'Channel Group',
RouteActionList => 'ChannelGroup0',
}},
#{run => 'download_route_table'},
#{run => 'download_channel_groups'},
];
for my $elem (@{ $schedule }) {
my ($name, $options) = @{ $elem }{('name', 'options')};
my $fun = "create_$name";
$resp = $self->$fun($options);
# $resp = $self->_create_generic($options, $name);
if ($log >= 1) {
my $ind = " " x ($self->classinfo->{$name}{indent}*4);
printf "%-37s: %d\n", "$ind$name", $resp->code;
if ($resp->code != 200) {
use DDP; p $resp->data;
}
}
}
$self->download_profiles;
$self->download_route_table;
$self->download_channel_groups;
return 0;
}
###### OTHER STUFF ######
sub _create_indent {
my ($self, @class) = @_;
my $classinfo = $self->classinfo;
@class = keys %{ $classinfo } unless @class;
for my $class (@class) {
next if ($classinfo->{$class}{indent});
my $parent = $classinfo->{$class}{parent};
if ($parent eq 'root') {
$classinfo->{$class}{indent} = 0;
} else {
$self->_create_indent($parent);
$classinfo->{$class}{indent} = $classinfo->{$parent}{indent} + 1;
}
}
return;
}
sub download_profiles {
my ($self) = @_;
my $appid = $self->appid;
my $pid = $self->pids->{profile_collection};
$self->set_header( 'Content-Length' => '0', );
my $resp = $self->put(
"/oamp/configuration/objects/Profiles/$pid/provisions/Cached?appid=$appid&sync_key=0",
);
return $resp;
}
sub download_route_table {
my ($self) = @_;
my $appid = $self->appid;
my $pid = $self->pids->{route_table};
$self->set_header( 'Content-Length' => '0', );
my $resp = $self->put(
"/oamp/configuration/objects/RouteTable/$pid/provisions/Cached?appid=$appid&sync_key=0",
);
return $resp;
}
sub download_channel_groups {
my ($self) = @_;
my $appid = $self->appid;
my $pid = $self->pids->{channel_group_collection};
$self->set_header( 'Content-Length' => '0', );
my $resp = $self->put(
"/oamp/configuration/objects/ChannelGroups/$pid/provisions/Cached?appid=$appid&sync_key=0",
);
return $resp;
}
#delete all children of bn2020 but not the node itself
sub delete_all_bn2020 {
my ($self) = @_;
my $pid = $self->pids->{bn2020};
my $appid = $self->appid;
my $resp = $self->get(
"/oamp/configuration/objects?appid=$appid",
);
if ($resp->code != 200) {
warn "failed to get objects\n";
return $resp;
}
my $root = $resp->data->{object}[0]; #bn2020
for my $ch (_get_all_children($root)) {
$self->_recursive_delete($ch);
}
return $resp;
}
sub _get_all_children {
my $node = shift;
if (defined $node->{object}) {
if ("HASH" eq ref $node->{object}) {
return ($node->{object},);
}
elsif ("ARRAY" eq ref $node->{object}) {
return @{ $node->{object} };
}
}
return ();
}
sub _recursive_delete {
my ($self, $node) = @_;
my $classname = $node->{classname};
my $oid = $node->{oid};
my $appid = $self->appid;
for my $ch (_get_all_children($node)) {
$self->_recursive_delete($ch);
}
return if $classname eq "Node"; # don't delete the bn2020 object (causes reboot)
my $path = "/oamp/configuration/objects/$classname/$oid?appid=$appid&sync_key=0";
$self->set_header( 'Content-Length' => '0', );
my $resp = $self->delete($path);
#use DDP; p $path; p $resp->code;
return;
}
sub reboot_and_wait {
my ($self) = @_;
my $appid = $self->appid;
$self->set_header( 'Content-Length' => '0', );
# my $resp = $self->delete("/oamp/configuration/database/new?appid=$appid",
# '');
my $resp = $self->_request_with_body("DELETE",
"/oamp/configuration/database/new?appid=$appid",
'');
if ($resp->code != 200) {
warn "failed to reset config\n";
return $resp;
}
sleep 2; # not to catch the old server
for (my $i = 0; $i < 100; $i++) { # 500 seconds on 5 seconds timeout
$resp = $self->get("/");
last if $resp->code < 500;
}
return $resp;
}
# warning: does create a lot of open transactions without deleting them.
# see the scriptfile for an example how to generate documentation of this
sub build_documentation {
my ($self) = @_;
my $classinfo = $self->classinfo;
for my $class (keys %{ $classinfo }) {
my $classname = $classinfo->{$class}{name};
my $appid = $self->appid;
my $pid = 10_000;
my $resp = $self->get(
"/oamp/configuration/objects/$classname/NULL?detaillevel=4&pid=$pid&appid=$appid",
);
if ($resp->code != 200) {
warn "$class: couldn't fetch info\n";
next;
}
my $data = $resp->data;
my $options = [];
for my $p ( keys %{ $data->{property} } ) {
next if lc($data->{property}{$p}{type}) ne "configure";
my @choices;
if ($data->{property}{$p}{choiceset}{choice} &&
ref $data->{property}{$p}{choiceset}{choice} eq "ARRAY") {
for my $v (@{ $data->{property}{$p}{choiceset}{choice} }) {
push @choices, $v->{value} =~ s/^value\((.*)\)$/$1/r;
}
}
push @{ $options }, {
name => $p,
default => $data->{property}{$p}{value},
displayname => $data->{property}{$p}{displayname},
@choices ? (choices => [@choices]) : (),
};
}
$classinfo->{$class}{options} = $options;
}
return $classinfo;
}
sub _build_response_data {
my ( $self, $req, $pid, $options ) = @_;
my $resp = {
pid => $pid,
property => {},
};
for my $p ( keys %{ $req->{property} } ) {
next
if lc($req->{property}{$p}{type}) ne "configure";
$resp->{property}{$p}
= { configuredvalue => $req->{property}{$p}{value} };
if ( defined $options->{$p} ) {
$resp->{property}{$p}{configuredvalue} = $options->{$p};
}
}
return $resp;
}
sub _build_validation_data {
my ( $self, $req, $pid, $options ) = @_;
my $response_data = $self->_build_response_data($req, $pid, $options);
my @changedopt = ();
if ("HASH" eq ref $options && keys %{ $options }) {
@changedopt = (
changedproperty => (keys %{ $options })[0],
);
}
my $resp = {
pid => $pid,
appid => $self->appid,
@changedopt,
};
for my $prop (keys %{ $response_data->{property} }) {
$resp->{$prop} = $response_data->{property}{$prop}{configuredvalue};
}
return $resp;
}
sub _build_user_agent { return HTTP::Tiny->new; }
sub objects {
return {
bn2020 => {
'property' => {
'IPType' => { 'configuredvalue' => 'IPv4' },
'_position_' => { 'configuredvalue' => '' },
'_localDbSyncKey_' => { 'configuredvalue' => '0' },
'PacketAudioChannels' => { 'configuredvalue' => 'Unknown' },
'CFCMode' => { 'configuredvalue' => 'Unknown' },
'srtpEnable' => { 'configuredvalue' => 'Disabled' },
'InterfaceType' => { 'configuredvalue' => 'Unknown' },
'MediaMode' => { 'configuredvalue' => 'Audio Dynamic Density Management' },
'SwVersion' => { 'configuredvalue' => 'Unknown' },
'SubNetMask' => { 'configuredvalue' => '' },
'Name' => { 'configuredvalue' => 'Node0' },
'ResendLogic' => { 'configuredvalue' => 'Disable' },
'PacketMultimediaChannels' =>
{ 'configuredvalue' => 'Unknown' },
'NetworkMultimediaChannels' =>
{ 'configuredvalue' => 'Unknown' },
'IPAddress' => { 'configuredvalue' => '' },
'ACLName' => { 'configuredvalue' => 'Unrestricted' },
'NodeType' => { 'configuredvalue' => 'Unknown' },
'ACL_ID' => { 'configuredvalue' => '0' },
'IPChannels' => { 'configuredvalue' => 'Unknown' },
'ID' => { 'configuredvalue' => '0' },
'_objectName_' =>
{ 'configuredvalue' => 'BN2020: Node0 - ID: 0' },
'LicIPChannels' => { 'configuredvalue' => '0' },
},
'pid' => '10000',
},
};
}
1;
# vim: set tabstop=4 expandtab: