Projet

Général

Profil

Révision 458ad1f5

ID458ad1f51f52808ca61326c587585a85c75a9b1e
Parent 5562eec2
Enfant 381c4a10

Ajouté par Kjetil Torgrim Homme il y a environ 5 ans

add varnish5_ to support Varnish 5.x and 6.x

This plugin has been used at Redpill Linpro for some time now. It adds
some measurements compared to varnish4_ and removes some others.
There are enough changes that it is probably not worth it to try to
merge the code into a generic varnish4_ plugin handling 4, 5 and 6.

Voir les différences:

plugins/varnish/varnish5_
1
#!/usr/bin/perl
2
# -*- perl -*-
3
#
4
# varnish5_ - Munin plugin to for Varnish 5.x and 6.x
5
# Copyright (C) 2009,2018  Redpill Linpro AS
6
#
7
# Author: Kristian Lyngstøl <kristian@bohemians.org>
8
#         Pål-Eivind Johnsen <pej@redpill-linpro.com>
9
#
10
# This program is free software; you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation; either version 2 of the License, or
13
# (at your option) any later version.
14
#
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
# GNU General Public License for more details.
19
#
20
# You should have received a copy of the GNU General Public License along
21
# with this program; if not, write to the Free Software Foundation, Inc.,
22
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23

  
24
=head1 NAME
25

  
26
varnish5_ - Munin plugin to monitor various aspects of varnish
27

  
28
=head1 APPLICABLE SYSTEMS
29

  
30
Varnish 5.x with varnishstat
31

  
32
=head1 CONFIGURATION
33

  
34
The plugin needs to be able to execute varnishstat.
35

  
36
The configuration section shows the defaults
37
  [varnish5_*]
38
     group varnish
39
     env.varnishstat varnishstat
40
     env.name
41

  
42
env.varnishstat can be a full path to varnishstat if it's
43
not in the path already.
44

  
45
env.name is blank (undefined) by default and can be used to specify a -n
46
name argument to varnish if multiple instances are running on the same
47
server.
48

  
49
A few aspects are not linked by default. They are marked as
50
'DEBUG' => 'yes' (or any other value). They are:
51

  
52
vcl, bans, bans_lurker, lru, objects_per_objhead,
53
losthdr, esi, hcb, shm, shm_writes, overflow,
54
session, session_herd, gzip
55

  
56
You can link them yourself with something like this:
57

  
58
  ln -s @@LIBDIR@@/plugins/varnish5_ \
59
    @@CONFDIR@@/plugins/varnish5_data_structures
60

  
61
=head1 INTERPRETATION
62

  
63
Each graph uses data from varnishstat.
64

  
65
=head1 MAGIC MARKERS
66

  
67
 #%# family=auto
68
 #%# capabilities=autoconf suggest
69

  
70
=head1 VERSION
71

  
72
 $Id$
73

  
74
=head1 BUGS
75

  
76
The hit_rate graph requires munin r2040 or newer to display
77
correctly.
78

  
79
=head1 PATCHES-TO
80

  
81
Please send patches to Kristian Lyngstøl <kristian@bohemians.org>
82
and/or varnish-misc@varnish-cache.org for significant changes. The
83
munin-contrib Git repo is the authoritative repository for this plugin.
84

  
85
=head1 AUTHOR
86

  
87
Kristian Lyngstøl <kristian@bohemians.org>
88

  
89
=head1 MODIFICATIONS
90

  
91
Ingo Oppermann <ingo.oppermann@gmail.com>
92
Pål-Eivind Johnsen <pej@redpill-linpro.com>
93

  
94
=head1 LICENSE
95

  
96
GPLv2
97

  
98
=cut
99

  
100

  
101
use XML::Parser;
102
use strict;
103

  
104
# Set to 1 to enable output when a variable is defined in a graph but
105
# omitted because it doesn't exist in varnishstat.
106
my $DEBUG = 0;
107

  
108
# Set to 1 to ignore 'DEBUG' and suggest all available aspects.
109
my $FULL_SUGGEST = 0;
110

  
111
# Varnishstat executable. Include full path if it's not in your path.
112
my $varnishstatexec = exists $ENV{'varnishstat'} ? $ENV{'varnishstat'} : "varnishstat";
113

  
114
# For multiple instances
115
my $varnishname = exists $ENV{'name'} ? $ENV{'name'} : undef;
116

  
117
my $self; # Haha, myself, what a clever pun.
118

  
119
# Parameters that can be defined on top level of a graph. Config will print
120
# them as "graph_$foo $value\n"
121
my @graph_parameters = ('title','total','order','scale','vlabel','args');
122

  
123
# Parameters that can be defined on a value-to-value basis and will be
124
# blindly passed to config. Printed as "$fieldname.$param $value\n".
125
#
126
# 'label' is hardcoded as it defaults to a varnishstat-description if not
127
# set.
128
my @field_parameters = ('graph', 'min', 'max', 'draw', 'cdef', 'warning',
129
			'colour', 'info', 'type');
130

  
131
# Varnishstat data is stored here. Example
132
# ('n_vbe' => { 'value' => '124', 'description'=>...,'flag'=>... }, SMA =>
133
# { s0 => { 'value' => '...', 'flag'=> '...' },'Transient' => ...})
134
# Both dynamic and static counters are kept here.
135
#
136
# Notes:
137
#  - The 'flag' field for a counter is in RRD-dialect, not varnishstat
138
my %data;
139

  
140
# Data structure that defines all possible graphs (aspects) and how they
141
# are to be plotted. Every top-level entry is a graph/aspect. Each
142
# top-level graph MUST have title set and 'values'.
143
#
144
# The 'values' hash must have at least one value definition. The actual
145
# value used is either fetched from varnishstat based on the value-name, or
146
# if 'rpn' is defined: calculated. 'type' SHOULD be set.
147
#
148
# Graphs with 'DEBUG' set to anything is omitted from 'suggest'.
149
#
150
# 'rpn' on values allows easy access to graphs consisting of multiple
151
# values from varnishstat. (Reverse polish notation). The RPN
152
# implementation only accepts +-*/ and varnishstat-values.
153
#
154
# With the exception of 'label', which is filled with the
155
# varnishstat-description if left undefined, any value left undefined will
156
# be left up to Munin to define/ignore/yell about.
157
#
158
# For dynamic counters, the values listed need to specify a counter and
159
# family. This will plot the specified counter for each identity within
160
# that family. Example: family of SMA, counter c_fail. This will create a
161
# c_fail-counter for each of the SMA-identities (e.g: Transient, s0, etc).
162
# For dynamic graphs, the value-name is only used to identify the data
163
# point, and does not relate to any varnishstat data as that is set by
164
# family/counter.
165
#
166
# Note that dynamic counters fetch the type from the XML and things like
167
# min/max are currently not supported (and silently ignored).
168
#
169
# See munin documentation or rrdgraph/rrdtool for more information.
170
my %ASPECTS = (
171
	'request_rate' => {
172
		'title' => 'Request rates',
173
		'order' => 'cache_hit cache_hitpass cache_miss cache_hitmiss'
174
			 . 'backend_conn backend_unhealthy '
175
			 . 'client_req client_conn' ,
176
		'values' => {
177
			'sess_conn' => {
178
				'type' => 'DERIVE',
179
				'min' => '0',
180
				'colour' => '444444',
181
				'graph' => 'ON'
182
			},
183
			'client_req' => {
184
				'type' => 'DERIVE',
185
				'colour' => '111111',
186
				'min' => '0'
187
			},
188
			'cache_hit' => {
189
				'type' => 'DERIVE',
190
				'draw' => 'AREA',
191
				'colour' => '00FF00',
192
				'min' => '0'
193
			},
194
			'cache_hitpass' => {
195
				'info' => 'Hitpass are cached passes: An '
196
					. 'entry in the cache instructing '
197
					. 'Varnish to pass. Typically '
198
					. 'achieved after a pass in '
199
					. 'vcl_fetch.',
200
				'type' => 'DERIVE',
201
				'draw' => 'STACK',
202
				'colour' => 'FFFF00',
203
				'min' => '0'
204
			},
205
			'cache_miss' => {
206
				'type' => 'DERIVE',
207
				'colour' => 'FF0000',
208
				'draw' => 'STACK',
209
				'min' => '0'
210
			},
211
			'cache_hitmiss' => {
212
				'info' => 'Hitmiss are cached missing: An '
213
					. 'entry in the cache instructing '
214
					. 'Varnish to pass. Typically '
215
					. 'achieved after a pass in '
216
					. 'vcl_fetch.',
217
				'type' => 'DERIVE',
218
				'draw' => 'STACK',
219
				'colour' => 'FFFF00',
220
				'min' => '0'
221
			},
222
			'backend_conn' => {
223
				'type' => 'DERIVE',
224
				'colour' => '995599',
225
				'min' => '0'
226
			},
227
			'backend_unhealthy' => {
228
				'type' => 'DERIVE',
229
				'min' => '0',
230
				'colour' => 'FF55FF'
231
			},
232
			's_pipe' => {
233
				'type' => 'DERIVE',
234
				'min' => '0',
235
				'colour' => '1d2bdf'
236
			},
237
			's_pass' => {
238
				'type' => 'DERIVE',
239
				'min' => '0',
240
				'colour' => '785d0d'
241
			}
242
		}
243
	},
244
	'hit_rate' => {
245
		'title' => 'Hit rates',
246
		'order' => 'client_req cache_hit cache_miss '
247
			 . 'cache_hitpass cache_hitmiss' ,
248
		'vlabel' => '%',
249
		'args' => '-l 0 -u 100 --rigid',
250
		'scale' => 'no',
251
		'values' => {
252
			'client_req' => {
253
				'type' => 'DERIVE',
254
				'min' => '0',
255
				'graph' => 'off',
256
				'rpn' => [ 'cache_hit' , 'cache_miss' , 'cache_hitpass' ,'cache_hitmiss', '+' , '+' ,'+' ]
257
			},
258
			'cache_hit' => {
259
				'type' => 'DERIVE',
260
				'min' => '0',
261
				'draw' => 'AREA',
262
				'cdef' => 'cache_hit,client_req,/,100,*'
263
			},
264
			'cache_miss' => {
265
				'type' => 'DERIVE',
266
				'draw' => 'STACK',
267
				'min' => '0',
268
				'cdef' => 'cache_miss,client_req,/,100,*'
269
			},
270
			'cache_hitpass' => {
271
				'type' => 'DERIVE',
272
				'draw' => 'STACK',
273
				'min' => '0',
274
				'cdef' => 'cache_hitpass,client_req,/,100,*'
275
			},
276
			'cache_hitmiss' => {
277
				'type' => 'DERIVE',
278
				'draw' => 'STACK',
279
				'min' => '0',
280
				'cdef' => 'cache_hitmiss,client_req,/,100,*'
281
			}
282
		}
283
	},
284
	'backend_traffic' => {
285
		'title' => 'Backend traffic',
286
		'values' => {
287
			'backend_conn' => {
288
				'type' => 'DERIVE',
289
				'min' => '0'
290
			},
291
			'backend_unhealthy' => {
292
				'type' => 'DERIVE',
293
				'min' => '0',
294
				'warning' => ':1'
295
			},
296
			'backend_busy' => {
297
				'type' => 'DERIVE',
298
				'min' => '0'
299
			},
300
			'backend_fail' => {
301
				'type' => 'DERIVE',
302
				'min' => '0'
303
			},
304
			'backend_reuse' => {
305
				'type' => 'DERIVE',
306
				'min' => 0
307
			},
308
			'backend_recycle' => {
309
				'type' => 'DERIVE',
310
				'min' => 0
311
			},
312
			'backend_retry' => {
313
				'type' => 'DERIVE',
314
				'min' => '0'
315
			},
316
			'backend_req' => {
317
				'type' => 'DERIVE',
318
				'min' => '0'
319
			}
320
		}
321
	},
322
	'objects' => {
323
		'title' => 'Number of objects',
324
		'order' => 'n_object n_objectcore n_vampireobject n_objecthead',
325
		'values' => {
326
			'n_object' => {
327
				'type' => 'GAUGE',
328
				'label' => 'Number of objects'
329
			},
330
			'n_objectcore' => {
331
				'type' => 'GAUGE',
332
				'label' => 'Number of object cores'
333
			},
334
			'n_vampireobject' => {
335
				'type' => 'GAUGE',
336
				'label' => 'Number of unresurrected objects'
337
			},
338
			'n_objecthead' => {
339
				'type' => 'GAUGE',
340
				'label' => 'Number of object heads',
341
				'info' => 'Each object head can have one '
342
					. 'or more object attached, '
343
					. 'typically based on the Vary: header'
344
			}
345
		}
346
	},
347
	'transfer_rates' => {
348
		'title' => 'Transfer rates',
349
		'order' => 's_resp_bodybytes s_resp_hdrbytes',
350
		'args' => '-l 0',
351
		'vlabel' => 'bit/s',
352
		'values' => {
353
			's_resp_hdrbytes' => {
354
				'type' => 'DERIVE',
355
				'label' => 'Header traffic',
356
				'draw' => 'STACK',
357
				'min' => '0',
358
				'info' => 'HTTP Header traffic. TCP/IP '
359
					. 'overhead is not included.',
360
				'cdef' => 's_resp_hdrbytes,8,*'
361
			},
362
			's_resp_bodybytes' => {
363
				'type' => 'DERIVE',
364
				'draw' => 'AREA',
365
				'label' => 'Body traffic',
366
				'min' => '0',
367
				'cdef' => 's_resp_bodybytes,8,*'
368
			}
369
		}
370
	},
371
	'threads' => {
372
		'title' => 'Thread status',
373
		'values' => {
374
			'threads' => {
375
				'type' => 'GAUGE',
376
				'min' => '0',
377
				'warning' => '1:'
378
			},
379
			'threads_created' => {
380
				'type' => 'DERIVE',
381
				'min' => '0'
382
			},
383
			'threads_failed' => {
384
				'type' => 'DERIVE',
385
				'min' => '0',
386
				'warning' => ':1'
387
			},
388
			'threads_limited' => {
389
				'type' => 'DERIVE',
390
				'min' => '0'
391
			},
392
			'threads_destroyed' => {
393
				'type' => 'DERIVE',
394
				'min' => '0',
395
				'warning' => ':1'
396
			}
397
		}
398
	},
399
	'memory_usage' => {
400
		'title' => 'Memory usage',
401
		'args' => '--base 1024',
402
		'vlabel' => 'bytes',
403
		'values' => {
404
			'SMA_1' => {
405
				'counter' => 'g_bytes',
406
				'family' => 'SMA',
407
			},
408
			'SMA_2' => {
409
				'counter' => 'g_space',
410
				'family' => 'SMA',
411
			},
412
			'SMA_3' => {
413
				'counter' => 'c_bytes',
414
				'family' => 'SMA'
415
			},
416
			'SMF_1' => {
417
				'counter' => 'g_bytes',
418
				'family' => 'SMF',
419
			},
420
			'SMF_2' => {
421
				'counter' => 'g_space',
422
				'family' => 'SMF',
423
			},
424
			'SMF_3' => {
425
				'counter' => 'c_bytes',
426
				'family' => 'SMF',
427
			}
428
		}
429
	},
430
	'main_uptime' => {
431
		'type' => 'MAIN',
432
		'title' => 'Varnish Child uptime',
433
		'vlabel' => 'days',
434
		'scale' => 'no',
435
		'values' => {
436
			'uptime' => {
437
				'type' => 'GAUGE',
438
				'cdef' => 'uptime,86400,/'
439
			},
440
		}
441
	},
442
	'mgt_uptime' => {
443
		'type' => 'MGT',
444
		'title' => 'Varnish Management uptime',
445
		'vlabel' => 'days',
446
		'scale' => 'no',
447
		'values' => {
448
			'uptime' => {
449
				'type' => 'GAUGE',
450
				'cdef' => 'uptime,86400,/'
451
			},
452
		}
453
	},
454
	'objects_per_objhead' => {
455
		'title' => 'Objects per objecthead',
456
		'DEBUG' => 'yes',
457
		'values' => {
458
			'n_objecthead' => {
459
				'type' => 'GAUGE',
460
				'label' => 'Objects per object heads',
461
				'rpn' => [ 'n_object','n_objecthead','/' ]
462
			}
463
		}
464
	},
465
	'losthdr' => {
466
		'title' => 'HTTP Header overflows',
467
		'DEBUG' => 'yes',
468
		'values' => {
469
			'losthdr' => {
470
				'type' => 'DERIVE',
471
				'min' => '0'
472
			}
473
		}
474
	},
475
	'hcb' => {
476
		'title' => 'Critbit data',
477
		'DEBUG' => 'yes',
478
		'values' => {
479
			'hcb_nolock' => {
480
				'type' => 'DERIVE',
481
				'min' => '0'
482
			},
483
			'hcb_lock' => {
484
				'type' => 'DERIVE',
485
				'min' => '0'
486
			},
487
			'hcb_insert' => {
488
				'type' => 'DERIVE',
489
				'min' => '0'
490
			}
491
		}
492
	},
493
	'esi' => {
494
		'title' => 'ESI',
495
		'DEBUG' => 'yes',
496
		'values' => {
497
			'esi_errors' => {
498
				'type' => 'DERIVE',
499
				'min' => '0'
500
			},
501
			'esi_warnings' => {
502
				'type' => 'DERIVE',
503
				'min' => '0'
504
			}
505
		}
506
	},
507
	'session' => {
508
		'title' => 'Sessions',
509
		'DEBUG' => 'yes',
510
		'values' => {
511
			'sess_conn' => {
512
				'type' => 'DERIVE',
513
				'min' => '0'
514
			},
515
			'sess_drop' => {
516
				'type' => 'DERIVE',
517
				'min' => '0'
518
			},
519
			'sess_fail' => {
520
				'type' => 'DERIVE',
521
				'min' => '0'
522
			},
523
			'sess_pipe_overflow' => {
524
				'type' => 'DERIVE',
525
				'min' => '0'
526
			},
527
			'sess_queued' => {
528
				'type' => 'DERIVE',
529
				'min' => '0'
530
			},
531
			'sess_dropped' => {
532
				'type' => 'DERIVE',
533
				'min' => '0'
534
			},
535
			'sess_closed' => {
536
				'type' => 'DERIVE',
537
				'min' => '0'
538
			},
539
			'sess_pipeline' => {
540
				'type' => 'DERIVE',
541
				'min' => '0'
542
			},
543
			'sess_readahead' => {
544
				'type' => 'DERIVE',
545
				'min' => '0'
546
			}
547
		}
548
	},
549
	'session_herd' => {
550
		'title' => 'Session herd',
551
		'DEBUG' => 'yes',
552
		'values' => {
553
			'sess_herd' => {
554
				'type' => 'DERIVE',
555
				'min' => '0'
556
			}
557
		}
558
	},
559
	'shm_writes' => {
560
		'title' => 'SHM writes and records',
561
		'DEBUG' => 'yes',
562
		'values' => {
563
			'shm_records' => {
564
				'type' => 'DERIVE',
565
				'min' => '0'
566
			},
567
			'shm_writes' => {
568
				'type' => 'DERIVE',
569
				'min' => '0'
570
			}
571
		}
572
	},
573
	'shm' => {
574
		'title' => 'Shared memory activity',
575
		'DEBUG' => 'yes',
576
		'values' => {
577
			'shm_flushes' => {
578
				'type' => 'DERIVE',
579
				'min' => '0'
580
			},
581
			'shm_cont' => {
582
				'type' => 'DERIVE',
583
				'min' => '0'
584
			},
585
			'shm_cycles' => {
586
				'type' => 'DERIVE',
587
				'min' => '0'
588
			}
589
		}
590
	},
591
	'allocations' => {
592
		'title' => 'Memory allocation requests',
593
		'DEBUG' => 'yes',
594
		'values' => {
595
			'sm_nreq' => {
596
				'type' => 'DERIVE',
597
				'min' => '0'
598
			},
599
			'sma_nreq' => {
600
				'type' => 'DERIVE',
601
				'min' => '0'
602
			},
603
			'sms_nreq' => {
604
				'type' => 'DERIVE',
605
				'min' => '0'
606
			}
607
		}
608
	},
609
	'vcl' => {
610
		'title' => 'VCL',
611
		'DEBUG' => 'yes',
612
		'values' => {
613
			'n_backend' => {
614
				'type' => 'GAUGE'
615
			},
616
			'n_vcl' => {
617
				'type' => 'DERIVE',
618
				'min' => '0'
619
			},
620
			'n_vcl_avail' => {
621
				'type' => 'DERIVE',
622
				'min' => '0'
623
			},
624
			'n_vcl_discard' => {
625
				'type' => 'DERIVE',
626
				'min' => '0'
627
			}
628
		}
629
	},
630
	'bans' => {
631
		'title' => 'Bans',
632
		'DEBUG' => 'yes',
633
		'values' => {
634
			'bans' => {
635
				'type' => 'GAUGE'
636
			},
637
			'bans_added' => {
638
				'type' => 'DERIVE',
639
				'min' => '0'
640
			},
641
			'bans_deleted' => {
642
				'type' => 'DERIVE',
643
				'min' => '0'
644
			},
645
			'bans_completed' => {
646
				'type' => 'GAUGE'
647
			},
648
			'bans_obj' => {
649
				'type' => 'GAUGE'
650
			},
651
			'bans_req' => {
652
				'type' => 'GAUGE'
653
			},
654
			'bans_tested' => {
655
				'type' => 'DERIVE',
656
                'min' => '0'
657
			},
658
			'bans_obj_killed' => {
659
				'type' => 'DERIVE',
660
                'min' => '0'
661
			},
662
			'bans_tests_tested' => {
663
				'type' => 'DERIVE',
664
                'min' => '0'
665
			},
666
			'bans_dups' => {
667
				'type' => 'GAUGE'
668
			},
669
			'bans_persisted_bytes' => {
670
				'type' => 'GAUGE'
671
			},
672
			'bans_persisted_fragmentation' => {
673
				'type' => 'GAUGE'
674
			}
675
		}
676
	},
677
	'bans_lurker' => {
678
		'title' => 'Ban Lurker',
679
		'DEBUG' => 'yes',
680
		'values' => {
681
			'bans_lurker_tested' => {
682
				'type' => 'DERIVE',
683
				'min' => '0'
684
			},
685
			'bans_lurker_tests_tested' => {
686
				'type' => 'DERIVE',
687
				'min' => '0'
688
			},
689
			'bans_lurker_obj_killed' => {
690
				'type' => 'DERIVE',
691
				'min' => '0'
692
			},
693
			'bans_lurker_contention' => {
694
				'type' => 'DERIVE',
695
				'min' => '0'
696
			}
697
		}
698
	},
699
	'expunge' => {
700
		'title' => 'Object expunging',
701
		'order' => 'n_expired n_lru_nuked',
702
		'values' => {
703
			'n_expired' => {
704
				'type' => 'DERIVE',
705
				'min' => '0'
706
			},
707
			'n_lru_nuked' => {
708
				'type' => 'DERIVE',
709
				'min' => '0'
710
			}
711
		}
712
	},
713
	'lru' => {
714
		'title' => 'LRU activity',
715
		'DEBUG' => 'yes',
716
		'values' => {
717
			'n_lru_nuked' => {
718
				'type' => 'DERIVE',
719
				'min' => '0'
720
			},
721
			'n_lru_moved' => {
722
				'type' => 'DERIVE',
723
				'min' => '0'
724
			}
725
		}
726
	},
727
	'bad' => {
728
		'title' => 'Misbehavior',
729
		'values' => {
730
			'SMA_1' => {
731
				'counter' => 'c_fail',
732
				'family' => 'SMA',
733
			},
734
			'SMF_1' => {
735
				'counter' => 'c_fail',
736
				'family' => 'SMF',
737
			},
738
			'sess_drop' => {
739
				'type' => 'DERIVE'
740
			},
741
			'backend_unhealthy' => {
742
				'type' => 'DERIVE'
743
			},
744
			'fetch_failed' => {
745
				'type' => 'DERIVE'
746
			},
747
			'backend_busy' => {
748
				'type' => 'DERIVE'
749
			},
750
			'threads_failed' => {
751
				'type' => 'DERIVE'
752
			},
753
			'threads_limited' => {
754
				'type' => 'DERIVE'
755
			},
756
			'threads_destroyed' => {
757
				'type' => 'DERIVE'
758
			},
759
			'thread_queue_len' => {
760
				'type' => 'GAUGE'
761
			},
762
			'losthdr' => {
763
				'type' => 'DERIVE'
764
			},
765
			'esi_errors' => {
766
				'type' => 'DERIVE'
767
			},
768
			'esi_warnings' => {
769
				'type' => 'DERIVE'
770
			},
771
			'sess_fail' => {
772
				'type' => 'DERIVE'
773
			},
774
			'sess_pipe_overflow' => {
775
				'type' => 'DERIVE'
776

  
777
			}
778
		}
779
	},
780
	'gzip' => {
781
		'title' => 'GZIP activity',
782
		'DEBUG' => 'yes',
783
		'values' => {
784
			'n_gzip' => {
785
				'type' => 'DERIVE',
786
				'min' => '0'
787
			},
788
			'n_gunzip' => {
789
				'type' => 'DERIVE',
790
				'min' => '0'
791
			}
792
		}
793
	},
794
	'backend' => {
795
		'title' => 'Backend Status',
796
		'DEBUG' => 'yes',
797
		'values' => {
798
                        'VBE.boot_1' => {
799
                                'counter' => 'happy',
800
                                'family' => 'VBE',
801
                        },
802

  
803
		}
804
	}
805
		
806
);
807

  
808
################################
809
# Various helper functions     #
810
################################
811

  
812
# Translate $_[0] from varnish' internal types (flags) to munin/rrd
813
# variants (e.g: from 'i' to GAUGE). Returns the result.
814
sub translate_type
815
{
816
	my $d = $_[0];
817
	if ($d eq "i" or $d eq "g") {
818
		$d = "GAUGE";
819
	} elsif ($d eq "a" or $d eq "c") {
820
		$d = "DERIVE";
821
	}
822
	return $d;
823
}
824

  
825
# Print the value of a two-dimensional hash if it exist.
826
# Returns false if non-existent.
827
#
828
# Output is formatted for plugins if arg4 is blank, otherwise arg4 is used
829
# as the title/name of the field (ie: arg4=graph_title).
830
sub print_if_exist
831
{
832
	my %values = %{$_[0]};
833
	my $value = $_[1];
834
	my $field = $_[2];
835
	my $pvalue = normalize_name($value);
836

  
837
	my $title = "$pvalue.$field";
838
	if (defined($_[3])) {
839
		$title = $_[3];
840
	}
841
	if (defined($values{$value}{$field})) {
842
		print "$title $values{$value}{$field}\n";
843
	} else {
844
		return 0;
845
	}
846
}
847

  
848
# Create a output-friendly name
849
sub normalize_name
850
{
851
	my $name = $_[0];
852
	$name =~ s/[^a-zA-Z0-9]/_/g;
853
	return $name;
854
}
855

  
856
# Braindead RPN: +,-,/,* will pop two items from @stack, and perform
857
# the relevant operation on the items. If the item in the array isn't one
858
# of the 4 basic math operations, a value from varnishstat is pushed on to
859
# the stack. IE: 'client_req','client_conn','/' will leave the value of
860
# "client_req/client_conn" on the stack.
861
#
862
# If only one item is left on the stack, it is printed. Otherwise, an error
863
# message is printed.
864
sub rpn
865
{
866
	my @stack;
867
	my $left;
868
	my $right;
869
	foreach my $item (@{$_[0]}) {
870
		if ($item eq "+") {
871
			$right = pop(@stack);
872
			$left = pop(@stack);
873
			push(@stack,$left+$right);
874
		} elsif ($item eq "-") {
875
			$right = pop(@stack);
876
			$left = pop(@stack);
877
			push(@stack,$left-$right);
878
		} elsif ($item eq "/") {
879
			$right = pop(@stack);
880
			$left = pop(@stack);
881
			push(@stack,$left/$right);
882
		} elsif ($item eq "*") {
883
			$right = pop(@stack);
884
			$left = pop(@stack);
885
			push(@stack,$left*$right);
886
		} else {
887
			push(@stack,int($data{$item}{'value'}));
888
		}
889
	}
890
	if (@stack > 1)
891
	{
892
		print STDERR "RPN error: Stack has more than one item left.\n";
893
		print STDERR "@stack\n";
894
		exit 255;
895
	}
896
	print "@stack";
897
	print "\n";
898
}
899

  
900
# Bail-function.
901
sub usage
902
{
903
	if (@_) {
904
		print STDERR "@_" . "\n\n";
905
	}
906
	print STDERR "Known arguments: suggest, config, autoconf.\n";
907
	print STDERR "Run with suggest to get a list of known aspects.\n";
908
	exit 1;
909
}
910

  
911
################################
912
# XML Parsing                  #
913
################################
914
# The following code is for parsing varnishstat -x. While %data should be
915
# stable, the following bits can easily be replaced with anything (json, an
916
# other xml-parser, magic, etc)
917
#
918
# The basic concept is simple enough. Only worry about stuff inside
919
# <state>. Updating %state on each new data field, and commit it to %data
920
# when </state> is seen.
921
#
922
# We do use translate_type() on the 'flag' field.
923

  
924

  
925
# Internal state for the XML parsing
926
my %state = (
927
	'stat' => 0,     # inside <stat> or not
928
	'field' => 'none', # <name>, <value>, <stat>, etc.
929
);
930

  
931
# Reset the state of XML, mainly used for end-elements.
932
sub xml_reset_state() {
933
	$state{'stat'} = '0';
934
	$state{'field'} = 'none';
935
	$state{'values'} = ();
936
}
937

  
938
# Callback for data entry. Cleans leading whitespace and updates state.
939
sub xml_characters {
940
	my $d = $_[1];
941
	if ($state{'stat'} == 0) {
942
		return;
943
	}
944
	if ($state{'field'} eq "type" && $d eq "MAIN") {
945
		return;
946
	}
947
	$d =~ s/^\s*$//g;
948
	if ($d eq "") {
949
		return;
950
	}
951
	$state{'values'}{$state{'field'}} = $d;
952
}
953

  
954
# Store the current state in %data. Issued at </stat>
955
# Note that 'flag' is translated to RRD-equivalents here.
956
sub xml_commit_state
957
{
958
	my $configtype = $ASPECTS{$self}{'type'};
959
	my $name = $state{'values'}{'name'};
960
	my @namelist = split(/\./,$name);
961
	my $type = shift @namelist;
962
	if ($configtype eq '' || $configtype eq $type) {
963
		my $name = "";
964
		my $ident = "";
965
		if (scalar(@namelist) == 1) {
966
			$name = shift @namelist;
967
		} elsif (scalar(@namelist) == 2) {
968
			$ident = shift @namelist;
969
			$name = shift @namelist;
970
		} else {
971
			$name = pop @namelist;
972
			$ident = join('_', @namelist);
973
		}
974
		foreach my $key (keys %{$state{'values'}}) {
975
			my $data = $state{'values'}{$key};
976
			if ($key eq 'flag') {
977
				$data = translate_type($data);
978
			}
979
			if (defined($type) and $type ne '' and defined($ident) and $ident ne '') {
980
				$data{$type}{$ident}{$name}{$key} = $data;
981
			} else {
982
				$data{$name}{$key} = $data;
983
			}
984
		}
985
	}
986
}
987

  
988
# Callback for end tag. E.g: </stat>
989
sub xml_end_elem {
990
	my $element = $_[1];
991
	if ($element ne "stat") {
992
		return;
993
	}
994

  
995
	xml_commit_state();
996
	xml_reset_state();
997
}
998

  
999
# Callback for opening tag. E.g: <stat>
1000
sub xml_start_elem {
1001
	$state{'field'} = $_[1];
1002
	if ($state{'field'} eq "stat") {
1003
		$state{'stat'} = 1;
1004
	}
1005
}
1006

  
1007
################################
1008
# Internal API                 #
1009
################################
1010

  
1011

  
1012
# Populate %data, includes both values and descriptions and more.
1013
# Currently driven by XML, but that could change.
1014
sub populate_stats
1015
{
1016
	my $arg = "-x";
1017
	my $parser = new XML::Parser(Handlers => {Start => \&xml_start_elem,
1018
						End => \&xml_end_elem,
1019
						Char => \&xml_characters} );
1020

  
1021
	if ($varnishname) {
1022
		$arg .= " -n $varnishname";
1023
	}
1024

  
1025
	open (XMLDATA, "$varnishstatexec $arg|") or die "meh";
1026
	$parser->parse(*XMLDATA, ProtocolEncoding => 'ISO-8859-1');
1027
	close(XMLDATA);
1028
}
1029

  
1030
# Prints the fields in the list in $_[2] (e.g: 'value'/'description') for
1031
# each identity of the varnish counter/family combination as defined by
1032
# the $_[0]-counter on the aspect definition. Err, that's jibberish, so
1033
# an example:
1034
#
1035
# e.g: dynamic_print('SMA_1','',('value'))
1036
# e.g: dynamic_print('SMA_2','.label',('ident','description'))
1037
# SMA_1 is the counter-value. If it is a dynamic counter, it has a counter
1038
# and family-member (e.g: counter: c_req, family: SMA) and print_dynamic
1039
# will print c_req for each SMA-identity.
1040
#
1041
# Note that the variables to print is a list. This is to allow printing a
1042
# single item with multiple fields. Typically for identity+description so
1043
# you can distinguish between different data points.
1044
#
1045
# Returns true if it was a dynamic counter.
1046
sub print_dynamic
1047
{
1048
	my $name = $_[0];
1049
	shift;
1050
	my $suffix = $_[0];
1051
	shift;
1052
	my @field = @_;
1053
	if (!defined($ASPECTS{$self}{'values'}{$name}{'counter'})) {
1054
		return 0;
1055
	}
1056
	if (!defined($ASPECTS{$self}{'values'}{$name}{'family'})) {
1057
		return 0;
1058
	}
1059
	my $counter = $ASPECTS{$self}{'values'}{$name}{'counter'};
1060
	my $type = $ASPECTS{$self}{'values'}{$name}{'family'};
1061

  
1062
	foreach my $key (keys %{$data{$type}}) {
1063
		my $pname = normalize_name($type . "_" . $key . "_" . $counter);
1064
		print $pname . $suffix . " ";
1065
		my $i = 0;
1066
		foreach my $f (@field) {
1067
			if ($i != 0) {
1068
				print " ";
1069
			}
1070
			$i += 1;
1071
			print $data{$type}{$key}{$counter}{$f};
1072
		}
1073
		print "\n";
1074
	}
1075
	return 1;
1076
}
1077

  
1078
# Read and verify the aspect ($self).
1079
sub set_aspect
1080
{
1081
	$self = $0;
1082
	$self =~ s/^.*\/varnish[0-9]?_//;
1083
	return if defined($ASPECTS{$self});
1084
	# remove instance name and try again
1085
	$self =~ s/^.*?_//;
1086
	if (!defined($ASPECTS{$self}) && @ARGV == 0) {
1087
		usage "No such aspect";
1088
	}
1089
}
1090

  
1091
# Print 'yes' if it's reasonable to use this plugin, or 'no' with a
1092
# human-readable error message. Always exit true, even if the response
1093
# is 'no'.
1094
sub autoconf
1095
{
1096
	# XXX: Solaris outputs errors to stderr and always returns true.
1097
	# XXX: See #873
1098
	if (`which $varnishstatexec 2>/dev/null` =~ m{^/}) {
1099
		print "yes\n";
1100
	} else {
1101
		print "no ($varnishstatexec could not be found)\n";
1102
	}
1103
	exit 0;
1104
}
1105

  
1106
# Suggest relevant aspects/values of $self.
1107
# 'DEBUG'-graphs are excluded.
1108
sub suggest
1109
{
1110
	foreach my $key (keys %ASPECTS) {
1111
		if (defined($ASPECTS{$key}{'DEBUG'}) && $FULL_SUGGEST != 1) {
1112
			next;
1113
		}
1114
		print "$key\n";
1115
	}
1116
}
1117

  
1118
# Walk through the relevant aspect and print all top-level configuration
1119
# values and value-definitions.
1120
sub get_config
1121
{
1122
	my $graph = $_[0];
1123

  
1124
	# Need to double-check since set_aspect only checks this if there
1125
	# is no argument (suggest/autoconf doesn't require a valid aspect)
1126
	if (!defined($ASPECTS{$graph})) {
1127
		usage "No such aspect";
1128
	}
1129
	my %values = %{$ASPECTS{$graph}{'values'}};
1130

  
1131
	print "graph_category webserver\n";
1132
	foreach my $field (@graph_parameters) {
1133
		print_if_exist(\%ASPECTS,$graph,$field,"graph_$field");
1134
	}
1135

  
1136
	foreach my $value (sort keys %values) {
1137
		# Just print the description/type if it's a dynamic
1138
		# counter. It'll be silent if it isn't.
1139
		if(print_dynamic($value,'.label',('description','type','ident'))) {
1140
			print_dynamic($value,'.type',('flag'));
1141
			next;
1142
		}
1143

  
1144
		# Need either RPN definition or a varnishstat value.
1145
		if (!defined($data{$value}{'value'}) &&
1146
		    !defined($values{$value}{'rpn'})) {
1147
			if ($DEBUG) {
1148
				print STDERR "ERROR: $value not part of varnishstat.\n"
1149
			}
1150
			next;
1151
		}
1152

  
1153
		if (!print_if_exist(\%values,$value,'label')) {
1154
			my $pvalue =  normalize_name($value);
1155
			print "$pvalue.label $data{$value}{'description'}\n";
1156
		}
1157
		foreach my $field (@field_parameters) {
1158
			print_if_exist(\%values,$value,$field);
1159
		}
1160
	}
1161
}
1162

  
1163
# Handle arguments (config, autoconf, suggest)
1164
# Populate stats for config is necessary, but we want to avoid it for
1165
# autoconf as it would generate a nasty error.
1166
sub check_args
1167
{
1168
	if (@ARGV && $ARGV[0] eq '') {
1169
		shift @ARGV;
1170
	}
1171
	if (@ARGV == 1) {
1172
		if ($ARGV[0] eq "config") {
1173
			populate_stats;
1174
			get_config($self);
1175
			exit 0;
1176
		} elsif ($ARGV[0] eq "autoconf") {
1177
			autoconf($self);
1178
			exit 0;
1179
		} elsif ($ARGV[0] eq "suggest") {
1180
			suggest;
1181
			exit 0;
1182
		}
1183
		usage "Unknown argument";
1184
	}
1185
}
1186

  
1187
################################
1188
# Execution starts here        #
1189
################################
1190

  
1191
set_aspect;
1192
check_args;
1193
populate_stats;
1194

  
1195
# We only get here if we're supposed to.
1196
# Walks through the relevant values and either prints the varnishstat, or
1197
# if the 'rpn' variable is set, calls rpn() to execute ... the rpn.
1198
#
1199
# NOTE: Due to differences in varnish-versions, this checks if the value
1200
# actually exist before using it.
1201
foreach my $value (keys %{$ASPECTS{$self}{'values'}}) {
1202
        my $pvalue =  normalize_name($value);
1203
	if (defined($ASPECTS{$self}{'values'}{$value}{'rpn'})) {
1204
		print "$pvalue.value ";
1205
		rpn($ASPECTS{$self}{'values'}{$value}{'rpn'});
1206
	} else {
1207
		if (print_dynamic($value,'.value',('value'))) {
1208
			next;
1209
		}
1210

  
1211
		if (!defined($data{$value}{'value'})) {
1212
			if ($DEBUG) {
1213
				print STDERR "Error: $value not part of "
1214
					   . "varnishstat.\n";
1215
			}
1216
			next;
1217
		}
1218
		print "$pvalue.value ";
1219
		print "$data{$value}{'value'}\n";
1220
	}
1221
}
1222

  

Formats disponibles : Unified diff