#!/usr/bin/perl # use strict; use warnings; use Getopt::Long; use LWP::Simple; # FIXME: use of this makes 'mog check' hang too long when multiple things down use Socket; my @topcmds = qw(check host device domain class slave); my $usage = { check => { des => "Check the state of the MogileFS world.", }, host => { des => "Add/modify hosts.", subcmd => { list => { des => "List all hosts.", }, add => { des => "Add a host to MogileFS.", args => " [opts]", opts => { "" => "Hostname of machine", "--status=s" => "One of {alive,down}. Default 'down'.", "--ip=s" => "IP address of machine.", "--port=i" => "HTTP port of mogstored", "--getport=i" => "Alternate HTTP port serving readonly traffic", "--altip=s" => "Alternate IP that is machine is reachable from", "--altmask=s" => "Netmask which, when matches client, uses alt IP", }, }, modify => { des => "Modify a host's properties.", args => " [opts]", opts => { "" => "Host name.", "--status=s" => "One of {alive,down}.", "--ip=s" => "IP address of machine.", "--port=i" => "HTTP port of mogstored", "--getport=i" => "Alternate HTTP port serving readonly traffic", "--altip=s" => "Alternate IP that is machine is reachable from", "--altmask=s" => "Netmask which, when matches client, uses alt IP", }, }, mark => { des => "Change the status of a host. (equivalent to 'modify --status')", args => " ", opts => { "" => "Host name to bring up or down.", "" => "One of {alive,down}.", } }, delete => { des => "Delete a host.", args => "", opts => { "" => "Host name to delete.", }, }, }, }, device => { des => "Add/modify devices.", subcmd => { list => { des => "List all devices, for each host.", }, add => { des => "Add a device to a host.", args => " [opts]", opts => { "" => "Hostname to add a device", "" => "Numeric devid. Never reuse these.", "--status=s" => "One of 'alive' or 'down'. Defaults to 'alive'.", }, }, mark => { des => "Mark a device as {alive,dead,down,readonly}", args => " ", opts => { "" => "Hostname of device", "" => "Numeric devid to modify.", "" => "One of {alive,dead,down,readonly}", }, }, }, }, domain => { des => "Add/modify domains (namespaces)", subcmd => { list => { des => "List all hosts.", }, add => { des => "Add a domain (namespace)", args => "", opts => { "" => "Domain (namespace) to add.", }, }, delete => { des => "Delete a domain.", args => "", opts => { "" => "Domain (namespace) to add.", }, }, }, }, class => { des => "Add/modify file classes.", subcmd => { list => { des => "List all classes, for each domain.", }, add => { des => "Add a file class to a domain.", args => " [opts]", opts => { "" => "Domain to add class to.", "" => "Name of class to add.", "--mindevcount=i" => "Mininum number of replicas.", }, }, modify => { des => "Modify properties of a file class.", args => " [opts]", opts => { "" => "Domain to add class to.", "" => "Name of class to add.", "--mindevcount=i" => "Mininum number of replicas.", }, }, delete => { des => "Delete a file class from a domain.", args => " ", opts => { "" => "Domain of class to delete.", "" => "Class to delete.", }, }, }, }, slave => { des => 'Manipulate slave database information in a running mogilefsd.', subcmd => { list => { des => 'List current store slave nodes.', }, add => { des => 'Add a slave node for store usage', args => ' [opts]', opts => { '--dsn=s' => "DBI DSN specifying what database to connect to.", '--username=s' => "DBI username for connecting.", '--password=s' => "DBI password for connecting.", }, }, modify => { des => 'Modify a slave node for store usage', args => ' [opts]', opts => { '--dsn=s' => "DBI DSN specifying what database to connect to.", '--username=s' => "DBI username for connecting.", '--password=s' => "DBI password for connecting.", }, }, delete => { des => 'Delete a slave node for store usage', args => '', }, }, }, }; # load up our config files my %opts; my @configs = ($opts{config}, "$ENV{HOME}/.mogilefs.conf", "/etc/mogilefs/mogilefs.conf"); foreach my $fn (reverse @configs) { next unless $fn && -e $fn; open FILE, "<$fn" or die "unable to open $fn: $!\n"; while () { s/\#.*//; next unless m!^\s*(\w+)\s*=\s*(.+?)\s*$!; $opts{$1} = $2; } close FILE; } Getopt::Long::Configure("require_order", "pass_through"); GetOptions( "trackers=s" => \$opts{trackers}, "config=s" => \$opts{config}, "lib=s" => \$opts{lib}, "help" => \$opts{help}, "verbose" => \$opts{verbose}, ) or abortWithUsage(); Getopt::Long::Configure("require_order", "no_pass_through"); # bail for help abortWithUsage() if $opts{help}; # make sure we have at least a topcmd my $topcmd = shift(@ARGV); abortWithUsage() unless $topcmd && $usage->{$topcmd}; # break up the trackers and ensure we got some if ($opts{trackers}) { $opts{trackers} = [ split(/\s*,\s*/, $opts{trackers}) ]; } fail_text('no_trackers') unless ($opts{trackers} && @{$opts{trackers}}) || detect_local_tracker(); # okay, load up the libraries that we need if ($opts{lib}) { eval "use lib '$opts{lib}';"; } eval "use MogileFS::Admin; use MogileFS::Client; 1;" or fail_text('cant_find_module'); # dispatch if it's special if ($topcmd eq 'check') { die "Unknown options/arguments to 'check' command.\n" if @ARGV; cmd_check(); } # get the verb my $verb = shift(@ARGV) or abort_with_topcmd_help($topcmd); my $cmdinfo = $usage->{$topcmd}{subcmd}{$verb}; abort_with_topcmd_help($topcmd) unless $cmdinfo; my $badargs = sub { my $msg = shift; abort_with_topcmd_help($topcmd, $verb, $msg); }; # get the non-option (non --foo) arguments: my %cmdargs; if (my $args = $cmdinfo->{args}) { my @args = split(/ /, $args); foreach my $arg (@args) { # positional (but named) parameter if ($arg =~ /^<(.+)>$/) { my $argname = $1; my $val = shift @ARGV; # map e.g. "dev5" to 5 if ($argname eq "devid" && $val && $val =~ /^dev(\d+)$/) { $val = $1; } $badargs->("Missing argument '$argname'") unless $val; $badargs->("Unexpected option. Expected argument '$argname'") if $val =~ /^-/; $cmdargs{$argname} = $val; } elsif ($arg eq "[opts]") { # handled later. } else { die "INTERNAL ERROR."; } } $badargs->("Unexpected extra argument.") if @ARGV && $ARGV[0] !~ /^-/; } else { $badargs->("Unexpected arguments when expecting none.") if @ARGV; } # parse the options if (my $opts = $cmdinfo->{opts}) { my %getopts; foreach (keys %$opts) { my $k = $_; next if $k =~ /^(\%cmdargs); exit 0; sub detect_local_tracker { require IO::Socket::INET; my $loctrack = "127.0.0.1:7001"; my $sock = IO::Socket::INET->new(PeerAddr => $loctrack, Timeout => 1); return 0 unless $sock; $opts{trackers} = [$loctrack]; return 1; } ########################################################################### ## command routines ########################################################################### sub cmd_check { # step 1: we want to check each tracker for responsiveness my $now = time(); my ($hosts, $devices); $| = 1; print "Checking trackers...\n"; foreach my $t (@{$opts{trackers}}) { print " $t ... "; my $mogadm = mogadm($t); if ($mogadm) { my $lhosts = hosts($mogadm); my $ldevs = devices($mogadm); if ($lhosts && $ldevs) { print "OK\n"; $hosts = $lhosts; $devices = $ldevs; } else { print "REQUEST FAILURE (is the tracker up?)\n"; } } else { print "INITIAL FAILURE\n"; } } # we should have hosts if we get here fail_text('no_hosts') unless $hosts; print "\n"; # step 2: now hit each of the hosts for responsiveness print "Checking hosts...\n"; my @urls; foreach my $hostid (sort { $a <=> $b } keys %$hosts) { printf " [%2d] %s ... ", $hostid, $hosts->{$hostid}->{hostname}; if ($hosts->{$hostid}->{status} eq 'alive') { my $url = 'http://' . $hosts->{$hostid}->{hostip} . ':' . $hosts->{$hostid}->{http_port} . '/'; my $file = get($url); if (defined $file) { print "OK\n"; push @urls, [ $hostid, $url ]; } else { print "REQUEST FAILURE\n"; } } else { print "skipping; status = $hosts->{$hostid}->{status}\n"; } } # everything should be chill fail_text('no_devices') unless @urls; print "\n"; # step 3: check devices for each host print "Checking devices...\n"; printf " host device %10s %10s %10s %7s %7s %4s\n", 'size(G)', 'used(G)', 'free(G)', 'use% ', 'ob state', 'I/O%'; printf " ---- ------------ ---------- ---------- ---------- ------ ---------- -----\n"; my %total; foreach my $hosturl (@urls) { my ($hostid, $url) = @$hosturl; my $devs = $devices->{$hostid}; foreach my $devid (sort { $a <=> $b } keys %$devs) { printf " [%2d] %-7s", $hostid, "dev$devid"; my $usage = get($url . "/dev$devid/usage"); if (defined $usage) { my %data = ( map { split(/:\s+/, $_) } split(/\r?\n/, $usage) ); $data{age} = $now - $data{time}; $data{used} /= 1024**2; $data{total} /= 1024**2; $data{avail} = $data{total} - $data{used}; my $pct = $data{used}/$data{total}*100; $total{used} += $data{used}; $total{avail} += $data{avail}; $total{total} += $data{total}; printf(" %10.3f %10.3f %10.3f %6.2f%% %-7s %5.1f\n", (map { $data{$_} } qw(total used avail)), $pct, ($devs->{$devid}->{observed_state} || "?"), ($devs->{$devid}->{utilization} || 0)); } else { print "REQUEST FAILURE\n"; } } } my $pct = $total{used}/$total{total}*100; printf " ---- ------------ ---------- ---------- ---------- ------\n"; printf " total:%10.3f %10.3f %10.3f %6.2f%%\n", (map { $total{$_} } qw(total used avail)), $pct; # if we get here, all's well ok(); } sub cmd_host_list { my $hosts = hosts(); fail_text('no_hosts') unless $hosts; foreach my $hostid (sort keys %$hosts) { my $host = $hosts->{$hostid}; print "$host->{hostname} [$hostid]: $host->{status}\n"; my @data = ( 'IP', "$host->{hostip}:$host->{http_port}", 'Alt IP', $host->{altip}, 'Alt Mask', $host->{altmask}, 'GET Port', $host->{http_get_port}, ); while (my ($k, $v) = splice(@data, 0, 2)) { next unless $v; printf " %-10s\%s\n", "$k:", $v; } print "\n"; } ok(); } sub cmd_host_add { my $args = shift; my $hosts = hosts_byname(); fail_text('no_hosts') unless $hosts; my $name = delete $args->{hostname}; cmd_help_die("No hostname") unless $name; fail('Host already exists.') if $hosts->{$name}; # make sure we have an ip unless ($args->{ip}) { my $addr = gethostbyname($name); fail_text('host_add_no_ip') unless $addr; $args->{ip} = inet_ntoa($addr); } # defaults $args->{port} ||= 7500; $args->{status} ||= 'down'; # FIXME: verify the status can't be 'alive' if we can't get to ip:port # OR BETTER: also make default status the reachability of that ip:port # now create the host my $mogadm = mogadm(); $mogadm->create_host($name, $args); if ($mogadm->err) { fail("Failure creating host: " . $mogadm->errstr); } ok('Host has been created.'); } sub cmd_host_modify { my $args = shift; my $name = delete $args->{hostname}; # FIXME: verify the status can't be 'alive' if we can't get to ip:port # now modify the host my $mogadm = mogadm(); $mogadm->update_host($name, $args); if ($mogadm->err) { fail("Failure modifying host: " . $mogadm->errstr); } ok('Host has been modified.'); } sub cmd_host_delete { my $args = shift; my $name = delete $args->{hostname}; # now modify the host my $mogadm = mogadm(); $mogadm->delete_host($name); if ($mogadm->err) { fail("Failure deleting host: " . $mogadm->errstr); } ok('Host has been deleted.'); } sub cmd_host_mark { my $args = shift; my $mogadm = mogadm(); $mogadm->update_host($args->{hostname}, { status => $args->{status} }); if ($mogadm->err) { fail("Failure updating host status: " . $mogadm->errstr); } ok('Host status updated.'); } sub cmd_domain_list { # actually lists domains and classes my $domains = domains() or fail_text('no_domains'); # now iterate printf " %-20s %-20s \%s\n", "domain", "class", "mindevcount"; printf "%-20s %-20s \%s\n", '-' x 20, '-' x 20, '-' x 13; foreach my $domain (sort keys %$domains) { foreach my $class (sort keys %{$domains->{$domain}}) { printf " %-20s %-20s %d\n", $domain, $class, $domains->{$domain}->{$class} || 0; } print "\n"; } ok(); } sub cmd_domain_add { my $args = shift; my $domains = domains() or fail_text('no_domains'); # make sure it doesn't exist my $domain = delete $args->{domain}; fail('Domain already exists.') if $domains->{$domain}; # create my $mogadm = mogadm(); $mogadm->create_domain($domain); if ($mogadm->err) { fail('Error creating domain: ' . $mogadm->errstr); } ok('Domain created.'); } sub cmd_domain_delete { my $args = shift; my $domains = domains() or fail_text('no_domains'); # make sure it doesn't exist my $domain = $args->{domain}; fail('Domain not found.') unless $domains->{$domain}; # destroy my $mogadm = mogadm(); $mogadm->delete_domain($domain); if ($mogadm->err) { fail('Error deleting domain: ' . $mogadm->errstr); } ok('Domain deleted.'); } sub cmd_class_list { # same, pass it through cmd_domain_list(); } sub cmd_class_add { my $args = shift; my $domains = domains() or fail_text('no_domains'); my $domain = $args->{domain}; my $class = $args->{class}; cmd_help_die() unless $domain && $class; fail('Domain not found.') unless $domains->{$domain}; fail('Class already exists.') if $domains->{$domain}->{$class}; $args->{mindevcount} ||= 2; my $mogadm = mogadm(); $mogadm->create_class($domain, $class, $args->{mindevcount}); if ($mogadm->err) { fail('Error creating class: ' . $mogadm->errstr); } ok('Class created.'); } sub cmd_class_modify { my $args = shift; my $domains = domains() or fail_text('no_domains'); my $domain = $args->{domain}; my $class = $args->{class}; cmd_help_die() unless $domain && $class; fail('Domain not found.') unless $domains->{$domain}; fail('Class does not exist.') unless $domains->{$domain}->{$class}; $args->{mindevcount} ||= 2; my $mogadm = mogadm(); $mogadm->update_class($domain, $class, $args->{mindevcount}); if ($mogadm->err) { fail('Error updating class: ' . $mogadm->errstr); } ok('Class updated.'); } sub cmd_class_delete { my $args = shift; my $domains = domains() or fail_text('no_domains'); my $domain = $args->{domain}; my $class = $args->{class}; cmd_help_die() unless $domain && $class; fail('Domain not found.') unless $domains->{$domain}; fail('Class does not exist.') unless $domains->{$domain}->{$class}; my $mogadm = mogadm(); $mogadm->delete_class($domain, $class); if ($mogadm->err) { fail('Error deleting class: ' . $mogadm->errstr); } ok('Class deleted.'); } sub cmd_device_add { my $args = shift; my $hosts = hosts() or fail_text('no_hosts'); my $host = $args->{hostname}; my $devid = $args->{devid}; my $state = $args->{status} || "alive"; cmd_help_die("devid should be numeric") unless $devid =~ /^\d+$/; # FIXME: server should be fixed to verify via HTTP that the devid directory exists my $mogadm = mogadm(); $mogadm->create_device(hostname => $host, devid => $devid, state => $state); if ($mogadm->err) { fail('Error adding device: ' . $mogadm->errstr); } ok('Device added.'); } sub cmd_device_mark { my $args = shift; my $mogadm = mogadm(); $mogadm->change_device_state($args->{hostname}, $args->{devid}, $args->{status}); if ($mogadm->err) { fail('Error updating device: ' . $mogadm->errstr); } ok('Device updated.'); } sub cmd_device_list { my $hosts = hosts(); fail_text('no_hosts') unless $hosts; my $devs = devices(); fail_text('no_devices') unless $devs; foreach my $hostid (sort keys %$hosts) { my $host = $hosts->{$hostid}; print "$host->{hostname} [$hostid]: $host->{status}\n"; printf "%6s %-10s %7s %7s %7s\n", '', '', 'used(G)', 'free(G)', 'total(G)'; foreach my $devid (sort keys %{$devs->{$hostid} || {}}) { my $dev = $devs->{$hostid}->{$devid}; my $total = $dev->{mb_total} / 1024; my $used = $dev->{mb_used} / 1024; my $free = $total - $used; printf "%6s: %-10s %-7.3f %-7.3f %-7.3f\n", "dev$devid", $dev->{status}, $used, $free, $total; } print "\n"; } ok(); } sub cmd_slave_list { my $mogadm = mogadm(); my $slaves = $mogadm->slave_list(); foreach my $key (sort keys %$slaves) { my $slavedata = $slaves->{$key}; my ($dsn, $username, $password) = @$slavedata; print "$key --dsn=$dsn --username=$username --password=$password\n"; } ok(); } sub cmd_slave_add { my $mogadm = mogadm(); my $args = shift; my $rc = $mogadm->slave_add($args->{slave_key}, $args->{dsn}, $args->{username}, $args->{password}); if ($rc) { ok("Slave added"); } else { fail("Slave failed to be added"); } } sub cmd_slave_modify { my $mogadm = mogadm(); my $args = shift; my $key = delete $args->{slave_key} or cmd_help_die("Key argument is required"); my $rc = $mogadm->slave_modify($key, %$args); if ($rc) { ok("Slave modify success"); } else { fail("Slave modify failure: " . $mogadm->errstr); } } sub cmd_slave_delete { my $mogadm = mogadm(); my $args = shift; my $rc = $mogadm->slave_delete($args->{slave_key}); if ($rc) { ok("Slave deleted"); } else { fail("Slave delete failed"); } } ########################################################################### ## helper routines ########################################################################### sub abortWithUsage { my $ret = "Usage: (enter any command prefix, leaving off options, for further help)\n\n"; foreach my $cmd (@topcmds) { my $sbc = $usage->{$cmd}->{subcmd}; if ($sbc) { $ret .= " mogadm $cmd ...\n"; } else { $ret .= sprintf(" mogadm %-25s %-s\n", "$cmd", $usage->{$cmd}{des} || ""); next; } foreach my $v (sort keys %$sbc) { my $scv = $usage->{$cmd}{subcmd}{$v}; $ret .= " "; my $dotdot = $scv->{args} ? "..." : ""; $ret .= sprintf(" %-25s %-s\n", "$cmd $v $dotdot", $scv->{des} || ""); } } print $ret, "\n"; exit(1); } sub abort_with_topcmd_help { my ($cmd, $verb, $msg) = @_; if ($msg) { print "\nERROR: $msg\n\n"; } my $cmdsfx = $verb ? "-$verb" : ""; my $ret = "Help for '$cmd$cmdsfx' command:\n"; unless ($verb) { $ret .= " (enter any command prefix, leaving off options, for further help)\n"; } $ret .= "\n"; foreach my $subcmdv (sort keys %{$usage->{$cmd}{subcmd}}) { next if $verb && $verb ne $subcmdv; my $scv = $usage->{$cmd}{subcmd}{$subcmdv}; $ret .= sprintf(" %-50s %-s\n", "mogadm $cmd $subcmdv " . ($scv->{args} || ""), $scv->{des}); } print $ret, "\n"; if ($verb) { my $scv = $usage->{$cmd}{subcmd}{$verb}; foreach my $opt (sort { (substr($b, 0, 1) cmp substr($a, 0, 1)) || $a cmp $b } keys %{$scv->{opts} || {}}) { printf(" %-20s %s\n", $opt, $scv->{opts}->{$opt}); } print "\n"; } exit 1; } sub cmd_help_die { my ($msg) = @_; abort_with_topcmd_help($topcmd, $verb, $msg); } sub text { return { ###################################################################### cant_find_module => < < "Unable to retrieve domains from tracker(s).\n", ###################################################################### no_devices => "No devices found on tracker(s).\n", ###################################################################### host_add_no_ip => < --ip= [...] END ###################################################################### no_hosts => < <{$_[0]} || "UNDEFINED [$_[0]]"; } sub fail_text { print STDERR text($_[0]) . "\n"; exit 1; } sub fail { print STDERR $_[0] . "\n"; exit 1; } sub ok { if ($opts{verbose}) { print STDOUT $_[0] . "\n" if $_[0]; } exit 0; } sub mogadm { my $host = shift(); if ($host) { $host = [ $host ] unless ref $host; } else { $host = $opts{trackers}; } # $MogileFS::DEBUG = 2; my $mogadm = MogileFS::Admin->new( hosts => $host ); fail_text('no_mogadm') unless $mogadm; return $mogadm; } sub hosts_byname { my $mogadm = shift() || mogadm(); fail_text('no_mogadm') unless $mogadm; my $res; eval { $res = _array_to_hashref($mogadm->get_hosts(), 'hostname'); }; return undef if $@; return $res; } sub hosts { my $mogadm = shift() || mogadm(); fail_text('no_mogadm') unless $mogadm; my $res; eval { $res = _array_to_hashref($mogadm->get_hosts(), 'hostid'); }; return undef if $@; return $res; } sub devices { my $mogadm = shift() || mogadm(); fail_text('no_mogadm') unless $mogadm; my $res; eval { $res = _array_to_hashref($mogadm->get_devices(), [ 'hostid', 'devid' ]); }; return undef if $@; return $res; } sub domains { my $mogadm = shift() || mogadm(); fail_text('no_mogadm') unless $mogadm; my $res; eval { $res = $mogadm->get_domains(); }; return undef if $@; return $res; } sub _array_to_hashref { my ($array, $key) = @_; die "bad caller to _array_to_hashref\n" unless $array && $key; $key = [ $key ] unless ref $key eq 'ARRAY'; my $kmax = scalar(@$key) - 1; # and a dose of handwavium... my %res; foreach my $row (@$array) { my $ref = \%res; for (my $i = 0; $i <= $kmax; $i++) { if ($i == $kmax) { # we're on the last key so just assign into $ref $ref->{$row->{$key->[$i]}} = $row; } else { # not on the last, so keep descending $ref->{$row->{$key->[$i]}} ||= {}; $ref = $ref->{$row->{$key->[$i]}}; } } } # return result.. duh return \%res; } __END__ =head1 NAME mogadm - MogileFS admin tool =head1 SYNOPSIS mogadm [config options] [argument options] =head1 OPTIONS =over 8 =item B<--lib=/path/to/lib> Set this option to a path to include this directory in the module search path. =item B<--trackers=10.0.0.117:7001,10.0.0.118:7001,...> Use these MogileFS trackers for status information. =back =head1 ARGUMENTS =over 8 =item B Check to ensure that all of the MogileFS system components are functioning and that we can contact everybody. The quickest way of ensuring that the entire MogileFS system is functional from the current machine's point of view. =item BhostE [host options]> =item BhostE [host options]> =item BhostE EstatusE> =item BhostE> =item B Functions for manipulating hosts. For add and modify, host options is in the format of normal command line options and can include anything in the L section. =item BhostE [device id]> =item BhostE Edevice idE EstatusE> =item BhostE EdeviceE> =item B Adding, editing, and deleting devices. The device id is optional in the add command: if you provide one, it will be setup using the provided id; else, a new id is assigned and provided to you for setting up the device. When marking devices, the status can be one of {alive|down|dead|readonly}. =item BdomainE> =item BdomainE> =item B Simple commands for managing MogileFS domains. Note that you cannot delete a domain unless it has no classes and is devoid of files. =item BdomainE EclassE [class options]> =item BdomainE EclassE [class options]> =item BdomainE EclassE> =item B Commands for working with classes. Please see the L section for the options to use with add/modify. Also, delete requires that the class have no files in it before it will allow the deletion. =back =head1 HOST OPTIONS =over 8 =item B<--ip=Eip of hostE> =item B<--port=Eport of mogstored on hostE> Contact information for the host. This is the minimum set of information needed to setup a host. =item B<--getport=Ealternate retrieval part on hostE> If provided, causes the tracker to use this port for retrieving files. Uploads are still processed at the standard port. =item B<--altip=Ealternate IPE> =item B<--altmask=Emask to activate alternate IPE> If a client request comes in from an IP that matches the alternate mask, then the host IP is treated as the alternate IP instead of the standard IP. This can be used, for example, if you have two networks and you need to return one IP to reach the node on one network, but a second IP to reach it on the alternate network. =item B<--status=Ehost statusE> Valid statuses are one of: alive, down, dead. =back =head1 CLASS OPTIONS =over 8 =item B<--mindevcount=EvalueE> Number of devices the files in this class should be replicated across. Can be set to anything >= 1. =back =head1 EXAMPLES Host manipulation: $ mogadm host list $ mogadm host add foo.local $ mogadm host add foo.local --status=down --ip=10.0.0.34 --port=7900 $ mogadm host mark foo.local down $ mogadm host modify foo.local --port=7500 $ mogadm host delete foo.local Device manipulation: $ mogadm device list $ mogadm device add foo.local 16 $ mogadm device add foo.local 17 --status=alive $ mogadm device mark foo.local 17 down $ mogadm device delete foo.local 17 Domain manipulation: $ mogadm domain list $ mogadm domain add first.domain $ mogadm domain delete first.domain Class manipulation $ mogadm class list $ mogadm class add first.domain my.class $ mogadm class add first.domain my.class --mindevcount=3 $ mogadm class modify first.domain my.class --mindevcount=2 $ mogadm class delete first.domain my.class Check the status of your entire MogileFS system: $ mogadm check =head1 CONFIGURATION It is recommended that you create a configuration file such as C to be used for configuration information. Basically all you need is something like: lib = /home/mogilefs/cgi-bin trackers = 10.0.0.23:7001, 10.0.0.15:7001 The C directive needs to point to the directory containing under it the relative file C. The trackers directive points us at the proper location for finding your trackers. Note that these can also be specified on the command line, as per above. =head1 AUTHOR Mark Smith EL<"junior@danga.com">E. Brad Fitzpatrick EL<"brad@danga.com">E. =head1 BUGS Please report any on the MogileFS mailing list: L<"http://lists.danga.com/mogilefs/">. =head1 LICENSE Licensed for use and redistribution under the same terms as Perl itself. =cut