Permalink
Browse files

Shuffle Network abstraction and multiple network replication policies…

… into a 'server-plugin' even though this isn't technically correct.

git-svn-id: http://code.sixapart.com/svn/mogilefs/trunk/server-plugins/MogileFS-Network@1162 f67b2e87-0811-0410-a7e0-dd94e48410d6
  • Loading branch information...
0 parents commit db5fb56481faf3d27a211976fbf4d2a37f7b7ad9 hachi committed Apr 3, 2008
@@ -0,0 +1,65 @@
+package MogileFS::Network;
+
+use strict;
+use warnings;
+
+use Net::Netmask;
+use MogileFS::Config;
+
+my %cache; # '192.168.0.0/24' => Net::Netmask->new2('192.168.0.0/24');
+my $age; # increments everytime we look
+
+sub zone_for_ip {
+ my $class = shift;
+ my $ip = shift;
+
+ return unless $ip;
+
+ # clear the cache occasionally
+ if ((!defined $age) or ($age == 0) or ($age++ > 500)) {
+ clear_and_build_cache();
+ $age = 1;
+ }
+
+ foreach my $zone (keys %cache) {
+ if ($cache{$zone}->match($ip)) {
+ return $zone;
+ }
+ }
+ return;
+}
+
+sub clear_and_build_cache {
+ undef %cache;
+
+ my @zones = split(/\s*,\s*/,MogileFS::Config->server_setting("network_zones"));
+
+ foreach my $zone (@zones) {
+ my $netmask = MogileFS::Config->server_setting("zone_$zone");
+
+ if (not $netmask) {
+ warn "couldn't find network_zone <<zone_$zone>> check your server settings";
+ next;
+ }
+
+ if ($cache{$zone}) {
+ warn "duplicate netmask <$netmask> in network zones. check your server settings";
+ }
+
+ $cache{$zone} = Net::Netmask->new2($netmask);
+
+ if (Net::Netmask::errstr()) {
+ warn "couldn't parse <$zone> as a netmask. error was <" . Net::Netmask::errstr().
+ ">. check your server settings";
+ }
+ }
+}
+
+sub stuff_cache { # for testing, or it'll try the db
+ my ($self, $ip, $netmask) = @_;
+
+ $cache{$ip} = $netmask;
+ $age = 1;
+}
+
+1;
@@ -0,0 +1,157 @@
+package MogileFS::ReplicationPolicy::HostsPerNetwork;
+
+use strict;
+use base 'MogileFS::ReplicationPolicy';
+
+use MogileFS::Network;
+use MogileFS::Util qw(weighted_list);
+use MogileFS::ReplicationRequest qw(ALL_GOOD TOO_GOOD TEMP_NO_ANSWER);
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+
+ my $self = bless {}, $class;
+
+ $self->{hosts_per_zone} = delete $args{hosts_per_zone}
+ if $args{hosts_per_zone};
+
+ return $self;
+}
+
+sub new_from_policy_args {
+ my ($class, $argref) = @_;
+ # Note: "MultipleNetworks()" is okay, in which case the 'mindevcount'
+ # on the class is used. (see below)
+ $$argref =~ s/^\s* \( \s* ( [^)]*?) \s* \) \s*//x
+ or die "$class failed to parse args: $$argref";
+
+ my @args = split /\s*,\s*/, $1;
+ my %hosts_per_zone;
+
+ foreach my $arg (@args) {
+ my ($zone, $count) = split /\s*=\s*/, $arg;
+ $hosts_per_zone{$zone} = $count;
+ }
+
+ return $class->new(hosts_per_zone => \%hosts_per_zone);
+}
+
+sub replicate_to {
+ my ($self, %args) = @_;
+
+ my $hosts_per_zone = $self->{hosts_per_zone};
+
+ my $fid = delete $args{fid}; # fid scalar to copy
+ my $on_devs = delete $args{on_devs}; # arrayref of device objects
+ my $all_devs = delete $args{all_devs}; # hashref of { devid => MogileFS::Device }
+ my $failed = delete $args{failed}; # hashref of { devid => 1 } of failed attempts this round
+
+ delete $args{min}; # We don't use this.
+
+ warn "Unknown parameters: " . join(", ", sort keys %args) if %args;
+ die "Missing parameters" unless $on_devs && $all_devs && $failed && $fid;
+
+ # see which and how many unique hosts/networks we're already on.
+ my %on_dev;
+ my %on_host;
+
+ my %on_host_per_zone;
+ my %on_dev_per_zone;
+
+ foreach my $dev (@$on_devs) {
+ my $on_ip = $dev->host->ip;
+ my $hostid = $dev->host->id;
+
+ if ($on_ip) {
+ my $zone = MogileFS::Network->zone_for_ip($on_ip);
+
+ $on_dev_per_zone{$zone}++;
+
+ # If we've already counted this host, then don't increment it for this zone
+ $on_host_per_zone{$zone}++ unless $on_host{$dev->hostid};
+ }
+
+ $on_dev{$dev->id}++;
+ $on_host{$dev->hostid}++;
+ }
+
+ my %available_hosts_per_zone;
+ my %available_hosts;
+
+ foreach my $dev (values %$all_devs) {
+ next unless $dev->dstate->should_have_files;
+ my $ip = $dev->host->ip;
+ my $hostid = $dev->host->id;
+ my $zone = MogileFS::Network->zone_for_ip($ip);
+ $available_hosts_per_zone{$zone}++ unless $available_hosts{$hostid};
+ $available_hosts{$hostid}++;
+ }
+
+ my %needed_network;
+ my $too_good = 0;
+
+ while (my ($zone, $needed) = each %$hosts_per_zone) {
+ # If we already on all hosts in the target zone, and we're still not happy, then
+ # we need to start doubling up on devices, but now devs is not to exceed the requested
+ # number of hosts.
+ my $on = ($needed <= $available_hosts_per_zone{$zone}) ? $on_host_per_zone{$zone} : $on_dev_per_zone{$zone};
+ $on ||= 0;
+
+ if ($on < $needed) {
+ $needed_network{$zone} = 1;
+ } elsif ($on_dev_per_zone{$zone} > $needed) {
+ $too_good++;
+ }
+ }
+
+ unless (keys %needed_network) {
+ return TOO_GOOD if $too_good;
+ return ALL_GOOD;
+ }
+
+ my @all_dests = weighted_list map {
+ [$_, 100 * $_->percent_free]
+ } grep {
+ ! $on_dev{$_->devid} &&
+ ! $failed->{$_->devid} &&
+ $_->should_get_replicated_files
+ } MogileFS::Device->devices;
+
+ return TEMP_NO_ANSWER unless @all_dests;
+
+ my @ideal;
+ my @desp;
+
+ foreach my $dev (@all_dests) {
+ my $ip = $dev->host->ip;
+ my $host_id = $dev->host->id;
+ my $zone = MogileFS::Network->zone_for_ip($ip);
+
+ # If we don't need more devices in this current network
+ # zone, then don't include the current device.
+ next unless $needed_network{$zone};
+
+ if ($on_host{$host_id}) {
+ push @desp, $dev;
+ } else {
+ push @ideal, $dev;
+ }
+ }
+
+ return TEMP_NO_ANSWER unless @desp or @ideal;
+
+ return MogileFS::ReplicationRequest->new(
+ ideal => \@ideal,
+ desperate => \@desp,
+ );
+}
+
+1;
+
+# Local Variables:
+# mode: perl
+# c-basic-indent: 4
+# indent-tabs-mode: nil
+# End:
+# vim: filetype=perl softtabstop=4 expandtab
Oops, something went wrong.

0 comments on commit db5fb56

Please sign in to comment.