File Coverage

blib/lib/Helios/Config.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Helios::Config;
2              
3 1     1   795 use 5.008;
  1         3  
  1         30  
4 1     1   3 use strict;
  1         1  
  1         19  
5 1     1   3 use warnings;
  1         1  
  1         15  
6 1     1   3 use File::Spec;
  1         1  
  1         18  
7 1     1   449 use Sys::Hostname;
  1         908  
  1         50  
8              
9 1     1   692 use Config::IniFiles;
  1         27479  
  1         33  
10              
11 1     1   514 use Helios::ObjectDriver::DBI;
  0            
  0            
12             use Helios::ConfigParam;
13             use Helios::Error::ConfigError;
14              
15             our $VERSION = '2.811_3850';
16              
17             =head1 NAME
18              
19             Helios::Config - base class for Helios configuration system
20              
21             =head1 SYNOPSIS
22              
23             # to determine a service's config based on
24             # a particular conf file and host
25             use Helios::Config;
26             Helios::Config->init(
27             conf_file => '/path/to/helios.ini',
28             hostname => 'host',
29             service => 'ServiceName'
30             );
31             my $config = Helios::Config->parseConfig();
32            
33             # if $HELIOS_INI env var is set and current host is used
34             use Helios::Config;
35             Helios::Config->init(service => 'ServiceName');
36             my $config = Helios::Config->parseConfig();
37            
38             # same as above; parseConfig() will automatically call init()
39             use Helios::Config;
40             my $config = Helios::Config->parseConfig(service => 'ServiceName');
41            
42             # you can also use accessor methods; for example, with Helios::TestService:
43             use Helios::Config;
44             Helios::Config->setService('Helios::TestService');
45             Helios::Config->init();
46             my $config = Helios::Config->parseConfig();
47              
48             # catch config errors with eval {}
49             # Try::Tiny works too
50             use Helios::Config;
51             my $config;
52             eval {
53             Helios::Config->init(service => 'Helios::TestService');
54             $config = Helios::Config->parseConfig();
55             } or do {
56             my $E = $@;
57             if ( $E->isa('Helios::Error::ConfigError') ) {
58             print "Helios configuration error: $E\n";
59             } else {
60             print "I do not know what happened, but it was bad.\n";
61             }
62             };
63            
64              
65             =head1 DESCRIPTION
66              
67             Helios::Config is the standard class for determining configuration information
68             in the Helios framework. It handles parsing configuration information from the
69             Helios configuration file, and determining the configuration for services from
70             information in the Helios collective database. Helios::Config also acts as a
71             base class for the Helios configuration API; services can define specialized
72             Helios::Config subclasses to extend configuration subsystem functionality.
73              
74             Normally, the developer of Helios services does not need to interact with
75             Helios::Config directly. Helios normally handles all configuration
76             transparently during service setup before a service's run() method is called.
77             A Helios service need only call its getConfig() method to retrieve a hashref of
78             its configuration parameters. Only those wanting to retrieve a Helios service
79             configuration outside of the service (e.g. to write an external utility as
80             an adjunct to a Helios service) or those with advanced configuration needs
81             will need to work with Helios::Config directly.
82              
83             It should be noted that, like Helios::Logger subclasses, Helios::Config methods
84             are actually class methods, not instance (object) methods. If you need to
85             implement other methods outside of the methods defined
86             below, make sure you implement them as class methods.
87              
88             =head1 ACCESSOR METHODS
89              
90             Helios::Config provides 7 set/get accessor pairs to provide access to
91             configuration data. There are 3 categories of accessors: ones that need to
92             be set before configuration parsing is initialized, those that are used during
93             configuration parsing, and those that hold the end results of the parsing
94             procedure (i.e. the actual configuration helios.pl and the Helios service will
95             need).
96              
97             =head2 INITIALIZATION ACCESSORS
98              
99             These need to be set before configuration parsing is initialized with the
100             init() method. If they are not set, the init() method will try to set them
101             from data in the environment.
102              
103             set/getConfFile() path to the Helios conf file
104             -defaults to $HELIOS_INI env variable
105             set/getHostname() hostname the Helios service is running on
106             -defaults to results of Sys::Hostname::hostname()
107             set/getService() name of the running Helios service
108             -defaults to undefined, which will cause the
109             resulting config to contain the contents of the
110             helios.ini [global] section only
111              
112             =head2 CONFIG PARSING ACCESSORS
113              
114             These methods will be set during the configuration parsing process. Most
115             Helios service developers will not need to be aware of these, but if you are
116             developing a specialized Helios::Config subclass, they may be useful.
117              
118             set/getConfFileConfig() the config info parsed from helios.ini
119             set/getDbConfig() the config info parsed from the collective database
120             set/getDriver() Data::ObjectDriver object connected to collective db
121              
122             =head2 PARSING RESULTS ACCESSORS
123              
124             This method contains the results of the configuration parsing process. In
125             other words, the actual configuration information for the given Helios service.
126              
127             set/getConfig() the complete config info from both conf file & db
128              
129             =cut
130              
131             my $Debug = 0;
132             my $Errstr;
133             sub debug { my $self = shift; @_ ? $Debug = shift : $Debug; }
134             sub errstr { my $self = shift; @_ ? $Errstr = shift : $Errstr; }
135              
136             my $ConfFile = $ENV{HELIOS_INI};
137             sub setConfFile {
138             my $var = $_[0]."::ConfFile";
139             no strict 'refs';
140             $$var = $_[1];
141             }
142             sub getConfFile {
143             my $var = $_[0]."::ConfFile";
144             no strict 'refs';
145             return $$var;
146             }
147              
148             my $Hostname = hostname();
149             sub setHostname {
150             my $var = $_[0]."::Hostname";
151             no strict 'refs';
152             $$var = $_[1];
153             }
154             sub getHostname {
155             my $var = $_[0]."::Hostname";
156             no strict 'refs';
157             return $$var;
158             }
159              
160              
161             my $ServiceName;
162             sub setServiceName {
163             my $var = $_[0]."::ServiceName";
164             no strict 'refs';
165             $$var = $_[1];
166             }
167             sub getServiceName {
168             my $var = $_[0]."::ServiceName";
169             no strict 'refs';
170             return $$var;
171             }
172             sub setService { setServiceName(@_); }
173             sub getService { getServiceName(@_); }
174              
175             my $Driver;
176             sub setDriver {
177             my $var = $_[0]."::Driver";
178             no strict 'refs';
179             $$var = $_[1];
180             }
181             sub getDriver {
182             initDriver(@_);
183             }
184             sub initDriver {
185             my $self = shift;
186             my $config = $self->getConfFileConfig();
187             if ($self->debug) { print __PACKAGE__.'->initDriver('.$config->{dsn}.','.$config->{user}.")\n"; }
188             my $driver = Helios::ObjectDriver::DBI->new(
189             dsn => $config->{dsn},
190             username => $config->{user},
191             password => $config->{password}
192             );
193             if ($self->debug) { print __PACKAGE__.'->initDriver() DRIVER: ',$driver,"\n"; }
194             $self->setDriver($driver);
195             return $driver;
196             }
197              
198              
199             my $Config;
200             sub setConfig {
201             my $var = $_[0]."::Config";
202             no strict 'refs';
203             $$var = $_[1];
204             }
205             sub getConfig {
206             my $var = $_[0]."::Config";
207             no strict 'refs';
208             return $$var;
209             }
210              
211             my $ConfFileConfig;
212             sub setConfFileConfig {
213             my $var = $_[0]."::ConfFileConfig";
214             no strict 'refs';
215             $$var = $_[1];
216             }
217             sub getConfFileConfig {
218             my $var = $_[0]."::ConfFileConfig";
219             no strict 'refs';
220             return $$var;
221             }
222              
223             my $DbConfig;
224             sub setDbConfig {
225             my $var = $_[0]."::DbConfig";
226             no strict 'refs';
227             $$var = $_[1];
228             }
229             sub getDbConfig {
230             my $var = $_[0]."::DbConfig";
231             no strict 'refs';
232             return $$var;
233             }
234              
235              
236              
237             =head1 CONFIGURATION INITIALIZATION METHODS
238              
239             =head2 init([%params])
240              
241             Prepares Helios::Config to parse the configuration for a particular Helios
242             service. Accepts initialization information as a hash of parameters; if a
243             parameter is not given, init() will attempt to default to values based on
244             information from the environment.
245              
246             The init() method accepts 4 arguments:
247              
248             CONF_FILE path to the helios.ini file (default: $HELIOS_INI env var)
249             HOSTNAME hostname (default: current hostname from Sys::Hostname::hostname())
250             DEBUG enable/disable debug mode (default: disabled)
251             SERVICE name of the Helios service to determine configuration for
252             (default: none)
253              
254             For example, to initialize Helios::Config to parse the configuration
255             information from /etc/helios/helios.ini for the Helios::TestService service
256             on the host named host1.hosting.com, one would call init() as:
257              
258             Helios::Config->init(
259             CONF_FILE => '/etc/helios/helios.ini',
260             HOSTNAME => 'host1.hosting.com',
261             SERVICE => 'Helios::TestService'
262             );
263              
264             Normally the host and config file are specified by the operating system and the
265             $HELIOS_INI environment variable, so a more typical init() call in a properly
266             set up Helios collective would only specify the service:
267              
268             Helios::Config->init(SERVICE => 'Helios::TestService');
269              
270             =cut
271              
272             sub init {
273             my $self = shift;
274             my %params = @_;
275             foreach (keys %params) {
276             $params{lc($_)} = $params{$_};
277             }
278             if ( defined($params{conf_file}) ) { $self->setConfFile($params{conf_file}); }
279             if ( defined($params{service}) ) { $self->setServiceName($params{service}); }
280             if ( defined($params{hostname}) ) { $self->setHostname($params{hostname}); }
281             if ( defined($params{debug}) ) { $self->debug($params{debug}); }
282            
283             # pull hostname from the environment if not already set
284             unless ( $self->getHostname() ) {
285             $self->setHostname( hostname() );
286             }
287             # again, pull conf file from environment if not already set
288             if ( !defined($self->getConfFile()) && defined($ENV{HELIOS_INI}) ) {
289             $self->setConfFile( $ENV{HELIOS_INI} );
290             }
291            
292             # init() clears previous config
293             $self->setConfFileConfig(undef);
294             $self->setDbConfig(undef);
295             $self->setConfig(undef);
296            
297             return $self;
298             }
299              
300              
301             =head1 CONFIGURATION PARSING METHODS
302              
303             =head2 parseConfig([%params])
304              
305             Given a set of optional initialization parameters, parseConfig() will parse
306             the helios.ini config file and query the Helios collective database for
307             configuration information for a particular Helios service, combining the
308             information into a single set of configuration information, which is returned
309             to the calling routine as a hash reference.
310              
311             The parseConfig() method controls the actual parsing and derivation of a
312             service's configuration. This process has 4 steps:
313              
314             =over 4
315              
316             =item * Initialization (optional)
317              
318             If parseConfig() was given options, it will call the init() method
319             to (re-)initialize the configuration parsing process. If no options were
320             specified, parseConfig() assumes all the necessary options have already been
321             set.
322              
323             =item * Conf file parsing
324              
325             If the configuration file has not yet been parsed, parseConfig() calls
326             parseConfFile() to parse it. If the conf file information has already been
327             parsed, parseConfig() skips this step. This is to ensure the helios.pl daemon
328             and Helios worker processes do not become unstable if the filesystem with the
329             config file becomes unmounted.
330              
331             See the parseConfFile() method entry for more information about this phase of
332             configuration parsing.
333              
334             =item * Conf database parsing
335              
336             Given the information obtained in the previous step, parseConfig() calls the
337             parseConfDb() method to query the Helios collective database for configuration
338             information for the specified Helios service. Unlike the previous step,
339             parseConfig() B calls parseConfDb(). This is so the helios.pl daemon
340             and Helios worker processes can dynamically update their configuration from the
341             database.
342              
343             See the parseConfDb() method entry for more information about this phase of
344             configuration parsing.
345              
346             =item * Merging configurations
347              
348             Once the configurations from the conf file and the database have been
349             acquired, parseConfig() merges the config hashes together into a single hash of
350             configuration parameters for the specified service. This single config hashref
351             is returned to the calling routine. A cached copy is also made available
352             via the getConfig() method.
353              
354             Configuration parameters for a service specified in the collective
355             database override parameters specified in the conf file.
356              
357             NOTE: Prior to Helios::Config, Helios assembled configuration parameter hashes
358             differently. Originally, both helios.ini and database config parameters were
359             reparsed each time a config refresh was requested, and the new parameters were
360             merged with the old configuration values. This caused config values to "stick"
361             even if they were completely deleted from the database or conf file. For
362             example, deleting a HOLD parameter was not enough to take a service out of hold
363             mode; the Helios administrator had to set HOLD to 0.
364              
365             Helios::Config merges configurations differently. Though the conf file config
366             is only parsed once, each refresh of the database config starts with a new
367             hash, and the config merge process starts with a brand new hash as well. That
368             way the config hash returned by parseConfig() contains only the B
369             config parameters, leading to a more predictable configuration subsystem.
370              
371             =back
372              
373             =cut
374              
375             sub parseConfig {
376             my $self = shift;
377             my $conf_file_config;
378             my $conf_db_config;
379              
380             # if we were passed options,
381             # OR we haven't been initialized,
382             # go ahead and call init() (with the given options)
383             if (@_ || !( $self->getConfFile() && $self->getHostname() ) ) {
384             $self = $self->init(@_);
385             }
386            
387             # only parse conf file once
388             if ( $self->getConfFileConfig() ) {
389             $conf_file_config = $self->getConfFileConfig();
390             } else {
391             $conf_file_config = $self->parseConfFile();
392             }
393            
394             # conf db always gets reparsed
395             $conf_db_config = $self->parseConfDb();
396              
397             # merge configs
398             # deref conf file hashref so db conf
399             # doesn't leak into file conf when merged
400             my %conf = %{$self->getConfFileConfig()};
401             while ( my ($key, $value) = each %$conf_db_config ) {
402             $conf{$key} = $value;
403             }
404             $self->setConfig(\%conf);
405             return \%conf;
406             }
407              
408              
409             =head2 parseConfFile([$conf_file, $service_name])
410              
411             Given an optional conf file and an optional service name, parseConfFile()
412             parses the conf file and returns the resulting hashref to the calling routine.
413             It also makes the hashref available via the getConfFileConfig() accessor.
414             If either the conf file or the service is not specified, the values from the
415             getConfFile() and/or getService() accessor(s) are used. The conf file
416             location is set by init() to the value of the $HELIOS_INI environment variable
417             unless otherwise specified.
418              
419             The default Helios configuration file is the common .ini file format, where
420             section headings are denoted by brackets ([]). Lines not starting with [ are
421             considered parameters belonging to the last declared section. Lines starting
422             with # or ; are considered comments and are ignored. See L
423             (the default underlying file parser) for more format details.
424              
425             Helios requires at least one section, [global], in the conf file, which should
426             contain at least 3 parameters:
427              
428             dsn DBI datasource name of the Helios collective database
429             user the user to use to access the Helios collective db
430             password the password to use to access the Helios collective db
431              
432             Without these, the helios.pl daemon will be unable to connect to the collective
433             database and will fail to start.
434              
435             You may also specify other configuration parameters in the [global] section.
436             Options set in the [global] section will appear in the configuration parameters
437             of all services using that conf file. This can be useful if you need multiple
438             services on a host to share a configuration (e.g. you want to configure all
439             services on a host to log messages to a syslogd facility using
440             HeliosX::Logger::Syslog).
441              
442             In addition to [global], you can create other sections as well. If a section
443             name matches the service name specified, the configuration parameters in that
444             section will be included in the config hash returned to the calling routine.
445             You can use this feature to set defaults for a service, or to set sensitive
446             parameters (e.g. passwords) that you do not want to be changable from the
447             Helios::Panoptes web admin console.
448              
449             For example, a Helios conf file that configures the Helios collective db and
450             sets some config parameters for the Helios::TestService service would look
451             something like:
452              
453             [global]
454             dsn=dbi:mysql:host=dbhost;db=helios_db
455             user=helios_user
456             password=xyz123
457            
458             [Helios::TestService]
459             MAX_WORKERS=1
460             loggers=HeliosX::Logger::Syslog
461             syslog_facility=user
462             syslog_options=pid
463              
464             =cut
465              
466             sub parseConfFile {
467             my $self = shift;
468             my $conf_file = @_ ? shift : $self->getConfFile();
469             my $service_name = @_ ? shift : $self->getServiceName();
470             my $conf;
471            
472             unless ($conf_file) { Helios::Error::ConfigError->throw("No conf file specified"); }
473             unless (-r $conf_file) { Helios::Error::ConfigError->throw("Cannot read conf file $conf_file"); }
474            
475             my $cif = Config::IniFiles->new( -file => $conf_file );
476             unless ( defined($cif) ) {
477             # @Config::IniFiles::errors contains the parse error(s);
478             my $E = join(" ", @Config::IniFiles::errors);
479             Helios::Error::ConfigError->throw("parseConfFile(): Invalid config file: $E");
480             }
481              
482             # global must exist
483             if ($cif->SectionExists("global") ) {
484             foreach ( $cif->Parameters("global") ) {
485             $conf->{$_} = $cif->val("global", $_);
486             }
487             }
488              
489             # if there's a section specifically for this service class, read it too
490             # (it will effectively override the global section, BTW)
491             if ( $cif->SectionExists( $service_name ) ) {
492             foreach ( $cif->Parameters($service_name) ) {
493             $conf->{$_} = $cif->val($service_name, $_);
494             }
495             }
496              
497             $self->setConfFileConfig($conf);
498              
499             return $conf;
500             }
501              
502              
503             =head2 parseConfDb([$service_name, $hostname])
504              
505             The parseConfDb() method queries the Helios collective database for
506             configuration parameters matching the specified service name and hostname and
507             returns a hashref with those parameters to the calling routine. If the service
508             name and hostname are not specified, the values returned from the
509             getService() and getHostname() accessors are used. The getHostname() value
510             is normally set by init() to the value returned by Sys::Hostname::hostname()
511             unless otherwise specified.
512              
513             The default parseConfDb() queries the HELIOS_PARAMS_TB table in the Helios
514             collective database. Two separate queries are done:
515              
516             =over 4
517              
518             =item *
519              
520             Config params matching the service name and a host of '*'. Config params
521             with a '*' host apply to all instances of the service in the entire
522             collective.
523              
524             =item *
525              
526             Config params matching the service name and the current hostname. Config
527             params with a specific hostname apply only to instances of that service on
528             that particular host. These are useful for HOLDing or HALTing services only on
529             one host, or working with differences between hosts (e.g. a host with 4 cores
530             and 16GB of RAM can support a higher MAX_WORKERS value than a dual core system
531             with 2GB of memory).
532              
533             =back
534              
535             The results of these two queries are merged, and the resulting hashref returned
536             to the calling routine. Config parameters for a specific host override config
537             params for all ('*') hosts.
538              
539             Configuration parameters in the Helios collective database can be set using
540             the Helios::Panoptes web admin console or using your database's standard SQL
541             commands.
542              
543             =cut
544              
545             sub parseConfDb {
546             my $self = shift;
547             my $service_name = @_ ? shift : $self->getServiceName();
548             my $hostname = @_ ? shift : $self->getHostname();
549             my $conf_all_hosts = {};
550             my $conf_this_host = {};
551             my $conf = {};
552             my @dbparams;
553              
554             my $driver = $self->getDriver();
555            
556             @dbparams = $driver->search( 'Helios::ConfigParam' => {
557             worker_class => $service_name,
558             host => ['*', $self->getHostname() ],
559             }
560             );
561             foreach(@dbparams) {
562             if ($self->debug) { print $_->param(),'=>',$_->value(),"\n"; }
563             if ( $_->host eq '*') {
564             $conf_all_hosts->{$_->param()} = $_->value();
565             } else {
566             $conf_this_host->{ $_->param() } = $_->value();
567             }
568             }
569            
570             $conf = $conf_all_hosts;
571             while ( my ($key, $value) = each %$conf_this_host) {
572             $conf->{$key} = $value;
573             }
574             $self->setDbConfig($conf);
575             return $conf;
576             }
577              
578              
579              
580              
581              
582             =head2 getParam(param => $param_name [, service => $service_name] [, hostname => $hostname])
583              
584             Given a service name, parameter name, and (optionally) a hostname, getParam()
585             returns the parameter name's value to the calling routine.
586              
587             If hostname is not specified, the current host is assumed.
588              
589             =cut
590              
591             sub getParam {
592             my $self = shift;
593             my %params;
594             if (scalar @_ == 1) {
595             $params{param} = $_[0];
596             } else {
597             %params = @_;
598             }
599             my $service_name = defined($params{service}) ? $params{service} : $self->getServiceName;
600             my $param_name = $params{param};
601             my $host = defined($params{hostname}) ? $params{hostname} : $self->getHostname();
602             my $conf = {};
603              
604             # shortcut: if the current config hash has already been retrieved
605             # and the requested param is set, return *that* param
606             if ( defined($self->getConfig()) &&
607             defined($self->getService) && ($self->getService eq $service_name) &&
608             defined($self->getHostname) && ($self->getHostname eq $host) &&
609             defined($self->getConfig()->{$param_name})
610             ) {
611             return $self->getConfig()->{$param_name};
612             }
613              
614             # if we don't have everything, stop before we try
615             unless ($service_name && $host && $param_name) {
616             Helios::Error::ConfigError->throw('getParam(): service and param are required.');
617             }
618              
619              
620             eval {
621             my $driver = $self->getDriver();
622             my @dbparams = $driver->search( 'Helios::ConfigParam' => {
623             worker_class => $service_name,
624             param => $param_name,
625             host => ['*', $host ],
626             }
627             );
628             my %conf_all_hosts;
629             my %conf_this_host;
630             foreach(@dbparams) {
631             if ($self->debug) { print $_->worker_class(),'|', $_->host(),'|', $_->param(),'=>',$_->value(),"\n"; }
632             if ( $_->host() eq '*') {
633             $conf_all_hosts{$_->param()} = $_->value();
634             } else {
635             $conf_this_host{ $_->param() } = $_->value();
636             }
637             }
638             $conf = \%conf_all_hosts;
639            
640             # if host=*, we're done
641             # otherwise, use the given host, if the param is available
642             if ( $host ne '*' && defined($conf_this_host{$param_name}) ) {
643             $conf->{$param_name} = $conf_this_host{$param_name};
644             }
645              
646             1;
647             } or do {
648             my $E = $@;
649             Helios::Error::ConfigError->throw("getParam(): $E");
650             };
651              
652             if ($self->debug && !defined($conf->{$param_name})) {
653             print "$service_name|$host|$param_name not found.\n";
654             }
655              
656             return $conf->{$param_name};
657             }
658              
659             # this is officially *undocumented*
660             sub getAllParams {
661             my $self = shift;
662             my $conf_all_hosts = {};
663             my $conf_this_host = {};
664             my $conf = {};
665              
666             eval {
667             my $driver = $self->getDriver();
668             my @dbparams = $driver->search( 'Helios::ConfigParam' );
669             foreach(@dbparams) {
670             if ($self->debug) { print $_->param(),'=>',$_->value(),"\n"; }
671             $conf->{ $_->worker_class() }->{ $_->host() }->{ $_->param() } = $_->value();
672             }
673            
674             1;
675             } or do {
676             my $E = $@;
677             Helios::Error::ConfigError->throw("getAllParams(): $E");
678             };
679              
680             return $conf;
681             }
682              
683              
684             =head2 setParam(param => $param_name [, service => $service_name] [, hostname => $hostname], value => $value)
685              
686             Given a service name, parameter name, parameter value, and (optionally)
687             a hostname, setParam() sets the value for that parameter for that service
688             (and host) in the Helios collective database. If hostname is not specified,
689             the current host is assumed. To set a parameter for all instances of a
690             service in a collective, set the hostname to '*'.
691              
692             =cut
693              
694             sub setParam {
695             my $self = shift;
696             my %params = @_;
697             my $service_name = defined($params{service}) ? $params{service} : $self->getServiceName;
698             my $param_name = $params{param};
699             my $host = defined($params{hostname}) ? $params{hostname} : $self->getHostname;
700             my $value = $params{value};
701             my $cp;
702              
703             # if we don't have everything, stop before we try
704             unless ($service_name && $host && $param_name && defined($value) ) {
705             Helios::Error::ConfigError->throw('setParam(): Service, param name, and value are required.');
706             }
707              
708             eval {
709             # HELIOS_PARAMS_TB does not have a Primary Key.
710             # Because of that, we cannot use D::OD in the normal way (search() or
711             # lookup(), change the value, then save()).
712             # If we find an existing param matching service|host|param, we have to
713             # delete ALL of the matching service|host|param in the table, then
714             # create a new one.
715             # In SQL terms, we'll always do SELECT>DELETE>INSERT instead of
716             # SELECT>UPDATE|INSERT.
717              
718             # query for existing service/host/param
719             my $driver = $self->getDriver();
720             my @cps = $driver->search( 'Helios::ConfigParam' => {
721             worker_class => $service_name,
722             param => $param_name,
723             host => $host,
724             }
725             );
726             my $cp = shift @cps;
727            
728             # ok, if there is a service/host/param exists,
729             # we have to clear it out first, then create a new one from scratch
730             if (defined($cp)) {
731             if ($self->debug) { print "$service_name|$host|$param_name already set to ",$cp->value,". Clearing.\n"; }
732             $driver->remove('Helios::ConfigParam' =>
733             {
734             worker_class => $service_name,
735             host => $host,
736             param => $param_name
737             },
738             { nofetch => 1 }
739             );
740             } else {
741             if ($self->debug) { print "$service_name|$host|$param_name not found. Creating.\n"; }
742             }
743              
744             # now, create a new Helios::ConfigParam and insert into the database
745             if ($self->debug) { print "$service_name|$host|$param_name setting to $value\n"; }
746             $cp = Helios::ConfigParam->new();
747             $cp->worker_class($service_name);
748             $cp->host($host);
749             $cp->param($param_name);
750             $cp->value($value);
751             $driver->insert($cp);
752            
753             1;
754             } or do {
755             my $E = $@;
756             # rethrow the error as a ConfigError
757             Helios::Error::ConfigError->throw("setParam(): $E");
758             };
759              
760             return 1;
761             }
762              
763              
764             =head2 unsetParam(param => $param_name [, service => $service_name] [, hostname => $hostname,])
765              
766             Given a service name, parameter name, and (optionally) a hostname, unsetParam()
767             deletes that parameter's entry in the Helios collective database.
768              
769             If hostname is not specified, the current host is assumed. To unset a
770             parameter that is in effect for all instances of a service, you must set the
771             hostname to '*'.
772              
773             =cut
774              
775             sub unsetParam {
776             my $self = shift;
777             my %params = @_;
778             my $service_name = defined($params{service}) ? $params{service} : $self->getServiceName;
779             my $param_name = $params{param};
780             my $host = defined($params{hostname}) ? $params{hostname} : $self->getHostname;
781             my $cp;
782              
783             # if we don't have everything, stop before we try
784             unless ($service_name && $host && $param_name) {
785             Helios::Error::ConfigError->throw('setParam(): Service and param name are required.');
786             }
787              
788             eval {
789             # delete ALL of the matching service|host|param in the table
790             my $driver = $self->getDriver();
791             if ($self->debug) { print "Clearing $service_name|$host|$param_name from param table.\n"; }
792             $driver->remove('Helios::ConfigParam' =>
793             {
794             worker_class => $service_name,
795             host => $host,
796             param => $param_name
797             },
798             { nofetch => 1 }
799             );
800            
801             1;
802             } or do {
803             my $E = $@;
804             # rethrow the error as a ConfigError
805             Helios::Error::ConfigError->throw("unsetParam(): $E");
806             };
807              
808             return 1;
809             }
810              
811              
812              
813             1;
814             __END__