Projet

Général

Profil

Révision cb21db87

IDcb21db87c763ff719c935141dbe7e69653ab8e35
Parent 1d244291
Enfant b81e26cd

Ajouté par Stefan Seidel il y a plus de 13 ans

update view data; make everything work via vCenter too; add option to flatten the view to allow moving of VMs between hosts without breaking the VM graphs

Voir les différences:

plugins/vmware/esx_
1 1
#!/usr/bin/perl -w
2
#
3
# -== Munin plugin for VMware ESXi/vSphere monitoring ==-
4
#
5
# Copyright (c) 2012 - Stefan Seidel <munin@stefanseidel.info>
6
#
7
#    This program is free software: you can redistribute it and/or modify
8
#    it under the terms of the GNU General Public License as published by
9
#    the Free Software Foundation, either version 3 of the License, or
10
#    (at your option) any later version.
11
#
12
#    This program is distributed in the hope that it will be useful,
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
#    GNU General Public License for more details.
16
#
17
#    You should have received a copy of the GNU General Public License
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
#
20
#
21
# This plugin uses the vSphere SDK for Perl available at
22
# http://www.vmware.com/support/developer/viperltoolkit/
23
# or included in the vSphere CLI available at
24
# http://www.vmware.com/support/developer/vcli/
25
# The use of the SDK is subject to the terms and condition
26
# of VMware, Inc. to which you must agree upon installation.
27
#
28

  
29

  
30
#
31
# -== Usage ==-
32
# Put this file in /usr/share/munin/plugins, `chmod +x` it and
33
# `ln -s` it to /etc/munin/plugins/esx_<hostname of server to monitor>
34
#
2
=HEADER
3
        -== Munin plugin for VMware ESXi/vSphere monitoring ==-
4

  
5
 Copyright (c) 2012 - Stefan Seidel <munin@stefanseidel.info>
6

  
7
    This program is free software: you can redistribute it and/or modify
8
    it under the terms of the GNU General Public License as published by
9
    the Free Software Foundation, either version 3 of the License, or
10
    (at your option) any later version.
11

  
12
    This program is distributed in the hope that it will be useful,
13
    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
    GNU General Public License for more details.
16

  
17
    You should have received a copy of the GNU General Public License
18
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19

  
20

  
21
 This plugin uses the vSphere SDK for Perl available at
22
 http://www.vmware.com/support/developer/viperltoolkit/
23
 or included in the vSphere CLI available at
24
 http://www.vmware.com/support/developer/vcli/
25
 The use of the SDK is subject to the terms and condition
26
 of VMware, Inc. to which you must agree upon installation.
27
=cut
28

  
29
=USAGE
30
       -== Usage ==-
31
Put this file in /usr/share/munin/plugins, `chmod +x` it and
32
`ln -s` it to /etc/munin/plugins/esx_<hostname of server to monitor>
33
Add a file "esx_" /etc/munin/plugin-conf.d with content like this
34
(omit the "# " at the beginning of each line)
35

  
36
---- snip ----
37
[esx_*]
38
timeout 60
39
env.user <username on ESX server or vCenter>
40
env.password <password of user on ESX server or vCenter>
41
---- snip ----
42

  
43
Then you need to add this host to your munin.conf on the munin server
44
(often this is the same as your munin node, i.e. this host) and restart
45
munin-node, and wait for the data to populate.
46

  
47

  
48
       -== Graphs don't render ==-
49
Munin 1.4 has a bug with complex multigraphs like this, see
50
http://munin-monitoring.org/ticket/1224 for details and a fix if
51
your graphs don't render!
52

  
53

  
54
       -== Option flatview ==-
55
There is an option to render all VMs and Host Systems in a flat
56
structure, i.e. not rendering VMs as sub-items of their host.
57
This is useful if you frequently move VMs between hosts and want to
58
keep the VM graphs running. To activate this option, add
59

  
60
---- snip ----
61
env.flatview top_level_entry
62
---- snip ----
63

  
64
to the entry in your config file in /etc/munin/plugin-conf.d (see above).
65
Be aware that this has some drawbacks:
66
 - you cannot have the same VM name in two hosts you monitor
67
   (the VM name is the unique identifier for the graphs)
68
 - you will only indirectly be able to see which VM is on which host
69
   (running VMs will appear in the CPU graphs of their hosts)
70
 - it's a flat structure, so it can become quite a long list
71
 - because of the way Munin works, all hosts will be queried serially,
72
   not in parallel as it would be the case without "flat view" - this
73
   MAY lead to timing problems if you have a large number of hosts or VMs
74

  
75

  
76
       -== Option vCenter ==-
77
If you wish to access the host system indirectly through a vCenter, just
78
specify this parameter:
79

  
80
---- snip ----
81
env.vCenter <address or hostname of the vCenter>
82
---- snip ----
83

  
84
This option can be used with or without the "flatview" option. Make sure your
85
password and username are valid on the vCenter. The plugin name will still have
86
to contain the hostname of the host you want to monitor - be aware that you have
87
to use the hostname exactly as it is registered in the vCenter, so IPs and
88
hostnames are NOT interchangeable.
89
=cut
90

  
91
=ACK
92
     -== Ackknowledgements ==-
93
I would like to thank VMware for their SDK and the good documentation.
94

  
95
Special thanks go to MEGABIT Informationstechnik GmbH (www.megabit.net)
96
who graciously sponsored the development of the "flat view" option
97
and the ability to access hosts via vCenter.
98
=cut
35 99

  
36 100
use strict;
37 101
use sort 'stable'; # guarantee stability
......
45 109
use List::Util qw(sum max);
46 110
use List::MoreUtils qw(all);
47 111
use Munin::Plugin;
112
use Time::HiRes qw(time);
113
my $DEBUG = ${Munin::Plugin::DEBUG};
114

  
115
# Important: this is needed if you do not use a "proper" SSL certificate
116
# on your vSphere/vCenter/ESX(i) server (which is the default)
117
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
48 118

  
49 119
# get hostname from filename and blurt it out immediately
50 120
# so that when something goes wrong, at least the plugin
51 121
# output is linked with the right host
52 122
$0 =~ /esx_(.+)$/;
53 123
my $host_name = $1;
54
print "host_name $host_name\n";
55 124

  
56
# env.user and env.password need to be set in plugin-conf/munin-node
125
if ((defined $ARGV[0]) and ($ARGV[0] eq "config")) {
126
    if ($ENV{flatview}) {
127
        print "host_name $ENV{flatview}\n";
128
        print "# for host $host_name\n" if $DEBUG;
129
    } else {
130
        print "host_name $host_name\n";
131
    }
132
}
133

  
134
# env.user and env.password need to be set in plugin-conf.d
57 135
Opts::set_option ('username', $ENV{user} || 'root');
58 136
Opts::set_option ('password', $ENV{password} || '');
59
Opts::set_option ('url',"https://$host_name/sdk/webService");
137
if ($ENV{vCenter}) {
138
    print "# vCenter: $ENV{vCenter} - host $host_name\n" if $DEBUG;
139
    Opts::add_options ( (vihost => { alias => "h", type => "=s", required => 0 }) );
140
    Opts::set_option ('vihost',"$host_name");
141
    Opts::set_option ('url',"https://$ENV{vCenter}/sdk/webService");
142
} else {
143
    Opts::set_option ('url',"https://$host_name/sdk/webService");
144
}
145

  
60 146

  
61 147
# plugin needs Munin 1.4 or later
62 148
need_multigraph();
63 149

  
64 150
# for datetime parsing later on
65 151
my $iso8601 = DateTime::Format::ISO8601->new;
152
my $sstarttime = time();
66 153

  
67 154
# connect to vSphere host
68 155
Util::connect();
69 156

  
70 157
# central object host_view holds all relevant items (VMs, network, etc.)
71
my $host_view = VIExt::get_host_view(1, ['summary', 'network', 'datastore', 'vm', 'runtime', 'configManager.networkSystem']);
158
my $host_view = VIExt::get_host_view(1, ['summary', 'network', 'datastore', 'vm', 'runtime', 'configManager.networkSystem', 'configManager.dateTimeSystem']);
72 159
Opts::assert_usage(defined($host_view), "Invalid host.");
73 160

  
161
my $serviceInst = Vim::get_view (mo_ref => ManagedObjectReference->new(type => 'ServiceInstance', value => 'ServiceInstance'));
74 162
# Performance Manager for getting the actual values
75
my $perfMan = Vim::get_view (mo_ref => ManagedObjectReference->new(type => 'PerformanceManager', value => 'ha-perfmgr'));
163
my $perfMan = Vim::get_view (mo_ref => $serviceInst->content->perfManager);
76 164
Opts::assert_usage(defined($perfMan), "No PerformanceManager.");
77 165

  
78 166
# may be needed later
......
81 169

  
82 170
# used for getting the current vSphere server time and then
83 171
# defining the (now - 5minutes) interval
84
my $dtsys = Vim::get_view(mo_ref => ManagedObjectReference->new(type => 'HostDateTimeSystem', value => 'dateTimeSystem'));
172
my $dtsys = Vim::get_view(mo_ref => $host_view->{'configManager.dateTimeSystem'});
85 173
Opts::assert_usage(defined($dtsys), "No DateTimeSystem.");
86 174

  
175
print "# time to connect and get objects: ", time() - $sstarttime, "\n" if $DEBUG;
176

  
87 177
# enumerate all performance counters by their IDs
88 178
my %perfCounter = map { $_->key => $_ } @{$perfMan->perfCounter};
89 179
# holds all performance data
......
93 183
# IDs/UUIDs to human readable names
94 184
my $resolveNames;
95 185

  
186
$host_view->update_view_data();
96 187
# retrieve performance counters for host
97 188
push @all_perf_data, get_perf_data($host_view);
98 189
# manually set UF name for host system
......
115 206
        my $datastore = Vim::get_view (mo_ref => $_);
116 207
        # update freeSpace values (doesn't work on free ESXi)
117 208
        eval { $datastore->RefreshDatastore(); };
209
        $datastore->update_view_data();
118 210
        my $uuid =$datastore->summary->url;
119 211
        $uuid =~ s!.+/!!;
120 212
        $resolveNames->{datastore}->{$uuid} = $datastore->name;
......
152 244
for ($host_view->vm) {
153 245
    for (@$_) {
154 246
        my $vm = Vim::get_view (mo_ref => $_);
247
        $vm->update_view_data();
155 248
        # store VM id for later iteration
156 249
        my $vmId = $_->{value};
157 250
        push @all_vms, $vmId;
158 251
        # ID to VM name
159 252
        $resolveNames->{vm}->{$vmId} = "VM ".$vm->summary->config->name;
253
        $resolveNames->{vmuuid}->{$vmId} = $vm->summary->config->uuid;
160 254
        # fetch disk space usage per datastore
161 255
        for (@{$vm->storage->perDatastoreUsage}) {
162 256
            my $uuid = Vim::get_view(mo_ref => $_->datastore)->summary->url;
......
233 327
                    unit => "Numbers" });
234 328
}
235 329

  
236
# -> DEBUG
237
foreach (sort { $a->{group} cmp $b->{group} || $a->{instance} cmp $b->{instance} || $a->{name} cmp $b->{name} || $a->{rollup} cmp $b->{rollup} || $a->{vm} cmp $b->{vm} } @all_perf_data) {
238
   print "# $_->{vm}\t$_->{rollup}\t$_->{group}\t$_->{instance}\t$_->{name}\t$_->{value}\t$_->{unit}\n";
330
if ($DEBUG) {
331
    foreach (sort { $a->{group} cmp $b->{group} || $a->{instance} cmp $b->{instance} || $a->{name} cmp $b->{name} || $a->{rollup} cmp $b->{rollup} || $a->{vm} cmp $b->{vm} } @all_perf_data) {
332
        print "# $_->{vm}\t$_->{rollup}\t$_->{group}\t$_->{instance}\t$_->{name}\t$_->{value}\t$_->{unit}\n";
333
    }
239 334
}
240
# <- DEBUG
241 335

  
242 336
# which graphs to draw
243 337
my @all_graphs = ();
......
245 339
# host system
246 340
push @all_graphs, (
247 341
    {   selector => { group => qr/^cpu$/i, name => qr/^usagemhz$/i, instance => qr/^$/ },
248
          config => { groupBy => "group", graphName => "usage_", graphTitle => "CPU usage per " }
342
          config => { groupBy => "group", graphName => "host_cpu", graphTitle => "CPU usage per " }
249 343
    },
250 344
    {   selector => { group => qr/^disk$/i, name => qr/^(read|usage|write)$/i, instance => qr/.+/ },
251
          config => { groupBy => "group", graphName => "transfer_", graphTitle => "Disk Transfer Rates per " }
345
          config => { groupBy => "group", graphName => "host_disk_transfer", graphTitle => "Disk Transfer Rates per " }
252 346
    },
253 347
    {   selector => { group => qr/^disk$/i, name => qr/^.+Averaged$/i, instance => qr/.+/ },
254
          config => { groupBy => "group", graphName => "iops_", graphTitle => "Disk I/O operations per " }
348
          config => { groupBy => "group", graphName => "host_disk_iops", graphTitle => "Disk I/O operations per " }
255 349
    },
256 350
    {   selector => { group => qr/^disk$/i, name => qr/^.+Latency$/i, instance => qr/.+/, vm => qr/^$/ },
257
          config => { groupBy => "vm", graphName => "latency_disk", graphTitle => "Disk latency for " }
351
          config => { groupBy => "vm", graphName => "host_disk_latency", graphTitle => "Disk latency for " }
258 352
    },
259 353
    {   selector => { group => qr/^mem$/i, unit => qr/^KB$/i, rollup => qr/^none$/, vm => qr/^$/ },
260
          config => { groupBy => "vm", graphName => "mem_host", graphTitle => "Memory usage for " }
354
          config => { groupBy => "vm", graphName => "host_memory", graphTitle => "Memory usage for " }
261 355
    },
262 356
    {   selector => { group => qr/^datastore$/i, unit => qr/^Bytes$/i, vm => qr/^$/ },
263 357
          config => { groupBy => "vm", graphName => "usage_datastore", graphTitle => "Disk space usage for ", graphArgs => "--lower-limit 10737418240 --logarithmic --alt-autoscale-min --units=si" }
264 358
    },
265 359
    {   selector => { group => qr/^net$/i, unit => qr/^KBps$/i, vm => qr/^$/ },
266
          config => { groupBy => "vm", graphName => "traffic_net", graphTitle => "Network traffic for " }
360
          config => { groupBy => "vm", graphName => "host_traffic_net", graphTitle => "Network traffic for " }
267 361
    },
268 362
    {   selector => { group => qr/^net$/i, unit => qr/^Number$/i, vm => qr/^$/ },
269
          config => { groupBy => "vm", graphName => "packets_net", graphTitle => "Network packets for " }
363
          config => { groupBy => "vm", graphName => "host_packets_net", graphTitle => "Network packets for " }
364
    },
365
    {   selector => { group => qr/^power$/i, name => qr/^power$/i },
366
          config => { groupBy => "group", graphName => "power_usage", graphTitle => "Host System and VM " }
270 367
    },
271 368
    {   selector => { group => qr/^sys$/i, name => qr/^diskUsage$/i },
272
          config => { groupBy => "name", graphName => "host_", graphTitle => "Host System " }
369
          config => { groupBy => "name", graphName => "host_disk_usage", graphTitle => "Host System " }
273 370
    },
274 371
    {   selector => { group => qr/^sys$/i, name => qr/^uptime$/i },
275
          config => { groupBy => "name", graphName => "host_", graphTitle => "Host System and VM ", graphArgs => "--lower-limit 1000 --logarithmic --alt-autoscale-min" }
372
          config => { groupBy => "name", graphName => "uptimes", graphTitle => "Host System and VM ", graphArgs => "--lower-limit 1000 --logarithmic --alt-autoscale-min" }
276 373
    }
277 374
);
278 375

  
......
281 378
    my $vmName = clean_fieldname($resolveNames->{vm}->{$_});
282 379
    push @all_graphs, (
283 380
        {   selector => { group => qr/^cpu$/i, name => qr/^usagemhz$/i, vm => qr/^$_$/ },
284
              config => { groupBy => "vm", graphName => "$vmName.cpu_", graphTitle => "CPU usage for " }
381
              config => { groupBy => "vm", graphName => "$vmName.vm_cpu", graphTitle => "CPU usage for " }
285 382
        },
286 383
        {   selector => { group => qr/^mem$/i, unit => qr/^KB$/i, rollup => qr/^none$/, vm => qr/^$_$/ },
287
              config => { groupBy => "vm", graphName => "$vmName.memory_", graphTitle => "Memory usage for " }
384
              config => { groupBy => "vm", graphName => "$vmName.vm_memory", graphTitle => "Memory usage for " }
288 385
        },
289 386
        {   selector => { group => qr/^datastore$/i, unit => qr/^Bytes$/i, vm => qr/^$_$/ },
290
              config => { groupBy => "vm", graphName => "$vmName.datastore_", graphTitle => "Disk space usage for ", graphArgs => "--lower-limit 10485760 --logarithmic --alt-autoscale-min --units=si" }
387
              config => { groupBy => "vm", graphName => "$vmName.vm_datastore", graphTitle => "Disk space usage for ", graphArgs => "--lower-limit 10485760 --logarithmic --alt-autoscale-min --units=si" }
291 388
        },
292 389
        {   selector => { group => qr/^virtualDisk$/i, unit => qr/^Millisecond$/i, vm => qr/^$_$/ },
293
              config => { groupBy => "vm", graphName => "$vmName.disklat_", graphTitle => "Disk latency for " }
390
              config => { groupBy => "vm", graphName => "$vmName.vm_disklat", graphTitle => "Disk latency for " }
294 391
        },
295 392
        {   selector => { group => qr/^virtualDisk$/i, unit => qr/^Number$/i, vm => qr/^$_$/ },
296
              config => { groupBy => "vm", graphName => "$vmName.diskiops_", graphTitle => "Disk I/O operations for " }
393
              config => { groupBy => "vm", graphName => "$vmName.vm_diskiops", graphTitle => "Disk I/O operations for " }
297 394
        },
298 395
        {   selector => { group => qr/^virtualDisk$/i, unit => qr/^KBps$/i, vm => qr/^$_$/ },
299
              config => { groupBy => "vm", graphName => "$vmName.disktrans_", graphTitle => "Disk transfer rates for " }
396
              config => { groupBy => "vm", graphName => "$vmName.vm_disktrans", graphTitle => "Disk transfer rates for " }
300 397
        },
301 398
        {   selector => { group => qr/^net$/i, unit => qr/^KBps$/i, vm => qr/^$_$/ },
302
              config => { groupBy => "vm", graphName => "$vmName.traffic_net_", graphTitle => "Network traffic for " }
399
              config => { groupBy => "vm", graphName => "$vmName.vm_traffic_net", graphTitle => "Network traffic for " }
303 400
        },
304 401
        {   selector => { group => qr/^net$/i, unit => qr/^Number$/i, vm => qr/^$_$/ },
305
              config => { groupBy => "vm", graphName => "$vmName.packets_net_", graphTitle => "Network packets for " }
402
              config => { groupBy => "vm", graphName => "$vmName.vm_packets_net", graphTitle => "Network packets for " }
306 403
        },
307 404
        {   selector => { group => qr/^sys$/i, name => qr/^uptime$/i, vm => qr/^$_$/ },
308
              config => { groupBy => "vm", graphName => "$vmName.uptime_", graphTitle => "VM uptime " }
405
              config => { groupBy => "vm", graphName => "$vmName.vm_uptime", graphTitle => "VM uptime " }
309 406
        }
310 407
    );
311 408
}
......
313 410
# sensor graphs
314 411
push @all_graphs, (
315 412
    {   selector => { group => qr/^sensors$/i },
316
          config => { groupBy => "unit", graphName => "sensor_", graphTitle => "Sensors " }
413
          config => { groupBy => "unit", graphName => "sensor_", graphTitle => "Sensors ", multiGraph => 1 }
317 414
    });
318 415

  
416
print "# time to collect all data: ", time() - $sstarttime, "\n" if $DEBUG;
319 417

  
320 418
# actual processing
321 419
foreach (@all_graphs) {
......
327 425
    }
328 426
}
329 427

  
428
print "# time of the script: ", time() - $sstarttime, "\n" if $DEBUG;
429

  
330 430
0;
331 431

  
332 432
####################################################################
......
350 450
sub get_perf_data {
351 451
    my $entity = shift;
352 452
    my @ret = ();
453
    my $gathstart = time();
353 454
    # get the current server time
354 455
    my $curtime = $iso8601->parse_datetime($dtsys->QueryDateTime());
355 456
    # and subtract 5 minutes to get all values for the last period
......
375 476
                           unit => $perfDesc->unitInfo->label };
376 477
        }
377 478
    }
479
    print "# time to gather info for $entity :", time() - $gathstart, "\n" if $DEBUG;
378 480
    return @ret;
379 481
}
380 482

  
381 483
# generate a munin-friendly and unique field name
382 484
sub gen_dp_name {
383
    return clean_fieldname("$_[0]->{name}v$_[0]->{vm}i$_[0]->{instance}");
485
    my $fname = $_[0]->{name};
486
    $fname .= "v".$resolveNames->{vmuuid}->{$_[0]->{vm}} unless $_[1] eq "vm" or $_[0]->{vm} eq "";
487
    $fname .= "i$_[0]->{instance}" unless $_[1] eq "instance" or $_[0]->{instance} eq "";
488
    return clean_fieldname($fname);
384 489
}
385 490

  
386 491
# trim white spaces
......
403 508
    $par = $par->{selector};
404 509
    my $oldGroup = "_-_";
405 510
    my $factor;
511
    if ($ENV{flatview}) {
512
        $cfg->{graphName} = clean_fieldname("Host_$host_name").".".$cfg->{graphName} unless $cfg->{graphName} =~ m/\./;
513
    }
406 514

  
407 515
    # find values according to criteria in $par and sort by grouping parameter
408
    foreach (sort { $a->{$cfg->{groupBy}} cmp $b->{$cfg->{groupBy}} } grep { my $d = $_; all { (not exists $d->{$_}) || $d->{$_} =~ /$par->{$_}/ } keys %$par; } @$arr) {
409
        my $groupCrit = $cfg->{groupBy};
410
        my $curGroup = $_->{$groupCrit};
516
    #foreach (sort { $a->{$cfg->{groupBy}} cmp $b->{$cfg->{groupBy}} } grep { my $d = $_; all { (not exists $d->{$_}) || $d->{$_} =~ /$par->{$_}/ } keys %$par; } @$arr) {
517
     foreach (sort { $a->{$cfg->{groupBy}} cmp $b->{$cfg->{groupBy}} } grep { my $d = $_; all { (not exists $d->{$_}) || $d->{$_} =~ /$par->{$_}/ } keys %$par; } @$arr) {
518
        my $groupCrit = $cfg->{groupBy} || "";
519
        my $curGroup = $_->{$groupCrit} || "";
411 520

  
412 521
        if (!($curGroup eq $oldGroup)) {
413 522
            # we're in a new group, meaning a new graph starts
414 523
            $factor = 0;
415 524
            # clean up group name for multigraph name
416 525
            my $ccurGroup = $curGroup;
417
    	    $ccurGroup =~ s/ |\./_/g;
418
            print "multigraph ",$cfg->{graphName},$ccurGroup,"\n";
526
            $ccurGroup =~ s/ |\./_/g;
527
            print "multigraph ",$cfg->{graphName},(exists $cfg->{multiGraph}?$ccurGroup:""),"\n";
419 528

  
420 529
            if ("config" eq $act) {
421 530
                # want configuration
......
457 566

  
458 567
        }
459 568
        $oldGroup = $curGroup;
460
        my $dpName = gen_dp_name($_);
569
        my $dpName = gen_dp_name($_, $groupCrit);
461 570
        if ("config" eq $act) {
462 571
            # want configuration
463 572
            # get instance and VM names and UF names, if applicable
......
485 594
            }
486 595
        } else {
487 596
            # just print value
488
            print gen_dp_name ($_), ".value $_->{value}\n";
597
            print "$dpName.value $_->{value}\n";
489 598
        }
490 599
    }
491 600
}

Formats disponibles : Unified diff