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