diff --git a/admin/import_nodes.pl b/admin/import_nodes.pl index 550c15a..ea28ce4 100755 --- a/admin/import_nodes.pl +++ b/admin/import_nodes.pl @@ -1,7 +1,5 @@ #!/usr/bin/perl # -## $Id: import_nodes.pl,v 1.1 2012/08/13 05:09:17 keiths Exp $ -# # Copyright (C) Opmantek Limited (www.opmantek.com) # # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM @@ -88,24 +86,29 @@ sub loadNodes { my $csvfile = shift; my $nmisnodes = shift; - + print $t->markTime(). " Loading the Local Node List\n"; my $LNT = loadLocalNodeTable(); print " done in ".$t->deltaTime() ."\n"; - + print $t->markTime(). " Loading the Import Nodes from $csvfile\n"; my %newNodes = &myLoadCSV($csvfile,"name",","); print " done in ".$t->deltaTime() ."\n"; - + + my $nodenamerule = $C->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/; + print "\n"; my $sum = initSummary(); - foreach my $node (keys %newNodes) { + foreach my $node (keys %newNodes) + { + die "Invalid node name \"$node\"\n" + if ($node !~ $nodenamerule); - if ( $newNodes{$node}{name} ne "" - and $newNodes{$node}{host} ne "" - and $newNodes{$node}{role} ne "" - and $newNodes{$node}{community} ne "" - ) { + if ( $newNodes{$node}{name} ne "" + and $newNodes{$node}{host} ne "" + and $newNodes{$node}{role} ne "" + and $newNodes{$node}{community} ne "" + ) { my $nodekey = $newNodes{$node}{name}; ++$sum->{total}; @@ -118,7 +121,7 @@ sub loadNodes { print "ADDING: node=$newNodes{$node}{name} host=$newNodes{$node}{host} group=$newNodes{$node}{group}\n"; ++$sum->{add}; } - + my $roleType = $newNodes{$node}{role} || "access"; $roleType = $newNodes{$node}{roleType} if $newNodes{$node}{roleType}; @@ -130,7 +133,7 @@ sub loadNodes { $LNT->{$nodekey}{group} = $newNodes{$node}{group} || "NMIS8"; $LNT->{$nodekey}{roleType} = $newNodes{$node}{role} || "access"; $LNT->{$nodekey}{community} = $newNodes{$node}{community} || "public"; - + $LNT->{$nodekey}{businessService} = $newNodes{$node}{businessService} || ""; $LNT->{$nodekey}{serviceStatus} = $newNodes{$node}{serviceStatus} || "Production"; $LNT->{$nodekey}{location} = $newNodes{$node}{location} || "default"; @@ -181,7 +184,7 @@ sub loadNodes { else { print "ERROR: no file to save to provided\n"; } - + } sub initSummary { @@ -197,7 +200,7 @@ sub initSummary { sub backupFile { my %arg = @_; my $buff; - if ( not -f $arg{backup} ) { + if ( not -f $arg{backup} ) { if ( -r $arg{file} ) { open(IN,$arg{file}) or warn ("ERROR: problem with file $arg{file}; $!"); open(OUT,">$arg{backup}") or warn ("ERROR: problem with file $arg{backup}; $!"); @@ -226,7 +229,7 @@ sub myLoadCSV { my $seperator = shift; my $reckey; my $line = 1; - + if ( $seperator eq "" ) { $seperator = "\t"; } my $passCounter = 0; @@ -238,13 +241,13 @@ sub myLoadCSV { my @keylist; my $row; my $head; - + my %data; - + #open (DATAFILE, "$file") if (sysopen(DATAFILE, "$file", O_RDONLY)) { flock(DATAFILE, LOCK_SH) or warn "can't lock filename: $!"; - + while () { s/[\r\n]*$//g; #$_ =~ s/\n$//g; @@ -280,7 +283,7 @@ sub myLoadCSV { if ( $reckey eq "" or $key eq "" ) { $reckey = join("-", @rowElements); } - + for ($i = 0; $i <= $#rowElements; ++$i) { if ( $rowElements[$i] eq "null" ) { $rowElements[$i] = ""; } $data{$reckey}{$headers[$i]} = $rowElements[$i]; @@ -292,6 +295,6 @@ sub myLoadCSV { } else { logMsg("cannot open file $file, $!"); } - + return (%data); } diff --git a/admin/migrate_rrd_locations.pl b/admin/migrate_rrd_locations.pl index 05229a6..41f194a 100755 --- a/admin/migrate_rrd_locations.pl +++ b/admin/migrate_rrd_locations.pl @@ -35,7 +35,7 @@ # # nmis collection is disabled while this operation is performed, and a record # of operations is kept for rolling back in case of problems. -our $VERSION = "8.6.6G"; +our $VERSION = "8.6.7G"; use strict; use File::Copy; diff --git a/admin/node_admin.pl b/admin/node_admin.pl index 43b3bab..df1aa63 100755 --- a/admin/node_admin.pl +++ b/admin/node_admin.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl # -# Copyright 1999-2014 Opmantek Limited (www.opmantek.com) +# Copyright 1999-2018 Opmantek Limited (www.opmantek.com) # # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM # @@ -29,7 +29,7 @@ # ***************************************************************************** # # a command-line node administration tool for NMIS -our $VERSION = "1.3.0"; +our $VERSION = "1.4.0"; if (@ARGV == 1 && $ARGV[0] eq "--version") { @@ -382,8 +382,9 @@ die "Cannot create or update node without node argument!\n\n$usage\n" if (!$node); die "File \"$file\" does not exist!\n" if (defined $file && $file ne "-" && ! -f $file); + my $nodenamerule = $config->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/; die "Invalid node name \"$node\"\n" - if ($node =~ /[^a-zA-Z0-9_-]/); + if ($node !~ $nodenamerule); my $noderec = $nodeinfo->{$node}; die "Node $node does not exist.\n" if (!$noderec && $args{act} eq "update"); @@ -424,6 +425,9 @@ die "Invalid node data, does not have required attributes name, host and group\n" if (!$mayberec->{name} or !$mayberec->{host} or !$mayberec->{group}); + die "Invalid node data, name doesn't match command line argument!\n" + if ($node ne $mayberec->{name}); + die "Invalid node data, netType \"$mayberec->{netType}\" is not known!\n" if (!grep($mayberec->{netType} eq $_, split(/\s*,\s*/, $config->{nettype_list}))); die "Invalid node data, roleType \"$mayberec->{roleType}\" is not known!\n" diff --git a/admin/service_graph_helper.pl b/admin/service_graph_helper.pl index 3c8f56c..01e5909 100755 --- a/admin/service_graph_helper.pl +++ b/admin/service_graph_helper.pl @@ -35,7 +35,7 @@ # # this helper guides you through the process of creating custom graphs # for such services. -our $VERSION = "1.1.0"; +our $VERSION = "1.2.0"; if (@ARGV == 1 && $ARGV[0] eq "--version") { @@ -50,7 +50,7 @@ use File::Basename; use JSON::XS; use Data::Dumper; -use UI::Dialog; +use UI::Dialog 1.13; use version 0.77; use func; @@ -78,7 +78,7 @@ my $dia = UI::Dialog->new('title' => "Service Graph Helper", height => 20, width => 70, - listheight => 15, order => [ 'cdialog', 'ascii']); # whiptail/newt doesn't behave well + listheight => 15, order => [ 'cdialog', 'whiptail', 'ascii']); $dia->msgbox("text" => "This helper will guide you through the creation of a simple custom graph for an custom NMIS service. @@ -178,14 +178,14 @@ # ask for: titles (with default) my $newtitle = $dia->inputbox( text => "Please set the new graph title below. This is full-sized graphs. NMIS-variables written as \"\$varname\" will be substituted.", - entry => escape($graph{title}->{standard}) ); + entry => $graph{title}->{standard} ); die "User cancelled operation.\n" if ($dia->state ne "OK"); $graph{title}->{standard} = $newtitle if ($newtitle and $newtitle !~ /^\s*$/ and $newtitle ne $graph{title}->{standard}); $newtitle = $dia->inputbox( text => "Please set the new graph title below. This is for small graphs. NMIS-variables written as \"\$varname\" will be substituted.", - entry => escape($graph{title}->{short}) ); + entry => $graph{title}->{short} ); die "User cancelled operation.\n" if ($dia->state ne "OK"); $graph{title}->{short} = $newtitle if ($newtitle and $newtitle !~ /^\s*$/ and $newtitle ne $graph{title}->{short}); @@ -354,17 +354,3 @@ } exit 0; - - -# versions of ui::dialog before 1.13 don't escape their inputs properly -# https://rt.cpan.org/Public/Bug/Display.html?id=107364 -sub escape -{ - my ($input) = @_; - - if (version->parse($UI::Dialog::VERSION) < version->parse("1.13")) - { - $input =~ s/\$/\\\$/g; - } - return $input; -} diff --git a/admin/update_config_defaults.pl b/admin/update_config_defaults.pl index f6040d1..72bf087 100755 --- a/admin/update_config_defaults.pl +++ b/admin/update_config_defaults.pl @@ -29,7 +29,7 @@ # http://support.opmantek.com/users/ # # ***************************************************************************** -our $VERSION = "8.6.6G"; +our $VERSION = "8.6.7G"; use strict; use List::Util 1.33; diff --git a/admin/upgrade_models.pl b/admin/upgrade_models.pl index f9ac92f..4e7aa1c 100755 --- a/admin/upgrade_models.pl +++ b/admin/upgrade_models.pl @@ -29,7 +29,7 @@ # ***************************************************************************** # # this helper upgrades model files where safe to do so -our $VERSION="8.6.6G"; +our $VERSION="8.6.7G"; use strict; use Digest::MD5; # good enough @@ -164,7 +164,7 @@ sub compute_signature # model file, signatures for the last few releases are stored here __DATA__ Common-ADSL.nmis 8c5779cf5faaf45a -Common-Cisco-asset.nmis 675e126af3677a52 +Common-Cisco-asset.nmis eedb1042cf1c3b9c 675e126af3677a52 Common-Cisco-cbqos.nmis e270054af44bc308 Common-Cisco-cpu.nmis d0570e92ed3e4985 Common-Cisco-macTable.nmis e503d0cbd7220f8f @@ -173,7 +173,7 @@ sub compute_signature Common-Cisco-netflow.nmis 33ad2b9786e1e4f4 Common-Cisco-rtt.nmis 4df49293858fac3d 7cd3a757c422f5bd Common-Cisco-status.nmis f0c8c45368792ae1 c08e756f6ccd2fa8 5075457bccea2102 -Common-Cisco-temp.nmis 0eea6a6191e02701 83bd6bac56c7b7fb +Common-Cisco-temp.nmis 459df2f60abd48b7 0eea6a6191e02701 83bd6bac56c7b7fb Common-Cisco-vlan.nmis 2e41983677ada8b7 Common-Huawei-cbqos.nmis 58b9266692e45cae Common-Juniper-jnxCoS.nmis f2520160747fd14f @@ -184,13 +184,13 @@ sub compute_signature Common-Windows-wmi.nmis dbc5c412d3fa5cb5 eae47688035b325d 75a6d5002bb1e851 Common-asset.nmis f6c2fe2777c14437 Common-calls.nmis 77ca79216fd1aefa -Common-cbqos-in-nexus.nmis f5b4752af6beb6bd +Common-cbqos-in-nexus.nmis ae383bace4a1566f f5b4752af6beb6bd Common-cbqos-in.nmis 68c58453714e91dc a0bbc467ffd18646 6c8df3ec0d0c0858 -Common-cbqos-out-nexus.nmis 70c5bdfbb3c17bf3 +Common-cbqos-out-nexus.nmis a6442955ad6fa8b4 70c5bdfbb3c17bf3 Common-cbqos-out.nmis ab31ff8eee5db591 48665448110af552 1210e379c4b6a92d -Common-database.nmis f5fde11eb5101a3f b3d083221e94f22c 8b309566ec783d52 6514b77ecc0dd2c8 5bacf781a1495ba5 a934b015029bbe63 a41276db67e9be61 92a7f5ebc9af25aa 5f1f1a8792f0498d b70fbcbf9e210596 1b1d1620e6b66683 5dbac9d4f0590b2c 2227e7b78b5547b4 e56cad8dc7066418 4bd336af049d4135 0cf0a876087bd7f2 b32bf11f5b860b3e 411a0d92a51a96a3 b2ff737dc0c025c1 +Common-database.nmis 122c407add4be823 b3d083221e94f22c 8b309566ec783d52 6514b77ecc0dd2c8 5bacf781a1495ba5 a934b015029bbe63 a41276db67e9be61 92a7f5ebc9af25aa 5f1f1a8792f0498d b70fbcbf9e210596 1b1d1620e6b66683 5dbac9d4f0590b2c 2227e7b78b5547b4 e56cad8dc7066418 4bd336af049d4135 0cf0a876087bd7f2 b32bf11f5b860b3e f5fde11eb5101a3f 411a0d92a51a96a3 b2ff737dc0c025c1 Common-entityMib.nmis 23129c70d072e098 -Common-event.nmis 3c17ac2753efd729 +Common-event.nmis 5284eb931e6a5714 3c17ac2753efd729 Common-heading.nmis 03e97ba31018fe04 dc1e5ee59839f42a 6b6ffeeb92a8996f fc120bc0906f3b70 7e458f0172e120ea 0f6d824494640deb 8e453ea283e3fd7a c1c8d886c4c7f7f1 4be5135841352538 a2c592a8fe826493 f652ed992cf8d4a2 087587a01227a79e 1c1c2e7cf3a0606c 5c37adf22c7f4ab4 c2bd5a05f75efac3 56bbc1eafea2ffbb e6e2c1a1df569f27 Common-ifStack.nmis 273aabe064bfb46b Common-lldp.nmis e2d224fefdae20fb @@ -200,7 +200,7 @@ sub compute_signature Common-software.nmis b8a70318d469754b Common-stats.nmis 2f7157c230386ee2 efbcfd8340518376 14dd2080e99197df 051ad0c9af4e10ba 67a57e6c34135bc1 6094dfc29937dd19 17ac95f3a6a726cb Common-summary.nmis 10d878a1904ebb31 -Common-threshold.nmis d72ac1154c8b2264 2085498abd902193 5f00df141ba53a85 709aa976ce2acd85 42a0e451c9206d1a c787902cfc0496e9 306e9d25639e3af6 43500a40644ea0e6 a20ec1fe3d77e0a8 +Common-threshold.nmis 180eb2cd756c9b66 2085498abd902193 5f00df141ba53a85 709aa976ce2acd85 42a0e451c9206d1a c787902cfc0496e9 306e9d25639e3af6 43500a40644ea0e6 a20ec1fe3d77e0a8 d72ac1154c8b2264 Graph-APCBattTemp.nmis 236bfec034b1269c de5f3206f3eeae68 Graph-APCCapacity.nmis 67294b482fbb1e1f a243dadb1d7875dd Graph-APCCurrent.nmis 0c04f8bd2eaaeccd 907121ea0bcf9be0 @@ -437,7 +437,7 @@ sub compute_signature Model-AlcatelASAM.nmis 05d31dc406b41af5 83e3f4595c6719e6 acda353c60ca0a18 d2d5d9b2142e1989 50c1fdaa738fb401 Model-AlcatelASAMv2.nmis 60bd4fe058528bd8 Model-Alcoma.nmis 750123d82f0525c1 363404f6741b1ab6 7716542b9fa6ba59 -Model-AristaSwitch.nmis 61521e62e243c3dc 56a548305c5cc437 d050d520cb81b95c +Model-AristaSwitch.nmis 246662a74269c8c0 56a548305c5cc437 d050d520cb81b95c 61521e62e243c3dc Model-BTI-7000.nmis a359908ab5bdaddc 54fa2081ba36ff46 Model-BTI-7800.nmis 7335b6cb886f60b8 e3da5cfdd3cba22c Model-BTI-OPS.nmis 1951ea24c83a6ab8 c8ce180bbb663559 @@ -461,11 +461,11 @@ sub compute_signature Model-CiscoDefault.nmis bceed295301338cf 4bc694b868f257b4 Model-CiscoGeneric.nmis 7d766076ec3446ae 9f159c3f25a627b9 f589324fb67f9793 Model-CiscoIOSXE.nmis fbe5563477ffdb80 72b549bdcc0804e9 0cb858418067430b 11f60115dd4cdfad e85f66570c90c7fd -Model-CiscoIOSXR.nmis 50feb5a84dc20a4d e12b72b784ad1c5f 459049c58d8319dc 1754b6d20d61a74a c1d83637f22d1aea 71c999e29bc3b680 a1affb7cf1ae5495 3fb8ea566f34b6e4 8a531fb238516665 ac6cc4e079004be4 -Model-CiscoNXOS.nmis 2b60649213da9262 aeac57c06f2a8af6 5688a246aed01961 161ae6c49c6f1b3d a00d69038b17aa39 c80d009aa3d6cf1b 155625644907cfdc d80d5e84a5f55776 bc4df1a19cdd8e9b eabfe0a428f6aa5e 4faba51a761da835 b1111378863a7d68 +Model-CiscoIOSXR.nmis 5b2b16cc41d95d0e e12b72b784ad1c5f 459049c58d8319dc 1754b6d20d61a74a c1d83637f22d1aea 71c999e29bc3b680 a1affb7cf1ae5495 3fb8ea566f34b6e4 8a531fb238516665 50feb5a84dc20a4d ac6cc4e079004be4 +Model-CiscoNXOS.nmis 34955712baf7592f aeac57c06f2a8af6 5688a246aed01961 161ae6c49c6f1b3d a00d69038b17aa39 c80d009aa3d6cf1b 155625644907cfdc d80d5e84a5f55776 bc4df1a19cdd8e9b eabfe0a428f6aa5e 2b60649213da9262 4faba51a761da835 b1111378863a7d68 Model-CiscoPIX.nmis 475b6fe16f5f55d4 59f330a515797f53 Model-CiscoRouter.nmis f4cb68fb386f54c4 54e66e08f36229d9 83ff33224c7c63c0 1bd439a8e31cb653 0bc12598dda8c8a6 24ca17edafeae0b8 d552abe84ee95e51 06598a8a95e10b4e -Model-CiscoVG.nmis bba37e84817dfed5 +Model-CiscoVG.nmis 7b596980452ce9c3 bba37e84817dfed5 Model-Default-HC.nmis a8825ef25dbf4890 7d9ba9976f553b4b fbd901de4a88285a Model-Default.nmis f74f795c534ea46a 72f68603c0a271fe 4af750909edab9c8 Model-EES.nmis 2f96a9629bc08555 811546e34d172b7c @@ -494,9 +494,10 @@ sub compute_signature Model-MW-HP.nmis 32b3ed500d4c4a90 819a5ca08a073521 11c677ca4856af85 Model-MW-Intel.nmis 5cafb6dfa62191ce deb74f6313ead88c c52e3076b8ff51dc Model-MW-Juniper.nmis 0ab8ebdf3e23a6c8 61b9fba4cb792f82 664d25120b038e46 -Model-MikroTik.nmis 8027b5c755f728e2 ff4009ac1488aab1 a353c76c562abc23 f8d9d7eb0925d15a 763ac63ebdf8f155 f5e69106f5b1cbc5 c2ac6bd4b9045cd3 +Model-MikroTik.nmis 3bb7ef6043685503 ff4009ac1488aab1 a353c76c562abc23 f8d9d7eb0925d15a 763ac63ebdf8f155 f5e69106f5b1cbc5 c2ac6bd4b9045cd3 8027b5c755f728e2 Model-MikroTikRouter.nmis 6e7f5c821b5f15c7 afa45e050d9e2ad3 Model-Mimosa.nmis 10672b91765dafdb 85bbe1cd540bdafd f96793bf3d691117 +Model-NetVanta.nmis d50913960c16d7f2 Model-Netgear-GS108T.nmis caa1f22f37fe1652 a97b7d693220f705 324868d459b73597 Model-Netgear-GS724T.nmis fc8bcdd017f3016a a4a0581c2c07b4bb 1454c0c9656baeb8 Model-Netgear-Manual.nmis e50a79fc13613fa0 5efe06d9638880e3 a2412f592876f41f @@ -506,6 +507,7 @@ sub compute_signature Model-OmniSwitch.nmis 95c870d2b7f0c439 858ae29f81cb4357 98c10c891ca47415 45ac9fe2f91bf84e Model-PaloAltoNetworks.nmis 2b0a5c3709bb9917 b3b7673a119b8680 14d93ccadfd65f0f ef2a3aa4669365c0 Model-PingOnly.nmis 50ef2e6b894887b4 +Model-PulseSecure.nmis fe762e631540f136 Model-QNAP.nmis f0bfacd519eeaaf9 54ca09a7da6d4d95 3fbd3e2fa514bf1d 19ec408694e5a14b Model-RadOptimux.nmis 97eb50a719a90945 ac1b50627cf3a86e 6b9949dc6c180ee6 aa0ffde7a6626880 Model-RadwinWireless.nmis f06706f9701019a5 5b7a0e1baa408001 193dbf61b138b6a9 b05456b6497cdf07 @@ -529,4 +531,4 @@ sub compute_signature Model-ZyXEL-IES.nmis 25aaccabbd188208 593c69cbe0391cf3 0da840e65c219e00 3ab9322fece09797 4e5954eff8347757 7b098aa3c8afa7de Model-ZyXEL-MGS.nmis c3ea5aec5b903e8e bff9ef1e5d0a70d8 94ca1a1be8a5eeee 701cf09b9a9dae1e Model-net-snmp.nmis c9b5fa32bd1cd51f a78ed1067f7f14ab e321e3f8a79b25c0 13f1d8c3e10ebebc 997fc7bd3be516be 70491c897fe8d828 e106c9b396e76944 d24bab000b0a6fbe b4d10d3789afa1a6 5d97f9cf73a61919 b6518274fab46b78 aa24077be26e5897 -Model.nmis 8bd4ab3e997c6228 bece80b7b44d959b 34592112596682e2 a6443ed36ccd2120 c91082df42a88c17 fc6e00d8485d47c7 85b6e9852b359133 0b8ce0fbc6085bea fc31c4ba46c1f4be b8427208bee2fc4d 11d418a22fc2adfb 3c7c7f1471f80e2c efb216ab07a50fd0 d0c4c790f815e46a af53a22555c57f63 ba5cdf626ee516c1 +Model.nmis 8337a078c939fbe4 bece80b7b44d959b 34592112596682e2 a6443ed36ccd2120 c91082df42a88c17 fc6e00d8485d47c7 85b6e9852b359133 0b8ce0fbc6085bea fc31c4ba46c1f4be b8427208bee2fc4d 11d418a22fc2adfb 3c7c7f1471f80e2c efb216ab07a50fd0 d0c4c790f815e46a af53a22555c57f63 8bd4ab3e997c6228 ba5cdf626ee516c1 diff --git a/admin/upgrade_tables.pl b/admin/upgrade_tables.pl index ebbe5c9..ca8b90c 100755 --- a/admin/upgrade_tables.pl +++ b/admin/upgrade_tables.pl @@ -29,7 +29,7 @@ # ***************************************************************************** # # this helper upgrades table files where safe to do so -our $VERSION="8.6.6G"; +our $VERSION="8.6.7G"; use strict; use Digest::MD5; # good enough @@ -190,7 +190,7 @@ sub compute_signature Table-Links.nmis 485da1859e3cc036 3f6a43471d7e4cfe Table-Locations.nmis 2d63c7fa7954e92a bd24e107f8158818 2e2e263c0c08d268 Table-Logs.nmis e70cc904a9d923e5 -Table-Nodes.nmis 78693ff40aba93fb 32815befe46ad8e6 c536bfe56a073e41 703420fa55f06eb6 5b144d5f598eb815 013e95cef5802716 80d4c960b5570f70 a8c9b78dd1ad3910 ef34b637d02ba140 948b836801802bf9 7f1a618619af76f7 6cf38465b6dec232 +Table-Nodes.nmis 6d64423651260c20 32815befe46ad8e6 c536bfe56a073e41 703420fa55f06eb6 5b144d5f598eb815 013e95cef5802716 80d4c960b5570f70 a8c9b78dd1ad3910 ef34b637d02ba140 78693ff40aba93fb 948b836801802bf9 7f1a618619af76f7 6cf38465b6dec232 Table-Polling-Policy.nmis 7abb03f496c1943f Table-Portal.nmis f092a05f0f7a2bbb Table-PrivMap.nmis dccc46beb3506fa8 diff --git a/bin/fpingd.pl b/bin/fpingd.pl index c866679..0bb9e35 100755 --- a/bin/fpingd.pl +++ b/bin/fpingd.pl @@ -27,7 +27,7 @@ # http://support.opmantek.com/users/ # # ***************************************************************************** -our $VERSION = "8.6.6G"; +our $VERSION = "8.6.7G"; use FindBin qw($Bin); use lib "$FindBin::Bin/../lib"; @@ -41,6 +41,7 @@ use Test::Deep::NoTest; use Statistics::Lite; use List::Util 1.33; +use Clone; use NMIS; use func; @@ -87,26 +88,40 @@ my $pidfile = $C->{''}."/nmis-fpingd.pid"; # check for any running fpingd instance -my $alreadyrunning; +my @others; if ( -f $pidfile ) { open(F, "<$pidfile") or die "failed to read $pidfile: $!\n"; - $alreadyrunning = ; + my $alreadyrunning = ; chomp $alreadyrunning; + + push @others, $alreadyrunning + if (int($alreadyrunning) # it's numeric + and $alreadyrunning != $$ # it's not me (should be impossible) + and kill(0, $alreadyrunning)); # and it's really alive close(F); } -if ( int($alreadyrunning) # it's a number - and $alreadyrunning != $$ # and it's not me (should be impossible) - and kill(0, $alreadyrunning) # and it's really alive - and ( $killwanted or $restartwanted )) # and we're to get rid of it +# and also make sure no others are lurking +my $ptable = Proc::ProcessTable->new(enable_ttys => 0); +# note: strict equality is required, or wrapping processes like sudo ./bin/fpingd.pl will be killed as well. +for my $maybe (grep($_->cmndline eq $C->{'daemon_fping_filename'}, + @{$ptable->table})) +{ + my $thatpid = $maybe->pid; + next if ($thatpid == $$); + + push @others, $thatpid + if (!grep($_ eq $thatpid, @others)); +} +# there must be at most one fpingd instanc under any circumstances +for my $unwanted (@others) { - kill('TERM', $alreadyrunning); # polite then firm + kill('TERM', $unwanted); # polite then firm sleep(1); - kill('KILL', $alreadyrunning); - unlink($pidfile); + kill('KILL', $unwanted); - debug("Killed process $alreadyrunning"); + logMsg("INFO killed fpingd process $unwanted"); } exit(0) if ($killwanted); @@ -289,6 +304,7 @@ # second time round, handle the secondary else { + $noderec = Clone::clone($noderec); # no overwriting of the original record! $noderec->{host} = $noderec->{host_backup}; $statekey .= ":1"; } @@ -299,9 +315,12 @@ host => $noderec->{host}, # this is either the primary or the secondary host/ip policy => $noderec->{polling_policy} || 'default', }; - $thisstate->{has_sibling} = $thisstate->{uuid} - if (exists($multihomed{$thisstate->{uuid}})); # temporary property is present for both + if (exists($multihomed{$thisstate->{uuid}})) # temporary property is present for both + { + $thisstate->{has_sibling} = $thisstate->{uuid}; + $thisstate->{is_primary} = $statekey =~ /:0$/?1:0; + } # dynamically managed: ip, lastping,nextping, policy , nextdns, avg min max loss # honor fixed ip address given @@ -337,6 +356,7 @@ . ($thisstate->{nextping}? sprintf(", was due %.2fs ago", $now - $thisstate->{nextping}): "" )) if ($debug > 1); push @todos, $statekey; + $thisstate->{pending} = 1; } } @@ -364,16 +384,26 @@ die "Failed to run fping: $!\n"; } + # compile a regex for faster use later + my $ignoreIcmpMessages =~ qr/(ICMP Time Exceeded|ICMP Host Unreachable)/; + while (my $line = ) { chomp $line; # goodnode : xmt/rcv/%loss = 5/5/0%, min/avg/max = 0.89/0.96/1.05 # badnode : xmt/rcv/%loss = 5/0/100% + # or weirdnode : xmt/rcv/%return = 2/64/3200%, min/avg/max = 0.49/7.39/8.13 # or nothing for unresolvable. debug("fping returned: $line") if ($debug > 2); - my ($hostnameorip,$loss,$min,$avg,$max) = ($1,$2,$3,$4,$5,$6) - if ($line =~ m!^\s*(\S+)\s*:\s*xmt/rcv/%loss\s*=\s*\d+/\d+/(\d+)%(?:,\s*min/avg/max\s*=\s*(\d+(?:\.\d+)?)/(\d+(?:\.\d+)?)/(\d+(?:\.\d+)?))?$!); + my ($hostnameorip,$dupsorloss,$loss,$min,$avg,$max) = ($1,$2,$3,$4,$5,$6,$7) + if ($line =~ m!^\s*(\S+)\s*:\s*xmt/rcv/%(loss|return)\s*=\s*\d+/\d+/(\d+)%(?:,\s*min/avg/max\s*=\s*(\d+(?:\.\d+)?)/(\d+(?:\.\d+)?)/(\d+(?:\.\d+)?))?$!); + + if ($dupsorloss eq "return") + { + logMsg("WARN fping reports ICMP packet duplication: \"$line\""); + $loss = 0; + } if ($hostnameorip # parseable? && $ip2staterec{$hostnameorip} # known? @@ -402,14 +432,14 @@ $thisstate->{lastping} = $now; $thisstate->{nextping} = $now + $interval; + delete $thisstate->{pending}; debug("parsed result for node $thisstate->{name}: ".Dumper($thisstate)) if ($debug > 2); } } else { - debug("ERROR fping result \"$line\" was not parseable!"); - logMsg("ERROR result \"$line\" was not parseable!"); + debug("WARN fping result \"$line\" was not parseable!"); } } close FROMFPING; @@ -453,41 +483,56 @@ # now raise/close nmis events according to the node status # note that multi-homed nodes are down IFF all ips are unreachable - if ($thisstate->{loss} == 100) + # no answers whatsoever? then complain, we don't know if up or down so can't really raise any events + if ($thisstate->{pending}) { - my $raisenodedown; + logMsg("ERROR fping has not reported any state for Node $thisstate->{name} ($thisstate->{ip})!"); + # remove any misleading data from previous runs + delete @{$thisstate}{qw(avg min max loss lastping)}; + } + elsif ($thisstate->{loss} == 100) + { + my ($whatevent, $details); - # not multihomed? normal down handling + # not multihomed? normal 'node down' event if (!$thisstate->{has_sibling}) { debug("Node $thisstate->{name} ($thisstate->{ip}) is NOT REACHABLE, fping reported loss=$thisstate->{loss}%"); - $raisenodedown = 1; + $whatevent = "Node Down"; + $details = "Ping failed: fping reported 100% ping loss"; } - # multihomed and all dead + # multihomed and all dead, 'node down' elsif (List::Util::none { $_->{has_sibling} eq $thisstate->{has_sibling} && $_->{loss} != 100 } (values %state)) { - debug("Node $thisstate->{name} ($thisstate->{ip} and all siblings) is NOT REACHABLE, fping reported loss=$thisstate->{loss}%"); - $raisenodedown = 1; + debug("Node $thisstate->{name} ($thisstate->{ip} and its sibling) is NOT REACHABLE, fping reported loss=$thisstate->{loss}%"); + $whatevent = "Node Down"; + $details = "Ping failed: both primary and backup address are unreachable"; } - # multihomed but somebody else is up + # multihomed but not all dead? different events for primary and backup address else { - debug("Node $thisstate->{name} ($thisstate->{ip}) is NOT REACHABLE, fping reported loss=$thisstate->{loss}%, but other siblings are up."); + debug("Node $thisstate->{name} ($thisstate->{ip}) is NOT REACHABLE, fping reported loss=$thisstate->{loss}%, but other sibling is up."); + # node polling failover events are also raised by the snmp/wmi collect code, so clearing is race-y. + $whatevent = ($thisstate->{is_primary}? "Node Polling Failover" : "Backup Host Down"); + $details = ($thisstate->{is_primary}?"Primary address":"Backup address"). " $thisstate->{ip} is unreachable (but the other one is up)."; } - if ($raisenodedown) + if ($whatevent) { # for unreachable nodes where we're caching the dns-ip assocition, we mark it as 'recheck dns' # for fixed-ip nodes this is not relevant undef $thisstate->{nextdns}; - - if (!eventExist($thisstate->{name}, "Node Down", undef)) + if (!eventExist($thisstate->{name}, $whatevent, undef)) { - # Device is DOWN, was up, as no entry in event database - debug("$thisstate->{name} is now DOWN, was UP, updating event database"); - fpingNotify($thisstate->{name}); + debug("$thisstate->{name} is DOWN, raising event $whatevent"); + my $sys = Sys->new; + $sys->init(name => $thisstate->{name}, snmp => 'false'); + notify(sys => $sys, + event => $whatevent, + details => $details, + context => { type => "node" } ); ++$escalatables; } } @@ -495,18 +540,38 @@ else # node somewhat pingable, not 100% loss { debug("$thisstate->{name} ($thisstate->{ip}) is pingable: returned min/avg/max = $thisstate->{min}/$thisstate->{avg}/$thisstate->{max}ms loss=$thisstate->{loss}%"); + my $details = "Ping ok: fping reported $thisstate->{loss}% loss"; - # check the event existence AND its currency! - my $event_exists = eventExist($thisstate->{name}, "Node Down", undef); - my $erec = eventLoad(filename => $event_exists) if ($event_exists); + # what kind of event(s) do we have to cancel/close? + # if not multihomed, or multihomed (and at least one of primary/backup is up, + # which is already certain when in this logic branch): node down + # plus, if multihomed: 'backup host down' for the backup, or 'node polling failover' for the primary + my @toclear = ("Node Down"); + push @toclear, ($thisstate->{is_primary}? "Node Polling Failover" : "Backup Host Down") if ($thisstate->{has_sibling}); + my $sys; - if ($event_exists and $erec and getbool($erec->{current})) + for my $clearme (@toclear) { - # Device was down is now UP! - # Only post the status if the event database records as currently down - debug("$thisstate->{name} ($thisstate->{ip}) is now UP, was DOWN, updating event database"); - fpingCheckEvent($thisstate->{name}); - ++$escalatables; + # check the event existence AND its currency! + my $event_exists = eventExist($thisstate->{name}, $clearme, undef); + my $erec = eventLoad(filename => $event_exists) if ($event_exists); + + if ($event_exists and $erec and getbool($erec->{current})) + { + debug("$thisstate->{name} ($thisstate->{ip}) is now UP, clearing event $clearme"); + if (!ref($sys)) + { + $sys = Sys->new; + $sys->init(name => $thisstate->{name}, snmp => 'false'); + } + checkEvent( sys => $sys, + event => $clearme, + # the failover event should be logged with this name + upevent => ($clearme eq "Node Polling Failover"? "Node Polling Failover Closed" : undef), + level => "Normal", + details => $details); + ++$escalatables; + } } } } @@ -543,44 +608,6 @@ exit 0; -# check-and-remove existing node down event -# args: node name -# returns: nothing -sub fpingCheckEvent -{ - my $node = shift; - debug("\tUpdating event database via sub checkEvent() host: $node event: Node Up"); - - my $S = Sys::->new; - $S->init(name => $node, snmp => 'false'); - my $NI = $S->ndinfo; # pointer to node info table - - checkEvent( sys => $S, - event => "Node Down", - element => "", - level => "Normal", - details => "Ping failed" ); -} - -# create a new node down event -# args: node name -# returns: nothing -sub fpingNotify -{ - my $node = shift; - - debug("\tUpdating event database via sub notify() host: $node event: Node Down"); - - my $S = Sys::->new; - $S->init(name=>$node, snmp=>'false'); - - notify( sys => $S, - event => "Node Down", - element => "", - details => "Ping failed", - context => { type => "node" }); -} - sub debug { my (@msgs) = @_; @@ -607,7 +634,6 @@ sub catch_zap debug("I was killed by $sig"); logMsg("INFO daemon fpingd killed by $sig", do_not_lock => 1); - unlink $pidfile; } # this is a general-purpose reaper of zombies diff --git a/bin/nmis.pl b/bin/nmis.pl index 5d1f494..e63715c 100755 --- a/bin/nmis.pl +++ b/bin/nmis.pl @@ -48,6 +48,7 @@ # this imports the LOCK_ *constants (eg. LOCK_UN, LOCK_EX), also the stat modes use Fcntl qw(:DEFAULT :flock :mode); use Errno qw(EAGAIN ESRCH EPERM); +use Sys::Syslog qw(:standard :macros); use NMIS; use NMIS::Connect; @@ -78,6 +79,7 @@ my $starttime = Time::HiRes::time; my $C = loadConfTable(conf => $cmdargs->{conf}, debug=>$cmdargs->{debug}, info=>$cmdargs->{info}); die "nmis cannot operate without config!\n" if (ref($C) ne "HASH"); +diag_log(LOG_INFO, "NMIS process started."); # and the status of the database dir, as reported by the selftest - 0 bad, 1 ok, undef unknown # this is used by rrdfunc::createRRD(), so needs to be scoped suitably. @@ -99,11 +101,13 @@ } my $selftest_cache = "$varsysdir/selftest"; + diag_log(LOG_DEBUG, "Performing Selftest."); my ($allok, $tests) = func::selftest(config => $C, delay_is_ok => 'false', report_database_status => \$selftest_dbdir_status, perms => 'false'); writeHashtoFile(file => $selftest_cache, json => 1, data => { status => $allok, lastupdate => time, tests => $tests }); + diag_log($allok? LOG_DEBUG: LOG_INFO, "Selftest completed ".($allok?"ok":"FAILED!")); info("Selftest completed (status ".($allok?"ok":"FAILED!")."), cache file written"); if (-f $lockoutfile) { @@ -111,17 +115,22 @@ # installer should not need to lock this box for more than a few minutes if (-f $installerpresence && (stat($installerpresence))[9] > time - 3600) { + diag_log(LOG_INFO, "NMIS is currently disabled, installer is performing upgrade, exiting."); logMsg("INFO NMIS is currently disabled, installer is performing upgrade, exiting."); + exit(0); } else { + diag_log(LOG_WARNING, "NMIS is currently disabled! Remove the file $lockoutfile to re-enable."); logMsg("WARNING NMIS is currently disabled! Remove the file $lockoutfile to re-enable."); die "Attention: NMIS is currently disabled!\nRemove the file $lockoutfile to re-enable.\n\n"; } } else { + diag_log(LOG_WARNING, "NMIS is currently disabled! Set the configuration variable \"global_collect\" to \"true\" to re-enable"); + die "Attention: NMIS is currently disabled!\nSet the configuration variable \"global_collect\" to \"true\" to re-enable.\n\n"; } } @@ -180,6 +189,7 @@ / if $C->{debug} or $C->{info}; +diag_log(LOG_DEBUG, "Performing preliminary operations."); # the first thing we do is to upgrade up the event # data structure - it's a nop if it was already done. NMIS::upgrade_events_structure(); @@ -210,6 +220,8 @@ elsif ( $type eq "rme" ) { loadRMENodes($rmefile); } elsif ( $type eq "threshold" ) { + diag_log(LOG_INFO, "Starting (independent) threshold operation"); + my @cand = expand_candidate_list($nodeselect, $groupselect); if (@cand) { @@ -219,6 +231,7 @@ { runThreshold(); } + diag_log(LOG_INFO, "(independent) threshold operation finished"); printRunTime(); } elsif ( $type eq "master" ) { nmisMaster(); printRunTime(); } # MIGHT be included in type=collect @@ -227,9 +240,9 @@ my $error = purge_files(); die "$error\n" if $error; my $res = NMIS::purge_outages(); die "$res->error\n" if !$res->{success}; } -else { checkArgs(); } +else { checkArgs(); exit 1; } -exit; +exit 0; #========================================================================================= @@ -274,7 +287,7 @@ sub expand_candidate_list my $mthread = getbool($args{mthread}); my $mthreadDebug = getbool($args{mthreadDebug}); - dbg("Starting, operation is $type"); + diag_log(LOG_INFO, "Starting $type operation"); # returns empty list if unfiltered my @candnodes = expand_candidate_list($args{nodeselect}, $args{groupselect}); @@ -407,6 +420,7 @@ sub expand_candidate_list { die "Unknown operation type=$type, terminating!\n"; } + logMsg("INFO start of $type process"); # update the operation start/stop timestamp @@ -513,37 +527,57 @@ sub expand_candidate_list push @todo_nodes, $maybe; $whichflavours{$maybe}->{wmi} = $whichflavours{$maybe}->{snmp} = 1; # and ignore the last-xyz markers } - # nodes that have not been pollable since forever: run at most once daily - # ...except if the demote_faulty_nodes config option is set to false - elsif (!$ninfo->{system}->{nodeModel} or $ninfo->{system}->{nodeModel} eq "Model") - { - my $lasttry = $ninfo->{system}->{last_poll} // 0; - # try once every 5 minutes if demote_faulty_nodes is set to false, - # otherwise: was polling attempted at all and in the last 30 days? then once daily from - # that last try - otherwise try one now - my $nexttry = !getbool($C->{demote_faulty_nodes},"invert")? # === ne false - ($lasttry && ($now - $lasttry) <= 30*86400)? ($lasttry + 86400 * 0.95) : $now : $lasttry + 300 ; + # logic for dead node demotion/rate-limiting + # if demote_faulty_nodes config option is true, demote nodes that have + # not been pollable (or updatable) ever: + # after 14 days of normal attempts change to try at most once daily + elsif ( + !getbool($C->{demote_faulty_nodes}, "invert") # === ne false + && (!$ninfo->{system}->{nodeModel} or $ninfo->{system}->{nodeModel} eq "Model" ) ) + { + # this property gets updated on every attempt + my $lasttry = $ninfo->{system}->{ $type eq "collect" ? + "last_poll_attempt" : "last_update_attempt" }; - if ($nexttry <= $now) + my $graceperiod_start = $ninfo->{system}->{demote_grace}; + # none set? then set one + if (!defined($graceperiod_start)) { - push @todo_nodes, $maybe; - $whichflavours{$maybe}->{wmi} = $whichflavours{$maybe}->{snmp} = 1; + $ninfo->{system}->{demote_grace} = $graceperiod_start = $now; + # and make sure the changed data is written out + writeTable(dir => 'var', name => lc("$maybe-node"), data => $ninfo); } - # if demotion is enabled, log this pretty dire situation - # but not too noisily - with a default poll every minute this - # will log the issue once an hour. - elsif (!getbool($C->{demote_faulty_nodes},"invert")) # === ne false + + # try only once a day if beyond the grace time, min of snmp/wmi/update policy otherwise; + my $normalperiod + = $type eq "collect" + ? Statistics::Lite::min( $intervals{$polname}->{snmp}, $intervals{$polname}->{wmi} ) + : $intervals{$polname}->{update}; + + # but do make sure to try a newly added node NOW! + my $fudgefactor = ($C->{polling_interval_factor} || 0.9); + my $nexttry = defined $lasttry? $lasttry + + $fudgefactor * $normalperiod : $now; + + if ($now - $graceperiod_start > 14 * 86400) { - my $goodtimes = int((($now - $lasttry) % 3600) / 60); - my $msg = "Node $maybe has no valid nodeModel, never polled successfully, " - . "demoted to frequency once daily, last attempt $lasttry, next $nexttry"; - logMsg($msg) if ($goodtimes == 0); - dbg($msg); + $nexttry = ( $lasttry // $now) + 86400 * $fudgefactor; + + logMsg( "Node $maybe has no valid nodeModel, never polled successfully, " + . "past demotion grace window (started at $graceperiod_start) so demoted to frequency once daily, last $type attempt $lasttry, next $nexttry" ) if ($debug); } else { - dbg("Node $maybe has no valid nodeModel, never polled successfully. demote_faulty_nodes is disabled, last attempt $lasttry, next $nexttry."); + logMsg("Node $maybe has no valid nodeModel, never polled successfully, demote_faulty_nodes is on, grace window started at $graceperiod_start, last $type attempt " + .($lasttry // "never").", next $nexttry." ) if ($debug); + } + + if ( $nexttry <= $now ) + { + push @todo_nodes, $maybe; + $whichflavours{$maybe}->{wmi} = $whichflavours{$maybe}->{snmp} = 1 + if ( $type eq "collect" ); } } # logic for collect now or later: candidate if no past successful collect whatsoever, @@ -569,10 +603,11 @@ sub expand_candidate_list dbg("Node $maybe is non-collecting, applying snmp policy to last check at $lastsnmp"); } - # accept delta-previous-now interval if it's at least 95% of the configured interval + # accept delta-previous-now interval if it's at least 90% of the configured interval # strict 100% would mean that we might skip a full interval when polling takes longer - my $nextsnmp = ($lastsnmp // 0) + $intervals{$polname}->{snmp} * 0.95; - my $nextwmi = ($lastwmi // 0) + $intervals{$polname}->{wmi} * 0.95; + my $fudgefactor = $C->{polling_interval_factor} || 0.9; + my $nextsnmp = ($lastsnmp // 0) + $intervals{$polname}->{snmp} * $fudgefactor; + my $nextwmi = ($lastwmi // 0) + $intervals{$polname}->{wmi} * $fudgefactor; # only flavours which worked in the past contribute to the now-or-later logic if ((defined($lastsnmp) && $nextsnmp <= $now ) @@ -787,8 +822,9 @@ sub expand_candidate_list # not false if (!getbool($C->{threshold_poll_cycle},"invert") ) { - dbg("Starting runThreshold (for all selected nodes)"); + diag_log(LOG_INFO, "Starting post-collect threshold operation"); map { runThreshold($_) } (@candnodes); + diag_log(LOG_INFO, "post-collect threshold operation finished"); } else { @@ -806,6 +842,10 @@ sub expand_candidate_list $D->{total}{value} = Time::HiRes::time() - $starttime; $D->{total}{option} = 'gauge,0:U'; + # OMK-4630 add nodecount to be saved, probably the following will work well + #$D->{nodecount}{value} = $nodecount; + #$D->{nodecount}{option} = 'gauge,0:U'; + my $nr_processes = 1+ scalar %{&func::find_nmis_processes(config => $C)}; # current one isn't returned by find_nmis_processes $D->{nr_procs} = { option => "gauge,0:U", value => $nr_processes }; @@ -877,7 +917,7 @@ sub expand_candidate_list func::update_operations_stamp(type => $type, start => $starttime, stop => Time::HiRes::time()); - dbg("Finished"); + diag_log(LOG_INFO, "$type operation finished"); return; } @@ -970,6 +1010,9 @@ sub doUpdate my $NI = $S->ndinfo; my $NC = $S->ndcfg; + # record that we are trying an update; last_update records only successfully completed updates... + $NI->{system}->{last_update_attempt} = Time::HiRes::time; + # look for any current outages with options.nostats set, # and set a marker in nodeinfo so that updaterrd writes nothing but 'U' my $outageres = NMIS::check_outages(sys => $S, time => time); @@ -1069,6 +1112,9 @@ sub doUpdate } $S->close; # close snmp session if one is open $NI->{system}{last_update} = time(); + + # we updated something, so certainly outside of dead node demotion grace period + delete $NI->{system}->{demote_grace}; } my $reachdata = runReach(sys=>$S, delayupdate => 1); # don't let it make the rrd update, we want to add updatetime! @@ -1300,6 +1346,10 @@ sub doCollect my $NC = $S->ndcfg; $S->readNodeView; # s->init does NOT load that, but we need it as we're overwriting some view info + # record that we are trying a collect/poll; + # last_poll (and last_poll_wmi/snmp) only record successfully completed operations + $NI->{system}->{last_poll_attempt} = $starttime; + # look for any current outages with options.nostats set, # and set a marker in nodeinfo so that updaterrd writes nothing but 'U' my $outageres = NMIS::check_outages(sys => $S, time => time); @@ -1537,13 +1587,16 @@ sub runPing && ref($PT) eq "HASH") { # for multihomed nodes there are two records to check, keyed nodename:N - my @tocheck = (defined $NC->{node}->{host_backup}? + my @tocheck = ((defined($NC->{node}->{host_backup}) + && $NC->{node}->{host_backup})? grep($_ =~ /^$nodename:\d$/, keys %$PT) : $nodename); for my $onekey (@tocheck) { - if (ref($PT->{$onekey}) eq "HASH" - && (time - $PT->{$onekey}->{lastping}) < $staleafter) # and not stale, 15 minutes seems ample + if (ref($PT->{$onekey}) eq "HASH" # present + && defined($PT->{$onekey}->{lastping}) # and valid past data is a/v + && defined($PT->{$onekey}->{nextping}) # and there's a working polling policy + && (time - $PT->{$onekey}->{nextping}) < $staleafter) # and the daemon isn't too far behind with its work { # copy the fastping data... ($ping_min, $ping_avg, $ping_max, $ping_loss) = @{$PT->{$onekey}}{"min","avg","max","loss"}; @@ -2430,7 +2483,14 @@ sub getIntfInfo info("Getting Device IP Address Table"); if ( $ifAdEntTable = $SNMP->getindex('ipAdEntIfIndex')) { if ( $ifMaskTable = $SNMP->getindex('ipAdEntNetMask')) { - foreach my $addr (keys %{$ifAdEntTable}) { + foreach my $addr (keys %{$ifAdEntTable}) + { + if ($ifMaskTable->{$addr} eq "255.255.255.255" + && $IF->{$ifAdEntTable->{$addr}}->{ifDescr} !~ /loopback/i) + { + info("SKIPPING HSRP Addr on $IF->{$ifAdEntTable->{$addr}}{ifDescr} :: ifIndex=$ifAdEntTable->{$addr}, addr=$addr mask=$ifMaskTable->{$addr}"); + next; + } my $index = $ifAdEntTable->{$addr}; next if ($singleInterface and $intf_one ne $index); $ifCnt{$index} += 1; @@ -3290,6 +3350,22 @@ sub getSystemHealthInfo { dbg("section=$section index=$index_var, found value=$indexvalue"); + # allow disabling of collection for this instance, + # based on regex match against the index value + if (ref($thissection->{nocollect}) eq "HASH" + && defined($thissection->{nocollect}->{$index_var})) + { + # this supports both 'nocollect' => { 'first' => qr/somere/i, 'second' => 'plaintext' } + my $rex = ref($thissection->{nocollect}->{$index_var}) eq "Regexp"? + $thissection->{nocollect}->{$index_var} : qr/$thissection->{nocollect}->{$index_var}/; + + if ($indexvalue =~ $rex) + { + dbg("nocollect match for systemHealth section=$section key=$index_var value=$indexvalue - skipping"); + next; + } + } + # save the seen index value $NI->{$section}->{$indexvalue}->{$index_var} = $indexvalue; @@ -3322,10 +3398,27 @@ sub getSystemHealthInfo if ( $oid =~ /$index_regex/ ) { $index = $1; } - $healthIndexNum{$index}=$index; - dbg("section=$section index=$index is found, value=$healthIndexTable->{$oid}"); - $NI->{$section}->{$index}->{$index_var} = $healthIndexTable->{$oid}; + my $indexvalue = $healthIndexNum{$index} = $index; + dbg("section=$section index=$index is found, value=$indexvalue"); + + # allow disabling of collection for this instance, + # based on regex match against the index value + if (ref($thissection->{nocollect}) eq "HASH" + && defined($thissection->{nocollect}->{$index_var})) + { + # this supports both 'nocollect' => { 'first' => qr/somere/i, 'second' => 'plaintext' } + my $rex = ref($thissection->{nocollect}->{$index_var}) eq "Regexp"? + $thissection->{nocollect}->{$index_var} : qr/$thissection->{nocollect}->{$index_var}/; + + if ($indexvalue =~ $rex) + { + dbg("nocollect match for systemHealth section=$section key=$index_var value=$indexvalue - skipping"); + next; + } + } + + $NI->{$section}->{$index}->{$index_var} = $indexvalue; } } else @@ -3494,8 +3587,8 @@ sub updateNodeInfo # save what we need now for check of this node my $sysObjectID = $NI->{system}{sysObjectID}; my $ifNumber = $NI->{system}{ifNumber}; - my $sysUpTimeSec = $NI->{system}{sysUpTimeSec}; - my $sysUpTime = $NI->{system}{sysUpTime}; + my $sysUpTimeSec = $NI->{system}{sysUpTimeSec}; # seconds + my $sysUpTime = $NI->{system}{sysUpTime}; # text like "8 days, 22:01:51" # this returns 0 iff none of the possible/configured sources worked, sets details my $loadsuccess = $S->loadInfo(class=>'system', model=>$model); @@ -3579,19 +3672,29 @@ sub updateNodeInfo $V->{system}{snmpUpTime_value} = $NI->{system}{snmpUpTime}; $V->{system}{snmpUpTime_title} = 'SNMP Uptime'; } - info("sysUpTime: Old=$sysUpTime New=$NI->{system}{sysUpTime}"); - if ($NI->{system}->{sysUpTimeSec} && $sysUpTimeSec > $NI->{system}{sysUpTimeSec}) + + # has that node really been reset or has the uptime counter wrapped at 497 days and change? + # sysUpTime is in 0.01s timeticks and 32 bit wide, so 497.1 days is all it can hold + my $newuptime = $NI->{system}->{sysUpTimeSec}; + if ($newuptime && $sysUpTimeSec > $newuptime) { - info("NODE RESET: Old sysUpTime=$sysUpTimeSec New sysUpTime=$NI->{system}{sysUpTimeSec}"); - notify(sys=>$S, event=>"Node Reset", - element=>"", - details => "Old_sysUpTime=$sysUpTime New_sysUpTime=$NI->{system}{sysUpTime}", - context => { type => "node" } ); + if ($sysUpTimeSec >= 496*86400) # ie. old uptime value within one day of the rollover + { + logMsg("INFO ($NI->{system}{name}) sysUpTime has wrapped after 497 days"); + } + else + { + info("NODE RESET: Old sysUpTime=$sysUpTimeSec New sysUpTime=$newuptime"); + notify(sys=>$S, event=>"Node Reset", + element=>"", + details => "Old_sysUpTime=$sysUpTime New_sysUpTime=$newuptime", + context => { type => "node" } ); - # now stash this info in the node info object, to ensure we insert ONE set of U's into the rrds - # so that no spikes appear in the graphs - $NI->{admin}->{node_was_reset}=1; + # now stash this info in the node info object, to ensure we insert ONE set of U's into the rrds + # so that no spikes appear in the graphs + $NI->{admin}->{node_was_reset}=1; + } } $V->{system}{sysUpTime_value} = $NI->{system}{sysUpTime}; @@ -3601,6 +3704,9 @@ sub updateNodeInfo $V->{system}{lastUpdate_title} = 'Last Update'; $NI->{system}{last_poll} = $time_marker; + # we polled something, so outside of dead node demotion grace period + delete $NI->{system}->{demote_grace}; + # get and apply any nodeconf override if such exists for this node my $node = $NI->{system}{name}; my ($errmsg, $override) = get_nodeconf(node => $node) @@ -4259,9 +4365,13 @@ sub getCBQoSdata return undef; } + # fix the debug for Cisco Nexus which only has post policy. + my $byte = defined $D->{'PrePolicyByte'}{value} ? $D->{'PrePolicyByte'}{value} : $D->{'PostPolicyByte'}{value}; + my $pkts = defined $D->{'PrePolicyPkt'}{value} ? $D->{'PrePolicyPkt'}{value} : $D->{'PostPolicyPkt'}{value}; + # oke, store the data - dbg("bytes transfered $D->{'PrePolicyByte'}{value}, bytes dropped $D->{'DropByte'}{value}"); - dbg("packets transfered $D->{'PrePolicyPkt'}{value}, packets dropped $D->{'DropPkt'}{value}"); + dbg("bytes transfered $byte, bytes dropped $D->{'DropByte'}{value}"); + dbg("packets transfered $pkts, packets dropped $D->{'DropPkt'}{value}"); dbg("packets dropped no buffer $D->{'NoBufDropPkt'}{value}"); # # update RRD @@ -4295,6 +4405,7 @@ sub getCBQoSwalk my %args = @_; my $S = $args{sys}; my $NI = $S->ndinfo; + my $M = $S->mdl; if (!$S->status->{snmp_enabled}) { @@ -4358,7 +4469,7 @@ sub getCBQoSwalk # the OID will be 1.3.6.1.4.1.9.9.166.1.5.1.1.2.$PIndex.$OIndex = Gauge BLOCK2: - foreach my $OIndex (keys %{$qosIndexTable}) { + foreach my $OIndex (sort keys %{$qosIndexTable}) { # look for the Object type for each ($answer->{'cbQosObjectsType'}) = $SNMP->getarray("cbQosObjectsType.$PIndex.$OIndex"); dbg("look for object at $PIndex.$OIndex, type $answer->{'cbQosObjectsType'}"); @@ -4428,8 +4539,12 @@ sub getCBQoSwalk next BLOCK2; # skip this class-map, is part of a match statement } # concatenate names - if ($answer->{'cbQosParentObjectsIndex2'} ne 0) { - $answer->{'cbQosCMName'} = "$answer->{'cbQosName'}--$answer->{'cbQosCMName'}"; + if ($answer->{'cbQosParentObjectsIndex2'} ne 0) + { + # OMK-5182, customer wants / as delimiter, not our default -- + $answer->{'cbQosCMName'} = join( ($C->{cbqos_classmap_name_delimiter} // "--"), + $answer->{'cbQosName'}, + $answer->{'cbQosCMName'}); } } @@ -4455,9 +4570,32 @@ sub getCBQoSwalk $CMRate = $answer->{'cbQosQueueingCfgBandwidth'} * $inoutIfSpeed/100; } if ($CMRate eq 0) { $CMRate = "undef"; } + + # if this is Nexus we want to use one of the queuing classes for getting the data. + # how do we select the right class? + #$CMValues{"H".$OIndex}{'CMIndex'} = $OIndex ; + my $thing = "cbqos-$direction"; + if ( defined $M->{$thing}->{rrd}->{$thing}{nexus} and getbool($M->{$thing}->{rrd}->{$thing}{nexus}) ) { + $answer->{'cbQosQueueingCurrentQDepth'} = undef; + $answer->{'cbQosQueueingCurrentQDepthPkt'} = undef; + ($answer->{'cbQosQueueingCurrentQDepth'},$answer->{'cbQosQueueingCurrentQDepthPkt'}) = $SNMP->getarray("1.3.6.1.4.1.9.9.166.1.18.1.1.1.$PIndex.$OIndex","1.3.6.1.4.1.9.9.166.1.18.1.1.9.$PIndex.$OIndex"); + + # lets just get the first one we find. + if ( defined $answer->{'cbQosQueueingCurrentQDepth'} + and $answer->{'cbQosQueueingCurrentQDepth'} ne "" + and not defined $CMValues{"H".$answer->{'cbQosParentObjectsIndex'}}{'QIndex'} + ) { + dbg("nexus queuing class found $OIndex using this class for data collection not the class map"); + $CMValues{"H".$answer->{'cbQosParentObjectsIndex'}}{'CMIndex'} = $OIndex ; + $CMValues{"H".$answer->{'cbQosParentObjectsIndex'}}{'QIndex'} = $OIndex ; + } + dbg("nexus debug $OIndex cbQosQueueingCurrentQDepth=$answer->{'cbQosQueueingCurrentQDepth'}"); + } + + $CMValues{"H".$answer->{'cbQosParentObjectsIndex'}}{'CMCfgRate'} = $CMRate; dbg("queueing - bandwidth $answer->{'cbQosQueueingCfgBandwidth'}, units $answer->{'cbQosQueueingCfgBandwidthUnits'},". "rate $CMRate, parent ID $answer->{'cbQosParentObjectsIndex'}"); - $CMValues{"H".$answer->{'cbQosParentObjectsIndex'}}{'CMCfgRate'} = $CMRate ; + } elsif ($answer->{'cbQosObjectsType'} eq 6) { # traffic shaping ($answer->{'cbQosTSCfgRate'},$answer->{'cbQosParentObjectsIndex'}) @@ -4495,6 +4633,7 @@ sub getCBQoSwalk $cbQosTable{$intf}{$direction}{'ClassMap'}{$index}{'Name'} = $CMValues{$index}{'CMName'}; $cbQosTable{$intf}{$direction}{'ClassMap'}{$index}{'Index'} = $CMValues{$index}{'CMIndex'}; + $cbQosTable{$intf}{$direction}{'ClassMap'}{$index}{'QIndex'} = $CMValues{$index}{'QIndex'} if defined $CMValues{$index}{'QIndex'}; # lets print the just type if (exists $CMValues{$index}{'CMCfgRate'}) { @@ -6101,7 +6240,7 @@ sub runAlerts if ( defined($CA->{$sect}{$alrt}{control}) and $CA->{$sect}{$alrt}{control} ne '' ) { my $control_result = $S->parseString(string=>"($CA->{$sect}{$alrt}{control}) ? 1:0", index=>$index, type=>$sect, sect=>$sect); - dbg("control_result sect=$sect index=$index control_result=$control_result"); + dbg("control_result event=$CA->{$sect}{$alrt}{event} sect=$sect index=$index control_result=$control_result"); next if not $control_result; } @@ -7032,11 +7171,15 @@ sub nmisSummary { my %args = @_; + diag_log(LOG_INFO, "starting summary operation"); + # check that there are no other running/stuck/delayed summary processes my $others = func::find_nmis_processes(config => $C, type => 'summary'); if (keys %$others) { + diag_log(LOG_ERR, "other type=summary processes running (" + .join(", ", keys %$others)."), aborting operation."); logMsg("ERROR other type=summary processes running (".join(", ", keys %$others)."), aborting operation."); info("ERROR other type=summary processes running (".join(", ", keys %$others)."), aborting operation."); return; @@ -7045,7 +7188,7 @@ sub nmisSummary my $pollTimer = NMIS::Timing->new; - dbg("Calculating NMIS network stats for cgi cache"); + func::update_operations_stamp(type => "summary", start => $starttime, stop => undef) if ($type eq "summary"); # not if part of collect @@ -7064,7 +7207,6 @@ sub nmisSummary my $NS = getNodeSummary(C => $C); my $file = "nmis-nodesum"; writeTable(dir=>'var',name=>$file,data=>$NS); - dbg("Finished calculating NMIS network stats for cgi cache - wrote $k nodes"); func::update_operations_stamp(type => "summary", start => $starttime, stop => Time::HiRes::time()) if ($type eq "summary"); # not if part of collect @@ -7072,6 +7214,8 @@ sub nmisSummary my $polltime = $pollTimer->elapTime(); logMsg("Poll Time: $polltime"); } + + diag_log(LOG_INFO, "summary operation finished"); } sub summaryCache @@ -7132,6 +7276,8 @@ sub runEscalate my %args = @_; my $C = loadConfTable(); + diag_log(LOG_INFO, "Starting escalate operation"); + if (getbool($args{independent})) { # check that there are no other running/stuck/delayed escalate processes @@ -7139,6 +7285,8 @@ sub runEscalate type => 'escalate'); if (keys %$others) { + diag_log(LOG_ERR, "other type=escalate processes running (".join(", ", keys %$others)."), aborting operation."); + logMsg("ERROR other type=escalate processes running (".join(", ", keys %$others)."), aborting operation."); info("ERROR other type=escalate processes running (".join(", ", keys %$others)."), aborting operation."); return; @@ -7177,7 +7325,6 @@ sub runEscalate my $serial_ns = 0; my %seen; - dbg("Starting"); func::update_operations_stamp(type => "escalate", start => $starttime, stop => undef) if ($type eq "escalate"); # not if part of collect # load Contacts table @@ -7974,9 +8121,7 @@ sub runEscalate } } - # Cologne, send the messages now sendMSG(data=>\%msgTable); - dbg("Finished"); if ( defined $C->{log_polling_time} and getbool($C->{log_polling_time})) { my $polltime = $pollTimer->elapTime(); logMsg("Poll Time: $polltime"); @@ -7985,6 +8130,8 @@ sub runEscalate start => $starttime, stop => Time::HiRes::time()) if ($type eq "escalate"); # not if part of collect + + diag_log(LOG_INFO, "escalate operation finished"); } # end runEscalate #========================================================================================= @@ -8178,11 +8325,15 @@ sub runMetrics my %args = @_; my $S = $args{sys}; + diag_log(LOG_INFO, "Starting metrics operation"); + # check that there are no other running/stuck/delayed summary processes my $others = func::find_nmis_processes(config => $C, type => 'metrics'); if (keys %$others) { + diag_log(LOG_ERR, "other type=metrics processes running (" + .join(", ", keys %$others)."), aborting operation."); logMsg("ERROR other type=metrics processes running (".join(", ", keys %$others)."), aborting operation."); info("ERROR other type=metrics processes running (".join(", ", keys %$others)."), aborting operation."); return; @@ -8207,7 +8358,7 @@ sub runMetrics my $pollTimer = NMIS::Timing->new; - dbg("Starting"); + # Doing the whole network - this defaults to -8 hours span my $groupSummary = getGroupSummary(); @@ -8262,10 +8413,11 @@ sub runMetrics logMsg("ERROR updateRRD failed: ".getRRDerror()); } } - dbg("Finished"); + logMsg("Poll Time: ". $pollTimer->elapTime()) if ( defined $C->{log_polling_time} and getbool($C->{log_polling_time})); + diag_log(LOG_INFO, "metrics operation finished"); } # end runMetrics @@ -8287,7 +8439,7 @@ sub runLinks return; } - dbg("Start"); + diag_log(LOG_INFO, "Starting links operation"); if (!($II = loadInterfaceInfo())) { logMsg("ERROR reading all interface info"); @@ -8422,7 +8574,7 @@ sub runLinks } logMsg("Check table Links and update link names and other entries"); - dbg("Finished"); + diag_log(LOG_INFO,"links operation finished"); } @@ -8447,8 +8599,10 @@ sub runDaemons my $pt = new Proc::ProcessTable(); foreach my $pentry (@{$pt->table}) { - # fpingd is identifyable only by cmdline - $fpingd_found = 1 if ($pentry->cmndline =~ $C->{daemon_fping_filename}); + # fpingd is identifyable only by cmdline, + # and only strict equality is ok or we might misinterpret wrappers like + # sudo ./bin/fpingd.pl, or ps ax|fgrep fpingd... + $fpingd_found = 1 if ($pentry->cmndline eq $C->{daemon_fping_filename}); $ipslad_found = 1 if ($pentry->fname eq $C->{daemon_ipsla_filename}); last if ($fpingd_found && $ipslad_found); } @@ -8504,6 +8658,8 @@ sub checkConfig my $change = $args{change}; my $audit = $args{audit}; + diag_log(LOG_INFO, "Starting ".($audit?"audit":"config")." operation"); + my $ext = getExtension(dir=>'conf'); @@ -8669,6 +8825,7 @@ sub checkConfig convertConfFiles(); info(" Continue with bin/nmis.pl type=apache for configuration rules of the Apache web server\n"); + diag_log(LOG_INFO, ($audit?"audit":"config")." operation finished") } @@ -8713,8 +8870,8 @@ sub printCrontab 22 8 * * * $usercol $C->{''}/admin/config_backup.pl $C->{''} 30 ###################################################### -# purge old files every few days -2 2 */3 * * $usercol $C->{''}/bin/nmis.pl type=purge +# purge old files frequently +27 * * * * $usercol $C->{''}/bin/nmis.pl type=purge ###################################################### # Save the Reports, Daily Monthly Weekly @@ -8741,14 +8898,14 @@ sub printApache24 # NMIS Aliases for static files: Alias $C->{''} "$C->{web_root}" {web_root}"> - Options Indexes FollowSymLinks MultiViews + Options FollowSymLinks MultiViews AllowOverride None Require all granted Alias $C->{''}/ "$C->{''}/" {''}"> - Options Indexes FollowSymLinks MultiViews + Options FollowSymLinks MultiViews AllowOverride None Require all granted @@ -8834,7 +8991,7 @@ sub printApache Alias $C->{''} "$C->{web_root}" {web_root}"> - Options Indexes FollowSymLinks MultiViews + Options FollowSymLinks MultiViews AllowOverride None Order allow,deny Allow from all @@ -8842,7 +8999,7 @@ sub printApache Alias $C->{''}/ "$C->{''}/" {''}"> - Options Indexes FollowSymLinks MultiViews + Options FollowSymLinks MultiViews AllowOverride None Order allow,deny Allow from all @@ -8951,13 +9108,7 @@ sub runThreshold # check global_threshold not explicitely set to false if (!getbool($C->{global_threshold},"invert")) { - my $node_select; - if ($node) - { - die "Invalid node=$node: No node of that name\n" - if (!($node_select = checkNodeName($node))); - } - doThreshold(name=>$node_select, table => doSummaryBuild(name => $node_select)); + doThreshold(name=>$node, table => doSummaryBuild(name => $node)); } else { @@ -9085,10 +9236,13 @@ sub doSummaryBuild next unless getbool($IF->{$index}{collect}); my $sts = getSummaryStats(sys=>$S, type=>$tp, start=>$threshold_period, end=>time(), index=>$index); - foreach (keys %{$sts->{$index}}) { $stats{$nd}{interface}{$index}{$_} = $sts->{$index}{$_}; } # save for threshold - - # copy all stats into the stsintf info. - foreach (keys %{$sts->{$index}}) { $stsintf{"${index}.$S->{name}"}{$_} = $sts->{$index}{$_}; } + foreach (keys %{$sts->{$index}}) + { + # save for threshold + $stats{$nd}{interface}{$index}{$_} = $sts->{$index}{$_}; + # get all the stats fields into the stts info. + $stsintf{"${index}.$S->{name}"}{$_} = $sts->{$index}{$_}; + } } } } @@ -9120,7 +9274,6 @@ sub doThreshold my $sts = $args{table}; # pointer to data built up by doSummaryBuild my $S = $args{sys}; - dbg("Starting"); func::update_operations_stamp(type => "threshold", start => $starttime, stop => undef) if ($type eq "threshold"); # not if part of collect my $pollTimer = NMIS::Timing->new; @@ -9331,7 +9484,6 @@ sub doThreshold $S->writeNodeInfo() if ($type eq "threshold"); } - dbg("Finished"); if ( defined $C->{log_polling_time} and getbool($C->{log_polling_time})) { my $polltime = $pollTimer->elapTime(); @@ -9773,6 +9925,13 @@ sub purge_files info("Starting to look for purgable files"); # config option, extension, where to look... my @purgatory = ( + { ext => qr/\.png$/, + minage => $C->{purge_graphcache_after} || 3600, + location => "$C->{web_root}/cache", + also_empties => 1, + description => "Old Graph Images", + }, + { ext => qr/\.rrd$/, minage => $C->{purge_rrd_after} || 30*86400, location => $C->{database_root}, @@ -9937,6 +10096,20 @@ sub makesysuptime return; } +# log diagnostic message to local syslog, if syslog_diag_facility is configured +# args: priority (text or constant, ie. 'info' or LOG_DEBUG), +# message; both required. +# returns: nothing +sub diag_log +{ + my ($priority, $message) = @_; + + if (my $facility = $C->{syslog_diag_facility}) + { + openlog("nmis", "ndelay,pid,nofatal", $facility); + syslog($priority, $message); + } +} # ***************************************************************************** # Copyright (C) Opmantek Limited (www.opmantek.com) diff --git a/bin/nmis.sh b/bin/nmis.sh index 838d0ea..a7b2a6d 100755 --- a/bin/nmis.sh +++ b/bin/nmis.sh @@ -36,7 +36,7 @@ nmis=$nmis_base/bin/nmis.pl nmis_log=$nmis_base/logs/nmis.log event_log=$nmis_base/logs/event.log error_log=/var/log/httpd/error_log -editor=/bin/vi +editor=`which vi` DEBUG="debug=false" MODEL="model=false" @@ -55,8 +55,10 @@ helptext() { echo " $0 crontab" echo " $0 config" echo " $0 audit" - echo " $0 Nodes" echo " $0 Config" + echo " $0 Locations" + echo " $0 Nodes" + echo " $0 Services" echo " $0 Users" echo " $0 fixperms" echo " $0 fpingd restart" @@ -164,12 +166,24 @@ then exit 0 fi +if [ "$1" = "Locations" ] +then + $editor $nmis_base/conf/Locations.nmis + exit 0 +fi + if [ "$1" = "Nodes" ] then $editor $nmis_base/conf/Nodes.nmis exit 0 fi +if [ "$1" = "Services" ] +then + $editor $nmis_base/conf/Services.nmis + exit 0 +fi + if [ "$1" = "Config" ] then $editor $nmis_base/conf/Config.nmis diff --git a/cgi-bin/config.pl b/cgi-bin/config.pl index 16822a6..2314d27 100755 --- a/cgi-bin/config.pl +++ b/cgi-bin/config.pl @@ -30,7 +30,7 @@ # # ***************************************************************************** use strict; -our $VERSION="8.6.6G"; +our $VERSION="8.6.7G"; use FindBin; use lib "$FindBin::Bin/../lib"; diff --git a/cgi-bin/ipsla.pl b/cgi-bin/ipsla.pl index bb25742..55f5c4a 100755 --- a/cgi-bin/ipsla.pl +++ b/cgi-bin/ipsla.pl @@ -555,7 +555,7 @@ sub displayIPSLAmenu { } elsif ( !getbool($C->{daemon_ipsla_active}) ) { $class = "Error"; $message = "  parameter daemon_ipsla_active in nmis.conf is not set on true to start the daemon ipslad.pl"; - } + } elsif (not -r "/var/run/ipslad.pid") { $class = "Error"; @@ -925,17 +925,23 @@ sub displayRTTnode { my $glunits = $C->{graph_unit}; my $win_width = $C->{graph_width} + 100; my $win_height = $C->{graph_height} + 320; - my $nmiscgi_script = "$C->{rrddraw}"; + my $tmpurl=url()."?conf=$Q->{conf}&type=graph&graphtype=$aref->[2]&glamount=&glunits=&node=$pnode"; return td({align=>"center", colspan=>"2", bgcolor=>"white"}, a({href=>$tmpurl, target=>"ViewWindow", onMouseOver=>"window.status='Drill into $aref->[1].';return true", onClick=>"viewdoc('$tmpurl',$win_width,$win_height)"}, img({border=>"0", alt=>"$aref->[1]", - src=>"$C->{rrddraw}?conf=$Q->{conf}&act=draw_graph_view&node=$pnode&graphtype=$aref->[2]&start=0&end=0&width=350&height=50&title=small"}))); + src=> htmlGraph(only_link => 1, # need the unwrapped link here + node => $pnode, + graphtype => $aref->[2], + # fixme: why are no time arguments passed in? + width => 350, # fixme: why are these not based on c->{graph_xyz}? + height => 50, ) + }))); + } - #src="/cgi-nmis8/rrddraw.pl?conf=Config.xxxx&act=draw_graph_view&node=wanedge1&group=&graphtype=cpu&start=1318428782&end=1318601582&width=700&height=250&intf=&item=" align="MIDDLE" /> } sub displayRTTdata { diff --git a/cgi-bin/logs.pl b/cgi-bin/logs.pl index 40db5b8..695cec5 100755 --- a/cgi-bin/logs.pl +++ b/cgi-bin/logs.pl @@ -140,14 +140,24 @@ # Therefore, create a hash, keyed by name records, values being the node key. ## TBD - add the node sysName in here, as a log source address could be it's sysname. my $NT = loadNodeTable(); + +# this is just a hash of groupname=>groupname as observed from nodes' configurations - NOT filtered! +my $raw = loadGroupTable(); +my $GT = {}; +for my $configuredgroup (split(/\s*,\s*/, $C->{group_list})) +{ + $GT->{$configuredgroup} = $configuredgroup if (defined $raw->{$configuredgroup}); +} + my $NN; foreach my $k ( keys %{$NT} ) { + next if (!exists ($GT->{$NT->{$k}->{group}})); # skip nodes whose group isn't a configured one $NN->{ $NT->{$k}{name} } = $k; # create reference to node file key in new hash by name $NN->{ $NT->{$k}{host} } = $k; # create reference to node file key in new hash by host $NN->{ $k } = $k; # create reference to node file key in new hash by node file key } -my $GT = loadGroupTable(); + my $logFileName; # this gets set to the full qualified filename in conf/Logs.xxxx config fiel # defaults @@ -824,7 +834,10 @@ sub outputLine { ### 2012-08-29 keiths, enabling Authentication if group not blank # Rule 3: if Authentication enabled, only print if user enabled for select Group - if ( $NT->{$logNode}{group} ne "" and $AU->Require and not $AU->InGroup($NT->{$logNode}{group}) ) { return 0 } + # same for recognised node whose group is not a configured one + return 0 + if ( $NT->{$logNode}{group} ne "" and (($AU->Require and not $AU->InGroup($NT->{$logNode}{group}) ) + or !$GT->{$NT->{$logNode}->{group}})); if ( getbool($C->{syslogDNSptr}) and (lc $logName eq "cisco_syslog")) { # have a go at finding out the hostname for any ip address that may have been referenced in the log. diff --git a/cgi-bin/models.pl b/cgi-bin/models.pl index 9f4ad75..a9744d2 100755 --- a/cgi-bin/models.pl +++ b/cgi-bin/models.pl @@ -28,7 +28,7 @@ # # ***************************************************************************** use strict; -our $VERSION = "8.6.6G"; +our $VERSION = "8.6.7G"; use FindBin; use lib "$FindBin::Bin/../lib"; diff --git a/cgi-bin/network.pl b/cgi-bin/network.pl index eea4e69..93b9d9e 100755 --- a/cgi-bin/network.pl +++ b/cgi-bin/network.pl @@ -107,9 +107,17 @@ logMsg("TIMING: ".$t->elapTime()." Begin act=$Q->{act}") if $timing; -# select function -my $select; +# these need loading before the yucky if selection below, which is terminal for some acts +my $NT = loadNodeTable(); +# this is just a hash of groupname=>groupname as observed from nodes' configurations - NOT filtered! +my $raw = loadGroupTable(); +my $GT = {}; +for my $configuredgroup (split(/\s*,\s*/, $C->{group_list})) +{ + $GT->{$configuredgroup} = $configuredgroup if (defined $raw->{$configuredgroup}); +} +my $select; if ($Q->{act} eq 'network_summary_health') { $select = 'health'; } elsif ($Q->{act} eq 'network_summary_view') { $select = 'view'; } elsif ($Q->{act} eq 'network_summary_small') { $select = 'small'; @@ -174,8 +182,6 @@ sub notfound { logMsg("TIMING: ".$t->elapTime()." Load Nodes and Groups") if $timing; -my $NT = loadNodeTable(); -my $GT = loadGroupTable(); # graph request my $ntwrk = ($select eq 'large') ? 'network' : ($Q->{group} eq '') ? 'network' : $Q->{group} ; @@ -282,7 +288,6 @@ sub getSummaryStatsbyGroup { } elsif ($metric >= 1) { $metric_color = colorPercentHi($metric); $icon{metric_icon} = 'arrow_up_big'; } - ## ehg 17 sep 02 add node down counter with colour my $percentDown = 0; if ( $groupSummary->{average}{countdown} > 0 and $groupSummary->{average}{counttotal} > 0 ) { $percentDown = sprintf("%2.0u",$groupSummary->{average}{countdown}/$groupSummary->{average}{counttotal}) * 100; @@ -1388,10 +1393,6 @@ sub viewMetrics { print 'You are not authorized for this request'; return; } - - #prepend the network group! - #my @grouplist = split(",","network,$C->{group_list}"); - my $GT = loadGroupTable; my @grouplist = values %{$GT}; my @groups = grep { $AU->InGroup($_) } sort (@grouplist); @@ -1660,10 +1661,18 @@ sub viewNode { $color = "#0F0"; } } - # skip if not present + # skip if not present, color up if state is known elsif ($k eq "host_addr_backup") { next if (!defined $value or $value eq ""); + $color = ($status{failover_ping_status}? "#00ff00" : "#ff0000") if (defined $status{failover_ping_status}); + } + # color up if state is known - but select primary state tag if multihomed + elsif ($k eq "host_addr") + { + my $source = defined($status{failover_status})? 'primary_ping_status' : 'ping_status'; + + $color = ($status{$source}? "#00ff00" : "#ff0000") if (defined $status{$source}); } # from outageCheck, neither nodeinfo nor view elsif ($k eq 'outage') @@ -1691,7 +1700,7 @@ sub viewNode { my $time = $NI->{system}{last_poll}; if ( $time ne "" ) { if ($time < (time - 60*15)) { - $color = "#ffcc00"; # to late + $color = "#ffcc00"; # too long ago } } } @@ -2018,6 +2027,8 @@ sub viewInterface push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning', colspan=>'2'},"Node degraded, " . join(", ",@causes) @@ -2181,6 +2192,8 @@ sub viewAllIntf { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning'},"Node degraded, " . join(", ", @causes) @@ -2341,6 +2354,8 @@ sub viewActivePort { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning'},"Node degraded, " . join(", ", @causes) @@ -2463,6 +2478,8 @@ sub viewStorage { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'3'},"Node degraded, " . join(", ",@causes) @@ -2541,6 +2558,8 @@ sub viewService push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'3'},"Node degraded, " . join(", ", @causes) @@ -2639,6 +2658,8 @@ sub viewServiceList { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'7'},"Node degraded, " . join(", ", @causes) @@ -2745,6 +2766,8 @@ sub viewCpuList { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'7'},"Node degraded, " . join(", ", @causes) @@ -2820,6 +2843,8 @@ sub viewStatus { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>$colspan},"Node degraded, " .join(", ", @causes) @@ -2927,6 +2952,8 @@ sub viewEnvironment { push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'3'},"Node degraded, " . join(", ", @causes) @@ -3048,6 +3075,8 @@ sub viewSystemHealth push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>$colspan},"Node degraded, " . join(", ",@causes) @@ -3155,6 +3184,8 @@ sub viewCSSGroup push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'3'},"Node degraded, " . join(", ", @causes) @@ -3208,6 +3239,8 @@ sub viewCSSContent push @causes, "WMI ".($status{wmi_status}? "Up":"Down") if ($status{wmi_enabled}); push @causes, "Node Polling Failover" if (defined($status{failover_status}) && !$status{failover_status}); + push @causes, "Backup Host Down" + if (defined($status{failover_ping_status}) && !$status{failover_ping_status}); print Tr(td({class=>'Warning',colspan=>'3'},"Node degraded, " .join(", ", @causes) @@ -3242,7 +3275,6 @@ sub viewOverviewIntf { my $NT = loadNodeTable(); my $II = loadInterfaceInfo(); - my $GT = loadGroupTable(); my $ii_cnt = keys %{$II}; my $gr_menu = ""; @@ -3251,7 +3283,6 @@ sub viewOverviewIntf { print start_form(-id=>"ntw_int_overview",-href=>url(-absolute=>1)."?conf=$C->{conf}&act=network_interface_overview"); if ($ii_cnt > 1000) { - my $GT = loadGroupTable(); my @groups = ('',sort keys %{$GT}); $gr_menu = td({class=>'header', colspan=>'1'}, "Select group ". @@ -3330,7 +3361,6 @@ sub viewTop10 { print ''; my $NT = loadNodeTable(); - my $GT = loadGroupTable(); my $S = Sys::->new; my $start = time()-(15*60); diff --git a/cgi-bin/nmiscgi.pl b/cgi-bin/nmiscgi.pl index fb5be30..d2f55a6 100755 --- a/cgi-bin/nmiscgi.pl +++ b/cgi-bin/nmiscgi.pl @@ -3,31 +3,31 @@ ## $Id: nmiscgi.pl,v 8.26 2012/09/18 01:40:59 keiths Exp $ # # Copyright (C) Opmantek Limited (www.opmantek.com) -# +# # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM -# +# # This file is part of Network Management Information System ("NMIS"). -# +# # NMIS 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. -# +# # NMIS 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 NMIS (most likely in a file named LICENSE). +# along with NMIS (most likely in a file named LICENSE). # If not, see -# +# # For further information on NMIS or for a license other than GPL please see -# www.opmantek.com or email contact@opmantek.com -# +# www.opmantek.com or email contact@opmantek.com +# # User group details: # http://support.opmantek.com/users/ -# +# # All NMIS documentation can be found @ # https://community.opmantek.com/ # @@ -53,10 +53,10 @@ my $C = loadConfTable(conf=>$Q->{conf},debug=>$Q->{debug}); if ( ($Q->{conf} eq "" ) - and -f "$C->{''}/Tenants.nmis" - and -f "$C->{''}/tenants.pl" ) + and -f "$C->{''}/Tenants.nmis" + and -f "$C->{''}/tenants.pl" ) { - logMsg("TENANT Redirect, conf=$Q->{conf}, $C->{''}/tenants.pl"); + logMsg("TENANT Redirect, conf=$Q->{conf}, $C->{''}/tenants.pl"); print $q->header($q->redirect( -url=>"$C->{''}/tenants.pl", -nph=>1, @@ -74,7 +74,7 @@ my $user; # the updated login screen code needs to know what modules are available -my $M = NMIS::Modules->new(nmis_base => $C->{''}, +my $M = NMIS::Modules->new(nmis_base => $C->{''}, nmis_cgi_url_base => $C->{''}); my $moduleCode = $M->getModuleCode(); my $installedModules = $M->installedModules(); @@ -96,8 +96,8 @@ username=>$Q->{auth_username}, password=>$Q->{auth_password}, headeropts=>$headeropts, - listmodules => - (!getbool($C->{display_module_list}, 'invert')? + listmodules => + (!getbool($C->{display_module_list}, 'invert')? $M->getModuleLinks : undef)) ; $privlevel = $AU->{privlevel}; $user = $AU->{user}; @@ -121,7 +121,7 @@ # Version 1.8.15 stable # # open index.html and copy the required files paths to the header of this file -# +# # # # @@ -142,20 +142,20 @@ my $serverCode = loadServerCode(conf=>$Q->{conf}); my $portalCode = loadPortalCode(conf=>$Q->{conf}); - + my $logoCode; if ( $C->{company_logo} ) { $logoCode = qq| |; -} - +} + my $logout = qq|
|; - + if ($C->{auth_method_1} eq "apache") { $logout = ""; } @@ -212,28 +212,32 @@ my $NT = loadNodeTable(); # load node table my $NSum = loadNodeSummary(); -#Only show authorised nodes in the list. -my $auth; - +# Only show authorised nodes in the list - and only if members of configured groups! my @valNode; -for my $node ( sort keys %{$NT}) { - $auth = 1; - if ($AU->Require) { +my %configuredgroups = map { $_ => 1 } (split(/\s*,\s*/, $C->{group_list})); + +for my $node (sort keys %{$NT}) +{ + my $auth = 1; + if ($AU->Require) + { my $lnode = lc($NT->{$node}{name}); - if ( $NT->{$node}{group} ne "" ) { - if ( not $AU->InGroup($NT->{$node}{group}) ) { - $auth = 0; + if ( $NT->{$node}{group} ne "" ) + { + if ( not $AU->InGroup($NT->{$node}{group}) ) { + $auth = 0; } } - else { - logMsg("WARNING ($node) not able to find correct group. Name=$NT->{$node}{name}.") - } - } - if ($auth) { - if ( getbool($NT->{$node}{active}) ) { - push @valNode, $NT->{$node}{name}; + else + { + logMsg("WARNING ($node) not able to find correct group. Name=$NT->{$node}{name}.") } } + + # to be included node must be ok to see, active and belonging to a configured group + push @valNode, $NT->{$node}{name} if ($auth + && getbool($NT->{$node}->{active}) + && $configuredgroups{$NT->{$node}->{group}}); } # my $jsNode = encode_json(\@valNode ); # print script("namesAll = ".$jsNode); @@ -243,11 +247,6 @@ # @nk is the matching hash key my @header=( 'Type', 'Vendor', 'Model', 'Role', 'Net', 'Group'); my @nk =( 'nodeType', 'nodeVendor', 'nodeModel', 'roleType', 'netType', 'group'); -# init the hash -# my %NS = (); -# foreach ( @header ) { -# $NS{$_} = (); -# } # read the hash - note al filenames are lowercase - loadTable should take care of this # list of nodes is already authorised, just load the details. @@ -258,7 +257,7 @@ next unless defined $NSum->{$node}{$nk[$i]}; # $nodeInfo->{$node}{$header[$i]} = $NSum->{$node}{$nk[$i]}; $NSum->{$node}{$nk[$i]} =~ s/\s+/_/g; - $nodeValues->{$header[$i]} = $NSum->{$node}{$nk[$i]}; + $nodeValues->{$header[$i]} = $NSum->{$node}{$nk[$i]}; # push @{ $NS{ $header[$i] }{ $NSum->{$node}{$nk[$i]} } } , $NT->{$node}{name}; } push( @{$nodeInfo}, $nodeValues ); @@ -270,12 +269,12 @@ $C->{'display_opmaps_widget'} = "true" if $C->{'display_opmaps_widget'} eq ""; $C->{'display_opflow_widget'} = "true" if $C->{'display_opflow_widget'} eq ""; $C->{'display_network_view'} = "true" if $C->{'display_network_view'} eq ""; - + $C->{'rss_widget_width'} = 210 if $C->{'rss_widget_width'} eq ""; -$C->{'rss_widget_height'} = 240 if $C->{'rss_widget_height'} eq ""; +$C->{'rss_widget_height'} = 240 if $C->{'rss_widget_height'} eq ""; $C->{'opmaps_widget_width'} = 750 if $C->{'opmaps_widget_width'} eq ""; -$C->{'opmaps_widget_height'} = 450 if $C->{'opmaps_widget_height'} eq ""; +$C->{'opmaps_widget_height'} = 450 if $C->{'opmaps_widget_height'} eq ""; $C->{'opflow_widget_width'} = 750 if $C->{'opflow_widget_width'} eq ""; $C->{'opflow_widget_height'} = 460 if $C->{'opflow_widget_height'} eq ""; @@ -283,14 +282,14 @@ my $savedWindowState = "false"; my $userWindowData = "false"; if( defined $windowData && defined($windowData->{$user}) && $windowData->{$user} ne '' ) -{ +{ $savedWindowState = "true"; $userWindowData = encode_json($windowData->{$user}); } # show the setup if not hidden and user sufficiently authorized -my $showsetup = (getbool($C->{'hide_setup_widget'}) - or !$AU->CheckAccess("table_config_rw","check") +my $showsetup = (getbool($C->{'hide_setup_widget'}) + or !$AU->CheckAccess("table_config_rw","check") or !$AU->CheckAccess("table_config_view","check"))? 'false' : 'true'; ### 2012-02-22 keiths, added widget_refresh timer, and passing through to jQuery @@ -324,7 +323,7 @@ # ***************************************************************************** # Copyright (C) Opmantek Limited (www.opmantek.com) # This program comes with ABSOLUTELY NO WARRANTY; -# This is free software licensed under GNU GPL, and you are welcome to +# This is free software licensed under GNU GPL, and you are welcome to # redistribute it under certain conditions; see www.opmantek.com or email # contact@opmantek.com # ***************************************************************************** diff --git a/cgi-bin/node.pl b/cgi-bin/node.pl index 8846ace..9959e86 100755 --- a/cgi-bin/node.pl +++ b/cgi-bin/node.pl @@ -39,11 +39,11 @@ use CGI qw(:standard *table *Tr *td *form *Select *div); use Text::CSV; +use func; use NMIS; use Auth; use Sys; -use rrdfunc; -use func; +use rrdfunc; # for getrrdashash my $q = new CGI; # This processes all parameters passed via GET and POST my $Q = $q->Vars; # values in hash @@ -72,7 +72,14 @@ # check for remote request if ($Q->{server} ne "") { exit if requestServer(headeropts=>$headeropts); } -#====================================================================== +my $NT = loadLocalNodeTable(); +# this is just a hash of groupname=>groupname as observed from nodes' configurations - NOT filtered! +my $raw = loadGroupTable(); +my $GT = {}; +for my $configuredgroup (split(/\s*,\s*/, $C->{group_list})) +{ + $GT->{$configuredgroup} = $configuredgroup if (defined $raw->{$configuredgroup}); +} # cancel? go to graph view if ($Q->{cancel} || $Q->{act} eq 'network_graph_view') @@ -135,9 +142,6 @@ sub typeGraph my $length; - my $NT = loadLocalNodeTable(); - my $GT = loadGroupTable(); - my $S = Sys::->new; # get system object $S->init(name=>$node); # load node info and Model if name exists my $NI = $S->ndinfo; @@ -188,12 +192,14 @@ sub typeGraph # verify that user is authorized to view the node within the user's group list if ( $node ) { - if ( ! $AU->InGroup($NT->{$node}{group}) ) { + if ( !$AU->InGroup($NT->{$node}{group}) or !exists $GT->{$NT->{$node}{group}} ) + { print "Not Authorized to view graphs on node '$node' in group $NT->{$node}{group}"; return 0; } } elsif ( $group ) { - if ( ! $AU->InGroup($group) ) { + if ( ! $AU->InGroup($group) or !exists $GT->{$group} ) + { print "Not Authorized to view graphs on nodes in group $group"; return 0; } @@ -294,11 +300,13 @@ sub typeGraph $item = ''; } - ### 2012-04-12 keiths, fix for node list with unauthorised nodes. my @nodelist; - for my $node ( sort keys %{$NT}) { + for my $node ( sort keys %{$NT}) + { my $auth = 1; - if ($AU->Require) { + + if ($AU->Require) + { my $lnode = lc($NT->{$node}{name}); if ( $NT->{$node}{group} ne "" ) { if ( not $AU->InGroup($NT->{$node}{group}) ) { @@ -309,11 +317,11 @@ sub typeGraph logMsg("WARNING ($node) not able to find correct group. Name=$NT->{$node}{name}.") } } - if ($auth) { - if ( getbool($NT->{$node}{active}) ) { - push(@nodelist, $NT->{$node}{name}); - } - } + + # to be included node must be ok to see, active and belonging to a configured group + push @nodelist, $NT->{$node}{name} if ($auth + && getbool($NT->{$node}->{active}) + && $GT->{$NT->{$node}->{group}}); } my %sshealth = get_graphtype_systemhealth(sys => $S, graphtype => $graphtype); @@ -584,18 +592,34 @@ sub typeGraph push @output, end_Tr; } - my $graphLink="$C->{'rrddraw'}?conf=$Q->{conf}&act=draw_graph_view". - "&node=$urlsafenode&group=$urlsafegroup&graphtype=$graphtype&start=$start&end=$end&width=$width&height=$height&intf=$urlsafeindex&item=$urlsafeitem"; - - if ( $graphtype ne "service-cpumem" or $NI->{graphtype}{$index}{service} =~ /service-cpumem/ ) { - push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'},image_button(-name=>'graphimg',-src=>"$graphLink",-align=>'MIDDLE', -tabindex=>"-1"))); - push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'},"Clickable graphs: Left -> Back; Right -> Forward; Top Middle -> Zoom In; Bottom Middle-> Zoom Out, in time")); + my $graphLink = htmlGraph(only_link => 1, # we need the unwrapped link! + node => $node, + group => $group, + graphtype => $graphtype, + intf => $index, + item => $item, + start => $start, + end => $end, + width => $width, + height => $height ); + # logMsg("asked for $graphtype, start $start end $end, got $graphLink"); + + if ( $graphtype ne "service-cpumem" or $NI->{graphtype}{$index}{service} =~ /service-cpumem/ ) + { + push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'}, + image_button(-name=>'graphimg',-src => $graphLink, + -align=>'MIDDLE', -tabindex=>"-1"))); + push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'}, + "Clickable graphs: Left -> Back; Right -> Forward; Top Middle -> Zoom In; Bottom Middle-> Zoom Out, in time")); } - else { - push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'},"Graph type not applicable for this data set.")); + else + { + push @output, Tr(td({class=>'info Plain',align=>'center',colspan=>'4'}, + "Graph type not applicable for this data set.")); } } else { - push @output, Tr(td({colspan=>'4',align=>'center'},"waiting for selection or no data available")); + push @output, Tr(td({colspan=>'4',align=>'center'}, + "waiting for selection or no data available")); push @output, hidden(-name=>'item', -default=>"$item",-override=>'1'); } # push on page @@ -610,8 +634,6 @@ sub typeGraph print hidden(-name=>'p_end', -default=>"$p_end",-override=>'1'); print hidden(-name=>'p_time', -default=>"$time",-override=>'1'); print hidden(-name=>'act', -default=>"network_graph_view", -override=>'1'); - print hidden(-name=>'obj', -default=>"graph",-override=>'1'); # for rrddraw - print hidden(-name=>'func', -default=>"view",-override=>'1'); # fixme: looks unused print "", comment("typeGraph end"); print end_html; @@ -631,12 +653,12 @@ sub show_export_options # verify that user is authorized to view the node within the user's group list my $nodegroup = $S->ndinfo->{system}->{group}; - if ($node && !$AU->InGroup($nodegroup)) + if ($node && (!$AU->InGroup($nodegroup || !exists $GT->{$nodegroup}))) { bailout(code => 403, message => escapeHTML("Not Authorized to export rrd data for node '$node' in group '$nodegroup'.")); } - elsif ($group && !$AU->InGroup($group)) + elsif ($group && (!$AU->InGroup($group) || !exists $GT->{$group})) { bailout(code => 403, message => escapeHTML("Not Authorized to export rrd data for nodes in group '$group'.")); diff --git a/cgi-bin/opmantek_rrddraw.pl b/cgi-bin/opmantek_rrddraw.pl deleted file mode 100755 index dcb5458..0000000 --- a/cgi-bin/opmantek_rrddraw.pl +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/perl -# -## $Id: rrddraw.pl,v 8.10 2012/08/24 05:35:22 keiths Exp $ -# -# Copyright (C) Opmantek Limited (www.opmantek.com) -# -# ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM -# -# This file is part of Network Management Information System (“NMIS”). -# -# NMIS 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. -# -# NMIS 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 NMIS (most likely in a file named LICENSE). -# If not, see -# -# For further information on NMIS or for a license other than GPL please see -# www.opmantek.com or email contact@opmantek.com -# -# User group details: -# http://support.opmantek.com/users/ -# -# ***************************************************************************** -# Auto configure to the /lib -use FindBin; -use lib "$FindBin::Bin/../lib"; - -use NMIS::uselib; -use lib "$NMIS::uselib::rrdtool_lib"; -# -#****** Shouldn't be anything else to customise below here ******************* - -require 5; - -use strict; -use RRDs 1.4004; -use func; -#use rrdfunc; -use Sys; -use NMIS; -use Data::Dumper; -use LWP::Simple qw(!head); - -use CGI qw(:standard *table *Tr *td *form *Select *div); - -use vars qw($q $Q $C $AU); - -$q = new CGI; # This processes all parameters passed via GET and POST -$Q = $q->Vars; - -if (!($C = loadConfTable(conf=>$Q->{conf},debug=>$Q->{debug}))) { exit 1; }; -$C->{auth_require} = 0; # bypass auth - -# NMIS Authentication module -use Auth; - -# variables used for the security mods -use vars qw($headeropts); $headeropts = {type=>'text/html',expires=>'now'}; -$AU = Auth->new(conf => $C); # Auth::new will reap init values from NMIS::config - -if ($AU->Require) { - exit 0 unless $AU->loginout(type=>$Q->{auth_type},username=>$Q->{auth_username}, - password=>$Q->{auth_password},headeropts=>$headeropts) ; -} - -# check for remote request -if ($Q->{server} ne "") { exit if requestServer(headeropts=>$headeropts); } - -#====================================================================== - -# select function -if ($Q->{act} eq 'draw_graph_view') { rrdDraw(); -} else { notfound(); } - -exit; - -sub notfound { - logMsg("rrddraw; Command unknown act=$Q->{act}"); -} - -#============================================================================ - -sub error { - print header($headeropts); - print start_html(); - print "Network: ERROR on getting graph
\n"; - print "Request not found\n"; - print end_html; -} - -#============================================================================ - -sub rrdDraw { - my %args = @_; - - # Break the query up for the names - my $type = $Q->{obj}; - my $nodename = $Q->{node}; - my $debug = $Q->{debug}; - my $grp = $Q->{group}; - my $graphtype = $Q->{graphtype}; - my $graphlength = $Q->{graphlength}; - my $graphstart = $Q->{graphstart}; - my $width = $Q->{width}; - my $height = $Q->{height}; - my $start = $Q->{start}; - my $end = $Q->{end}; - my $intf = $Q->{intf}; - my $item = $Q->{item}; - my $filename = $Q->{filename}; - - - # print STDERR $q->query_string(); - - my $content = get("http://localhost:3000/nmis/nmis_graph/chart?".$q->query_string); - print "Content-type: application/json\n\n"; - print $content; - return; -} -sub id { - my $x = 10 *shift; - return '_'.sprintf("%02X", $x); -} diff --git a/cgi-bin/rrddraw.pl b/cgi-bin/rrddraw.pl index 51e4b38..28c0176 100755 --- a/cgi-bin/rrddraw.pl +++ b/cgi-bin/rrddraw.pl @@ -1,6 +1,5 @@ #!/usr/bin/perl # -## $Id: rrddraw.pl,v 8.10 2012/08/24 05:35:22 keiths Exp $ # # Copyright (C) Opmantek Limited (www.opmantek.com) # @@ -29,21 +28,24 @@ # http://support.opmantek.com/users/ # # ***************************************************************************** -# Auto configure to the /lib +use strict; +our $VERSION = "8.6.7G"; + use FindBin; use lib "$FindBin::Bin/../lib"; use NMIS::uselib; use lib "$NMIS::uselib::rrdtool_lib"; -use strict; use RRDs 1.4004; +use Data::Dumper; +use CGI qw(:standard *table *Tr *td *form *Select *div); + use func; use Sys; use NMIS; -use Data::Dumper; - -use CGI qw(:standard *table *Tr *td *form *Select *div); +use NMIS::RRDdraw; +use Auth; my $q = new CGI; # This processes all parameters passed via GET and POST my $Q = $q->Vars; @@ -54,9 +56,6 @@ # bypass auth iff called from command line $C->{auth_require} = 0 if (@ARGV); -# NMIS Authentication module -use Auth; - # variables used for the security mods my $headeropts = {type=>'text/html',expires=>'now'}; my $AU = Auth->new(conf => $C); # Auth::new will reap init values from NMIS::config @@ -67,45 +66,44 @@ } # check for remote request -if ($Q->{server} ne "") { exit if requestServer(headeropts=>$headeropts); } - -#====================================================================== +if ($Q->{server} ne "") +{ + exit if requestServer(headeropts=>$headeropts); +} # select function -if ($Q->{act} eq 'draw_graph_view') { rrdDraw(); -} else { notfound(); } - -exit; - -sub notfound { - logMsg("rrddraw; Command unknown act=$Q->{act}"); +if ($Q->{act} eq 'draw_graph_view') +{ + rrdDraw(); } +else +{ + error("Command unknown act=$Q->{act}"); +} +exit 0; -#============================================================================ - -sub error { - print header($headeropts); - print start_html(); - print "Network: ERROR on getting graph
\n"; - print "Request not found\n"; - print end_html; +sub error +{ + my ($msg) = @_; + print header($headeropts), start_html(), + ($msg || "Request not found\n"), end_html; } # produce one graph # args: pretty much all coming from a global $Q object -# returns: rrds::graph result array +# returns: rrds::graph result array or empty sub rrdDraw { my %args = @_; # Break the query up for the names - my $type = $Q->{obj}; my $nodename = $Q->{node}; my $debug = $Q->{debug}; my $grp = $Q->{group}; my $graphtype = $Q->{graphtype}; - my $graphstart = $Q->{graphstart}; + my $graphstart = $Q->{graphstart}; # fixme not used anywhere? + my $width = $Q->{width}; my $height = $Q->{height}; my $start = $Q->{start}; @@ -113,585 +111,24 @@ sub rrdDraw my $intf = $Q->{intf}; my $item = $Q->{item}; my $filename = $Q->{filename}; - - my $S = Sys::->new; # get system object - $S->init(name=>$nodename, snmp=>'false'); - my $NI = $S->ndinfo; - my $IF = $S->ifinfo; - - ### 2012-02-06 keiths, handling default graph length - # default is hours! - my $graphlength = $C->{graph_amount}; - if ( $C->{graph_unit} eq "days" ) - { - $graphlength = $C->{graph_amount} * 24; - } - - if ( $start eq "" or $start == 0) - { - $start = time() - ($graphlength*3600); - } - if ( $end eq "" or $end == 0) { - $end = time(); - } - - my $ERROR; - my $graphret; - my $xs; - my $ys; - my @options; - my @opt; - my $db; - - if ($graphtype eq 'metrics') - { - $item = $Q->{group}; - $intf = ""; - } - - # special graphtypes: cbqos is dynamic (multiple inputs create one graph), ditto calls - if ($graphtype =~ /cbqos/) - { - @opt = graphCBQoS(sys=>$S, - graphtype=>$graphtype, - intf=>$intf, - item=>$item, - start=>$start,end=>$end,width=>$width,height=>$height); - } - elsif ($graphtype eq "calls") - { - @opt = graphCalls(sys=>$S,graphtype=>$graphtype,intf=>$intf,item=>$item,start=>$start,end=>$end,width=>$width,height=>$height); - } - else - { - if (!($db = $S->getDBName(graphtype=>$graphtype,index=>$intf,item=>$item)) ) { # get database name from node info - error(); - return 0; - } - - my $graph; - if (!($graph = loadTable(dir=>'models',name=>"Graph-$graphtype")) - or !keys %$graph ) - { - logMsg("ERROR failed to read Graph-$graphtype!"); - error(); - return 0; - } - - my $title = 'standard'; - my $vlabel = 'standard'; - my $size = 'standard'; - my $ttl; - my $lbl; - - $title = 'short' if $width <= 400 and $graph->{title}{short} ne ""; - $vlabel = 'short' if $width <= 400 and $graph->{vlabel}{short} ne ""; - $vlabel = 'split' if getbool($C->{graph_split}) and $graph->{vlabel}{split} ne ""; - $size = 'small' if $width <= 400 and $graph->{option}{small} ne ""; - - - if (($ttl = $graph->{title}{$title}) eq "") - { - logMsg("no title->$title found in Graph-$graphtype"); - } - if (($lbl = $graph->{vlabel}{$vlabel}) eq "") - { - logMsg("no vlabel->$vlabel found in Graph-$graphtype"); - } - - @opt = ( - "--title", $ttl, - "--vertical-label", $lbl, - "--start", $start, - "--end", $end, - "--width", $width, - "--height", $height, - "--imgformat", "PNG", - "--interlaced", - "--disable-rrdtool-tag", - "--color", 'BACK#ffffff', # Background Color - "--color", 'SHADEA#ffffff', # Left and Top Border Color - "--color", 'SHADEB#ffffff', # was CFCFCF - "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) - "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' - "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 - "--color", 'FONT#222222', # Font Color - "--color", 'ARROW#924040', # Arrow Color for X/Y Axis - "--color", 'FRAME#808080' # Canvas Frame Color - ); - - if ($width > 400) { - push(@opt,"--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; - } - else { - push(@opt,"--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; - } - - # add option rules - foreach my $str (@{$graph->{option}{$size}}) { - push @opt, $str; - } - } - - # define length of graph - my $l; - if (($end - $start) < 3600) { - $l = int(($end - $start) / 60) . " minutes"; - } elsif (($end - $start) < (3600*48)) { - $l = int(($end - $start) / (3600)) . " hours"; - } else { - $l = int(($end - $start) / (3600*24)) . " days"; - } - - { - # scalars must be global - no strict; # *shudder* this is so utterly wrong and broken - if ($intf ne "") - { - $indx = $intf; - - $ifDescr = $IF->{$intf}{ifDescr}; - $ifSpeed = $IF->{$intf}{ifSpeed}; - $ifSpeedIn = $IF->{$intf}{ifSpeed}; - $ifSpeedOut = $IF->{$intf}{ifSpeed}; - $ifSpeedIn = $IF->{$intf}{ifSpeedIn} if $IF->{$intf}{ifSpeedIn}; - $ifSpeedOut = $IF->{$intf}{ifSpeedOut} if $IF->{$intf}{ifSpeedOut}; - if ($ifSpeed eq "auto" ) { - $ifSpeed = 10000000; - } - - if ( $IF->{$intf}{ifSpeedIn} and $IF->{$intf}{ifSpeedOut} ) { - $speed = "IN\\: ". convertIfSpeed($ifSpeedIn) ." OUT\\: ". convertIfSpeed($ifSpeedOut); - } - else { - $speed = convertIfSpeed($ifSpeed); - } - } - $node = $NI->{system}{name}; - $datestamp_start = returnDateStamp($start); - $datestamp_end = returnDateStamp($end); - $datestamp = returnDateStamp(time); - $database = $db; - - $group = $grp; - $itm = $item; - $length = $l; - $split = getbool($C->{graph_split}) ? -1 : 1 ; - $GLINE = getbool($C->{graph_split}) ? "AREA" : "LINE1" ; - $weight = 0.983; - - for my $idx (0..$#opt) - { - my $str = $opt[$idx]; - - # escape any ':' chars which might be in the database name (e.g C:\\) or the other - # inputs (e.g. indx == service name). this must be done for ALL substitutables, - # but no thanks to no strict we don't exactly know who they are, nor can we safely change - # their values without side-effects...so we do it on the go, and only where not already pre-escaped. - - # EXCEPT in --title, where we can't have colon escaping. grrrrrr! - if ($idx > 0 && $opt[$idx-1] eq "--title") - { - $str =~ s{\$(\w+)}{if(defined${$1}){${$1};}else{"ERROR, no variable \'\$$1\' ";}}egx; - } - else - { - $str =~ s{\$(\w+)}{if(defined${$1}){NMIS::postcolonial(${$1});}else{"ERROR, no variable \'\$$1\' ";}}egx; - } - - if ($str =~ /ERROR/) { - logMsg("ERROR in expanding variables, $str"); - return; - } - push @options,$str; - } - } - - # Do the graph! - if (!$filename) + my $when = $Q->{time}; + + my ($error, $graphret) = NMIS::RRDdraw::draw(node => $nodename, + group => $grp, + graphtype => $graphtype, + intf => $intf, + item => $item, + width => $width, + height => $height, + filename => $filename, + start => $start, + end => $end, + debug => $debug, + time => $when); + if ($error) { - # buffer stdout to avoid Apache timing out on the header tag while waiting for the PNG image stream from RRDs - select((select(STDOUT), $| = 1)[0]); - print "Content-type: image/png\n\n"; - ($graphret,$xs,$ys) = RRDs::graph('-', @options); - select((select(STDOUT), $| = 0)[0]); # unbuffer stdout - } - else { - ($graphret,$xs,$ys) = RRDs::graph($filename, @options); - } - - if ($ERROR = RRDs::error) - { - logMsg("$db Graphing Error for $graphtype: $ERROR"); + error("rrddraw failed: $error"); + return; } return $graphret; } - - -# CBQoS Support -# this handles both cisco and huawei flavour cbqos -sub graphCBQoS -{ - my %args = @_; - my $S = $args{sys}; - my $NI = $S->ndinfo; - my $IF = $S->ifinfo; - my $graphtype = $args{graphtype}; - my $intf = $args{intf}; - my $item = $args{item}; - my $start = $args{start}; - my $end = $args{end}; - my $width = $args{width}; - my $height = $args{height}; - my $debug = $Q->{debug}; - - my $database; - my @opt; - my $title; - - # order the names, find colors and bandwidth limits, index and section names - my ($CBQosNames,$CBQosValues) = NMIS::loadCBQoS(sys=>$S, graphtype=>$graphtype, index=>$intf); - - if ( $item eq "" ) { - # display all class-maps in one graph - my $i; - my $avgppr; - my $maxppr; - my $avgdbr; - my $maxdbr; - my $direction = ($graphtype eq "cbqos-in") ? "input" : "output" ; - my $ifDescr = shortInterface($IF->{$intf}{ifDescr}); - my $vlabel = "Avg Bits per Second"; - if ( $width <= 400 ) { - $title = "$NI->{name} $ifDescr $direction"; - $title .= " - $CBQosNames->[0]" if ($CBQosNames->[0] && $CBQosNames->[0] !~ /^(in|out)bound$/i); - $title .= ' - $length'; - $vlabel = "Avg bps"; - } else { - $title = "$NI->{name} $ifDescr $direction - CBQoS from ".'$datestamp_start to $datestamp_end'; - } - - @opt = ( - "--title", $title, - "--vertical-label",$vlabel, - "--start", "$start", - "--end", "$end", - "--width", "$width", - "--height", "$height", - "--imgformat", "PNG", - "--interlaced", - "--disable-rrdtool-tag", - "--color", 'BACK#ffffff', # Background Color - "--color", 'SHADEA#ffffff', # Left and Top Border Color - "--color", 'SHADEB#ffffff', # - "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) - "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' - "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 - "--color", 'FONT#222222', # Font Color - "--color", 'ARROW#924040', # Arrow Color for X/Y Axis - "--color", 'FRAME#808080' # Canvas Frame Color - ); - - if ($width > 400) { - push(@opt,"--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; - } - else { - push(@opt,"--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; - } - - # calculate the sum (avg and max) of all Classmaps for PrePolicy and Drop - # note that these CANNOT be graphed by themselves, as 0 isn't a valid RPN expression in rrdtool - $avgppr = "CDEF:avgPrePolicyBitrate=0"; - $maxppr = "CDEF:maxPrePolicyBitrate=0"; - $avgdbr = "CDEF:avgDropBitrate=0"; - $maxdbr = "CDEF:maxDropBitrate=0"; - - # is this hierarchical or flat? - my $HQOS = 0; - foreach my $i (1..$#$CBQosNames) { - if ( $CBQosNames->[$i] =~ /^([\w\-]+)\-\-\w+\-\-/ ) { - $HQOS = 1; - } - } - - my $gtype = "AREA"; - my $gcount = 0; - my $parent_name = ""; - foreach my $i (1..$#$CBQosNames) - { - my $thisinfo = $CBQosValues->{$intf.$CBQosNames->[$i]}; - - $database = $S->getDBName(graphtype => $thisinfo->{CfgSection}, - index => $thisinfo->{CfgIndex}, - item => $CBQosNames->[$i] ); - my $parent = 0; - if ( $CBQosNames->[$i] !~ /\w+\-\-\w+/ and $HQOS ) { - $parent = 1; - $gtype = "LINE1"; - } - - if ( $CBQosNames->[$i] =~ /^([\w\-]+)\-\-\w+\-\-/ ) { - $parent_name = $1; - print STDERR "DEBUG parent_name=$parent_name\n" if ($debug); - } - - if ( not $parent and not $gcount) { - $gtype = "AREA"; - ++$gcount; - } - elsif ( not $parent and $gcount) { - $gtype = "STACK"; - ++$gcount; - } - my $alias = $CBQosNames->[$i]; - $alias =~ s/$parent_name\-\-//g; - $alias =~ s/\-\-/\//g; - - # rough alignment for the columns, necessarily imperfect - # as X-char strings aren't equally wide... - my $tab = "\\t"; - if ( length($alias) <= 5 ) { - $tab = $tab x 4; - } - elsif ( length($alias) <= 14 ) { - $tab = $tab x 3; - } - elsif ( length($alias) <= 19 ) { - $tab = $tab x 2; - } - - my $color = $CBQosValues->{$intf.$CBQosNames->[$i]}{'Color'}; - - push(@opt,"DEF:avgPPB$i=$database:".$thisinfo->{CfgDSNames}->[0].":AVERAGE"); - push(@opt,"DEF:maxPPB$i=$database:".$thisinfo->{CfgDSNames}->[0].":MAX"); - push(@opt,"DEF:avgDB$i=$database:".$thisinfo->{CfgDSNames}->[2].":AVERAGE"); - push(@opt,"DEF:maxDB$i=$database:".$thisinfo->{CfgDSNames}->[2].":MAX"); - - push(@opt,"CDEF:avgPPR$i=avgPPB$i,8,*"); - push(@opt,"CDEF:maxPPR$i=maxPPB$i,8,*"); - push(@opt,"CDEF:avgDBR$i=avgDB$i,8,*"); - push(@opt,"CDEF:maxDBR$i=maxDB$i,8,*"); - - if ($width > 400) { - push @opt,"$gtype:avgPPR$i#$color:$alias$tab"; - push(@opt,"GPRINT:avgPPR$i:AVERAGE:Avg %8.2lf%s\\t"); - push(@opt,"GPRINT:maxPPR$i:MAX:Max %8.2lf%s\\t"); - push(@opt,"GPRINT:avgDBR$i:AVERAGE:Avg Drops %6.2lf%s\\t"); - push(@opt,"GPRINT:maxDBR$i:MAX:Max Drops %6.2lf%s\\l"); - } - else { - push(@opt,"$gtype:avgPPR$i#$color:$alias"); - } - - #push(@opt,"LINE1:avgPPR$i#$color:$CBQosNames->[$i]"); - $avgppr = $avgppr.",avgPPR$i,+"; - $maxppr = $maxppr.",maxPPR$i,+"; - $avgdbr = $avgdbr.",avgDBR$i,+"; - $maxdbr = $maxdbr.",maxDBR$i,+"; - } - push(@opt,$avgppr); - push(@opt,$maxppr); - push(@opt,$avgdbr); - push(@opt,$maxdbr); - - if ($width > 400) { - push(@opt,"COMMENT:\\l"); - push(@opt,"GPRINT:avgPrePolicyBitrate:AVERAGE:PrePolicyBitrate\\t\\t\\tAvg %8.2lf%s\\t"); - push(@opt,"GPRINT:maxPrePolicyBitrate:MAX:Max\\t%8.2lf%s\\l"); - push(@opt,"GPRINT:avgDropBitrate:AVERAGE:DropBitrate\\t\\t\\tAvg %8.2lf%s\\t"); - push(@opt,"GPRINT:maxDropBitrate:MAX:Max\\t%8.2lf%s\\l"); - } - - } else { - # display ONLY the selected class-map - my $thisinfo = $CBQosValues->{$intf.$item}; - - my $speed = defined $thisinfo->{CfgRate}? &convertIfSpeed($thisinfo->{'CfgRate'}) : undef; - my $direction = ($graphtype eq "cbqos-in") ? "input" : "output" ; - - $database = $S->getDBName(graphtype => $thisinfo->{CfgSection}, - index => $thisinfo->{CfgIndex}, - item => $item ); - - # in this case we always use the FIRST color, not the one for this item - my $color = $CBQosValues->{$intf.$CBQosNames->[1]}->{'Color'}; - - my $ifDescr = shortInterface($IF->{$intf}{ifDescr}); - $title = "$ifDescr $direction - $item from ".'$datestamp_start to $datestamp_end'; - - @opt = ( - "--title", "$title", - "--vertical-label", 'Avg Bits per Second', - "--start", "$start", - "--end", "$end", - "--width", "$width", - "--height", "$height", - "--imgformat", "PNG", - "--interlaced", - "--disable-rrdtool-tag", - "--color", 'BACK#ffffff', # Background Color - "--color", 'SHADEA#ffffff', # Left and Top Border Color - "--color", 'SHADEB#ffffff', # - "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) - "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' - "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 - "--color", 'FONT#222222', # Font Color - "--color", 'ARROW#924040', # Arrow Color for X/Y Axis - "--color", 'FRAME#808080', # Canvas Frame Color - ); - - if ($width > 400) { - push(@opt,"--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; - } - else { - push(@opt,"--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; - } - - # needs to work for both types of qos, hence uses the CfgDSNames - push @opt, ( - "DEF:PrePolicyByte=$database:".$thisinfo->{CfgDSNames}->[0].":AVERAGE", - "DEF:maxPrePolicyByte=$database:".$thisinfo->{CfgDSNames}->[0].":MAX", - "DEF:DropByte=$database:".$thisinfo->{CfgDSNames}->[2].":AVERAGE", - "DEF:maxDropByte=$database:".$thisinfo->{CfgDSNames}->[2].":MAX", - "DEF:PrePolicyPkt=$database:".$thisinfo->{CfgDSNames}->[3].":AVERAGE", - "DEF:DropPkt=$database:".$thisinfo->{CfgDSNames}->[5].":AVERAGE"); - - # huawei doesn't have NoBufDropPkt - push @opt, "DEF:NoBufDropPkt=$database:".$thisinfo->{CfgDSNames}->[6].":AVERAGE" - if (defined $thisinfo->{CfgDSNames}->[6]); - - push @opt, ( - "CDEF:PrePolicyBitrate=PrePolicyByte,8,*", - "CDEF:maxPrePolicyBitrate=maxPrePolicyByte,8,*", - "CDEF:DropBitrate=DropByte,8,*", - "TEXTALIGN:left", - "AREA:PrePolicyBitrate#$color:PrePolicyBitrate", - ); - - # detailed legends are only shown on the 'big' graphs - if ($width > 400) { - push(@opt,"GPRINT:PrePolicyBitrate:AVERAGE:\\tAvg %8.2lf %sbps\\t"); - push(@opt,"GPRINT:maxPrePolicyBitrate:MAX:Max %8.2lf %sbps"); - } - # move back to previous line, then right-align - push @opt, "COMMENT:\\u", "AREA:DropBitrate#ff0000:DropBitrate\\r:STACK"; - - if ($width > 400) - { - push(@opt,"GPRINT:PrePolicyByte:AVERAGE:Bytes transferred\\t\\tAvg %8.2lf %sB/s\\n"); - - push(@opt,"GPRINT:DropByte:AVERAGE:Bytes dropped\\t\\t\\tAvg %8.2lf %sB/s\\t"); - push(@opt,"GPRINT:maxDropByte:MAX:Max %8.2lf %sB/s\\n"); - - push(@opt,"GPRINT:PrePolicyPkt:AVERAGE:Packets transferred\\t\\tAvg %8.2lf\\l"); - push(@opt,"GPRINT:DropPkt:AVERAGE:Packets dropped\\t\\t\\tAvg %8.2lf"); - - # huawei doesn't have that - push(@opt,"COMMENT:\\l","GPRINT:NoBufDropPkt:AVERAGE:Packets No buffer dropped\\tAvg %8.2lf\\l") - if (defined $thisinfo->{CfgDSNames}->[6]); - - # not all qos setups have a graphable bandwidth limit - push @opt, "COMMENT:\\u", "COMMENT:".$thisinfo->{CfgType}." $speed\\r" if (defined $speed); - } - } - - return @opt; -} - -sub graphCalls -{ - my %args = @_; - my $S = $args{sys}; - my $NI = $S->ndinfo; - my $IF = $S->ifinfo; - my $graphtype = $args{graphtype}; - my $intf = $args{intf}; - my $start = $args{start}; - my $end = $args{end}; - my $width = $args{width}; - my $height = $args{height}; - - my $database; - my @opt; - my $title; - - my $device = ($intf eq "") ? "total" : $IF->{$intf}{ifDescr}; - if ( $width <= 400 ) { $title = "$NI->{name} Calls ".'$length'; } - else { $title = "$NI->{name} - $device - ".'$length from $datestamp_start to $datestamp_end'; } - - # display Calls summarized or only one port - @opt = ( - "--title", $title, - "--vertical-label","Call Stats", - "--start", "$start", - "--end", "$end", - "--width", "$width", - "--height", "$height", - "--imgformat", "PNG", - "--interlaced", - "--disable-rrdtool-tag" - ); - - my $CallCount = "CDEF:CallCount=0"; - my $AvailableCallCount = "CDEF:AvailableCallCount=0"; - my $totalIdle = "CDEF:totalIdle=0"; - my $totalUnknown = "CDEF:totalUnknown=0"; - my $totalAnalog = "CDEF:totalAnalog=0"; - my $totalDigital = "CDEF:totalDigital=0"; - my $totalV110 = "CDEF:totalV110=0"; - my $totalV120 = "CDEF:totalV120=0"; - my $totalVoice = "CDEF:totalVoice=0"; - - - foreach my $i ($S->getTypeInstances(section => 'calls')) { - next unless $intf eq "" or $intf eq $i; - $database = $S->getDBName(graphtype => 'calls', - index => $i); - next if (!$database); - - push(@opt,"DEF:CallCount$i=$database:CallCount:MAX"); - push(@opt,"DEF:AvailableCallCount$i=$database:AvailableCallCount:MAX"); - push(@opt,"DEF:totalIdle$i=$database:totalIdle:MAX"); - push(@opt,"DEF:totalUnknown$i=$database:totalUnknown:MAX"); - push(@opt,"DEF:totalAnalog$i=$database:totalAnalog:MAX"); - push(@opt,"DEF:totalDigital$i=$database:totalDigital:MAX"); - push(@opt,"DEF:totalV110$i=$database:totalV110:MAX"); - push(@opt,"DEF:totalV120$i=$database:totalV120:MAX"); - push(@opt,"DEF:totalVoice$i=$database:totalVoice:MAX"); - - $CallCount .= ",CallCount$i,+"; - $AvailableCallCount .= ",AvailableCallCount$i,+"; - $totalIdle .= ",totalIdle$i,+"; - $totalUnknown .= ",totalUnknown$i,+"; - $totalAnalog .= ",totalAnalog$i,+"; - $totalDigital .= ",totalDigital$i,+"; - $totalV110 .= ",totalV110$i,+"; - $totalV120 .= ",totalV120$i,+"; - $totalVoice .= ",totalVoice$i,+"; - if ($intf ne "") { last; } - } - - push(@opt,$CallCount); - push(@opt,$AvailableCallCount); - push(@opt,$totalIdle); - push(@opt,$totalUnknown); - push(@opt,$totalAnalog); - push(@opt,$totalDigital); - push(@opt,$totalV110); - push(@opt,$totalV120); - push(@opt,$totalVoice); - - push(@opt,"LINE1:AvailableCallCount#FFFF00:AvailableCallCount"); - push(@opt,"LINE2:totalIdle#000000:totalIdle"); - push(@opt,"LINE2:totalUnknown#FF0000:totalUnknown"); - push(@opt,"LINE2:totalAnalog#00FFFF:totalAnalog"); - push(@opt,"LINE2:totalDigital#0000FF:totalDigital"); - push(@opt,"LINE2:totalV110#FF0080:totalV110"); - push(@opt,"LINE2:totalV120#800080:totalV120"); - push(@opt,"LINE2:totalVoice#00FF00:totalVoice"); - push(@opt,"COMMENT:\\l"); - push(@opt,"GPRINT:AvailableCallCount:MAX:Available Call Count %1.2lf"); - push(@opt,"GPRINT:CallCount:MAX:Total Call Count %1.0lf"); - - return @opt; -} diff --git a/cgi-bin/tables.pl b/cgi-bin/tables.pl index 5630d88..641b72c 100755 --- a/cgi-bin/tables.pl +++ b/cgi-bin/tables.pl @@ -28,7 +28,7 @@ # # ***************************************************************************** use strict; -our $VERSION="8.6.6G"; +our $VERSION="8.6.7G"; use FindBin; use lib "$FindBin::Bin/../lib"; @@ -615,7 +615,7 @@ sub doeditTable # "float" => [ min, max, above, below ] - rejects X < min or X <= above, X > max or X >= below # that's required to express 'positive float' === strictly above zero: [0 or undef,dontcare,0,dontcare] # "resolvable" => [ 4 or 6 or 4, 6] - accepts ip of that type or hostname that resolves to that ip type - # "int-or-empty", "float-or-empty", "resolvable-or-empty" work like their namesakes, + # "int-or-empty", "float-or-empty", "resolvable-or-empty", "regex-or-empty" work like their namesakes, # but accept nothing/blank/undef as well. # "regex" => qr//, # "ip" => [ 4 or 6 or 4, 6], @@ -660,11 +660,16 @@ sub doeditTable } } } - elsif ($valtype eq "regex") + elsif ($valtype =~ /^regex(-or-empty)?$/) { - my $expected = ref($valprops) eq "Regexp"? $valprops : qr//; # fallback will match anything - return validation_abort($item, "'$value' didn't match regular expression!") - if ($value !~ $expected); + my $emptyisok = $1; + + if (!$emptyisok or (defined($value) and $value ne "")) + { + my $expected = ref($valprops) eq "Regexp"? $valprops : qr//; # fallback will match anything + return validation_abort($item, "'$value' didn't match regular expression!") + if ($value !~ $expected); + } } elsif ($valtype eq "ip") { @@ -753,6 +758,7 @@ sub doeditTable # renaming? if ($new_name && $new_name ne $thisentry->{name}) { + # this rewrites nodes.nmis twice, by necessity; backs out nodes.nmis if unsuccessful my ($error,$message) = NMIS::rename_node(old => $thisentry->{name}, new => $new_name, originator => "tables.pl.editNodeTable"); diff --git a/install.pl b/install.pl index bdf9f8a..1ab9081 100755 --- a/install.pl +++ b/install.pl @@ -877,7 +877,7 @@ package installation without Internet access in that case: # patch config changes that affect existing entries, which update_config_defaults # doesn't handle # which includes enabling uuid and showing the polling_policy - execPrint("$site/admin/patch_config.pl -b $site/conf/Config.nmis /system/non_stateful_events='Node Configuration Change, Node Reset, NMIS runtime exceeded' /globals/uuid_add_with_node=true /system/node_summary_field_list,=uuid /system/json_node_fields,=uuid /system/network_viewNode_field_list,=polling_policy"); + execPrint("$site/admin/patch_config.pl -b $site/conf/Config.nmis /system/non_stateful_events='Node Configuration Change, Node Reset, NMIS runtime exceeded' /system/node_summary_field_list,=uuid /system/json_node_fields,=uuid /system/network_viewNode_field_list,=polling_policy"); echolog("\n"); echolog("By default this version NMIS demotes nodes that have never @@ -1242,20 +1242,17 @@ package installation without Internet access in that case: printBanner("Checking configuration and fixing file permissions (takes a few minutes) ..."); execPrint("$site/bin/nmis.pl type=config info=true"); -if ($isnewinstall) -{ - printBanner("Integration with Apache"); - - # determine apache version - my $prog = $osflavour eq "redhat"? "httpd" : "apache2"; - my $versioninfo = `$prog -v 2>/dev/null`; - $versioninfo =~ s/^.*Apache\/(\d+\.\d+\.\d+).*$/$1/s; - my $istwofour = ($versioninfo =~ /^2\.4\./); +printBanner("Integration with Apache"); +# determine apache version +my $prog = $osflavour eq "redhat"? "httpd" : "apache2"; +my $versioninfo = `$prog -v 2>/dev/null`; +$versioninfo =~ s/^.*Apache\/(\d+\.\d+\.\d+).*$/$1/s; +my $istwofour = ($versioninfo =~ /^2\.4\./); - if (!$versioninfo) - { - echolog("No Apache found!"); - print " +if (!$versioninfo) +{ + echolog("No Apache found!"); + print " It seems that you don't have Apache 2.x installed, so the installer can't configure Apache for NMIS. @@ -1267,55 +1264,76 @@ package installation without Internet access in that case: NMIS Installation guide at https://community.opmantek.com/x/Dgh4 for further info.\n"; - &input_ok; - } - else - { - echolog("Found Apache version $versioninfo"); + &input_ok; +} +else +{ + echolog("Found Apache version $versioninfo"); - my $apacheconf = "nmis.conf"; - my $res = system("$site/bin/nmis.pl type=" - .($istwofour?"apache24":"apache")." > /tmp/$apacheconf"); - my $finaltarget = $osflavour eq "redhat"? - "/etc/httpd/conf.d/$apacheconf" : - ($osflavour eq "debian" or $osflavour eq "ubuntu")? "/etc/apache2/sites-available/$apacheconf" : undef; + # older vms ship 00nmis.conf, which definitely needs to be replaced + my $oldvmconfig = "/etc/httpd/conf.d/00nmis.conf"; + unlink($oldvmconfig) if (-e $oldvmconfig); - if ($finaltarget - && input_yn("Ok to install Apache config file to $finaltarget?")) - { - execPrint("mv /tmp/$apacheconf $finaltarget"); - execPrint("ln -s $finaltarget /etc/apache2/sites-enabled/") - if (-d "/etc/apache2/sites-enabled" && !-l "/etc/apache2/sites-enabled/$apacheconf"); + my $apacheconf = "nmis.conf"; + my $res = system("$site/bin/nmis.pl type=" + .($istwofour?"apache24":"apache")." > /tmp/$apacheconf"); + my $finaltarget = $osflavour eq "redhat"? + "/etc/httpd/conf.d/$apacheconf" : + ($osflavour eq "debian" or $osflavour eq "ubuntu")? "/etc/apache2/sites-available/$apacheconf" : undef; - # meh. rh/centos doesn't have a2enmod - if ($istwofour && $osflavour ne "redhat") - { - execPrint("a2enmod cgi"); - } + my $copyneeded = (!-f $finaltarget); + my $copyok = ($copyneeded && input_yn("Ok to install Apache config file to $finaltarget?")); - if ($osflavour eq "redhat") - { - execPrint("usermod -G nmis apache"); - execPrint("service httpd restart"); - } - elsif ($osflavour eq "debian" or $osflavour eq "ubuntu") - { - execPrint("adduser www-data nmis"); - execPrint("service apache2 restart"); - } + if (-f $finaltarget) + { + # diff exits 0 if no changes, 1 otherwise if (-f $finaltarget) + my $isdifferent = system("diff", "-q", $finaltarget, "/tmp/$apacheconf") >> 8; + $copyneeded = $isdifferent; + if ($isdifferent) + { + echolog("Existing Apache config is different from shipped config."); + $copyok = input_yn("Ok to update Apache config file at $finaltarget?"); } else { - echolog("Continuing without Apache configuration."); - print "You will need to integrate NMIS with your + echolog("Existing Apache config is uptodate."); + } + } + + if ($copyneeded && $copyok) + { + execPrint("mv /tmp/$apacheconf $finaltarget"); + execPrint("ln -s $finaltarget /etc/apache2/sites-enabled/") + if (-d "/etc/apache2/sites-enabled" && !-l "/etc/apache2/sites-enabled/$apacheconf"); + + # meh. rh/centos doesn't have a2enmod + if ($istwofour && $osflavour ne "redhat") + { + execPrint("a2enmod cgi"); + } + + if ($osflavour eq "redhat") + { + execPrint("usermod -G nmis apache"); + execPrint("service httpd restart"); + } + elsif ($osflavour eq "debian" or $osflavour eq "ubuntu") + { + execPrint("adduser www-data nmis"); + execPrint("service apache2 restart"); + } + } + elsif ($copyneeded) # ie rejected + { + echolog("Continuing without Apache configuration update."); + print "You will need to integrate NMIS with your web server manually. Please use the output of 'nmis.pl type=apache' (or type=apache24) and check the NMIS Installation guide at https://community.opmantek.com/x/Dgh4 for further info.\n"; - &input_ok; - } + &input_ok; } } diff --git a/install/Config.nmis b/install/Config.nmis index 39f528c..6d10ad4 100644 --- a/install/Config.nmis +++ b/install/Config.nmis @@ -60,9 +60,10 @@ my %hash = ( 'global_noevent_Description' => '', 'global_noevent_ifType' => '', 'global_events_bandwidth' => 'true', - 'uuid_add_with_node' => 'true', 'uuid_namespace_type' => 'NameSpace_URL', - 'uuid_namespace_name' => 'www.domain.com' + 'uuid_namespace_name' => 'www.domain.com', + + 'node_name_rule' => qr/^[a-zA-Z0-9_. -]+$/, # note: rule MUST exclude '/' }, 'system' => { @@ -93,6 +94,8 @@ my %hash = ( # to have additional fields in the node summary data. 'node_summary_field_list' => 'host,uuid,location,customer,businessService,serviceStatus,snmpdown,wmidown', + 'polling_interval_factor' => 0.9, # fraction of the polling interval after which node is considered for next poll + 'nmis_mthread' => 'false', 'nmis_maxthreads' => '10', @@ -103,6 +106,7 @@ my %hash = ( 'cache_var_tables' => 'true', 'page_refresh_time' => '300', 'widget_refresh_time' => '180', + 'graph_cache_maxage' => 120, # set to zero to disable graph caching 'os_posix' => 'false', 'os_cmd_read_file_reverse' => 'tac', 'os_cmd_file_decompress' => 'gzip -d -c', @@ -216,7 +220,7 @@ my %hash = ( 'files' => { 'nmis' => '/nmiscgi.pl', - 'rrddraw' => '/rrddraw.pl', #'rrddraw' => '/opmantek_rrddraw.pl', + 'rrddraw' => '/rrddraw.pl', 'network' => '/network.pl', 'node' => '/node.pl', 'ipsla' => '/ipsla.pl', @@ -504,6 +508,7 @@ my %hash = ( 'purge_jsonlog_after' => 30*86400, # json log files 'purge_reports_after' => 365*86400, # html reports 'purge_outages_after' => 86400, # past non-recurring outages + 'purge_graphcache_after' => 3600, # cached graph images }, 'gui' => { diff --git a/install/Enterprise.nmis b/install/Enterprise.nmis index 3d98586..aa3e33b 100644 --- a/install/Enterprise.nmis +++ b/install/Enterprise.nmis @@ -73724,4 +73724,8 @@ 'Enterprise' => 'Oplink Communications, Inc.', 'OID' => '19547' }, + '664' => { + 'Enterprise' => 'Adtran', + 'OID' => '664' + }, ); diff --git a/install/Events.nmis b/install/Events.nmis index 6b29cfb..f808588 100644 --- a/install/Events.nmis +++ b/install/Events.nmis @@ -143,7 +143,17 @@ 'Log' => 'true', 'Stateful' => 'true', 'CancelingEvent' => 'Node Polling Failover Closed', - 'Description' => 'SNMP has failed over to the host_backup address.', + 'Description' => 'Ping or SNMP has failed over to the host_backup address.', + }, + + 'Backup Host Down' => { + 'Event' => 'Backup Host Down', + 'Notify' => 'true', + 'Status' => 'true', + 'Log' => 'true', + 'Stateful' => 'true', + 'CancelingEvent' => 'Backup Host Up', + 'Description' => 'The host_backup address has become unreachable.', }, 'Node Configuration Change' => { diff --git a/install/Locations.nmis b/install/Locations.nmis index 9954bfc..7f588c4 100644 --- a/install/Locations.nmis +++ b/install/Locations.nmis @@ -1,32 +1,3 @@ -# -# Copyright Opmantek Limited (www.opmantek.com) -# -# ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM -# -# This file is part of Network Management Information System ("NMIS"). -# -# NMIS 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. -# -# NMIS 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 NMIS (most likely in a file named LICENSE). -# If not, see -# -# For further information on NMIS or for a license other than GPL please see -# www.opmantek.com or email contact@opmantek.com -# -# User group details: -# http://support.opmantek.com/users/ -# -# ***************************************************************************** - %hash = ( 'default' => { 'Address1' => '', @@ -43,22 +14,7 @@ 'State' => '', 'Suburb' => '' }, - 'Cloud' => { - 'Address1' => '', - 'Address2' => '', - 'City' => '', - 'Country' => '', - 'Floor' => '', - 'Geocode' => 'St Louis, Misouri', - 'Latitude' => '38.612469', - 'Location' => 'Cloud', - 'Longitude' => '-90.198830', - 'Postcode' => '', - 'Room' => '', - 'State' => '', - 'Suburb' => '' - }, - 'HeadOffice' => { + 'Head Office' => { 'Address1' => '', 'Address2' => '', 'City' => '', @@ -66,14 +22,14 @@ 'Floor' => '', 'Geocode' => 'Charlotte, NC', 'Latitude' => '35.215215', - 'Location' => 'Cloud', + 'Location' => 'Head Office', 'Longitude' => '-80.840213', 'Postcode' => '', 'Room' => '', 'State' => '', 'Suburb' => '' }, - 'DataCenter' => { + 'Data Center' => { 'Address1' => '', 'Address2' => '', 'City' => '', @@ -81,11 +37,477 @@ 'Floor' => '', 'Geocode' => 'Lenior, NC', 'Latitude' => '35.914635', - 'Location' => 'DataCenter', + 'Location' => 'Data Center', 'Longitude' => '-81.526108', 'Postcode' => '', 'Room' => '', 'State' => '', 'Suburb' => '' }, + 'Kabul' => { + 'Country' => 'Afghanistan', + 'Location' => 'Kabul', + 'City' => 'Kabul', + 'Latitude' => '34.543896', + 'Longitude' => '69.160652', + }, + 'Buenos Aires' => { + 'Country' => 'Argentina', + 'Location' => 'Buenos Aires', + 'City' => 'Buenos Aires', + 'Latitude' => '-34.603722', + 'Longitude' => '-58.381592', + }, + 'Sydney' => { + 'Country' => 'Australia', + 'Location' => 'Sydney', + 'City' => 'Sydney', + 'Latitude' => '-33.865143', + 'Longitude' => '151.209900', + }, + 'Brisbane' => { + 'Country' => 'Australia', + 'Location' => 'Brisbane', + 'City' => 'Brisbane', + 'Latitude' => '-27.470125', + 'Longitude' => '153.021072', + }, + 'Gold Coast' => { + 'Country' => 'Australia', + 'Location' => 'Gold Coast', + 'City' => 'Gold Coast', + 'Latitude' => '-28.0167', + 'Longitude' => '153.4000', + }, + 'Dhaka' => { + 'Country' => 'Bangladesh', + 'Location' => 'Dhaka', + 'City' => 'Dhaka', + 'Latitude' => '23.777176', + 'Longitude' => '90.399452', + }, + 'Brussels' => { + 'Country' => 'Belgium', + 'Location' => 'Brussels', + 'City' => 'Brussels', + 'Latitude' => '50.85045', + 'Longitude' => '4.34878', + }, + 'Sao Paulo' => { + 'Country' => 'Brazil', + 'Location' => 'Sao Paulo', + 'City' => 'Sao Paulo', + 'Latitude' => '-23.533773', + 'Longitude' => '-46.625290', + }, + 'Rio De Janeiro' => { + 'Country' => 'Brazil', + 'Location' => 'Rio De Janeiro', + 'City' => 'Rio De Janeiro', + 'Latitude' => '-22.752754', + 'Longitude' => '-42.864876', + }, + 'Quebec' => { + 'Country' => 'Canada', + 'Location' => 'Quebec', + 'City' => 'Quebec', + 'Latitude' => '46.803284', + 'Longitude' => '-71.242798', + }, + 'Winnipeg' => { + 'Country' => 'Canada', + 'Location' => 'Winnipeg', + 'City' => 'Winnipeg', + 'Latitude' => '49.895077', + 'Longitude' => '-97.138451', + }, + 'Santiago' => { + 'Country' => 'Chile', + 'Location' => 'Santiago', + 'City' => 'Santiago', + 'Latitude' => '-33.447487', + 'Longitude' => '-70.673676', + }, + 'Beijing' => { + 'Country' => 'China', + 'Location' => 'Beijing', + 'City' => 'Beijing', + 'Latitude' => '39.913818', + 'Longitude' => '116.363625', + }, + 'Bogota' => { + 'Country' => 'Colombia', + 'Location' => 'Bogota', + 'City' => 'Bogota', + 'Latitude' => '4.624335', + 'Longitude' => '-74.063644', + }, + 'Cali' => { + 'Country' => 'Colombia', + 'Location' => 'Cali', + 'City' => 'Cali', + 'Latitude' => '3.359889', + 'Longitude' => '-76.638565', + }, + 'San Jose' => { + 'Country' => 'Costa Rica', + 'Location' => 'San Jose', + 'City' => 'San Jose', + 'Latitude' => '9.934739', + 'Longitude' => '-84.087502', + }, + 'Havana' => { + 'Country' => 'Cuba', + 'Location' => 'Havana', + 'City' => 'Havana', + 'Latitude' => '23.113592', + 'Longitude' => '-82.366592', + }, + 'Copenhagen' => { + 'Country' => 'Denmark', + 'Location' => 'Copenhagen', + 'City' => 'Copenhagen', + 'Latitude' => '55.676098', + 'Longitude' => '12.568337', + }, + 'Santo Domingo' => { + 'Country' => 'Dominican Republic', + 'Location' => 'Santo Domingo', + 'City' => 'Santo Domingo', + 'Latitude' => '18.483402', + 'Longitude' => '-69.929611', + }, + 'Quito' => { + 'Country' => 'Ecuador', + 'Location' => 'Quito', + 'City' => 'Quito', + 'Latitude' => '-0.180653', + 'Longitude' => '-78.467834', + }, + 'Cairo' => { + 'Country' => 'Egypt', + 'Location' => 'Cairo', + 'City' => 'Cairo', + 'Latitude' => '30.044420', + 'Longitude' => '31.235712', + }, + 'San Salvador' => { + 'Country' => 'El Salvador', + 'Location' => 'San Salvador', + 'City' => 'San Salvador', + 'Latitude' => '13.692940', + 'Longitude' => '-89.218191', + }, + 'London' => { + 'Country' => 'England', + 'Location' => 'London', + 'City' => 'London', + 'Latitude' => '51.507351', + 'Longitude' => '-0.127758', + }, + 'Helsinki' => { + 'Country' => 'Finland', + 'Location' => 'Helsinki', + 'City' => 'Helsinki', + 'Latitude' => '60.169856', + 'Longitude' => '24.938379', + }, + 'Paris' => { + 'Country' => 'France', + 'Location' => 'Paris', + 'City' => 'Paris', + 'Latitude' => '48.856614', + 'Longitude' => '2.352222', + }, + 'Berlin' => { + 'Country' => 'Germany', + 'Location' => 'Berlin', + 'City' => 'Berlin', + 'Latitude' => '52.520007', + 'Longitude' => '13.404954', + }, + 'Athens' => { + 'Country' => 'Greece', + 'Location' => 'Athens', + 'City' => 'Athens', + 'Latitude' => '37.983810', + 'Longitude' => '23.727539', + }, + 'Guatemala City' => { + 'Country' => 'Guatemala', + 'City' => 'Guatemala City', + 'Location' => 'Guatemala City', + 'Latitude' => '14.634915', + 'Longitude' => '-90.506882', + }, + 'Tegucigalpa' => { + 'Country' => 'Honduras', + 'Location' => 'Tegucigalpa', + 'City' => 'Tegucigalpa', + 'Latitude' => '14.072275', + 'Longitude' => '-87.192136', + }, + 'Budapest' => { + 'Country' => 'Hungary', + 'Location' => 'Budapest', + 'City' => 'Budapest', + 'Latitude' => '47.497912', + 'Longitude' => '19.040235', + }, + 'New Dehli' => { + 'Country' => 'India', + 'Location' => 'New Dehli', + 'City' => 'New Dehli', + 'Latitude' => '28.613939', + 'Longitude' => '77.209021', + }, + 'Jakarta' => { + 'Country' => 'Indonesia', + 'Location' => 'Jakarta', + 'City' => 'Jakarta', + 'Latitude' => '-6.175110', + 'Longitude' => '106.865039', + }, + 'Tehran' => { + 'Country' => 'Iran', + 'Location' => 'Tehran', + 'City' => 'Tehran', + 'Latitude' => '35.689197', + 'Longitude' => '51.388974', + }, + 'Baghdad' => { + 'Country' => 'Iraq', + 'Location' => 'Baghdad', + 'City' => 'Baghdad', + 'Latitude' => '33.312806', + 'Longitude' => '44.361488', + }, + 'Dublin' => { + 'Country' => 'Ireland', + 'Location' => 'Dublin', + 'City' => 'Dublin', + 'Latitude' => '53.349805', + 'Longitude' => '-6.260310', + }, + 'Jerusalem' => { + 'Country' => 'Israel', + 'Location' => 'Jerusalem', + 'City' => 'Jerusalem', + 'Latitude' => '31.768319', + 'Longitude' => '35.213710', + }, + 'Rome' => { + 'Country' => 'Italy', + 'Location' => 'Rome', + 'City' => 'Rome', + 'Latitude' => '41.902783', + 'Longitude' => '12.496366', + }, + 'Kingston' => { + 'Country' => 'Jamaica', + 'Location' => 'Kingston', + 'City' => 'Kingston', + 'Latitude' => '18.017874', + 'Longitude' => '-76.809904', + }, + 'Tokyo' => { + 'Country' => 'Japan', + 'Location' => 'Tokyo', + 'City' => 'Tokyo', + 'Latitude' => '35.689487', + 'Longitude' => '139.691706', + }, + 'Nairobi' => { + 'Country' => 'Kenya', + 'Location' => 'Nairobi', + 'City' => 'Nairobi', + 'Latitude' => '-1.292066', + 'Longitude' => '36.821946', + }, + 'Seoul' => { + 'Country' => 'Korea', + 'Location' => 'Seoul', + 'City' => 'Seoul', + 'Latitude' => '37.566535', + 'Longitude' => '126.977969', + }, + 'Beirut' => { + 'Country' => 'Lebanon', + 'Location' => 'Beirut', + 'City' => 'Beirut', + 'Latitude' => '33.893791', + 'Longitude' => '35.501777', + }, + 'Mexico City' => { + 'Country' => 'Mexico', + 'Location' => 'Mexico City', + 'City' => 'Mexico City', + 'Latitude' => '19.432608', + 'Longitude' => '-99.133208', + }, + 'Amsterdam' => { + 'Country' => 'Netherlands', + 'Location' => 'Amsterdam', + 'City' => 'Amsterdam', + 'Latitude' => '52.370216', + 'Longitude' => '4.895168', + }, + 'Wellington' => { + 'Country' => 'New Zealand', + 'Location' => 'Wellington', + 'City' => 'Wellington', + 'Latitude' => '-41.286460', + 'Longitude' => '174.776236', + }, + 'Oslo' => { + 'Country' => 'Norway', + 'Location' => 'Oslo', + 'City' => 'Oslo', + 'Latitude' => '59.913869', + 'Longitude' => '10.752245', + }, + 'Islamabad' => { + 'Country' => 'Pakistan', + 'Location' => 'Islamabad', + 'City' => 'Islamabad', + 'Latitude' => '33.684420', + 'Longitude' => '73.047885', + }, + 'Panama City' => { + 'Country' => 'Panama', + 'Location' => 'Panama City', + 'City' => 'Panama City', + 'Latitude' => '8.982379', + 'Longitude' => '-79.519870', + }, + 'Lima' => { + 'Country' => 'Peru', + 'Location' => 'Lima', + 'City' => 'Lima', + 'Latitude' => '-12.046373', + 'Longitude' => '-77.042754', + }, + 'Manila' => { + 'Country' => 'Philippines', + 'Location' => 'Manila', + 'City' => 'Manila', + 'Latitude' => '14.599512', + 'Longitude' => '120.984219', + }, + 'Warsaw' => { + 'Country' => 'Poland', + 'Location' => 'Warsaw', + 'City' => 'Warsaw', + 'Latitude' => '52.229676', + 'Longitude' => '21.012229', + }, + 'San Juan' => { + 'Country' => 'Puerto Rico', + 'Location' => 'San Juan', + 'City' => 'San Juan', + 'Latitude' => '18.465539', + 'Longitude' => '-66.105735', + }, + 'Moscow' => { + 'Country' => 'Russia', + 'Location' => 'Moscow', + 'City' => 'Moscow', + 'Latitude' => '55.755826', + 'Longitude' => '37.617300', + }, + 'Glasgow' => { + 'Country' => 'Scotland', + 'Location' => 'Glasgow', + 'City' => 'Glasgow', + 'Latitude' => '55.864237', + 'Longitude' => '-4.251806', + }, + 'Singapore' => { + 'Country' => 'Singapore', + 'Location' => 'Singapore', + 'City' => 'Singapore', + 'Latitude' => '1.352083', + 'Longitude' => '103.819836', + }, + 'Cape Town' => { + 'Country' => 'South Africa', + 'Location' => 'Cape Town', + 'City' => 'Cape Town', + 'Latitude' => '-33.924869', + 'Longitude' => '18.424055', + }, + 'Barcelona' => { + 'Country' => 'Spain', + 'Location' => 'Barcelona', + 'City' => 'Barcelona', + 'Latitude' => '41.385064', + 'Longitude' => '2.173403', + }, + 'Stockholm' => { + 'Country' => 'Sweden', + 'Location' => 'Stockholm', + 'City' => 'Stockholm', + 'Latitude' => '59.329323', + 'Longitude' => '18.068581', + }, + 'Kiev' => { + 'Country' => 'Ukraine', + 'Location' => 'Kiev', + 'City' => 'Kiev', + 'Latitude' => '50.450100', + 'Longitude' => '30.523400', + }, + 'Los Angeles' => { + 'Country' => 'United States', + 'Location' => 'Los Angeles', + 'City' => 'Los Angeles', + 'Latitude' => '34.052234', + 'Longitude' => '-118.243685', + }, + 'Caracas' => { + 'Country' => 'Venezuela', + 'Location' => 'Caracas', + 'City' => 'Caracas', + 'Latitude' => '10.480594', + 'Longitude' => '-66.903606', + }, + 'Hanoi' => { + 'Country' => 'Vietnam', + 'Location' => 'Hanoi', + 'City' => 'Hanoi', + 'Latitude' => '21.027764', + 'Longitude' => '105.834160', + }, + 'Scottsdale, AZ' => { + 'Location' => 'Scottsdale, AZ', + 'Country' => 'United States', + 'State' => 'Arizona', + 'City' => 'Scottsdale', + 'Latitude' => '33.501324', + 'Longitude' => '-111.925278', + }, + 'Dallas, TX' => { + 'Location' => 'allas, TX', + 'Country' => 'United States', + 'State' => 'Texas', + 'City' => 'Dallas', + 'Latitude' => '32.736259', + 'Longitude' => '-96.864586', + }, + 'San Jose, CA' => { + 'Country' => 'United States', + 'Location' => 'San Jose, CA', + 'State' => 'California', + 'City' => 'San Jose', + 'Latitude' => '37.3388', + 'Longitude' => '-121.8910', + }, + 'Herndon, VA' => { + 'Country' => 'United States', + 'Location' => 'Herndon, VA', + 'State' => 'Virginia', + 'City' => 'Herndon', + 'Latitude' => '38.971389', + 'Longitude' => '-77.388611', + }, ); diff --git a/install/Table-Nodes.nmis b/install/Table-Nodes.nmis index 69d1623..a3f6978 100644 --- a/install/Table-Nodes.nmis +++ b/install/Table-Nodes.nmis @@ -61,8 +61,9 @@ my $uuid = NMIS::UUID::getUUID(); Nodes => [ { name => { mandatory => 'true', header => 'Name',display => 'key,header,text',value => [""], - validate => { 'regex' => qr!^[^/]+$! } }}, - { new_name => { header => 'New Name', display => 'text,editonly', value => [""] }}, + validate => { 'regex' => $C->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/ } }}, + { new_name => { header => 'New Name', display => 'text,editonly', value => [""], + validate => { 'regex-or-empty' => $C->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/ } }}, { uuid => { header => 'UUID',display => 'header,readonly',value => ["$uuid"] }}, diff --git a/install/plugins/Cisco_Features.pm b/install/plugins/Cisco_Features.pm index 980046b..511b2f6 100644 --- a/install/plugins/Cisco_Features.pm +++ b/install/plugins/Cisco_Features.pm @@ -30,7 +30,7 @@ # a small update plugin for handling various Cisco features like CBQoS and Netflow package Cisco_Features; -our $VERSION = "1.0.1"; +our $VERSION = "1.2.0"; use strict; @@ -117,9 +117,56 @@ sub update_plugin $changesweremade = 1; } } - } + } + + # if there is EntityMIB data then load map out the entPhysicalVendorType to the vendor type fields + # store in the field called Type. + if (ref($NI->{entityMib}) eq "HASH") { + info("Working on $node entityMib"); + + my $vendorOids = loadVendorOids($C); + for my $key (keys %{$NI->{entityMib}}) + { + my $entry = $NI->{entityMib}->{$key}; + + if ( defined $entry->{entPhysicalVendorType} and $vendorOids->{$entry->{entPhysicalVendorType}} ne "" ) { + $entry->{cardType} = $vendorOids->{$entry->{entPhysicalVendorType}}; + $entry->{cardType} =~ s/^cev//; + $changesweremade = 1; + } + } + } + return ($changesweremade,undef); # report if we changed anything } +sub loadVendorOids { + my $C = shift; + + my $oids = "$C->{mib_root}/CISCO-ENTITY-VENDORTYPE-OID-MIB.oid"; + my $vendorOids; + + info("Loading Vendor OIDs from $oids"); + + open(OIDS,$oids) or warn "ERROR could not load $oids: $!\n"; + + my $match = qr/\"(\w+)\"\s+\"([\d+\.]+)\"/; + + while () { + if ( $_ =~ /$match/ ) { + $vendorOids->{$2} = $1; + } + elsif ( $_ =~ /^#|^\s+#/ ) { + #all good comment + } + else { + info("ERROR: no match $_"); + } + } + close(OIDS); + + return ($vendorOids); +} + 1; diff --git a/lib/NMIS.pm b/lib/NMIS.pm index cc2fb4f..59cb0c9 100644 --- a/lib/NMIS.pm +++ b/lib/NMIS.pm @@ -27,7 +27,7 @@ # # ***************************************************************************** package NMIS; -our $VERSION = "8.6.6G"; +our $VERSION = "8.6.7G"; use NMIS::uselib; use lib "$NMIS::uselib::rrdtool_lib"; @@ -52,6 +52,8 @@ use Clone; use List::Util 1.33; use CGI qw(); # very ugly but createhrbuttons needs it :( use NMIS::UUID; +use Digest::MD5; # for htmlGraph, nothing stronger is needed +use NMIS::RRDdraw; # for htmlGraph #! Imports the LOCK_ *constants (eg. LOCK_UN, LOCK_EX) use Fcntl qw(:DEFAULT :flock); @@ -210,6 +212,9 @@ sub loadLocalNodeTable my $badones; # deemed critical: name, uuid, host, group properties my @musthave = qw(name uuid host group); + # also deemed critical: node name must match the rules + my $nodenamerule = $C->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/; + for my $maybebad (keys %$lotsanodes) { my $noderec = $lotsanodes->{$maybebad}; @@ -227,6 +232,11 @@ sub loadLocalNodeTable { $because = "invalid structure, name and key don't match"; } + elsif ($noderec->{name} !~ $nodenamerule) + { + $because = "node name is invalid, does not match 'node_name_rule' regexp"; + } + if ($because) { ++$badones; @@ -325,9 +335,15 @@ sub loadNodeTable { return $NT_cache; } -sub loadGroupTable { - - if( not defined $GT_cache or not defined $NT_cache or ( mtimeFile(dir=>'conf',name=>'Nodes') ne $NT_modtime) ) { +# returns a hash of groupname => groupname, +# with all groups that were observed configured for active nodes +# note: does NOT filter by group_list from the configuration! +sub loadGroupTable +{ + if (not defined $GT_cache + or not defined $NT_cache + or ( mtimeFile(dir=>'conf',name=>'Nodes') ne $NT_modtime) ) + { loadNodeTable(); } @@ -405,6 +421,7 @@ sub loadWindowStateTable return loadTable(dir=>'var',name=>'nmis-windowstate'); } +# az [2018-08-09 Thu 11:47] deprecated DO NOT USE! # check node name case insentive, return good one sub checkNodeName { my $name = shift; @@ -677,8 +694,14 @@ sub nodeStatus { # this is a variation of nodeStatus, which doesn't say why a node is degraded # args: system object (doesn't have to be init'd with snmp/wmi) -# returns: hash of error (if dud args), overall (-1,0,1), snmp_enabled (0,1), snmp_status (0,1,undef if unknown), -# ping_enabled and ping_status, wmi_enabled and wmi_status, failover_status (0,1,undef if unknown/irrelevant) +# returns: hash of error (if dud args), +# overall (-1 deg, 0 down, 1 up), +# snmp_enabled (0,1), snmp_status (0,1,undef if unknown), +# ping_enabled and ping_status (note: ping status is 1 if primary or backup address are up) +# wmi_enabled and wmi_status, +# failover_status (0 failover, 1 ok, undef if unknown/irrelevant) +# failover_ping_status (0 backup host is down, 1 ok, undef if irrelevant) +# primary_ping_status (0 primary host is down, 1 ok, undef if irrelevant) sub PreciseNodeStatus { my (%args) = @_; @@ -703,12 +726,25 @@ sub PreciseNodeStatus snmp_status => undef, wmi_status => undef, ping_status => undef, - failover_status => undef ); # 1 ok, 0 in failover, undef if unknown + failover_status => undef, # 1 ok, 0 in failover, undef if unknown/irrelevant + failover_ping_status => undef, # 1 backup host is pingable, 0 not, undef unknown/irrelevant + primary_ping_status => undef, + ); + + my $downexists = eventExist($nodename, "Node Down"); + my $failoverexists = eventExist($nodename, "Node Polling Failover"); + my $backupexists = eventExist($nodename, "Backup Host Down"); - $precise{ping_status} = (eventExist($nodename, "Node Down")?0:1) if ($precise{ping_enabled}); # otherwise we don't care + $precise{ping_status} = ($downexists? 0:1) if ($precise{ping_enabled}); # otherwise we don't care $precise{wmi_status} = (eventExist($nodename, "WMI Down")?0:1) if ($precise{wmi_enabled}); $precise{snmp_status} = (eventExist($nodename, "SNMP Down")?0:1) if ($precise{snmp_enabled}); - $precise{failover_status} = eventExist($nodename, "Node Polling Failover")? 0:1 if ($precise{snmp_enabled}); + + if ($nisys->{host_backup}) # s->ndcfg is not populated if sys::init was called with snmp/wmi false :-( + { + $precise{failover_status} = $failoverexists? 0:1; + $precise{primary_ping_status} = ($downexists || $failoverexists)? 0:1; # the primary is dead if all are dead or if we failed-over + $precise{failover_ping_status} = ($backupexists || $downexists)? 0:1; # the secondary is dead if known to be dead or if all are dead + } # overall status: ping disabled -> the WORSE one of snmp and wmi states is authoritative if (!$precise{ping_enabled} @@ -725,8 +761,9 @@ sub PreciseNodeStatus # ping enabled, pingable but dead snmp or dead wmi or failover -> degraded # only applicable is collect eq true, handles SNMP Down incorrectness elsif ( ($precise{wmi_enabled} and !$precise{wmi_status}) - or ($precise{snmp_enabled} and - (!$precise{snmp_status} or !$precise{failover_status})) + or ($precise{snmp_enabled} and !$precise{snmp_status}) + or (defined($precise{failover_status}) && !$precise{failover_status}) + or (defined($precise{failover_ping_status}) && !$precise{failover_ping_status}) ) { $precise{overall} = -1; @@ -2705,40 +2742,112 @@ sub resolveDNStoAddr return $v4[0]; } -# create http for a clickable graph -sub htmlGraph { +# produce clickable graph and return html that can be pasted onto a page +# rrd graph is created by this function and cached on disk +# +# args: node/group, intf/item, server, graphtype, width, height (all required), +# start, end (optional), +# only_link (optional, default: 0, if set ONLY the href for the graph is returned) +# returns: html or link/href value +sub htmlGraph +{ my %args = @_; + + my $C = loadConfTable(); + my $graphtype = $args{graphtype}; my $group = $args{group}; my $node = $args{node}; my $intf = $args{intf}; + my $item = $args{item}; my $server = $args{server}; - - my $target = $node; - if ($node eq "" and $group ne "") { - $target = $group; - } - - my $id = uri_escape("$target-$intf-$graphtype"); # intf and node are unsafe - my $C = loadConfTable(); - my $width = $args{width}; # graph size my $height = $args{height}; - my $win_width = $C->{win_width}; # window size - my $win_height = $C->{win_height}; + my $omit_fluff = getbool($args{only_link}); # return wrapped etc. or just the href? my $urlsafenode = uri_escape($node); my $urlsafegroup = uri_escape($group); my $urlsafeintf = uri_escape($intf); + my $urlsafeitem = uri_escape($item); + + my $target = $node || $group; # only used for js/widget linkage + my $clickurl = "$C->{'node'}?conf=$C->{conf}&act=network_graph_view&graphtype=$graphtype&group=$urlsafegroup&intf=$urlsafeintf&item=$urlsafeitem&server=$server&node=$urlsafenode"; my $time = time(); - my $clickurl = "$C->{'node'}?conf=$C->{conf}&act=network_graph_view&graphtype=$graphtype&group=$urlsafegroup&intf=$urlsafeintf&server=$server&node=$urlsafenode"; + my $graphlength = ( $C->{graph_unit} eq "days" )? + 86400 * $C->{graph_amount} : 3600 * $C->{graph_amount}; + my $start = $args{start} || time-$graphlength; + my $end = $args{end} || $time; + + # where to put the graph file? let's use htdocs/cache, that's web-accessible + my $cachedir = $C->{'web_root'}."/cache"; + createDir($cachedir) if (!-d $cachedir); + + # we need a time-invariant, short and safe file name component, + # which also must incorporate a server-specific bit of secret sauce + # that an external party does not have access to (to eliminate guessing) + my $graphfile_prefix = Digest::MD5::md5_hex( + join("__", + $C->{auth_web_key}, + $C->{conf}, # fixme: needed? useful? relevant? + $group, $node, $intf, $item, + $graphtype, + $server, # fixme: needed? + $width, $height)); + + # do we want to reuse an existing, 'new enough' graph? + opendir(D, $cachedir); + my @recyclables = grep(/^$graphfile_prefix/, readdir(D)); + closedir(D); - my $src = "$C->{'rrddraw'}?conf=$C->{conf}&act=draw_graph_view&group=$urlsafegroup&graphtype=$graphtype&node=$urlsafenode&intf=$urlsafeintf&server=$server". - "&start=&end=&width=$width&height=$height&time=$time"; - ### 2012-03-28 keiths, changed graphs to come up in their own Window with the target of node, handy for comparing graphs. - return qq| -Network Info|; + my $graphfilename; + my $cachefilemaxage = $C->{graph_cache_maxage} // 60; + + for my $maybe (sort { $b cmp $a } @recyclables) + { + next if ($maybe !~ /^\S+_(\d+)_(\d+)\.png$/); # should be impossible + my ($otherstart, $otherend) = ($1,$2); + + # let's accept anything newer than 60 seconds as good enough + my $deltastart = $start - $otherstart; + $deltastart *= -1 if ($deltastart < 0); + my $deltaend = $end - $otherend; + $deltaend *= -1 if ($deltaend < 0); + + if ($deltastart <= $cachefilemaxage && $deltaend <= $cachefilemaxage) + { + $graphfilename = $maybe; + dbg("reusing cached graph $maybe for $graphtype, node $node: requested period off by ".($start-$otherstart)." seconds"); + + last; + } + } + + # nothing useful in the cache? then generate a new graph + if (!$graphfilename) + { + $graphfilename = $graphfile_prefix."_${start}_${end}.png"; + dbg("graphing args for new graph: node=$node, group=$group, graphtype=$graphtype, intf=$intf, item=$item, server=$server, start=$start, end=$end, width=$width, height=$height, filename=$cachedir/$graphfilename"); + + my $target = "$cachedir/$graphfilename"; + my ($error, $graphret) = NMIS::RRDdraw::draw(node => $node, + group => $group, + graphtype => $graphtype, + intf => $intf, + item => $item, + server => $server, + start => $start, + end => $end, + width => $width, + height => $height, + filename => $target); + return qq|

Error: $error

| if ($error); + func::setFileProt($target); # to make the selftest happy... + } + + # return just the href? or html? + return $omit_fluff? "$C->{''}/cache/$graphfilename" + : qq|Network Info|; } # args: user, node, system, refresh, widget, au (object), @@ -4564,8 +4673,8 @@ sub rename_node my $oldnoderec = $nodeinfo->{$old}; return (1, "Old node $old does not exist!") if (!$oldnoderec); - # fixme: less picky? spaces required? - return(1, "Invalid node name \"$new\"") if ($new =~ /[^a-zA-Z0-9_-]/); + my $nodenamerule = $C->{node_name_rule} || qr/^[a-zA-Z0-9_. -]+$/; + return(1, "Invalid node name \"$new\"") if ($new !~ $nodenamerule); my $newnoderec = $nodeinfo->{$new}; return(1, "New node $new already exists, NOT overwriting!") diff --git a/lib/NMIS/RRDdraw.pm b/lib/NMIS/RRDdraw.pm new file mode 100644 index 0000000..c177d85 --- /dev/null +++ b/lib/NMIS/RRDdraw.pm @@ -0,0 +1,633 @@ +# +# Copyright 1999-2018 Opmantek Limited (www.opmantek.com) +# +# ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM +# +# This file is part of Network Management Information System ("NMIS"). +# +# NMIS 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. +# +# NMIS 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 NMIS (most likely in a file named LICENSE). +# If not, see +# +# For further information on NMIS or for a license other than GPL please see +# www.opmantek.com or email contact@opmantek.com +# +# User group details: +# http://support.opmantek.com/users/ +# +# ***************************************************************************** +package NMIS::RRDdraw; +our $VERSION = "8.6.7G"; + +use strict; +use FindBin; +use lib "$FindBin::Bin/../lib"; + +# note: caller is expected to 'use NMIS' already +use func; +use Sys; +use RRDs 1.4004; + +# produce one graph +# args: node/group, graphtype, intf/item, width, height (all required), +# start, end, filename (optional) +# +# if filename is given then the graph is saved there. +# if no filename is given, then the graph is printed to stdout with minimal content-type header. +# +# returns: error message or (undef, rrds::graph result array) +sub draw +{ + my %args = @_; + + my ($nodename,$mygroup,$graphtype,$intf,$item, + $width,$height,$filename,$start,$end,$time,$debug) + = @args{qw(node group graphtype intf item width height filename start end time debug)}; + + my $C = loadConfTable; + + my $S = Sys->new; + $S->init(name=>$nodename, snmp=>'false'); + my $NI = $S->ndinfo; + my $IF = $S->ifinfo; + + # default unit is hours! + my $graphlength = ( $C->{graph_unit} eq "days" )? + 86400 * $C->{graph_amount} : 3600 * $C->{graph_amount}; # want seconds + + my $when = $time // time; + $start = $when-$graphlength if (!$start); + $end = $when if (!$end); + + # prep human-friendly (imprecise!) length of graph period + my $mylength; # cannot be called length + if (($end - $start) < 3600) + { + $mylength = int(($end - $start) / 60) . " minutes"; + } + elsif (($end - $start) < (3600*48)) + { + $mylength = int(($end - $start) / (3600)) . " hours"; + } + else + { + $mylength = int(($end - $start) / (3600*24)) . " days"; + } + + my (@rrdargs, # final rrd graph args + $mydatabase); # path to the rrd file + + # special graphtypes: global metrics + if ($graphtype eq 'metrics') + { + $item = $mygroup; + undef $intf; + } + + # special graphtypes: cbqos is dynamic (multiple inputs create one graph), ditto calls + if ($graphtype =~ /cbqos/) + { + @rrdargs = graphCBQoS(sys=>$S, + graphtype=>$graphtype, + intf=>$intf, + item=>$item, + start=>$start, end=>$end, + width=>$width, height=>$height); + } + elsif ($graphtype eq "calls") + { + @rrdargs = graphCalls(sys=>$S, + graphtype=>$graphtype, + intf=>$intf, + item=>$item, + start=>$start, end=>$end, + width=>$width, height=>$height); + } + else + { + $mydatabase = $S->getDBName(graphtype=>$graphtype, index=>$intf, item=>$item); + return "failed to find database for graphtype $graphtype!" if (!$mydatabase); + + my $graph = loadTable(dir=>'models', name=>"Graph-$graphtype"); + return "failed to read Graph-$graphtype!" if (ref($graph) ne "HASH" or !keys %$graph); + + my $titlekey = ($width <= 400 and $graph->{title}{short})? 'short' : 'standard'; + my $vlabelkey = (getbool($C->{graph_split}) and $graph->{vlabel}{split})? 'split' + : ($width <= 400 and $graph->{vlabel}{short})? 'short' : 'standard'; + my $size = ($width <= 400 and $graph->{option}{small})? 'small' : 'standard'; + + my $title = $graph->{title}{$titlekey}; + my $label = $graph->{vlabel}{$vlabelkey}; + + logMsg("no title->$titlekey found in Graph-$graphtype") if (!$title); + logMsg("no vlabel->$vlabelkey found in Graph-$graphtype") if (!$label); + + @rrdargs = ( + "--title", $title, + "--vertical-label", $label, + "--start", $start, + "--end", $end, + "--width", $width, + "--height", $height, + "--imgformat", "PNG", + "--interlaced", + "--disable-rrdtool-tag", + "--color", 'BACK#ffffff', # Background Color + "--color", 'SHADEA#ffffff', # Left and Top Border Color + "--color", 'SHADEB#ffffff', # was CFCFCF + "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) + "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' + "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 + "--color", 'FONT#222222', # Font Color + "--color", 'ARROW#924040', # Arrow Color for X/Y Axis + "--color", 'FRAME#808080' # Canvas Frame Color + ); + + if ($width > 400) { + push(@rrdargs, "--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; + } + else + { + push(@rrdargs, "--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; + } + push @rrdargs, @{$graph->{option}{$size}}; + } + + { + no strict; # *shudder* this is so utterly wrong and broken + # any scalars used in here must be global, + # having an outside my $whatever does NOT WORK! + if ($intf ne "") + { + $indx = $intf; + $ifDescr = $IF->{$intf}{ifDescr}; + $ifSpeed = $IF->{$intf}{ifSpeed}; + $ifSpeedIn = $IF->{$intf}{ifSpeed}; + $ifSpeedOut = $IF->{$intf}{ifSpeed}; + $ifSpeedIn = $IF->{$intf}{ifSpeedIn} if $IF->{$intf}{ifSpeedIn}; + $ifSpeedOut = $IF->{$intf}{ifSpeedOut} if $IF->{$intf}{ifSpeedOut}; + if ($ifSpeed eq "auto" ) { + $ifSpeed = 10000000; + } + + if ( $IF->{$intf}{ifSpeedIn} and $IF->{$intf}{ifSpeedOut} ) { + $speed = "IN\\: ". convertIfSpeed($ifSpeedIn) ." OUT\\: ". convertIfSpeed($ifSpeedOut); + } + else { + $speed = convertIfSpeed($ifSpeed); + } + } + # global scalars, cannot inherit from outer my $xyz + $node = $NI->{system}{name}; # should be the same as args{node}... + $length = $mylength; + $group = $mygroup; + + $datestamp_start = returnDateStamp($start); + $datestamp_end = returnDateStamp($end); + $datestamp = returnDateStamp(time); + $database = $mydatabase; + + $itm = $item; + $split = getbool($C->{graph_split}) ? -1 : 1 ; + $GLINE = getbool($C->{graph_split}) ? "AREA" : "LINE1" ; + $weight = 0.983; + + for my $idx (0..$#rrdargs) + { + my $str = $rrdargs[$idx]; + + # escape any ':' chars which might be in the database name (e.g C:\\) or the other + # inputs (e.g. indx == service name). this must be done for ALL substitutables, + # but no thanks to no strict we don't exactly know who they are, nor can we safely change + # their values without side-effects...so we do it on the go, and only where not already pre-escaped. + + # EXCEPT in --title, where we can't have colon escaping. grrrrrr! + if ($idx > 0 && $rrdargs[$idx-1] eq "--title") + { + $str =~ s{\$(\w+)}{if(defined${$1}){${$1};}else{"ERROR, no variable \'\$$1\' ";}}egx; + } + else + { + $str =~ s{\$(\w+)}{if(defined${$1}){NMIS::postcolonial(${$1});}else{"ERROR, no variable \'\$$1\' ";}}egx; + } + + if ($str =~ /ERROR/) + { + logMsg("ERROR in expanding variables, $str"); + return "ERROR in expanding variables, $str"; + } + $rrdargs[$idx] = $str; + } + } + + my $graphret; + # finally, generate the graph - as an indep http response to stdout + # (bit uggly, no etag, expiration, content-length...)... + if (!$filename) + { + # if this isn't done, then the graph output overtakes the header output, + # and apache considers the cgi script broken and returns 500 + STDOUT->autoflush(1); + print "Content-type: image/png\n\n"; + ($graphret,undef,undef) = RRDs::graph('-', @rrdargs); + } + # ...or as a file. + else + { + ($graphret,undef,undef) = RRDs::graph($filename, @rrdargs); + } + if (my $error = RRDs::error) + { + logMsg("Graphing Error for graphtype $graphtype, database $mydatabase: $error"); + return "Graphing Error for graphtype $graphtype, database $mydatabase: $error"; + } + return (undef, $graphret); +} + +# special graph helper for CBQoS +# this handles both cisco and huawei flavour cbqos +# args: sys, graphtype, intf/item, start, end, width, height (all required) +# returns: array of rrd args +sub graphCBQoS +{ + my %args = @_; + + my $C = loadConfTable; + my ($S,$graphtype,$intf,$item,$start,$end,$width,$height,$debug) + = @args{qw(sys graphtype intf item start end width height debug)}; + + my $NI = $S->ndinfo; + my $IF = $S->ifinfo; + + # OMK-5182, customer wants / as delimiter for the cbqos names, not our default -- + my $delimiter = $C->{cbqos_classmap_name_delimiter} // "--"; + + # order the names, find colors and bandwidth limits, index and section names + my ($CBQosNames, $CBQosValues) = NMIS::loadCBQoS(sys=>$S, graphtype=>$graphtype, index=>$intf); + + # display all class-maps in one graph... + if ($item eq "") + { + my $direction = ($graphtype eq "cbqos-in") ? "input" : "output" ; + my $ifDescr = shortInterface($IF->{$intf}{ifDescr}); + my $vlabel = "Avg Bits per Second"; + my $title; + + if ( $width <= 400 ) { + $title = "$NI->{name} $ifDescr $direction"; + $title .= " - $CBQosNames->[0]" if ($CBQosNames->[0] && $CBQosNames->[0] !~ /^(in|out)bound$/i); + $title .= ' - $length'; + $vlabel = "Avg bps"; + } + else + { + $title = "$NI->{name} $ifDescr $direction - CBQoS from ".'$datestamp_start to $datestamp_end'; # fixme: why replace later?? + } + + my @opt = ( + "--title", $title, + "--vertical-label", $vlabel, + "--start", $start, + "--end", $end, + "--width", $width, + "--height", $height, + "--imgformat", "PNG", + "--interlaced", + "--disable-rrdtool-tag", + "--color", 'BACK#ffffff', # Background Color + "--color", 'SHADEA#ffffff', # Left and Top Border Color + "--color", 'SHADEB#ffffff', # + "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) + "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' + "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 + "--color", 'FONT#222222', # Font Color + "--color", 'ARROW#924040', # Arrow Color for X/Y Axis + "--color", 'FRAME#808080' # Canvas Frame Color + ); + + if ($width > 400) { + push(@opt,"--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; + } + else { + push(@opt,"--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; + } + + # calculate the sum (avg and max) of all Classmaps for PrePolicy and Drop + # note that these CANNOT be graphed by themselves, as 0 isn't a valid RPN expression in rrdtool + my $avgppr = "CDEF:avgPrePolicyBitrate=0"; + my $maxppr = "CDEF:maxPrePolicyBitrate=0"; + my $avgdbr = "CDEF:avgDropBitrate=0"; + my $maxdbr = "CDEF:maxDropBitrate=0"; + + # is this hierarchical or flat? + my $HQOS = 0; + foreach my $i (1..$#$CBQosNames) + { + # \w is [a-zA-Z0-9_] + if ( $CBQosNames->[$i] =~ /^[\w-]+$delimiter\w+$delimiter/ ) + { + $HQOS = 1; + last; + } + } + + my $gtype = "AREA"; + my $gcount = 0; + my $parent_name = ""; + foreach my $i (1..$#$CBQosNames) + { + my $thisinfo = $CBQosValues->{$intf.$CBQosNames->[$i]}; + + my $database = $S->getDBName(graphtype => $thisinfo->{CfgSection}, + index => $thisinfo->{CfgIndex}, + item => $CBQosNames->[$i] ); + my $parent = 0; + if ( $CBQosNames->[$i] !~ /\w+$delimiter\w+/ and $HQOS ) + { + $parent = 1; + $gtype = "LINE1"; + } + + if ( $CBQosNames->[$i] =~ /^([\w-]+)$delimiter\w+$delimiter/ ) + { + $parent_name = $1; + dbg("parent_name=$parent_name\n") if ($debug); + } + + if ( not $parent and not $gcount) + { + $gtype = "AREA"; + ++$gcount; + } + elsif ( not $parent and $gcount) + { + $gtype = "STACK"; + ++$gcount; + } + my $alias = $CBQosNames->[$i]; + $alias =~ s/${parent_name}$delimiter//g; + $alias =~ s!$delimiter!/!g; + + # rough alignment for the columns, necessarily imperfect + # as X-char strings aren't equally wide... + my $tab = "\\t"; + if ( length($alias) <= 5 ) + { + $tab = $tab x 4; + } + elsif ( length($alias) <= 14 ) + { + $tab = $tab x 3; + } + elsif ( length($alias) <= 19 ) + { + $tab = $tab x 2; + } + + my $color = $CBQosValues->{$intf.$CBQosNames->[$i]}{'Color'}; + + push @opt, ("DEF:avgPPB$i=$database:".$thisinfo->{CfgDSNames}->[0].":AVERAGE", + "DEF:maxPPB$i=$database:".$thisinfo->{CfgDSNames}->[0].":MAX", + "DEF:avgDB$i=$database:".$thisinfo->{CfgDSNames}->[2].":AVERAGE", + "DEF:maxDB$i=$database:".$thisinfo->{CfgDSNames}->[2].":MAX", + "CDEF:avgPPR$i=avgPPB$i,8,*", + "CDEF:maxPPR$i=maxPPB$i,8,*", + "CDEF:avgDBR$i=avgDB$i,8,*", + "CDEF:maxDBR$i=maxDB$i,8,*",); + + if ($width > 400) + { + push @opt, ("$gtype:avgPPR$i#$color:$alias$tab", + "GPRINT:avgPPR$i:AVERAGE:Avg %8.2lf%s\\t", + "GPRINT:maxPPR$i:MAX:Max %8.2lf%s\\t", + "GPRINT:avgDBR$i:AVERAGE:Avg Drops %6.2lf%s\\t", + "GPRINT:maxDBR$i:MAX:Max Drops %6.2lf%s\\l"); + } + else + { + push(@opt,"$gtype:avgPPR$i#$color:$alias"); + } + + #push(@opt,"LINE1:avgPPR$i#$color:$CBQosNames->[$i]"); + $avgppr .= ",avgPPR$i,+"; + $maxppr .= ",maxPPR$i,+"; + $avgdbr .= ",avgDBR$i,+"; + $maxdbr .= ",maxDBR$i,+"; + } + + push @opt,$avgppr,$maxppr,$avgdbr, $maxdbr; + + if ($width > 400) + { + push(@opt,"COMMENT:\\l", + "GPRINT:avgPrePolicyBitrate:AVERAGE:PrePolicyBitrate\\t\\t\\tAvg %8.2lf%s\\t", + "GPRINT:maxPrePolicyBitrate:MAX:Max\\t%8.2lf%s\\l", + "GPRINT:avgDropBitrate:AVERAGE:DropBitrate\\t\\t\\tAvg %8.2lf%s\\t", + "GPRINT:maxDropBitrate:MAX:Max\\t%8.2lf%s\\l"); + } + return @opt; + } + + # ...or display ONLY the selected class-map + + my $thisinfo = $CBQosValues->{$intf.$item}; + my $speed = defined $thisinfo->{CfgRate}? &convertIfSpeed($thisinfo->{'CfgRate'}) : undef; + my $direction = ($graphtype eq "cbqos-in") ? "input" : "output" ; + + my $database = $S->getDBName(graphtype => $thisinfo->{CfgSection}, + index => $thisinfo->{CfgIndex}, + item => $item ); + + # in this case we always use the FIRST color, not the one for this item + my $color = $CBQosValues->{$intf.$CBQosNames->[1]}->{'Color'}; + + my $ifDescr = shortInterface($IF->{$intf}{ifDescr}); + my $title = "$ifDescr $direction - $item from ".'$datestamp_start to $datestamp_end'; # fixme: why replace later?? + + my @opt = ( + "--title", $title, + "--vertical-label", 'Avg Bits per Second', + "--start", $start, + "--end", $end, + "--width", $width, + "--height", $height, + "--imgformat", "PNG", + "--interlaced", + "--disable-rrdtool-tag", + "--color", 'BACK#ffffff', # Background Color + "--color", 'SHADEA#ffffff', # Left and Top Border Color + "--color", 'SHADEB#ffffff', # + "--color", 'CANVAS#FFFFFF', # Canvas (Grid Background) + "--color", 'GRID#E2E2E2', # Grid Line ColorGRID#808020' + "--color", 'MGRID#EBBBBB', # Major Grid Line ColorMGRID#80c080 + "--color", 'FONT#222222', # Font Color + "--color", 'ARROW#924040', # Arrow Color for X/Y Axis + "--color", 'FRAME#808080', # Canvas Frame Color + ); + + if ($width > 400) + { + push(@opt,"--font", $C->{graph_default_font_standard}) if $C->{graph_default_font_standard}; + } + else + { + push(@opt,"--font", $C->{graph_default_font_small}) if $C->{graph_default_font_small}; + } + + # needs to work for both types of qos, hence uses the CfgDSNames + push @opt, ( + "DEF:PrePolicyByte=$database:".$thisinfo->{CfgDSNames}->[0].":AVERAGE", + "DEF:maxPrePolicyByte=$database:".$thisinfo->{CfgDSNames}->[0].":MAX", + "DEF:DropByte=$database:".$thisinfo->{CfgDSNames}->[2].":AVERAGE", + "DEF:maxDropByte=$database:".$thisinfo->{CfgDSNames}->[2].":MAX", + "DEF:PrePolicyPkt=$database:".$thisinfo->{CfgDSNames}->[3].":AVERAGE", + "DEF:DropPkt=$database:".$thisinfo->{CfgDSNames}->[5].":AVERAGE"); + + # huawei doesn't have NoBufDropPkt + push @opt, "DEF:NoBufDropPkt=$database:".$thisinfo->{CfgDSNames}->[6].":AVERAGE" + if (defined $thisinfo->{CfgDSNames}->[6]); + + push @opt, ( + "CDEF:PrePolicyBitrate=PrePolicyByte,8,*", + "CDEF:maxPrePolicyBitrate=maxPrePolicyByte,8,*", + "CDEF:DropBitrate=DropByte,8,*", + "TEXTALIGN:left", + "AREA:PrePolicyBitrate#$color:PrePolicyBitrate", + ); + + # detailed legends are only shown on the 'big' graphs + if ($width > 400) { + push(@opt,"GPRINT:PrePolicyBitrate:AVERAGE:\\tAvg %8.2lf %sbps\\t"); + push(@opt,"GPRINT:maxPrePolicyBitrate:MAX:Max %8.2lf %sbps"); + } + # move back to previous line, then right-align + push @opt, "COMMENT:\\u", "AREA:DropBitrate#ff0000:DropBitrate\\r:STACK"; + + if ($width > 400) + { + push @opt, ( "GPRINT:PrePolicyByte:AVERAGE:Bytes transferred\\t\\tAvg %8.2lf %sB/s\\n", + "GPRINT:DropByte:AVERAGE:Bytes dropped\\t\\t\\tAvg %8.2lf %sB/s\\t", + "GPRINT:maxDropByte:MAX:Max %8.2lf %sB/s\\n", + "GPRINT:PrePolicyPkt:AVERAGE:Packets transferred\\t\\tAvg %8.2lf\\l", + "GPRINT:DropPkt:AVERAGE:Packets dropped\\t\\t\\tAvg %8.2lf"); + + # huawei doesn't have that + push(@opt,"COMMENT:\\l","GPRINT:NoBufDropPkt:AVERAGE:Packets No buffer dropped\\tAvg %8.2lf\\l") + if (defined $thisinfo->{CfgDSNames}->[6]); + + # not all qos setups have a graphable bandwidth limit + push @opt, "COMMENT:\\u", "COMMENT:".$thisinfo->{CfgType}." $speed\\r" if (defined $speed); + } + return @opt; +} + +sub graphCalls +{ + my %args = @_; + + my $S = $args{sys}; + my $NI = $S->ndinfo; + my $IF = $S->ifinfo; + my $graphtype = $args{graphtype}; + my $intf = $args{intf}; + my $start = $args{start}; + my $end = $args{end}; + my $width = $args{width}; + my $height = $args{height}; + + my $database; + my @opt; + my $title; + + my $device = ($intf eq "") ? "total" : $IF->{$intf}{ifDescr}; + if ( $width <= 400 ) { $title = "$NI->{name} Calls ".'$length'; } + else { $title = "$NI->{name} - $device - ".'$length from $datestamp_start to $datestamp_end'; } + + # display Calls summarized or only one port + @opt = ( + "--title", $title, + "--vertical-label","Call Stats", + "--start", "$start", + "--end", "$end", + "--width", "$width", + "--height", "$height", + "--imgformat", "PNG", + "--interlaced", + "--disable-rrdtool-tag" + ); + + my $CallCount = "CDEF:CallCount=0"; + my $AvailableCallCount = "CDEF:AvailableCallCount=0"; + my $totalIdle = "CDEF:totalIdle=0"; + my $totalUnknown = "CDEF:totalUnknown=0"; + my $totalAnalog = "CDEF:totalAnalog=0"; + my $totalDigital = "CDEF:totalDigital=0"; + my $totalV110 = "CDEF:totalV110=0"; + my $totalV120 = "CDEF:totalV120=0"; + my $totalVoice = "CDEF:totalVoice=0"; + + + foreach my $i ($S->getTypeInstances(section => 'calls')) { + next unless $intf eq "" or $intf eq $i; + $database = $S->getDBName(graphtype => 'calls', + index => $i); + next if (!$database); + + push(@opt,"DEF:CallCount$i=$database:CallCount:MAX"); + push(@opt,"DEF:AvailableCallCount$i=$database:AvailableCallCount:MAX"); + push(@opt,"DEF:totalIdle$i=$database:totalIdle:MAX"); + push(@opt,"DEF:totalUnknown$i=$database:totalUnknown:MAX"); + push(@opt,"DEF:totalAnalog$i=$database:totalAnalog:MAX"); + push(@opt,"DEF:totalDigital$i=$database:totalDigital:MAX"); + push(@opt,"DEF:totalV110$i=$database:totalV110:MAX"); + push(@opt,"DEF:totalV120$i=$database:totalV120:MAX"); + push(@opt,"DEF:totalVoice$i=$database:totalVoice:MAX"); + + $CallCount .= ",CallCount$i,+"; + $AvailableCallCount .= ",AvailableCallCount$i,+"; + $totalIdle .= ",totalIdle$i,+"; + $totalUnknown .= ",totalUnknown$i,+"; + $totalAnalog .= ",totalAnalog$i,+"; + $totalDigital .= ",totalDigital$i,+"; + $totalV110 .= ",totalV110$i,+"; + $totalV120 .= ",totalV120$i,+"; + $totalVoice .= ",totalVoice$i,+"; + if ($intf ne "") { last; } + } + + push(@opt,$CallCount); + push(@opt,$AvailableCallCount); + push(@opt,$totalIdle); + push(@opt,$totalUnknown); + push(@opt,$totalAnalog); + push(@opt,$totalDigital); + push(@opt,$totalV110); + push(@opt,$totalV120); + push(@opt,$totalVoice); + + push(@opt,"LINE1:AvailableCallCount#FFFF00:AvailableCallCount"); + push(@opt,"LINE2:totalIdle#000000:totalIdle"); + push(@opt,"LINE2:totalUnknown#FF0000:totalUnknown"); + push(@opt,"LINE2:totalAnalog#00FFFF:totalAnalog"); + push(@opt,"LINE2:totalDigital#0000FF:totalDigital"); + push(@opt,"LINE2:totalV110#FF0080:totalV110"); + push(@opt,"LINE2:totalV120#800080:totalV120"); + push(@opt,"LINE2:totalVoice#00FF00:totalVoice"); + push(@opt,"COMMENT:\\l"); + push(@opt,"GPRINT:AvailableCallCount:MAX:Available Call Count %1.2lf"); + push(@opt,"GPRINT:CallCount:MAX:Total Call Count %1.0lf"); + + return @opt; +} + +1; diff --git a/lib/Sys.pm b/lib/Sys.pm index 7a56e95..fbf91c1 100644 --- a/lib/Sys.pm +++ b/lib/Sys.pm @@ -27,7 +27,7 @@ # # ***************************************************************************** package Sys; -our $VERSION = "2.1.0"; +our $VERSION = "2.1.1"; use strict; use lib "../../lib"; @@ -88,7 +88,7 @@ sub cbinfo { my $self = shift; return $self->{info}{cbqos} }; # my $CB = $S->c sub pvcinfo { my $self = shift; return $self->{info}{pvc} }; # my $PVC = $S->pvcinfo sub callsinfo { my $self = shift; return $self->{info}{calls} }; # my $CALL = $S->callsinfo sub reach { my $self = shift; return $self->{reach} }; # my $R = $S->reach -sub ndcfg { my $self = shift; return $self->{cfg} }; # my $NC = $S->ndcfg +sub ndcfg { my $self = shift; return $self->{cfg} }; # ONLY set after init( snmp or wmi=>true)! sub envinfo { my $self = shift; return $self->{info}{environment} };# my $ENV = $S->envinfo sub syshealth { my $self = shift; return $self->{info}{systemHealth} };# my $SH = $S->syshealth sub alerts { my $self = shift; return $self->{mdl}{alerts} };# my $CA = $S->alerts @@ -123,7 +123,7 @@ sub status # attention: while it's possible to reuse a sys object for different nodes, # it is NOT RECOMMENDED! # -# node config is loaded if snmp or wmi args are true +# ATTENTION: node config is loaded ONLY if snmp or wmi args are true! # args: node (mostly required, or name), snmp (defaults to 1), wmi (defaults to the value for snmp), # update (defaults to 0), cache_models (see code comments for defaults), force (defaults to 0), # policy (default unset) @@ -1559,6 +1559,7 @@ sub parseString @args{"string","index","item","sect","type","extras"}; dbg("parseString:: string to parse '$str'",3); + my $C = loadConfTable(); { no strict; # *shudder* @@ -1629,8 +1630,8 @@ sub parseString $hrDiskFree = $hrDiskSize - $hrDiskUsed; } - # fixing auto-vivification bug! - if ($indx ne '' and exists $self->{info}{interface}{$indx}) { + if ($indx ne '' and ref($self->{info}{interface}{$indx}) eq "HASH") + { ### 2013-06-11 keiths, submission by Mateusz Kwiatkowski for thresholding $ifAlias = $self->{info}{interface}{$indx}{Description}; $Description = $self->{info}{interface}{$indx}{Description}; @@ -1641,17 +1642,42 @@ sub parseString $ifMaxOctets = ($ifSpeed ne 'U') ? int($ifSpeed / 8) : 'U'; $maxBytes = ($ifSpeed ne 'U') ? int($ifSpeed / 4) : 'U'; $maxPackets = ($ifSpeed ne 'U') ? int($ifSpeed / 50) : 'U'; - if ( defined $self->{info}{entPhysicalDescr} and $self->{info}{entPhysicalDescr}{$indx}{entPhysicalDescr} ne "" ) { + + if ( ref($self->{info}{entPhysicalDescr}) eq "HASH" + and ref($self->{info}->{entPhysicalDescr}->{$indx}) eq "HASH" + and $self->{info}{entPhysicalDescr}->{$indx}->{entPhysicalDescr} ne "" ) + { $entPhysicalDescr = $self->{info}{entPhysicalDescr}{$indx}{entPhysicalDescr}; } - } else { + + if ($str =~ /\$cdp/ && ref($self->{info}->{cdp}) eq "HASH") + { + ($cdpDevice,$cdpPlatform,$cdpCapabilities) = ('','',''); + foreach my $cdpIndx (keys %{$self->{info}{cdp}}) + { + next unless ($indx == $self->{info}{cdp}{$cdpIndx}{cdpCacheIfIndex}); + + $cdpDevice .= '|'.$self->{info}{cdp}{$cdpIndx}{cdpCacheDeviceId} + if (defined($self->{info}{cdp}{$cdpIndx}{cdpCacheDeviceId})); + $cdpPlatform .= '|'.$self->{info}{cdp}{$cdpIndx}{cdpCachePlatform} + if (defined($self->{info}{cdp}{$cdpIndx}{cdpCachePlatform})); + $cdpCapabilities .= '|'.$self->{info}{cdp}{$cdpIndx}{cdpCacheCapabilities} + if (defined($self->{info}{cdp}{$cdpIndx}{cdpCacheCapabilities})); + } + } + } + else + { $ifDescr = $ifType = ''; $ifSpeed = $ifMaxOctets = 'U'; } - $InstalledModems = $self->{info}{system}{InstalledModems} || 0; - $item = ''; - $item = $itm; + $index = $indx; + $InstalledModems = $self->{info}{system}{InstalledModems} || 0; + # OMK-5182, customer wants all non-word chars removed + # (which is most of what convertIfName does) + $item = !getbool($C->{preserve_item_string},"invert")? # === ne false + $itm : convertIfName($itm); } dbg("node=$node, nodeModel=$nodeModel, nodeType=$nodeType, nodeVendor=$nodeVendor, sysObjectName=$sysObjectName\n". diff --git a/lib/WMI.pm b/lib/WMI.pm index 5b2d154..e877010 100644 --- a/lib/WMI.pm +++ b/lib/WMI.pm @@ -29,13 +29,14 @@ # # this module queries WMI services via the standalone wmic executable package WMI; -our $VERSION = "2.0.0"; +our $VERSION = "2.1.0"; use strict; use File::Temp; use Encode 2.23; # core module, version is what came with 5.10.0 which we can make do with -# the constructor is not doing much at this time, merely checks that the arguments are sufficient +# the constructor is not doing much at this time, merely checks +# that the arguments are sufficient # # args: host, username, (required), password, timeout, program (optional), # program: full path to wmic, if not given wmic is expected to be in the PATH @@ -215,7 +216,19 @@ sub _run_query # -A format is badly documented. smbclient manpage has a little bit of info # however, unclear if that password can be quoted or contain spaces or the like... - print $authfh "username = $self->{username}\n"; + + # let's accept usernames with domains, as user@domain or domain/user + if ($self->{username} =~ m!^([^/@]+)([/@])(.+)$!) + { + my ($user,$delim,$domain) = ($1,$2,$3); + ($user,$domain) = ($domain,$user) if ($delim eq "/"); + + print $authfh "username = $user\ndomain = $domain\n"; + } + else + { + print $authfh "username = $self->{username}\n"; + } print $authfh "password = $self->{password}\n" if ($self->{password}); close $authfh; diff --git a/lib/func.pm b/lib/func.pm index ab5e7dc..2b8ab2b 100644 --- a/lib/func.pm +++ b/lib/func.pm @@ -256,12 +256,12 @@ sub convertIfName { $ifName =~ s/[^A-Za-z0-9_.]+/-/g; } else { - $ifName =~ s/\W+/-/g; + $ifName =~ s/\W+/-/g; # anything outside [a-zA-Z0-9_] } $ifName =~ s/\-$//g; $ifName = lc($ifName); - return $ifName + return $ifName; } sub rmBadChars { @@ -1461,7 +1461,7 @@ sub logMsg } } - $string .= "
$msg"; + $string .= "[$$]
$msg"; $string =~ s/\n/ /g; #remove all embedded newlines # we MUST NOT use setfileprot because it may call logmsg... diff --git a/lib/rrdfunc.pm b/lib/rrdfunc.pm index 06cb9aa..0252061 100644 --- a/lib/rrdfunc.pm +++ b/lib/rrdfunc.pm @@ -27,7 +27,7 @@ # # ***************************************************************************** package rrdfunc; -our $VERSION = "2.4.1"; +our $VERSION = "3.0.0"; use NMIS::uselib; use lib "$NMIS::uselib::rrdtool_lib"; @@ -703,11 +703,26 @@ sub updateRRD } } - my (@options, @ds); + my (@updateargs, @ds, %blankme); my @values = ("N"); # that's 'reading is for Now' - # if the node has gone through a reset, then insert a U to avoid spikes - but log once only - dbg("node was reset, inserting U values") if ($NI->{admin}->{node_was_reset}); + # if the node has gone through a reset, then insert a U to avoid spikes for all COUNTER-ish DS + if ($NI->{admin}->{node_was_reset}) + { + dbg("node was reset, inserting U values"); + + # get the DS definitions, extract the DS types and mark the counter-ish ones as blankable + for (grep(/^DS:/, optionsRRD(data=>$data, sys=>$S, type=>$type, index=>$index))) + { + my (undef, $dsid, $dstype) = split(/:/, $_); + if ($dstype ne "GAUGE") # basically anything non-gauge is counter-ish + { + dbg("marking DS $dsid in $type as blankable, DS type $dstype"); + $blankme{$dsid} = 1; + } + } + } + # similar to the node reset case, but this also blanks GAUGE DS dbg("node has current outage with nostats option, inserting U values") if ($NI->{admin}->{outage_nostats}); foreach my $var (keys %{$data}) { @@ -720,9 +735,13 @@ sub updateRRD push @ds, $var; - # type health, ds outage, polltime, updatetime: are never overridden - if ( ($NI->{admin}->{node_was_reset} or $NI->{admin}->{outage_nostats}) - and ($type ne "health" or $var !~ /^(outage|polltime|updatetime)$/)) + # in outage with nostats option active? + # then all rrds INCL health but EXCEPT health's outage/polltime/updatetime DS are overwritten + # or was the node reset? then all known-blankable DS are overwritten + # (as health holds only gauges and no counters the U isn't needed to avoid spikes) + if (($NI->{admin}->{node_was_reset} and $blankme{$var}) + or ($NI->{admin}->{outage_nostats} + and ($type ne "health" or $var !~ /^(outage|polltime|updatetime)$/))) { push @values, 'U'; } @@ -748,7 +767,7 @@ sub updateRRD } my $thevalue = join(":",@values); my $theds = join(":",@ds); - push @options,("-t", $theds, $thevalue); + push @updateargs,("-t", $theds, $thevalue); my $points = scalar @ds; # for bytes consider a 64 bit word, 8 bytes for each thing. @@ -763,17 +782,17 @@ sub updateRRD logPolling("$type,$NI->{system}{name},$index,$item,$theds,$thevalue"); - if ( @options) + if (@updateargs) { # update RRD - RRDs::update($database,@options); + RRDs::update($database,@updateargs); ++$stats{rrdcount}; if (my $ERROR = RRDs::error()) { if ($ERROR !~ /contains more DS|unknown DS name/) { - $stats{error} = "($S->{name}) database=$database: $ERROR: options = @options"; + $stats{error} = "($S->{name}) database=$database: $ERROR: arguments = @updateargs"; logMsg("ERROR $stats{error}"); } else diff --git a/mibs/nmis_mibs.oid b/mibs/nmis_mibs.oid index 8413908..d67fa40 100644 --- a/mibs/nmis_mibs.oid +++ b/mibs/nmis_mibs.oid @@ -970,6 +970,12 @@ "cbQosPolicyMapName" "1.3.6.1.4.1.9.9.166.1.6.1.1.1" "cbQosQueueingCfgBandwidth" "1.3.6.1.4.1.9.9.166.1.9.1.1.1" "cbQosQueueingCfgBandwidthUnits" "1.3.6.1.4.1.9.9.166.1.9.1.1.2" +"cbQosQueueingCurrentQDepth" "1.3.6.1.4.1.9.9.166.1.18.1.1.1" +"cbQosQueueingCurrentQDepthPkt" "1.3.6.1.4.1.9.9.166.1.18.1.1.9" +"cbQosQueueingTransmitByte64" "1.3.6.1.4.1.9.9.166.1.18.1.1.11" +"cbQosQueueingTransmitPkt64" "1.3.6.1.4.1.9.9.166.1.18.1.1.12" +"cbQosQueueingDiscardByte64" "1.3.6.1.4.1.9.9.166.1.18.1.1.5" +"cbQosQueueingDiscardPkt64" "1.3.6.1.4.1.9.9.166.1.18.1.1.8" "cbQosTSCfgRate" "1.3.6.1.4.1.9.9.166.1.13.1.1.1" "cceHttpPerfBytesPerSec" "1.3.6.1.4.1.9.9.178.1.1.2.3" "cceHttpPerfCpuLoad" "1.3.6.1.4.1.9.9.178.1.1.2.8" diff --git a/models-install/Common-Cisco-asset.nmis b/models-install/Common-Cisco-asset.nmis index 5d78273..30cd9e1 100644 --- a/models-install/Common-Cisco-asset.nmis +++ b/models-install/Common-Cisco-asset.nmis @@ -34,7 +34,7 @@ 'sys' => { 'entityMib' => { 'indexed' => 'entPhysicalDescr', - 'headers' => 'index,entPhysicalDescr,entPhysicalContainedIn,entPhysicalName,entPhysicalModelName,entPhysicalVendorType,entPhysicalClass,entPhysicalParentRelPos,entPhysicalHardwareRev,entPhysicalFirmwareRev,entPhysicalSerialNum', + 'headers' => 'index,entPhysicalDescr,entPhysicalContainedIn,entPhysicalName,entPhysicalModelName,cardType,entPhysicalVendorType,entPhysicalClass,entPhysicalParentRelPos,entPhysicalHardwareRev,entPhysicalFirmwareRev,entPhysicalSerialNum', 'snmp' => { 'entPhysicalDescr' => { 'oid' => 'entPhysicalDescr', @@ -94,6 +94,9 @@ 'oid' => 'entPhysicalModelName', 'title' => 'Model Name' }, + 'cardType' => { + 'title' => 'Type' + }, #'entAliasMappingIdentifier' => { # 'oid' => 'entAliasMappingIdentifier', # 'title' => 'Mapping ID' diff --git a/models-install/Common-Cisco-temp.nmis b/models-install/Common-Cisco-temp.nmis index 15ab3da..419ca63 100644 --- a/models-install/Common-Cisco-temp.nmis +++ b/models-install/Common-Cisco-temp.nmis @@ -71,7 +71,7 @@ 'sys' => { 'env-temp' => { 'indexed' => 'entSensorStatus', - 'headers' => 'tempDescr,currentTemp', + 'headers' => 'tempDescr,tempPhysicalName,currentTemp', 'snmp' => { 'tempDescr' => { 'oid' => 'entPhysicalDescr', @@ -86,6 +86,10 @@ 'oid' => 'entSensorStatus', 'title' => 'Status' }, + 'tempPhysicalName' => { + 'oid' => 'entPhysicalName', + 'title' => 'Physical Name', + }, 'tempType' => { 'replace' => { '1' => 'other', diff --git a/models-install/Common-cbqos-in-nexus.nmis b/models-install/Common-cbqos-in-nexus.nmis index a19f686..d3eff02 100644 --- a/models-install/Common-cbqos-in-nexus.nmis +++ b/models-install/Common-cbqos-in-nexus.nmis @@ -1,6 +1,4 @@ # -## $Id: Common-cbqos-in.nmis,v 8.2 2011/08/28 15:11:41 nmisdev Exp $ -# # Copyright 1999-2011 Opmantek Limited (www.opmantek.com) # # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM @@ -40,12 +38,12 @@ 'cbqos-in' => { 'indexed' => 'true', 'graphtype' => 'cbqos-in', - 'threshold' => 'qos_pkt_drop', + 'nexus' => 'true', + #'threshold' => 'qos_pkt_drop', 'snmp' => { 'DropPkt' => { 'cbqosMibObject' => 'cbQosCMDropPkt64', - 'snmpObjectName' => 'cbQosQueueingDiscardPkt64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.8', + 'oid' => 'cbQosQueueingDiscardPkt64', 'option' => 'counter,U:U' }, #'NoBufDropPkt' => { @@ -60,8 +58,7 @@ #}, 'DropByte' => { 'cbqosMibObject' => 'cbQosCMDropByte64', - 'snmpObjectName' => 'cbQosQueueingDiscardByte64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.5', + 'oid' => 'cbQosQueueingDiscardByte64', 'option' => 'counter,U:U' }, #'PrePolicyByte' => { @@ -71,14 +68,12 @@ #}, 'PostPolicyByte' => { 'cbqosMibObject' => 'cbQosCMPostPolicyByte64', - 'snmpObjectName' => 'cbQosQueueingTransmitByte64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.11', + 'oid' => 'cbQosQueueingTransmitByte64', 'option' => 'counter,U:U' }, 'PostPolicyPkt' => { 'cbqosMibObject' => 'no related cbQosCMPostPolicy mib exists', - 'snmpObjectName' => 'cbQosQueueingTransmitPkt64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.12', + 'oid' => 'cbQosQueueingTransmitPkt64', 'option' => 'counter,U:U' } }, diff --git a/models-install/Common-cbqos-out-nexus.nmis b/models-install/Common-cbqos-out-nexus.nmis index 2bbadb0..f84cec6 100644 --- a/models-install/Common-cbqos-out-nexus.nmis +++ b/models-install/Common-cbqos-out-nexus.nmis @@ -1,6 +1,4 @@ # -## $Id: Common-cbqos-out.nmis,v 8.2 2011/08/28 15:11:41 nmisdev Exp $ -# # Copyright 1999-2011 Opmantek Limited (www.opmantek.com) # # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM @@ -40,12 +38,12 @@ 'cbqos-out' => { 'indexed' => 'true', 'graphtype' => 'cbqos-out', - 'threshold' => 'qos_pkt_drop', + 'nexus' => 'true', + #'threshold' => 'qos_pkt_drop', 'snmp' => { 'DropPkt' => { 'cbqosMibObject' => 'cbQosCMDropPkt64', - 'snmpObjectName' => 'cbQosQueueingDiscardPkt64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.8', + 'oid' => 'cbQosQueueingDiscardPkt64', 'option' => 'counter,U:U' }, #'NoBufDropPkt' => { @@ -60,8 +58,7 @@ #}, 'DropByte' => { 'cbqosMibObject' => 'cbQosCMDropByte64', - 'snmpObjectName' => 'cbQosQueueingDiscardByte64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.5', + 'oid' => 'cbQosQueueingDiscardByte64', 'option' => 'counter,U:U' }, #'PrePolicyByte' => { @@ -71,14 +68,12 @@ #}, 'PostPolicyByte' => { 'cbqosMibObject' => 'cbQosCMPostPolicyByte64', - 'snmpObjectName' => 'cbQosQueueingTransmitByte64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.11', + 'oid' => 'cbQosQueueingTransmitByte64', 'option' => 'counter,U:U' }, 'PostPolicyPkt' => { 'cbqosMibObject' => 'no related cbQosCMPostPolicy mib exists', - 'snmpObjectName' => 'cbQosQueueingTransmitPkt64', - 'oid' => '1.3.6.1.4.1.9.9.166.1.18.1.1.12', + 'oid' => 'cbQosQueueingTransmitPkt64', 'option' => 'counter,U:U' } }, diff --git a/models-install/Common-database.nmis b/models-install/Common-database.nmis index d78be03..e99155c 100644 --- a/models-install/Common-database.nmis +++ b/models-install/Common-database.nmis @@ -157,6 +157,7 @@ 'Chain' => '/nodes/$node/health/Chain-$index.rrd', 'Stream' => '/nodes/$node/health/Stream-$index.rrd', 'mikrotikCpu' => '/nodes/$node/health/mikrotikCpu.rrd', + 'systemStatsMulti' => '/nodes/$node/health/systemStatsMulti-$index.rrd', } } ); diff --git a/models-install/Common-event.nmis b/models-install/Common-event.nmis index d3cb5a1..753afa0 100644 --- a/models-install/Common-event.nmis +++ b/models-install/Common-event.nmis @@ -151,6 +151,23 @@ 'level' => 'Major' } }, + 'node polling failover' => { + 'core' => { + 'logging' => 'true', + 'syslog' => 'true', + 'level' => 'Critical' + }, + 'access' => { + 'logging' => 'true', + 'syslog' => 'true', + 'level' => 'Warning' + }, + 'distribution' => { + 'logging' => 'true', + 'syslog' => 'true', + 'level' => 'Major' + } + }, 'default' => { 'core' => { 'logging' => 'true', diff --git a/models-install/Common-threshold.nmis b/models-install/Common-threshold.nmis index 7ecd3c5..ef485c6 100644 --- a/models-install/Common-threshold.nmis +++ b/models-install/Common-threshold.nmis @@ -65,7 +65,7 @@ 'warning' => '30', 'major' => '50' }, - 'control' => '$ifSpeed == 100000000' + 'control' => '$ifSpeed >= 100000000 and $ifSpeed < 1000000000' }, '40' => { 'value' => { @@ -75,7 +75,7 @@ 'warning' => '20', 'major' => '40' }, - 'control' => '$ifSpeed == 1000000000' + 'control' => '$ifSpeed >= 1000000000' }, 'default' => { 'value' => { @@ -534,7 +534,7 @@ 'warning' => '20', 'major' => '40' }, - 'control' => '$ifSpeed == 1000000000' + 'control' => '$ifSpeed >= 1000000000' }, '10' => { 'value' => { @@ -554,7 +554,7 @@ 'warning' => '30', 'major' => '50' }, - 'control' => '$ifSpeed == 100000000' + 'control' => '$ifSpeed >= 100000000 and $ifSpeed < 1000000000' }, 'default' => { 'value' => { diff --git a/models-install/Model-AristaSwitch.nmis b/models-install/Model-AristaSwitch.nmis index 7a1e9e7..35651be 100644 --- a/models-install/Model-AristaSwitch.nmis +++ b/models-install/Model-AristaSwitch.nmis @@ -275,7 +275,7 @@ 'nodeModel' => 'AristaSwitch', 'nodeType' => 'switch', 'nodeVendor' => 'Arista Networks', - 'nodegraph' => 'health,response,ip', + 'nodegraph' => 'health,response,ip,hrmem', 'rrd' => { 'mib2ip' => { 'graphtype' => 'ip,frag', @@ -632,5 +632,32 @@ } } } - } + }, + 'storage' => { + 'sys' => { + 'storage' => { + 'snmp' => { + 'hrStorageUnits' => { + 'oid' => 'hrStorageAllocationUnits' + }, + 'hrStorageSize' => { + 'oid' => 'hrStorageSize' + }, + 'hrStorageUsed' => { + 'oid' => 'hrStorageUsed' + }, + 'hrStorageDescr' => { + 'oid' => 'hrStorageDescr' + }, + 'hrStorageType' => { + 'oid' => 'hrStorageType' + } + }, + 'indexed' => 'true' + } + }, + 'nocollect' => { + 'Description' => '/mnt/cdrom|boot' + } + } ); diff --git a/models-install/Model-CiscoIOSXR.nmis b/models-install/Model-CiscoIOSXR.nmis index 90e6e4e..42f0132 100644 --- a/models-install/Model-CiscoIOSXR.nmis +++ b/models-install/Model-CiscoIOSXR.nmis @@ -738,7 +738,14 @@ 'PRINT:totalUtil:AVERAGE:totalUtil=%1.2lf', 'PRINT:inputBits:AVERAGE:inputBits=%1.2lf', 'PRINT:outputBits:AVERAGE:outputBits=%1.2lf' - ] + ], + 'cempMemPool' => [ + 'DEF:MemUsed=$database:MemPoolUsed:AVERAGE', + 'DEF:MemFree=$database:MemPoolFree:AVERAGE', + 'CDEF:memTotal=MemUsed,MemFree,+', + 'CDEF:memUtil=MemUsed,memTotal,/,100,*', + 'PRINT:memUtil:AVERAGE:memUtil=%1.2lf' + ], } }, 'summary' => { diff --git a/models-install/Model-CiscoNXOS.nmis b/models-install/Model-CiscoNXOS.nmis index 680a799..6aa7cbc 100644 --- a/models-install/Model-CiscoNXOS.nmis +++ b/models-install/Model-CiscoNXOS.nmis @@ -37,18 +37,18 @@ 'threshold' => { 'common-model' => 'threshold' }, - 'cbqos-in' => { - 'common-model' => 'cbqos-in' - }, 'heading' => { 'common-model' => 'heading' }, - 'cbqos-out' => { - 'common-model' => 'cbqos-out' - }, 'event' => { 'common-model' => 'event' }, + 'cbqos-in' => { + 'common-model' => 'cbqos-in-nexus' + }, + 'cbqos-out' => { + 'common-model' => 'cbqos-out-nexus' + }, 'asset' => { 'common-model' => 'Cisco-asset' }, @@ -58,21 +58,12 @@ 'vlan' => { 'common-model' => 'Cisco-vlan' }, + 'cbqos' => { + 'common-model' => 'Cisco-cbqos' + } } }, 'alerts' => { - 'bgpPeer' => { - 'bgpPeerStateDown' => { - 'element' => 'index', - 'event' => 'BGP Peer Down', - 'level' => 'Warning', - 'test' => 'CVAR1=bgpPeerState;$CVAR1 ne 100', - 'type' => 'test', - 'title' => "BGP Peer Status", - 'unit' => '', - 'value' => 'CVAR1=bgpPeerState;int($CVAR1)' - } - }, 'env-temp' => { 'tempStatus' => { 'type' => 'threshold-rising', @@ -280,7 +271,7 @@ 'nodegraph' => 'health,response,cpu,ip,mem-proc,routenumber,topo' }, 'systemHealth' => { - 'sections' => 'cdp,entityMib,env-temp,vtpVlan', + 'sections' => 'Cisco_CBQoS,cdp,entityMib,env-temp,vtpVlan', 'sys' => { 'env-temp' => { 'indexed' => 'entSensorStatus', @@ -317,7 +308,7 @@ '14' => 'dBm', }, 'oid' => 'entSensorType', - 'title' => 'Status' + 'title' => 'Type' }, 'currentTemp' => { 'oid' => 'entSensorValue', diff --git a/models-install/Model-CiscoVG.nmis b/models-install/Model-CiscoVG.nmis index 497ac2f..795db15 100644 --- a/models-install/Model-CiscoVG.nmis +++ b/models-install/Model-CiscoVG.nmis @@ -47,7 +47,13 @@ }, 'event' => { 'common-model' => 'event' - } + }, + 'neighbor' => { + 'common-model' => 'Cisco-neighbor' + }, + 'status' => { + 'common-model' => 'Cisco-status' + }, } }, 'system' => { @@ -491,221 +497,6 @@ } }, 'systemHealth' => { - 'sections' => 'addressTable,cdp,fanStatus,psuStatus,tempStatus', - 'rrd' => { - 'fanStatus' => { - 'control' => '$sysObjectName =~ /./', - 'graphtype' => 'fan-status', - 'indexed' => 'true', - 'snmp' => { - 'fanValue' => { - 'oid' => 'ciscoEnvMonFanState', - 'replace' => { - '1' => '100', - '2' => '75', - '3' => '0', - '4' => '50', - '5' => '50', - '6' => '25' - } - } - } - }, - 'psuStatus' => { - 'control' => '$sysObjectName =~ /./', - 'indexed' => 'true', - 'graphtype' => 'psu-status', - 'snmp' => { - 'psuValue' => { - 'oid' => 'ciscoEnvMonSupplyState', - 'replace' => { - '1' => '100', - '2' => '75', - '3' => '0', - '4' => '50', - '5' => '50', - '6' => '25', - }, - }, - }, - }, - 'tempStatus' => { - 'control' => '$sysObjectName =~ /./', - 'graphtype' => 'temp-status', - 'indexed' => 'true', - 'snmp' => { - 'tempValue' => { - 'oid' => 'ciscoEnvMonTemperatureState', - 'replace' => { - '1' => '100', - '2' => '75', - '3' => '0', - '4' => '50', - '5' => '50', - '6' => '25' - } - } - } - } - }, - 'sys' => { - 'addressTable' => { - 'headers' => 'ifDescr,ipNetToMediaIfIndex,ipNetToMediaNetAddress,ipNetToMediaPhysAddress,ipNetToMediaType', - 'index_regex' => '\\.(\\d+\\.\\d+\\.\\d+\\.\\d+\\.\\d+)$', - 'indexed' => 'ipNetToMediaIfIndex', - 'snmp' => { - 'ifDescr' => { - 'title' => 'Interface' - }, - 'ipNetToMediaIfIndex' => { - 'oid' => 'ipNetToMediaIfIndex', - 'title' => 'ifIndex' - }, - 'ipNetToMediaNetAddress' => { - 'oid' => 'ipNetToMediaNetAddress', - 'title' => 'IP Address' - }, - 'ipNetToMediaPhysAddress' => { - 'oid' => 'ipNetToMediaPhysAddress', - 'title' => 'MAC Address' - }, - 'ipNetToMediaType' => { - 'oid' => 'ipNetToMediaType', - 'replace' => { - '1' => 'other', - '2' => 'invalid', - '3' => 'dynamic', - '4' => 'static' - }, - 'title' => 'Media Type' - } - } - }, - 'cdp' => { - 'headers' => 'ifDescr,cdpCacheAddress,cdpCacheDeviceId,cdpCacheVersion,cdpCacheDevicePort,cdpCachePlatform', - 'index_oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.6', - 'index_regex' => '\\.(\\d+\.\\d+)$', - 'indexed' => 'cdpCacheDeviceId', - 'snmp' => { - 'ifDescr' => { - 'title' => 'Local Interface' - }, - 'cdpCacheAddressType' => { - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.3', - 'sysObjectName' => 'cdpCacheAddressType', - 'replace' => { - '1' => 'ip', - }, - 'title' => 'Neighbor AddressType' - }, - 'cdpCacheAddress' => { - 'calculate' => '$r =~ s/^0x//; $r = join ".", map { hex } $r =~ /../g;', - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.4', - 'sysObjectName' => 'cdpCacheAddress', - 'title' => 'Neighbor Address' - }, - 'cdpCacheVersion' => { - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.5', - 'sysObjectName' => 'cdpCacheVersion', - 'title' => 'Neighbor Version' - }, - 'cdpCacheDeviceId' => { - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.6', - 'sysObjectName' => 'cdpCacheDeviceId', - 'title' => 'Neighbor DeviceId' - }, - 'cdpCacheDevicePort' => { - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.7', - 'sysObjectName' => 'cdpCacheDevicePort', - 'title' => 'Neighbor Interface' - }, - 'cdpCachePlatform' => { - 'oid' => '1.3.6.1.4.1.9.9.23.1.2.1.1.8', - 'sysObjectName' => 'cdpCachePlatform', - 'title' => 'Neighbor Platform' - }, - } - }, - 'fanStatus' => { - 'control' => '$sysObjectName =~ /./', - 'headers' => 'FanStatusDescr', - 'indexed' => 'ciscoEnvMonFanStatusDescr', - 'snmp' => { - 'FanStateName' => { - 'oid' => 'ciscoEnvMonFanState', - 'replace' => { - '1' => 'normal', - '2' => 'warning', - '3' => 'critical', - '4' => 'shutdown', - '5' => 'notPresent', - '6' => 'notFunctioning' - }, - 'title' => 'Fan State' - }, - 'FanStatusDescr' => { - 'oid' => 'ciscoEnvMonFanStatusDescr', - 'title' => 'Fan Status Descr' - } - } - }, - 'psuStatus' => { - 'control' => '$sysObjectName =~ /./', - 'indexed' => 'ciscoEnvMonSupplyStatusDescr', - 'headers' => 'SupplyStatusDescr,SupplyStatusSource', - 'snmp' => { - 'SupplyStatusDescr' => { - 'oid' => 'ciscoEnvMonSupplyStatusDescr', - 'title' => 'PSU Status Descr' - }, - 'SupplyStatusSource' => { - 'oid' => 'ciscoEnvMonSupplySource', - 'title' => 'PSU Source', - 'replace' => { - '1' => 'unknown', - '2' => 'ac', - '3' => 'dc', - '4' => 'externalPowerSupply', - '5' => 'internalRedundant', - }, - }, - 'SupplyStateName' => { - 'oid' => 'ciscoEnvMonSupplyState', - 'title' => 'PSU State', - 'replace' => { - '1' => 'normal', - '2' => 'warning', - '3' => 'critical', - '4' => 'shutdown', - '5' => 'notPresent', - '6' => 'notFunctioning', - }, - }, - }, - }, - 'tempStatus' => { - 'control' => '$sysObjectName =~ /cisco8../', - 'headers' => 'TemperatureStatusDescr', - 'indexed' => 'ciscoEnvMonTemperatureStatusDescr', - 'snmp' => { - 'TemperatureStateName' => { - 'oid' => 'ciscoEnvMonTemperatureState', - 'replace' => { - '1' => 'normal', - '2' => 'warning', - '3' => 'critical', - '4' => 'shutdown', - '5' => 'notPresent', - '6' => 'notFunctioning' - }, - 'title' => 'Temperature State' - }, - 'TemperatureStatusDescr' => { - 'oid' => 'ciscoEnvMonTemperatureStatusDescr', - 'title' => 'Temperature Status Descr' - } - } - } - } + 'sections' => 'addressTable,cdp,fanStatus,psuStatus', } ); diff --git a/models-install/Model-MikroTik.nmis b/models-install/Model-MikroTik.nmis index d7e017f..7d7e0f0 100644 --- a/models-install/Model-MikroTik.nmis +++ b/models-install/Model-MikroTik.nmis @@ -367,6 +367,7 @@ 'nodeType' => 'router', 'rrd' => { 'mikrotikCpu' => { + 'control' => '$sysDescr !~ /RB951G|RB433AH|RB\d+/', #'indexed' => 'true', 'graphtype' => 'mikrotikCpu', 'snmp' => { diff --git a/models-install/Model-NetVanta.nmis b/models-install/Model-NetVanta.nmis new file mode 100644 index 0000000..b6d2e78 --- /dev/null +++ b/models-install/Model-NetVanta.nmis @@ -0,0 +1,392 @@ +# +## $Id: Model-Default.nmis,v 8.7 2012/11/16 05:46:04 keiths Exp $ +# +# Copyright 1999-2011 Opmantek Limited (www.opmantek.com) +# +# ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM +# +# This file is part of Network Management Information System (“NMIS”). +# +# NMIS 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. +# +# NMIS 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 NMIS (most likely in a file named LICENSE). +# If not, see +# +# For further information on NMIS or for a license other than GPL please see +# www.opmantek.com or email contact@opmantek.com +# +# User group details: +# http://support.opmantek.com/users/ +# +# ***************************************************************************** + +%hash = ( + '-common-' => { + 'class' => { + 'database' => { + 'common-model' => 'database' + }, + 'threshold' => { + 'common-model' => 'threshold' + }, + 'summary' => { + 'common-model' => 'summary' + }, + 'heading' => { + 'common-model' => 'heading' + }, + 'stats' => { + 'common-model' => 'stats' + }, + 'event' => { + 'common-model' => 'event' + }, + 'lldp' => { + 'common-model' => 'lldp' + }, + 'entityMib' => { + 'common-model' => 'entityMib' + }, + } + }, + 'systemHealth' => { + 'sections' => 'lldp,lldpLocal,entityMib', + }, + 'system' => { + 'nodegraph' => 'health,response,ip', + 'nodeModel' => 'NetVanta', + 'nodeType' => 'switch', + 'sys' => { + 'standard' => { + 'snmp' => { + 'sysLocation' => { + 'replace' => { + '' => 'default' + }, + 'oid' => 'sysLocation', + 'title' => 'Location' + }, + 'sysDescr' => { + 'oid' => 'sysDescr', + 'title' => 'Description' + }, + 'sysObjectID' => { + 'oid' => 'sysObjectID' + }, + 'sysUpTime' => { + 'oid' => 'sysUpTime', + 'title' => 'Uptime' + }, + 'ifNumber' => { + 'oid' => 'ifNumber', + 'title' => 'Interfaces' + }, + 'sysContact' => { + 'replace' => { + '' => 'default' + }, + 'oid' => 'sysContact', + 'title' => 'Contact' + }, + 'sysName' => { + 'oid' => 'sysName' + } + } + } + }, + 'rrd' => { + 'mib2ip' => { + 'snmp' => { + 'ipInAddrErrors' => { + 'oid' => 'ipInAddrErrors', + 'option' => 'counter,0:U' + }, + 'ipFragCreates' => { + 'oid' => 'ipFragCreates', + 'option' => 'counter,0:U' + }, + 'ipInDiscards' => { + 'oid' => 'ipInDiscards', + 'option' => 'counter,0:U' + }, + 'ipInReceives' => { + 'oid' => 'ipInReceives', + 'option' => 'counter,0:U' + }, + 'ipFragOKs' => { + 'oid' => 'ipFragOKs', + 'option' => 'counter,0:U' + }, + 'ipInDelivers' => { + 'oid' => 'ipInDelivers', + 'option' => 'counter,0:U' + }, + 'ipReasmFails' => { + 'oid' => 'ipReasmFails', + 'option' => 'counter,0:U' + }, + 'ipReasmReqds' => { + 'oid' => 'ipReasmReqds', + 'option' => 'counter,0:U' + }, + 'ipFragFails' => { + 'oid' => 'ipFragFails', + 'option' => 'counter,0:U' + }, + 'ipOutRequests' => { + 'oid' => 'ipOutRequests', + 'option' => 'counter,0:U' + }, + 'ipOutNoRoutes' => { + 'oid' => 'ipOutNoRoutes' + }, + 'ipInHdrErrors' => { + 'oid' => 'ipInHdrErrors', + 'option' => 'counter,0:U' + }, + 'ipForwDatagrams' => { + 'oid' => 'ipForwDatagrams', + 'option' => 'counter,0:U' + }, + 'ipOutDiscards' => { + 'oid' => 'ipOutDiscards', + 'option' => 'counter,0:U' + }, + 'ipReasmOKs' => { + 'oid' => 'ipReasmOKs', + 'option' => 'counter,0:U' + }, + 'ipInUnknownProtos' => { + 'oid' => 'ipInUnknownProtos', + 'option' => 'counter,0:U' + } + }, + 'graphtype' => 'ip,frag' + } + } + }, + 'custom' => { + 'interface' => { + 'ifAdminStatus' => 'true', # default = true + 'ifNumber' => 'true', # default = true + 'ifLastChange' => 'true', # default = false + 'ifTableLastChange' => 'true', # default = false + 'skipIfComment' => 'This is used for mega devices to get rid of unwanted interfaces. Big SP switches for example. If skipIfType or skipIfDescr regex is matched, then just skip this interface all together. Must be used with ifNumber = false, ifAdminStatus = false, ifLastChange = false, ifTableLastChange = false', + 'skipIfType' => 'ppp|tunnel', + 'skipIfDescr' => 'tunnel\d+|ppp\d+' + } + }, + 'interface' => { + 'sys' => { + 'standard' => { + 'indexed' => 'true', + 'snmp' => { + 'ifOperStatus' => { + 'replace' => { + '6' => 'notPresent', + '1' => 'up', + '4' => 'unknown', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifOperStatus', + 'title' => 'Oper Status' + }, + 'ifDescr' => { + 'oid' => 'ifDescr', + 'title' => 'Name (ifDescr)' + }, + 'ifPhysAddress' => { + # 'oid' => 'ifPhysAddress', + 'title' => 'Physical Address', + }, + + 'ifSpeed' => { + # 'oid' => 'ifSpeed', + 'title' => 'Bandwidth' + }, + 'ifAdminStatus' => { + 'replace' => { + '6' => 'notPresent', + '1' => 'up', + '4' => 'unknown', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifAdminStatus', + 'title' => 'Admin Status' + }, + 'ifType' => { + 'oid' => 'ifType', + 'title' => 'Type (ifType)' + }, + 'ifLastChange' => { + 'oid' => 'ifLastChange', + 'title' => 'Last Change' + } + } + }, + 'extra' => { + 'indexed' => 'true', + 'snmp' => { + 'ifHighSpeed' => { + # 'oid' => 'ifHighSpeed' + }, + 'Description' => { + 'oid' => 'ifAlias', + 'title' => 'Description (ifAlias)' + } + } + } + }, + 'rrd' => { + 'interface' => { + 'indexed' => 'true', + 'snmp' => { + 'ifPhysAddress' => { + 'oid' => 'ifPhysAddress', + 'option' => 'nosave', + }, + 'ifSpeed' => { + 'oid' => 'ifSpeed', + 'option' => 'nosave', + }, + 'ifHighSpeed' => { + 'oid' => 'ifHighSpeed', + 'option' => 'nosave', + }, + + 'ifOperStatus' => { + 'replace' => { + '6' => 'notPresent', + '1' => 'up', + '4' => 'unknown', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifOperStatus', + 'option' => 'gauge,0:100' + }, + 'ifDescr' => { + 'oid' => 'ifDescr' + }, + 'ifInOctets' => { + 'oid' => 'ifInOctets', + 'option' => 'counter,0:U' + }, + 'ifHCOutOctets' => { + 'oid' => 'ifHCOutOctets', + 'option' => 'counter,0:U' + }, + 'ifAdminStatus' => { + 'replace' => { + '6' => 'notPresent', + '1' => 'up', + '4' => 'unknown', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifAdminStatus' + }, + 'ifOutOctets' => { + 'oid' => 'ifOutOctets', + 'option' => 'counter,0:U' + }, + 'ifHCInOctets' => { + 'oid' => 'ifHCInOctets', + 'option' => 'counter,0:U' + } + }, + 'threshold' => 'util_in,util_out', + 'graphtype' => 'bits,abits,maxbits,util,autil' + }, + 'pkts_hc' => { + 'indexed' => 'true', + 'threshold' => 'pkt_errors_in,pkt_errors_out', + 'graphtype' => 'pkts_hc,errpkts_hc', + 'snmp' => { + 'ifHCInOctets' => { + 'oid' => 'ifHCInOctets', + 'option' => 'counter,0:U' + }, + 'ifHCInUcastPkts' => { + 'oid' => 'ifHCInUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCInMcastPkts' => { + 'oid' => 'ifHCInMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCInBcastPkts' => { + 'oid' => 'ifHCInBroadcastPkts', + 'option' => 'counter,0:U' + }, + 'ifInDiscards' => { + 'oid' => 'ifInDiscards', + 'option' => 'counter,0:U' + }, + 'ifInErrors' => { + 'oid' => 'ifInErrors', + 'option' => 'counter,0:U' + }, + 'ifHCOutOctets' => { + 'oid' => 'ifHCOutOctets', + 'option' => 'counter,0:U' + }, + 'ifHCOutUcastPkts' => { + 'oid' => 'ifHCOutUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCOutMcastPkts' => { + 'oid' => 'ifHCOutMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCOutBcastPkts' => { + 'oid' => 'ifHCOutBroadcastPkts', + 'option' => 'counter,0:U' + }, + 'ifOutDiscards' => { + 'oid' => 'ifOutDiscards', + 'option' => 'counter,0:U' + }, + 'ifOutErrors' => { + 'oid' => 'ifOutErrors', + 'option' => 'counter,0:U' + }, + } + } + }, + 'collect' => { + 'Description' => '(CNOC|Collection)', + }, + 'nocollect' => { + 'ifOperStatus' => 'other', + 'ifDescr' => 'sublayer|null|controller|^e0|^e1|^t0|^t1|^t3|async|bri[0-9]$|virtual|Bearer Channel|virtual-access', + 'Description' => '^#', + 'noDescription' => 'true', + 'ifType' => 'lapd|ds0|ds1|other|softwareloopback|isdn' + }, + 'noevent' => { + 'Description' => '^!', + 'ifDescr' => 'null', + 'ifType' => 'other' + } + } +); diff --git a/models-install/Model-PulseSecure.nmis b/models-install/Model-PulseSecure.nmis new file mode 100644 index 0000000..80d2d43 --- /dev/null +++ b/models-install/Model-PulseSecure.nmis @@ -0,0 +1,660 @@ +# +# Model-PulseSecure, v 1.0 2018/03/30 markh@opmantek.com +# +# Based on PULSESECURE-PSG-MIB REVISION "201607071610Z" +# PULSESECURE-PSG-MIB DEFINITIONS +# NOTIFICATION-TYPE, MODULE-IDENTITY, enterprises FROM SNMPv2-SMI +# NetworkAddress FROM RFC1155-SMI +# IpAddress FROM RFC1155-SMI +# +# Copyright 1999-2018 Opmantek Limited (www.opmantek.com) +# +# ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM +# +# This file is part of Network Management Information System (“NMIS”). +# +# NMIS 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. +# +# NMIS 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 NMIS (most likely in a file named LICENSE). +# If not, see +# +# For further information on NMIS or for a license other than GPL please see +# www.opmantek.com or email contact@opmantek.com +# +# User group details: +# http://support.opmantek.com/users/ +# +# ***************************************************************************** + +%hash = ( + '-common-' => { + 'class' => { + 'database' => { + 'common-model' => 'database' + }, + 'summary' => { + 'common-model' => 'summary' + }, + 'threshold' => { + 'common-model' => 'threshold' + }, + 'stats' => { + 'common-model' => 'stats' + }, + 'calls' => { + 'common-model' => 'calls' + }, + 'heading' => { + 'common-model' => 'heading' + }, + 'event' => { + 'common-model' => 'event' + }, + } + }, + 'system' => { + 'nodeModel' => 'PulseSecure', + 'nodeVendor' => 'PulseSecure', + 'nodeType' => 'gateway', + 'nodegraph' => 'health,response,ip,tcp-conn,tcp-segs,hrsystem,laload,hrsmpcpu,hrmem,hrcachemem,hrbufmem,hrswapmem,hrvmem', + 'rrd' => { + 'nodehealth' => { + 'graphtype' => 'tcp-conn,tcp-segs', + 'snmp' => { + 'tcpActiveOpens' => { + 'oid' => 'tcpActiveOpens', + 'option' => 'counter,0:U' + }, + 'tcpPassiveOpens' => { + 'oid' => 'tcpPassiveOpens', + 'option' => 'counter,0:U' + }, + 'tcpAttemptFails' => { + 'oid' => 'tcpAttemptFails', + 'option' => 'counter,0:U' + }, + 'tcpEstabResets' => { + 'oid' => 'tcpEstabResets', + 'option' => 'counter,0:U' + }, + 'tcpCurrEstab' => { + 'oid' => 'tcpCurrEstab', + 'option' => 'gauge,0:U' + }, + 'tcpInSegs' => { + 'oid' => 'tcpInSegs', + 'option' => 'counter,0:U' + }, + 'tcpOutSegs' => { + 'oid' => 'tcpOutSegs', + 'option' => 'counter,0:U' + }, + 'tcpRetransSegs' => { + 'oid' => 'tcpRetransSegs', + 'option' => 'counter,0:U' + }, + 'tcpInErrs' => { + 'oid' => 'tcpInErrs', + 'option' => 'counter,0:U' + }, + 'tcpOutRsts' => { + 'oid' => 'tcpOutRsts', + 'option' => 'counter,0:U' + } + }, + }, + 'laload' => { + 'graphtype' => 'laload', + 'snmp' => { + 'laLoad1' => { + 'oid' => 'laLoad.1', + 'option' => 'gauge,0:U' + }, + 'laLoad5' => { + 'oid' => 'laLoad.2', + 'option' => 'gauge,0:U' + } + } + }, + 'mib2ip' => { + 'graphtype' => 'ip,frag', + 'snmp' => { + 'ipInAddrErrors' => { + 'oid' => 'ipInAddrErrors', + 'option' => 'counter,0:U' + }, + 'ipFragCreates' => { + 'oid' => 'ipFragCreates', + 'option' => 'counter,0:U' + }, + 'ipInDiscards' => { + 'oid' => 'ipInDiscards', + 'option' => 'counter,0:U' + }, + 'ipInReceives' => { + 'oid' => 'ipInReceives', + 'option' => 'counter,0:U' + }, + 'ipFragOKs' => { + 'oid' => 'ipFragOKs', + 'option' => 'counter,0:U' + }, + 'ipInDelivers' => { + 'oid' => 'ipInDelivers', + 'option' => 'counter,0:U' + }, + 'ipReasmFails' => { + 'oid' => 'ipReasmFails', + 'option' => 'counter,0:U' + }, + 'ipReasmReqds' => { + 'oid' => 'ipReasmReqds', + 'option' => 'counter,0:U' + }, + 'ipFragFails' => { + 'oid' => 'ipFragFails', + 'option' => 'counter,0:U' + }, + 'ipOutRequests' => { + 'oid' => 'ipOutRequests', + 'option' => 'counter,0:U' + }, + 'ipOutNoRoutes' => { + 'oid' => 'ipOutNoRoutes', + 'option' => 'counter,0:U' + }, + 'ipInHdrErrors' => { + 'oid' => 'ipInHdrErrors', + 'option' => 'counter,0:U' + }, + 'ipForwDatagrams' => { + 'oid' => 'ipForwDatagrams', + 'option' => 'counter,0:U' + }, + 'ipOutDiscards' => { + 'oid' => 'ipOutDiscards', + 'option' => 'counter,0:U' + }, + 'ipReasmOKs' => { + 'oid' => 'ipReasmOKs', + 'option' => 'counter,0:U' + }, + 'ipInUnknownProtos' => { + 'oid' => 'ipInUnknownProtos', + 'option' => 'counter,0:U' + } + }, + } + }, + 'sys' => { + 'standard' => { + 'snmp' => { + 'ifNumber' => { + 'oid' => 'ifNumber', + 'title' => 'Interfaces' + }, + 'sysName' => { + 'oid' => 'sysName' + }, + 'sysLocation' => { + 'replace' => { + '' => 'default' + }, + 'oid' => 'sysLocation', + 'title' => 'Location' + }, + 'sysObjectID' => { + 'oid' => 'sysObjectID' + }, + 'sysDescr' => { + 'oid' => 'sysDescr', + 'title' => 'Description' + }, + 'sysUpTime' => { + 'oid' => 'snmpEngineTime', + 'title' => 'Uptime', + 'calculate' => '$r * 100' + }, + 'snmpUpTime' => { + 'oid' => 'sysUpTime', + 'title' => 'SNMP_Uptime', + }, + 'sysContact' => { + 'replace' => { + '' => 'default' + }, + 'oid' => 'sysContact', + 'title' => 'Contact' + } + } + }, + 'laLoad' => { + 'snmp' => { + 'laLoad1' => { + 'oid' => 'laLoad.1', + }, + 'laLoad5' => { + 'oid' => 'laLoad.2', + } + } + }, + 'extra' => { + 'snmp' => { + # IVE Licensed Product Name + 'productName' => { + 'oid' => '1.3.6.1.4.1.12532.6.0', + 'snmpObjectName' => 'productName', + 'title' => 'productName' + }, + # IVE System Software Version + 'productVersion' => { + 'oid' => '1.3.6.1.4.1.12532.7.0', + 'snmpObjectName' => 'productVersion', + 'title' => 'productVersion' + }, + # Active ESAP Version + 'esapVersion' => { + 'oid' => '1.3.6.1.4.1.12532.45.0', + 'snmpObjectName' => 'esapVersion', + 'title' => 'esapVersion' + }, + # Log file nearly full + 'logFullPercent' => { + 'oid' => '1.3.6.1.4.1.12532.1.0', + 'snmpObjectName' => 'logFullPercent', + 'title' => 'logFullPercent', + 'option' => 'gauge,0:U' + }, + # Number of Signed-In Web Users + 'signedInWebUsers' => { + 'oid' => '1.3.6.1.4.1.12532.2.0', + 'snmpObjectName' => 'signedInWebUsers', + 'title' => 'signedInWebUsers', + 'option' => 'gauge,0:U' + }, + # Number of Signed-In Mail Users + 'signedInMailUsers' => { + 'oid' => '1.3.6.1.4.1.12532.3.0', + 'snmpObjectName' => 'signedInMailUsers', + 'title' => 'signedInMailUsers', + 'option' => 'gauge,0:U' + }, + # The Total number of Concurrent user Licenses used for the IVE Node + 'iveConcurrentUsers' => { + 'oid' => '1.3.6.1.4.1.12532.12.0', + 'snmpObjectName' => 'iveConcurrentUsers', + 'title' => 'iveConcurrentUsers', + 'option' => 'gauge,0:U' + }, + # The Total number of Concurrent user Licenses used for the Cluster + 'clusterConcurrentUsers' => { + 'oid' => '1.3.6.1.4.1.12532.13.0', + 'snmpObjectName' => 'clusterConcurrentUsers', + 'title' => 'clusterConcurrentUsers', + 'option' => 'gauge,0:U' + }, + # The Total number of hits to the IVE since last reboot + 'iveTotalHits' => { + 'oid' => '1.3.6.1.4.1.12532.14.0', + 'snmpObjectName' => 'iveTotalHits', + 'title' => 'iveTotalHits', + 'option' => 'counter,0:U' + }, + # The Total number of hits via the Web Interface since the last reboot + 'iveWebHits' => { + 'oid' => '1.3.6.1.4.1.12532.16.0', + 'snmpObjectName' => 'iveWebHits', + 'title' => 'iveWebHits', + 'option' => 'counter,0:U' + }, + } + }, + } + }, + 'interface' => { + 'rrd' => { + 'interface' => { + 'indexed' => 'true', + 'threshold' => 'util_in,util_out', + 'graphtype' => 'bits,abits,maxbits,util,autil', + 'snmp' => { + 'ifOperStatus' => { + 'replace' => { + '6' => 'notPresent', + '4' => 'unknown', + '1' => 'up', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifOperStatus', + 'option' => 'gauge,0:100' + }, + 'ifDescr' => { + 'oid' => 'ifDescr' + }, + + 'ifHCOutOctets' => { + 'oid' => 'ifHCOutOctets', + 'option' => 'counter,0:$ifSpeed' + }, + 'ifOutOctets' => { + 'oid' => 'ifOutOctets', + 'option' => 'counter,0:$ifSpeed' + }, + 'ifAdminStatus' => { + 'replace' => { + '6' => 'notPresent', + '4' => 'unknown', + '1' => 'up', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifAdminStatus' + }, + 'ifLastChange' => { + 'oid' => 'ifLastChange' + }, + 'ifInOctets' => { + 'oid' => 'ifInOctets', + 'option' => 'counter,0:$ifSpeed' + }, + 'ifHCInOctets' => { + 'oid' => 'ifHCInOctets', + 'option' => 'counter,0:$ifSpeed' + } + } + }, + 'pkts_hc' => { + 'indexed' => 'true', + 'threshold' => 'pkt_errors_in,pkt_errors_out,pkt_discards_in,pkt_discards_out', + 'graphtype' => 'pkts_hc,errpkts_hc', + 'snmp' => { + 'ifInOctets' => { + 'oid' => 'ifInOctets', + 'option' => 'counter,0:U' + }, + 'ifInUcastPkts' => { + 'oid' => 'ifInUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifInMcastPkts' => { + 'oid' => 'ifInMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifInBcastPkts' => { + 'oid' => 'ifInBroadcastPkts', + 'option' => 'counter,0:U' + }, + 'ifInDiscards' => { + 'oid' => 'ifInDiscards', + 'option' => 'counter,0:U' + }, + 'ifInErrors' => { + 'oid' => 'ifInErrors', + 'option' => 'counter,0:U' + }, + 'ifOutOctets' => { + 'oid' => 'ifOutOctets', + 'option' => 'counter,0:U' + }, + 'ifOutUcastPkts' => { + 'oid' => 'ifOutUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifOutMcastPkts' => { + 'oid' => 'ifOutMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifOutBcastPkts' => { + 'oid' => 'ifOutBroadcastPkts', + 'option' => 'counter,0:U' + }, + 'ifOutDiscards' => { + 'oid' => 'ifOutDiscards', + 'option' => 'counter,0:U' + }, + 'ifOutErrors' => { + 'oid' => 'ifOutErrors', + 'option' => 'counter,0:U' + }, + 'ifHCInOctets' => { + 'oid' => 'ifHCInOctets', + 'option' => 'counter,0:U' + }, + 'ifHCInUcastPkts' => { + 'oid' => 'ifHCInUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCInMcastPkts' => { + 'oid' => 'ifHCInMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCInBcastPkts' => { + 'oid' => 'ifHCInBroadcastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCOutOctets' => { + 'oid' => 'ifHCOutOctets', + 'option' => 'counter,0:U' + }, + 'ifHCOutUcastPkts' => { + 'oid' => 'ifHCOutUcastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCOutMcastPkts' => { + 'oid' => 'ifHCOutMulticastPkts', + 'option' => 'counter,0:U' + }, + 'ifHCOutBcastPkts' => { + 'oid' => 'ifHCOutBroadcastPkts', + 'option' => 'counter,0:U' + } + }, + } + }, + 'sys' => { + 'standard' => { + 'indexed' => 'true', + 'snmp' => { + 'ifOperStatus' => { + 'replace' => { + '6' => 'notPresent', + '4' => 'unknown', + '1' => 'up', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifOperStatus', + 'title' => 'Oper Status' + }, + 'ifDescr' => { + 'oid' => 'ifDescr', + 'title' => 'Name (ifDescr)' + }, + 'ifPhysAddress' => { + 'oid' => 'ifPhysAddress', + 'title' => 'Physical Address', + }, + + 'ifAdminStatus' => { + 'replace' => { + '6' => 'notPresent', + '4' => 'unknown', + '1' => 'up', + '3' => 'testing', + '7' => 'lowerLayerDown', + '2' => 'down', + '5' => 'dormant' + }, + 'oid' => 'ifAdminStatus', + 'title' => 'Admin Status' + }, + 'ifSpeed' => { + 'oid' => 'ifSpeed', + 'title' => 'Bandwidth' + }, + 'ifLastChange' => { + 'oid' => 'ifLastChange', + 'title' => 'Last Change' + }, + 'ifType' => { + 'oid' => 'ifType', + 'title' => 'Type (ifType)' + } + } + }, + 'extra' => { + 'snmp' => { + 'ifHighSpeed' => { + 'oid' => 'ifHighSpeed' + }, + 'Description' => { + 'oid' => 'ifAlias', + 'title' => 'Description' + } + }, + 'indexed' => 'true' + } + }, + 'noevent' => { + 'ifDescr' => 'null', + 'Description' => '^!|^0', + 'ifType' => 'other' + }, + 'collect' => { + 'Description' => '(CNOC|Collection)', + }, + 'nocollect' => { + 'ifOperStatus' => 'other', + 'ifDescr' => 'sublayer|null', + 'Description' => '^#', + 'noDescription' => 'false', + 'ifType' => 'other|softwareloopback|isdn' + } + }, +'systemHealth' => { + 'sections' => 'systemStatsMulti', + 'sys' => { + 'systemStatsMulti' => { + 'headers' => 'index', + 'index_oid' => '1.3.6.1.4.1.2021.11.50', + 'indexed' => 'ssCpuRawUser', + 'snmp' => { + 'ssCpuRawUser' => { + 'oid' => 'ssCpuRawUser', + 'title' => 'ssCpuRawUser' + }, + }, + }, + }, + + 'rrd' => { + 'systemStatsMulti' => { + 'graphtype' => 'ss-cpu,ss-blocks,ss-intcon', + 'threshold' => 'ssCpuRawUser,ssCpuRawSystem,ssCpuRawWait,ssCpuRawIdle', + 'indexed' => 'true', + 'snmp' => { + 'ssCpuRawUser' => { + 'oid' => 'ssCpuRawUser', + 'title' => 'ssCpuRawUser', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawUser.0 = Counter32: 114857779' + }, + 'ssCpuRawNice' => { + 'oid' => 'ssCpuRawNice', + 'title' => 'ssCpuRawNice', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawNice.0 = Counter32: 1294466' + }, + 'ssCpuRawSystem' => { + 'oid' => 'ssCpuRawSystem', + 'title' => 'ssCpuRawSystem', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawSystem.0 = Counter32: 15958442' + }, + 'ssCpuRawIdle' => { + 'oid' => 'ssCpuRawIdle', + 'title' => 'ssCpuRawIdle', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawIdle.0 = Counter32: 336740221' + }, + 'ssCpuRawWait' => { + 'oid' => 'ssCpuRawWait', + 'title' => 'ssCpuRawWait', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawWait.0 = Counter32: 17718957' + }, + 'ssCpuRawKernel' => { + 'oid' => 'ssCpuRawKernel', + 'title' => 'ssCpuRawKernel', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawKernel.0 = Counter32: 0' + }, + 'ssCpuRawInterrupt' => { + 'oid' => 'ssCpuRawInterrupt', + 'title' => 'ssCpuRawInterrupt', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawInterrupt.0 = Counter32: 93048' + }, + 'ssCpuRawSoftIRQ' => { + 'oid' => 'ssCpuRawSoftIRQ', + 'title' => 'ssCpuRawSoftIRQ', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssCpuRawSoftIRQ.0 = Counter32: 4101707' + }, + 'ssIORawSent' => { + 'oid' => 'ssIORawSent', + 'title' => 'ssIORawSent', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssIORawSent.0 = Counter32: 2246167484' + }, + 'ssIORawReceived' => { + 'oid' => 'ssIORawReceived', + 'title' => 'ssIORawReceived', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssIORawReceived.0 = Counter32: 15277366' + }, + 'ssRawInterrupts' => { + 'oid' => 'ssRawInterrupts', + 'title' => 'ssRawInterrupts', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssRawInterrupts.0 = Counter32: 1995002251' + }, + 'ssRawContexts' => { + 'oid' => 'ssRawContexts', + 'title' => 'ssRawContexts', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssRawContexts.0 = Counter32: 1623525406' + }, + 'ssRawSwapIn' => { + 'oid' => 'ssRawSwapIn', + 'title' => 'ssRawSwapIn', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssRawSwapIn.0 = Counter32: 1580' + }, + 'ssRawSwapOut' => { + 'oid' => 'ssRawSwapOut', + 'title' => 'ssRawSwapOut', + 'option' => 'counter,0:U', + 'info' => 'UCD-SNMP-MIB::ssRawSwapOut.0 = Counter32: 10695' + }, + }, + }, + }, + }, +); diff --git a/models-install/Model.nmis b/models-install/Model.nmis index ca980f2..9965147 100644 --- a/models-install/Model.nmis +++ b/models-install/Model.nmis @@ -605,6 +605,20 @@ }, } }, + 'Neoteris, Inc.' => { + 'order' => { + '10' => { + 'PulseSecure' => 'Pulse Secure' + } + } + }, + 'Adtran' => { + 'order' => { + '10' => { + 'NetVanta' => 'NetVanta' + } + } + }, }, 'system' => { 'nodeModel' => 'Default',