per-agent configuration of batch processors, #71837
[freeside.git] / torrus / perllib / Torrus / ConfigBuilder.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: ConfigBuilder.pm,v 1.1 2010-12-27 00:03:40 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
19
20 # XML configuration builder
21
22 package Torrus::ConfigBuilder;
23
24 use strict;
25 use XML::LibXML;
26 use IO::File;
27
28 use Torrus::Log;
29
30 sub new
31 {
32     my $self = {};
33     my $class = shift;
34     bless $self, $class;
35
36     my $doc = XML::LibXML->createDocument( "1.0", "UTF-8" );
37     my $root = $doc->createElement('configuration');
38     $doc->setDocumentElement( $root );
39     $self->{'doc'} = $doc;
40     $self->{'docroot'} = $root;
41
42     $root->appendChild($doc->createComment('DO NOT EDIT THIS FILE'));
43
44     my $dsnode = $doc->createElement('datasources');
45     $self->{'docroot'}->appendChild( $dsnode );
46     $self->{'datasources'} = $dsnode;
47
48     $self->{'required_templates'} = {};
49
50     $self->{'statistics'} = {};
51
52     $self->{'registry_overlays'} = [];
53     
54     return $self;
55 }
56
57
58 sub setRegistryOverlays
59 {
60     my $self = shift;
61     
62     $self->{'registry_overlays'} = [];
63     push( @{$self->{'registry_overlays'}}, @_ );
64 }
65
66
67 sub lookupRegistry
68 {
69     my $self = shift;
70     my $template = shift;
71
72     my $ret = undef;
73
74     foreach my $regOverlay ( @{$self->{'registry_overlays'}} )
75     {
76         if( defined( $regOverlay->{$template} ) )
77         {
78             $ret = $regOverlay->{$template};
79         }
80     }
81     
82     if( not defined( $ret ) and
83         defined( $Torrus::ConfigBuilder::templateRegistry{$template} ) )
84     {
85         $ret = $Torrus::ConfigBuilder::templateRegistry{$template};
86     }
87     
88     if( not defined( $ret ) )
89     {
90         if( scalar( %Torrus::ConfigBuilder::templateRegistry ) > 0 )
91         {
92             Warn('Template ' . $template .
93                  ' is not listed in ConfigBuilder template registry');
94         }
95     }
96
97     return $ret;
98 }
99     
100
101
102
103 sub addCreatorInfo
104 {
105     my $self = shift;
106     my $creatorInfo = shift;
107
108     my $creatorNode = $self->{'doc'}->createElement('creator-info');
109     $creatorNode->appendText( $creatorInfo );
110     $self->{'docroot'}->insertBefore( $creatorNode, $self->{'datasources'} );
111 }
112
113
114 sub addRequiredFiles
115 {
116     my $self = shift;
117
118     foreach my $file ( $self->requiredFiles() )
119     {
120         $self->addFileInclusion( $file );
121     }
122 }
123
124
125 sub addFileInclusion
126 {
127     my $self = shift;
128     my $file = shift;
129
130     my $node = $self->{'doc'}->createElement('include');
131     $node->setAttribute( 'filename', $file );
132     $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
133 }
134
135
136 sub startDefinitions
137 {
138     my $self = shift;
139
140     my $node = $self->{'doc'}->createElement('definitions');
141     $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
142     return $node;
143 }
144
145
146 sub addDefinition
147 {
148     my $self = shift;
149     my $definitionsNode = shift;;
150     my $name = shift;
151     my $value = shift;
152
153     my $node = $self->{'doc'}->createElement('def');
154     $node->setAttribute( 'name', $name );
155     $node->setAttribute( 'value', $value );
156     $definitionsNode->appendChild( $node );
157 }
158
159
160 sub startParamProps
161 {
162     my $self = shift;
163
164     my $node = $self->{'doc'}->createElement('param-properties');
165     $self->{'docroot'}->insertBefore( $node, $self->{'datasources'} );
166     return $node;
167 }
168
169
170 sub addParamProp
171 {
172     my $self = shift;
173     my $propsNode = shift;;
174     my $param = shift;
175     my $prop = shift;
176     my $value = shift;
177
178     my $node = $self->{'doc'}->createElement('prop');
179     $node->setAttribute( 'param', $param );
180     $node->setAttribute( 'prop', $prop );
181     $node->setAttribute( 'value', $value );
182     $propsNode->appendChild( $node );
183 }
184
185
186
187 sub addSubtree
188 {
189     my $self = shift;
190     my $parentNode = shift;
191     my $subtreeName = shift;
192     my $params = shift;      # hash reference with param name-value pairs
193     my $templates = shift;   # array reference with template names
194
195     return $self->addChildElement( 0, $parentNode, $subtreeName,
196                                    $params, $templates );
197 }
198
199
200 sub addLeaf
201 {
202     my $self = shift;
203     my $parentNode = shift;
204     my $leafName = shift;
205     my $params = shift;      # hash reference with param name-value pairs
206     my $templates = shift;   # array reference with template names
207
208     return $self->addChildElement( 1, $parentNode, $leafName,
209                                    $params, $templates );
210 }
211
212
213 sub addChildElement
214 {
215     my $self = shift;
216     my $isLeaf = shift;
217     my $parentNode = shift;
218     my $childName = shift;
219     my $params = shift;
220     my $templates = shift;
221
222     my $doc = $self->{'doc'};
223
224     if( not ref( $parentNode ) )
225     {
226         $parentNode = $self->{'datasources'};
227     }
228
229     my $childNode = $doc->createElement( $isLeaf ? 'leaf' : 'subtree' );
230     $childNode->setAttribute( 'name', $childName );
231     $childNode = $parentNode->appendChild( $childNode );
232
233     if( ref( $templates ) )
234     {
235         foreach my $tmpl ( sort @{$templates} )
236         {
237             $self->addTemplateApplication( $childNode, $tmpl );
238         }
239     }
240
241     $self->addParams( $childNode, $params );
242
243     return $childNode;
244 }
245
246
247 sub getChildSubtree
248 {
249     my $self = shift;
250     my $parentNode = shift;
251     my $childName = shift;
252
253     if( not ref( $parentNode ) )
254     {
255         $parentNode = $self->{'datasources'};
256     }
257     
258     my @subtrees =
259         $parentNode->findnodes( 'subtree[@name="' . $childName . '"]' );
260     if( not @subtrees )
261     {
262         Error('Cannot find subtree named ' . $childName);
263         return undef;
264     }
265     return $subtrees[0];
266 }
267
268
269 # Reconstruct the path to the given subtree or leaf
270 sub getElementPath
271 {
272     my $self = shift;
273     my $node = shift;
274
275     my $path = '';
276     if( $node->nodeName() eq 'subtree' )
277     {
278         $path = '/';
279     }
280
281     while( not $node->isSameNode( $self->{'datasources'} ) )
282     {
283         $path = '/' . $node->getAttribute( 'name' ) . $path;
284         $node = $node->parentNode();
285     }
286     
287     return $path;
288 }
289
290
291 sub getTopSubtree
292 {
293     my $self = shift;
294     return $self->{'datasources'};
295 }
296
297
298 sub addTemplateApplication
299 {
300     my $self = shift;
301     my $parentNode = shift;
302     my $template = shift;
303
304     if( not ref( $parentNode ) )
305     {
306         $parentNode = $self->{'datasources'};
307     }
308
309     my $found = 0;
310
311     my $reg = $self->lookupRegistry( $template );
312     if( defined( $reg ) )
313     {
314         $self->{'required_templates'}{$template} = 1;
315         my $name = $reg->{'name'};
316         if( defined( $name ) )
317         {
318             $template = $name;
319         }
320     }
321     
322     my $tmplNode = $self->{'doc'}->createElement( 'apply-template' );
323     $tmplNode->setAttribute( 'name', $template );
324     $parentNode->appendChild( $tmplNode );
325 }
326
327
328 sub addParams
329 {
330     my $self = shift;
331     my $parentNode = shift;
332     my $params = shift;
333
334     if( ref( $params ) )
335     {
336         foreach my $paramName ( sort keys %{$params} )
337         {
338             $self->addParam( $parentNode, $paramName, $params->{$paramName} );
339         }
340     }
341 }
342
343
344 sub addParam
345 {
346     my $self = shift;
347     my $parentNode = shift;
348     my $param = shift;
349     my $value = shift;
350
351     if( not ref( $parentNode ) )
352     {
353         $parentNode = $self->{'datasources'};
354     }
355
356     my $paramNode = $self->{'doc'}->createElement( 'param' );
357     $paramNode->setAttribute( 'name', $param );
358     $paramNode->setAttribute( 'value', $value );
359     $parentNode->appendChild( $paramNode );
360 }
361
362
363 sub addAlias
364 {
365     my $self = shift;
366     my $parentNode = shift;
367     my $aliasPath = shift;
368
369     if( not ref( $parentNode ) ) # I hope nobody would need this
370     {
371         $parentNode = $self->{'datasources'};
372     }
373
374     my $aliasNode = $self->{'doc'}->createElement( 'alias' );
375     $aliasNode->appendText( $aliasPath );
376     $parentNode->appendChild( $aliasNode );
377 }
378
379
380 sub setVar
381 {
382     my $self = shift;
383     my $parentNode = shift;
384     my $name = shift;
385     my $value = shift;
386
387     my $setvarNode = $self->{'doc'}->createElement( 'setvar' );
388     $setvarNode->setAttribute( 'name', $name );
389     $setvarNode->setAttribute( 'value', $value );
390     $parentNode->appendChild( $setvarNode );
391 }
392     
393     
394
395 sub startMonitors
396 {
397     my $self = shift;
398
399     my $node = $self->{'doc'}->createElement('monitors');
400     $self->{'docroot'}->appendChild( $node );
401     return $node;
402 }
403
404
405 sub addMonitorAction
406 {
407     my $self = shift;
408     my $monitorsNode = shift;;
409     my $name = shift;
410     my $params = shift;
411
412     my $node = $self->{'doc'}->createElement('action');
413     $node->setAttribute( 'name', $name );
414     $monitorsNode->appendChild( $node );
415
416     $self->addParams( $node, $params );
417 }
418
419
420 sub addMonitor
421 {
422     my $self = shift;
423     my $monitorsNode = shift;;
424     my $name = shift;
425     my $params = shift;
426
427     my $node = $self->{'doc'}->createElement('monitor');
428     $node->setAttribute( 'name', $name );
429     $monitorsNode->appendChild( $node );
430
431     $self->addParams( $node, $params );
432 }
433
434
435 sub startTokensets
436 {
437     my $self = shift;
438
439     my $node = $self->{'doc'}->createElement('token-sets');
440     $self->{'docroot'}->appendChild( $node );
441     return $node;
442 }
443
444
445 sub addTokenset
446 {
447     my $self = shift;
448     my $tsetsNode = shift;;
449     my $name = shift;
450     my $params = shift;
451
452     my $node = $self->{'doc'}->createElement('token-set');
453     $node->setAttribute( 'name', $name );
454     $tsetsNode->appendChild( $node );
455
456     $self->addParams( $node, $params );
457 }
458
459
460 sub addStatistics
461 {
462     my $self = shift;
463
464     foreach my $stats ( sort keys %{$self->{'statistics'}} )
465     {
466         my $node = $self->{'doc'}->createElement('configbuilder-statistics');
467         $node->setAttribute( 'category', $stats );
468         $node->setAttribute( 'value', $self->{'statistics'}{$stats} );
469         $self->{'docroot'}->appendChild( $node );
470     }
471 }
472
473
474
475 sub requiredFiles
476 {
477     my $self = shift;
478
479     my %files;
480     foreach my $template ( keys %{$self->{'required_templates'}} )
481     {
482         my $file;
483         my $reg = $self->lookupRegistry( $template );
484         if( defined( $reg ) )
485         {
486             $file = $reg->{'source'};
487         }
488         
489         if( defined( $file ) )
490         {
491             $files{$file} = 1;
492         }
493         else
494         {
495             Error('Source file is not defined for template ' . $template .
496                   ' in ConfigBuilder template registry');
497         }
498     }
499     return( sort keys %files );
500 }
501
502
503
504 sub toFile
505 {
506     my $self = shift;
507     my $filename = shift;
508
509     my $fh = new IO::File('> ' . $filename);
510     if( defined( $fh ) )
511     {
512         my $ok = $self->{'doc'}->toFH( $fh, 2 );
513         $fh->close();
514         return $ok;
515     }
516     else
517     {
518         return undef;
519     }
520 }
521
522 1;
523
524
525 # Local Variables:
526 # mode: perl
527 # indent-tabs-mode: nil
528 # perl-indent-level: 4
529 # End: