1 # Copyright (C) 2002-2007 Stanislav Sinyagin
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.
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.
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.
17 # $Id: Writer.pm,v 1.1 2010-12-27 00:03:45 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
21 # Write access for ConfigTree
24 package Torrus::ConfigTree::Writer;
26 use Torrus::ConfigTree;
27 our @ISA=qw(Torrus::ConfigTree);
30 use Torrus::TimeStamp;
31 use Torrus::SiteConfig;
32 use Torrus::ServiceID;
35 use Digest::MD5 qw(md5); # needed as hash function
38 our %multigraph_remove_space =
40 'graph-legend-' => 0);
43 # instance of Torrus::ServiceID object, if needed
46 # tree names where we initialized service IDs
54 my $class = ref($proto) || $proto;
55 $options{'-WriteAccess'} = 1;
56 my $self = $class->SUPER::new( %options );
57 if( not defined( $self ) )
64 $self->{'viewparent'} = {};
65 $self->{'mayRunCollector'} =
66 Torrus::SiteConfig::mayRunCollector( $self->treeName() );
68 $self->{'collectorInstances'} =
69 Torrus::SiteConfig::collectorInstances( $self->treeName() );
71 $self->{'db_collectortokens'} = [];
72 foreach my $instance ( 0 .. ($self->{'collectorInstances'} - 1) )
74 $self->{'db_collectortokens'}->[$instance] =
75 new Torrus::DB( 'collector_tokens' . '_' .
76 $instance . '_' . $self->{'ds_config_instance'},
77 -Subdir => $self->treeName(),
82 # delay writing of frequently changed values
83 $self->{'db_dsconfig'}->delay();
84 $self->{'db_otherconfig'}->delay();
92 my $token = $self->{'next_free_token'};
93 $token = 1 unless defined( $token );
94 $self->{'next_free_token'} = $token + 1;
95 return sprintf('T%.4d', $token);
106 if( $self->getParamProperty( $param, 'remspace' ) )
111 $self->{'paramcache'}{$name}{$param} = $value;
112 $self->{'db_otherconfig'}->put( 'P:'.$name.':'.$param, $value );
113 $self->{'db_otherconfig'}->addToList('Pl:'.$name, $param);
123 if( $self->getParamProperty( $param, 'remspace' ) )
128 $self->{'paramcache'}{$name}{$param} = $value;
129 $self->{'db_dsconfig'}->put( 'P:'.$name.':'.$param, $value );
130 $self->{'db_dsconfig'}->addToList('Pl:'.$name, $param);
141 $self->{'paramprop'}{$prop}{$param} = $value;
142 $self->{'db_paramprops'}->put( $param . ':' . $prop, $value );
149 if( not defined( $self->token('/') ) )
151 my $token = $self->newToken();
152 $self->{'db_dsconfig'}->put( 'pt:/', $token );
153 $self->{'db_dsconfig'}->put( 'tp:'.$token, '/' );
154 $self->{'db_dsconfig'}->put( 'n:'.$token, 0 );
155 $self->{'nodetype_cache'}{$token} = 0;
163 my $childname = shift;
166 if( not $self->isSubtree( $token ) )
168 Error('Cannot add a child to a non-subtree node: ' .
169 $self->path($token));
173 my $path = $self->path($token) . $childname;
175 # If the child already exists, do nothing
177 my $ctoken = $self->token($path);
178 if( not defined($ctoken) )
180 $ctoken = $self->newToken();
182 $self->{'db_dsconfig'}->put( 'pt:'.$path, $ctoken );
183 $self->{'db_dsconfig'}->put( 'tp:'.$ctoken, $path );
185 $self->{'db_dsconfig'}->addToList( 'c:'.$token, $ctoken );
186 $self->{'db_dsconfig'}->put( 'p:'.$ctoken, $token );
187 $self->{'parentcache'}{$ctoken} = $token;
192 $nodeType = 2; # alias
194 elsif( $childname =~ /\/$/o )
196 $nodeType = 0; # subtree
200 $nodeType = 1; # leaf
202 $self->{'db_dsconfig'}->put( 'n:'.$ctoken, $nodeType );
203 $self->{'nodetype_cache'}{$ctoken} = $nodeType;
216 my $iamLeaf = $self->isLeaf($token);
218 # TODO: Add more verification here
219 if( not defined($apath) or $apath !~ /^\//o or
220 ( not $iamLeaf and $apath !~ /\/$/o ) or
221 ( $iamLeaf and $apath =~ /\/$/o ) )
223 my $path = $self->path($token);
224 Error("Incorrect alias at $path: $apath"); $ok = 0;
226 elsif( $self->token( $apath ) )
228 my $path = $self->path($token);
229 Error("Alias already exists: $apath at $path"); $ok = 0;
233 # Go through the alias and create subtrees if neccessary
235 my @pathelements = $self->splitPath($apath);
236 my $aliasChildName = pop @pathelements;
239 my $parent_token = $self->token('/');
241 foreach my $nodename ( @pathelements )
243 $nodepath .= $nodename;
244 my $child_token = $self->token( $nodepath );
245 if( not defined( $child_token ) )
247 $child_token = $self->addChild( $parent_token, $nodename );
248 if( not defined( $child_token ) )
253 $parent_token = $child_token;
256 my $alias_token = $self->addChild( $parent_token, $aliasChildName, 1 );
257 if( not defined( $alias_token ) )
262 $self->{'db_dsconfig'}->put( 'a:'.$alias_token, $token );
263 $self->{'db_dsconfig'}->addToList( 'ar:'.$token, $alias_token );
264 $self->{'db_aliases'}->put( $apath, $token );
274 $self->{'db_otherconfig'}->addToList('V:', $vname);
275 if( defined( $parent ) )
277 $self->{'viewparent'}{$vname} = $parent;
286 $self->{'db_otherconfig'}->addToList('M:', $mname);
294 $self->{'db_otherconfig'}->addToList('A:', $aname);
303 $self->{'db_dsconfig'}->put( 'd:'.$name, $value );
304 $self->{'db_dsconfig'}->addToList('D:', $name);
315 $self->{'setvar'}{$token}{$name} = $value;
327 while( defined( $token ) and
328 not defined( $self->{'setvar'}{$token}{$name} ) )
330 $token = $self->getParent( $token );
333 if( defined( $token ) )
335 my $value = $self->{'setvar'}{$token}{$name};
336 if( defined( $value ) )
338 if( $value eq 'true' or
339 $value =~ /^\d+$/o and $value )
357 $self->{'db_dsconfig'}->commit();
358 $self->{'db_otherconfig'}->commit();
360 Verbose('Configuration has compiled successfully. Switching over to ' .
361 'DS config instance ' . $self->{'ds_config_instance'} .
362 ' and Other config instance ' .
363 $self->{'other_config_instance'} );
366 if( not $self->{'-NoDSRebuild'} )
368 $self->{'db_config_instances'}->
369 put( 'ds:' . $self->treeName(),
370 $self->{'ds_config_instance'} );
373 $self->{'db_config_instances'}->
374 put( 'other:' . $self->treeName(),
375 $self->{'other_config_instance'} );
377 Torrus::TimeStamp::init();
378 Torrus::TimeStamp::setNow($self->treeName() . ':configuration');
379 Torrus::TimeStamp::release();
388 my $ok = $self->postProcessNodes();
390 # Propagate view inherited parameters
391 $self->{'viewParamsProcessed'} = {};
392 foreach my $vname ( $self->getViewNames() )
394 &Torrus::DB::checkInterrupted();
396 $self->propagateViewParams( $vname );
408 &Torrus::DB::checkInterrupted();
412 if( not defined( $token ) )
414 $token = $self->token('/');
417 my $nodeid = $self->getNodeParam( $token, 'nodeid', 1 );
418 if( defined( $nodeid ) )
420 # verify the uniqueness of nodeid
422 my $oldToken = $self->{'db_nodeid'}->get($nodeid);
423 if( defined($oldToken) )
425 Error('Non-unique nodeid ' . $nodeid .
426 ' in ' . $self->path($token) .
427 ' and ' . $self->path($oldToken));
432 $self->{'db_nodeid'}->put($nodeid, $token);
437 if( $self->isLeaf($token) )
439 # Process static tokenset members
441 my $tsets = $self->getNodeParam( $token, 'tokenset-member' );
442 if( defined( $tsets ) )
444 foreach my $tset ( split(/,/o, $tsets) )
446 my $tsetName = 'S'.$tset;
447 if( not $self->tsetExists( $tsetName ) )
449 my $path = $self->path( $token );
450 Error("Referenced undefined token set $tset in $path");
455 $self->tsetAddMember( $tsetName, $token, 'static' );
460 my $dsType = $self->getNodeParam( $token, 'ds-type' );
461 if( defined( $dsType ) )
463 if( $dsType eq 'rrd-multigraph' )
465 # Expand parameter substitutions in multigraph leaves
468 split(/,/o, $self->getNodeParam($token, 'ds-names') );
470 foreach my $dname ( @dsNames )
472 foreach my $param ( 'ds-expr-', 'graph-legend-' )
474 my $dsParam = $param . $dname;
475 my $value = $self->getNodeParam( $token, $dsParam );
476 if( defined( $value ) )
478 my $newValue = $value;
479 if( $multigraph_remove_space{$param} )
481 $newValue =~ s/\s+//go;
484 $self->expandSubstitutions( $token, $dsParam,
486 if( $newValue ne $value )
488 $self->setNodeParam( $token, $dsParam,
495 elsif( $dsType eq 'collector' and $self->{'mayRunCollector'} )
497 # Split the collecting job between collector instances
499 my $nInstances = $self->{'collectorInstances'};
502 $self->getNodeParam($token, 'collector-timeoffset');
503 my $newOffset = $oldOffset;
506 $self->getNodeParam($token, 'collector-period');
508 if( $nInstances > 1 )
511 $self->getNodeParam($token,
512 'collector-instance-hashstring');
513 if( not defined( $hashString ) )
515 Error('collector-instance-hashstring is not defined ' .
516 'in ' . $self->path( $token ));
521 unpack( 'N', md5( $hashString ) ) % $nInstances;
524 $self->setNodeParam( $token,
525 'collector-instance',
529 $self->getNodeParam($token,
530 'collector-dispersed-timeoffset');
531 if( defined( $dispersed ) and $dispersed eq 'yes' )
533 # Process dispersed collector offsets
536 foreach my $param ( 'collector-timeoffset-min',
537 'collector-timeoffset-max',
538 'collector-timeoffset-step',
539 'collector-timeoffset-hashstring' )
541 my $val = $self->getNodeParam( $token, $param );
542 if( not defined( $val ) )
544 Error('Mandatory parameter ' . $param . ' is not '.
545 ' defined in ' . $self->path( $token ));
556 my $min = $p{'collector-timeoffset-min'};
557 my $max = $p{'collector-timeoffset-max'};
560 Error('collector-timeoffset-max is less than ' .
561 'collector-timeoffset-min in ' .
562 $self->path( $token ));
567 my $step = $p{'collector-timeoffset-step'};
569 $p{'collector-timeoffset-hashstring'};
571 my $bucketSize = int( ($max - $min) / $step );
575 $step * ( unpack( 'N', md5( $hashString ) ) %
578 $instance * int( $step / $nInstances );
584 $newOffset += $instance * int( $period / $nInstances );
587 $newOffset %= $period;
589 if( $newOffset != $oldOffset )
591 $self->setNodeParam( $token,
592 'collector-timeoffset',
596 $self->{'db_collectortokens'}->[$instance]->put
597 ( $token, sprintf('%d:%d', $period, $newOffset) );
600 $self->getNodeParam( $token, 'storage-type' );
601 foreach my $stype ( split(/,/o, $storagetypes) )
603 if( $stype eq 'ext' )
605 if( not defined( $srvIdParams ) )
608 new Torrus::ServiceID( -WriteAccess => 1 );
612 $self->getNodeParam($token, 'ext-service-trees');
614 if( not defined( $srvTrees ) or
615 length( $srvTrees ) == 0 )
617 $srvTrees = $self->treeName();
621 $self->getNodeParam($token, 'ext-service-id');
623 foreach my $srvTree (split(/\s*,\s*/o, $srvTrees))
625 if( not Torrus::SiteConfig::treeExists($srvTree) )
628 ('Error processing ext-service-trees' .
629 'for ' . $self->path( $token ) .
630 ': tree ' . $srvTree .
636 if( not $srvIdInitialized{$srvTree} )
638 $srvIdParams->cleanAllForTree
640 $srvIdInitialized{$srvTree} = 1;
644 if( $srvIdParams->idExists( $serviceid,
647 Error('Duplicate ServiceID: ' .
648 $serviceid . ' in tree ' .
658 # sorry for ackward Emacs auto-indent
660 'trees' => $srvTrees,
663 $self->getNodeParam($token,
667 ($token, 'ext-service-units')
670 $srvIdParams->add( $serviceid, $params );
678 my $path = $self->path( $token );
679 Error("Mandatory parameter 'ds-type' is not defined for $path");
685 foreach my $ctoken ( $self->getChildren( $token ) )
687 if( not $self->isAlias( $ctoken ) )
689 $ok = $self->postProcessNodes( $ctoken ) ? $ok:0;
697 sub propagateViewParams
702 # Avoid processing the same view twice
703 if( $self->{'viewParamsProcessed'}{$vname} )
708 # First we do the same for parent
709 my $parent = $self->{'viewparent'}{$vname};
710 if( defined( $parent ) )
712 $self->propagateViewParams( $parent );
714 my $parentParams = $self->getParams( $parent );
715 foreach my $param ( keys %{$parentParams} )
717 if( not defined( $self->getParam( $vname, $param ) ) )
719 $self->setParam( $vname, $param, $parentParams->{$param} );
724 # mark this view as processed
725 $self->{'viewParamsProcessed'}{$vname} = 1;
735 $self->{'is_writing'} = undef;
737 if( not $self->{'-NoDSRebuild'} )
739 $ok = Torrus::ConfigTree::Validator::validateNodes($self);
741 $ok = Torrus::ConfigTree::Validator::validateViews($self) ? $ok:0;
742 $ok = Torrus::ConfigTree::Validator::validateMonitors($self) ? $ok:0;
743 $ok = Torrus::ConfigTree::Validator::validateTokensets($self) ? $ok:0;
753 # indent-tabs-mode: nil
754 # perl-indent-level: 4