From 6cb44166110f01e9a336244c28d5c7acf3d12911 Mon Sep 17 00:00:00 2001 From: dzenichev Date: Tue, 19 Jul 2022 12:23:08 +0200 Subject: [PATCH] TT#168102 Create script for instances connections validation Introduce a list of checks related to connections: * check that we don't have instance names duplicates * prove an existence of a preferred for instnace host * an instance to which instance gets connected exists * a host to which instance gets connected exists * interface via which instance gets connected is defined Change-Id: I24f43b0fb24e308f571a88f30a72a9b6dd04b94d --- Makefile | 1 + debian/ngcp-ngcpcfg.install | 1 + debian/rules | 1 + sbin/ngcp-instances-validator | 220 ++++++++++++++++++++++++++++++++++ scripts/check | 4 + 5 files changed, 227 insertions(+) create mode 100755 sbin/ngcp-instances-validator diff --git a/Makefile b/Makefile index 33c54ddf..28d342af 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ PERL_SCRIPTS = \ helper/sync-db \ helper/tt2-process \ helper/validate-yml helper/fileformat_version \ + sbin/ngcp-instances-validator \ sbin/ngcp-network \ sbin/ngcp-network-validator \ sbin/ngcp-sync-constants \ diff --git a/debian/ngcp-ngcpcfg.install b/debian/ngcp-ngcpcfg.install index 113bded8..4a4d979c 100644 --- a/debian/ngcp-ngcpcfg.install +++ b/debian/ngcp-ngcpcfg.install @@ -15,6 +15,7 @@ helper/validate-yml usr/share/ngcp-ngcpcfg/helper/ hooks/ usr/share/ngcp-ngcpcfg/ lib/get_* usr/lib/ngcp-ngcpcfg/ lib/set_* usr/lib/ngcp-ngcpcfg/ +sbin/ngcp-instances-validator usr/sbin/ sbin/ngcp-network usr/sbin/ sbin/ngcp-network-validator usr/sbin/ sbin/ngcp-sync-constants usr/sbin/ diff --git a/debian/rules b/debian/rules index 071d6122..8a77c3c4 100755 --- a/debian/rules +++ b/debian/rules @@ -5,6 +5,7 @@ # export DH_VERBOSE=1 SCRIPTS = \ + $(CURDIR)/usr/sbin/ngcp-instances-validator \ $(CURDIR)/usr/sbin/ngcp-network \ $(CURDIR)/usr/sbin/ngcp-network-validator diff --git a/sbin/ngcp-instances-validator b/sbin/ngcp-instances-validator new file mode 100755 index 00000000..85a7799d --- /dev/null +++ b/sbin/ngcp-instances-validator @@ -0,0 +1,220 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use open qw(:std :encoding(UTF-8)); + +use Storable qw(dclone); +use List::Util qw(any); +use Getopt::Long qw(:config posix_default no_ignorecase auto_help auto_version); +use Pod::Usage; +use YAML::XS qw(); +use Kwalify; + +my $network_file = '/etc/ngcp-config/network.yml'; + +GetOptions( + 'n|network-file=s' => \$network_file, +) or pod2usage(1); + +my $yaml = YAML::XS::LoadFile($network_file) + or die "cannot parse file $network_file"; + +sub get_instance +{ + my ($instances, $name) = @_; + my $instance_found; + + foreach my $instance ( @{ $instances } ) { + if ($instance->{name} eq $name) { + $instance_found = $instance; + last; + } + } + + return $instance_found; +} + +sub dup_check +{ + my ($dup, $text) = @_; + my $errors = 0; + + foreach my $id (sort keys %{$dup}) { + next if scalar @{$dup->{$id}} <= 1; + + warn " - $text $id (@{$dup->{$id}})\n"; + $errors++; + } + + return $errors; +} + +# only run these checks, if we have any of instances defined +if (exists $yaml->{instances}) { + + my $errors = 0; + + my %duplicate_name_with_another_instance; + my %duplicated_connections; + + my @instances_names = (); + my @hosts_names = (); + + my $i = 0; + + # get existing instances names + foreach my $instance ( @{ $yaml->{instances} } ) + { + push(@instances_names, $instance->{name}); + } + + # get existing hosts names + foreach my $hostname (sort keys %{$yaml->{hosts}}) + { + push(@hosts_names, $hostname); + } + + # cycle through all existing instances definitions to find mistakes in connections + foreach my $instance ( @{ $yaml->{instances} } ) + { + my $instance_host = $instance->{host}; + my $j = 0; + + # just in case, catch name duplicates used for general instances names + push @{$duplicate_name_with_another_instance{"$instance->{name}"}}, "instance number: $i"; + + # iterate through the list of connections + foreach my $instance_connection ( @{ $instance->{connections} } ) + { + # catch connections names duplicates of each given instance + my $instance_connection_id = $instance->{name} . ': ' . $instance_connection->{name}; + push @{$duplicated_connections{"$instance_connection_id"}}, "con #$j: $instance_connection->{name} ;"; + + # iterate through the list of connection links + foreach my $instance_connection_link ( @{ $instance_connection->{links} } ) + { + my $instance_connection_link_name = $instance_connection_link->{name}; + + # check that each connection of type instance/host indeed exists + if ($instance_connection_link->{type} eq 'instance') { + unless ( grep { /^$instance_connection_link_name$/ } @instances_names ) { + warn " - required instance: $instance_connection_link->{name} for instance connection from: $instance->{name} does not exist\n"; + $errors ++; + } + } elsif ($instance_connection_link->{type} eq 'host') { + unless ( grep { /^$instance_connection_link_name$/ } @hosts_names ) { + warn " - required host: $instance_connection_link->{name} for instance connection from: $instance->{name} does not exist\n"; + $errors ++; + } + } + + # iterate through the list of link interfaces + foreach my $link_iface ( @{ $instance_connection_link->{interfaces} } ) + { + my $link_iface_name = $link_iface->{name}; + my $found = 0; + my $remote_instance = get_instance($yaml->{instances}, $instance_connection_link_name); + + # requested instance must indeed have requested interface, otherwise not possible to interconnect + foreach my $remote_iface ( @{ $remote_instance->{interfaces} } ) + { + if ($remote_iface->{name} eq $link_iface_name) { $found = 1; } + } + + if ($found != 1) { + warn " - required link iface: $link_iface_name for instance connection from: $instance->{name} does not exist\n"; + $errors ++; + } + } + } + + $j++; + } + + # just in case, check also that a host, on which we must run this instance, exists + unless ( grep { /^$instance_host$/ } @hosts_names ) { + warn " - required host: $instance->{host} for instance: $instance->{name} does not exist\n"; + $errors ++; + } + + $i++; + } + + # check duplicates and raise an error if found + $errors += dup_check(\%duplicate_name_with_another_instance, '[/instances/] Duplicate instance name with an existing instance'); + $errors += dup_check(\%duplicated_connections, '[/instances/] Duplicate connetion name'); + + exit 1 if $errors; +} + +1; + +# vim: ts=4 sw=4 et + +__END__ + +=encoding utf-8 + +=head1 NAME + +ngcp-instances-validator - dynamically validates the instances connections, if instances are defined. + +=head1 SYNOPSIS + +B + +=head1 DESCRIPTION + +B iterates through currently existing instances +defined in the network.yml with an intention of checking their +connections to other instances/hosts. + +=head1 OPTIONS + +=over 8 + +=item B<-n>, B<--network-file> I + +The B file to validate. + +=item B<--help> + +Print a help message. + +=back + +=head1 EXIT STATUS + +=over 8 + +=item B<0> + +The connections are getting validated correctly. + +=item B<1> + +The connections are getting validated incorrectly. + +=back + +=head1 AUTHOR + +Donat Zenichev + +=head1 LICENSE + +Copyright © 2022 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 . diff --git a/scripts/check b/scripts/check index 7835dc0e..b9e440c4 100755 --- a/scripts/check +++ b/scripts/check @@ -168,6 +168,10 @@ validate_config() { log_error "Invalid schema detected for ${f}" rc=1 fi + if ! ngcp-instances-validator "$f"; then + log_error "Invalid schema detected for instances connections" + rc=1 + fi else local schema