diff --git a/admin/migrate_rrd_locations.pl b/admin/migrate_rrd_locations.pl index 7443c5e..e04721f 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.3G"; +our $VERSION = "8.6.4G"; use strict; use File::Copy; diff --git a/admin/support.pl b/admin/support.pl index fcb82dc..dc942db 100755 --- a/admin/support.pl +++ b/admin/support.pl @@ -27,11 +27,12 @@ # http://support.opmantek.com/users/ # # ***************************************************************************** -our $VERSION = "1.7.0"; +our $VERSION = "1.8.0"; use strict; use Data::Dumper; use File::Basename; use File::Temp; +use File::Path; use POSIX qw(); use Cwd; use FindBin; @@ -51,7 +52,7 @@ \n\n"; die $usage if (@ARGV == 1 && $ARGV[0] =~ /^-[h\?]/); - + my %args = getArguements(@ARGV); die $usage if ($args{action} ne "collect"); @@ -139,17 +140,17 @@ # collect evidence my $timelabel = POSIX::strftime("%Y-%m-%d-%H%M",localtime); my $targetdir = "$td/nmis-collect.$timelabel"; - mkdir($targetdir); + File::Path::make_path($targetdir, { chmod => 0755 }); print "collecting support evidence...\n"; my $status = collect_evidence($targetdir, \%args); die "failed to collect evidence: $status\n" if ($status); - + my $omkzfn; # if omk and its support tool found, run that as well if allowed to! if (-d "/usr/local/omk" && -f "/usr/local/omk/bin/support.pl" && !$args{no_other_tools}) { open(LF, ">$targetdir/omk-support.log"); - + print "\nFound local OMK installation with OMK support tool. Please wait while we collect OMK information as well.\n"; open(F, "/usr/local/omk/bin/support.pl action=collect no_system_stats=1 no_other_tools=1 2>&1 |") @@ -165,16 +166,16 @@ close F; close LF; } - + print "\nEvidence collection complete, zipping things up...\n"; - + # do we have zip? or only tar+gz? my $canzip=0; $status = system("zip --version >/dev/null 2>&1"); $canzip=1 if (POSIX::WIFEXITED($status) && !POSIX::WEXITSTATUS($status)); - + my $zfn = "/tmp/nmis-support-$timelabel.".($canzip?"zip":"tgz"); - + # zip mustn't become too large, hence we possibly tail/truncate some or all log files opendir(D,"$targetdir/logs") or warn "can't read $targetdir/logs dir: $!\n"; @@ -194,11 +195,11 @@ $status = system("tar","-czf",$zfn,"nmis-collect.$timelabel"); } chdir($curdir); - + die "cannot create support zip file $zfn: $!\n" if (POSIX::WEXITSTATUS($status)); last if (-s $zfn < $maxzip); - + # hmm, too big: shrink the log files one by one until the size works out unlink($zfn); print "zipfile too big, trying to shrink some logfiles...\n"; @@ -213,8 +214,8 @@ die "\nPROBLEM: cannot reduce zip file size any further!\nPlease rerun $0 with maxzipsize=N higher than $maxzip.\n"; } } - - + + print "\nAll done.\n\nCollected system information is in $zfn\n"; print "OMK information is in $omkzfn\n\n" if ($omkzfn); print "Please include ".($omkzfn? "these zip files": "this zip file"). " when you contact @@ -293,7 +294,7 @@ sub collect_evidence $dirstocheck .= " $dbdir" if ($dbdir !~ /^$basedir/); $dirstocheck .= " $logdir" if ($logdir !~ /^$basedir/); - mkdir("$targetdir/system_status"); + File::Path::make_path("$targetdir/system_status", { chmod => 0755 }); # dump a recursive file list, ls -haRH does NOT work as it won't follow links except given on the cmdline # this needs to cover dbdir and vardir if outside system("find -L $dirstocheck -type d -print0| xargs -0 ls -laH > $targetdir/system_status/filelist.txt") == 0 @@ -328,7 +329,7 @@ sub collect_evidence system("mount >> $targetdir/system_status/disk_info"); system("uname -av > $targetdir/system_status/uname"); - mkdir("$targetdir/system_status/osrelease"); + File::Path::make_path("$targetdir/system_status/osrelease", { chmod => 0755 }); system("cp -a /etc/*release /etc/*version $targetdir/system_status/osrelease/ 2>/dev/null"); if (!$args->{no_system_stats}) @@ -352,7 +353,7 @@ sub collect_evidence or warn "can't save routing table: $!\n"; # capture the cron files, root's and nmis's tabs - mkdir("$targetdir/system_status/cron"); + File::Path::make_path("$targetdir/system_status/cron", { chmod => 0755 }); system("cp -a /etc/cron* $targetdir/system_status/cron") == 0 or warn "can't save cron files: $!\n"; @@ -364,7 +365,7 @@ sub collect_evidence if ($apachehome) { my $apachetarget = "$targetdir/system_status/apache"; - mkdir ($apachetarget) if (!-d $apachetarget); + File::Path::make_path($apachetarget, { chmod => 0755 }); # on centos/RH there are symlinks pointing to all the apache module binaries, we don't # want these (so -a or --dereference is essential) system("cp -a $apachehome/* $apachetarget"); @@ -380,7 +381,7 @@ sub collect_evidence } # collect all defined log files - mkdir("$targetdir/logs"); + File::Path::make_path("$targetdir/logs", { chmod => 0755 }); my @logfiles = grep(/^.+$/, (map { $globalconf->{$_} } (grep(/_log$/, keys %$globalconf)))); if (!@logfiles) # if the nmis load failed, fall back to the most essential standard logs { @@ -415,9 +416,7 @@ sub collect_evidence or warn "ATTENTION: can't copy logfile $lfn to $targetdir!\n"; } } - mkdir("$targetdir/conf",0755); - mkdir("$targetdir/conf/scripts",0755); - mkdir("$targetdir/conf/nodeconf",0755); + File::Path::make_path("$targetdir/conf/scripts", "$targetdir/conf/nodeconf" , { chmod => 0755 }); # copy all of conf/ and models/ but NOT any stray stuff beneath system("cp","-r","$basedir/models",$targetdir) == 0 @@ -458,43 +457,64 @@ sub collect_evidence } # copy generic var files (=var/nmis-*) - mkdir("$targetdir/var"); - opendir(D,"$vardir") or warn "can't read var dir $vardir: $!\n"; - my @generics = grep(/^nmis[-_]/, readdir(D)); - closedir(D); - system("cp", "-r", (map { "$vardir/$_" } (@generics)), - "$targetdir/var") == 0 or warn "can't copy var files: $!\n"; + File::Path::make_path("$targetdir/var", { chmod => 0755 }); + opendir(D,"$vardir") or warn "can't read var dir $vardir: $!\n"; + my @generics = grep(/^nmis[-_]/, readdir(D)); + closedir(D); + system("cp", "-r", (map { "$vardir/$_" } (@generics)), + "$targetdir/var") == 0 or warn "can't copy var files: $!\n"; - # if node info requested copy those files as well - # special case: want ALL nodes - if ($thisnode eq "*") + # if node info requested copy those files as well + # special case: want ALL nodes + if ($thisnode eq "*") + { + # all node-related files... + system("cp $vardir/*.* $targetdir/var/") == 0 + or warn "can't copy all nodes' files: $!\n"; + #...and their current events + opendir(D, "$vardir/events") or warn "can't read $vardir/events: $!\n"; + for my $onenode (grep(!/^\./,readdir(D))) { - system("cp $vardir/* $targetdir/var/") == 0 - or warn "can't copy all nodes' files: $!\n"; + # no events for this node -> skip + next if (!(my @list = glob("$vardir/events/$onenode/current/*"))); + my $curevdir = "$targetdir/var/events/$onenode/current"; + File::Path::make_path($curevdir, { chmod => 0755 }); + system("cp $vardir/events/$onenode/current/* $curevdir") == 0 + or warn "can't copy $onenode events to $curevdir: $!\n"; } - elsif ($thisnode) + closedir(D); + } + elsif ($thisnode) + { + my $lnt = &loadLocalNodeTable; + for my $nextnode (split(/\s*,\s*/,$thisnode)) { - my $lnt = &loadLocalNodeTable; - for my $nextnode (split(/\s*,\s*/,$thisnode)) + if ($lnt->{$nextnode}) { - if ($lnt->{$nextnode}) - { - my $fileprefix = "$vardir/".lc($nextnode); - my @files_to_copy = (-r "$fileprefix-node.json")? - ("$fileprefix-node.json", "$fileprefix-view.json") : - ("$fileprefix-node.nmis", "$fileprefix-view.nmis"); + my $fileprefix = "$vardir/".lc($nextnode); + my @files_to_copy = (-r "$fileprefix-node.json")? + ("$fileprefix-node.json", "$fileprefix-view.json") : + ("$fileprefix-node.nmis", "$fileprefix-view.nmis"); - system("cp", @files_to_copy, "$targetdir/var/") == 0 - or warn "can't copy node ${nextnode}'s node files: $!\n"; - } - else + system("cp", @files_to_copy, "$targetdir/var/") == 0 + or warn "can't copy node ${nextnode}'s node files: $!\n"; + + my $curevdir = "$targetdir/var/events/$nextnode/current"; + if (my @list = glob("$vardir/events/$nextnode/current/*")) # no events for this node -> skip { - warn("ATTENTION: the requested node \"$nextnode\" isn't known to NMIS!\n"); + File::Path::make_path($curevdir, { chmod => 0755 }); + system("cp $vardir/events/$nextnode/current/* $curevdir") == 0 + or warn "can't copy $nextnode events to $curevdir: $!\n"; } } + else + { + warn("ATTENTION: the requested node \"$nextnode\" isn't known to NMIS!\n"); + } } + } - return undef; + return undef; } # print question, return true if y (or in unattended mode). default is yes. diff --git a/admin/update_config_defaults.pl b/admin/update_config_defaults.pl index f1ca260..2e90c3c 100755 --- a/admin/update_config_defaults.pl +++ b/admin/update_config_defaults.pl @@ -30,7 +30,7 @@ # # ***************************************************************************** use strict; -our $VERSION = "8.6.3G"; +our $VERSION = "8.6.4G"; # Auto configure to the /lib use FindBin; diff --git a/admin/upgrade_models.pl b/admin/upgrade_models.pl index 9ea3639..c086b38 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.3G"; +our $VERSION="8.6.4G"; use strict; use Digest::MD5; # good enough @@ -527,4 +527,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 ba5cdf626ee516c1 bece80b7b44d959b 34592112596682e2 a6443ed36ccd2120 c91082df42a88c17 fc6e00d8485d47c7 85b6e9852b359133 0b8ce0fbc6085bea fc31c4ba46c1f4be b8427208bee2fc4d 11d418a22fc2adfb 3c7c7f1471f80e2c efb216ab07a50fd0 d0c4c790f815e46a af53a22555c57f63 +Model.nmis 8bd4ab3e997c6228 bece80b7b44d959b 34592112596682e2 a6443ed36ccd2120 c91082df42a88c17 fc6e00d8485d47c7 85b6e9852b359133 0b8ce0fbc6085bea fc31c4ba46c1f4be b8427208bee2fc4d 11d418a22fc2adfb 3c7c7f1471f80e2c efb216ab07a50fd0 d0c4c790f815e46a af53a22555c57f63 ba5cdf626ee516c1 diff --git a/admin/upgrade_tables.pl b/admin/upgrade_tables.pl index a1c26ac..05090ad 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.3G"; +our $VERSION="8.6.4G"; use strict; use Digest::MD5; # good enough diff --git a/bin/fpingd.pl b/bin/fpingd.pl index e437d4e..e8e4806 100755 --- a/bin/fpingd.pl +++ b/bin/fpingd.pl @@ -27,7 +27,7 @@ # http://support.opmantek.com/users/ # # ***************************************************************************** -our $VERSION = "8.6.3G"; +our $VERSION = "8.6.4G"; use FindBin qw($Bin); use lib "$FindBin::Bin/../lib"; diff --git a/bin/nmis.pl b/bin/nmis.pl index 5e57f07..4248536 100755 --- a/bin/nmis.pl +++ b/bin/nmis.pl @@ -192,7 +192,7 @@ { runThreads(type=>$type, nodeselect=>$nodeselect, groupselect=>$groupselect, mthread=>$mthread, mthreadDebug=>$mthreadDebug); } -elsif ( $type eq "escalate") { runEscalate(); printRunTime(); } # included in type=collect +elsif ( $type eq "escalate") { runEscalate(independent => 1); printRunTime(); } # included in type=collect elsif ( $type eq "config" ) { checkConfig(change => "true"); } elsif ( $type eq "audit" ) { checkConfig(audit => "true", change => "false"); } elsif ( $type eq "links" ) { runLinks(); } # included in type=update @@ -221,7 +221,10 @@ } elsif ( $type eq "master" ) { nmisMaster(); printRunTime(); } # MIGHT be included in type=collect elsif ( $type eq "groupsync" ) { sync_groups(); } -elsif ( $type eq "purge" ) { my $error = purge_files(); die "$error\n" if $error; } +elsif ( $type eq "purge" ) { + my $error = purge_files(); die "$error\n" if $error; + my $res = NMIS::purge_outages(); die "$res->error\n" if !$res->{success}; +} else { checkArgs(); } exit; @@ -516,29 +519,37 @@ sub expand_candidate_list $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; - # was polling attempted at all and in the last 30 days? then once daily from + # 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 = ($lasttry && ($now - $lasttry) <= 30*86400)? ($lasttry + 86400 * 0.95) : $now; + my $nexttry = !getbool($C->{demote_faulty_nodes},"invert")? # === ne false + ($lasttry && ($now - $lasttry) <= 30*86400)? ($lasttry + 86400 * 0.95) : $now : $lasttry + 300 ; if ($nexttry <= $now) { push @todo_nodes, $maybe; $whichflavours{$maybe}->{wmi} = $whichflavours{$maybe}->{snmp} = 1; } - else + # 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 { - # log this pretty dire situation but not too noisy - with a default poll every minute this - # will log the issue once an hour 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); } + else + { + dbg("Node $maybe has no valid nodeModel, never polled successfully. demote_faulty_nodes is disabled, last attempt $lasttry, next $nexttry."); + } } # logic for collect now or later: candidate if no past successful collect whatsoever, # or if either of the two worked and was done long enough ago. @@ -5622,7 +5633,7 @@ sub runServices # program is disconnected from stdin; stderr goes into a tmpfile # and is collected separately for diagnostics - my $stderrsink = POSIX::tmpnam(); # good enough, no atomic open required + my $stderrsink = File::Temp::mktemp(File::Spec->tmpdir()."/nmis.XXXXXX"); # good enough, no atomic open required dbg("running external program '$thisservice->{Program} $finalargs', " .(getbool($thisservice->{Collect_Output})? "collecting":"ignoring")." output"); $pid = open(PRG,"$thisservice->{Program} $finalargs $stderrsink |"); @@ -7028,17 +7039,29 @@ sub summaryCache ### things, ie if escalate0 = 5 then an interface goes down, no alert sent, next ### poll interface goes up and event cancelled! Downside is a little longer before ### receiving first notification, so it depends on what the support SLA is. - -### 11-Nov-11, keiths, update to this, changed the escalation so that through policy you can -### wait for 5 mins or just notify now, so Ecalation0 is 0 seconds, Escalation1 is 300 seconds -### then in Ecalations.xxxx, core devices might notify at Escalation0 while others at Escalation1 +# args: independent (optional, default 0; if set we ensure that only one runescalate process is active) +# returns: nothing sub runEscalate { my %args = @_; + my $C = loadConfTable(); + + if (getbool($args{independent})) + { + # check that there are no other running/stuck/delayed escalate processes + my $others = func::find_nmis_processes(config => $C, + type => 'escalate'); + if (keys %$others) + { + 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; + } + } + $0 = "nmis-".$C->{conf}."-escalate"; my $pollTimer = NMIS::Timing->new; - my $C = loadConfTable(); my $NT = loadLocalNodeTable(); my $outage_time; diff --git a/cgi-bin/config.pl b/cgi-bin/config.pl index da21f03..baaac92 100755 --- a/cgi-bin/config.pl +++ b/cgi-bin/config.pl @@ -30,7 +30,7 @@ # # ***************************************************************************** use strict; -our $VERSION="8.6.3G"; +our $VERSION="8.6.4G"; use FindBin; use lib "$FindBin::Bin/../lib"; diff --git a/cgi-bin/logs.pl b/cgi-bin/logs.pl index e8baf0e..40db5b8 100755 --- a/cgi-bin/logs.pl +++ b/cgi-bin/logs.pl @@ -91,7 +91,7 @@ $user = $ENV{'REMOTE_USER'} if $ENV{'REMOTE_USER'}; $logoutButton = qq|disabled="disabled"|; } - exit 0 unless $AU->loginout(conf=>$Q->{conf},type=>$Q->{auth_type},username=>$Q->{auth_username}, + exit 0 unless $AU->loginout(type=>$Q->{auth_type},username=>$Q->{auth_username}, password=>$Q->{auth_password},headeropts=>$headeropts) ; $privlevel = $AU->{privlevel}; } else { diff --git a/cgi-bin/models.pl b/cgi-bin/models.pl index ba06a03..c3f22b2 100755 --- a/cgi-bin/models.pl +++ b/cgi-bin/models.pl @@ -28,7 +28,7 @@ # # ***************************************************************************** use strict; -our $VERSION = "8.6.3G"; +our $VERSION = "8.6.4G"; use FindBin; use lib "$FindBin::Bin/../lib"; diff --git a/cgi-bin/node.pl b/cgi-bin/node.pl index 07ca4ff..c1b3908 100755 --- a/cgi-bin/node.pl +++ b/cgi-bin/node.pl @@ -84,8 +84,8 @@ # select function if ($Q->{act} eq 'network_graph_view') { typeGraph(); -} elsif ($Q->{act} eq 'network_export') { typeExport(); # unreachable dead code as of 2016-08 -} elsif ($Q->{act} eq 'network_stats') { typeStats(); # unreachable dead code as of 2016-08 +} elsif ($Q->{act} eq 'network_export') { typeExport(); +} elsif ($Q->{act} eq 'network_stats') { typeStats(); } else { notfound(); } sub notfound { @@ -624,7 +624,6 @@ sub typeGraph { } # end typeGraph -# unreachable dead code as of 2016-08 sub typeExport { my $S = Sys::->new; # get system object @@ -685,7 +684,6 @@ sub typeExport { } } -# unreachable dead code as of 2016-08 sub typeStats { my $S = Sys::->new; # get system object diff --git a/cgi-bin/nodeconf.pl b/cgi-bin/nodeconf.pl index 1b713c3..2d35b00 100755 --- a/cgi-bin/nodeconf.pl +++ b/cgi-bin/nodeconf.pl @@ -46,10 +46,8 @@ #====================================================================== -# if somehow someone defines refresh disable it. -if ( defined $Q->{refresh} ) { - delete $Q->{refresh}; -} +# nodeconf should not refresh, ever. +$Q->{refresh} = undef; my $widget = getbool($Q->{widget},"invert") ? 'false' : 'true'; $Q->{expand} = "true" if ($widget eq "true"); @@ -120,7 +118,7 @@ sub displayNodemenu print header($headeropts); if (!$wantwidget) { - pageStart(title => $Q->{node}." Node Configuration", refresh => $Q->{refresh}); + pageStart(title => $Q->{node}." Node Configuration"); } my $menuformid = "${formid}_menu"; diff --git a/cgi-bin/tables.pl b/cgi-bin/tables.pl index 6678bac..f539a34 100755 --- a/cgi-bin/tables.pl +++ b/cgi-bin/tables.pl @@ -28,7 +28,7 @@ # # ***************************************************************************** use strict; -our $VERSION="8.6.3G"; +our $VERSION="8.6.4G"; use FindBin; use lib "$FindBin::Bin/../lib"; diff --git a/cgi-bin/view-event.pl b/cgi-bin/view-event.pl index 0ba4678..5be6024 100755 --- a/cgi-bin/view-event.pl +++ b/cgi-bin/view-event.pl @@ -390,7 +390,7 @@ sub displayEventItems { printRow(1,"user",$thisevent->{user}); printRow(1,"notify up",$ntf_str); - my ($outage,undef) = outageCheck($thisevent->{node},time()); + my ($outage,undef) = outageCheck(node => $thisevent->{node}, time => time()); if ( $outage eq "current" and getbool($thisevent->{ack},"invert") ) { # check outage diff --git a/install.pl b/install.pl index 79f12be..2ffa9d3 100755 --- a/install.pl +++ b/install.pl @@ -273,7 +273,7 @@ perl-Excel-Writer-XLSX perl-Net-IP perl-DateTime perl-Digest-HMAC perl-Crypt-DES perl-Clone perl-ExtUtils-CBuilder perl-ExtUtils-ParseXS perl-ExtUtils-MakeMaker perl-Test-Fatal perl-Test-Number-Delta -perl-Test-Requires )); +perl-Test-Requires perl-JSON perl-XML-SAX perl-XML-SAX-Writer perl-Convert-ASN1)); # perl-Time-modules no longer a/v in rh/centos7 push @rhpackages, ($osflavour eq "redhat" && $osmajor < 7)? @@ -352,7 +352,7 @@ package installation without Internet access in that case: and $osflavour eq "ubuntu" and version->parse($osversion) < version->parse("15.10")); - if (`dpkg -l $pkg 2>/dev/null` =~ /^ii\s*$pkg\s*/m) + if (`dpkg -l $pkg 2>/dev/null` =~ /^[hi]i\s*$pkg\s*/m) { echolog("Required package $pkg is already installed."); } @@ -598,7 +598,12 @@ package installation without Internet access in that case: # of cpan seem to start it automatically print "\n If the CPAN configuration doesn't start automatically, then please -enter 'o conf init' on the CPAN prompt. To return to the installer when done, +enter 'o conf init' on the CPAN prompt. + +Should you get prompted to choose Perl Library directories, 'local::lib' +or the like, please choose 'sudo' or 'manual' - NOT 'local::lib'! + +To return to the installer when done, please exit the CPAN\nshell with 'exit'.\n"; &input_ok; system("cpan"); @@ -874,6 +879,19 @@ package installation without Internet access in that case: 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"); echolog("\n"); + echolog("By default this version NMIS demotes nodes that have never +been collected successfully to a single collection attempt once every 24 hours. + +If you choose Y below, then the installer will change the configuration +setting demote_faulty_nodes to false, and NMIS will try to collect such nodes +every 5 minutes."); + + if (input_yn("Should NMIS retry totally uncollectable nodes every 5 min?")) + { + execPrint("$site/admin/patch_config.pl $site/conf/Config.nmis /system/demote_faulty_nodes=false"); + echolog("\n"); + } + if (input_yn("OK to remove syslog and JSON logging from default event escalation?")) { execPrint("$site/admin/patch_config.pl -b $site/conf/Escalations.nmis /default_default_default_default__/Level0=''"); @@ -1559,7 +1577,7 @@ sub check_installed_modules # has a much too old perl. many of these modules are in core since 5.19 or thereabouts $nmisModules->{"IO::Socket::IP"} = { file => "MODULE NOT FOUND", type => "use", - by => "lib/Auth.pm", priority => 99 }; + by => "lib/Auth.pm", minversion => "0.37", priority => 99 }; # io socket ip needs at least this version of socket... $nmisModules->{"Socket"} = { file => "MODULE NOT FOUND", type => "use", by => "lib/Auth.pm", minversion => "1.97", @@ -1647,9 +1665,9 @@ sub moduleVersion sub listModules { my (@missing, @critmissing); - my %noncritical = ("Net::LDAP"=>1, "Net::LDAPS"=>1, "IO::Socket::SSL"=>1, - "Crypt::UnixCrypt"=>1, "Authen::TacacsPlus"=>1, "Authen::Simple::RADIUS"=>1, - "SNMP_util"=>1, "SNMP_Session"=>1, "SOAP::Lite" => 1, "UI::Dialog" => 1, ); + my %noncritical = ( "Authen::TacacsPlus"=>1, "Authen::Simple::RADIUS"=>1, + "SNMP_util"=>1, "SNMP_Session"=>1, + "SOAP::Lite" => 1, "UI::Dialog" => 1, ); logInstall("Module status follows:\nName - Path - Current Version - Minimum Version\n"); # sort by install prio, or file @@ -1673,16 +1691,12 @@ sub listModules { printBanner("Some Optional Perl Modules are missing (or too old)"); print qq|The following optional modules are missing or too old:\n| .join(" ", @optionals) - .qq|\n\nNote: The modules Net::LDAP, Net::LDAPS, IO::Socket::SSL, Crypt::UnixCrypt, -Authen::TacacsPlus, Authen::Simple::RADIUS are optional components for the -NMIS AAA system. + .qq|\n\nNote: The modules Authen::TacacsPlus and Authen::Simple::RADIUS +are optional components for the NMIS AAA system. The modules SNMP_util and SNMP_Session are also optional (needed only for the ipsla subsystem) and can be installed either with -'yum install perl-SNMP_Session' or 'apt-get install libsnmp-session-perl'. - -The modules Digest::HMAC and Crypt::DES are required if any of your -devices use SNMP Version 3.\n\n|; +'yum install perl-SNMP_Session' or 'apt-get install libsnmp-session-perl'.\n\n|; } if (@critmissing) diff --git a/install/Config.nmis b/install/Config.nmis index b2c897e..1f4af5f 100644 --- a/install/Config.nmis +++ b/install/Config.nmis @@ -71,7 +71,7 @@ my %hash = ( "roletype_list" => "core,distribution,access,default", "nettype_list" => "wan,lan,vpn,man,san,voice,default", "nodetype_list" => "generic,switch,router,firewall,server", - + # new configuration option to have a configurable field to use for location, e.g. sysLocation or location. 'location_field_name' => 'sysLocation', @@ -90,6 +90,7 @@ my %hash = ( 'node_status_uses_status_summary' => 'false', 'display_status_summary' => 'false', + 'demote_faulty_nodes' => 'true', # try totally unpollable nodes just once a day? # to have additional fields in the node summary data. 'node_summary_field_list' => 'host,uuid,customer,businessService,serviceStatus,snmpdown,wmidown', @@ -301,7 +302,7 @@ my %hash = ( 'daemon_ipsla_filename' => 'ipslad.pl', 'daemon_fping_active' => 'true', 'daemon_fping_dns_cache' => 'true', - 'daemon_fping_run_escalation' => 'true', + 'daemon_fping_run_escalation' => 'true', 'daemon_fping_filename' => 'fpingd.pl' }, @@ -506,5 +507,6 @@ my %hash = ( 'purge_event_after' => 30*86400, # historic, closed events 'purge_jsonlog_after' => 30*86400, # json log files 'purge_reports_after' => 365*86400, # html reports + 'purge_outages_after' => 86400, # past non-recurring outages }, ); diff --git a/install/Modules.nmis b/install/Modules.nmis index 16d3d79..e41d283 100644 --- a/install/Modules.nmis +++ b/install/Modules.nmis @@ -27,7 +27,7 @@ 'file' => '/install/opconfigd.init.d', 'link' => '/omk/opConfig', 'description' => 'opConfig is an easy-to-use, web based network configuration management tool. opConfig allows organisations to simply manage configuration files in multi-vendor environments assisting in disaster recovery, troubleshooting and change management.', - 'tagline' => 'Configuration backup, archving and change detection.', + 'tagline' => 'Configuration backup, archiving and change detection.', }, 'opFlow 3' => { diff --git a/lib/Auth.pm b/lib/Auth.pm index b528951..30b8cf8 100644 --- a/lib/Auth.pm +++ b/lib/Auth.pm @@ -47,7 +47,7 @@ # incorporated in NMIS and more generally into other web programs needing # user authentication. package Auth; -our $VERSION = "2.0.0"; +our $VERSION = "2.1.0"; use strict; use vars qw(@ISA @EXPORT); @@ -1274,27 +1274,21 @@ sub _tacacs_verify { } ##################################################################### -# -# 5-03-07, Jan v. K. -# # check login - logout - go - +# args: type, username, password, headeropts, listmodules sub loginout { - my $self = shift; - my %args = @_; + my ($self, %args) = @_; + my $type = lc($args{type}); my $username = $args{username}; my $password = $args{password}; - # that's the NAME not the config data - my $config = $args{conf} || $self->{confname}; - my $listmodules = $args{listmodules}; my $headeropts = $args{headeropts}; my @cookies = (); - logAuth("DEBUG: loginout type=$type username=$username config=$config") + logAuth("DEBUG: loginout type=$type username=$username") if $self->{debug}; #2011-11-14 Integrating changes from Till Dierkesmann @@ -1331,7 +1325,7 @@ sub loginout { if( $self->user_verify($username,$password)) { #logAuth("DEBUG: user verified $username") if $self->{debug}; - #logAuth("self.privilevel=$self->{privilevel} self.config=$self->{config} config=$config") if $self->{debug}; + #logAuth("self.privilevel=$self->{privilevel} self.config=$self->{config}") if $self->{debug}; # login accepted, set privs $self->SetUser($username); @@ -1346,16 +1340,8 @@ sub loginout { return 0; } - # check the name of the NMIS config file specified on url - # only bypass for administrator - if ($self->{privlevel} gt 1 and $self->{config} ne '' and $config ne $self->{config}) { - $self->do_login(msg=>"Invalid config file specified on url", - listmodules => $listmodules); - return 0; - } - - logAuth("user=$self->{user} logged in with config=$config"); - logAuth("DEBUG: loginout user=$self->{user} logged in with config=$config") if $self->{debug}; + logAuth("user=$self->{user} logged in"); + logAuth("DEBUG: loginout user=$self->{user} logged in") if $self->{debug}; } else { # bad login: try again, up to N times diff --git a/lib/NMIS.pm b/lib/NMIS.pm index 649719f..e0d9f28 100644 --- a/lib/NMIS.pm +++ b/lib/NMIS.pm @@ -27,7 +27,7 @@ # # ***************************************************************************** package NMIS; -our $VERSION = "8.6.3G"; +our $VERSION = "8.6.4G"; use NMIS::uselib; use lib "$NMIS::uselib::rrdtool_lib"; @@ -2018,7 +2018,7 @@ sub update_outage func::audit_log(who => $meta->{user}, what => ($op_create? "create_outage" : "update_outage"), - where => $outid, how => "ok", defails => $meta->{details}, when => undef); + where => $outid, how => "ok", details => $meta->{details}, when => undef); return { success => 1, id => $outid}; } @@ -2069,6 +2069,32 @@ sub upgrade_outages return undef; } +# removes past none-recurring outages after a configurable time +# returns: hashref, keys success/error +sub purge_outages +{ + my $C = loadConfTable(); # likely cached + + my $maxage = $C->{purge_outages_after} // 86400; + return { success => 1, message => "Outage expiration is disabled." } if ($maxage <= 0); # 0 or negative? no purging + + my $data = loadTable(dir => "conf", name => "Outages") + if (existFile(dir => "conf", name => "Outages")); # or we get lots of log noise + return { success => 1, message => "No outages exist." } if !$data; + + my @problems; + for my $outid (keys %$data) + { + my $thisoutage = $data->{$outid}; + next if ($thisoutage->{frequency} ne "once" + or $thisoutage->{end} >= time - $maxage); + + my $res = remove_outage(id => $outid, meta => {details => "purging expired past outage" }); + push @problems, "$outid: $res->{error}" if (!$res->{success}); # but let's continue + } + + return (@problems? { error => join("\n", @problems) } : { success => 1 }); +} # take a relative/incomplete time and day specification and make into absolute timestamp @@ -2182,7 +2208,7 @@ sub _prev_next_interval # remove existing outage # args: id, optional meta (for audit logging, keys user, details) -# returns: hashrev, keys success/error +# returns: hashref, keys success/error sub remove_outage { my (%args) = @_; @@ -2208,7 +2234,7 @@ sub remove_outage what => "remove_outage", where => $id, how => "ok", - defails => $meta->{details}, + details => $meta->{details}, when => undef); return { success => 1}; @@ -3022,7 +3048,6 @@ sub pageStart { my $jscript = $args{jscript}; $jscript = getJavaScript() if ($jscript eq ""); $title = "NMIS by Opmantek" if ($title eq ""); - $refresh = 300 if ($refresh eq ""); my $C = loadConfTable(); @@ -3030,9 +3055,11 @@ sub pageStart { | - $title - - + $title|, + (defined($refresh) && $refresh > 0 ? + qq|\n| : ""), + + qq| @@ -3059,7 +3086,6 @@ sub pageStartJscript { my $title = $args{title}; my $refresh = $args{refresh}; $title = "NMIS by Opmantek" if ($title eq ""); - $refresh = 86400 if ($refresh eq ""); my $C = loadConfTable(); @@ -3067,9 +3093,11 @@ sub pageStartJscript { | - $title - - + $title|, + (defined($refresh) && $refresh > 0? + qq|\n| : "" ), + + qq| diff --git a/lib/Sys.pm b/lib/Sys.pm index ef8a38c..b1bd2eb 100644 --- a/lib/Sys.pm +++ b/lib/Sys.pm @@ -820,15 +820,6 @@ sub getValues } } - # check if we should just skip any collect and leave this to a plugin to collect - # we need to have an rrd section so we can define the graphtypes. - if ($thissection->{skip_collect} and getbool($thissection->{skip_collect})) - { - dbg("skip_collect $thissection->{skip_collect} found for section=$sectionname",2); - $status{skipped} = "skipped $sectionname because skip_collect set to true"; - next; - } - # should we add graphtype to given (info) table? if (ref($tbl) eq "HASH") { @@ -855,6 +846,15 @@ sub getValues } } + # check if we should just skip any collect and leave this to a plugin to collect + # we need to have an rrd section so we can define the graphtypes. + if ($thissection->{skip_collect} and getbool($thissection->{skip_collect})) + { + dbg("skip_collect $thissection->{skip_collect} found for section=$sectionname",2); + $status{skipped} = "skipped $sectionname because skip_collect set to true"; + next; + } + # prep the list of things to tackle, snmp first - iff snmp is ok for this node if (ref($thissection->{snmp}) eq "HASH" && $self->{snmp}) { diff --git a/lib/WMI.pm b/lib/WMI.pm index 377eb63..a2bf9d3 100644 --- a/lib/WMI.pm +++ b/lib/WMI.pm @@ -29,7 +29,7 @@ # # this module queries WMI services via the standalone wmic executable package WMI; -our $VERSION = "1.0.0"; +our $VERSION = "1.0.1"; use strict; use File::Temp; @@ -231,9 +231,11 @@ sub _run_query { $result{error} .= " exit code ".($exitcode>>8); } + unlink($tfn); # not needed anymore } else { + unlink($tfn); # not needed here # worked? extract class, fieldnames # produce hash for each class, array of subhashes for the rows my ($classname, @fieldnames, %nicedata); @@ -277,7 +279,6 @@ sub _run_query $result{ok} = 1; $result{data} = \%nicedata; } - unlink $tfn; return %result; } diff --git a/models-install/Model.nmis b/models-install/Model.nmis index 990d936..ca980f2 100644 --- a/models-install/Model.nmis +++ b/models-install/Model.nmis @@ -1,7 +1,5 @@ # -## $Id: Model.nmis,v 8.13 2012/12/03 07:47:26 keiths Exp $ -# -# Copyright 1999-2011 Opmantek Limited (www.opmantek.com) +# Copyright 1999-2018 Opmantek Limited (www.opmantek.com) # # ALL CODE MODIFICATIONS MUST BE SENT TO CODE@OPMANTEK.COM # @@ -611,7 +609,7 @@ 'system' => { 'nodeModel' => 'Default', 'nodeType' => 'generic', - + 'nodegraph' => 'health,response', 'sys' => { # contents of this section are required for automatic model determination 'standard' => @@ -677,7 +675,47 @@ 'heading' => { 'graphtype' => { 'nmis' => 'NMIS collect runtime', - 'metrics' => 'Metrics' + 'metrics' => 'Metrics', + 'health' => 'Overall Reachability, Availability and Health', + 'response' => 'Response Time in milliseconds', + } + }, + + 'stats' => { + 'type' => { + 'health' => [ + 'DEF:reach=$database:reachability:AVERAGE', + 'DEF:avail=$database:availability:AVERAGE', + 'DEF:health=$database:health:AVERAGE', + 'DEF:response=$database:responsetime:AVERAGE', + 'DEF:loss=$database:loss:AVERAGE', + 'DEF:intfCollect=$database:intfCollect:AVERAGE', + 'DEF:intfColUp=$database:intfColUp:AVERAGE', + 'DEF:reachabilityHealth=$database:reachabilityHealth:AVERAGE', + 'DEF:availabilityHealth=$database:availabilityHealth:AVERAGE', + 'DEF:responseHealth=$database:responseHealth:AVERAGE', + 'DEF:cpuHealth=$database:cpuHealth:AVERAGE', + 'DEF:memHealth=$database:memHealth:AVERAGE', + 'DEF:intHealth=$database:intHealth:AVERAGE', + 'DEF:diskHealth=$database:diskHealth:AVERAGE', + 'DEF:swapHealth=$database:swapHealth:AVERAGE', + 'PRINT:intfCollect:AVERAGE:intfCollect=%1.3lf', + 'PRINT:intfColUp:AVERAGE:intfColUp=%1.3lf', + 'PRINT:reach:AVERAGE:reachable=%1.3lf', + 'PRINT:avail:AVERAGE:available=%1.3lf', + 'PRINT:health:AVERAGE:health=%1.3lf', + 'PRINT:response:AVERAGE:response=%1.2lf', + 'PRINT:loss:AVERAGE:loss=%1.2lf', + 'PRINT:reachabilityHealth:AVERAGE:reachabilityHealth=%1.2lf', + 'PRINT:availabilityHealth:AVERAGE:availabilityHealth=%1.2lf', + 'PRINT:responseHealth:AVERAGE:responseHealth=%1.2lf', + 'PRINT:cpuHealth:AVERAGE:cpuHealth=%1.2lf', + 'PRINT:memHealth:AVERAGE:memHealth=%1.2lf', + 'PRINT:intHealth:AVERAGE:intHealth=%1.2lf', + 'PRINT:diskHealth:AVERAGE:diskHealth=%1.2lf', + 'PRINT:swapHealth:AVERAGE:swapHealth=%1.2lf' + ], } } + );