Skip to content
Permalink
6e569c0626
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 547 lines (418 sloc) 13.6 KB
#! /usr/local/system/perl/bin/perl
use Socket;
use Sys::Hostname;
use Getopt::Long;
use strict;
use warnings;
my $JBODMAP = '/etc/amd/amd.jbod';
my $MIRRORMAP = '/etc/mxmirrors';
my %CONFIG = ( CMD => 'STRING', );
my %opt = (
from => undef,
to => undef,
check => undef,
verbose => 0,
debug => 0,
);
my $fullhostname = hostname;
my ($hostname) = $fullhostname =~ /^(.*?)\./;
my $hostip = inet_ntoa( scalar gethostbyname($fullhostname) );
my $jbodmap = read_jbodmap($JBODMAP);
my $mirrors = read_mirrormap($MIRRORMAP);
my $result = GetOptions(
"from:s" => \$opt{from},
"to:s" => \$opt{to},
"destinationhosts" => \$opt{destinationhosts},
"sourcehosts" => \$opt{sourcehosts},
"all" => \$opt{all},
"format|fmt:s" => \$opt{format},
"execute" => \$opt{execute},
"command|cmd:s" => \$opt{command},
"check:s" => \$opt{check},
"verbose" => \$opt{verbose},
"debug" => \$opt{debug},
"help" => \$opt{help}
);
if ( $opt{help} ) {
print <<"EOF";
$0 [options] [action]
actions:
<default> print mirror-cmds to mirror local filesystems
--sourcehosts list all hosts to mirror
--destinationhosts list all mirror hosts
--all list all defined mirrors
--check check mirrorstatus of localhost
options:
--format=<fmt> define output format
--execute execute <fmt> if <cmd> is not specified
--command=<cmd> execute <cmd> for every host listed
--config=<file> read mirror-definitions from <file>
EOF
exit 0;
}
if ( defined $opt{destinationhosts} ) {
exit print_and_exec_hostlist( mirrors_reverse($mirrors) );
}
if ( defined $opt{sourcehosts} ) {
exit print_and_exec_hostlist($mirrors);
}
if ( defined $opt{all} ) {
exit print_and_exec_all($mirrors);
}
if ( defined $opt{check} ) {
my $host = $opt{check} || $hostname;
my $dest = mirrors_reverse($mirrors);
foreach ( @{ $dest->{$host} } ) {
printf "RULE: $_->{string}\n" if ( $opt{debug} );
print get_mirror_status($_);
printf " $_->{source}->{host}:$_->{source}->{hostpath} $_->{destination}->{host}:$_->{destination}->{hostpath} \t$_->{args}\n";
}
exit 0;
}
my $err = 0;
foreach ( @{ $mirrors->{$hostname} } ) {
my $default_fmt = $CONFIG{CMD};
my $fmt = ( defined $opt{format} ) ? $opt{format} : $default_fmt;
my $cmd = $opt{command};
if ( not defined $cmd and $opt{execute} ) {
$cmd = $fmt;
}
if ( not $fmt or ( defined $cmd and not $cmd ) ) {
print "\n";
print " default format: '$default_fmt'\n";
print " default command: <none>\n\n";
print " format/command variables:\n";
print " DSTHOST destination host\n";
print " DSTPATH destination path\n";
print " SRCHOST source host\n";
print " SRCPATH source path\n";
print " ARGS additional args\n";
print " DESTINATION DSTHOST==SRCHOST : DSTHOST:DSTPATH \n";
print " DSTHOST!=SRCHOST : DSTPATH \n\n";
exit 1;
}
print do_format( $fmt, $_ ) . "\n";
if ($cmd) {
my $command = do_format( $cmd, $_ );
if ( system($command) != 0 ) {
print STDERR "mxmirror FAILED CMD: $command\n";
$err++;
}
}
}
exit($err);
###################################################################
sub print_and_exec_all {
my $list = shift;
my $default_fmt = "SRCHOST:SRCPATH DSTHOST:DSTPATH ARGS";
my $fmt = ( defined $opt{format} ) ? $opt{format} : $default_fmt;
my $cmd = $opt{command};
if ( not defined $cmd and $opt{execute} ) {
$cmd = $fmt;
}
if ( not $fmt or ( defined $cmd and not $cmd ) ) {
print "\n";
print " default format: '$default_fmt'\n";
print " default command: <none>\n\n";
print " format/command variables:\n";
print " DSTHOST destination host\n";
print " DSTPATH destination path\n";
print " SRCHOST source host\n";
print " SRCPATH source path\n";
print " ARGS additional args\n";
print " DESTINATION DSTHOST==SRCHOST : DSTHOST:DSTPATH \n";
print " DSTHOST!=SRCHOST : DSTPATH \n\n";
return 1;
}
my @hosts = sort keys %{$list};
foreach my $h (@hosts) {
my %vars = ();
foreach ( @{ $list->{$h} } ) {
print do_format( $fmt, $_, \%vars ) . "\n" if ( $opt{format} or not $opt{command} );
if ($cmd) {
my $command = do_format( $cmd, $_, \%vars );
print qx($command);
}
}
}
return 0;
}
sub print_and_exec_hostlist {
my $list = shift;
my $default_fmt = "HOST";
my $fmt = ( defined $opt{format} ) ? $opt{format} : $default_fmt;
my $cmd = $opt{command};
if ( not defined $cmd and $opt{execute} ) {
$cmd = $fmt;
}
if ( not $fmt or ( defined $cmd and not $cmd ) ) {
print "\n";
print " default format: '$default_fmt'\n";
print " default command: <none>\n\n";
print " format/command variables:\n";
print " HOST destination\n\n";
return 1;
}
foreach ( sort keys %{$list} ) {
print do_format( $fmt, {}, { HOST => $_ } ) . "\n" if ( $opt{format} or not $opt{command} );
if ($cmd) {
my $command = do_format( $cmd, {}, { HOST => $_ } );
print qx($command);
}
}
return 0;
}
sub mirrors_reverse {
my $mirrors = shift;
my $r;
foreach my $h ( sort keys %{$mirrors} ) {
foreach ( @{ $mirrors->{$h} } ) {
push @{ $r->{ $_->{destination}->{host} } }, $_;
}
}
return $r;
}
sub get_mirror_status {
my $mirror = shift;
return 'REMOTE' unless ( $hostname eq $mirror->{destination}->{host} );
if ( open( F, '<', "$mirror->{destination}->{hostpath}/.PMIRROR_STATUS" ) ) {
my $line = <F>;
close F;
my ( $status, $timestamp ) = $line =~ /^(\S+) (\d+) /;
if ( $status eq 'OK' ) {
return sprintf( "%2.2f", ( time() - $timestamp ) / 24 / 60 / 60 );
}
return $line;
}
return 'UNKNOWN';
}
sub do_format {
my $fmt = shift;
my $data = shift;
my $vars = shift || {};
if ( defined $data ) {
my $dst
= ( $data->{source}->{host} eq $data->{destination}->{host} )
? $data->{destination}->{hostpath}
: "$data->{destination}->{host}:$data->{destination}->{hostpath}";
$fmt =~ s/DESTINATION/$dst/g;
$fmt =~ s/SRCHOST/$data->{source}->{host}/g;
$fmt =~ s/DSTHOST/$data->{destination}->{host}/g;
$fmt =~ s/SRCPATH/$data->{source}->{hostpath}/g;
$fmt =~ s/DSTPATH/$data->{destination}->{hostpath}/g;
$fmt =~ s/ARGS/$data->{args}/g;
$fmt =~ s/STRING/$data->{string}/g;
# $fmt =~ s/TIME/$data->{timeslot}->{time_str}/g;
}
foreach ( keys %{$vars} ) {
print "XXX" . $_;
$fmt =~ s/$_/$vars->{$_}/g;
}
return $fmt;
}
sub to_jbod_path {
my $host = shift;
my $jbod = shift;
my $path = shift;
#return "/amd/${host}/X/${jbod}${prepend}${path}";
return "/jbod/${jbod}${path}";
}
sub to_amd_path {
my $host = shift;
my $partition = shift;
my $path = shift;
return "/amd/${host}/${partition}${path}";
}
# from := <host1>:<p1><path1>
# <path1> must be absolute or empty
# <p1> or <path1> must be set
#
# isset(<p1>): /amd/<host1>/<p1><path1>
# isempty(<p1>): <path1>
# to := <host2>:<p2><path2>
# <path2> must be absolute or empty
# <p2> or <path2> must be set
#
# isemtpy(<path2>): <host2>:/amd/<host2>/<p2>/mirror/(<jbod1>|<host1>/<p1>)<path1>
# isset(<p2>): <host2>:/amd/<host2>/<p2><path2>
# isempty(<p2>): <host2>:<path2>
#
# from := <jbod1><path1>
# /jbod/<jbod1><path1>
# to := <jbod2><path2>
# isset(<path2>): /jbod/<jbod2><path2>
# isjbod(<from>): /jbod/<jbod2>/mirror/<jbod1><path1>
# ishost(<from>): /jbod/<jbod2>/mirror/<host1>/<p1><path1>
sub convert_to_sourcepath {
my $string = shift;
my ( $jbod, $path, $host, $hostpath, $partition );
if ( ( $jbod, $path ) = $string =~ /^([XCMD][\dA-Z]\d\d\d)(.*?)$/ ) {
unless ( defined $jbodmap->{$jbod} ) {
printf STDERR "**ERROR: unknown jbod: $string\n";
return undef;
}
$host = $jbodmap->{$jbod};
$hostpath = to_jbod_path( $host, $jbod, $path );
return (
{ host => $host,
jbod => $jbod,
path => $path,
hostpath => $hostpath
}
);
}
if ( ( $host, $partition, $path ) = $string =~ /^(.*?):(\d+)(.*?)$/ ) {
$hostpath = to_amd_path( $host, $partition, $path );
return (
{ host => $host,
partition => $partition,
path => $path,
hostpath => $hostpath
}
);
}
if ( ( $host, $path ) = $string =~ m(^(.*?):(/.+?)$) ) {
return (
{ host => $host,
path => $path,
hostpath => $path
}
);
}
return;
}
# from := <host1>:<p1><path1>
# <path1> must be absolute or empty
# <p1> or <path1> must be set
#
# isset(<p1>): /amd/<host1>/<p1><path1>
# isempty(<p1>): <path1>
#
# to := <host2>:<p2><path2>
# <path2> must be absolute or empty
# <p2> or <path2> must be set
#
# isemtpy(<path2>): <host2>:/amd/<host2>/<p2>/mirror/(<jbod1>|<host1>/<p1>)<path1>
# isset(<p2>): <host2>:/amd/<host2>/<p2><path2>
# isempty(<p2>): <host2>:<path2>
#
# from := <jbod1><path1>
# /jbod/<jbod1><path1>
#
# to := <jbod2><path2>
# isset(<path2>): /jbod/<jbod2><path2>
# isjbod(<from>): /jbod/<jbod2>/mirror/<jbod1><path1>
# ishost(<from>): /jbod/<jbod2>/mirror/<host1>/<p1><path1>
sub convert_to_destinationpath {
my $string = shift;
my $source = shift;
my ( $jbod, $path, $host, $hostpath, $partition );
if ( ( $jbod, $path ) = $string =~ /^([XCMD][\dA-Z]\d\d\d)(.*?)$/ ) {
unless ( defined $jbodmap->{$jbod} ) {
printf STDERR "**ERROR: unknown jbod: $string\n";
return undef;
}
$host = $jbodmap->{$jbod};
unless ($path) {
if ( defined $source->{jbod} ) {
$path = "/mirror/$source->{jbod}$source->{path}";
}
elsif ( defined $source->{partition} ) {
$path = "/mirror/$source->{host}/$source->{partition}$source->{path}";
}
else {
$path = "/mirror/$source->{host}$source->{path}";
}
}
$hostpath = to_jbod_path( $host, $jbod, $path );
return (
{ host => $host,
jbod => $jbod,
path => $path,
hostpath => $hostpath
}
);
}
# to := <host2>:<p2><path2>
# <path2> must be absolute or empty
# <p2> or <path2> must be set
#
# isemtpy(<path2>): <host2>:/amd/<host2>/<p2>/mirror/(<jbod1>|<host1>/<p1>)<path1>
# isset(<p2>): <host2>:/amd/<host2>/<p2><path2>
# isempty(<p2>): <host2>:<path2>
if ( ( $host, $partition, $path ) = $string =~ /^(.*?):(\d+)(.*?)$/ ) {
unless ($path) {
if ( defined $source->{jbod} ) {
$path = "/mirror/$source->{jbod}$source->{path}";
}
else {
$path = "/mirror/$source->{host}/$source->{partition}$source->{path}";
}
}
$hostpath = to_amd_path( $host, $partition, $path );
return (
{ host => $host,
partition => $partition,
path => $path,
hostpath => $hostpath
}
);
}
if ( ( $host, $path ) = $string =~ m(^(.*?):(/.+?)$) ) {
return (
{ host => $host,
path => $path,
hostpath => $path
}
);
}
return;
}
sub read_mirrormap {
my $file = shift;
my $map = {};
my $string;
my ( $from, $to, $args, $host, $frompath, $topath );
my ( $source, $destination );
open( F, '<', $file ) or die "can't open $file: $?";
foreach (<F>) {
s/^\s+//;
s/\s+$//;
s/\s+/ /;
next if ( $_ eq "" );
if (/^#\s*(\S+)\s*=\s*(.*)/) {
$CONFIG{$1} = $2;
}
next if (/^#/);
$string = $_;
if ( ( $from, $to, $args ) = /^(\S+) (\S+)\s*(.*)$/ ) {
$source = convert_to_sourcepath($from);
unless ($source) {
print STDERR "**ERROR: CAN'T PARSE FROM: $_\n";
next;
}
$destination = convert_to_destinationpath( $to, $source );
unless ($destination) {
print STDERR "**ERROR: CAN'T PARSE TO: $_\n";
next;
}
push @{ $map->{ $source->{host} } }, { string => $string, source => $source, destination => $destination, args => $args };
}
else {
print STDERR "**ERROR: CAN'T PARSE: $_\n";
}
}
close F;
return $map;
}
sub read_jbodmap {
my $file = shift;
open( F, '<', $file ) or die "can't open $file: $?";
my $map = {};
foreach (<F>) {
next unless (m(/amd/(.*?)/[XCMD]/([XCMD][\dA-Z]\d\d\d)));
$map->{$2} = $1;
}
close F;
return $map;
}