File Coverage

blib/lib/MogileFS/Config.pm
Criterion Covered Total %
statement 37 203 18.2
branch 8 112 7.1
condition 0 32 0.0
subroutine 12 32 37.5
pod 0 15 0.0
total 57 394 14.4


line stmt bran cond sub pod time code
1             package MogileFS::Config;
2 21     21   112 use strict;
  21         35  
  21         573  
3             require Exporter;
4 21     21   8423 use MogileFS::ProcManager;
  21         44  
  21         847  
5 21     21   11966 use Getopt::Long;
  21         176564  
  21         71  
6 21     21   12808 use MogileFS::Store;
  21         53  
  21         502  
7 21     21   8296 use Sys::Hostname ();
  21         18675  
  21         1610  
8              
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw($DEBUG config set_config FSCK_QUEUE REBAL_QUEUE);
11             our @EXPORT_OK = qw(DEVICE_SUMMARY_CACHE_TIMEOUT);
12              
13             our ($DEFAULT_CONFIG, $MOGSTORED_STREAM_PORT, $DEBUG);
14             $DEBUG = 0;
15             $DEFAULT_CONFIG = "/etc/mogilefs/mogilefsd.conf";
16             $MOGSTORED_STREAM_PORT = 7501;
17              
18 21     21   122 use constant FSCK_QUEUE => 1;
  21         39  
  21         1077  
19 21     21   110 use constant REBAL_QUEUE => 2;
  21         38  
  21         907  
20 21     21   117 use constant DEVICE_SUMMARY_CACHE_TIMEOUT => 15;
  21         51  
  21         53501  
21              
22             my %conf;
23             my %server_settings;
24             my $has_cached_settings = 0;
25             sub set_config {
26 48 100   48 0 1363 shift if @_ == 3;
27 48         92 my ($k, $v) = @_;
28              
29             # if a child, propagate to parent
30 48 50       206 if (my $worker = MogileFS::ProcManager->is_child) {
    50          
31 0         0 $worker->send_to_parent(":set_config_from_child $k $v");
32             } elsif (defined $v) {
33 48         187 MogileFS::ProcManager->send_to_all_children(":set_config_from_parent $k $v");
34             }
35              
36 48         108 return set_config_no_broadcast($k, $v);
37             }
38              
39             sub set_config_no_broadcast {
40 78 100   78 0 189 shift if @_ == 3;
41 78         131 my ($k, $v) = @_;
42 78         176 return $conf{$k} = $v;
43             }
44              
45             set_config('default_mindevcount', 2);
46             set_config('min_fidid', 0);
47              
48             our (
49             %cmdline,
50             %cfgfile,
51             $config,
52             $skipconfig,
53             $daemonize,
54             $db_dsn,
55             $db_user,
56             $db_pass,
57             $conf_port,
58             $listen,
59             $query_jobs,
60             $delete_jobs,
61             $replicate_jobs,
62             $fsck_jobs,
63             $reaper_jobs,
64             $monitor_jobs,
65             $job_master, # boolean
66             $max_handles,
67             $min_free_space,
68             $max_disk_age,
69             $node_timeout, # time in seconds to wait for storage node responses
70             $conn_timeout, # time in seconds to wait for connection to storage node
71             $conn_pool_size, # size of the HTTP connection pool
72             $pidfile,
73             $repl_use_get_port,
74             $local_network,
75             );
76              
77             my $default_mindevcount;
78              
79             sub load_config {
80 0     0 0 0 my $dummy_workerport;
81              
82             # Command-line options will override
83 0         0 Getopt::Long::Configure( "bundling" );
84             Getopt::Long::GetOptions(
85             'c|config=s' => \$config,
86             's|skipconfig' => \$skipconfig,
87             'd|debug+' => \$cmdline{debug},
88             'D|daemonize' => \$cmdline{daemonize},
89             'dsn=s' => \$cmdline{db_dsn},
90             'dbuser=s' => \$cmdline{db_user},
91             'dbpass:s' => \$cmdline{db_pass},
92             'user=s' => \$cmdline{user},
93             'p|confport=i' => \$cmdline{conf_port},
94             'l|listen=s@' => \$cmdline{listen},
95             'w|workers=i' => \$cmdline{query_jobs},
96             'no_http' => \$cmdline{no_http}, # OLD, we just eat it to shut it up.
97             'workerport=i' => \$dummy_workerport, # eat it for backwards compat
98             'max_disk_age=i' => \$cmdline{max_disk_age},
99             'min_free_space=i' => \$cmdline{min_free_space},
100             'default_mindevcount=i' => \$cmdline{default_mindevcount},
101             'node_timeout=i' => \$cmdline{node_timeout},
102             'conn_timeout=i' => \$cmdline{conn_timeout},
103             'conn_pool_size=i' => \$cmdline{conn_pool_size},
104             'max_handles=i' => \$cmdline{max_handles},
105             'pidfile=s' => \$cmdline{pidfile},
106             'no_schema_check' => \$cmdline{no_schema_check},
107             'plugins=s@' => \$cmdline{plugins},
108             'repl_use_get_port=i' => \$cmdline{repl_use_get_port},
109             'local_network=s' => \$cmdline{local_network},
110             'mogstored_stream_port' => \$cmdline{mogstored_stream_port},
111             'job_master!' => \$cmdline{job_master},
112 0         0 );
113              
114             # warn of old/deprecated options
115 0 0       0 warn "The command line option --workerport is no longer needed (and has no necessary replacement)\n"
116             if $dummy_workerport;
117              
118 0 0 0     0 $config = $DEFAULT_CONFIG if !$config && -r $DEFAULT_CONFIG;
119              
120             # Read the config file if one was specified
121 0 0 0     0 if ($config && !$skipconfig) {
122 0 0       0 open my $cf, "<$config" or die "open: $config: $!";
123              
124 0         0 my $configLine = qr{
125             ^\s* # Leading space
126             ([\w.]+) # Key
127             \s+ =? \s* # space + optional equal + optional space
128             (.+?) # Value
129             \s*$ # Trailing space
130             }x;
131              
132 0         0 my $linecount = 0;
133 0         0 while (defined( my $line = <$cf> )) {
134 0         0 $linecount++;
135 0 0       0 next if $line =~ m!^\s*(\#.*)?$!;
136 0 0       0 die "Malformed config file (line $linecount)" unless $line =~ $configLine;
137              
138 0         0 my ( $key, $value ) = ( $1, $2 );
139 0 0       0 print STDERR "Setting '$key' to '$value'\n" if $cmdline{debug};
140 0         0 $cfgfile{$key} = $value;
141             }
142              
143 0         0 close $cf;
144             }
145              
146             # Fill in defaults for those values which were either loaded from config or
147             # specified on the command line. Command line takes precedence, then values in
148             # the config file, then the defaults.
149 0         0 $daemonize = choose_value( 'daemonize', 0 );
150 0         0 $db_dsn = choose_value( 'db_dsn', "DBI:mysql:mogilefs" );
151 0         0 $db_user = choose_value( 'db_user', "mogile" );
152 0         0 $db_pass = choose_value( 'db_pass', "", 1 );
153 0         0 $conf_port = choose_value( 'conf_port', 7001 );
154 0   0     0 $query_jobs = set_config("query_jobs",
155             choose_value( 'listener_jobs', undef) || # undef if not present, then we
156             choose_value( 'query_jobs', 20 )); # fall back to query_jobs, new name
157 0         0 $delete_jobs = choose_value( 'delete_jobs', 1 );
158 0         0 $replicate_jobs = choose_value( 'replicate_jobs', 1 );
159 0         0 $fsck_jobs = choose_value( 'fsck_jobs', 1 );
160 0         0 $reaper_jobs = choose_value( 'reaper_jobs', 1 );
161 0         0 $job_master = choose_value( 'job_master', 1 );
162 0         0 $monitor_jobs = choose_value( 'monitor_jobs', 1 );
163 0         0 $min_free_space = choose_value( 'min_free_space', 100 );
164 0         0 $max_disk_age = choose_value( 'max_disk_age', 5 );
165 0         0 $max_handles = choose_value( 'max_handles', 0 );
166 0   0     0 $DEBUG = choose_value( 'debug', $ENV{DEBUG} || 0 );
167 0         0 $pidfile = choose_value( 'pidfile', "" );
168              
169 0         0 choose_value( 'mogstored_stream_port', $MOGSTORED_STREAM_PORT );
170 0         0 choose_value( 'default_mindevcount', 2 );
171 0         0 $node_timeout = choose_value( 'node_timeout', 2 );
172 0         0 $conn_timeout = choose_value( 'conn_timeout', 2 );
173 0         0 $conn_pool_size = choose_value( 'conn_pool_size', 20 );
174              
175 0         0 choose_value( 'rebalance_ignore_missing', 0 );
176 0         0 $repl_use_get_port = choose_value( 'repl_use_get_port', 0 );
177 0         0 $local_network = choose_value( 'local_network', '' );
178              
179 0         0 choose_value( 'no_schema_check', 0 );
180              
181             # now load plugins
182 0         0 my @plugins;
183 0 0       0 push @plugins, $cfgfile{plugins} if $cfgfile{plugins};
184 0 0       0 push @plugins, @{$cmdline{plugins}} if $cmdline{plugins};
  0         0  
185              
186 0         0 foreach my $plugin (@plugins) {
187 0         0 load_plugins($plugin);
188             }
189              
190 0         0 choose_value('user', '');
191              
192             # fix up config file listen option
193 0 0       0 $cfgfile{listen} = [ split(/\s*,\s*/, $cfgfile{listen}) ] if defined $cfgfile{listen};
194              
195             # now let's fix up the listen option to include the port if it doesn't already; we can't use
196             # choose_value as that uses set_config and that sends to children; this option doesn't apply
197 0   0     0 my $temp_listen = $cmdline{listen} || $cfgfile{listen} || [ '0.0.0.0' ];
198 0 0       0 $conf{listen} = $listen = [ map { /:/ ? $_ : "$_:$conf{conf_port}" } @$temp_listen ];
  0         0  
199             }
200              
201             ### FUNCTION: choose_value( $name, $default )
202             sub choose_value {
203 0     0 0 0 my ( $name, $default ) = @_;
204 0 0       0 return set_config($name, $cmdline{$name}) if defined $cmdline{$name};
205 0 0       0 return set_config($name, $cfgfile{$name}) if defined $cfgfile{$name};
206 0         0 return set_config($name, $default);
207             }
208              
209             sub load_plugins {
210 0     0 0 0 my $plugins = shift;
211 0         0 foreach my $plugin (split(/\s*,\s*/, $plugins)) {
212 0         0 my $rv = eval "use MogileFS::Plugin::$plugin; MogileFS::Plugin::$plugin->load; 1;";
213 0 0       0 die "Unable to load $plugin: $@\n" unless $rv;
214             }
215             }
216              
217             sub config {
218 78 50   78 0 257 shift if @_ == 2;
219 78         234 my $k = shift;
220 78 50       215 die "No config variable '$k'" unless defined $conf{$k};
221 78         255 return $conf{$k};
222             }
223              
224             sub check_database {
225 0     0 0   my $sto = eval { Mgd::get_store() };
  0            
226 0 0 0       unless ($sto && $sto->ping) {
227 0           die qq{
228             Error: unable to establish connection with your MogileFS database.
229              
230             Please verify that you have correctly setup a configuration file or are
231             providing the correct information in order to reach the database and try
232             running the MogileFS server again. If you haven\'t setup your database yet,
233             run 'mogdbsetup'.
234              
235             Details: [sto=$sto, err=$@]
236             }
237             }
238              
239 0   0       my $sversion = MogileFS::Config->server_setting('schema_version') || 0;
240 0           my $expect_ver = MogileFS::Store->latest_schema_version;
241 0 0 0       unless ($sversion == $expect_ver || MogileFS::Config->config('no_schema_check')) {
242 0           die "Server's database schema version of $sversion doesn't match expected value of $expect_ver. Halting.\n\n".
243             "Please run mogdbsetup to upgrade your schema.\n";
244             }
245              
246 0           $sto->pre_daemonize_checks;
247              
248             # If MySQL gets restarted InnoDB may reset its auto_increment counter. If
249             # the first few fids have been deleted, the "reset to max on duplicate"
250             # code won't fire immediately.
251             # Instead, we also trigger it if a configured "min_fidid" is higher than
252             # what we got from innodb.
253             # For bonus points: This value should be periodically updated, in case the
254             # trackers don't go down as often as the database.
255 0           my $min_fidid = $sto->max_fidid;
256 0 0         $min_fidid = 0 unless $min_fidid;
257 0           set_config('min_fidid', $min_fidid);
258             }
259              
260             # set_server_setting( key, value )
261             # set value to undef to remove whatever is presently stored; returns 1 on success or
262             # undef on error
263             sub set_server_setting {
264 0     0 0   my ($class, $key, $val) = @_;
265 0 0         return unless $key;
266              
267 0           my $sto = Mgd::get_store();
268 0           $sto->set_server_setting($key, $val);
269 0           return 1;
270             }
271              
272             # get_server_setting( key )
273             # get value of server setting, undef on error (or no result)
274             sub server_setting {
275 0     0 0   my ($class, $key) = @_;
276 0           return Mgd::get_store()->server_setting($key);
277             }
278              
279             sub cache_server_setting {
280 0     0 0   my ($class, $key, $val) = @_;
281 0 0         $has_cached_settings++ unless $has_cached_settings;
282 0 0         if (! defined $val) {
283             delete $server_settings{$key}
284 0 0         if exists $server_settings{$key};
285             }
286 0           $server_settings{$key} = $val;
287             }
288              
289             sub server_setting_cached {
290 0     0 0   my ($class, $key, $fallback) = @_;
291 0 0         $fallback = 1 unless (defined $fallback);
292 0 0 0       if (!$has_cached_settings && $fallback) {
293 0           return MogileFS::Config->server_setting($key);
294             }
295 0           return $server_settings{$key};
296             }
297              
298             my $memc;
299             my $last_memc_server_fetch = 0;
300 21     21   2625 my $have_memc_module = eval "use Cache::Memcached; 1;";
  0         0  
  0         0  
301             sub memcache_client {
302 0 0   0 0   return undef unless $have_memc_module;
303              
304             # only reload the server list every 30 seconds
305 0           my $now = time();
306 0 0         return $memc if $last_memc_server_fetch > $now - 30;
307              
308 0   0       my @servers = grep(/:\d+$/, split(/\s*,\s*/, MogileFS::Config->server_setting_cached("memcache_servers") || ""));
309 0           $last_memc_server_fetch = $now;
310              
311 0 0         return ($memc = undef) unless @servers;
312              
313 0   0       $memc ||= Cache::Memcached->new;
314 0           $memc->set_servers(\@servers);
315              
316 0           return $memc;
317             }
318              
319             my $cache_hostname;
320             sub hostname {
321 0   0 0 0   return $cache_hostname ||= Sys::Hostname::hostname();
322             }
323              
324             sub server_setting_is_readable {
325 0     0 0   my ($class, $key) = @_;
326 0 0         return 1 if $key eq 'fsck_checksum';
327 0 0         return 0 if $key =~ /^fsck_/;
328 0           return 1;
329             }
330              
331             # returns subref which cleans (canonicalizes) the value, or dies if invalid format.
332             # my $cleanval = $code->($val);
333             sub server_setting_is_writable {
334 0     0 0   my ($class, $key) = @_;
335              
336             # common formats:
337 0     0     my $any = sub { $_[0]; };
  0            
338 0 0   0     my $del_if_blank = sub { $_[0] || undef };
  0            
339             my $bool = sub {
340 0     0     my $v = shift;
341 0 0         return "0" unless $v;
342 0 0         return "0" if $v =~ /^(0|f|off|n)/i;
343 0 0         return "1" if $v =~ /^(1|t|on|y)/i;
344 0           die "Unknown format";
345 0           };
346             my $num = sub {
347 0     0     my $v = shift;
348 0 0         return "0" unless $v;
349 0 0         return $v if $v =~ /^\d+$/;
350 0           die "Must be numeric";
351 0           };
352             my $matchre = sub {
353 0     0     my $re = shift;
354             return sub {
355 0           my $v = shift;
356 0 0         return $v if $v =~ /$re/;
357 0           die "Doesn't match acceptable format.";
358 0           };
359 0           };
360             my $valid_netmask = sub {
361 0     0     my $n = Net::Netmask->new2($_[0]);
362 0 0         die "Doesn't match an acceptable netmask" unless $n;
363 0           };
364             my $valid_netmask_list = sub {
365 0     0     my @ns = split /[,\s]+/, $_[0];
366 0           foreach my $n (@ns) {
367 0           $valid_netmask->($n);
368             }
369 0           return $_[0];
370 0           };
371              
372             # let slave settings go through unmodified, for now.
373 0 0         if ($key =~ /^slave_/) { return $del_if_blank };
  0            
374 0 0         if ($key eq "skip_devcount") { return $bool };
  0            
375 0 0         if ($key eq "skip_mkcol") { return $bool };
  0            
376 0 0         if ($key eq "case_sensitive_list_keys") { return $bool };
  0            
377 0 0         if ($key eq "memcache_servers") { return $any };
  0            
378 0 0         if ($key eq "memcache_ttl") { return $num };
  0            
379 0 0         if ($key eq "internal_queue_limit") { return $num };
  0            
380              
381             # ReplicationPolicy::MultipleNetworks
382 0 0         if ($key eq 'network_zones') { return $any };
  0            
383 0 0         if ($key =~ /^zone_/) { return $valid_netmask_list };
  0            
384              
385             # should probably restrict to (\d+)
386 0 0         if ($key =~ /^queue_/) { return $any };
  0            
387              
388 0 0         if ($key eq "fsck_checksum") {
389             return sub {
390 0     0     my $v = shift;
391 0 0         return "off" if $v eq "off";
392 0 0         return undef if $v eq "class";
393 0 0         return $v if MogileFS::Checksum->valid_alg($v);
394 0           die "Not a valid checksum algorithm";
395             }
396 0           }
397              
398 0           return 0;
399             }
400              
401             1;
402              
403             # Local Variables:
404             # mode: perl
405             # c-basic-indent: 4
406             # indent-tabs-mode: nil
407             # End: