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', # KeyAttr => { 'object' => '+classname' }, # ForceArray => ['IPProfile'], } ); return $s; } has '+serializer' => ( default => \&_set_serializer, ); 1; } { package LWP_Wrapper; use Moo; use HTTP::Request; use HTTP::Headers; extends 'LWP::UserAgent'; around request => sub { my ($next, $self, $method, $uri, $opts) = @_; my @headers; if ($opts->{headers}) { @headers = HTTP::Headers->new(%{ $opts->{headers} }); } my $req = HTTP::Request->new($method, $uri, @headers); if ($opts->{content}) { $req->content($opts->{content}); } my $res = $self->$next($req); return $res; }; } package NGCP::Panel::Utils::DialogicImg; use Moo; use Digest::MD5 qw/md5_hex/; use HTTP::Tiny; use Storable qw/freeze/; use Types::Standard qw(Int HashRef); 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, }, ds1_spans => { name => 'SpanGroup', parent => 'facility', revalidate => 0, }, ds3_optical => { name => 'DS3_Optical', parent => 'facility', revalidate => 1, }, optical_interface => { name => 'Optical_Interface', parent => 'ds3_optical', revalidate => 0, }, optical_link => { name => 'Optical_Link', parent => 'optical_interface', revalidate => 0, }, ds3_interface => { name => 'TDM_DS3', parent => 'optical_interface', revalidate => 0, }, ds1_spans_optical => { name => 'SpanGroup', # TODO: double occurence, is that acceptable? parent => 'ds3_interface', revalidate => 0, }, signaling => { name => 'Signaling', parent => 'bn2020', revalidate => 0, }, isdn => { name => 'ISDN', parent => 'signaling', revalidate => 0, }, isdn_d_chan => { name => 'ISDNDChan', parent => 'isdn', revalidate => 1, }, isdn_group => { name => 'ISDNGroup', parent => 'isdn_d_chan', revalidate => 0, }, isdn_circuit_group => { name => 'ISDNCircuitGroup', parent => 'isdn_group', revalidate => 1, }, sip => { name => 'SIP', parent => 'signaling', revalidate => 0, }, sip_ip => { name => 'SIPIP', parent => 'sip', revalidate => 0, }, call_tracing => { name => 'CallTracing', parent => 'bn2020', revalidate => 0, }, snmp_agent => { name => 'SNMPClient', parent => 'bn2020', revalidate => 0, }, ss7 => { name => 'SS7', parent => 'root', revalidate => 0, }, ss7_network => { name => 'SS7Network', parent => 'ss7', revalidate => 0, }, ss7_node_collection => { name => 'SS7Nodes', parent => 'ss7_network', revalidate => 0, }, ss7_node_primary => { name => 'SS7PrimaryNode', parent => 'ss7_node_collection', revalidate => 0, }, ss7_stack => { name => 'SS7Stack', parent => 'ss7_network', revalidate => 1, }, ss7_link_set => { name => 'SS7LinkSet', parent => 'ss7_stack', revalidate => 1, }, ss7_link => { name => 'SS7Link', parent => 'ss7_link_set', revalidate => 1, }, ss7_destination => { name => 'SS7Destination', parent => 'ss7_stack', revalidate => 1, }, ss7_route => { name => 'SS7Route', parent => 'ss7_destination', revalidate => 1, }, ss7_isup_group => { # note: needs isup profile name => 'ISUPGroup', parent => 'ss7_destination', revalidate => 1, }, ss7_circuit_group => { name => 'SS7CircuitGroup', parent => 'ss7_isup_group', revalidate => 1, }, 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 => 1, }, sip_headers => { name => 'SIPHeaders', parent => 'sip_profile', revalidate => 0, }, tdm_profile_collection => { name => 'TDMProfiles', parent => 'profile_collection', revalidate => 0, }, e1_profile => { name => 'E1Profile', parent => 'tdm_profile_collection', revalidate => 0, }, isup_profile_collection => { name => 'ISUPProfiles', parent => 'profile_collection', revalidate => 0, }, isup_itu_profile => { name => 'ISUP_ITUProfile', parent => 'isup_profile_collection', revalidate => 0, }, isup_ansi_profile => { name => 'ISUP_ANSIProfile', parent => 'isup_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, }, external_nfsserver_collection => { name => 'NFSServers', parent => 'external_network_elements', revalidate => 0, }, external_nfsserver => { name => 'NFSServer', parent => 'external_nfsserver_collection', revalidate => 1, }, external_snmpmanager_collection => { name => 'SNMPServers', parent => 'external_network_elements', revalidate => 0, }, external_snmpmanager => { name => 'SNMPServer', parent => 'external_snmpmanager_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 => 1, }, 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, }, cg_isdn_circuit_group => { name => 'SSLCircuitGroup', parent => 'channel_group', 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 } } ); if ( $resp->code == 200 ) { my $data = $resp->data; $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_ds1_spans { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ds1_spans'); } sub create_ds3_optical { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ds3_optical'); } sub create_optical_interface { my ( $self, $options ) = @_; return $self->_create_generic($options, 'optical_interface'); } sub create_optical_link { my ( $self, $options ) = @_; return $self->_create_generic($options, 'optical_link'); } sub create_ds3_interface { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ds3_interface'); } sub create_ds1_spans_optical { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ds1_spans_optical'); } sub create_signaling { my ( $self, $options ) = @_; return $self->_create_generic($options, 'signaling'); } sub create_isdn { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isdn'); } sub create_isdn_d_chan { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isdn_d_chan'); } sub create_isdn_group { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isdn_group'); } sub create_isdn_circuit_group { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isdn_circuit_group'); } 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_call_tracing { my ( $self, $options ) = @_; return $self->_create_generic($options, 'call_tracing'); } sub create_snmp_agent { my ( $self, $options ) = @_; return $self->_create_generic($options, 'snmp_agent'); } sub create_ss7 { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7'); } sub create_ss7_network { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_network'); } sub create_ss7_node_collection { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_node_collection'); } sub create_ss7_node_primary { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_node_primary'); } sub create_ss7_stack { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_stack'); } sub create_ss7_link_set { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_link_set'); } sub create_ss7_link { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_link'); } sub create_ss7_destination { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_destination'); } sub create_ss7_route { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_route'); } sub create_ss7_isup_group { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_isup_group'); } sub create_ss7_circuit_group { my ( $self, $options ) = @_; return $self->_create_generic($options, 'ss7_circuit_group'); } 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_sip_headers { my ( $self, $options ) = @_; return $self->_create_generic($options, 'sip_headers'); } sub create_tdm_profile_collection { my ( $self, $options ) = @_; return $self->_create_generic($options, 'tdm_profile_collection'); } sub create_e1_profile { my ( $self, $options ) = @_; return $self->_create_generic($options, 'e1_profile'); } sub create_isup_profile_collection { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isup_profile_collection'); } sub create_isup_itu_profile { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isup_itu_profile'); } sub create_isup_ansi_profile { my ( $self, $options ) = @_; return $self->_create_generic($options, 'isup_ansi_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_external_nfsserver_collection { my ( $self, $options ) = @_; return $self->_create_generic($options, 'external_nfsserver_collection'); } sub create_external_nfsserver { my ( $self, $options ) = @_; return $self->_create_generic($options, 'external_nfsserver'); } sub create_external_snmpmanager_collection { my ( $self, $options ) = @_; return $self->_create_generic($options, 'external_snmpmanager_collection'); } sub create_external_snmpmanager { my ( $self, $options ) = @_; return $self->_create_generic($options, 'external_snmpmanager'); } 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_cg_isdn_circuit_group { my ( $self, $options ) = @_; return $self->_create_generic($options, 'cg_isdn_circuit_group'); } 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 # necessary keys: ip_sip, ip_rtp, ip_client, out_codecs # optional: in_codecs, ip_sip_subnet, ip_rtp_subnet # for nfs: ip_nfs_server, nfs_path # for snmp: snmp_system_name, snmp_system_location, snmp_system_contact, snmp_community_name # for snmp optional: ip_snmp_manager, snmp_version # The management IP, the SIP IP and the RTP IP have to be in different subnets sub create_general_part_schedule { my ($self, $settings, $log) = @_; $self->_create_indent; my $in_codecs = ['G711 ulaw', 'G711 alaw', 'G729', 'AMR', 'AMR Bandwidth Efficient', 'AMR-WB', 'AMR-WB Bandwidth Efficient', 'Clear Channel', 'G723 5.3 Kbps', 'G723 6.3 Kbps', 'G722', 'iLBC 30ms', 'GSM-FR Static Payload Type', 'GSM-FR Dynamic Payload Type', 'G726-32/G721 Static Payload Type', 'G726-32/G721 Dynamic Payload Type', 'GSM-EFR']; my $resp = $self->create_bn2020; my @in_schedule = map { { name => 'vocoder_profile', options => { PayloadType => $_ }, }; } @{ $settings->{in_codecs} // $in_codecs }; my @out_schedule = map { { name => 'vocoder_profile', options => { PayloadType => $_ }, }; } @{ $settings->{out_codecs} }; my @nfs_schedule; if (defined $settings->{ip_nfs_server} && defined $settings->{nfs_path}) { @nfs_schedule = ( {name => 'external_nfsserver_collection', options => undef}, # needs external_network_elements {name => 'external_nfsserver', options => { Name => 'ngcp_nfs_server', IPAddress => $settings->{ip_nfs_server}, }}, {name => 'call_tracing', options => { TraceTime => '600', MountDirectory => $settings->{nfs_path}, }}, ); } my @snmp_schedule; if (defined $settings->{snmp_system_name} && defined $settings->{snmp_system_location} && defined $settings->{snmp_system_contact} && defined $settings->{snmp_community_name}) { @snmp_schedule = ( {name => 'external_snmpmanager_collection', options => undef}, {name => 'external_snmpmanager', options => { UserName => 'ngcp', CommunityName => $settings->{snmp_community_name}, ServerIPAddress => $settings->{ip_snmp_manager} // $settings->{ip_client}, defined $settings->{snmp_version} ? (ServerVersion => $settings->{snmp_version} ) : (), }}, {name => 'snmp_agent', options => { SystemName => $settings->{snmp_system_name}, SystemLoc => $settings->{snmp_system_location}, SystemContact => $settings->{snmp_system_contact}, }}, ); } my $schedule = [ {name => 'network', options => undef}, {name => 'interface_collection', options => undef}, {name => 'interface', options => undef}, # Control {name => 'interface', options => undef}, # Data A {name => 'ip_address', options => { NIIPAddress => $settings->{ip_sip}, NIIPPhy => 'Services', NIIPSubnet => $settings->{ip_sip_subnet} || '24', }}, {name => 'interface', options => undef}, # Data B {name => 'ip_address', options => { NIIPAddress => $settings->{ip_rtp}, NIIPPhy => 'Media 0', NIIPSubnet => $settings->{ip_rtp_subnet} || '24', }}, {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->{ip_sip}, }}, {name => 'profile_collection', options => undef}, {name => 'ip_profile_collection', options => undef}, {name => 'ip_profile', options => { DigitRelay => 'DTMF Packetized', Name => 'ngcp_in_profile', }}, @in_schedule, {name => 'ip_profile', options => { DigitRelay => 'DTMF Packetized', Name => 'ngcp_out_profile', }}, @out_schedule, {name => 'sip_profile_collection', options => undef}, {name => 'sip_profile', options => undef}, {name => 'sip_profile', options => { ProfileName => 'SIP-profile-ngcp', }}, {name => 'sip_headers', options => { UnsupportedHeader => 'Enabled', }}, #{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}, SGP => 'ID: 1 - SIP-profile-ngcp', SGPId => '1', }}, @nfs_schedule, @snmp_schedule, ]; return $schedule; } # log: 0: none, 1: short, 2: everything # necessary keys: ip_sip, ip_rtp, ip_client, out_codecs, optional: in_codecs sub create_all_sipsip { my ($self, $settings, $log) = @_; my $general_schedule = $self->create_general_part_schedule($settings, $log); my $schedule = [ @{ $general_schedule }, {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_out_profile', SupportA2F => 'True', Name => 'cg_ngcp_sipclient', }}, {name => 'cg_network_element', options => undef}, {name => 'node_association', options => undef}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_sipclient', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_sipclient', }}, #{run => 'download_route_table'}, #{run => 'download_channel_groups'}, ]; $self->_run_schedule($schedule, $log); $self->download_profiles; $self->download_route_table; $self->download_channel_groups; return 0; } # log: 0: none, 1: short # necessary keys: ip_sip, ip_rtp, ip_client, out_codecs, optional: in_codecs # optional: use_optical_spans (bool), is_isdn_userside (bool) sub create_all_sipisdn { my ($self, $settings, $log) = @_; my $general_schedule = $self->create_general_part_schedule($settings, $log); my @ds1_spans; if ($settings->{use_optical_spans}) { @ds1_spans = ( {name => 'ds3_optical', options => { FacilityType => 'Optical', }}, {name => 'optical_interface', options => undef}, {name => 'optical_link', options => undef}, {name => 'ds3_interface', options => undef}, {name => 'ds1_spans_optical', options => undef}, ); } else { @ds1_spans = ( {name => 'ds1_spans', options => { EndingOffset => '3', }}, ); } my $resp; my $schedule = [ @{ $general_schedule }, {name => 'isdn', options => undef}, {name => 'tdm_profile_collection', options => undef}, {name => 'e1_profile', options => undef}, @ds1_spans, {name => 'isdn_d_chan', options => $settings->{is_isdn_userside} ? { BaseVariant => 'Euro-ISDN User Side', BChanSelection => 'Linear Counter Clockwise', } : undef, }, {name => 'isdn_group', options => undef}, {name => 'isdn_circuit_group', options => { EndChannel => 'Span ID: 0 CID: 30', }}, {name => 'isdn_d_chan', options => $settings->{is_isdn_userside} ? undef : { BaseVariant => 'Euro-ISDN User Side', BChanSelection => 'Linear Counter Clockwise', }, }, {name => 'isdn_group', options => undef}, {name => 'isdn_circuit_group', options => { EndChannel => 'Span ID: 1 CID: 30', }}, #{run => 'download_profiles'}, {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_out_profile', SupportA2F => 'True', Name => 'cg_ngcp_sipclient', }}, {name => 'cg_network_element', options => undef}, {name => 'node_association', options => undef}, {name => 'channel_group', options => { SignalingType => 'ISDN', InRouteTable => 'ngcp_route_table - ID: 5', SupportA2F => 'True', Name => 'cg_ngcp_isdn_1', }}, {name => 'cg_isdn_circuit_group', options => undef}, {name => 'channel_group', options => { SignalingType => 'ISDN', InRouteTable => 'ngcp_route_table - ID: 5', SupportA2F => 'True', Name => 'cg_ngcp_isdn_2', }}, {name => 'cg_isdn_circuit_group', options => undef}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_sipclient', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_isdn_1', }}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_isdn_1', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_sipclient', }}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_isdn_2', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_sipclient', }}, #{run => 'download_route_table'}, #{run => 'download_channel_groups'}, ]; $self->_run_schedule($schedule, $log); $self->download_profiles; $self->download_route_table; $self->download_channel_groups; return 0; } # log: 0: none, 1: short # necessary keys: ip_sip, ip_rtp, ip_client, out_codecs, ss7_opc, ss7_apc, ss7_dpc # optionalkeys: in_codecs # optional: use_optical_spans (bool) sub create_all_sipss7 { my ($self, $settings, $log) = @_; my $general_schedule = $self->create_general_part_schedule($settings, $log); my @ds1_spans; if ($settings->{use_optical_spans}) { @ds1_spans = ( {name => 'ds3_optical', options => { FacilityType => 'Optical', }}, {name => 'optical_interface', options => undef}, {name => 'optical_link', options => undef}, {name => 'ds3_interface', options => undef}, {name => 'ds1_spans_optical', options => undef}, ); } else { @ds1_spans = ( {name => 'ds1_spans', options => { EndingOffset => '3', }}, ); } my $schedule = [ @{ $general_schedule }, {name => 'tdm_profile_collection', options => undef}, {name => 'e1_profile', options => undef}, {name => 'isup_profile_collection', options => undef}, {name => 'isup_itu_profile', options => undef}, @ds1_spans, #{run => 'download_profiles'}, {name => 'ss7', options => undef}, {name => 'ss7_network', options => undef}, {name => 'ss7_node_collection', options => undef}, {name => 'ss7_node_primary', options => undef}, {name => 'ss7_stack', options => { OPC => $settings->{ss7_opc}, }}, {name => 'ss7_link_set', options => { APC => $settings->{ss7_apc}, }}, {name => 'ss7_link', options => undef}, {name => 'ss7_destination', options => { DPC => $settings->{ss7_dpc}, }}, {name => 'ss7_route', options => { Linkset => 'SS7 Link Set APC: ' . $settings->{ss7_apc}, }}, {name => 'ss7_isup_group', options => undef}, {name => 'ss7_circuit_group', options => { StartCIC => '1', }}, {name => 'ss7_stack', options => { # for loopback purposes OPC => $settings->{ss7_dpc}, }}, {name => 'ss7_link_set', options => { APC => $settings->{ss7_opc}, }}, {name => 'ss7_link', options => undef}, {name => 'ss7_destination', options => { DPC => $settings->{ss7_opc}, }}, {name => 'ss7_route', options => { Linkset => 'SS7 Link Set APC: ' . $settings->{ss7_opc}, }}, {name => 'ss7_isup_group', options => undef}, {name => 'ss7_circuit_group', options => { StartCIC => '1', }}, {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_out_profile', SupportA2F => 'True', Name => 'cg_ngcp_sipclient', }}, {name => 'cg_network_element', options => undef}, {name => 'node_association', options => undef}, {name => 'channel_group', options => { SignalingType => 'SS7_ISUP', InRouteTable => 'ngcp_route_table - ID: 5', SupportA2F => 'True', Name => 'cg_ngcp_ss7_1', }}, {name => 'cg_isdn_circuit_group', options => undef}, {name => 'channel_group', options => { SignalingType => 'SS7_ISUP', InRouteTable => 'ngcp_route_table - ID: 5', SupportA2F => 'True', Name => 'cg_ngcp_ss7_2', }}, {name => 'cg_isdn_circuit_group', options => undef}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_sipclient', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_ss7_1', }}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_ss7_1', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_sipclient', }}, {name => 'route_element', options => { StringType => 'Channel Group', InChannelGroup => 'cg_ngcp_ss7_2', RouteActionType => 'Channel Group', RouteActionList => 'cg_ngcp_sipclient', }}, #{run => 'download_route_table'}, #{run => 'download_channel_groups'}, ]; $self->_run_schedule($schedule, $log); $self->download_profiles; $self->download_route_table; $self->download_channel_groups; return 0; } ###### OTHER STUFF ###### sub _run_schedule { my ($self, $schedule, $log) = @_; my $resp; my $ntotal = $#{$schedule}; my $i = 0; for my $elem (@{ $schedule }) { if (exists $elem->{run}) { my $command = $elem->{run}; $self->$command; next; } my ($name, $options) = @{ $elem }{('name', 'options')}; my $fun = "create_$name"; $resp = $self->$fun($options); # $resp = $self->_create_generic($options, $name); if ($log) { my $ind = " " x ($self->classinfo->{$name}{indent}*4); my $percent = (100 * $i++) / $ntotal; my $shortline = sprintf "%3d%% - %-37s: %d\n", $percent, "$ind$name", $resp->code; if ('CODE' eq ref $log) { &{ $log }($shortline, $resp); } elsif ($log >= 1) { print $shortline; if ($resp->code != 200) { use DDP; p $resp->data; } } } } return 0; } sub hash_config { my ($self, $config) = @_; $Storable::canonical = 1; return md5_hex(freeze $config); } sub get_config { my ($self) = @_; my $appid = $self->appid; my $resp = $self->get("/oamp/configuration/objects?appid=$appid"); my $config = {}; if ($resp->code != 200) { warn "failed to get objects\n"; return $resp; } my $classinfo = $self->classinfo; my $rev_lookup = {}; @{$rev_lookup}{map {$_->{name}} values %{$classinfo}} = keys %{$classinfo}; my $root = $resp->data->{object}[0]; for my $ch (_get_all_children($root)) { $self->_recursive_get($ch, $rev_lookup); } if ($classinfo->{ip_address}{configuredids}[0]) { ($config->{ip1}) = $self->_get_specifics( 'ip_address', $classinfo->{ip_address}{configuredids}[0], ['NIIPAddress']); } if ($classinfo->{ip_address}{configuredids}[1]) { ($config->{ip2}) = $self->_get_specifics( 'ip_address', $classinfo->{ip_address}{configuredids}[1], ['NIIPAddress']); } if ($classinfo->{external_gateway}{configuredids}[0]) { ($config->{ip_client}) = $self->_get_specifics( 'external_gateway', $classinfo->{external_gateway}{configuredids}[0], ['IPAddress']); } if (exists $classinfo->{vocoder_profile}{configuredids}) { for my $id (@{ $classinfo->{vocoder_profile}{configuredids} }) { push @{ $config->{in_codecs} }, $self->_get_specifics( 'vocoder_profile', $id, ['PayloadType']); } } return $config; } sub _recursive_get { my ($self, $node, $rev_lookup) = @_; my $classname = $node->{classname}; my $oname = $rev_lookup->{$classname}; my $oid = $node->{oid}; my $appid = $self->appid; for my $ch (_get_all_children($node)) { $self->_recursive_get($ch, $rev_lookup); } if ($oname) { $self->pids->{$oname} = $oid; push @{ $self->classinfo->{$oname}{configuredids} }, $oid; } return; } sub _get_specifics { my ($self, $class, $id, $keys) = @_; my $appid = $self->appid; my $classinfo = $self->classinfo->{$class}; my $classname = $classinfo->{name}; my @res; my $resp = $self->get("/oamp/configuration/objects/$classname/$id?appid=$appid&detaillevel=4"); if ($resp->code != 200) { warn "failed to get objects\n"; return (); } for my $key (@{ $keys }) { push @res, $resp->data->{property}{$key}{value}; } return @res; } sub _create_indent { my ($self, @class) = @_; my $classinfo = $self->classinfo; @class = keys %{ $classinfo } unless @class; for my $class (@class) { next if (exists $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 $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 < 30; $i++) { # 600 seconds on 20 seconds timeout $resp = $self->get("/"); last if $resp->code < 400; } 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 { my $ua = LWP_Wrapper->new( ssl_opts => { verify_hostname => 0 }, timeout => 20, ); return $ua; } 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: