Checking in changes prior to tagging of version 2.30.
[MogileFS-Utils.git] / mogadm
blob9c5bebb01a5656eacd39bea447be7a2c32978ac5
1 #!/usr/bin/perl
2 # vim:ts=4 sw=4 ft=perl et:
4 use strict;
5 use warnings;
6 use Getopt::Long;
7 use LWP::Simple; # FIXME: use of this makes 'mog check' hang too long when multiple things down
8 use Socket;
10 my @topcmds = qw(check stats host device domain class slave fsck rebalance settings);
11 my $usage = {
12 check => {
13 des => "Check the state of the MogileFS world.",
15 stats => {
16 des => "Show MogileFS system statistics. (DEPRECATED: use mogstats instead)",
18 settings => {
19 des => "Change/list server settings.",
20 subcmd => {
21 list => {
22 des => "List all server settings",
24 set => {
25 args => "<key> <value>",
26 des => "Set server setting 'key' to 'value'.",
30 host => {
31 des => "Add/modify hosts.",
32 subcmd => {
33 list => {
34 des => "List all hosts.",
36 add => {
37 des => "Add a host to MogileFS.",
38 args => "<hostname> [opts]",
39 opts => {
40 "<hostname>" => "Hostname of machine",
41 "--status=s" => "One of {alive,down,readonly}. Default 'down'.",
42 "--ip=s" => "IP address of machine.",
43 "--port=i" => "HTTP port of mogstored",
44 "--getport=i" => "Alternate HTTP port serving readonly traffic",
45 "--altip=s" => "Alternate IP that is machine is reachable from",
46 "--altmask=s" => "Netmask which, when matches client, uses alt IP",
49 modify => {
50 des => "Modify a host's properties.",
51 args => "<hostname> [opts]",
52 opts => {
53 "<hostname>" => "Host name.",
54 "--status=s" => "One of {alive,down,readonly}.",
55 "--ip=s" => "IP address of machine.",
56 "--port=i" => "HTTP port of mogstored",
57 "--getport=i" => "Alternate HTTP port serving readonly traffic",
58 "--altip=s" => "Alternate IP that is machine is reachable from",
59 "--altmask=s" => "Netmask which, when matches client, uses alt IP",
62 mark => {
63 des => "Change the status of a host. (equivalent to 'modify --status')",
64 args => "<hostname> <status>",
65 opts => {
66 "<hostname>" => "Host name to bring up or down.",
67 "<status>" => "One of {alive,down,readonly}.",
70 delete => {
71 des => "Delete a host.",
72 args => "<hostname>",
73 opts => {
74 "<hostname>" => "Host name to delete.",
79 device => {
80 des => "Add/modify devices.",
81 subcmd => {
82 list => {
83 des => "List all devices, for each host.",
84 args => "[opts]",
85 opts => {
86 "--all" => "Include dead devices in list.",
87 "--hostname=s" => "Limit results to a given host.",
90 summary => {
91 des => "List the summary of devices, for each host.",
92 args => "[opts]",
93 opts => {
94 "--status=s" => "Devices of status A. Defaults to 'alive,readonly,drain'",
95 "--hostname=s" => "Limit results to a given host.",
98 add => {
99 des => "Add a device to a host.",
100 args => "<hostname> <devid> [opts]",
101 opts => {
102 "<hostname>" => "Hostname to add a device",
103 "<devid>" => "Numeric devid. Never reuse these.",
104 "--status=s" => "One of 'alive' or 'down'. Defaults to 'alive'.",
107 mark => {
108 des => "Mark a device as {alive,dead,down,drain,readonly}",
109 args => "<hostname> <devid> <status>",
110 opts => {
111 "<hostname>" => "Hostname of device",
112 "<devid>" => "Numeric devid to modify.",
113 "<status>" => "One of {alive,dead,down,drain,readonly}",
116 modify => {
117 des => "Modify a device's properties.",
118 args => "<hostname> <devid> [opts]",
119 opts => {
120 "<hostname>" => "Hostname of device",
121 "<devid>" => "Numeric devid to modify.",
122 "--status=s" => "One of {alive,dead,down,drain,readonly}",
123 "--weight=i" => "Positive numeric weight for device",
126 next => {
127 des => "Show the next available devid.",
131 domain => {
132 des => "Add/modify domains (namespaces)",
133 subcmd => {
134 list => {
135 des => "List all hosts.",
137 add => {
138 des => "Add a domain (namespace)",
139 args => "<domain>",
140 opts => {
141 "<domain>" => "Domain (namespace) to add.",
144 delete => {
145 des => "Delete a domain.",
146 args => "<domain>",
147 opts => {
148 "<domain>" => "Domain (namespace) to add.",
153 class => {
154 des => "Add/modify file classes.",
155 subcmd => {
156 list => {
157 des => "List all classes, for each domain.",
159 add => {
160 des => "Add a file class to a domain.",
161 args => "<domain> <class> [opts]",
162 opts => {
163 "<domain>" => "Domain to add class to.",
164 "<class>" => "Name of class to add.",
165 "--mindevcount=i" => "Minimum number of replicas.",
166 "--replpolicy=s" => "Replication policy string.",
167 "--hashtype=s" => "Hash algorithm string ('MD5', 'NONE').",
170 modify => {
171 des => "Modify properties of a file class.",
172 args => "<domain> <class> [opts]",
173 opts => {
174 "<domain>" => "Domain to add class to.",
175 "<class>" => "Name of class to add.",
176 "--mindevcount=i" => "Minimum number of replicas.",
177 "--replpolicy=s" => "Replication policy string.",
178 "--hashtype=s" => "Hash algorithm string ('MD5', 'NONE').",
181 delete => {
182 des => "Delete a file class from a domain.",
183 args => "<domain> <class>",
184 opts => {
185 "<domain>" => "Domain of class to delete.",
186 "<class>" => "Class to delete.",
192 slave => {
193 des => 'Manipulate slave database information in a running mogilefsd.',
194 subcmd => {
195 list => {
196 des => 'List current store slave nodes.',
198 add => {
199 des => 'Add a slave node for store usage',
200 args => '<slave_key> [opts]',
201 opts => {
202 '--dsn=s' => "DBI DSN specifying what database to connect to.",
203 '--username=s' => "DBI username for connecting.",
204 '--password=s' => "DBI password for connecting.",
207 modify => {
208 des => 'Modify a slave node for store usage',
209 args => '<slave_key> [opts]',
210 opts => {
211 '--dsn=s' => "DBI DSN specifying what database to connect to.",
212 '--username=s' => "DBI username for connecting.",
213 '--password=s' => "DBI password for connecting.",
216 delete => {
217 des => 'Delete a slave node for store usage',
218 args => '<slave_key>',
222 rebalance => {
223 des => "Control file rebalancing operations.",
224 subcmd => {
225 start => {
226 des => 'Start a rebalance job',
228 stop => {
229 des => 'Stop a rebalance job',
231 status => {
232 des => 'Show status of current rebalance job',
234 settings => {
235 des => 'Display rebalance settings',
237 test => {
238 des => 'Show what devices the current policy would match',
240 reset => {
241 des => 'Reset an existing policy',
243 policy => {
244 des => 'Add or adjust the current policy',
245 args => '[opts]',
246 opts => {
247 '--options=s' => "Policy string (see docs/wiki for details)",
252 fsck => {
253 des => "Control a background filesystem check operation.",
254 subcmd => {
255 start => {
256 des => 'Start (or resume) background fsck',
258 stop => {
259 des => 'Stop (pause) background fsck',
261 status => {
262 des => 'Show fsck status',
264 reset => {
265 des => 'Reset fsck position back to the beginning',
266 args => '[opts]',
267 opts => {
268 '--policy-only' => "Check repl policy (assumed locations); don't stat storage nodes",
269 '--startpos=i' => "FID to start at.",
272 clearlog => {
273 des => 'Clear the fsck log',
275 printlog => {
276 des => 'Display the fsck log',
278 taillog => {
279 des => 'Tail the fsck log',
286 # load up our config files
287 my %opts;
289 Getopt::Long::Configure("require_order", "pass_through");
290 GetOptions(
291 "trackers=s" => \$opts{trackers},
292 "config=s" => \$opts{config},
293 "lib=s" => \$opts{lib},
294 "help" => \$opts{help},
295 "verbose" => \$opts{verbose},
296 ) or abortWithUsage();
297 Getopt::Long::Configure("require_order", "no_pass_through");
299 my @configs = ($opts{config}, "$ENV{HOME}/.mogilefs.conf", "/etc/mogilefs/mogilefs.conf");
300 foreach my $fn (reverse @configs) {
301 next unless $fn && -e $fn;
302 open FILE, "<$fn"
303 or die "unable to open $fn: $!\n";
304 while (<FILE>) {
305 s/\#.*//;
306 next unless m!^\s*(\w+)\s*=\s*(.+?)\s*$!;
307 $opts{$1} = $2 unless ( defined $opts{$1} );
309 close FILE;
312 # bail for help
313 abortWithUsage() if $opts{help};
315 # make sure we have at least a topcmd
316 my $topcmd = shift(@ARGV);
317 abortWithUsage() unless $topcmd && $usage->{$topcmd};
319 # break up the trackers and ensure we got some
320 if ($opts{trackers}) {
321 $opts{trackers} = [ split(/\s*,\s*/, $opts{trackers}) ];
323 fail_text('no_trackers')
324 unless ($opts{trackers} && @{$opts{trackers}}) || detect_local_tracker();
326 # okay, load up the libraries that we need
327 if ($opts{lib}) {
328 eval "use lib '$opts{lib}';";
330 eval "use MogileFS::Admin; use MogileFS::Client; 1;" or fail_text('cant_find_module');
332 # dispatch if it's special
333 if ($topcmd eq 'check') {
334 die "Unknown options/arguments to 'check' command.\n" if @ARGV;
335 cmd_check();
336 } elsif ($topcmd eq 'stats') {
337 die "Unknown options/arguments to 'stats' command.\n" if @ARGV;
338 cmd_stats();
341 # get the verb
342 my $verb = shift(@ARGV) or
343 abort_with_topcmd_help($topcmd);
344 my $cmdinfo = $usage->{$topcmd}{subcmd}{$verb};
345 abort_with_topcmd_help($topcmd) unless $cmdinfo;
347 my $badargs = sub {
348 my $msg = shift;
349 abort_with_topcmd_help($topcmd, $verb, $msg);
352 # get the non-option (non --foo) arguments:
353 my %cmdargs;
354 if (my $args = $cmdinfo->{args}) {
355 my @args = split(/ /, $args);
356 foreach my $arg (@args) {
357 # positional (but named) parameter
358 if ($arg =~ /^<(.+)>$/) {
359 my $argname = $1;
360 my $val = shift @ARGV;
361 # map e.g. "dev5" to 5
362 if ($argname eq "devid" && $val && $val =~ /^dev(\d+)$/) {
363 $val = $1;
365 $badargs->("Missing argument '$argname'") unless defined $val;
366 $badargs->("Unexpected option. Expected argument '$argname'") if $val =~ /^-/;
367 $cmdargs{$argname} = $val;
368 } elsif ($arg eq "[opts]") {
369 # handled later.
370 } else {
371 die "INTERNAL ERROR.";
374 $badargs->("Unexpected extra argument.") if @ARGV && $ARGV[0] !~ /^-/;
375 } else {
376 $badargs->("Unexpected arguments when expecting none.") if @ARGV;
379 # parse the options
380 if (my $opts = $cmdinfo->{opts}) {
381 my %getopts;
382 foreach (keys %$opts) {
383 my $k = $_;
384 next if $k =~ /^</; # don't care about these.
385 die "BOGUS KEY: '$k'" unless $k =~ /^--([\w\-]+)(=.+)?$/;
386 my ($oname, $type) = ($1, $2 || "");
387 $getopts{"$oname$type"} = \$cmdargs{$1};
389 GetOptions(%getopts)
390 or abort_with_topcmd_help($topcmd, $verb);
393 # see what we should do
394 my $cmdsub = do {
395 no strict 'refs';
396 *{"cmd_${topcmd}_${verb}"} or abortWithUsage();
399 # now call our lovely lovely sub
400 $cmdsub->(\%cmdargs);
401 exit 0;
403 sub detect_local_tracker {
404 require IO::Socket::INET;
405 my $loctrack = "127.0.0.1:7001";
406 my $sock = IO::Socket::INET->new(PeerAddr => $loctrack, Timeout => 1);
407 return 0 unless $sock;
408 $opts{trackers} = [$loctrack];
409 return 1;
412 ###########################################################################
413 ## command routines
414 ###########################################################################
416 sub cmd_check {
417 # step 1: we want to check each tracker for responsiveness
418 my $now = time();
419 my ($hosts, $devices);
420 $| = 1;
421 print "Checking trackers...\n";
422 foreach my $t (@{$opts{trackers}}) {
423 print " $t ... ";
424 my $mogadm = mogadm($t);
425 if ($mogadm) {
426 my $lhosts = hosts($mogadm);
427 my $ldevs = devices($mogadm);
428 if ($lhosts && $ldevs) {
429 print "OK\n";
430 $hosts = $lhosts;
431 $devices = $ldevs;
432 } else {
433 print "REQUEST FAILURE (is the tracker up?)\n";
435 } else {
436 print "INITIAL FAILURE (bad configuration?)\n";
440 # we should have hosts if we get here
441 fail_text('no_hosts') unless $hosts;
442 print "\n";
444 # step 2: now hit each of the hosts for responsiveness
445 print "Checking hosts...\n";
446 my @urls;
447 foreach my $hostid (sort { $a <=> $b } keys %$hosts) {
448 printf " [%2d] %s ... ", $hostid, $hosts->{$hostid}->{hostname};
449 if ($hosts->{$hostid}->{status} eq 'alive') {
450 my $url = 'http://' . $hosts->{$hostid}->{hostip} . ':' . $hosts->{$hostid}->{http_port} . '/';
451 my $file = get($url);
452 if (defined $file) {
453 print "OK\n";
454 push @urls, [ $hostid, $url ];
455 } else {
456 print "REQUEST FAILURE FETCHING: $url\n";
458 } else {
459 print "skipping; status = $hosts->{$hostid}->{status}\n";
463 # everything should be chill
464 fail_text('no_devices') unless @urls;
465 print "\n";
467 # step 3: check devices for each host
468 print "Checking devices...\n";
469 printf " host device %10s %10s %10s %7s %7s %4s\n", 'size(G)', 'used(G)', 'free(G)', 'use% ', 'ob state', 'I/O%';
470 printf " ---- ------------ ---------- ---------- ---------- ------ ---------- -----\n";
471 my %total;
472 # Initialize to zero so that the total outputs doesn't need to check for undefined.
473 map { $total{$_} = 0; } qw(total used avail);
475 foreach my $hosturl (@urls) {
476 my ($hostid, $url) = @$hosturl;
477 my $devs = $devices->{$hostid};
478 DEV: foreach my $devid (sort { $a <=> $b } keys %$devs) {
479 my $dev = $devs->{$devid};
480 my $status = $dev->{status} || "??";
481 next if $status eq "dead";
483 printf " [%2d] %-7s", $hostid, "dev$devid";
485 my $usage = get($url . "/dev$devid/usage");
486 if (! defined $usage) {
487 print "REQUEST FAILURE FETCHING: $url" . "dev$devid/usage\n";
488 next;
490 if (length($usage) < 1) {
491 print "USAGE FILE BROKEN OR EMPTY: $url" . "dev$devid/usage\n";
492 next;
494 my %data = ( map { split(/:\s+/, $_) } split(/\r?\n/, $usage) );
495 foreach (qw(time used total avail)) {
496 $data{$_} = 0 if (!$data{$_} ||
497 $data{$_} !~ /\A\d+(\.\d+)?\Z/);
499 foreach (qw(available device disk)) {
500 if (! exists $data{$_} || !$data{$_}) {
501 print "MISSING FIELD ($_) FROM USAGE FILE: $url" . "dev$devid/usage\n";
502 next DEV;
505 $data{age} = $now - $data{time};
506 $data{used} /= 1024**2;
507 $data{total} /= 1024**2;
508 $data{available} /= 1024**2;
509 $data{avail} = $data{available};
510 my $pct = 100 - $data{available}/$data{total}*100;
511 $total{used} += $data{used};
512 $total{avail} += $data{avail};
513 $total{total} += $data{total};
515 my $util = "N/A";
516 if (defined($dev->{utilization}) && $dev->{utilization} =~ /\A\d+(\.\d+)?\Z/) {
517 $util = sprintf("%.1f", $dev->{utilization});
520 printf(" %10.3f %10.3f %10.3f %6.2f%% %-7s %5s\n",
521 (map { $data{$_} } qw(total used avail)),
522 $pct, ($dev->{observed_state} || "?"),
523 $util);
526 my $pct = 0;
527 # Avoid division by zero
528 $pct = 100 - $total{avail}/$total{total}*100 if($total{total} > 0);
530 printf " ---- ------------ ---------- ---------- ---------- ------\n";
531 printf " total:%10.3f %10.3f %10.3f %6.2f%%\n", (map { $total{$_} } qw(total used avail)), $pct;
533 # if we get here, all's well
534 ok();
537 sub cmd_stats {
538 fail("mogadm stats is deprecated by new 'mogstats' utility");
541 sub cmd_host_list {
542 my $hosts = hosts();
543 fail_text('no_hosts') unless $hosts;
545 foreach my $hostid (sort keys %$hosts) {
546 my $host = $hosts->{$hostid};
547 print "$host->{hostname} [$hostid]: $host->{status}\n";
548 my @data = (
549 'IP', "$host->{hostip}:$host->{http_port}",
550 'Alt IP', $host->{altip},
551 'Alt Mask', $host->{altmask},
552 'GET Port', $host->{http_get_port},
554 while (my ($k, $v) = splice(@data, 0, 2)) {
555 next unless $v;
556 printf " %-10s\%s\n", "$k:", $v;
558 print "\n";
560 ok();
563 sub cmd_host_add {
564 my $args = shift;
566 my $hosts = hosts_byname();
567 fail_text('no_hosts') unless $hosts;
569 my $name = delete $args->{hostname};
570 cmd_help_die("No hostname") unless $name;
571 fail('Host already exists.') if $hosts->{$name};
573 # make sure we have an ip
574 unless ($args->{ip}) {
575 my $addr = gethostbyname($name);
576 fail_text('host_add_no_ip') unless $addr;
577 $args->{ip} = inet_ntoa($addr);
580 # defaults
581 $args->{port} ||= 7500;
582 $args->{status} ||= 'down';
584 # FIXME: verify the status can't be 'alive' if we can't get to ip:port
585 # OR BETTER: also make default status the reachability of that ip:port
587 # now create the host
588 my $mogadm = mogadm();
589 $mogadm->create_host($name, $args);
590 if ($mogadm->err) {
591 fail("Failure creating host: " . $mogadm->errstr);
594 ok('Host has been created.');
597 sub cmd_host_modify {
598 my $args = shift;
599 my $name = delete $args->{hostname};
601 # FIXME: verify the status can't be 'alive' if we can't get to ip:port
603 # now modify the host
604 my $mogadm = mogadm();
605 $mogadm->update_host($name, $args);
606 if ($mogadm->err) {
607 fail("Failure modifying host: " . $mogadm->errstr);
610 ok('Host has been modified.');
613 sub cmd_host_delete {
614 my $args = shift;
615 my $name = delete $args->{hostname};
617 # now modify the host
618 my $mogadm = mogadm();
619 $mogadm->delete_host($name);
620 if ($mogadm->err) {
621 fail("Failure deleting host: " . $mogadm->errstr);
624 ok('Host has been deleted.');
627 sub cmd_host_mark {
628 my $args = shift;
630 my $mogadm = mogadm();
631 $mogadm->update_host($args->{hostname}, { status => $args->{status} });
632 if ($mogadm->err) {
633 fail("Failure updating host status: " . $mogadm->errstr);
636 ok('Host status updated.');
639 sub cmd_domain_list {
640 # actually lists domains and classes
641 my $domains = domains() or
642 fail_text('no_domains');
643 # now iterate
644 printf " %-20s %-20s %-12s %-12s %-7s\n", "domain", "class", "mindevcount", "replpolicy", "hashtype";
645 printf "%-20s %-20s %-12s %-12s %-7s\n", '-' x 20, '-' x 20, '-' x 13, '-' x 12, '-' x 7;
646 foreach my $domain (sort keys %$domains) {
647 foreach my $class (sort keys %{$domains->{$domain}}) {
648 my $dom = $domains->{$domain}->{$class};
649 printf " %-20s %-20s %-8d %-13s %-7s\n", $domain, $class,
650 $dom->{mindevcount} || 0, $dom->{replpolicy} || '',
651 $dom->{hashtype} || '';
653 print "\n";
656 ok();
659 sub cmd_domain_add {
660 my $args = shift;
662 my $domain = delete $args->{domain};
664 # create
665 my $mogadm = mogadm();
666 $mogadm->create_domain($domain);
667 if ($mogadm->err) {
668 fail('Error creating domain: ' . $mogadm->errstr);
671 ok('Domain created.');
674 sub cmd_domain_delete {
675 my $args = shift;
677 my $domain = $args->{domain};
679 # destroy
680 my $mogadm = mogadm();
681 $mogadm->delete_domain($domain);
682 if ($mogadm->err) {
683 fail('Error deleting domain: ' . $mogadm->errstr);
686 ok('Domain deleted.');
689 sub cmd_class_list {
690 # same, pass it through
691 cmd_domain_list();
694 sub cmd_class_add {
695 my $args = shift;
697 my $domain = delete $args->{domain};
698 my $class = delete $args->{class};
700 cmd_help_die() unless $domain && $class;
702 $args->{mindevcount} ||= 2;
703 $args->{replpolicy} ||= '';
705 my $mogadm = mogadm();
706 $mogadm->create_class($domain, $class, $args);
707 if ($mogadm->err) {
708 fail('Error creating class: ' . $mogadm->errstr);
711 ok('Class created.');
714 sub cmd_class_modify {
715 my $args = shift;
717 my $domain = delete $args->{domain};
718 my $class = delete $args->{class};
720 cmd_help_die() unless $domain && $class;
722 $args->{mindevcount} ||= 2;
723 $args->{replpolicy} ||= '';
725 my $mogadm = mogadm();
726 $mogadm->update_class($domain, $class, $args);
727 if ($mogadm->err) {
728 fail('Error updating class: ' . $mogadm->errstr);
731 ok('Class updated.');
734 sub cmd_class_delete {
735 my $args = shift;
737 my $domain = $args->{domain};
738 my $class = $args->{class};
740 cmd_help_die() unless $domain && $class;
742 my $mogadm = mogadm();
743 $mogadm->delete_class($domain, $class);
744 if ($mogadm->err) {
745 fail('Error deleting class: ' . $mogadm->errstr);
748 ok('Class deleted.');
751 sub cmd_device_add {
752 my $args = shift;
754 my $hosts = hosts() or
755 fail_text('no_hosts');
757 my $host = $args->{hostname};
758 my $devid = $args->{devid};
759 my $state = $args->{status} || "alive";
761 cmd_help_die("devid should be numeric") unless $devid =~ /^\d+$/;
763 # FIXME: server should be fixed to verify via HTTP that the devid directory exists
765 my $mogadm = mogadm();
766 $mogadm->create_device(hostname => $host, devid => $devid, state => $state);
768 if ($mogadm->err) {
769 fail('Error adding device: ' . $mogadm->errstr);
772 ok('Device added.');
775 sub warn_on_drain {
776 my $args = shift;
777 if ($args->{status} && $args->{status} eq 'drain') {
778 print "***NOTE***: As of server version 2.40 'drain' has changed. See docs/wiki\n";
782 sub cmd_device_mark {
783 my $args = shift;
785 warn_on_drain($args);
786 my $mogadm = mogadm();
787 $mogadm->change_device_state($args->{hostname},
788 $args->{devid},
789 $args->{status});
790 if ($mogadm->err) {
791 fail('Error updating device: ' . $mogadm->errstr);
794 ok('Device updated.');
797 sub cmd_device_modify {
798 my $args = shift;
799 my $hostname = delete $args->{hostname};
800 my $devid = delete $args->{devid};
802 warn_on_drain($args);
803 my $mogadm = mogadm();
804 $mogadm->update_device($hostname, $devid, $args);
806 if ($mogadm->err) {
807 fail('Error updating device: ' . $mogadm->errstr);
810 ok('Device updated.');
813 sub cmd_device_list {
814 my $args = shift;
816 my $hosts = hosts();
817 fail_text('no_hosts') unless $hosts;
819 my $devs = devices();
820 fail_text('no_devices') unless $devs;
821 my $hostname = $args->{hostname};
823 foreach my $hostid (sort keys %$hosts) {
824 my $host = $hosts->{$hostid};
825 if (defined $hostname && $hostname ne $host->{hostname}) {
826 next;
828 print "$host->{hostname} [$hostid]: $host->{status}\n";
830 printf "%7s %-7s %10s %10s %10s %10s\n", '', '', 'used(G)', 'free(G)', 'total(G)', 'weight(%)';
831 foreach my $devid (sort keys %{$devs->{$hostid} || {}}) {
832 my $dev = $devs->{$hostid}->{$devid};
833 next if $dev->{status} eq "dead" && ! $args->{all};
835 my $total = $dev->{mb_total} / 1024;
836 my $used = $dev->{mb_used} / 1024;
837 my $free = $total - $used;
838 printf "%7s: %7s %10.3f %10.3f %10.3f %10u\n", "dev$devid", $dev->{status}, $used, $free, $total, $dev->{weight};
841 print "\n";
844 ok();
847 sub cmd_device_summary {
848 my $args = shift;
849 my $hostname = $args->{hostname};
850 my %show_state;
851 $show_state{$_} = 1 foreach split(/,/, ($args->{status} || "alive,readonly,drain"));
853 my $hosts = hosts();
854 fail_text('no_hosts') unless $hosts;
856 my $devs = devices();
857 fail_text('no_devices') unless $devs;
859 printf "%-15s %6s %7s %8s %8s %8s %8s\n", 'Hostname', 'HostID', 'Status', 'used(G)', 'free(G)', 'total(G)', '%Used';
860 foreach my $hostid (sort keys %$hosts) {
861 my $host = $hosts->{$hostid};
862 if (defined $hostname && $hostname ne $host->{hostname}) {
863 next;
865 my ($total,$used) = (0, 0);
867 foreach my $devid (sort keys %{$devs->{$hostid} || {}}) {
868 my $dev = $devs->{$hostid}->{$devid};
869 next unless $show_state{$dev->{status}};
871 my $devtotal = $dev->{mb_total} / 1024;
872 my $devused = $dev->{mb_used} / 1024;
874 $total += $devtotal;
875 $used += $devused;
877 my $free = $total - $used;
878 printf "%-15s [%4d]: %6s", $host->{hostname}, $hostid, $host->{status};
879 printf " %8.3f %8.3f %8.3f ", $used, $free, $total;
880 printf "%8.2f", 100*$used/$total if $total;
881 print "\n";
884 ok();
888 sub cmd_device_next {
889 my $devs = mogadm()->get_devices;
890 fail_text('no_devices') unless @$devs;
891 my $maxid = 0;
892 foreach my $dev (@$devs) {
893 my $devid = $dev->{devid};
894 if ($devid > $maxid) {
895 $maxid = $devid;
899 print($maxid + 1, "\n");
900 ok();
903 sub cmd_slave_list {
904 my $mogadm = mogadm();
906 my $slaves = $mogadm->slave_list();
908 foreach my $key (sort keys %$slaves) {
909 my $slavedata = $slaves->{$key};
910 my ($dsn, $username, $password) = @$slavedata;
911 print "$key --dsn=$dsn --username=$username --password=$password\n";
914 ok();
917 sub cmd_slave_add {
918 my $mogadm = mogadm();
919 my $args = shift;
921 my $rc = $mogadm->slave_add($args->{slave_key}, $args->{dsn}, $args->{username}, $args->{password});
923 if ($rc) {
924 ok("Slave added");
925 } else {
926 fail("Slave failed to be added");
930 sub cmd_slave_modify {
931 my $mogadm = mogadm();
932 my $args = shift;
934 my $key = delete $args->{slave_key} or cmd_help_die("Key argument is required");
936 my $rc = $mogadm->slave_modify($key, %$args);
938 if ($rc) {
939 ok("Slave modify success");
940 } else {
941 fail("Slave modify failure: " . $mogadm->errstr);
945 sub cmd_slave_delete {
946 my $mogadm = mogadm();
948 my $args = shift;
950 my $rc = $mogadm->slave_delete($args->{slave_key});
952 if ($rc) {
953 ok("Slave deleted");
954 } else {
955 fail("Slave delete failed");
959 sub cmd_rebalance_start {
960 my $mogadm = mogadm();
961 my $res = $mogadm->rebalance_start || fail($mogadm->errstr);
962 ok("rebalance started");
965 sub cmd_rebalance_stop {
966 my $mogadm = mogadm();
967 my $res = $mogadm->rebalance_stop || fail($mogadm->errstr);
968 ok("rebalance stopped");
971 sub cmd_rebalance_reset {
972 my $mogadm = mogadm();
973 my $res = $mogadm->rebalance_reset || fail($mogadm->errstr);
974 ok("rebalance reset");
977 # TODO: Make output prettier? Put hostname next to device name, print device
978 # info?
979 sub cmd_rebalance_test {
980 my $mogadm = mogadm();
981 my $res = $mogadm->rebalance_test || fail($mogadm->errstr);
982 print "Tested rebalance policy...\n";
983 my $s = $mogadm->server_settings;
984 print "Policy: ", $s->{rebal_policy}, "\n\n";
985 print "Source devices:\n";
986 for my $dev (sort split /,/, $res->{sdevs}) {
987 print " - ", $dev, "\n";
989 print "Destination devices:\n";
990 for my $dev (sort split /,/, $res->{ddevs}) {
991 print " - ", $dev, "\n";
995 sub cmd_rebalance_status {
996 my $mogadm = mogadm();
998 my $ss = $mogadm->server_settings or fail ($mogadm->errstr);
999 my $res = $mogadm->rebalance_status or fail ($mogadm->errstr);
1000 if ($ss->{rebal_host}) {
1001 print "Rebalance is running\n";
1002 } else {
1003 print "Rebalance is stopped\n";
1005 print "Rebalance status:\n";
1006 for my $o (sort split /\s+/, $res->{state}) {
1007 my ($k, $v) = split /=/, $o;
1008 printf("%25s = %-s\n", $k, $v);
1012 sub cmd_rebalance_policy {
1013 my $mogadm = mogadm();
1014 my $args = shift;
1016 my $res = $mogadm->rebalance_set_policy($args->{options})
1017 or fail($mogadm->errstr);
1019 ok("changed policy setting");
1022 sub cmd_rebalance_settings {
1023 my $mogadm = mogadm();
1025 my $ss = $mogadm->server_settings
1026 or fail("can't get settings");
1027 foreach my $k (sort keys %$ss) {
1028 next unless ($k =~ '^rebal_');
1029 next if ($k eq 'rebal_state');
1030 printf("%25s = %-s\n", $k, $ss->{$k});
1034 sub cmd_fsck_start {
1035 my $mogadm = mogadm();
1036 my $res = $mogadm->fsck_start || fail($mogadm->errstr);
1037 ok("fsck started");
1040 sub cmd_fsck_stop {
1041 my $mogadm = mogadm();
1042 my $res = $mogadm->fsck_stop || fail($mogadm->errstr);
1043 ok("fsck stopped");
1046 sub cmd_fsck_reset {
1047 my $mogadm = mogadm();
1048 my $args = shift;
1049 my $res = $mogadm->fsck_reset(
1050 policy_only => $args->{"policy-only"},
1051 startpos => $args->{"startpos"},
1053 or fail($mogadm->errstr);
1054 ok("fsck stopped");
1057 sub cmd_fsck_clearlog {
1058 my $mogadm = mogadm();
1059 my $res = $mogadm->fsck_clearlog || fail($mogadm->errstr);
1060 ok("fsck log cleared");
1063 sub _log_dump {
1064 my %opts = @_;
1065 my $max = $opts{start};
1066 my $mogadm = mogadm();
1068 my $fmt = "%-20s %5s %13s %10s\n";
1069 printf($fmt, "unixtime", "event", "fid", "devid");
1070 while (1) {
1071 my @rows = $mogadm->fsck_log_rows(after_logid => $max);
1072 unless (@rows) {
1073 $opts{on_stall}->();
1074 next;
1076 foreach my $row (@rows) {
1077 printf($fmt,
1078 $row->{utime},
1079 $row->{evcode},
1080 $row->{fid},
1081 $row->{devid} || "-");
1082 $max = $row->{logid};
1087 sub cmd_fsck_printlog {
1088 _log_dump(start => 0,
1089 on_stall => sub { exit 0; });
1092 sub cmd_fsck_taillog {
1093 my $mogadm = mogadm();
1094 my $status = $mogadm->fsck_status
1095 or fail("can't get fsck status");
1096 _log_dump(start => $status->{max_logid} - 20,
1097 on_stall => sub { sleep 5; });
1100 sub cmd_fsck_status {
1101 my $mogadm = mogadm();
1102 my $status = $mogadm->fsck_status
1103 or fail("can't get fsck status");
1105 my %known = map { $_ => 1 } qw(
1106 current_time
1107 max_logid
1109 my $st = sub {
1110 my $k = shift;
1111 $known{$k} = 1;
1112 return $status->{$k};
1115 my $line = sub {
1116 printf("%11s: %-s\n", @_);
1118 print "\n";
1119 my $host = $st->('host');
1120 $line->("Running", $st->('running') ? "Yes (on $host)" : "No");
1122 my $ratio = $st->('end_fid') ? ($st->('max_fid_checked') / $st->('end_fid')) : 0;
1123 my $perc = sprintf("%0.02f%%", 100 * $ratio);
1125 $line->("Status",
1126 $st->('max_fid_checked') . " / " . $st->('end_fid')
1127 . " ($perc)");
1128 my $elap = $st->('start_time') ?
1129 (($st->('stop_time') || $st->('current_time')) - $st->('start_time')) :
1131 my $as_time = sub {
1132 my $s = shift;
1133 return int($s) . "s" if $s < 60;
1134 return int($s/60) . "m";
1136 my $per_sec = $elap ? ($st->('max_fid_checked') / $elap) : 0;
1137 $line->("Time", sprintf("%s (%d fids/s; %s remain)",
1138 $as_time->($elap),
1139 sprintf("%0.1f", $per_sec),
1140 $as_time->($per_sec ?
1141 (($st->('end_fid') - $st->('max_fid_checked'))
1142 / $per_sec) :
1143 0)));
1145 $line->("Check Type", ($st->('policy_only') ?
1146 "Repl policy only (skip file checks)" :
1147 "Normal (check policy + files)"));
1149 if (my @unk = grep { !$known{$_} } sort keys %$status) {
1150 print "\n";
1151 foreach (@unk) {
1152 $line->("[$_]", $status->{$_});
1155 print "\n";
1158 sub cmd_settings_list {
1159 my $mogadm = mogadm();
1160 unless ($mogadm->can("server_settings")) {
1161 fail("settings commands require MogileFS::Client >= 1.07");
1163 my $ss = $mogadm->server_settings
1164 or fail("can't get settings");
1165 foreach my $k (sort keys %$ss) {
1166 # Don't list noisy "setting"
1167 next if ($k =~ '^rebal');
1168 printf("%25s = %-s\n", $k, $ss->{$k});
1172 sub cmd_settings_set {
1173 my $mogadm = mogadm();
1174 unless ($mogadm->can("set_server_setting")) {
1175 fail("settings commands require MogileFS::Client >= 1.07");
1177 my $args = shift;
1179 $mogadm->set_server_setting($args->{key}, $args->{value})
1180 or fail($mogadm->errstr);
1181 ok();
1184 ###########################################################################
1185 ## helper routines
1186 ###########################################################################
1188 sub abortWithUsage {
1189 my $ret = "Usage: (enter any command prefix, leaving off options, for further help)\n\n";
1190 foreach my $cmd (@topcmds) {
1191 my $sbc = $usage->{$cmd}->{subcmd};
1192 if ($sbc) {
1193 $ret .= " mogadm $cmd ...\n";
1194 } else {
1195 $ret .= sprintf(" mogadm %-25s %-s\n",
1196 "$cmd",
1197 $usage->{$cmd}{des} || "");
1198 next;
1200 foreach my $v (sort keys %$sbc) {
1201 my $scv = $usage->{$cmd}{subcmd}{$v};
1202 $ret .= " ";
1203 my $dotdot = $scv->{args} ? "..." : "";
1204 $ret .= sprintf(" %-25s %-s\n",
1205 "$cmd $v $dotdot",
1206 $scv->{des} || "");
1210 print $ret, "\n";
1211 exit(1);
1214 sub abort_with_topcmd_help {
1215 my ($cmd, $verb, $msg) = @_;
1216 if ($msg) {
1217 print "\nERROR: $msg\n\n";
1219 my $cmdsfx = $verb ? "-$verb" : "";
1220 my $ret = "Help for '$cmd$cmdsfx' command:\n";
1221 unless ($verb) {
1222 $ret .= " (enter any command prefix, leaving off options, for further help)\n";
1224 $ret .= "\n";
1225 foreach my $subcmdv (sort keys %{$usage->{$cmd}{subcmd}}) {
1226 next if $verb && $verb ne $subcmdv;
1227 my $scv = $usage->{$cmd}{subcmd}{$subcmdv};
1228 $ret .= sprintf(" %-50s %-s\n",
1229 "mogadm $cmd $subcmdv " . ($scv->{args} || ""),
1230 $scv->{des});
1232 print $ret, "\n";
1233 if ($verb) {
1234 my $scv = $usage->{$cmd}{subcmd}{$verb};
1235 foreach my $opt (sort {
1236 (substr($b, 0, 1) cmp substr($a, 0, 1)) ||
1237 $a cmp $b
1238 } keys %{$scv->{opts} || {}})
1240 printf(" %-20s %s\n", $opt, $scv->{opts}->{$opt});
1242 print "\n";
1244 exit 1;
1247 sub cmd_help_die {
1248 my ($msg) = @_;
1249 abort_with_topcmd_help($topcmd, $verb, $msg);
1253 sub text {
1254 return {
1256 ######################################################################
1257 cant_find_module => <<END,
1258 Error loading modules: $@
1260 Please ensure that you have the MogileFS::Admin and MogileFS::Client
1261 modules and all necessary dependencies installed in a location in your
1262 search path. Or, add a search path to mogadm:
1264 mogadm --lib=/path/to/lib
1266 Or add it to the configuration file:
1268 lib = /path/to/lib
1271 ######################################################################
1272 no_mogadm => <<END,
1273 Unable to access MogileFS tracker and/or instantiate a MogileFS::Admin object.
1276 ######################################################################
1277 no_domains => "Unable to retrieve domains from tracker(s).\n",
1279 ######################################################################
1280 no_devices => "No devices found on tracker(s).\n",
1282 ######################################################################
1283 host_add_no_ip => <<END,
1284 Hostname does not resolve to an IP, and you didn\'t specify one on the options
1285 list. Please either verify the host resolves, or try again:
1287 mogadm host add <hostname> --ip=<ipaddr> [...]
1290 ######################################################################
1291 no_hosts => <<END,
1292 Unable to retrieve host information from tracker(s).
1295 ######################################################################
1296 no_trackers => <<END,
1297 In order to use the mogadm toolkit, you need to provide the information about
1298 where your trackers are.
1300 In your configuration file:
1302 trackers = 10.10.0.33:7001, 10.10.0.34:7001
1304 Or on the command line
1306 mogadm --trackers=10.10.0.33:7001,10.10.0.34:7001
1309 }->{$_[0]} || "UNDEFINED [$_[0]]";
1312 sub fail_text {
1313 print STDERR text($_[0]) . "\n";
1314 exit 1;
1317 sub fail {
1318 print STDERR $_[0] . "\n";
1319 exit 1;
1322 sub ok {
1323 if ($opts{verbose}) {
1324 print STDOUT $_[0] . "\n" if $_[0];
1326 exit 0;
1329 sub mogadm {
1330 my $host = shift();
1331 if ($host) {
1332 $host = [ $host ] unless ref $host;
1333 } else {
1334 $host = $opts{trackers};
1336 # 10 seconds is the max time used for any of the admin locks (fsck status)
1337 # plus we leave a bit of time for work.
1338 my $timeout = 15;
1339 $timeout = $opts{timeout} if $opts{timeout} && $opts{timeout} =~ /^[0-9]+$/;
1340 # $MogileFS::DEBUG = 2;
1341 my $mogadm = MogileFS::Admin->new( hosts => $host, timeout => $timeout );
1342 fail_text('no_mogadm') unless $mogadm;
1343 return $mogadm;
1346 sub stats {
1347 my $mogadm = shift() || mogadm();
1348 my $res;
1349 eval {
1350 $res = $mogadm->get_stats();
1352 return undef if $@;
1353 return $res;
1356 sub hosts_byname {
1357 my $mogadm = shift() || mogadm();
1358 fail_text('no_mogadm') unless $mogadm;
1360 my $res;
1361 eval {
1362 $res = _array_to_hashref($mogadm->get_hosts(), 'hostname');
1364 return undef if $@;
1365 return $res;
1368 sub hosts {
1369 my $mogadm = shift() || mogadm();
1370 fail_text('no_mogadm') unless $mogadm;
1372 my $res;
1373 eval {
1374 $res = _array_to_hashref($mogadm->get_hosts(), 'hostid');
1376 return undef if $@;
1377 return $res;
1380 sub devices {
1381 my $mogadm = shift() || mogadm();
1382 fail_text('no_mogadm') unless $mogadm;
1384 my $res;
1385 eval {
1386 $res = _array_to_hashref($mogadm->get_devices(), [ 'hostid', 'devid' ]);
1388 return undef if $@;
1389 return $res;
1392 sub domains {
1393 my $mogadm = shift() || mogadm();
1394 fail_text('no_mogadm') unless $mogadm;
1396 my $res;
1397 eval {
1398 $res = $mogadm->get_domains();
1400 return undef if $@;
1401 return $res;
1404 sub _array_to_hashref {
1405 my ($array, $key) = @_;
1406 die "bad caller to _array_to_hashref\n"
1407 unless $array && $key;
1408 $key = [ $key ] unless ref $key eq 'ARRAY';
1409 my $kmax = scalar(@$key) - 1;
1411 # and a dose of handwavium...
1412 my %res;
1413 foreach my $row (@$array) {
1414 my $ref = \%res;
1415 for (my $i = 0; $i <= $kmax; $i++) {
1416 if ($i == $kmax) {
1417 # we're on the last key so just assign into $ref
1418 $ref->{$row->{$key->[$i]}} = $row;
1419 } else {
1420 # not on the last, so keep descending
1421 $ref->{$row->{$key->[$i]}} ||= {};
1422 $ref = $ref->{$row->{$key->[$i]}};
1427 # return result.. duh
1428 return \%res;
1431 __END__
1433 =head1 NAME
1435 mogadm - MogileFS admin tool
1437 =head1 SYNOPSIS
1439 $ mogadm [config options] <argument(s)> [argument options]
1441 $ mogadm
1442 ....
1443 (prints contextual help, if missing command/arguments)
1446 =head1 OPTIONS
1448 =over 8
1450 =item B<--lib=/path/to/lib>
1452 Set this option to a path to include this directory in the module
1453 search path.
1455 =item B<--trackers=10.0.0.117:7001,10.0.0.118:7001,...>
1457 Use these MogileFS trackers for status information.
1459 =back
1461 =head1 ARGUMENTS
1463 =over 8
1465 =item B<check>
1467 Check to ensure that all of the MogileFS system components are functioning
1468 and that we can contact everybody. The quickest way of ensuring that the
1469 entire MogileFS system is functional from the current machine's point of view.
1471 =item B<host add E<lt>hostE<gt> [host options]>
1473 =item B<host modify E<lt>hostE<gt> [host options]>
1475 =item B<host mark E<lt>hostE<gt> E<lt>statusE<gt>>
1477 =item B<host delete E<lt>hostE<gt>>
1479 =item B<host list>
1481 Functions for manipulating hosts. For add and modify, host options is in
1482 the format of normal command line options and can include anything in the
1483 L</"HOST OPTIONS"> section.
1485 =item B<device add E<lt>hostE<gt> E<lt>device idE<gt>>
1487 =item B<device mark E<lt>hostE<gt> E<lt>device idE<gt> E<lt>statusE<gt>>
1489 =item B<device modify E<lt>hostE<gt> E<lt>deviceE<gt> [device options]>
1491 =item B<device delete E<lt>hostE<gt> E<lt>deviceE<gt>>
1493 =item B<device list>
1495 =item B<device next>
1497 Functions for manipulating devices. For add and modify, device options are in
1498 the format of normal command line options and can include anything in the
1499 L</"DEVICE OPTIONS"> section.
1501 =item B<domain add E<lt>domainE<gt>>
1503 =item B<domain delete E<lt>domainE<gt>>
1505 =item B<domain list>
1507 Simple commands for managing MogileFS domains. Note that you cannot delete
1508 a domain unless it has no classes and is devoid of files.
1510 =item B<class add E<lt>domainE<gt> E<lt>classE<gt> [class options]>
1512 =item B<class modify E<lt>domainE<gt> E<lt>classE<gt> [class options]>
1514 =item B<class delete E<lt>domainE<gt> E<lt>classE<gt>>
1516 =item B<class list>
1518 Commands for working with classes. Please see the L</"CLASS OPTIONS"> section
1519 for the options to use with add/modify. Also, delete requires that the class
1520 have no files in it before it will allow the deletion.
1522 =item B<slave ...>
1524 Add/remove slaves replicating from MogileFS master database.
1526 TODO: detail this
1528 Run B<mogadm slave> by itself for contextual help.
1530 =item B<fsck printlog>
1532 =item B<fsck taillog>
1534 =item B<fsck clearlog>
1536 Display or clear the log of fsck events.
1538 =item B<fsck reset [fsck options]>
1540 Reset fsck position back to the beginning. Please see the L</"FSCK OPTIONS">
1541 section for options to use with fsck.
1543 =item B<fsck start>
1545 Start (or resume) background fsck from the last checked fid. If you want to
1546 check every fid, you must call B<fsck reset> before calling start.
1548 =item B<fsck status>
1550 Show the status of the presently active (or last if none active) fsck. This
1551 includes what FIDs are being checked, time statistics, check type as well as a
1552 summary of problems encountered so far.
1554 =item B<fsck stop>
1556 Stop (pause) background fsck
1558 =item B<settings list>
1560 Display all present MogileFS settings.
1562 =item B<settings set E<lt>keyE<gt> E<lt>valueE<gt>>
1564 Set the server setting for 'key' to 'value'.
1566 The current settings are E<lt>enable_rebalanceE<gt> (set to 1 to start
1567 rebalance mode to move files to under-used devices) and
1568 E<lt>memcache_serversE<gt> (enable memcached caching in the tracker).
1570 =back
1572 =head1 HOST OPTIONS
1574 =over 8
1576 =item B<--ip=E<lt>ip of hostE<gt>>
1578 =item B<--port=E<lt>port of mogstored on hostE<gt>>
1580 Contact information for the host. This is the minimum set of information needed
1581 to setup a host.
1583 =item B<--getport=E<lt>alternate retrieval part on hostE<gt>>
1585 If provided, causes the tracker to use this port for retrieving files. Uploads are
1586 still processed at the standard port.
1588 =item B<--altip=E<lt>alternate IPE<gt>>
1590 =item B<--altmask=E<lt>mask to activate alternate IPE<gt>>
1592 If a client request comes in from an IP that matches the alternate mask, then the
1593 host IP is treated as the alternate IP instead of the standard IP. This can be
1594 used, for example, if you have two networks and you need to return one IP to
1595 reach the node on one network, but a second IP to reach it on the alternate
1596 network.
1598 =item B<--status=E<lt>host statusE<gt>>
1600 Valid host statuses are one of: alive, down, readonly.
1602 =back
1604 =head1 DEVICE OPTIONS
1606 =over 8
1608 =item B<--status=E<lt>device statusE<gt>>
1610 Valid device statuses are one of: alive, dead, down, drain, readonly.
1612 =item B<--weight=E<lt>device weight<gt>>
1614 The weight used in calculation of preferred paths. It must be a positive
1615 integer.
1617 =back
1619 =head1 CLASS OPTIONS
1621 =over 8
1623 =item B<--mindevcount=E<lt>valueE<gt>>
1625 Number of devices the files in this class should be replicated across. Can be
1626 set to anything >= 1.
1628 =item B<--replpolicy=E<lt>valueE<gt>>
1630 Stringified replication policy. ie "MultipleHosts(3)" is equivalent to a
1631 --mindevcount=3. See documentation or plugins on alternative policies.
1633 =item B<--hashtype=E<lt>valueE<gt>>
1635 Name of the hash algorithm used for checksumming. "MD5" or "NONE" for no
1636 checksumming.
1638 =back
1640 =head1 FSCK OPTIONS
1642 =over 8
1644 =item B<--policy-only>
1646 Check replication policy (assumed locations) only; don't stat storage nodes for
1647 actual file presence.
1649 =back
1651 =head1 EXAMPLES
1653 Host manipulation:
1655 $ mogadm host list
1656 $ mogadm host add foo.local
1657 $ mogadm host add foo.local --status=down --ip=10.0.0.34 --port=7900
1658 $ mogadm host mark foo.local down
1659 $ mogadm host modify foo.local --port=7500
1660 $ mogadm host delete foo.local
1662 Device manipulation:
1664 $ mogadm device list
1665 $ mogadm device summary
1666 $ mogadm device summary --status=dead,down
1667 $ mogadm device next
1668 $ mogadm device add foo.local 16
1669 $ mogadm device add foo.local 17 --status=alive
1670 $ mogadm device mark foo.local 17 down
1671 $ mogadm device modify foo.local 17 --status=alive --weight=10
1672 $ mogadm device delete foo.local 17
1674 Domain manipulation:
1676 $ mogadm domain list
1677 $ mogadm domain add first.domain
1678 $ mogadm domain delete first.domain
1680 Class manipulation
1682 $ mogadm class list
1683 $ mogadm class add first.domain my.class
1684 $ mogadm class add first.domain my.class --mindevcount=3
1685 $ mogadm class add first.domain my.class --replpolicy="MultipleHosts(3)"
1686 $ mogadm class modify first.domain my.class --mindevcount=2
1687 $ mogadm class modify first.domain my.class --replpolicy="MultipleHosts(3)"
1688 $ mogadm class delete first.domain my.class
1690 Check the status of your entire MogileFS system:
1692 $ mogadm check
1694 Check every file in the entire MogileFS system:
1696 $ mogadm fsck reset
1697 $ mogadm fsck start
1698 $ mogadm fsck status
1699 $ mogadm fsck printlog
1701 See all the things mogadm can do:
1703 $ mogadm
1705 Get help on a sub-command:
1707 $ mogadm device
1710 =head1 CONFIGURATION
1712 It is recommended that you create a configuration file such as C</etc/mogilefs/mogilefs.conf> (or at C<~/.mogilefs.conf>) to
1713 be used for configuration information. Basically all you need is something like:
1715 trackers = 10.0.0.23:7001, 10.0.0.15:7001
1717 # if MogileFS::Admin files aren't installed in standard places:
1718 lib = /home/mogilefs/cgi-bin
1720 Note that these can also be specified on the command line, as per above.
1722 =head1 AUTHOR
1724 Brad Fitzpatrick E<lt>L<brad@danga.com>E<gt>
1726 Mark Smith E<lt>L<junior@danga.com>E<gt>
1728 Leon Brocard E<lt>L<acme@astray.com>E<gt>, open sourced permissions from Foxtons Ltd.
1730 Robin H. Johnson E<lt>robbat2@orbis-terrarum.netE<gt>
1732 =head1 BUGS
1734 Please report any on the MogileFS mailing list: L<http://groups.google.com/group/mogile/>.
1736 =head1 LICENSE
1738 Licensed for use and redistribution under the same terms as Perl itself.
1740 =cut