import torrus 1.0.9
[freeside.git] / torrus / perllib / Torrus / ConfigTree / Validator.pm
1 #  Copyright (C) 2002  Stanislav Sinyagin
2 #
3 #  This program is free software; you can redistribute it and/or modify
4 #  it under the terms of the GNU General Public License as published by
5 #  the Free Software Foundation; either version 2 of the License, or
6 #  (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software
15 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
16
17 # $Id: Validator.pm,v 1.1 2010-12-27 00:03:45 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
19
20
21 package Torrus::ConfigTree::Validator;
22
23 use Torrus::ConfigTree;
24 use Torrus::Log;
25 use Torrus::RPN;
26 use Torrus::SiteConfig;
27 use strict;
28
29 Torrus::SiteConfig::loadStyling();
30
31 %Torrus::ConfigTree::Validator::reportedErrors = ();
32
33 my %rrd_params =
34     (
35      'leaf-type' => {'rrd-def' => {'rrd-ds' => undef,
36                                    'rrd-cf' => {'AVERAGE' => undef,
37                                                 'MIN'     => undef,
38                                                 'MAX'     => undef,
39                                                 'LAST'    => undef},
40                                    'data-file' => undef,
41                                    'data-dir'  => undef},
42                      'rrd-cdef' => {'rpn-expr' => undef}},
43      );
44
45 my %rrdmulti_params = ( 'ds-names' => undef );
46
47 # Plugins might need to add a new storage type
48 our %collector_params =
49     (
50      'collector-type'  => undef,
51      '@storage-type'   => {
52          'rrd' => {
53              'data-file'              => undef,
54              'data-dir'               => undef,
55              'leaf-type'              => {
56                  'rrd-def'  => {'rrd-ds' => undef,
57                                 'rrd-cf' => {'AVERAGE' => undef,
58                                              'MIN'     => undef,
59                                              'MAX'     => undef,
60                                              'LAST'    => undef},
61                                 'rrd-create-dstype' => {'GAUGE' => undef,
62                                                         'COUNTER' => undef,
63                                                         'DERIVE'  => undef,
64                                                         'ABSOLUTE' => undef },
65                                 'rrd-create-rra'         => undef,
66                                 'rrd-create-heartbeat'   => undef,
67                                 '+rrd-hwpredict'         => {
68                                     'enabled' => {
69                                         'rrd-create-hw-rralen' => undef},
70                                     'disabled' => undef,
71                                 }}}},
72          'ext' => {
73              'ext-dstype' => {
74                  'GAUGE' => undef,
75                  'COUNTER32' => undef,
76                  'COUNTER64' => undef },
77              'ext-service-id' => undef,
78              '+ext-service-units' => {
79                  'bytes' => undef }}},
80      'collector-period'      => undef,
81      'collector-timeoffset'  => undef,
82      '+collector-scale'      => undef,
83      '+collector-dispersed-timeoffset' => {
84          'no' => undef,
85          'yes' => undef }
86      # collector-timeoffset-min, max, step, and hashstring are validated
87      # during post-processing
88      );
89
90
91 # Plugins might in theory create new datasource types
92 our %leaf_params =
93     ('ds-type' => {'rrd-file' => \%rrd_params,
94                    'rrd-multigraph' => \%rrdmulti_params,
95                    'collector' => \%collector_params},
96      'rrgraph-views'             => undef,
97      '+rrd-scaling-base'         => {'1000' => undef, '1024' => undef},
98      '+graph-logarithmic'        => {'yes'  => undef, 'no' => undef},
99      '+graph-rigid-boundaries'   => {'yes'  => undef, 'no' => undef},
100      '+graph-ignore-decorations' => {'yes'  => undef, 'no' => undef});
101
102
103 my %monitor_params =
104     ('monitor-type' => {'expression' => {'rpn-expr' => undef},
105                         'failures' => undef},
106      'action'       => undef,
107      'expires'      => undef
108      );
109
110 my %action_params =
111     ('action-type' => {'tset' => {'tset-name' => undef},
112                        'exec' => {'command' => undef} }
113      );
114
115 my %view_params =
116     ('expires' => undef,
117      'view-type' => {'rrgraph' => {'width' => undef,
118                                    'height' => undef,
119                                    'start' => undef,
120                                    'line-style' => undef,
121                                    'line-color' => undef,
122                                    '+ignore-limits' => {
123                                        'yes'=>undef, 'no'=>undef },
124                                    '+ignore-lower-limit' => {
125                                        'yes'=>undef, 'no'=>undef },
126                                    '+ignore-upper-limit' => {
127                                        'yes'=>undef, 'no'=>undef }},
128                      'rrprint' => {'start' => undef,
129                                    'print-cf' => undef},
130                      'html' => {'html-template' => undef},
131                      'adminfo' => undef}
132      );
133
134
135 # Load additional validation, configurable from
136 # torrus-config.pl and torrus-siteconfig.pl
137
138 foreach my $mod ( @Torrus::Validator::loadLeafValidators )
139 {
140     eval( 'require ' . $mod );
141     die( $@ ) if $@;
142     eval( '&' . $mod . '::initValidatorLeafParams( \%leaf_params )' );
143     die( $@ ) if $@;
144 }
145
146
147 sub validateNodes
148 {
149     my $config_tree = shift;
150     my $token = $config_tree->token('/');
151
152     if( defined($token) )
153     {
154         return validateNode($config_tree, $token);
155     }
156     else
157     {
158         Error("The datasource tree is empty");
159         return 0;
160     }
161 }
162
163 sub validateNode
164 {
165     my $config_tree = shift;
166     my $token = shift;
167
168     &Torrus::DB::checkInterrupted();
169
170     my $ok = 1;
171
172     if( $config_tree->isLeaf($token) )
173     {
174         # Verify the default view
175         my $view = $config_tree->getNodeParam( $token, 'default-leaf-view' );
176         if( not defined( $view ) )
177         {
178             my $path = $config_tree->path( $token );
179             Error("Default view is not defined for leaf $path");
180             $ok = 0;
181         }
182         elsif( not $config_tree->{'validator'}{'viewExists'}{$view} and
183                not $config_tree->viewExists( $view ) )
184         {
185             my $path = $config_tree->path( $token );
186             Error("Non-existent view is defined as default for leaf $path");
187             $ok = 0;
188         }
189         else
190         {
191             # Cache the view name
192             $config_tree->{'validator'}{'viewExists'}{$view} = 1;
193         }
194
195         # Verify parameters
196         $ok = validateInstanceParams($config_tree, $token,
197                                      'node', \%leaf_params);
198
199         if( $ok )
200         {
201             my $rrviewslist =
202                 $config_tree->getNodeParam( $token, 'rrgraph-views' );
203
204             # Check the cache first
205             if( not $config_tree->{'validator'}{'graphviews'}{$rrviewslist} )
206             {
207                 my @rrviews = split( ',', $rrviewslist );
208
209                 if( scalar(@rrviews) != 5 )
210                 {
211                     my $path = $config_tree->path( $token );
212                     Error('rrgraph-views sould refer 5 views in' . $path);
213                     $ok = 0;
214                 }
215                 else
216                 {
217                     foreach my $view ( @rrviews )
218                     {
219                         if( not $config_tree->viewExists( $view ) )
220                         {
221                             my $path = $config_tree->path( $token );
222                             Error("Non-existent view ($view) is defined in " .
223                                   "rrgraph-views for $path");
224                             $ok = 0;
225                         }
226                         elsif( $config_tree->getParam($view, 'view-type') ne
227                                'rrgraph' )
228                         {
229                             my $path = $config_tree->path( $token );
230                             Error("View $view is not of type rrgraph in " .
231                                   "rrgraph-views for $path");
232                             $ok = 0;
233                         }
234                     }
235                 }
236
237                 if( $ok )
238                 {
239                     # Store the cache
240                     $config_tree->{'validator'}{'graphviews'}{$rrviewslist}=1;
241                 }
242             }
243         }
244
245         # Verify monitor references
246         my $mlist = $config_tree->getNodeParam( $token, 'monitor' );
247         if( defined $mlist )
248         {
249             foreach my $param ( 'monitor-period', 'monitor-timeoffset' )
250             {
251                 if( not defined( $config_tree->getNodeParam( $token,
252                                                              $param ) ) )
253                 {
254                     my $path = $config_tree->path( $token );
255                     Error('Mandatory parameter ' . $param .
256                           ' is not defined in ' . $path);
257                     $ok = 0;
258                 }
259             }
260             
261             foreach my $monitor ( split(',', $mlist) )
262             {
263                 if( not $config_tree->{'validator'}{'monitorExists'}{$monitor}
264                     and
265                     not $config_tree->monitorExists( $monitor ) )
266                 {
267                     my $path = $config_tree->path( $token );
268                     Error("Non-existent monitor: $monitor in $path");
269                     $ok = 0;
270                 }
271                 else
272                 {
273                     $config_tree->{'validator'}{'monitorExists'}{$monitor} = 1;
274                 }
275             }
276             
277             my $varstring =
278                 $config_tree->getNodeParam( $token, 'monitor-vars' );
279             if( defined $varstring )
280             {
281                 foreach my $pair ( split( '\s*;\s*', $varstring ) )
282                 {
283                     if( $pair !~ /^\w+\s*\=\s*[0-9\-+.eU]+$/o )
284                     {
285                         Error("Syntax error in monitor variables: $pair");
286                         $ok = 0;
287                     }
288                 }
289             }
290
291             my $action_target =
292                 $config_tree->getNodeParam($token, 'monitor-action-target');
293             if( defined( $action_target ) )
294             {
295                 my $target = $config_tree->getRelative($token, $action_target);
296                 if( not defined( $target ) )
297                 {
298                     my $path = $config_tree->path( $token );
299                     Error('monitor-action-target points to an invalid path: ' .
300                           $action_target . ' in ' . $path);
301                     $ok = 0;
302                 }
303                 elsif( not $config_tree->isLeaf( $target ) )
304                 {
305                     my $path = $config_tree->path( $token );
306                     Error('monitor-action-target must point to a leaf: ' .
307                           $action_target . ' in ' . $path);
308                     $ok = 0;
309                 }
310             }
311         }
312
313         # Verify if the data-dir exists
314         my $datadir = $config_tree->getNodeParam( $token, 'data-dir' );
315         if( defined $datadir )
316         {
317             if( not $config_tree->{'validator'}{'dirExists'}{$datadir} and
318                 not ( -d $datadir ) and
319                 not $Torrus::ConfigTree::Validator::reportedErrors{$datadir} )
320             {
321                 my $path = $config_tree->path( $token );
322                 Error("Directory does not exist: $datadir in $path");
323                 $ok = 0;
324                 $Torrus::ConfigTree::Validator::reportedErrors{$datadir} = 1;
325             }
326             else
327             {
328                 # Store the cache
329                 $config_tree->{'validator'}{'dirExists'}{$datadir} = 1;
330             }
331         }
332
333         # Verify type-specific parameters
334         my $dsType = $config_tree->getNodeParam( $token, 'ds-type' );
335         if( not defined( $dsType ) )
336         {
337             # Writer has already complained
338             return 0;
339         }
340
341         if( $dsType eq 'rrd-multigraph' )
342         {
343             my @dsNames =
344                 split(',', $config_tree->getNodeParam( $token, 'ds-names' ) );
345
346             if( scalar(@dsNames) == 0 )
347             {
348                 my $path = $config_tree->path( $token );
349                 Error("ds-names list is empty in $path");
350                 $ok = 0;
351             }
352             foreach my $dname ( @dsNames )
353             {
354                 my $param = 'ds-expr-' . $dname;
355                 my $expr = $config_tree->getNodeParam( $token, $param );
356                 if( not defined( $expr ) )
357                 {
358                     my $path = $config_tree->path( $token );
359                     Error("Parameter $param is not defined in $path");
360                     $ok = 0;
361                 }
362                 else
363                 {
364                     $ok = validateRPN( $token, $expr, $config_tree ) ? $ok : 0;
365                 }
366
367                 foreach my $paramprefix ( 'graph-legend-', 'line-style-',
368                                           'line-color-', 'line-order-' )
369                 {
370                     my $param = $paramprefix.$dname;
371                     my $value = $config_tree->getNodeParam($token, $param);
372                     if( not defined( $value ) )
373                     {
374                         my $path = $config_tree->path( $token );
375                         Error('Parameter ' . $param .
376                               ' is not defined in ' . $path);
377                         $ok = 0;
378                     }
379                     elsif( $param eq 'line-style-' and
380                            not validateLine( $value ) )
381                     {
382                         my $path = $config_tree->path( $token );
383                         Error('Parameter ' . $param .
384                               ' is defined incorrectly in ' . $path);
385                         $ok = 0;
386                     }
387                     elsif( $param eq 'line-color-' and
388                            not validateColor( $value ) )
389                     {
390                         my $path = $config_tree->path( $token );
391                         Error('Parameter ' . $param .
392                               ' is defined incorrectly in ' . $path);
393                         $ok = 0;
394                     }
395                 }
396             }
397         }
398         elsif( $dsType eq 'rrd-file' and
399                $config_tree->getNodeParam( $token, 'leaf-type' ) eq 'rrd-cdef')
400         {
401             my $expr = $config_tree->getNodeParam( $token, 'rpn-expr' );
402             if( defined( $expr ) )
403             {
404                 $ok = validateRPN( $token, $expr, $config_tree ) ? $ok : 0;
405             }
406             # Otherwise already reported by  validateInstanceParams()
407         }
408         elsif($dsType eq 'collector' and
409               $config_tree->getNodeParam( $token, 'collector-type' ) eq 'snmp')
410         {
411             # Check the OID syntax
412             my $oid = $config_tree->getNodeParam( $token, 'snmp-object' );
413             if( defined($oid) and $oid =~ /^\./o )
414             {
415                 my $path = $config_tree->path( $token );
416                 Error("Invalid syntax for snmp-object in " .
417                       $path . ": OID must not start with dot");
418                 $ok = 0;
419             }
420         }
421     }
422     else
423     {
424         # This is subtree
425         my $view = $config_tree->getNodeParam( $token,
426                                                'default-subtree-view' );
427
428         if( not defined( $view ) )
429         {
430             my $path = $config_tree->path( $token );
431             Error("Default view is not defined for subtree $path");
432             $ok = 0;
433         }
434         elsif( not $config_tree->{'validator'}{'viewExists'}{$view} and
435                not $config_tree->viewExists( $view ) )
436         {
437             my $path = $config_tree->path( $token );
438             Error("Non-existent view is defined as default for subtree $path");
439             $ok = 0;
440         }
441         else
442         {
443             # Store the cache
444             $config_tree->{'validator'}{'viewExists'}{$view} = 1;
445         }
446
447         foreach my $ctoken ( $config_tree->getChildren($token) )
448         {
449             if( not $config_tree->isAlias($ctoken) )
450             {
451                 $ok = validateNode($config_tree, $ctoken)
452                     ? $ok:0;
453             }
454         }
455     }
456     return $ok;
457 }
458
459 my %validFuntcionNames =
460     ( 'AVERAGE' => 1,
461       'MIN'     => 1,
462       'MAX'     => 1,
463       'LAST'    => 1,
464       'T'       => 1 );
465
466
467 sub validateRPN
468 {
469     my $token = shift;
470     my $expr  = shift;
471     my $config_tree = shift;
472     my $timeoffset_supported = shift;
473
474     &Torrus::DB::checkInterrupted();
475     
476     my $ok = 1;
477
478     # There must be at least one DS reference
479     my $ds_couter = 0;
480
481     my $rpn = new Torrus::RPN;
482
483     # The callback for RPN translation
484     my $callback = sub
485     {
486         my ($noderef, $timeoffset) = @_;
487
488         my $function;
489         if( $noderef =~ s/^(.+)\@//o )
490         {
491             $function = $1;
492         }
493
494         if( defined( $function ) and not $validFuntcionNames{$function} )
495         {
496             my $path = $config_tree->path($token);
497             Error('Invalid function name ' . $function .
498                   ' in node reference at ' . $path);
499             $ok = 0;
500             return undef;
501         }            
502         
503         my $leaf = length($noderef) > 0 ?
504             $config_tree->getRelative($token, $noderef) : $token;
505
506         if( not defined $leaf )
507         {
508             my $path = $config_tree->path($token);
509             Error("Cannot find relative reference $noderef at $path");
510             $ok = 0;
511             return undef;
512         }
513         if( not $config_tree->isLeaf( $leaf ) )
514         {
515             my $path = $config_tree->path($token);
516             Error("Relative reference $noderef at $path is not a leaf");
517             $ok = 0;
518             return undef;
519         }
520         if( $config_tree->getNodeParam($leaf, 'leaf-type') ne 'rrd-def' )
521         {
522             my $path = $config_tree->path($token);
523             Error("Relative reference $noderef at $path must point to a ".
524                   "leaf of type rrd-def");
525             $ok = 0;
526             return undef;
527         }
528         if( defined( $timeoffset ) and not $timeoffset_supported )
529         {
530             my $path = $config_tree->path($token);
531             Error("Time offsets are not supported at $path");
532             $ok = 0;
533             return undef;
534         }
535
536         $ds_couter++;
537         return 'TESTED';
538     };
539
540     $rpn->translate( $expr, $callback );
541     if( $ok and $ds_couter == 0 )
542     {
543         my $path = $config_tree->path($token);
544         Error("RPN must contain at least one DS reference at $path");
545         $ok = 0;
546     }
547     return $ok;
548 }
549
550
551
552 sub validateViews
553 {
554     my $config_tree = shift;
555     my $ok = 1;
556
557     foreach my $view ($config_tree->getViewNames())
558     {
559         &Torrus::DB::checkInterrupted();
560         
561         $ok = validateInstanceParams($config_tree, $view,
562                                      'view', \%view_params) ? $ok:0;
563         if( $ok and $config_tree->getParam($view, 'view-type') eq 'rrgraph' )
564         {
565             my $hrulesList = $config_tree->getParam($view, 'hrules');
566             if( defined( $hrulesList ) )
567             {
568                 foreach my $hrule ( split(',', $hrulesList ) )
569                 {
570                     my $valueParam =
571                         $config_tree->getParam($view, 'hrule-value-' . $hrule);
572                     if( not defined( $valueParam ) or $valueParam !~ /^\S+$/o )
573                     {
574                         Error('Mandatory parameter hrule-value-' . $hrule .
575                               ' is not defined or incorrect for view ' .
576                               $view);
577                         $ok = 0;
578                     }
579                     my $color =
580                         $config_tree->getParam($view, 'hrule-color-'.$hrule);
581                     if( not defined( $color ) )
582                     {
583                         Error('Mandatory parameter hrule-color-' . $hrule .
584                               ' is not defined for view ' . $view);
585                         $ok = 0;
586                     }
587                     else
588                     {
589                         $ok = validateColor( $color ) ? $ok:0;
590                     }
591                 }
592             }
593
594             my $decorList = $config_tree->getParam($view, 'decorations');
595             if( defined( $decorList ) )
596             {
597                 foreach my $decorName ( split(',', $decorList ) )
598                 {
599                     foreach my $paramName ( qw(order style color expr) )
600                     {
601                         my $param = 'dec-' . $paramName . '-' . $decorName;
602                         if( not defined( $config_tree->
603                                          getParam($view, $param) ) )
604                         {
605                             Error('Missing parameter: ' . $param .
606                                   ' in view ' . $view);
607                             $ok = 0;
608                         }
609                     }
610
611                     $ok = validateLine( $config_tree->
612                                         getParam($view,
613                                                  'dec-style-' . $decorName) )
614                         ? $ok:0;
615                     $ok = validateColor( $config_tree->
616                                          getParam($view,
617                                                   'dec-color-' . $decorName) )
618                         ? $ok:0;
619                 }
620             }
621
622             $ok = validateColor( $config_tree->getParam($view, 'line-color') )
623                 ? $ok:0;
624             $ok = validateLine( $config_tree->getParam($view, 'line-style') )
625                 ? $ok:0;
626
627             my $gprintValues = $config_tree->getParam($view, 'gprint-values');
628             if( defined( $gprintValues ) and length( $gprintValues ) > 0 )
629             {
630                 foreach my $gprintVal ( split(',', $gprintValues ) )
631                 {
632                     my $format =
633                         $config_tree->getParam($view,
634                                                'gprint-format-' . $gprintVal);
635                     if( not defined( $format ) or length( $format ) == 0 )
636                     {
637                         Error('GPRINT format for ' . $gprintVal .
638                               ' is not defined for view ' .  $view);
639                         $ok = 0;
640                     }
641                 }
642             }
643         }
644     }
645     return $ok;
646 }
647
648
649 sub validateColor
650 {
651     my $color = shift;
652     my $ok = 1;
653
654     if( $color !~ /^\#[0-9a-fA-F]{6}$/o )
655     {
656         if( $color =~ /^\#\#(\S+)$/o )
657         {
658             if( not $Torrus::Renderer::graphStyles{$1}{'color'} )
659             {
660                 Error('Incorrect color reference: ' . $color);
661                 $ok = 0;
662             }
663         }
664         else
665         {
666             Error('Incorrect color syntax: ' . $color);
667             $ok = 0;
668         }
669     }
670
671     return $ok;
672 }
673
674
675 sub validateLine
676 {
677     my $line = shift;
678     my $ok = 1;
679
680     if( $line =~ /^\#\#(\S+)$/o )
681     {
682         if( not $Torrus::Renderer::graphStyles{$1}{'line'} )
683         {
684             Error('Incorrect line style reference: ' . $line);
685             $ok = 0;
686         }
687     }
688     elsif( not $Torrus::SiteConfig::validLineStyles{$line} )
689     {
690         Error('Incorrect line syntax: ' . $line);
691         $ok = 0;
692     }
693
694     return $ok;
695 }
696
697
698 sub validateMonitors
699 {
700     my $config_tree = shift;
701     my $ok = 1;
702
703     foreach my $action ($config_tree->getActionNames())
704     {        
705         $ok = validateInstanceParams($config_tree, $action,
706                                      'action', \%action_params) ? $ok:0;
707         my $atype = $config_tree->getParam($action, 'action-type');
708         if( $atype eq 'tset' )
709         {
710             my $tset = $config_tree->getParam($action, 'tset-name');
711             if( defined $tset )
712             {
713                 $tset = 'S'.$tset;
714                 if( not $config_tree->tsetExists( $tset ) )
715                 {
716                     Error("Token-set does not exist: $tset in action $action");
717                     $ok = 0;
718                 }
719             }
720             # Otherwise the error is already reported by validateInstanceParams
721         }
722         elsif( $atype eq 'exec' )
723         {
724             my $launch_when = $config_tree->getParam($action, 'launch-when');
725             if( defined $launch_when )
726             {
727                 foreach my $when ( split(',', $launch_when) )
728                 {
729                     my $matched = 0;
730                     foreach my $event ('set', 'repeat', 'clear', 'forget')
731                     {
732                         if( $when eq $event )
733                         {
734                             $matched = 1;
735                         }
736                     }
737                     if( not $matched )
738                     {
739                         if( $when eq 'throw' )
740                         {
741                             Error('Event type "throw" is no longer ' .
742                                   'supported. Replace with "set".');
743                         }
744                         else
745                         {
746                             Error("Invalid value in parameter launch-when " .
747                                   "in action $action: $when");
748                         }
749                         $ok = 0;
750                     }
751                 }
752             }
753
754             my $setenv_dataexpr =
755                 $config_tree->getParam( $action, 'setenv-dataexpr' );
756
757             if( defined( $setenv_dataexpr ) )
758             {
759                 # <param name="setenv_dataexpr"
760                 #        value="ENV1=expr1, ENV2=expr2"/>
761
762                 foreach my $pair ( split( ',', $setenv_dataexpr ) )
763                 {
764                     my ($env, $param) = split( '=', $pair );
765                     if( not $param )
766                     {
767                         Error("Syntax error in setenv-dataexpr in action " .
768                               $action . ": \"" . $pair . "\"");
769                         $ok = 0;
770                     }
771                     elsif( $env =~ /\W/o )
772                     {
773                         Error("Illegal characters in environment variable ".
774                               "name in setenv-dataexpr in action " . $action .
775                               ": \"" . $env . "\"");
776                         $ok = 0;
777                     }
778                     elsif( not defined ($config_tree->getParam( $action,
779                                                                 $param ) ) )
780                     {
781                         Error("Parameter referenced in setenv-dataexpr is " .
782                               "not defined in action " .
783                               $action . ": " . $param);
784                         $ok = 0;
785                     }
786                 }
787             }
788         }
789     }
790
791     foreach my $monitor ($config_tree->getMonitorNames())
792     {
793         $ok = validateInstanceParams($config_tree, $monitor,
794                                      'monitor', \%monitor_params) ? $ok:0;
795         my $alist = $config_tree->getParam( $monitor, 'action' );
796         foreach my $action ( split(',', $alist ) )
797         {
798             if( not $config_tree->actionExists( $action ) )
799             {
800                 Error("Non-existent action: $action in monitor $monitor");
801                 $ok = 0;
802             }
803         }
804     }
805     return $ok;
806 }
807
808
809 sub validateTokensets
810 {
811     my $config_tree = shift;
812     my $ok = 1;
813
814     my $view = $config_tree->getParam( 'SS', 'default-tsetlist-view' );
815     if( not defined( $view ) )
816     {
817         Error("View is not defined for tokensets list");
818         $ok = 0;
819     }
820     elsif( not $config_tree->viewExists( $view ) )
821     {
822         Error("Non-existent view is defined for tokensets list");
823         $ok = 0;
824     }
825
826     foreach my $tset ($config_tree->getTsets())
827     {
828         &Torrus::DB::checkInterrupted();
829         
830         $view = $config_tree->getParam($tset, 'default-tset-view');
831         if( not defined( $view ) )
832         {
833             $view = $config_tree->getParam('SS', 'default-tset-view');
834         }
835
836         if( not defined( $view ) )
837         {
838             Error("Default view is not defined for tokenset $tset");
839             $ok = 0;
840         }
841         elsif( not $config_tree->viewExists( $view ) )
842         {
843             Error("Non-existent view is defined for tokenset $tset");
844             $ok = 0;
845         }
846     }
847     return $ok;
848 }
849
850
851
852
853 sub validateInstanceParams
854 {
855     my $config_tree = shift;
856     my $inst_name = shift;
857     my $inst_type = shift;
858     my $mapref = shift;
859
860     &Torrus::DB::checkInterrupted();
861     
862     # Debug("Validating $inst_type $inst_name");
863
864     my $ok = 1;
865     my @namemaps = ($mapref);
866
867     while( $ok and scalar(@namemaps) > 0 )
868     {
869         my @next_namemaps = ();
870
871         foreach my $namemap (@namemaps)
872         {
873             foreach my $paramkey (keys %{$namemap})
874             {
875                 # Debug("Checking param: $pname");
876
877                 my $pname = $paramkey;
878                 my $mandatory = 1;
879                 if( $pname =~ s/^\+//o )
880                 {
881                     $mandatory = 0;
882                 }
883
884                 my $listval = 0;
885                 if( $pname =~ s/^\@//o )
886                 {
887                     $listval = 1;
888                 }
889                 
890                 my $pvalue =
891                     $config_tree->getInstanceParam($inst_type,
892                                                    $inst_name, $pname);
893
894                 my @pvalues;
895                 if( $listval )
896                 {
897                     @pvalues = split(',', $pvalue);
898                 }
899                 else
900                 {
901                     @pvalues = ( $pvalue );
902                 }
903                 
904                 if( not defined( $pvalue ) )
905                 {
906                     if( $mandatory )
907                     {
908                         my $msg;
909                         if( $inst_type eq 'node' )
910                         {
911                             $msg = $config_tree->path( $inst_name );
912                         }
913                         else
914                         {
915                             $msg = "$inst_type $inst_name";
916                         }
917                         Error("Mandatory parameter $pname is not ".
918                               "defined for $msg");
919                         $ok = 0;
920                     }
921                 }
922                 else
923                 {
924                     if( ref( $namemap->{$paramkey} ) )
925                     {
926                         foreach my $pval ( @pvalues )
927                         {
928                             if( exists $namemap->{$paramkey}->{$pval} )
929                             {
930                                 if( defined $namemap->{$paramkey}->{$pval} )
931                                 {
932                                     push( @next_namemaps,
933                                           $namemap->{$paramkey}->{$pval} );
934                                 }
935                             }
936                             else
937                             {
938                                 my $msg;
939                                 if( $inst_type eq 'node' )
940                                 {
941                                     $msg = $config_tree->path( $inst_name );
942                                 }
943                                 else
944                                 {
945                                     $msg = "$inst_type $inst_name";
946                                 }
947                                 Error("Parameter $pname has ".
948                                       "unknown value: $pval for $msg");
949                                 $ok = 0;
950                             }
951                         }
952                     }
953                 }
954             }
955         }
956         @namemaps = @next_namemaps;
957     }
958     return $ok;
959 }
960
961
962
963 1;
964
965 # Local Variables:
966 # mode: perl
967 # indent-tabs-mode: nil
968 # perl-indent-level: 4
969 # End: