#!/usr/bin/perl -w ###################################################################### # # cxfsdump.pl # # Copyright (c) 2002, Javier Herrero. All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl. # # Javier Herrero. July 30, 2002 # (jherrero@cnio.es) # # Last revision: September 3, 2002 # ###################################################################### use strict; # PATH to several commands my $STACKER = "/usr/sbin/stacker"; my $CLUSTER_MGR = "/usr/cluster/bin/cmgr"; my $MT = "/usr/bin/mt"; # Hardware configuration my $JUKEBOX; my $TAPE_DRIVE; # Cluster configuration my $CLUSTER_NAME; my $NODE_NAME; # Backup configuration my $FILESYSTEMS; my $DEVICE_SETS; my $LAST_SET; # Other default values my $VERBOSE = 2; my $CONFIG_FILE = "/etc/cxfsdump.conf"; open(STDERR, ">&STDOUT"); &main(@ARGV); ###################################################################### ## MAIN ###################################################################### sub main { my (@ARGV) = @_; my ($run_string, $result); # Try to read the CONFIG_FILE from arguments map {$CONFIG_FILE = $1 if ($_ =~ /^\-f=?(.+)$/)} @ARGV; # Launch configuration tool and exits if requested map {&CONFIG::configure_system($CONFIG_FILE) if ($_ =~ /^config$/)} @ARGV; # Try to read the VERBOSITY LEVEL from arguments map {$VERBOSE = $1 if ($_ =~ /^\-v=?(\d+)$/)} @ARGV; if ($VERBOSE) { print "cxfsdump: reading configuration from $CONFIG_FILE.\n"; } &CONFIG::read_configuration_file($CONFIG_FILE); # Get the number of configured sets my $number_of_sets = scalar(@{$DEVICE_SETS}); if ($VERBOSE) { print "cxfsdump: Jukebox ($JUKEBOX).\n"; print "cxfsdump: Tape drive ($TAPE_DRIVE).\n"; print "cxfsdump: Cluster ($CLUSTER_NAME).\n"; print "cxfsdump: Node ($NODE_NAME).\n"; print "cxfsdump: Filesystems (", join(", ", @{$FILESYSTEMS}), ")\n"; print "cxfsdump: $number_of_sets sets available.\n"; } # Get the selected set if (defined($LAST_SET)) { $LAST_SET ++; } else { $LAST_SET = 1; } my $selected_set = $LAST_SET; $selected_set = 1 if ($selected_set > $number_of_sets); # Try to read the selected set from the arguments map {$selected_set = $1 if ($_ =~ /^\-set=?(\d+)$/)} @ARGV; if ($VERBOSE) { print "cxfsdump: set $selected_set selected.\n"; } $LAST_SET = $selected_set; # Get selected set if it is available (or configured) if ($selected_set > $number_of_sets) { print "cxfsdump: ERROR (set $selected_set selected and only", " $number_of_sets sets available).\n"; exit(1); } my @set_content = @{$DEVICE_SETS->[$selected_set-1]}; if ($VERBOSE) { print "cxfsdump: set $selected_set include slots ", join(", ", @set_content), ".\n"; } &dump_set($selected_set, @set_content); &CONFIG::save_configuration_file($CONFIG_FILE); } ###################################################################### ## DUMP SET ###################################################################### sub dump_set { my ($selected_set, @set_content) = @_; my ($run_string, $result); my $current_set_number = 0; my $current_fs_number = 0; &load_tape_into_device($set_content[$current_set_number]); my $starting_media = 1; foreach my $fs (@{$FILESYSTEMS}) { $result = &relocate_metadataserver_to_current_node($fs); next if (!$result); # Skip dump on error my $current_tape = 0; my $dump_finished = 0; $run_string = &get_run_string_for_xfsdump($fs, $selected_set, $current_tape, $set_content[$current_set_number], $starting_media, 0); while (!$dump_finished) { if ($VERBOSE) { print "cxfsdump: exec ($run_string).\n"; } $result = qx"$run_string"; $starting_media = 0; $result =~ /xfsdump\: Dump Status\: (\w+)/; my $code = ($1 || ' -no code- '); if ($VERBOSE > 1) { $result =~ s/\n(.)/\n $1/g; print " ".$result; } elsif ($VERBOSE) { my @lines = split(/[\r\n]+/, $result); map {print " ".$_ if ($_ =~ /WARNING/)} @lines; } if ($code eq "SUCCESS") { if ($VERBOSE) { print "cxfsdump: successful dump of $fs.\n"; } $dump_finished = 1; } elsif ($code eq "INTERRUPT") { if ($VERBOSE) { print "cxfsdump: dump of $fs will span to next tape.\n"; } # Check for next tape if ($current_set_number >= (scalar(@set_content) - 1)) { print "cxfsdump: ERROR (No more tapes for this set).\n"; &unload_tape_from_device($set_content[$current_set_number]); exit(2); } #Change media &unload_tape_from_device($set_content[$current_set_number]); $current_set_number++; &load_tape_into_device($set_content[$current_set_number]); $starting_media = 1; $current_tape ++; # Get run string for resuming dump $run_string = &get_run_string_for_xfsdump($fs, $selected_set, $current_tape, $set_content[$current_set_number], $starting_media, 1); } else { if ($VERBOSE <= 1) { $result =~ s/\n(.)/\n $1/g; print " ".$result; } print "cxfsdump: ERROR (dump exited with code $code).\n"; &unload_tape_from_device($set_content[$current_set_number]); exit(2); } } } &unload_tape_from_device($set_content[$current_set_number]); } ###################################################################### ## RELOCATE METADATASERVER TO CURRENT NODE ###################################################################### sub relocate_metadataserver_to_current_node { my ($fs) = @_; my ($run_string, $result); # Getting name of the filesystem my $fs_name = $fs; $fs_name =~ s/[^\/]*\///g; # Checking the filesystem exists in the cluster $run_string = "$CLUSTER_MGR -c \"show cxfs_filesystems in cluster". " $CLUSTER_NAME\""; if ($VERBOSE) { print "cxfsdump: exec ($run_string).\n"; } $result = qx"$run_string"; if ($result !~ /\b$fs_name\b/) { print "cxfsdump: WARNING (filesystem $fs_name is not a CXFS filesytem". " in cluster $CLUSTER_NAME).\n"; # Trying to find filesystem in /etc/fstab $run_string = "fgrep '$fs' /etc/fstab"; if ($VERBOSE) { print "cxfsdump: exec ($run_string).\n"; } $result = qx"$run_string"; if ($result) { print "cxfsdump: PASS (filesystem $fs is present in /etc/fstab).\n"; return 2; } else { print "cxfsdump: WARNING (filesystem $fs not found /etc/fstab).\n"; print "cxfsdump: ERROR (Aborting dump of $fs).\n"; return 0; } } # Relocating the metadataserver of the filesystem to the current node $run_string = "$CLUSTER_MGR -c \"admin cxfs_relocate cxfs_filesystem". " $fs_name to node $NODE_NAME in cluster $CLUSTER_NAME\""; if ($VERBOSE) { print "cxfsdump: exec ($run_string).\n"; } $result = qx"$run_string"; if ($result !~ /cxfs_relocate operation successful/) { print "$result\n"; return 0; } return 1; } ###################################################################### ## GET RUN STRING FOR XFSDUMP ###################################################################### sub get_run_string_for_xfsdump { my ($fs, $set, $current_tape, $current_slot, $starting_media, $resume_dump) = @_; my $session_label = $fs; $session_label =~ s/\//_/g; $session_label =~ s/^_+//; $session_label .= ".$set.R$current_tape"; my $run_string = "xfsdump -f $TAPE_DRIVE -l 0"; $run_string .= " -R" if ($resume_dump); $run_string .= " -o" if ($starting_media); $run_string .= " -F -L $session_label -M tape$current_slot $fs"; return $run_string; } ###################################################################### ## LOAD TAPE INTO DEVICE ###################################################################### sub load_tape_into_device { my ($slot) = @_; # Load tape into device my $run_string = "$STACKER -l $slot $JUKEBOX"; if ($VERBOSE) { print "cxfsdump: exec ($run_string)\n"; } my $result = qx"$run_string"; if ($result ne "") { print "cxfsdump: ERROR (cannot load tape: $result).\n"; exit(2); } sleep(30); } ###################################################################### ## UNLOAD TAPE FROM DEVICE ###################################################################### sub unload_tape_from_device { my ($slot) = @_; my ($run_string, $result); sleep(30); # Rewind tape $run_string = "$MT -f $TAPE_DRIVE rewind"; if ($VERBOSE) { print "cxfsdump: exec ($run_string)\n"; } $result = qx"$run_string"; if ($result ne "") { print "cxfsdump: ERROR (cannot rewind tape: $result).\n"; exit(2); } # Eject tape from device sleep(30); $run_string = "$MT -f $TAPE_DRIVE unload"; if ($VERBOSE) { print "cxfsdump: exec ($run_string)\n"; } $result = qx"$run_string"; if ($result ne "") { print "cxfsdump: ERROR (cannot eject tape: $result).\n"; exit(2); } # Unload tape from device sleep(30); $run_string = "$STACKER -u $slot $JUKEBOX"; if ($VERBOSE) { print "cxfsdump: exec ($run_string)\n"; } $result = qx"$run_string"; if ($result ne "") { print "cxfsdump: ERROR (cannot unload tape: $result).\n"; exit(2); } } ###################################################################### ###################################################################### ###################################################################### ###################################################################### package CONFIG; ###################################################################### ## SAVE CONFIGURATION FILE ###################################################################### sub save_configuration_file { my ($config_file) = @_; &write_to_config_file($config_file, "verbose", $VERBOSE); &write_to_config_file($config_file, "jukebox", $JUKEBOX); &write_to_config_file($config_file, "tape_drive", $TAPE_DRIVE); &write_to_config_file($config_file, "cluster", $CLUSTER_NAME); &write_to_config_file($config_file, "node", $NODE_NAME); &write_to_config_file($config_file, "node", $NODE_NAME); &write_to_config_file($config_file, "filesystems", join("+", @{$FILESYSTEMS})); my $set_string = ""; foreach my $set_arr (@{$DEVICE_SETS}) { $set_string .= "[".join(" ", @$set_arr)."]+"; } $set_string =~ s/\+$//; &write_to_config_file($config_file, "sets", $set_string); &write_to_config_file($config_file, "last_set", $LAST_SET); } ###################################################################### ## READ CONFIGURATION FILE ###################################################################### sub read_configuration_file { my ($config_file) = @_; # Hardware configuration $JUKEBOX = &try_to_read_from_config_file($config_file, "jukebox"); $JUKEBOX = &configure_jukebox() if (!$JUKEBOX); $TAPE_DRIVE = &try_to_read_from_config_file($config_file, "tape_drive"); $TAPE_DRIVE = &configure_tapedrive() if (!$TAPE_DRIVE); # Cluster configuration $CLUSTER_NAME = &try_to_read_from_config_file($config_file, "cluster"); $CLUSTER_NAME = &configure_cluster() if (!$CLUSTER_NAME); $NODE_NAME = &try_to_read_from_config_file($config_file, "node"); $NODE_NAME = &configure_node($CLUSTER_NAME) if (!$NODE_NAME); # Backup configuration my $filesystems_resp = &try_to_read_from_config_file($config_file, "filesystems"); $filesystems_resp = &configure_filesystems($CLUSTER_NAME) if (!$filesystems_resp); $FILESYSTEMS = [split(/\s*\+\s*/, $filesystems_resp)]; my $set_resp = &try_to_read_from_config_file($config_file, "sets"); $set_resp = &configure_sets($JUKEBOX) if (!$set_resp); my @sets = split(/\s*\+\s*/, $set_resp); foreach my $set (@sets) { $set =~ s/[\[\]]//g; push(@{$DEVICE_SETS}, [split(/ +/, $set)]); } $LAST_SET = &try_to_read_from_config_file($config_file, "last_set"); } ###################################################################### ## TRY TO READ FROM CONFIG FILE ###################################################################### sub try_to_read_from_config_file { my ($config_file, $param) = @_; my $return = 0; open(CONFIG, "$config_file") || return 0; my @lines = ; map {$return = $1 if ($_ =~ /$param\s*=\s*(.+)$/i)} @lines; close(CONFIG); if ($return) { $return =~ s/^\s+//; $return =~ s/\s+$//; } return $return; } ###################################################################### ## WRITE TO CONFIG FILE ###################################################################### sub write_to_config_file { my ($config_file, $param, @values) = @_; my @lines; return if (!@values); if (open(CONFIG, "$config_file")) { @lines = ; close(CONFIG); } map {$_ = "" if ($_ =~ /$param=\S+/i)} @lines; push(@lines, "$param=".join("+", @values)."\n"); if (open(CONFIG, ">$config_file")) { print CONFIG @lines; close(CONFIG); chmod 0600, $config_file; } else { print STDERR "cxfsdump: WARNING (Cannot write config file)\n"; } } ###################################################################### ## CONFIGURE SYSTEM ## ## Things to be configurated: ## ## - jukebox device ($JUKEBOX) ## - tape device ($TAPE_DRIVE) ## - cluster name ($CLUSTER_NAME) ## - node name ($NODE_NAME) ## - cxfs filesytems ($FILESYSTEMS) ## - tape sets ($DEVICE_SETS) ## - default verbose level ($VERBOSE) ## ###################################################################### sub configure_system { my ($config_file) = @_; my $resp; print "Writting configuration to $config_file...\n"; # Configure Jukebox device ($JUKEBOX) my $jukebox_device = &configure_jukebox($config_file); # Configure TAPE device ($TAPE_DRIVE) my $tape_device = &configure_tapedrive($config_file); # Configure cluster name my $cluster = &configure_cluster($config_file); # Configure node name my $node = &configure_node($cluster, $config_file); # Configure cxfs_filesystems my @filesystems = &configure_filesystems($cluster, $config_file); # Configure sets of slots &configure_sets($jukebox_device, $config_file); exit(0); } ###################################################################### ## CONFIGURE SETS ###################################################################### sub configure_sets { my ($jukebox_device, $config_file) = @_; my $resp; $resp = qx{$STACKER -c $jukebox_device}; # Auto-conf mode if (!$config_file) { if ($resp =~ /(\d+) slot/) { my $number = $1; if ($number % 2 == 1) { $number--; } return "[".join(" ", (1 .. ($number/2)))."]+[". join(" ", ((1+$number/2) .. $number))."]"; } else { return undef; } } if ($resp =~ /(\d+) slot/) { my $slots = $1; print "Your jukebox has $slots available slots.\n"; print "Enter the number of sets you want to configure: "; $resp = ; if ($resp =~ /(\d+)/) { my $number = $1; $slots -= ($slots % $number); my @sets; my $first = 1; my $last = ($slots / $number); for (my $a=0; $a<$number; $a++) { push(@sets, "[". join(" ", ($first .. $last)). "]"); $first += ($slots / $number); $last += ($slots / $number); } &write_to_config_file($config_file, "sets", @sets); } } else { print "ERROR: I can't find any jukebox in $jukebox_device\n"; } } ###################################################################### ## CONFIGURE FILESYSTEMS ###################################################################### sub configure_filesystems { my ($cluster, $config_file) = @_; my $resp; $resp = qx{$CLUSTER_MGR -c "show cxfs_filesystems in cluster $cluster"}; $resp =~ s/^\s+//; $resp =~ s/\s+$//; my @filesystems = split(/\s+/, $resp); my @filesystem_devices; foreach my $filesystem (@filesystems) { push(@filesystem_devices, "/dev/cxvm/$filesystem") if (-e "/dev/cxvm/$filesystem"); } # Auto-conf mode if (!$config_file) { if (@filesystem_devices) { return join("+", @filesystem_devices); } else { return undef; } } if (@filesystem_devices) { &write_to_config_file($config_file, "filesystems", @filesystem_devices); print "FILESYSTEMS found:\n - ", join ("\n - ", @filesystem_devices), "\n"; return @filesystems; } else { print "ERROR: I can't find any cxfs_filesystem in cluster $cluster\n"; return undef; } } ###################################################################### ## CONFIGURE NODE ###################################################################### sub configure_node { my ($cluster, $config_file) = @_; my $resp; my $node; my $hostname = qx{hostname}; $resp = qx{$CLUSTER_MGR -c "show nodes in cluster $cluster"}; $resp =~ s/^\s*Cluster \S+ has following \d+ machine\(s\)\s+//; my @machines = split(/\s+/, $resp); map {$node = $_ if ($hostname =~ /$_/)} @machines; # Auto-conf mode if (!$config_file) { if ($node) { return $node; } else { return undef; } } if ($node) { &write_to_config_file($config_file, "node", $node); print "NODE found: $node\n"; } elsif (!@machines) { print "ERROR: I can't find any node in cluster $cluster\n"; } else { print "Here is the list of available nodes:\n"; print " - ", join("\n - ", @machines), "\n"; print "Enter this node name :"; $resp = ; if ($resp =~ /(\S+)/) { $node = $1; &write_to_config_file($config_file, "node", $node); } } } ###################################################################### ## CONFIGURE CLUSTER ###################################################################### sub configure_cluster { my ($config_file) = @_; my $resp; $resp = qx{$CLUSTER_MGR -c "show clusters"}; # Auto-conf mode if (!$config_file) { if ($resp =~ /\s1 Cluster\(s\) defined\s+(\S+)/) { return $1; } else { return undef; } } my $cluster; if ($resp =~ /\s1 Cluster\(s\) defined\s+(\S+)/) { $cluster = $1; &write_to_config_file($config_file, "cluster", $1); print "CLUSTER found: $cluster\n"; } else { print "Here is the list of available clusters:\n"; $resp =~ s/^\s*\d+ Cluster\(s\) defined\s+//; print " - ", join("\n - ", split(/\s+/, $resp)), "\n"; print "Enter cluster name :"; $resp = ; if ($resp =~ /(\S+)/) { $cluster = $1; &write_to_config_file($config_file, "cluster", $cluster); } } return $cluster; } ###################################################################### ## CONFIGURE TAPEDRIVE ###################################################################### sub configure_tapedrive { my ($config_file) = @_; my $resp; # Auto-conf mode if (!$config_file) { if (-e "/dev/nrtape") { return "/dev/nrtape"; } else { return undef; } } my $tape_device = ""; if (-e "/dev/nrtape") { $tape_device = "/dev/nrtape"; print "TAPE DRIVE found: $tape_device\n"; } else { my @tapes = &get_rmt_devices(); if (@tapes) { print "Here is the list of available tape devices:\n"; foreach my $tape (@tapes) { if ($tape =~ /tps(\d)d(\d)nr$/) { print " Tape drive: unit $2 on SCSI controller $1 [$tape].\n"; } elsif ($tape =~ /tps(\d)d(\d)nrc$/) { print " Tape drive: unit $2 on SCSI controller $1", " (with data compression) [$tape].\n"; } } } else { print "I can't find any tape device in .\n"; } print "Enter tape device"; print " [$tape_device]" if ($tape_device); print " :"; $resp = ; $tape_device = $1 if ($resp =~ /(\S+)/); } &write_to_config_file($config_file, "tape_drive", $tape_device); return $tape_device; } ###################################################################### ## CONFIGURE JUKEBOX ###################################################################### sub configure_jukebox { my ($config_file) = @_; my $resp; my $hinv = qx"hinv"; my @jukeboxes; foreach my $line (split(/[\r\n]+/, $hinv)) { push(@jukeboxes, $line) if ($line =~ /Jukebox:/i); } # Auto-conf mode if (!$config_file) { if (scalar(@jukeboxes) == 1) { return &get_scsi_device_from_hinv_description($jukeboxes[0]); } else { return undef; } } my $jukebox_device = ""; if (!@jukeboxes) { print "I can't find any jukebox device in you hardware inventory.\n"; } elsif (scalar(@jukeboxes) > 1) { print "I found several jukebox devices in you hardware inventory.\n"; print "Here is the list of available tape robots:\n"; foreach my $jukebox (@jukeboxes) { $jukebox_device = &get_scsi_device_from_hinv_description($jukebox); print " $jukebox [$jukebox_device]\n"; } $jukebox_device = ""; } else { $jukebox_device = &get_scsi_device_from_hinv_description($jukeboxes[0]); print "JUKEBOX found: $jukebox_device\n"; } if (!$jukebox_device) { print "Enter manually the device node for controlling the robot.\n"; print "Enter jukebox (tape robot) device: "; $resp = ; $jukebox_device = $1 if ($resp =~ /(\S+)/); } &write_to_config_file($config_file, "jukebox", $jukebox_device); return $jukebox_device; } ###################################################################### ## GET SCSI DEVICE FROM HINV DESCRIPTION ###################################################################### sub get_scsi_device_from_hinv_description { my ($hinv_description) = @_; my ($unit, $lun, $scsi); $unit='?'; $lun='?'; $scsi='?'; $unit = $1 if ($hinv_description =~ /unit (\d)/i); $lun = $1 if ($hinv_description =~ /lun (\d)/i); $scsi = $1 if ($hinv_description =~ /scsi controller (\d)/i); my $dev = qx{ls /dev/scsi/sc${scsi}d${unit}l${lun}}; $dev =~ s/\s//g; return $dev; } ###################################################################### ## GET RMT DEVICES ###################################################################### sub get_rmt_devices { my @dev; my $resp = qx{ls /dev/rmt/tps?d?nr}; @dev = split(/\s+/, $resp); $resp = qx{ls /dev/rmt/tps?d?nrc}; push(@dev, split(/\s+/, $resp)); return sort @dev; } =pod =head1 NAME cxfsdump - Dump CXFS filesystems using a jukebox (tape robot) =head1 SYNOPSYS B [B<-f=>CONFIG_FILE] [B<-set=>BACKUP_SET] [B<-v=>VERBOSE] B [B<-f=>CONFIG_FILE] B =head1 PREREQUISITE This script requires C, C, C and C programs. It also needs a jukebox and a tape drive attached to the system. The configuration module uses the C command. =head1 DESCRIPTION This script does all the job of loading tapes with the stacker (the robotics control program), relocating the metadataserver for the CXFS filesytems, launching the xfsdump utility, rewinding and ejecting tape and unloading it with the stacker again. If a xfsdump needs more than 1 tape, it will change the tape and span the dump to the next tape. The script is intended for CXFS filesystems but it is also able to dump local filesytems. Several sets of tapes can be configured. Each one will be used successively for dumping all the configured filesystems. Current implementation does not support incremental backups. =head1 COMMANDS =over 4 =item B Use this command the first time you use this program. It will try to find all the needed information. You can combine this command with the -f option (see below) for saving the configuration in a specific file. =item BdefaultE> If no command is specified, the script launch the backup. It will use next set of tapes, according to the configuration file (see below). If no configuration file is found or some information is missing, the script will try to configure itself automatically (see AUTO-CONFIGURATION below). =back =head1 OPTIONS =over 4 =item B<-f=CONFIG_FILE> Use CONFIG_FILE instead of F. =item B<-set=SET_NUMBER> You can configure several sets of tapes for backups. This flag allows you to specify the set you want to use. By default, next set of tapes is used. This information is stored in the CONFIG_FILE. =item B<-v=VERBOSE> Set the verbosity level to VERBOSE. Possible values are 0, 1 or 2. =back =head1 CONFIGURATION FILE FORMAT Every line contains the name of a parameter and its value separated by an equal symbol. Here is the list of available parameters: =over 4 =item B The name of the jukebox device. (/dev/scsi/sc1d1l0) =item B The name of the tape device. (/dev/nrtape or /dev/rmt/tps1d2nrc) =item B The name of the CXFS cluster. You can see the list of available clusters using C =item B The name of this node in the CXFS cluster. You can see the list of available nodes using C"> =item B The name of CXFS filesystems you want to backup. You can see the list of available CXFS filesystems using C">, but you must provide the full path to the filesystem. The filesystems must be separated a C<+>. =item B This is the most complicated parameter. Your jukebox (tape robot) should have several slots for tapes. This parameter will tell the program which tape you want to use for every backup. Each set is a list of slot numbers separated by white spaces and enclosed by square brackets. The sets are separated by a C<+>. For example for 10 slots divided into 2 sets of tapes: sets=[1 2 3 4 5]+[6 7 8 9 10] For example for 9 slots divided into 3 sets of tapes: sets=[1 2 3]+[4 5 6]+[7 8 9] =item B The verbosity level. Available values are 0, 1 or 2. =item B The last set dumped. This values will be used to dump next set next time. =back =head1 CONFIGURATION FILE EXAMPLE jukebox=/dev/scsi/sc1d1l0 tape_drive=/dev/nrtape cluster=my_cluster node=my_cxfs_server filesystems=/dev/cxvm/my_raid0+/dev/cxvm/my_raid1+/dev/cxvm/my_raid2 sets=[1 2 3]+[4 5 6]+[7 8 9] =head1 AUTO-CONFIGURATION If your hardware configuration is simple enough you can even skip the configuration step: if you have only one jukebox, only one tape drive, only one cluster defined and the name of the backup host (this host) in the cluster configuration is the same as its hostname, the script will use all of this and dump all available CXFS filesystems. By default it will split available slots into two sets and dump files to the first one. =head1 COMPLEX HARDWARE CONFIGURATIONS C is intended for simple configuration, i.e., 1 jukebox with 1 tape device for dumping all the CXFS filesystems of the unique cluster available, but you can easily fit your hardware needings by using several configuration files, one for each replicated device. =head1 LAUNCH CXFSDUMP FROM CRON Since the last used set number is saved in the configuration file, you can use the same command line for all the sets. This feature allows to run the dump, let say weekly from cron: This will run cxfsdump (you maybe need to prepend the complete path) every Saturday. If you configured several sets of tapes, it will use a different set every week. 15 20 * * 6 cxfsdump If you have got two tape device in your jukebox and made two configuration files for dumping different CXFS filesystems with each tape: 15 20 * * 6 cxfsdump -f /etc/cxfsdump_tape1.conf 15 20 * * 6 cxfsdump -f /etc/cxfsdump_tape2.conf =head1 KNOWN BUGS C must be executed from the metadataserver of the CXFS filesystem to be dumped. C does no show which node is the present metadataserver, so this scripts tries always to relocate it to the present node. If the present node is the metadaserver already you will get a message like C is the server already> in the SYSLOG file. You can ignore it. Incremental backups are not implemented yet. I did not fully checked the configuration module. I am sorry but I cannot test all the possible hardware configurations. I hope it will be OK in your case. Drop me an email if something goes wrong... =head1 FILES F F F F =head1 AUTHOR Javier Herrero Ejherrero@cnio.esE =head1 WARRANTY Just forget it. This script comes with absolutely no warranty. You can use it if you like it and find it useful but do not blame me if something goes wrong. In any case I recommend you to use the maximum verbosity level at the beginning and check that everything is OK. =head1 SEE ALSO L, L, L, L. L, L. =head1 COPYRIGHT AND LICENSE Copyright (c) 2002, Javier Herrero. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl. =begin comment =head1 README This script is intended for dumping CXFS filesystems using a jukebox (tape robot) It does all the job of loading tapes with the stacker (the robotics control program), relocating the metadataserver for the CXFS filesytems, launching the xfsdump utility, rewinding and ejecting tape and unloading it with the stacker again. If a xfsdump needs more than 1 tape, it is able to change the tape and span the dump to the next tape. The script is intended for CXFS filesystems but it is also able to dump local filesytems. Several sets of tapes can be configured. Each one will be used successively for dumping all the configured filesystems. Current implementation does not support incremental backups. =pod OSNAMES Irix =pod SCRIPT CATEGORIES UNIX/System_administration =cut