Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / network / if @ 42fd0b1a

Historique | Voir | Annoter | Télécharger (44,7 ko)

1 71bf7478 Gorlow Maxim aka Sheridan
#!/usr/bin/perl -w
2
# -*- perl -*-
3
4
=head1 NAME
5
6
if - Multigraph plugin to monitor network wired and wireless interfaces
7
8
=head1 INTERPRETATION
9
10
In the general graphs made key fields for each interface, a subsidiary graphs for each interface there are detailed
11
12
This plugin displays the following charts:
13 42fd0b1a Farhad Shahbazi
Traffic, bit
14 71bf7478 Gorlow Maxim aka Sheridan
Traffic, packets
15
Average packet size
16
Interface errors
17
WiFi interface errors
18
WiFi interface signal and noise
19
WiFi interface link quality
20
Interface utilisation
21
22 b6024a55 Gorlow Maxim aka Sheridan
Virtual interface names prefixes with '~'
23
24 71bf7478 Gorlow Maxim aka Sheridan
=head1 CONFIGURATION
25
26
This plugin is configurable environment variables.
27
env.exclude         - Removing interfaces from graphs, default empty
28
env.include         - Includes interfaces into graphs, default empty
29
env.if_max_bps      - Maximum interface bps. Avialable suffixes: k, M, G, default empty
30
env.protexct_peaks  - Protect graph peaks, default 'no'
31
env.min_packet_size - Minimal network packet size, default 20
32
Example:
33
 [if]
34
    env.exclude lo
35
    env.include wlan0 tun0
36
    env.wlan0_max_bps 54M
37
    env.eth0_max_bps 1G
38
    env.protect_peaks yes
39
40
Protect peak:
41 b6024a55 Gorlow Maxim aka Sheridan
1. Protect wifi signal and noise values, all values > 0 print as NaN
42
2. protect all percent values. All values > 100 print as NaN
43 71bf7478 Gorlow Maxim aka Sheridan
3. Protect bps values. env.if_max_bps must be set. All values > max_bps prints as 0
44
4. protect pps values. env.if_max_bps must be set. All values > max_bps/minimal packet size, prints as 0
45
46
=head1 AUTHOR
47
48
Gorlow Maxim aka Sheridan <sheridan@sheridan-home.ru> (email and jabber)
49
50
=head1 LICENSE
51
52
GPLv2
53
54
=head1 MAGIC MARKERS
55
56
  #%# family=auto
57
  #%# capabilities=autoconf
58
59
=cut
60
61
use strict;
62
use warnings;
63
use IO::Dir;
64
use Munin::Plugin;
65
use Data::Dumper;
66
67
# ------------------------------------------------------------- constants ---------------------
68
my $exclude         = $ENV{exclude}         || '';
69
my $include         = $ENV{include}         || '-';
70
my $protect_peacks  = $ENV{protect_peaks}   || 'no';
71
my $min_packet_size = $ENV{min_packet_size} || 20;
72
my $ifpath          = '/sys/class/net';
73
# ----------------------------------- global -----------------
74
my $interfaces = {};
75
76
# ------------------------ avialable graphs -------------------------
77
my $graphs =
78
{
79 42fd0b1a Farhad Shahbazi
  'if_bit' =>
80 71bf7478 Gorlow Maxim aka Sheridan
  {
81
    'munin' =>
82
    {
83
      'category' => 'network',
84 42fd0b1a Farhad Shahbazi
      'args'     => '--base 1000',
85
      'title'    => ':if: traffic, bit',
86
      'vlabel'   => 'Bit in (-) / out (+), avg. per second',
87
      'info'     => 'This graph shows the traffic in bit of the :if:, averaged value per second from last update'
88 71bf7478 Gorlow Maxim aka Sheridan
    },
89
    'per_if_fields'  => [qw(rx_bytes tx_bytes)],
90
    'general_fields' => [qw(rx_bytes tx_bytes)]
91
  },
92
  'if_packets' => 
93
  {
94
    'munin' =>
95
    {
96
      'category' => 'network',
97
      'args'     => '--base 1000',
98
      'title'    => ':if: traffic, packets', 
99
      'vlabel'   => 'Packets in (-) / out (+), avg. per second',
100
      'info'     => 'This graph shows the traffic in packets of the :if:, averaged value per second from last update'
101
    },
102
    'per_if_fields'  => [qw(rx_packets tx_packets rx_compressed tx_compressed rx_dropped tx_dropped multicast)],
103
    'general_fields' => [qw(rx_packets tx_packets)]
104
  },
105
  'if_errors' => 
106
  {
107
    'munin' =>
108
    {
109
      'category' => 'network',
110
      'args'     => '--base 1000',
111
      'title'    => ':if: errors',
112
      'vlabel'   => 'Errors RX (-) / TX (+)',
113
      'info'     => 'This graph shows the errors of the :if: from last update',
114
      'scale'    => 'no'
115
    },
116
    'per_if_fields'  => [qw(rx_errors tx_errors rx_fifo_errors tx_fifo_errors rx_crc_errors rx_frame_errors rx_length_errors rx_missed_errors rx_over_errors collisions tx_aborted_errors tx_carrier_errors tx_heartbeat_errors tx_window_errors)],
117
    'general_fields' => [qw(rx_errors tx_errors)]
118
  },
119
  'if_wifi_sino' =>
120
  {
121
    'munin' =>
122
    {
123
      'category' => 'wifi',
124
      'args'     => '--base 1000 -u 0',
125
      'title'    => ':if: signal and noise levels',
126
      'vlabel'   => 'dB',
127
      'info'     => 'This graph shows the WiFi signal and noise levels of the :if:',
128
      'scale'    => 'no'
129
    },
130
    'per_if_fields'  => [qw(signal noise)],
131
    'general_fields' => [qw(signal)]
132
  },
133
  'if_wifi_link_quality' =>
134
  {
135
    'munin' =>
136
    {
137
      'category' => 'wifi',
138
      'args'     => '--base 1000',
139
      'title'    => ':if: link quality',
140
      'vlabel'   => '%',
141
      'info'     => 'This graph shows the WiFi link quality of the :if:',
142
      'scale'    => 'no'
143
    },
144
    'per_if_fields'  => [qw(link)],
145
    'general_fields' => [qw(link)]
146
  },
147
  'if_wifi_errors' =>
148
  {
149
    'munin' =>
150
    {
151
      'category' => 'wifi',
152
      'args'     => '--base 1000',
153
      'title'    => ':if: errors',
154
      'vlabel'   => 'Errors RX (-) / TX (+)',
155
      'info'     => 'This graph shows the WiFi errors of the :if: from last update',
156
      'scale'    => 'no'
157
    },
158
    'per_if_fields'  => [qw(nwid fragment crypt beacon retries misc)],
159
    'general_fields' => [qw(rx_wifierr tx_wifierr)]
160
  },
161
  'if_utilisation' =>
162
  {
163
    'munin' =>
164
    {
165
      'category' => 'network',
166
      'args'     => '--base 1000',
167
      'title'    => ':if: utilisation',
168
      'vlabel'   => '%',
169
      'info'     => 'This graph shows utilisation of the :if:',
170
      'scale'    => 'no'
171
    },
172
    'per_if_fields'  => [qw(rx_percent tx_percent)],
173
    'general_fields' => [qw(rx_percent tx_percent)]
174
  },
175
  'if_avgpacketsize' =>
176
  {
177
    'munin' =>
178
    {
179
      'category' => 'network',
180
      'args'     => '--base 1024',
181
      'title'    => ':if: avgerage packet size',
182
      'vlabel'   => 'bytes',
183
      'info'     => 'This graph shows average packet size of the :if:'
184
    },
185
    'per_if_fields'  => [qw(rx_size tx_size)],
186
    'general_fields' => [qw(rx_size tx_size)]
187
  }
188
};
189
190
#-------------------------- avialable fields -------------------------
191
# info:
192
# 'munin' => {} - just copy fields to munin config
193
# 'source' => - field data source
194
#    {
195
#      'type' =>  types:
196
#         'file' - data just cat from file
197
#           'location' => file location
198
#         'calculated' => calculated data
199
#         {
200
#            'type' - types:
201
#               'percent',
202
#                 'full' => 
203
#                 {
204
#                    'source' => 'interface',
205
#                    'name' => 'bps'
206
#                 },
207
#                 'part' => 
208
#                 {
209
#                   'source' => 'field',
210
#                   'name' => 'tx_bytes'
211
#                 }
212
#              'division',
213
#                'dividend' =>
214
#                {
215
#                  'source' => 'field',
216
#                  'name' => 'rx_bytes'
217
#                },
218
#                'divider' =>
219
#                {
220
#                  'source' => 'field',
221
#                  'name' => 'rx_packets'
222
#                }
223
#              'sum',
224
#                'sum' => [qw(nwid fragment crypt)]
225
#
226
#          }
227
#    }
228
# 'difference'    => difference types:
229
#     count      - just count from last update
230
#     per_secund - count from last update / time difference per last update
231
# 'negative' => - if field under zero line
232
#    {
233
#      'type' => types:
234
#        'dummy' - dummy field, not draw
235
#          'value' => '' - value for dummy field in update
236
#        'field' - exists field, must be included in graph
237
#          'name' => '' - field name
238
#    }
239 b6024a55 Gorlow Maxim aka Sheridan
# 'peack_protect' => protect peaks. Using munin field.max and field.min and truncate data to NaN
240 71bf7478 Gorlow Maxim aka Sheridan
#       protect types
241
#        'max_interface_bps'     - maximum: max interface bps (if configured), minimum - zero
242
#        'packet_size_range'     - maximum: mtu, minimum: minimal packet size (may be configured)
243
#        'max_interface_pps'     - maximum: max interface bps/minimum packt size (if configured), minimum - zero
244
#        'percents'              - maximum: 100, minimum: 0
245
#        'min_number:max_number' - no comments :)
246
my $fields =
247
{
248
  'collisions' =>
249
  {
250
    'munin' =>
251
    {
252
      'label' => 'Collisions'         ,
253
      'info'  => 'Transmit collisions',
254
      'type'  => 'GAUGE',
255
      'draw'  => 'LINE1'
256
    },
257
    'source' =>
258
    {
259
      'type'     => 'file',
260
      'location' => $ifpath.'/:if:/statistics/collisions'
261
    },
262
    'difference' => 'count'
263
  },
264
  # --------------------------------------------------------------------------
265
  'multicast' => 
266
  {
267
    'munin' =>
268
    {
269
     'type'  => 'GAUGE',
270
     'draw'  => 'LINE1',
271
     'label' => 'Multicast packets',
272
     'info'  => 'Multicast packets received'
273
    },
274
    'source' =>
275
    {
276
      'type'     => 'file',
277
      'location' => $ifpath.'/:if:/statistics/multicast'
278
    },
279
    'negative' =>
280
    {
281
      'type'  => 'dummy',
282 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
283 71bf7478 Gorlow Maxim aka Sheridan
    },
284
    'difference'    => 'per_secund'
285
  },
286
  # --------------------------------------------------------------------------
287
  'rx_bytes' => 
288
  { 
289
    'munin' =>
290
    {
291
     'type'  => 'GAUGE',
292
     'draw'  => 'LINE1',
293 42fd0b1a Farhad Shahbazi
     'label' => 'RX bit',
294
     'info'  => 'RX bit'
295 71bf7478 Gorlow Maxim aka Sheridan
    },
296
    'source' =>
297
    {
298
      'type'     => 'file',
299
      'location' => $ifpath.'/:if:/statistics/rx_bytes'
300
    },
301 42fd0b1a Farhad Shahbazi
    'cdef' => '8,*',
302 71bf7478 Gorlow Maxim aka Sheridan
    'peack_protect' => 'max_interface_bps',
303
    'negative' =>
304
    {
305
      'type' => 'field',
306
      'name' => 'tx_bytes'
307
    },
308
    'difference' => 'per_secund'
309
  },
310
  # --------------------------------------------------------------------------
311
  'rx_compressed' =>
312
  {
313
    'munin' =>
314
    {
315
      'type'  => 'GAUGE',
316
      'draw'  => 'LINE1',
317
      'label' => 'RX compressed packets',
318
      'info'  => 'Compressed packets',
319
    },
320
    'source' =>
321
    {
322
      'type'     => 'file',
323
      'location' => $ifpath.'/:if:/statistics/rx_compressed'
324
    },
325
    'peack_protect' => 'max_interface_pps',
326
    'negative' =>
327
    {
328
      'type' => 'field',
329
      'name' => 'tx_compressed'
330
    },
331
    'difference' => 'per_secund'
332
  },
333
  # --------------------------------------------------------------------------
334
  'rx_crc_errors' =>
335
  {
336
    'munin' =>
337
    {
338
      'type'  => 'GAUGE', 
339
      'draw'  => 'LINE1', 
340
      'label' => 'CRC errors' , 
341
      'info'  => 'CRC errors'
342
    },
343
    'source' =>
344
    {
345
      'type'     => 'file',
346
      'location' => $ifpath.'/:if:/statistics/rx_crc_errors'
347
    },
348
    'negative' =>
349
    {
350
      'type'  => 'dummy',
351 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
352 71bf7478 Gorlow Maxim aka Sheridan
    },
353
    'difference' => 'count'
354
  },
355
  # --------------------------------------------------------------------------
356
  'rx_dropped' =>
357
  {
358
    'munin' =>
359
    {
360
      'type'  => 'GAUGE',
361
      'draw'  => 'LINE1',
362
      'label' => 'RX dropped packets',
363
      'info'  => 'Dropped frames'
364
    },
365
    'source' =>
366
    {
367
      'type'     => 'file',
368
      'location' => $ifpath.'/:if:/statistics/rx_dropped'
369
    },
370
    'negative' =>
371
    {
372
      'type' => 'field',
373
      'name' => 'tx_dropped'
374
    },
375
    'difference' => 'per_secund'
376
  },
377
  # --------------------------------------------------------------------------
378
  'rx_errors' =>
379
  {
380
    'munin' =>
381
    {
382
      'type'  => 'GAUGE',
383
      'draw'  => 'LINE1',
384
      'label' => 'RX errors',
385
      'info'  => 'Bad packets'
386
    },
387
    'source' =>
388
    {
389
      'type'     => 'file',
390
      'location' => $ifpath.'/:if:/statistics/rx_errors'
391
    },
392
    'negative' =>
393
    {
394
      'type' => 'field',
395
      'name' => 'tx_errors'
396
    },
397
    'difference' => 'count'
398
  },
399
  # --------------------------------------------------------------------------
400
  'rx_fifo_errors' =>
401
  {
402
    'munin' =>
403
    {
404
      'type'  => 'GAUGE',
405
      'draw'  => 'LINE1',
406
      'label' => 'RX FIFO errors',
407
      'info'  => 'FIFO overrun errors'
408
    },
409
    'source' =>
410
    {
411
      'type'     => 'file',
412
      'location' => $ifpath.'/:if:/statistics/rx_fifo_errors'
413
    },
414
    'negative' =>
415
    {
416
      'type' => 'field',
417
      'name' => 'tx_fifo_errors'
418
    },
419
    'difference' => 'count'
420
  },
421
  # --------------------------------------------------------------------------
422
  'rx_frame_errors' =>
423
  {
424
    'munin' =>
425
    {
426
      'type'  => 'GAUGE',
427
      'draw'  => 'LINE1',
428
      'label' => 'Frame format errors',
429
      'info'  => 'Frame format errors'
430
    },
431
    'source' =>
432
    {
433
      'type'     => 'file',
434
      'location' => $ifpath.'/:if:/statistics/rx_frame_errors'
435
    },
436
    'negative' =>
437
    {
438
      'type'  => 'dummy',
439 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
440 71bf7478 Gorlow Maxim aka Sheridan
    },
441
    'difference' => 'count'
442
  },
443
  # --------------------------------------------------------------------------
444
  'rx_length_errors' =>
445
  {
446
    'munin' =>
447
    {
448
      'type'  => 'GAUGE',
449
      'draw'  => 'LINE1',
450
      'label' => 'Length errors',
451
      'info'  => 'Length errors'
452
    },
453
    'source' =>
454
    {
455
      'type'     => 'file',
456
      'location' => $ifpath.'/:if:/statistics/rx_length_errors'
457
    },
458
    'negative' =>
459
    {
460
      'type'  => 'dummy',
461 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
462 71bf7478 Gorlow Maxim aka Sheridan
    },
463
    'difference' => 'count'
464
  },
465
  # --------------------------------------------------------------------------
466
  'rx_missed_errors' =>
467
  {
468
    'munin' =>
469
    {
470
      'type'  => 'GAUGE',
471
      'draw'  => 'LINE1',
472
      'label' => 'Missed packetss',
473
      'info'  => 'Missed packets'
474
    },
475
    'source' =>
476
    {
477
      'type'     => 'file',
478
      'location' => $ifpath.'/:if:/statistics/rx_missed_errors'
479
    },
480
    'negative' =>
481
    {
482
      'type'  => 'dummy',
483 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
484 71bf7478 Gorlow Maxim aka Sheridan
    },
485
    'difference' => 'count'
486
  },
487
  # --------------------------------------------------------------------------
488
  'rx_over_errors' =>
489
  {
490
    'munin' =>
491
    {
492
      'type'  => 'GAUGE',
493
      'draw'  => 'LINE1',
494
      'label' => 'Overrun errors',
495
      'info'  => 'Overrun errors'
496
    },
497
    'source' =>
498
    {
499
      'type'     => 'file',
500
      'location' => $ifpath.'/:if:/statistics/rx_over_errors'
501
    },
502
    'negative' =>
503
    {
504
      'type'  => 'dummy',
505 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
506 71bf7478 Gorlow Maxim aka Sheridan
    },
507
    'difference' => 'count'
508
  },
509
  # --------------------------------------------------------------------------
510
  'rx_packets' =>
511
  {
512
    'munin' =>
513
    {
514
      'type'  => 'GAUGE',
515
      'draw'  => 'LINE1',
516
      'label' => 'RX packets',
517
      'info'  => 'RX packets'
518
    },
519
    'source' =>
520
    {
521
      'type'     => 'file',
522
      'location' => $ifpath.'/:if:/statistics/rx_packets'
523
    },
524
    'peack_protect' => 'max_interface_pps',
525
    'negative' =>
526
    {
527
      'type' => 'field',
528
      'name' => 'tx_packets'
529
    },
530
    'difference' => 'per_secund'
531
  },
532
  # --------------------------------------------------------------------------
533
  'tx_aborted_errors' =>
534
  {
535
    'munin' =>
536
    {
537
      'type'  => 'GAUGE',
538
      'draw'  => 'LINE1',
539
      'label' => 'Aborted frames',
540
      'info'  => 'Aborted frames'
541
    },
542
    'source' =>
543
    {
544
      'type'     => 'file',
545
      'location' => $ifpath.'/:if:/statistics/tx_aborted_errors'
546
    },
547
    'difference' => 'count'
548
  },
549
  # --------------------------------------------------------------------------
550
  'tx_bytes' =>
551
  {
552
    'munin' =>
553
    {
554
      'type'  => 'GAUGE',
555
      'draw'  => 'LINE1',
556 42fd0b1a Farhad Shahbazi
      'label' => 'TX bit',
557
      'info'  => 'TX bit'
558 71bf7478 Gorlow Maxim aka Sheridan
    },
559
    'source' =>
560
    {
561
      'type' => 'file',
562
      'location' => $ifpath.'/:if:/statistics/tx_bytes'
563
    },
564 42fd0b1a Farhad Shahbazi
    'cdef' => '8,*',
565 71bf7478 Gorlow Maxim aka Sheridan
    'peack_protect'  => 'max_interface_bps',
566
    'difference'     => 'per_secund'
567
  },
568
  # --------------------------------------------------------------------------
569
  'tx_carrier_errors' =>
570
  {
571
    'munin' =>
572
    {
573
      'type'  => 'GAUGE',
574
      'draw'  => 'LINE1',
575
      'label' => 'Carrier errors',
576
      'info'  => 'Carrier errors'
577
    },
578
    'source' =>
579
    {
580
      'type'     => 'file',
581
      'location' => $ifpath.'/:if:/statistics/tx_carrier_errors'
582
    },
583
    'difference' => 'count'
584
  },
585
  # --------------------------------------------------------------------------
586
  'tx_compressed' =>
587
  {
588
    'munin' =>
589
    {
590
      'type'  => 'GAUGE',
591
      'draw'  => 'LINE1',
592
      'label' => 'TX compressed packets',
593
      'info'  => 'Compressed packets'
594
    },
595
    'source' =>
596
    {
597
      'type'     => 'file',
598
      'location' => $ifpath.'/:if:/statistics/tx_compressed'
599
    },
600
    'peack_protect' => 'max_interface_pps',
601
    'difference'    => 'per_secund'
602
  },
603
  # --------------------------------------------------------------------------
604
  'tx_dropped' =>
605
  {
606
    'munin' =>
607
    {
608
      'type'  => 'GAUGE',
609
      'draw'  => 'LINE1',
610
      'label' => 'TX dropped packets',
611
      'info'  => 'Dropped frames'
612
    },
613
    'source' =>
614
    {
615
      'type'     => 'file',
616
      'location' => $ifpath.'/:if:/statistics/tx_dropped'
617
    },
618
    'difference' => 'per_secund'
619
  },
620
  # --------------------------------------------------------------------------
621
  'tx_errors' =>
622
  {
623
    'munin' =>
624
    {
625
      'type'  => 'GAUGE',
626
      'draw'  => 'LINE1',
627
      'label' => 'TX errors',
628
      'info'  => 'Transmit problems'
629
    },
630
    'source' =>
631
    {
632
      'type'     => 'file',
633
      'location' => $ifpath.'/:if:/statistics/tx_errors'
634
    },
635
    'difference' => 'count'
636
  },
637
  # --------------------------------------------------------------------------
638
  'tx_fifo_errors' =>
639
  {
640
    'munin' =>
641
    {
642
      'type'  => 'GAUGE',
643
      'draw'  => 'LINE1',
644
      'label' => 'TX FIFO errors',
645
      'info'  => 'FIFO errors'
646
    },
647
    'source' =>
648
    {
649
      'type'     => 'file',
650
      'location' => $ifpath.'/:if:/statistics/tx_fifo_errors'
651
    },
652
    'difference' => 'count'
653
  },
654
  # --------------------------------------------------------------------------
655
  'tx_heartbeat_errors' =>
656
  {
657
    'munin' =>
658
    {
659
      'type'  => 'GAUGE',
660
      'draw'  => 'LINE1',
661
      'label' => 'Heartbeat errors',
662
      'info'  => 'Heartbeat errors'
663
    },
664
    'source' =>
665
    {
666
      'type'     => 'file',
667
      'location' => $ifpath.'/:if:/statistics/tx_heartbeat_errors'
668
    },
669
    'difference' => 'count'
670
  },
671
  # --------------------------------------------------------------------------
672
  'tx_packets' =>
673
  {
674
    'munin' =>
675
    {
676
      'type'  => 'GAUGE',
677
      'draw'  => 'LINE1',
678
      'label' => 'TX packets',
679
      'info'  => 'TX packets'
680
    },
681
    'source' =>
682
    {
683
      'type'     => 'file',
684
      'location' => $ifpath.'/:if:/statistics/tx_packets'
685
    },
686
    'peack_protect' => 'max_interface_pps',
687
    'difference'    => 'per_secund'
688
  },
689
  # --------------------------------------------------------------------------
690
  'tx_window_errors' =>
691
  {
692
    'munin' =>
693
    {
694
      'type'  => 'GAUGE',
695
      'draw'  => 'LINE1',
696
      'label' => 'Window errors',
697
      'info'  => 'Window errors'
698
    },
699
    'source' =>
700
    {
701
      'type'     => 'file',
702
      'location' => $ifpath.'/:if:/statistics/tx_window_errors'
703
    },
704
    'difference' => 'count'
705
  },
706
  # --------------------------------------------------------------------------
707
  'signal' =>
708
  {
709
    'munin' =>
710
    {
711
      'type'  => 'GAUGE',
712
      'draw'  => 'LINE1',
713
      'label' => 'Signal level',
714
      'info'  => 'WiFi signal level'
715
    },
716
    'source' =>
717
    {
718
      'type'     => 'file',
719
      'location' => $ifpath.'/:if:/wireless/level',
720
      'prepare'  => ':data:=:data:-256'
721
    },
722
#    'cdef'  => '-256,+',
723
    'peack_protect'  => '-256:0'
724
  },
725
  # --------------------------------------------------------------------------
726
  'noise' =>
727
  {
728
    'munin' =>
729
    {
730
      'type'  => 'GAUGE',
731
      'draw'  => 'LINE1',
732
      'label' => 'Noise level',
733
      'info'  => 'WiFi noise level'
734
    },
735
    'source' =>
736
    {
737
      'type'     => 'file',
738
      'location' => $ifpath.'/:if:/wireless/noise',
739
      'prepare'  => ':data:=:data:-256'
740
    },
741
#    'cdef'  => '-256,+',
742
    'peack_protect' => '-256:0'
743
  },
744
  # --------------------------------------------------------------------------
745
  'link' =>
746
  {
747
    'munin' =>
748
    {
749
      'type'  => 'GAUGE',
750
      'draw'  => 'LINE1',
751
      'label' => 'Signal quality',
752
      'info'  => 'WiFi signal quality'
753
    },
754
    'source' =>
755
    {
756
      'type'     => 'file',
757
      'location' => $ifpath.'/:if:/wireless/link'
758
    },
759
    'peack_protect' => 'percent'
760
  },
761
  # --------------------------------------------------------------------------
762
  'rx_percent' =>
763
  {
764
    'munin' =>
765
    {
766
      'type'  => 'GAUGE',
767
      'draw'  => 'LINE1',
768
      'label' => 'RX Utilisation',
769
      'info'  => 'RX utilisation'
770
    },
771
    'source' =>
772
    {
773
      'type' => 'calculated',
774
      'calculated' =>
775
      {
776
        'type' => 'percent',
777
        'full' => 
778
        {
779
          'source' => 'interface',
780
          'name'   => 'bps'
781
        },
782
        'part' => 
783
        {
784
          'source' => 'field',
785
          'name'   => 'rx_bytes'
786
        }
787
      }
788
    },
789
    'peack_protect' => 'percent',
790
    'negative' =>
791
    {
792
      'type' => 'field',
793
      'name' => 'tx_percent'
794
    }
795
  },
796
  # --------------------------------------------------------------------------
797
  'tx_percent' =>
798
  {
799
    'munin' =>
800
    {
801
      'type'  => 'GAUGE',
802
      'draw'  => 'LINE1',
803
      'label' => 'TX Utilisation',
804
      'info'  => 'TX utilisation'
805
    },
806
    'source' =>
807
    {
808
      'type' => 'calculated',
809
      'calculated' =>
810
      {
811
        'type' => 'percent',
812
        'full' => 
813
        {
814
          'source' => 'interface',
815
          'name'   => 'bps'
816
        },
817
        'part' => 
818
        {
819
          'source' => 'field',
820
          'name'   => 'tx_bytes'
821
        }
822
      }
823
    },
824
    'peack_protect' => 'percent'
825
  },
826
  # --------------------------------------------------------------------------
827
  'rx_size' =>
828
  {
829
    'munin' =>
830
    {
831
      'type'  => 'GAUGE',
832
      'draw'  => 'LINE1',
833
      'label' => 'RX packet size',
834
      'info'  => 'Average RX packet size'
835
    },
836
    'source' =>
837
    {
838
      'type' => 'calculated',
839
      'calculated' =>
840
      {
841
        'type' => 'division',
842
        'dividend' =>
843
        {
844
          'source' => 'field',
845
          'name'   => 'rx_bytes'
846
        },
847
        'divider' =>
848
        {
849
          'source' => 'field',
850
          'name'   => 'rx_packets'
851
        }
852
      }
853
    },
854
    'peack_protect' => 'packet_size_range',
855
    'negative' =>
856
    {
857
      'type' => 'field',
858
      'name' => 'tx_size'
859
    }
860
  },
861
  # --------------------------------------------------------------------------
862
  'tx_size' =>
863
  {
864
    'munin' =>
865
    {
866
      'type'  => 'GAUGE',
867
      'draw'  => 'LINE1',
868
      'label' => 'TX packet size',
869
      'info'  => 'Average TX packet size'
870
    },
871
    'source' =>
872
    {
873
      'type' => 'calculated',
874
      'calculated' =>
875
      {
876
        'type' => 'division',
877
        'dividend' =>
878
        {
879
          'source' => 'field',
880
          'name'   => 'tx_bytes'
881
        },
882
        'divider' =>
883
        {
884
          'source' => 'field',
885
          'name'   => 'tx_packets'
886
        }
887
      }
888
    },
889
    'peack_protect' => 'packet_size_range'
890
  },
891
  # --------------------------------------------------------------------------
892
  'retries' => 
893
  {
894
    'munin' =>
895
    {
896
      'type'  => 'GAUGE',
897
      'draw'  => 'LINE1',
898
      'label' => 'Max. retries reached',
899
      'info'  => 'Max MAC retries num reached'
900
    },
901
    'source' =>
902
    {
903
      'type'     => 'file',
904
      'location' => $ifpath.'/:if:/wireless/retries'
905
    },
906
    'difference' => 'count'
907
  },
908
  # --------------------------------------------------------------------------
909
  'nwid' => 
910
  {
911
    'munin' =>
912
    {
913
      'type'  => 'GAUGE',
914
      'draw'  => 'LINE1',
915
      'label' => 'Wrong nwid/essid',
916
      'info'  => 'Wrong nwid/essid'
917
    },
918
    'source' =>
919
    {
920
      'type'     => 'file',
921
      'location' => $ifpath.'/:if:/wireless/nwid'
922
    },
923
    'negative' =>
924
    {
925
      'type'  => 'dummy',
926 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
927 71bf7478 Gorlow Maxim aka Sheridan
    },
928
    'difference' => 'count'
929
  },
930
  # --------------------------------------------------------------------------
931
  'misc' => 
932
  {
933
    'munin' =>
934
    {
935
      'type'  => 'GAUGE',
936
      'draw'  => 'LINE1',
937
      'label' => 'Other',
938
      'info'  => 'Others cases'
939
    },
940
    'source' =>
941
    {
942
      'type'     => 'file',
943
      'location' => $ifpath.'/:if:/wireless/misc'
944
    },
945
    'difference' => 'count'
946
  },
947
  # --------------------------------------------------------------------------
948
  'fragment' => 
949
  {
950
    'munin' =>
951
    {
952
      'type'  => 'GAUGE',
953
      'draw'  => 'LINE1',
954
      'label' => 'MAC reassemby',
955
      'info'  => 'Can\'t perform MAC reassembly'
956
    },
957
    'source' =>
958
    {
959
      'type'     => 'file',
960
      'location' => $ifpath.'/:if:/wireless/fragment'
961
    },
962
    'negative' =>
963
    {
964
      'type'  => 'dummy',
965 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
966 71bf7478 Gorlow Maxim aka Sheridan
    },
967
    'difference' => 'count'
968
  },
969
  # --------------------------------------------------------------------------
970
  'beacon' => 
971
  {
972
    'munin' =>
973
    {
974
      'type'  => 'GAUGE',
975
      'draw'  => 'LINE1',
976
      'label' => 'Missed beacons',
977
      'info'  => 'Missed beacons/superframe'
978
    },
979
    'source' =>
980
    {
981
      'type'     => 'file',
982
      'location' => $ifpath.'/:if:/wireless/beacon'
983
    },
984
    'difference' => 'count'
985
  },
986
  # --------------------------------------------------------------------------
987
  'crypt' => 
988
  {
989
    'munin' =>
990
    {
991
      'type'  => 'GAUGE',
992
      'draw'  => 'LINE1',
993
      'label' => 'Code/decode',
994
      'info'  => 'Unable to code/decode (WEP)'
995
    },
996
    'source' =>
997
    {
998
      'type'     => 'file',
999
      'location' => $ifpath.'/:if:/wireless/crypt'
1000
    },
1001
    'negative' =>
1002
    {
1003
      'type'  => 'dummy',
1004 b6024a55 Gorlow Maxim aka Sheridan
      'value' => 'NaN'
1005 71bf7478 Gorlow Maxim aka Sheridan
    },
1006
    'difference' => 'count'
1007
  },
1008
  # --------------------------------------------------------------------------
1009
  'rx_wifierr' => 
1010
  {
1011
    'munin' =>
1012
    {
1013
      'type'  => 'GAUGE',
1014
      'draw'  => 'LINE1',
1015
      'label' => 'RX errors',
1016
      'info'  => 'Total RX Wifi Errors'
1017
    },
1018
    'source' =>
1019
    {
1020
      'type' => 'calculated',
1021
      'calculated' =>
1022
      {
1023
        'type' => 'sum',
1024
        'sum'  => [qw(nwid fragment crypt)]
1025
      }
1026
    },
1027
    'negative' =>
1028
    {
1029
      'type' => 'field',
1030
      'name' => 'tx_wifierr'
1031
    }
1032
  },
1033
  # --------------------------------------------------------------------------
1034
  'tx_wifierr' => 
1035
  {
1036
    'munin' =>
1037
    {
1038
      'type'  => 'GAUGE',
1039
      'draw'  => 'LINE1',
1040
      'label' => 'TX errors',
1041
      'info'  => 'Total TX Wifi errors'
1042
    },
1043
    'source' =>
1044
    {
1045
      'type' => 'calculated',
1046
      'calculated' =>
1047
      {
1048
        'type' => 'sum',
1049
        'sum'  => [qw(misc beacon retries)]
1050
      }
1051
    }
1052
  }
1053
};
1054
1055
# ----------------- main ----------------
1056
1057
need_multigraph();
1058
1059
if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) 
1060
{
1061
  printf("%s\n", -e $ifpath ? "yes" : "no ($ifpath not exists)");
1062
  exit (0);
1063
}
1064
$interfaces = get_interfaces();
1065
if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) 
1066
{
1067
  print_config();
1068
  exit (0);
1069
}
1070
print_values();
1071
exit(0);
1072
1073
1074
# ====================================== both config and values ===========================
1075
# ---------------  read sysfs file (one file - one value) --------------
1076
sub get_file_content
1077
{
1078
  my $file = $_[0];
1079 b6024a55 Gorlow Maxim aka Sheridan
  return 'NaN' if (-z $file);
1080 71bf7478 Gorlow Maxim aka Sheridan
  open (FH, '<', $file) or die "$! $file \n";
1081
  my $content = <FH>;
1082
  close (FH);
1083
  chomp $content;
1084
  #print "$content\n";
1085
  return trim($content);
1086
}
1087
1088
# ------------------ build interface list and his options -------------------------
1089
sub get_interfaces
1090
{
1091
  my $interfaces;
1092
  my $ifdir = IO::Dir->new($ifpath);
1093
  if(defined $ifdir)
1094
  {
1095
    my $if;
1096
    while (defined ($if = $ifdir->read))
1097
    {
1098
      next if $if =~ m/\./;
1099
      unless($if =~ m/$include/)
1100
      {
1101
       next unless get_file_content(sprintf("%s/%s/operstate", $ifpath, $if)) =~ m/(up|unknown)/;
1102
       next if $exclude =~ m/$if/;
1103
      }
1104
      my $mtufile = sprintf("%s/%s/mtu", $ifpath, $if);
1105
      if(-e $mtufile)
1106
      {
1107
        $interfaces->{$if}{'mtu'} = get_file_content($mtufile);
1108
      }
1109
      my $bps = $ENV{"${if}_max_bps"} || undef;
1110
      if(defined($bps))
1111
      {
1112
        my ($num, $suff) = $bps =~ /(\d+)(\w)/;
1113
        if   ($suff eq 'k') { $bps = $num * 1000 / 8; }
1114
        elsif($suff eq 'M') { $bps = $num * 1000 * 1000 / 8; }
1115
        elsif($suff eq 'G') { $bps = $num * 1000 * 1000 * 1000 / 8; }
1116
        $interfaces->{$if}{'bps'} = $bps;
1117
      }
1118 b6024a55 Gorlow Maxim aka Sheridan
      if (-e sprintf("/sys/devices/virtual/net/%s", $if)) { $interfaces->{$if}{'virtual'} = 1; }
1119
      $interfaces->{$if}{'name'} = exists($interfaces->{$if}{'virtual'}) ? sprintf("~%s", $if) : $if;
1120 71bf7478 Gorlow Maxim aka Sheridan
    }
1121 b6024a55 Gorlow Maxim aka Sheridan
    my ($maxlen, $tl) = (0, 0);
1122
    for (keys %{$interfaces}) { $tl = length($interfaces->{$_}{'name'}); $maxlen = $tl if $tl > $maxlen;  }
1123
    for (keys %{$interfaces}) { $interfaces->{$_}{'name'} = sprintf("[%${maxlen}s]", $interfaces->{$_}{'name'}); }
1124 71bf7478 Gorlow Maxim aka Sheridan
  }
1125 b6024a55 Gorlow Maxim aka Sheridan
  else { die "$ifpath not exists\n"; }
1126
  
1127 71bf7478 Gorlow Maxim aka Sheridan
  return $interfaces;
1128
}
1129
1130
# ----------------------- trim whitespace at begin and end of string ------------
1131
sub trim
1132
{
1133
  my($string)=@_;
1134
  for ($string) { s/^\s+//; s/\s+$//; }
1135
  return $string;
1136
}
1137
1138
# ------------------------ replacing :if: from strings to need value ----------------------
1139
sub replace_if_template
1140
{
1141
  my ($string, $replacement) = @_[0..1];
1142
  $string =~ s/:if:/$replacement/g;
1143
  return $string;
1144
}
1145
1146
# --------------------------- calculating range values for peack_protect --------------------------
1147
sub get_peak_range
1148
{
1149
  my ($field, $if) = @_[0..1];
1150
  my $range = {};
1151
  return $range unless defined($fields->{$field}{'peack_protect'});
1152
  # percent
1153
  if ($fields->{$field}{'peack_protect'} eq 'percent')
1154
  {
1155
    $range->{'max'} = 100;
1156
    $range->{'min'} = 0;
1157
  }
1158
  # numbers
1159
  elsif ($fields->{$field}{'peack_protect'} =~ m/[-\d]+:[-\d]+/)
1160
  {
1161
    my @r = split(/:/, $fields->{$field}{'peack_protect'});
1162
    $range->{'max'} = $r[1];
1163
    $range->{'min'} = $r[0];
1164
  }
1165
  # bytes per sec
1166
  elsif($fields->{$field}{'peack_protect'} eq 'max_interface_bps' and defined ($interfaces->{$if}{'bps'}))
1167
  {
1168
    $range->{'max'} = $interfaces->{$if}{'bps'};
1169
    $range->{'min'} = 0;
1170
  }
1171
  # packets per sec 
1172
  elsif($fields->{$field}{'peack_protect'} eq 'max_interface_pps' and defined ($interfaces->{$if}{'bps'}))
1173
  {
1174
    $range->{'max'} = $interfaces->{$if}{'bps'}/$min_packet_size;
1175
    $range->{'min'} = 0;
1176
  }
1177
  # packets size range
1178
  elsif($fields->{$field}{'peack_protect'} eq 'packet_size_range' and defined ($interfaces->{$if}{'mtu'}))
1179
  {
1180
    $range->{'max'} = $interfaces->{$if}{'mtu'};
1181
    $range->{'min'} = $min_packet_size;
1182
  }
1183
  return $range;
1184
}
1185
1186
1187
# ----------------------------- checking avialability of fields -------------------------
1188
sub check_field_avialability
1189
{
1190
  my ($if, $field) = @_[0..1];
1191
  unless(exists($fields->{$field}{'avialable'}{$if}))
1192
  {
1193
    # -------------------- file ----------------
1194
    if($fields->{$field}{'source'}{'type'} eq 'file')
1195
    {
1196
      my $file = $fields->{$field}{'source'}{'location'};
1197
      $file =~ s/:if:/$if/g;
1198
      $fields->{$field}{'avialable'}{$if} = 1 if (-e $file);
1199
    }
1200
    #---------------------------- calculated ----------------
1201
    elsif ($fields->{$field}{'source'}{'type'} eq 'calculated')
1202
    {
1203
      #------------------------------ percent ------------------------
1204
      if($fields->{$field}{'source'}{'calculated'}{'type'} eq 'percent')
1205
      {
1206
        my %avialable;
1207 b6024a55 Gorlow Maxim aka Sheridan
        for my $a ('full', 'part')
1208 71bf7478 Gorlow Maxim aka Sheridan
        {
1209
          if($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'interface')
1210
          {
1211
            $avialable{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}});
1212
          }
1213
          elsif($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'field')
1214
          {
1215
            $avialable{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'});
1216
          }
1217
        }
1218
        $fields->{$field}{'avialable'}{$if} = ($avialable{'full'} and $avialable{'part'});
1219
      }
1220
      #------------------------------ division ------------------------
1221
      elsif($fields->{$field}{'source'}{'calculated'}{'type'} eq 'division')
1222
      {
1223
        my %avialable;
1224 b6024a55 Gorlow Maxim aka Sheridan
        for my $a ('dividend', 'divider')
1225 71bf7478 Gorlow Maxim aka Sheridan
        {
1226
          if($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'interface')
1227
          {
1228
            $avialable{$a} = exists($interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$a}{'name'}});
1229
          }
1230
          elsif($fields->{$field}{'source'}{'calculated'}{$a}{'source'} eq 'field')
1231
          {
1232
            $avialable{$a} = check_field_avialability($if, $fields->{$field}{'source'}{'calculated'}{$a}{'name'});
1233
          }
1234
        }
1235
        $fields->{$field}{'avialable'}{$if} = ($avialable{'dividend'} and $avialable{'divider'});
1236
      }
1237
      #------------------------------ sum ------------------------
1238
      elsif($fields->{$field}{'source'}{'calculated'}{'type'} eq 'sum')
1239
      {
1240
        my $count = 0;
1241 b6024a55 Gorlow Maxim aka Sheridan
        for my $a (@{$fields->{$field}{'source'}{'calculated'}{'sum'}})
1242 71bf7478 Gorlow Maxim aka Sheridan
        {
1243
          $count++ if (check_field_avialability($if, $a));
1244
        }
1245
        $fields->{$field}{'avialable'}{$if} = ($count == scalar(@{$fields->{$field}{'source'}{'calculated'}{'sum'}}));
1246
      }
1247
    }
1248
  }
1249
  return $fields->{$field}{'avialable'}{$if};
1250
}
1251
1252
1253
# ==================================  config-only ==============================
1254
# --------------- concatenate field names ------------------
1255
sub concat_names
1256
{
1257
  my ($f1, $f2, $if) = @_[0..2];
1258
  my $name = $f1;
1259
  if ($f1 ne $f2)
1260
  {
1261
    my @a = split(' ', $f1);
1262
    my @b = split(' ', $f2);
1263
    my ($t, $ra, $rb) = ('','','');
1264
    for (my $i = scalar(@a) - 1; $i >= 0; $i--)
1265
    {
1266
      #printf("%s %s\n", $a[$i], $b[$i]);
1267
      if   ($a[$i] eq $b[$i]) { $t = sprintf("%s %s", $a[$i], $t); }
1268
      else { $ra = sprintf("%s %s", $a[$i], $ra); $rb = sprintf("%s %s", $b[$i], $rb); }
1269
    }
1270
    $name = trim(sprintf("%s/%s %s", trim($ra), trim($rb), trim($t)));
1271
  }
1272
  if (exists($interfaces->{$if}))
1273
  {
1274 b6024a55 Gorlow Maxim aka Sheridan
    $name = sprintf ("%s %s", $interfaces->{$if}{'name'}, $name);
1275 71bf7478 Gorlow Maxim aka Sheridan
  }
1276
  return $name;
1277
}
1278
1279
# --------------------------- generating graph field ----------------------
1280
sub generate_field
1281
{
1282
  my ($config, $graph_name, $field, $if, $is_general_graph) = @_[0..4];
1283
  return '' unless(check_field_avialability($if, $field));
1284
  my $field_graph_name = $is_general_graph ? sprintf("%s_%s", $if, $field) : $field;
1285 b6024a55 Gorlow Maxim aka Sheridan
  for my $option (keys %{$fields->{$field}{'munin'}}) 
1286 71bf7478 Gorlow Maxim aka Sheridan
  {
1287
    next if exists($config->{$graph_name}{'fields'}{$field_graph_name}{$option});
1288 b6024a55 Gorlow Maxim aka Sheridan
    $config->{$graph_name}{'fields'}{$field_graph_name}{$option} = replace_if_template($fields->{$field}{'munin'}{$option}, $interfaces->{$if}{'name'}); 
1289 71bf7478 Gorlow Maxim aka Sheridan
  }
1290
  if(exists($fields->{$field}{'cdef'}))
1291
  {
1292
    $config->{$graph_name}{'fields'}{$field_graph_name}{'cdef'} = sprintf("%s,%s", $field_graph_name, $fields->{$field}{'cdef'});
1293
  }
1294
  if(exists($fields->{$field}{'negative'}))
1295
  {
1296
    my ($up_field_name, $down_field_name) = ('', $field_graph_name);
1297
    my ($up_field, $down_field) = ('', $field);
1298
    if($fields->{$down_field}{'negative'}{'type'} eq 'field')
1299
    {
1300
      $up_field = $fields->{$down_field}{'negative'}{'name'};
1301
      $up_field_name = $is_general_graph ? sprintf("%s_%s", $if, $up_field) : $up_field;
1302
      $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = 
1303
        concat_names($fields->{$down_field}{'munin'}{'label'}, $fields->{$up_field}{'munin'}{'label'}, $is_general_graph ? $if : '');
1304
    }
1305
    elsif($fields->{$down_field}{'negative'}{'type'} eq 'dummy')
1306
    {
1307
      $up_field_name = $is_general_graph ? sprintf("%s_%s_dummy", $if, $down_field) : sprintf("%s_dummy", $down_field);
1308
      $config->{$graph_name}{'fields'}{$up_field_name}{'label'} = 
1309
        concat_names($fields->{$down_field}{'munin'}{'label'}, $fields->{$down_field}{'munin'}{'label'}, $is_general_graph ? $if : '');
1310
      $config->{$graph_name}{'fields'}{$up_field_name}{'info'} = $fields->{$down_field}{'munin'}{'info'};
1311
    }
1312
    $config->{$graph_name}{'fields'}{$up_field_name}{'negative'} = $down_field_name;
1313
    $config->{$graph_name}{'fields'}{$down_field_name}{'graph'} = 'no';
1314
    $config->{$graph_name}{'fields'}{$down_field_name}{'label'} = 'none';
1315
  }
1316
  # Fix field label on general graphs
1317
  if ($is_general_graph and not $config->{$graph_name}{'fields'}{$field_graph_name}{'label'} =~ m/$if/)
1318
  {
1319 b6024a55 Gorlow Maxim aka Sheridan
    $config->{$graph_name}{'fields'}{$field_graph_name}{'label'} = sprintf ("%s %s", $interfaces->{$if}{'name'}, $fields->{$field}{'munin'}{'label'});
1320 71bf7478 Gorlow Maxim aka Sheridan
  }
1321
  # do peaks protect
1322
  if($protect_peacks ne 'no' and exists($fields->{$field}{'peack_protect'}))
1323
  {
1324
    my $range = get_peak_range($field, $if);
1325 b6024a55 Gorlow Maxim aka Sheridan
    for my $a (qw(min max))
1326 71bf7478 Gorlow Maxim aka Sheridan
    {
1327
      if (exists($range->{$a}))
1328
      {
1329
        $config->{$graph_name}{'fields'}{$field_graph_name}{$a} = $range->{$a};
1330
      }
1331
    }
1332
  }
1333
  return $field_graph_name;
1334
}
1335
1336
# ------------------------------- generating graph ----------------------------
1337
sub generate_graph
1338
{
1339
  my ($config, $graph, $if, $is_general_graph) = @_[0..4];
1340
  my @order = ();
1341
  my $graph_name = $is_general_graph ? $graph : sprintf("%s.%s", $graph, $if);
1342
  if($is_general_graph)
1343
  {
1344 b6024a55 Gorlow Maxim aka Sheridan
    for my $field (@{$graphs->{$graph}{'general_fields'}})
1345 71bf7478 Gorlow Maxim aka Sheridan
    {
1346 b6024a55 Gorlow Maxim aka Sheridan
      for my $general_if (keys %{$interfaces}) 
1347 71bf7478 Gorlow Maxim aka Sheridan
      {
1348
        my $res_field = generate_field($config, $graph_name, $field, $general_if, 1);
1349
        push(@order, $res_field) if $res_field ne '';
1350
      }
1351
    }
1352
  }
1353
  else
1354
  {
1355 b6024a55 Gorlow Maxim aka Sheridan
    for my $field (@{$graphs->{$graph}{'per_if_fields'}})
1356 71bf7478 Gorlow Maxim aka Sheridan
    {
1357
      my $res_field = generate_field($config, $graph_name, $field, $if, 0);
1358
      push(@order, $res_field) if $res_field ne '';
1359
    }
1360
  }
1361
  if(scalar(@order) > 0)
1362
  {
1363 b6024a55 Gorlow Maxim aka Sheridan
    for my $option (keys %{$graphs->{$graph}{'munin'}}) 
1364 71bf7478 Gorlow Maxim aka Sheridan
    {
1365 b6024a55 Gorlow Maxim aka Sheridan
      $config->{$graph_name}{'graph'}{$option} = replace_if_template($graphs->{$graph}{'munin'}{$option}, $is_general_graph ? 'All interfaces' : $interfaces->{$if}{'name'}); 
1366 71bf7478 Gorlow Maxim aka Sheridan
    }
1367
    $config->{$graph_name}{'graph'}{'order'} = join(' ', @order); # if scalar(@order) > 1;
1368
    unless($is_general_graph)
1369
    {
1370
      $config->{$graph_name}{'graph'}{'category'} = $if;
1371
    }
1372
  }
1373
}
1374
1375
# ------------------------ generate general and per-interface graphs ------------------------------
1376
sub generate_graphs
1377
{
1378
  my ($config, $graph) = @_[0..1];
1379
  generate_graph($config, $graph, '', 1);
1380 b6024a55 Gorlow Maxim aka Sheridan
  for my $if (keys %{$interfaces}) 
1381 71bf7478 Gorlow Maxim aka Sheridan
  {
1382
    generate_graph($config, $graph, $if, 0);
1383
  }
1384
}
1385
1386
# ---------------------------------------------------------- config ------------------------------------------------------
1387
sub print_config
1388
{
1389
  my $config = {};
1390
  my $graph;
1391 b6024a55 Gorlow Maxim aka Sheridan
  for $graph (keys %{$graphs}) { generate_graphs($config, $graph); }
1392 71bf7478 Gorlow Maxim aka Sheridan
  #-------------------- print ---------------
1393 b6024a55 Gorlow Maxim aka Sheridan
  for $graph (sort  keys %{$config})
1394 71bf7478 Gorlow Maxim aka Sheridan
  {
1395
    printf ("multigraph %s\n", $graph);
1396 b6024a55 Gorlow Maxim aka Sheridan
    for my $option (sort keys %{$config->{$graph}{'graph'}})
1397 71bf7478 Gorlow Maxim aka Sheridan
    {
1398
      printf ("graph_%s %s\n", $option, $config->{$graph}{'graph'}{$option});
1399
    }
1400 b6024a55 Gorlow Maxim aka Sheridan
    for my $field (sort keys %{$config->{$graph}{'fields'}})
1401 71bf7478 Gorlow Maxim aka Sheridan
    {
1402 b6024a55 Gorlow Maxim aka Sheridan
      for my $type (sort keys %{$config->{$graph}{'fields'}{$field}})
1403 71bf7478 Gorlow Maxim aka Sheridan
      {
1404
        printf ("%s.%s %s\n", $field, $type, $config->{$graph}{'fields'}{$field}{$type});
1405
      }
1406
    }
1407
    print "\n";
1408
  }
1409
}
1410
1411
# ===========================================  values ==========================================================
1412
# ------------------------------- calculate percent --------------------------
1413
sub percent
1414
{
1415
  my ($full, $current) = @_[0..1];
1416
  return $current/($full/100);
1417
}
1418
1419
# ----------------------------------- saving state data using munin --------------------
1420
sub save_state_data
1421
{
1422
  my $data = $_[0];
1423
  my $d = Data::Dumper->new([$data]);
1424
  $d->Indent(0);
1425
  save_state($d->Dump);
1426
}
1427
1428
# -------------------------------- loading previous state data using munin -------------------
1429
sub restore_state_data
1430
{
1431
  my $VAR1;
1432
  my $states = (restore_state())[0];
1433
  eval $states if defined $states;
1434
  return $VAR1;
1435
}
1436
1437
# -------------------- protect field data from under zero value (for example prev tx_bytes = 10000, interface reset, current tx_bytes = 100, 100-1000=-900)
1438
sub underzero_protect
1439
{
1440
  my ($a, $b) = @_[0..1];
1441
  return $a > $b ? $b : $b - $a;
1442
}
1443
1444
# ------------------- calculating difference from last stored data ---------------------------------
1445
sub calc_diff
1446
{
1447
  my ($raw_data, $raw_prev_data, $if, $field) = @_[0..4];
1448
  return $raw_data->{$if}{$field} unless (exists($fields->{$field}{'difference'}) and defined($raw_prev_data));
1449
  if    ($fields->{$field}{'difference'} eq 'count'     ) { return underzero_protect($raw_prev_data->{$if}{$field}, $raw_data->{$if}{$field}); }
1450
  elsif ($fields->{$field}{'difference'} eq 'per_secund') { return underzero_protect($raw_prev_data->{$if}{$field}, $raw_data->{$if}{$field}) / ($raw_data->{'timestamp'} - $raw_prev_data->{'timestamp'}); }
1451
}
1452
1453
# ---------------------- protecting values from peaks ------------------------
1454
sub protect_data_peak
1455
{
1456
  my ($field, $if, $value) = @_[0..2];
1457
  my $range = get_peak_range($field, $if);
1458
  return $value if (
1459
                     $protect_peacks ne 'no'    or
1460
                     (
1461 b6024a55 Gorlow Maxim aka Sheridan
                      $value ne 'NaN'           and
1462 71bf7478 Gorlow Maxim aka Sheridan
                      exists($range->{'max'})   and
1463
                      $value <= $range->{'max'} and
1464
                      $value >= $range->{'min'}
1465
                     )
1466
                   );
1467 b6024a55 Gorlow Maxim aka Sheridan
  return 'NaN';
1468 71bf7478 Gorlow Maxim aka Sheridan
}
1469
1470
# --------------------------------- loading or calculating fields values ----------------------------
1471
sub get_field_data
1472
{
1473
  my ($data, $raw_data, $raw_prev_data, $if, $field) = @_[0..4];
1474
  unless (exists($data->{$if}{$field}))
1475
  {
1476
    # ----------------------------  file source ------------------------------------------------------------
1477
    if($fields->{$field}{'source'}{'type'} eq 'file' and not exists($raw_data->{$if}{$field}))
1478
    {
1479 b6024a55 Gorlow Maxim aka Sheridan
      $raw_data->{$if}{$field} = get_file_content(replace_if_template($fields->{$field}{'source'}{'location'}, $if));
1480 71bf7478 Gorlow Maxim aka Sheridan
      $data->{$if}{$field} = calc_diff($raw_data, $raw_prev_data, $if, $field);
1481
    }
1482
    # ----------------------------  calculated source ------------------------------------------------------------
1483
    elsif($fields->{$field}{'source'}{'type'} eq 'calculated')
1484
    {
1485
      # -------------------------------- percent ---------------------------
1486
      if($fields->{$field}{'source'}{'calculated'}{'type'} eq 'percent')
1487
      {
1488
        my $percents = {};
1489 b6024a55 Gorlow Maxim aka Sheridan
        for my $pf (qw(full part))
1490 71bf7478 Gorlow Maxim aka Sheridan
        {
1491
          if ($fields->{$field}{'source'}{'calculated'}{$pf}{'source'} eq 'interface')
1492
          {
1493
            $percents->{$pf} = $interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$pf}{'name'}};
1494
          }
1495
          elsif ($fields->{$field}{'source'}{'calculated'}{$pf}{'source'} eq 'field')
1496
          {
1497
            $percents->{$pf} = get_field_data($data, $raw_data, $raw_prev_data, $if, $fields->{$field}{'source'}{'calculated'}{$pf}{'name'});
1498
          }
1499
        }
1500
        $data->{$if}{$field} = percent($percents->{'full'}, $percents->{'part'});
1501
      }
1502
      # -------------------------------- division ---------------------------
1503
      if($fields->{$field}{'source'}{'calculated'}{'type'} eq 'division')
1504
      {
1505
        my $division = {};
1506 b6024a55 Gorlow Maxim aka Sheridan
        for my $df (qw(dividend divider))
1507 71bf7478 Gorlow Maxim aka Sheridan
        {
1508
          if ($fields->{$field}{'source'}{'calculated'}{$df}{'source'} eq 'interface')
1509
          {
1510
            $division->{$df} = $interfaces->{$if}{$fields->{$field}{'source'}{'calculated'}{$df}{'name'}};
1511
          }
1512
          elsif ($fields->{$field}{'source'}{'calculated'}{$df}{'source'} eq 'field')
1513
          {
1514
            $division->{$df} = get_field_data($data, $raw_data, $raw_prev_data, $if, $fields->{$field}{'source'}{'calculated'}{$df}{'name'});
1515
          }
1516
        }
1517 b6024a55 Gorlow Maxim aka Sheridan
        $data->{$if}{$field} = $division->{'divider'} != 0 ? $division->{'dividend'}/$division->{'divider'} : 'NaN';
1518 71bf7478 Gorlow Maxim aka Sheridan
      }
1519
      # -------------------------------- sum ---------------------------
1520
      if($fields->{$field}{'source'}{'calculated'}{'type'} eq 'sum')
1521
      {
1522
        my $sum = 0;
1523 b6024a55 Gorlow Maxim aka Sheridan
        for my $s (@{$fields->{$field}{'source'}{'calculated'}{'sum'}})
1524 71bf7478 Gorlow Maxim aka Sheridan
        {
1525
          $sum += get_field_data($data, $raw_data, $raw_prev_data, $if, $s);
1526
        }
1527
        $data->{$if}{$field} = $sum;
1528
      }
1529
    }
1530
    if(exists($fields->{$field}{'source'}{'prepare'}))
1531
    {
1532
      my $eval = $fields->{$field}{'source'}{'prepare'};
1533
      $eval =~ s/:data:/\$data->{\$if}{\$field}/g;
1534
      eval $eval;
1535
    }
1536
    $data->{$if}{$field} = protect_data_peak($field, $if, $data->{$if}{$field});
1537
  }
1538
  return $data->{$if}{$field};
1539
}
1540
1541
# ------------------------- preparing value for print ----------------------------
1542
sub prepare_value
1543
{
1544
  my ($values, $field, $field_name, $graph_name, $if, $data, $raw_data, $raw_prev_data) = @_[0..7];
1545
  if(check_field_avialability($if, $field))
1546
  {
1547
    $values->{$graph_name}{$field_name} = get_field_data($data, $raw_data, $raw_prev_data, $if, $field);
1548
    if(exists($fields->{$field}{'negative'}) and $fields->{$field}{'negative'}{'type'} eq 'dummy')
1549
    {
1550
      $values->{$graph_name}{$field_name.'_dummy'} = $fields->{$field}{'negative'}{'value'};
1551
    }
1552
  }
1553
}
1554
1555
# --------------------------------- print field.value value for every graph ----------------------
1556
sub print_values
1557
{
1558
  my $data     = {};
1559
  my $raw_data = {};
1560
  my $raw_prev_data = restore_state_data();
1561
  my $values   = {};
1562
  $raw_data->{'timestamp'} = time();
1563 b6024a55 Gorlow Maxim aka Sheridan
  for my $graph (keys %{$graphs})                                                                                     {
1564
    for my $field (@{$graphs->{$graph}{'general_fields'}})                                                          {
1565
      for my $if (keys %{$interfaces})                                                                            {
1566 71bf7478 Gorlow Maxim aka Sheridan
        prepare_value($values, $field, sprintf("%s_%s", $if, $field), $graph, $if, $data, $raw_data, $raw_prev_data); } } }
1567 b6024a55 Gorlow Maxim aka Sheridan
  for my $if (keys %{$interfaces}) 
1568 71bf7478 Gorlow Maxim aka Sheridan
  {
1569 b6024a55 Gorlow Maxim aka Sheridan
    for my $graph (keys %{$graphs}) 
1570 71bf7478 Gorlow Maxim aka Sheridan
    {
1571
        my $graph_name = sprintf("%s.%s", $graph, $if);
1572 b6024a55 Gorlow Maxim aka Sheridan
        for my $field (@{$graphs->{$graph}{'per_if_fields'}})
1573 71bf7478 Gorlow Maxim aka Sheridan
        {
1574
          prepare_value($values, $field, $field, $graph_name, $if, $data, $raw_data, $raw_prev_data);
1575
        }
1576
    }
1577
  }
1578
  save_state_data($raw_data);
1579
  exit (0) unless defined ($raw_prev_data); # first update need just for collect and save data
1580
  # ------------------------ print ------------------------
1581 b6024a55 Gorlow Maxim aka Sheridan
  for my $graph (sort (keys %{$values}))
1582 71bf7478 Gorlow Maxim aka Sheridan
  {
1583
    printf ("multigraph %s\n", $graph);
1584 b6024a55 Gorlow Maxim aka Sheridan
    for my $field (sort keys %{$values->{$graph}})
1585 71bf7478 Gorlow Maxim aka Sheridan
    {
1586
      printf("%s.value %s\n", $field, $values->{$graph}{$field});
1587
    }
1588
    print "\n";
1589
  }
1590
}