/usr/local/CPAN/MogileFS-Client/MogileFS/Admin.pm
package MogileFS::Admin;
use strict;
use Carp;
use MogileFS::Backend;
use fields qw(backend readonly);
sub new {
my MogileFS::Admin $self = shift;
$self = fields::new($self) unless ref $self;
my %args = @_;
$self->{readonly} = $args{readonly} ? 1 : 0;
my %backend_args = ( hosts => $args{hosts} );
$backend_args{timeout} = $args{timeout} if $args{timeout};
$self->{backend} = MogileFS::Backend->new( %backend_args )
or _fail("couldn't instantiate MogileFS::Backend");
return $self;
}
sub readonly {
my MogileFS::Admin $self = shift;
return $self->{readonly} = $_[0] ? 1 : 0 if @_;
return $self->{readonly};
}
sub replicate_now {
my MogileFS::Admin $self = shift;
my $res = $self->{backend}->do_request("replicate_now", {})
or return undef;
return 1;
}
sub get_hosts {
my MogileFS::Admin $self = shift;
my $hostid = shift;
my $args = $hostid ? { hostid => $hostid } : {};
my $res = $self->{backend}->do_request("get_hosts", $args)
or return undef;
my @ret = ();
foreach my $ct (1..$res->{hosts}) {
push @ret, { map { $_ => $res->{"host${ct}_$_"} }
qw(hostid status hostname hostip http_port http_get_port altip altmask) };
}
return \@ret;
}
sub get_devices {
my MogileFS::Admin $self = shift;
my $devid = shift;
my $args = $devid ? { devid => $devid } : {};
my $res = $self->{backend}->do_request("get_devices", $args)
or return undef;
my @ret = ();
foreach my $ct (1..$res->{devices}) {
push @ret, { (map { $_ => $res->{"dev${ct}_$_"} } qw(devid hostid status observed_state utilization)),
(map { $_ => $res->{"dev${ct}_$_"}+0 } qw(mb_total mb_used weight)) };
}
return \@ret;
}
# get raw information about fids, for enumerating the dataset
# ( $from_fid, $count )
# returns:
# { fid => { hashref with keys: domain, class, devcount, length, key } }
sub list_fids {
my MogileFS::Admin $self = shift;
my ($fromfid, $count) = @_;
my $res = $self->{backend}->do_request('list_fids', { from => $fromfid, to => $count })
or return undef;
my $ret = {};
foreach my $i (1..$res->{fid_count}) {
$ret->{$res->{"fid_${i}_fid"}} = {
key => $res->{"fid_${i}_key"},
length => $res->{"fid_${i}_length"},
class => $res->{"fid_${i}_class"},
domain => $res->{"fid_${i}_domain"},
devcount => $res->{"fid_${i}_devcount"},
};
}
return $ret;
}
sub clear_cache {
my MogileFS::Admin $self = shift;
# do the request, default to request all stats if they didn't specify any
push @_, 'all' unless @_;
my $res = $self->{backend}->do_request("clear_cache", { map { $_ => 1 } @_ })
or return undef;
return 1;
}
# get a hashref of the domains we know about in the format of
# { domain_name => { class_name => mindevcount, class_name => mindevcount, ... }, ... }
sub get_domains {
my MogileFS::Admin $self = shift;
my $res = $self->{backend}->do_request("get_domains", {})
or return undef;
my $ret = {};
foreach my $i (1..$res->{domains}) {
$ret->{$res->{"domain$i"}} = {
map {
$res->{"domain${i}class${_}name"} =>
{ mindevcount => $res->{"domain${i}class${_}mindevcount"},
replpolicy => $res->{"domain${i}class${_}replpolicy"} || '',
}
} (1..$res->{"domain${i}classes"})
};
}
return $ret;
}
# create a new domain
sub create_domain {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my $domain = shift;
my $res = $self->{backend}->do_request("create_domain", { domain => $domain });
return undef unless $res->{domain} eq $domain;
return 1;
}
# delete a domain
sub delete_domain {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my $domain = shift;
$self->{backend}->do_request("delete_domain", { domain => $domain })
or return undef;
return 1;
}
# create a class within a domain
sub create_class {
my MogileFS::Admin $self = shift;
# wrapper around _mod_class(create)
return $self->_mod_class(@_, 'create');
}
# update a class's mindevcount within a domain
sub update_class {
my MogileFS::Admin $self = shift;
# wrapper around _mod_class(update)
return $self->_mod_class(@_, 'update');
}
# delete a class
sub delete_class {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my ($domain, $class) = @_;
$self->{backend}->do_request("delete_class", {
domain => $domain,
class => $class,
}) or return undef;
return 1;
}
# create a host
sub create_host {
my MogileFS::Admin $self = shift;
my $host = shift;
return undef unless $host;
my $args = shift;
return undef unless ref $args eq 'HASH';
return undef unless $args->{ip} && $args->{port};
return $self->_mod_host($host, $args, 'create');
}
# edit a host
sub update_host {
my MogileFS::Admin $self = shift;
my $host = shift;
return undef unless $host;
my $args = shift;
return undef unless ref $args eq 'HASH';
return $self->_mod_host($host, $args, 'update');
}
# delete a host
sub delete_host {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my $host = shift;
return undef unless $host;
$self->{backend}->do_request("delete_host", { host => $host })
or return undef;
return 1;
}
# create a new device
sub create_device {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my (%opts) = @_; #hostname or hostid, devid, state (optional)
my $res = $self->{backend}->do_request("create_device", \%opts)
or return undef;
return 1;
}
# edit a device
sub update_device {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my $host = shift;
my $device = shift;
return undef unless $host;
return undef unless $device;
my $args = shift;
return undef unless ref $args eq 'HASH';
# TODO: provide a native update_device in the MogileFS::Admin command set.
if ($args->{status}){
$self->change_device_state($host, $device, $args->{status}) or return undef;
}
if ($args->{weight}){
$self->change_device_weight($host, $device, $args->{weight}) or return undef;
}
return 1;
}
# change the state of a device; pass in the hostname of the host the
# device is located on, the device id number, and the state you want
# the host to be set to. returns 1 on success, undef on error.
sub change_device_state {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my ($host, $device, $state) = @_;
my $res = $self->{backend}->do_request("set_state", {
host => $host,
device => $device,
state => $state,
}) or return undef;
return 1;
}
# change the weight of a device by passing in the hostname and
# the device id
sub change_device_weight {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my ($host, $device, $weight) = @_;
$weight += 0;
my $res = $self->{backend}->do_request("set_weight", {
host => $host,
device => $device,
weight => $weight,
}) or return undef;
return 1;
}
# returns a hash (list) of key => weight
sub _get_slave_keys {
my MogileFS::Admin $self = shift;
my $backend = $self->{backend};
my $keys_res = $backend->do_request("server_setting", {
key => "slave_keys",
});
return () unless $keys_res;
my %slave_keys;
foreach my $slave (split /,/, $keys_res->{value}) {
my ($key, $weight) = split /=/, $slave, 2;
# Weight can be zero, so don't default to 1 if it's defined and longer than 0 characters.
unless (defined $weight && length $weight) {
$weight = 1;
}
$slave_keys{$key} = $weight;
}
return %slave_keys;
}
# returns a hash (list) of key => options
sub _set_slave_keys {
my MogileFS::Admin $self = shift;
my $backend = $self->{backend};
my %slave_keys = @_;
my @keys;
foreach my $key (keys %slave_keys) {
my $weight = $slave_keys{$key};
if (defined $weight && length $weight && $weight != 1) {
$key .= "=$weight";
}
push @keys, $key;
}
my $keys_res = $backend->do_request("set_server_setting", {
key => "slave_keys",
value => join(',', @keys),
});
return 0 unless $keys_res;
return 1;
}
# returns a hashref of key => [dsn, username, password] specifying slave nodes which can be connected to.
sub slave_list {
my MogileFS::Admin $self = shift;
my $backend = $self->{backend};
my %slave_keys = $self->_get_slave_keys;
my %return;
foreach my $key (keys %slave_keys) {
my $slave_res = $backend->do_request("server_setting", {
key => "slave_$key",
});
next unless $slave_res;
my ($dsn, $username, $password) = split /\|/, $slave_res->{value};
$return{$key} = [$dsn, $username, $password];
}
return \%return;
}
sub slave_add {
my MogileFS::Admin $self = shift;
my ($key, $dsn, $username, $password) = @_;
my $backend = $self->{backend};
my %slave_keys = $self->_get_slave_keys;
if (exists $slave_keys{$key}) {
return 0;
}
my $res = $backend->do_request("set_server_setting", {
key => "slave_$key",
value => join('|', $dsn, $username, $password),
}) or return undef;
$slave_keys{$key} = undef;
$self->_set_slave_keys(%slave_keys);
return 1;
}
sub slave_modify {
my MogileFS::Admin $self = shift;
my $key = shift;
my %opts = @_;
my $backend = $self->{backend};
my %slave_keys = $self->_get_slave_keys;
unless (exists $slave_keys{$key}) {
return 0;
}
my $get_res = $backend->do_request("server_setting", {
key => "slave_$key",
}) or return undef;
my ($dsn, $username, $password) = split /\|/, $get_res->{value};
$dsn = $opts{dsn} if exists $opts{dsn};
$username = $opts{username} if exists $opts{username};
$password = $opts{password} if exists $opts{password};
my $set_res = $backend->do_request("set_server_setting", {
key => "slave_$key",
value => join('|', $dsn, $username, $password),
}) or return undef;
return 1;
}
sub slave_delete {
my MogileFS::Admin $self = shift;
my $key = shift;
my $backend = $self->{backend};
my %slave_keys = $self->_get_slave_keys;
unless (exists $slave_keys{$key}) {
return 0;
}
my $res = $backend->do_request("set_server_setting", {
key => "slave_$key",
value => undef,
}) or return undef;
delete $slave_keys{$key};
$self->_set_slave_keys(%slave_keys);
return 1;
}
sub fsck_start {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("fsck_start", {});
}
sub fsck_stop {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("fsck_stop", {});
}
sub fsck_reset {
my MogileFS::Admin $self = shift;
my %opts = @_;
my $polonly = delete $opts{policy_only};
my $startpos = delete $opts{startpos};
Carp::croak("Unknown options: ". join(", ", keys %opts)) if %opts;
return $self->{backend}->do_request("fsck_reset", {
policy_only => $polonly,
startpos => $startpos,
});
}
sub fsck_clearlog {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("fsck_clearlog", {});
}
sub fsck_status {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("fsck_status", {});
}
sub fsck_log_rows {
my MogileFS::Admin $self = shift;
my %args = @_;
my $after = delete $args{after_logid};
die if %args;
my $ret = $self->{backend}->do_request("fsck_getlog", {
after_logid => $after,
});
my @ret;
for (my $i = 1; $i <= $ret->{row_count}; $i++) {
my $rec = {};
foreach my $k (qw(logid utime fid evcode devid)) {
$rec->{$k} = $ret->{"row_${i}_$k"};
}
push @ret, $rec;
}
return @ret;
}
sub set_server_setting {
my MogileFS::Admin $self = shift;
my ($key, $val) = @_;
my $res = $self->{backend}->do_request("set_server_setting", {
key => $key,
value => $val,
});
return 0 unless $res;
return 1;
}
sub server_settings {
my MogileFS::Admin $self = shift;
my ($key, $val) = @_;
my $res = $self->{backend}->do_request("server_settings", {});
return 0 unless $res;
my $ret = {};
for (my $i = 1; $i <= $res->{key_count}; $i++) {
$ret->{$res->{"key_$i"}} = $res->{"value_$i"};
}
return $ret;
}
sub rebalance_status {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("rebalance_status", {});
}
sub rebalance_start {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("rebalance_start", {});
}
sub rebalance_test {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("rebalance_test", {});
}
sub rebalance_stop {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("rebalance_stop", {});
}
sub rebalance_reset {
my MogileFS::Admin $self = shift;
return $self->{backend}->do_request("rebalance_reset", {});
}
sub rebalance_set_policy {
my MogileFS::Admin $self = shift;
my $policy = shift;
return $self->{backend}->do_request("rebalance_set_policy", {
policy => $policy,
});
}
################################################################################
# MogileFS::Admin class methods
#
sub _fail {
croak "MogileFS::Admin: $_[0]";
}
# FIXME: is this used?
sub _debug {
return 1 unless $MogileFS::DEBUG;
my $msg = shift;
my $ref = shift;
chomp $msg;
eval "use Data::Dumper;";
print STDERR "$msg\n" . Dumper($ref) . "\n";
return 1;
}
# modify a class within a domain
sub _mod_class {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my ($domain, $class, $args, $verb) = @_;
$verb ||= 'create';
my $res = $self->{backend}->do_request("${verb}_class", {
domain => $domain,
class => $class,
%$args,
});
return undef unless $res->{class} eq $class;
return 1;
}
# modify a host
sub _mod_host {
my MogileFS::Admin $self = shift;
return undef if $self->{readonly};
my ($host, $args, $verb) = @_;
$args ||= {};
$args->{host} = $host;
$verb ||= 'create';
my $res = $self->{backend}->do_request("${verb}_host", $args);
return undef unless $res->{host} eq $host;
return 1;
}
sub errstr {
my MogileFS::Admin $self = shift;
return undef unless $self->{backend};
return $self->{backend}->errstr;
}
sub errcode {
my MogileFS::Admin $self = shift;
return undef unless $self->{backend};
return $self->{backend}->errcode;
}
sub err {
my MogileFS::Admin $self = shift;
return undef unless $self->{backend};
return $self->{backend}->err;
}
1;