check_esx3.pl: Added timeshift argument, which could fix 'UNKNOWN error' issues for...
[nagios/op5plugins.git] / check_esx3.pl
1 #!/usr/bin/perl -w
2 #
3 # Nagios plugin to monitor vmware esx servers
4 #
5 # License: GPL
6 # Copyright (c) 2008 op5 AB
7 # Author: Kostyantyn Hushchyn <>
8 # Contributor(s): Patrick Müller, Jeremy Martin, Eric Jonsson, stumpr, John Cavanaugh, Libor Klepac, maikmayers, Steffen Poulsen, Mark Elliott
9 #
10 # For direct contact with any of the op5 developers send a mail to
11
12 # Discussions are directed to the mailing list ,
13 # see http://lists.op5.com/mailman/listinfo/op5-users
14 #
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License version 2 as
17 # published by the Free Software Foundation.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program.  If not, see .
26 #
27
28 use strict;
29 use warnings;
30 use vars qw($PROGNAME $VERSION $output $values $result);
31 use Nagios::Plugin;
32 use File::Basename;
33 my $perl_module_instructions="
34 Download the latest version of Perl Toolkit from VMware support page. 
35 In this example we use VMware-vSphere-SDK-for-Perl-4.0.0-161974.x86_64.tar.gz,
36 but the instructions should apply to newer versions as well.
37   
38 Upload the file to your op5 Monitor server's /root dir and execute:
39
40     cd /root
41     tar xvzf VMware-vSphere-SDK-for-Perl-4.0.0-161974.x86_64.tar.gz
42     cd vmware-vsphere-cli-distrib/
43     ./vmware-install.pl
44   
45 Follow the on screen instructions, described below:
46
47   \"Creating a new vSphere CLI installer database using the tar4 format.
48
49   Installing vSphere CLI.
50
51   Installing version 161974 of vSphere CLI
52
53   You must read and accept the vSphere CLI End User License Agreement to
54   continue.
55   Press enter to display it.\" 
56   
57     
58
59   \"Read through the License Agreement\" 
60   \"Do you accept? (yes/no) 
61   
62     yes
63
64
65   \"The following Perl modules were found on the system but may be too old to work
66   with VIPerl:
67   
68   Crypt::SSLeay
69   Compress::Zlib\"
70   
71   \"In which directory do you want to install the executable files? [/usr/bin]\"
72
73     
74
75   \"Please wait while copying vSphere CLI files...
76
77   The installation of vSphere CLI 4.0.0 build-161974 for Linux completed
78   successfully. You can decide to remove this software from your system at any
79   time by invoking the following command:
80   \"/usr/bin/vmware-uninstall-vSphere-CLI.pl\".
81   
82   This installer has successfully installed both vSphere CLI and the vSphere SDK
83   for Perl.
84   Enjoy,
85   
86   --the VMware team\"
87
88 Note: \"Crypt::SSLeay\" and \"Compress::Zlib\" are not needed for check_esx3 to work.  
89 ";
90
91
92 eval { 
93         require VMware::VIRuntime
94 } or Nagios::Plugin::Functions::nagios_exit(UNKNOWN, "Missing perl module VMware::VIRuntime. Download and install \'VMware Infrastructure (VI) Perl Toolkit\', available at http://www.vmware.com/download/sdk/\n $perl_module_instructions");
95
96 $PROGNAME = basename($0);
97 $VERSION = '0.5.0';
98
99 my $np = Nagios::Plugin->new(
100   usage => "Usage: %s -D  | -H  [ -N  ]\n"
101     . "    -u  -p  | -f \n"
102     . "    -l  [ -s  ]\n"
103     . "    [ -x  ] [ -o  ]\n"
104     . "    [ -t  ] [ -w  ] [ -c  ]\n"
105     . '    [ -V ] [ -h ]',
106   version => $VERSION,
107   plugin  => $PROGNAME,
108   shortname => uc($PROGNAME),
109   blurb => 'VMWare Infrastructure plugin',
110   extra   => "Supported commands(^ means blank or not specified parameter) :\n"
111     . "    Common options for VM, Host and DC :\n"
112     . "        * cpu - shows cpu info\n"
113     . "            + usage - CPU usage in percentage\n"
114     . "            + usagemhz - CPU usage in MHz\n"
115     . "            ^ all cpu info\n"
116     . "        * mem - shows mem info\n"
117     . "            + usage - mem usage in percentage\n"
118     . "            + usagemb - mem usage in MB\n"
119     . "            + swap - swap mem usage in MB\n"
120     . "            + overhead - additional mem used by VM Server in MB\n"
121     . "            + overall - overall mem used by VM Server in MB\n"
122     . "            + memctl - mem used by VM memory control driver(vmmemctl) that controls ballooning\n"
123     . "            ^ all mem info\n"
124     . "        * net - shows net info\n"
125     . "            + usage - overall network usage in KBps(Kilobytes per Second) \n"
126     . "            + receive - receive in KBps(Kilobytes per Second) \n"
127     . "            + send - send in KBps(Kilobytes per Second) \n"
128     . "            ^ all net info\n"
129     . "        * io - shows disk io info\n"
130     . "            + read - read latency in ms (totalReadLatency.average)\n"
131     . "            + write - write latency in ms (totalWriteLatency.average)\n"
132     . "            ^ all disk io info\n"
133     . "        * runtime - shows runtime info\n"
134     . "            + status - overall host status (gray/green/red/yellow)\n"
135     . "            + issues - all issues for the host\n"
136     . "            ^ all runtime info\n"
137     . "    VM specific :\n"
138     . "        * cpu - shows cpu info\n"
139     . "            + wait - CPU wait time in ms\n"
140     . "            + ready - CPU ready time in ms\n"
141     . "        * mem - shows mem info\n"
142     . "            + swapin - swapin mem usage in MB\n"
143     . "            + swapout - swapout mem usage in MB\n"
144     . "            + active - active mem usage in MB\n"
145     . "        * io - shows disk I/O info\n"
146     . "            + usage - overall disk usage in MB/s\n"
147     . "        * runtime - shows runtime info\n"
148     . "            + con - connection state\n"
149     . "            + cpu - allocated CPU in MHz\n"
150     . "            + mem - allocated mem in MB\n"
151     . "            + state - virtual machine state (UP, DOWN, SUSPENDED)\n"
152     . "            + consoleconnections - console connections to VM\n"
153     . "            + guest - guest OS status, needs VMware Tools\n"
154     . "            + tools - VMWare Tools status\n"
155     . "    Host specific :\n"
156     . "        * net - shows net info\n"
157     . "            + nic - makes sure all active NICs are plugged in\n"
158     . "        * io - shows disk io info\n"
159     . "            + aborted - aborted commands count\n"
160     . "            + resets - bus resets count\n"
161     . "            + kernel - kernel latency in ms\n"
162     . "            + device - device latency in ms\n"
163     . "            + queue - queue latency in ms\n"
164     . "        * vmfs - shows Datastore info\n"
165     . "            + (name) - free space info for datastore with name (name)\n"
166     . "            ^ all datastore info\n"
167     . "        * runtime - shows runtime info\n"
168     . "            + con - connection state\n"
169     . "            + health - checks cpu/storage/memory/sensor status\n"
170     . "            + maintenance - shows whether host is in maintenance mode\n"
171     . "            + list(vm) - list of VMWare machines and their statuses\n"
172     . "        * service - shows Host service info\n"
173     . "            + (names) - check the state of one or several services specified by (names), syntax for (names):,,...,\n"
174     . "            ^ show all services\n"
175     . "        * storage - shows Host storage info\n"
176     . "            + adapter - list bus adapters\n"
177     . "            + lun - list SCSI logical units\n"
178     . "            + path - list logical unit paths\n"
179     . "    DC specific :\n"
180     . "        * io - shows disk io info\n"
181     . "            + aborted - aborted commands count\n"
182     . "            + resets - bus resets count\n"
183     . "            + kernel - kernel latency in ms\n"
184     . "            + device - device latency in ms\n"
185     . "            + queue - queue latency in ms\n"
186     . "        * vmfs - shows Datastore info\n"
187     . "            + (name) - free space info for datastore with name (name)\n"
188     . "            ^ all datastore info\n"
189     . "        * runtime - shows runtime info\n"
190     . "            + list(vm) - list of VMWare machines and their statuses\n"
191     . "            + listhost - list of VMWare esx host servers and their statuses\n"
192     . "            + tools - VMWare Tools status\n"
193     . "        * recommendations - shows recommendations for cluster\n"
194     . "            + (name) - recommendations for cluster with name (name)\n"
195     . "            ^ all clusters recommendations\n"
196     . "\n\nCopyright (c) 2008 op5",
197   timeout => 30,
198 );
199
200 $np->add_arg(
201   spec => 'host|H=s',
202   help => "-H, --host=\n"
203     . '   ESX or ESXi hostname.',
204   required => 0,
205 );
206
207 $np->add_arg(
208   spec => 'cluster|C=s',
209   help => "-C, --cluster=\n"
210     . '   ESX or ESXi clustername.',
211   required => 0,
212 );
213
214 $np->add_arg(
215   spec => 'datacenter|D=s',
216   help => "-D, --datacenter=\n"
217     . '   Datacenter hostname.',
218   required => 0,
219 );
220
221 $np->add_arg(
222   spec => 'name|N=s',
223   help => "-N, --name=\n"
224     . '   Virtual machine name.',
225   required => 0,
226 );
227
228 $np->add_arg(
229   spec => 'username|u=s',
230   help => "-u, --username=\n"
231     . '   Username to connect with.',
232   required => 0,
233 );
234
235 $np->add_arg(
236   spec => 'password|p=s',
237   help => "-p, --password=\n"
238     . '   Password to use with the username.',
239   required => 0,
240 );
241
242 $np->add_arg(
243   spec => 'authfile|f=s',
244   help => "-f, --authfile=\n"
245     . "   Authentication file with login and password. File syntax :\n"
246     . "   username=\n"
247     . '   password=',
248   required => 0,
249 );
250
251 $np->add_arg(
252   spec => 'warning|w=s',
253   help => "-w, --warning=THRESHOLD\n"
254     . "   Warning threshold. See\n"
255     . "   http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT\n"
256     . '   for the threshold format.',
257   required => 0,
258 );
259
260 $np->add_arg(
261   spec => 'critical|c=s',
262   help => "-c, --critical=THRESHOLD\n"
263     . "   Critical threshold. See\n"
264     . "   http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT\n"
265     . '   for the threshold format.',
266   required => 0,
267 );
268
269 $np->add_arg(
270   spec => 'command|l=s',
271   help => "-l, --command=COMMAND\n"
272     . '   Specify command type (CPU, MEM, NET, IO, VMFS, RUNTIME, ...)',
273   required => 1,
274 );
275
276 $np->add_arg(
277   spec => 'subcommand|s=s',
278   help => "-s, --subcommand=SUBCOMMAND\n"
279     . '   Specify subcommand',
280   required => 0,
281 );
282
283 $np->add_arg(
284   spec => 'sessionfile|S=s',
285   help => "-S, --sessionfile=SESSIONFILE\n"
286     . '   Specify a filename to store sessions for faster authentication',
287   required => 0,
288 );
289
290 $np->add_arg(
291   spec => 'exclude|x=s',
292   help => "-x, --exclude=\n"
293     . '   Specify black list',
294   required => 0,
295 );
296
297 $np->add_arg(
298   spec => 'options|o=s',
299   help => "-o, --options= \n"
300     . '   Specify additional command options',
301   required => 0,
302 );
303
304 $np->getopts;
305
306 my $host = $np->opts->host;
307 my $cluster = $np->opts->cluster;
308 my $datacenter = $np->opts->datacenter;
309 my $vmname = $np->opts->name;
310 my $username = $np->opts->username;
311 my $password = $np->opts->password;
312 my $authfile = $np->opts->authfile;
313 my $warning = $np->opts->warning;
314 my $critical = $np->opts->critical;
315 my $command = $np->opts->command;
316 my $subcommand = $np->opts->subcommand;
317 my $sessionfile = $np->opts->sessionfile;
318 my $blacklist = $np->opts->exclude;
319 my $addopts = $np->opts->options;
320 my $percw;
321 my $percc;
322 $output = "Unknown ERROR!";
323 $result = CRITICAL;
324
325 if (defined($subcommand))
326 {
327         $subcommand = undef if ($subcommand eq '');
328 }
329
330 if (defined($critical))
331 {
332         ($percc, $critical) = check_percantage($critical);
333         $critical = undef if ($critical eq '');
334 }
335
336 if (defined($warning))
337 {
338         ($percw, $warning) = check_percantage($warning);
339         $warning = undef if ($warning eq '');
340 }
341
342 $np->set_thresholds(critical => $critical, warning => $warning);
343
344 eval
345 {
346         die "Provide either Password/Username or Auth file or Session file\n" if ((!defined($password) || !defined($username) || defined($authfile)) && (defined($password) || defined($username) || !defined($authfile)) && (defined($password) || defined($username) || defined($authfile) || !defined($sessionfile)));
347         die "Both threshold values must be the same units\n" if (($percw && !$percc && defined($critical)) || (!$percw && $percc && defined($warning)));
348         if (defined($authfile))
349         {
350                 open (AUTH_FILE, $authfile) || die "Unable to open auth file \"$authfile\"\n";
351                 while(  ) {
352                         if(s/^[ \t]*username[ \t]*=//){
353                                 s/^\s+//;s/\s+$//;
354                                 $username = $_;
355                         }
356                         if(s/^[ \t]*password[ \t]*=//){
357                                 s/^\s+//;s/\s+$//;
358                                 $password = $_;
359                         }
360                 }
361                 die "Auth file must contain both username and password\n" if (!(defined($username) && defined($password)));
362         }
363
364         my $host_address;
365
366         if (defined($datacenter))
367         {
368                 $host_address = $datacenter;
369         }
370         elsif (defined($host))
371         {
372                 $host_address = $host;
373         }
374         else
375         {
376                 $np->nagios_exit(CRITICAL, "No Host or Datacenter specified");
377         }
378
379         $host_address .= ":443" if (index($host_address, ":") == -1);
380         $host_address = "https://" . $host_address . "/sdk/webService";
381
382         if (defined($sessionfile) and -e $sessionfile)
383         {
384                 Opts::set_option("sessionfile", $sessionfile);
385                 eval {
386                         Util::connect($host_address, $username, $password);
387                         die "Connected host doesn't match reqested once\n" if (Opts::get_option("url") ne $host_address);
388                 };
389                 if ($@) {
390                         Opts::set_option("sessionfile", undef);
391                         Util::connect($host_address, $username, $password);
392                 }
393         }
394         else
395         {
396                 Util::connect($host_address, $username, $password);
397         }
398
399         if (defined($sessionfile))
400         {
401                 Vim::save_session(session_file => $sessionfile);
402         }
403         $command = uc($command);
404         if (defined($vmname))
405         {
406                 if ($command eq "CPU")
407                 {
408                         ($result, $output) = vm_cpu_info($vmname, $np, local_uc($subcommand));
409                 }
410                 elsif ($command eq "MEM")
411                 {
412                         ($result, $output) = vm_mem_info($vmname, $np, local_uc($subcommand));
413                 }
414                 elsif ($command eq "NET")
415                 {
416                         ($result, $output) = vm_net_info($vmname, $np, local_uc($subcommand));
417                 }
418                 elsif ($command eq "IO")
419                 {
420                         ($result, $output) = vm_disk_io_info($vmname, $np, local_uc($subcommand));
421                 }
422                 elsif ($command eq "RUNTIME")
423                 {
424                         ($result, $output) = vm_runtime_info($vmname, $np, local_uc($subcommand));
425                 }
426                 else
427                 {
428                         $output = "Unknown HOST-VM command\n" . $np->opts->_help;
429                         $result = CRITICAL;
430                 }
431         }
432         elsif (defined($host))
433         {
434                 my $esx;
435                 $esx = {name => $host} if (defined($datacenter));
436                 if ($command eq "CPU")
437                 {
438                         ($result, $output) = host_cpu_info($esx, $np, local_uc($subcommand), $addopts);
439                 }
440                 elsif ($command eq "MEM")
441                 {
442                         ($result, $output) = host_mem_info($esx, $np, local_uc($subcommand), $addopts);
443                 }
444                 elsif ($command eq "NET")
445                 {
446                         ($result, $output) = host_net_info($esx, $np, local_uc($subcommand));
447                 }
448                 elsif ($command eq "IO")
449                 {
450                         ($result, $output) = host_disk_io_info($esx, $np, local_uc($subcommand));
451                 }
452                 elsif ($command eq "VMFS")
453                 {
454                         ($result, $output) = host_list_vm_volumes_info($esx, $np, $subcommand, $blacklist, $percc || $percw, $addopts);
455                 }
456                 elsif ($command eq "RUNTIME")
457                 {
458                         ($result, $output) = host_runtime_info($esx, $np, local_uc($subcommand), $blacklist);
459                 }
460                 elsif ($command eq "SERVICE")
461                 {
462                         ($result, $output) = host_service_info($esx, $np, $subcommand);
463                 }
464                 elsif ($command eq "STORAGE")
465                 {
466                         ($result, $output) = host_storage_info($esx, $np, local_uc($subcommand), $blacklist);
467                 }
468                 else
469                 {
470                         $output = "Unknown HOST command\n" . $np->opts->_help;
471                         $result = CRITICAL;
472                 }
473         }
474         elsif (defined($cluster))
475         {
476                 if ($command eq "CPU")
477                 {
478                         ($result, $output) = cluster_cpu_info($cluster, $np, local_uc($subcommand));
479                 }
480                 elsif ($command eq "MEM")
481                 {
482                         ($result, $output) = cluster_mem_info($cluster, $np, local_uc($subcommand), $addopts);
483                 }
484                 elsif ($command eq "CLUSTER")
485                 {
486                         ($result, $output) = cluster_cluster_info($cluster, $np, local_uc($subcommand));
487                 }
488                 elsif ($command eq "VMFS")
489                 {
490                         ($result, $output) = cluster_list_vm_volumes_info($cluster, $np, $subcommand, $blacklist, $percc || $percw, $addopts);
491                 }
492                 elsif ($command eq "RUNTIME")
493                 {
494                         ($result, $output) = cluster_runtime_info($cluster, $np, local_uc($subcommand), $blacklist);
495                 }
496                 else
497                 {
498                         $output = "Unknown CLUSTER command\n" . $np->opts->_help;
499                         $result = CRITICAL;
500                 }
501         }
502         else
503         {
504                 if ($command eq "RECOMMENDATIONS")
505                 {
506                         my $cluster_name;
507                         $cluster_name = {name => $subcommand} if (defined($subcommand));
508                         ($result, $output) = return_cluster_DRS_recommendations($np, $cluster_name);
509                 }
510                 elsif ($command eq "CPU")
511                 {
512                         ($result, $output) = dc_cpu_info($np, local_uc($subcommand), $addopts);
513                 }
514                 elsif ($command eq "MEM")
515                 {
516                         ($result, $output) = dc_mem_info($np, local_uc($subcommand), $addopts);
517                 }
518                 elsif ($command eq "NET")
519                 {
520                         ($result, $output) = dc_net_info($np, local_uc($subcommand));
521                 }
522                 elsif ($command eq "IO")
523                 {
524                         ($result, $output) = dc_disk_io_info($np, local_uc($subcommand));
525                 }
526                 elsif ($command eq "VMFS")
527                 {
528                         ($result, $output) = dc_list_vm_volumes_info($np, $subcommand, $blacklist, $percc || $percw, $addopts);
529                 }
530                 elsif ($command eq "RUNTIME")
531                 {
532                         ($result, $output) = dc_runtime_info($np, local_uc($subcommand), $blacklist);
533                 }
534                 else
535                 {
536                         $output = "Unknown HOST command\n" . $np->opts->_help;
537                         $result = CRITICAL;
538                 }               
539         }
540 };
541 if ($@)
542 {
543         if (uc(ref($@)) eq "HASH")
544         {
545                 $output = $@->{msg};
546                 $result = $@->{code};
547         }
548         else
549         {
550                 $output = $@ . "";
551                 $result = CRITICAL;
552         }
553 }
554
555 Util::disconnect();
556 $np->nagios_exit($result, $output);
557
558 #######################################################################################################################################################################
559
560 sub get_key_metrices {
561         my ($perfmgr_view, $group, @names) = @_;
562
563         my $perfCounterInfo = $perfmgr_view->perfCounter;
564         my @counters;
565
566         die "Insufficient rights to access perfcounters\n" if (!defined($perfCounterInfo));
567
568         foreach (@$perfCounterInfo) {
569                 if ($_->groupInfo->key eq $group) {
570                         my $cur_name = $_->nameInfo->key . "." . $_->rollupType->val;
571                         foreach my $index (0..@names-1)
572                         {
573                                 if ($names[$index] =~ /$cur_name/)
574                                 {
575                                         $names[$index] =~ /(\w+).(\w+):*(.*)/;
576                                         $counters[$index] = PerfMetricId->new(counterId => $_->key, instance => $3);
577                                 }
578                         }
579                 }
580         }
581
582         return \@counters;
583 }
584
585 sub generic_performance_values {
586         my ($views, $group, @list) = @_;
587         my $counter = 0;
588         my @values = ();
589         my $amount = @list;
590         my $perfMgr = Vim::get_view(mo_ref => Vim::get_service_content()->perfManager, properties => [ 'perfCounter' ]);
591         my $metrices = get_key_metrices($perfMgr, $group, @list);
592
593         my @perf_query_spec = ();
594         push(@perf_query_spec, PerfQuerySpec->new(entity => $_, metricId => $metrices, format => 'csv', intervalId => 20, maxSample => 1)) foreach (@$views);
595         my $perf_data = $perfMgr->QueryPerf(querySpec => \@perf_query_spec);
596         $amount *= @$perf_data;
597
598         while (@$perf_data)
599         {
600                 my $unsorted = shift(@$perf_data)->value;
601                 my @host_values = ();
602
603                 foreach my $id (@$unsorted)
604                 {
605                         foreach my $index (0..@$metrices-1)
606                         {
607                                 if ($id->id->counterId == $$metrices[$index]->counterId)
608                                 {
609                                         $counter++ if (!defined($host_values[$index]));
610                                         $host_values[$index] = $id;
611                                 }
612                         }
613                 }
614                 push(@values, \@host_values);
615         }
616         return undef if ($counter != $amount || $counter == 0);
617         return \@values;
618 }
619
620 sub return_host_performance_values {
621         my $values;
622         my $host_name = shift(@_);
623         my $host_view = Vim::find_entity_views(view_type => 'HostSystem', filter => $host_name, properties => [ 'name', 'runtime.inMaintenanceMode' ]); # Added properties named argument.
624         die "Runtime error\n" if (!defined($host_view));
625         die "Host \"" . $$host_name{"name"} . "\" does not exist\n" if (!@$host_view);
626         die {msg => ("NOTICE: \"" . $$host_view[0]->name . "\" is in maintenance mode, check skipped\n"), code => OK} if (uc($$host_view[0]->get_property('runtime.inMaintenanceMode')) eq "TRUE");
627         $values = generic_performance_values($host_view, @_);
628
629         return undef if ($@);
630         return ($host_view, $values);
631 }
632
633 sub return_host_vmware_performance_values {
634         my $values;
635         my $vmname = shift(@_);
636         my $vm_view = Vim::find_entity_views(view_type => 'VirtualMachine', filter => {name => "$vmname"}, properties => [ 'name', 'runtime.powerState' ]);
637         die "Runtime error\n" if (!defined($vm_view));
638         die "VMware machine \"" . $vmname . "\" does not exist\n" if (!@$vm_view);
639         die "VMware machine \"" . $vmname . "\" is not running. Current state is \"" . $$vm_view[0]->get_property('runtime.powerState')->val . "\"\n" if ($$vm_view[0]->get_property('runtime.powerState')->val ne "poweredOn");
640         $values = generic_performance_values($vm_view, @_);
641
642         return $@ if ($@);
643         return ($vm_view, $values);
644 }
645
646 sub return_dc_performance_values {
647         my $values;
648         my $host_views = Vim::find_entity_views(view_type => 'HostSystem', properties => [ 'name' ]);
649         die "Runtime error\n" if (!defined($host_views));
650         die "Datacenter does not contain any hosts\n" if (!@$host_views);
651         $values = generic_performance_values($host_views, @_);
652
653         return undef if ($@);
654         return ($host_views, $values);
655 }
656
657 sub return_cluster_performance_values {
658         my $values;
659         my $cluster_name = shift(@_);
660         my $cluster_view = Vim::find_entity_views(view_type => 'ClusterComputeResource', filter => { name => "$cluster_name" }, properties => [ 'name' ]); # Added properties named argument.
661         die "Runtime error\n" if (!defined($cluster_view));
662         die "Cluster \"" . $cluster_name . "\" does not exist\n" if (!@$cluster_view);
663         $values = generic_performance_values($cluster_view, @_);
664
665         return undef if ($@);
666         return $values;
667 }
668
669 # Temporary solution to overcome zeros in network output
670 sub return_host_temporary_vc_4_1_network_performance_values {
671         my @values;
672         my ($host_name, @list) = @_;
673
674         my $host_view = Vim::find_entity_views(view_type => 'HostSystem', filter => $host_name, properties => [ 'name', 'runtime.inMaintenanceMode', 'summary.config.product.version' ]); # Added properties named argument.
675         die "Runtime error\n" if (!defined($host_view));
676         die "Host \"" . $$host_name{"name"} . "\" does not exist\n" if (!@$host_view);
677         die {msg => ("NOTICE: \"" . $$host_view[0]->name . "\" is in maintenance mode, check skipped\n"), code => OK} if (uc($$host_view[0]->get_property('runtime.inMaintenanceMode')) eq "TRUE");
678         my $software_version = $$host_view[0]->get_property('summary.config.product.version');
679         return undef if (substr($software_version, 0, 4) ne '4.1.');
680
681         my $perfMgr = Vim::get_view(mo_ref => Vim::get_service_content()->perfManager, properties => [ 'perfCounter' ]);
682         my $metrices = get_key_metrices($perfMgr, 'net', @list);
683
684         my $amount = @list;
685         my @perf_query_spec = ();
686         push(@perf_query_spec, PerfQuerySpec->new(entity => $_, metricId => $metrices, format => 'csv', intervalId => 20, maxSample => 1)) foreach (@$host_view);
687         my $perf_data = $perfMgr->QueryPerf(querySpec => \@perf_query_spec);
688         $amount *= @$perf_data;
689
690         my $counter = 0;
691         while (@$perf_data)
692         {
693                 my $unsorted = shift(@$perf_data)->value;
694                 my @host_values = ();
695
696                 foreach my $id (@$unsorted)
697                 {
698                         foreach my $index (0..@$metrices-1)
699                         {
700                                 if ($id->id->counterId == $$metrices[$index]->counterId)
701                                 {
702                                         if (!defined($host_values[$index]))
703                                         {
704                                                 $counter++;
705                                                 $host_values[$index] = bless({ 'value' => '0' }, "PerfMetricSeriesCSV");
706                                         }
707                                         $host_values[$index]{"value"} += convert_number($id->value) if ($id->id->instance ne '');
708                                 }
709                         }
710                 }
711                 push(@values, \@host_values);
712         }
713
714         return undef if ($counter != $amount || $counter == 0 || $@);
715         return ($host_view, \@values);
716 }
717 # Remove as soon as possible
718
719 sub local_uc
720 {
721         my ($val) = shift(@_);
722         return defined($val)?uc($val):undef;
723 }
724
725 sub simplify_number
726 {
727         my ($number, $cnt) = @_;
728         $cnt = 2 if (!defined($cnt));
729         return sprintf("%.${cnt}f", "$number");
730 }
731
732 sub convert_number
733 {
734         my @vals = split(/,/, shift(@_));
735         return shift(@vals) if ($vals[-1] < 0);
736         return pop(@vals);
737 }
738
739 sub check_percantage
740 {
741         my ($number) = shift(@_);
742         my $perc = $number =~ s/\%//;
743         return ($perc, $number);
744 }
745
746 sub check_health_state
747 {
748         my ($state) = shift(@_);
749         my $res = UNKNOWN;
750
751         if (uc($state) eq "GREEN") {
752                 $res = OK
753         } elsif (uc($state) eq "YELLOW") {
754                 $res = WARNING;
755         } elsif (uc($state) eq "RED") {
756                 $res = CRITICAL;
757         }
758         
759         return $res;
760 }
761
762 sub format_issue {
763         my ($issue) = shift(@_);
764
765         my $output = '';
766
767         if (defined($issue->datacenter))
768         {
769                 $output .= 'Datacenter "' . $issue->datacenter->name . '", ';
770         }
771         if (defined($issue->host))
772         {
773                 $output .= 'Host "' . $issue->host->name . '", ';
774         }
775         if (defined($issue->vm))
776         {
777                 $output .= 'VM "' . $issue->vm->name . '", ';
778         }
779         if (defined($issue->computeResource))
780         {
781                 $output .= 'Compute Resource "' . $issue->computeResource->name . '", ';
782         }
783         if (exists($issue->{dvs}) && defined($issue->dvs))
784         {
785                 # Since vSphere API 4.0
786                 $output .= 'Virtual Switch "' . $issue->dvs->name . '", ';
787         }
788         if (exists($issue->{ds}) && defined($issue->ds))
789         {
790                 # Since vSphere API 4.0
791                 $output .= 'Datastore "' . $issue->ds->name . '", ';
792         }
793         if (exists($issue->{net}) && defined($issue->net))
794         {
795                 # Since vSphere API 4.0
796                 $output .= 'Network "' . $issue->net->name . '" ';
797         }
798
799         $output =~ s/, $/ /;
800         $output .= ": " . $issue->fullFormattedMessage;
801         $output .= "(caused by " . $issue->userName . ")" if ($issue->userName ne "");
802
803         return $output;
804 }
805
806 sub datastore_volumes_info
807 {
808         my ($datastore, $np, $subcommand, $blacklist, $perc, $addopts) = @_;
809
810         my $res = OK;
811         my $output = '';
812
813         my $usedflag;
814         my $briefflag;
815         my $regexpflag;
816         my $blackregexpflag;
817         $usedflag = $addopts =~ m/(^|\s|\t|,)\Qused\E($|\s|\t|,)/ if (defined($addopts));
818         $briefflag = $addopts =~ m/(^|\s|\t|,)\Qbrief\E($|\s|\t|,)/ if (defined($addopts));
819         $regexpflag = $addopts =~ m/(^|\s|\t|,)\Qregexp\E($|\s|\t|,)/ if (defined($addopts));
820         $blackregexpflag = $addopts =~ m/(^|\s|\t|,)\Qblacklistregexp\E($|\s|\t|,)/ if (defined($addopts));
821
822         die "Blacklist is supported only in generic check or regexp subcheck\n" if (defined($subcommand) && defined($blacklist) && !defined($regexpflag));
823
824         if (defined($regexpflag) && defined($subcommand))
825         {
826                 eval
827                 {
828                         qr{$subcommand};
829                 };
830                 if ($@)
831                 {
832                         $@ =~ s/ at.*line.*\.//;
833                         die $@;
834                 }
835         }
836
837         my $state;
838         foreach my $ref_store (@{$datastore})
839         {
840                 my $store = Vim::get_view(mo_ref => $ref_store, properties => ['summary', 'info']);
841                 my $name = $store->summary->name;
842                 if (!defined($subcommand) || ($name eq $subcommand) || (defined($regexpflag) && $name =~ /$subcommand/))
843                 {
844                         if (defined($blacklist))
845                         {
846                                 next if ($blackregexpflag?$name =~ /$blacklist/:$blacklist =~ m/(^|\s|\t|,)\Q$name\E($|\s|\t|,)/);
847                         }
848
849                         if ($store->summary->accessible)
850                         {
851                                 my $value1 = simplify_number(convert_number($store->summary->freeSpace) / 1024 / 1024);
852                                 my $value2 = convert_number($store->summary->capacity);
853                                 $value2 = simplify_number(convert_number($store->info->freeSpace) / $value2 * 100) if ($value2 > 0);
854
855                                 if ($usedflag)
856                                 {
857                                         $value1 = simplify_number(convert_number($store->summary->capacity) / 1024 / 1024) - $value1;
858                                         $value2 = 100 - $value2;
859                                 }
860
861                                 $state = $np->check_threshold(check => $perc?$value2:$value1);
862                                 $res = Nagios::Plugin::Functions::max_state($res, $state);
863                                 $np->add_perfdata(label => $name, value => $perc?$value2:$value1, uom => $perc?'%':'MB', threshold => $np->threshold);
864                                 $output .= $name . "=". $value1 . " MB (" . $value2 . "%), " if (!$briefflag || $state != OK);
865                         }
866                         else
867                         {
868                                 $res = CRITICAL;
869                                 $output .= $name . " is not accessible, ";
870                         }
871                         last if (!$regexpflag && defined($subcommand) && ($name eq $subcommand));
872                         $blacklist .= $blackregexpflag?"|^$name\$":",$name";
873                 }
874         }
875
876         if ($output)
877         {
878                 chop($output);
879                 chop($output);
880                 $output = "Storages : " . $output;
881         }
882         else
883         {
884                 if ($briefflag)
885                 {
886                         $output = "There are no alerts";
887                 }
888                 else
889                 {
890                         $res = WARNING;
891                         $output = defined($subcommand)?$regexpflag? "No matching volumes for regexp \"$subcommand\" found":"No volume named \"$subcommand\" found":"There are no volumes";
892                 }
893         }
894
895         return ($res, $output);
896 }
897
898 #=====================================================================| HOST |============================================================================#
899
900 sub host_cpu_info
901 {
902         my ($host, $np, $subcommand, $addopts) = @_;
903
904         my $res = CRITICAL;
905         my $output = 'HOST CPU Unknown error';
906
907         my $quickStats;
908         $quickStats = $addopts =~ m/(^|\s|\t|,)\Qquickstats\E($|\s|\t|,)/ if (defined($addopts));
909
910         if (defined($subcommand))
911         {
912                 if ($subcommand eq "USAGE")
913                 {
914                         my $value;
915                         if (defined($quickStats))
916                         {
917                                 my $host_view = Vim::find_entity_view(view_type => 'HostSystem', filter => $host, properties => ['name', 'runtime.inMaintenanceMode', 'summary.hardware', 'summary.quickStats']);
918                                 die "Host \"" . $$host{"name"} . "\" does not exist\n" if (!defined($host_view));
919                                 die {msg => ("NOTICE: \"" . $host_view->name . "\" is in maintenance mode, check skipped\n"), code => OK} if (uc($host_view->get_property('runtime.inMaintenanceMode')) eq "TRUE");
920                                 $values = $host_view->get_property('summary.quickStats');
921                                 my $hardinfo = $host_view->get_property('summary.hardware');
922                                 $value = simplify_number($values->overallCpuUsage / ($hardinfo->numCpuCores * $hardinfo->cpuMhz) * 100) if exists($values->{overallCpuUsage}) && defined($hardinfo);
923                         }
924                         else
925                         {
926                                 $values = return_host_performance_values($host, 'cpu', ('usage.average'));
927                                 $value = simplify_number(convert_number($$values[0][0]->value) * 0.01) if (defined($values));
928                         }
929                         if (defined($value))
930                         {
931                                 $np->add_perfdata(label => "cpu_usage", value => $value, uom => '%', threshold => $np->threshold);
932                                 $output = "cpu usage=" . $value . " %"; 
933                                 $res = $np->check_threshold(check => $value);
934                         }
935                 }
936                 elsif ($subcommand eq "USAGEMHZ")
937                 {
938                         my $value;
939                         if (defined($quickStats))
940                         {
941                                 my $host_view = Vim::find_entity_view(view_type => 'HostSystem', filter => $host, properties => ['name', 'runtime.inMaintenanceMode', 'summary.quickStats']);
942                                 die "Host \"" . $$host{"name"} . "\" does not exist\n" if (!defined($host_view));
943                                 die {msg => ("NOTICE: \"" . $host_view->name . "\" is in maintenance mode, check skipped\n"), code => OK} if (uc($host_view->get_property('runtime.inMaintenanceMode')) eq "TRUE");
944                                 $values = $host_view->get_property('summary.quickStats');
945                                 $value = simplify_number($values->overallCpuUsage) if exists($values->{overallCpuUsage});
946                         }
947                         else
948                         {
949                                 $values = return_host_performance_values($host, 'cpu', ('usagemhz.average'));
950                                 $value = simplify_number(convert_number($$values[0][0]->value)) if (defined($values));
951                         }
952                         if (defined($value))
953                         {
954                                 $np->add_perfdata(label => "cpu_usagemhz", value => $value, uom => 'Mhz', threshold => $np->threshold);
955                                 $output = "cpu usagemhz=" . $value . " MHz";
956                                 $res = $np->check_threshold(check => $value);
957                         }
958                 }
959                 else
960                 {
961                         $res = CRITICAL;
962                         $output = "HOST CPU - unknown subcommand\n" . $np->opts->_help;
963                 }
964         }
965         else
966         {
967                 my $value1;
968                 my $value2;
969                 if (defined($quickStats))
970                 {
971                         my $host_view = Vim::find_entity_view(view_type => 'HostSystem', filter => $host, properties => ['name', 'runtime.inMaintenanceMode', 'summary.hardware', 'summary.quickStats']);
972                         die "Host \"" . $$host{"name"} . "\" does not exist\n" if (!defined($host_view));
973                         die {msg => ("NOTICE: \"" . $host_view->name . "\" is in maintenance mode, check skipped\n"), code => OK} if (uc($host_view->get_property('runtime.inMaintenanceMode')) eq "TRUE");
974                         $values = $host_view->get_property('summary.quickStats');
975                         my $hardinfo = $host_view->get_property('summary.hardware');
976                         if (exists($values->{overallCpuUsage}) && defined($hardinfo))
977                         {
978                                 $value1 = simplify_number($values->overallCpuUsage);
979                                 $value2 = simplify_number($values->overallCpuUsage / ($hardinfo->numCpuCores * $hardinfo->cpuMhz) * 100);
980                         }
981                 }
982                 else
983                 {
984                         $values = return_host_performance_values($host, 'cpu', ('usagemhz.average', 'usage.average'));
985                         if ($values) {
986                                 $value1 = simplify_number(convert_number($$values[0][0]->value));
987                                 $value2 = simplify_number(convert_number($$values[0][1]->value) * 0.01);
988                         }
989                 }
990                 if (defined($value1) && defined($value2))
991                 {
992                         $np->add_perfdata(label => "cpu_usagemhz", value => $value1, uom => 'Mhz', threshold => $np->threshold);
993                         $np->add_perfdata(label => "cpu_usage", value => $value2, uom => '%', threshold => $np->threshold);
994                         $res = OK;
995                         $output = "cpu usage=" . $value1 . " MHz (" . $value2 . "%)";
996                 }
997         }
998
999         return ($res, $output);
1000 }
1001
1002 sub host_mem_info
1003 {
1004         my ($host, $np, $subcommand, $addopts) = @_;
1005
1006         my $res = CRITICAL;
1007         my $output = 'HOST MEM Unknown error';
1008
1009         my $quickStats;
1010         my $outputlist;
1011         $quickStats = $addopts =~ m/(^|\s|\t|,)\Qquickstats\E($|\s|\t|,)/ if (defined($addopts));
1012         $outputlist = $addopts =~ m/(^|\s|\t|,)\Qlistvm\E($|\s|\t|,)/ if (defined($addopts));
1013
1014         if (defined($subcommand))
1015         {
1016                 if ($subcommand eq "USAGE")
1017                 {