Editing
2 Node Cluster: Dual Primary DRBD + CLVM + KVM + Live Migrations
(section)
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==== DRBD backing Device ==== * you will have to edit some variables for this to work properly * It will also set DRBD and others in unmanaged mode, so pacemaker will not potential fence on failures * This is a heavily modified version of ''http://repo.firewall-services.com/misc/virt/virt-backup.pl'' (other options like cleanup do not work) ;Usage: ./virt-backup-drbd_backdevice.pl vm=<virt_name> [--compress] ; virt-backup-drbd_backdevice.pl <pre> #!/usr/bin/perl -w # vm == drbd use XML::Simple; use Sys::Virt; use Getopt::Long; # Set umask umask(022); # Some constant my $drbd_dir = '/etc/drbd.d/'; our %opts = (); our @vms = (); our @excludes = (); our @disks = (); our $drbd_dev; my $migrate_to = 'bigeye'; ## host to migrate machines to if they are running locally my $migrate_from = 'blindpig'; ## ht # Sets some defaults values my $host =`hostname`; chomp($host); my $migration = 0; #placeholder # What to run. The default action is to dump $opts{action} = 'dump'; # Where backups will be stored. This directory must already exists $opts{backupdir} = '/NFS/_local_/_backups/DRBD/'; # Size of LVM snapshots (which will be used to backup VM with minimum downtown # if the VM store data directly on a LV) $opts{snapsize} = '5G'; # Debug $opts{debug} = 1; $opts{snapshot} = 1; $opts{compress} = 'none'; $opts{lvcreate} = '/sbin/lvcreate -c 512'; $opts{lvremove} = '/sbin/lvremove'; $opts{blocksize} = '262144'; $opts{nice} = 'nice -n 19'; $opts{ionice} = 'ionice -c 2 -n 7'; $opts{livebackup} = 1; $opts{wasrunning} = 1; # get command line arguments GetOptions( "debug" => \$opts{debug}, "keep-lock" => \$opts{keeplock}, "state" => \$opts{state}, "snapsize=s" => \$opts{snapsize}, "backupdir=s" => \$opts{backupdir}, "vm=s" => \@vms, "action=s" => \$opts{action}, "cleanup" => \$opts{cleanup}, "dump" => \$opts{dump}, "unlock" => \$opts{unlock}, "connect=s" => \$opts{connect}, "snapshot!" => \$opts{snapshot}, "compress:s" => \$opts{compress}, "exclude=s" => \@excludes, "blocksize=s" => \$opts{blocksize}, "help" => \$opts{help} ); # Set compression settings if ($opts{compress} eq 'lzop'){ $opts{compext} = ".lzo"; $opts{compcmd} = "lzop -c"; } elsif ($opts{compress} eq 'bzip2'){ $opts{compext} = ".bz2"; $opts{compcmd} = "bzip2 -c"; } elsif ($opts{compress} eq 'pbzip2'){ $opts{compext} = ".bz2"; $opts{compcmd} = "pbzip2 -c"; } elsif ($opts{compress} eq 'xz'){ $opts{compext} = ".xz"; $opts{compcmd} = "xz -c"; } elsif ($opts{compress} eq 'lzip'){ $opts{compext} = ".lz"; $opts{compcmd} = "lzip -c"; } elsif ($opts{compress} eq 'plzip'){ $opts{compext} = ".lz"; $opts{compcmd} = "plzip -c"; } # Default is gzip elsif (($opts{compress} eq 'gzip') || ($opts{compress} eq '')) { $opts{compext} = ".gz"; $opts{compcmd} = "gzip -c"; } else{ $opts{compext} = ""; $opts{compcmd} = "cat"; } # Allow comma separated multi-argument @vms = split(/,/,join(',',@vms)); @excludes = split(/,/,join(',',@excludes)); # Backward compatible with --dump --cleanup --unlock $opts{action} = 'dump' if ($opts{dump}); $opts{action} = 'cleanup' if ($opts{cleanup}); $opts{action} = 'unlock' if ($opts{unlock}); # Stop here if we have no vm # Or the help flag is present if ((!@vms) || ($opts{help})){ usage(); exit 1; } if (! -d $opts{backupdir} ){ print "$opts{backupdir} is not a valid directory\n"; exit 1; } print "\n" if ($opts{debug}); foreach our $vm (@vms){ print "Checking $vm status\n\n" if ($opts{debug}); our $backupdir = $opts{backupdir}.'/'.$vm; if ($opts{action} eq 'cleanup'){ print "Running cleanup routine for $vm\n\n" if ($opts{debug}); # run_cleanup(); } elsif ($opts{action} eq 'dump'){ print "Running dump routine for $vm\n\n" if ($opts{debug}); run_dump(); } # else { # usage(); # exit 1; # } } ############################################################################ ############## FUNCTIONS #################### ############################################################################ sub prepare_backup{ my ($source,$res); my $target = $vm; my $match=0; ## locate the backing device for this res my @drbd_res = &runcmd("drbdadm dump $vm"); foreach my $line (@drbd_res) { $res = $line; if ($match == 1 && $line =~ /disk\s+(.*);/) { $source = $1; $match = 0; } if ($line =~ /device\s+.*(drbd\d+)\s+minor/) { $drbd_dev = $1; } if ($line =~ /on\s$host\s+{/i) { $match = 1; } } if (!$source) { print "Did not find DRBD backing deviced for VM\n"; exit; } else { ## set target backup file based on device $target = $source; $target =~ s/\//_-_/g; ## rename / to _-_ $target =~ s/^_-_//g; ## remove leading _-_ } ## Check if VM is running locally - migrate if off to backup ## set migrate = 1, to migrate back when done my $local_test = join("",&runcmd("virsh list")); if ($local_test =~ /$vm.*running/i) { print "$vm running locally - migration to $migrate_to\n"; my $pvd = &GetPVD($vm); &runcmd("crm resource migrate $pvd $migrate_to"); $migration = 1; sleep 1; my $remote_test = join("",&runcmd("ssh $migrate_to -C virsh list | grep -i $vm",1)); while($local_test =~ /(.*$vm.*)/) { print " $migrate_from:\t" . $1 . "\n"; print "(r)$migrate_to:\t$remote_test\n"; sleep 5; $local_test = join("",&runcmd("virsh list",1)); $remote_test = join("",&runcmd("ssh $migrate_to -C virsh list | grep -i $vm",1)); } $remote_test = join("",&runcmd("ssh $migrate_to -C virsh list | grep -i $vm",1)); print "We must of migrated ok... \n"; print "(r)$migrate_to:\t$remote_test\n"; } &runcmd("crm resource unmanage clone_lvm-" . $vm); &runcmd("crm resource unmanage ms_drbd-" . $vm); #&runcmd("crm resource unmanage p_drbd-" . $vm); sleep 1; &runcmd("vgchange -aln drbd_" . $vm,0,5); sleep 2; &runcmd("drbdadm secondary " . $vm); &runcmd("ssh $migrate_to -C touch /tmp/backup.$drbd_dev"); &runcmd("ssh $migrate_to -C touch /tmp/backup.p_drbd-$vm"); &runcmd("touch /tmp/backup.$drbd_dev"); &runcmd("touch /tmp/backup.p_drbd-$vm"); my $sec_check = join("",&runcmd("drbdadm role $vm")); if( $sec_check !~ /Secondary\/Primary/) { print "Fail: DRBD res [$vm] is not Secondary! result: $sec_check\n"; exit; } else { print "OK: DRBD res [$vm] is Secondary. result: $sec_check\n"; } if (!-d $backupdir) { mkdir $backupdir || die $!; } if (!-d $backupdir.'.meta') { mkdir $backupdir . '.meta' || die $!; } lock_vm(); save_drbd_res($res); my $time = "_".time(); # Try to snapshot the source if snapshot is enabled if ( ($opts{snapshot}) && (create_snapshot($source,$time)) ){ print "$source seems to be a valid logical volume (LVM), a snapshot has been taken as " . $source . $time ."\n" if ($opts{debug}); $source = $source.$time; push (@disks, {source => $source, target => $target . '_' . $time, type => 'snapshot'}); } # Summarize the list of disk to be dumped if ($opts{debug}){ if ($opts{action} eq 'dump'){ print "\n\nThe following disks will be dumped:\n\n"; foreach $disk (@disks){ print "Source: $disk->{source}\tDest: $backupdir/$vm" . '_' . $disk->{target} . ".img$opts{compext}\n"; } } } if ($opts{livebackup}){ print "\nWe can run a live backup\n" if ($opts{debug}); } } sub run_dump{ # Pause VM, dump state, take snapshots etc.. prepare_backup(); # Now, it's time to actually dump the disks foreach $disk (@disks){ my $source = $disk->{source}; my $dest = "$backupdir/$vm" . '_' . $disk->{target} . ".img$opts{compext}"; print "\nStarting dump of $source to $dest\n\n" if ($opts{debug}); my $ddcmd = "$opts{ionice} dd if=$source bs=$opts{blocksize} | $opts{nice} $opts{compcmd} > $dest 2>/dev/null"; print $ddcmd . "\n"; unless( system("$ddcmd") == 0 ){ die "Couldn't dump the block device/file $source to $dest\n"; } # Remove the snapshot if the current dumped disk is a snapshot destroy_snapshot($source) if ($disk->{type} eq 'snapshot'); } &runcmd("crm resource manage p_drbd-" . $vm); &runcmd("crm resource manage ms_drbd-" . $vm); &runcmd("crm resource manage clone_lvm-" . $vm); &runcmd("drbdadm primary " . $vm); sleep 1; &runcmd("ssh $migrate_to -C rm /tmp/backup.$drbd_dev"); &runcmd("ssh $migrate_to -C rm /tmp/backup.p_drbd-$vm"); &runcmd("rm /tmp/backup.$drbd_dev"); &runcmd("rm /tmp/backup.p_drbd-$vm"); &runcmd("vgchange -ay drbd_" . $vm); sleep 1; &runcmd("crm_resource -r ms_drbd-$vm -C"); sleep 1; &runcmd("crm_resource -r clone_lvm-$vm -C"); sleep 3; my $prim_check = join("",&runcmd("drbdadm role $vm")); print "DRBD resource: $prim_check\n"; ## if this was migrations, move it back if ($migration) { if ($prim_check =~ /primary\/primary/i) { ## migrate back my $local_test = join("",&runcmd("virsh list")); if ($local_test !~ /$vm.*running/i) { print "$vm NOT running locally - migration to $migrate_from\n"; my $pvd = &GetPVD($vm); &runcmd("crm resource migrate $pvd $migrate_from"); sleep 1; my$remote_test = join("",&runcmd("ssh $migrate_to -C virsh list | grep -i $vm")); my $status = 'unknown'; while($local_test !~ /(.*$vm.*running)/i) { if ($local_test =~ /(.*$vm.*)/i) { $status = $1; } print " $migrate_from:\t" . $status . "\n"; print "(r)$migrate_to:\t$remote_test\n"; sleep 5; $local_test = join("",&runcmd("virsh list",1)); $remote_test = join("",&runcmd("ssh $migrate_to -C virsh list | grep -i $vm",1)); } print "Migration is Done!\n"; print "(r)$migrate_from:\t$local_test\n"; } } } ## done # And remove the lock file, unless the --keep-lock flag is present unlock_vm() unless ($opts{keeplock}); } sub usage{ print "usage:\n$0 --action=[dump|cleanup|chunkmount|unlock] --vm=vm1[,vm2,vm3] [--debug] [--exclude=hda,hdb] [--compress] ". "[--state] [--no-snapshot] [--snapsize=<size>] [--backupdir=/path/to/dir] [--connect=<URI>] ". "[--keep-lock] [--bs=<block size>]\n" . "\n\n" . "\t--action: What action the script will run. Valid actions are\n\n" . "\t\t- dump: Run the dump routine (dump disk image to temp dir, pausing the VM if needed). It's the default action\n" . "\t\t- unlock: just remove the lock file, but don't cleanup the backup dir\n\n" . "\t--vm=name: The VM you want to work on (as known by libvirt). You can backup several VMs in one shot " . "if you separate them with comma, or with multiple --vm argument. You have to use the name of the domain, ". "ID and UUID are not supported at the moment\n\n" . "\n\nOther options:\n\n" . "\t--snapsize=<snapsize>: The amount of space to use for snapshots. Use the same format as -L option of lvcreate. " . "eg: --snapsize=15G. Default is 5G\n\n" . "\t--compress[=[gzip|bzip2|pbzip2|lzop|xz|lzip|plzip]]: On the fly compress the disks images during the dump. If you " . "don't specify a compression algo, gzip will be used.\n\n" . "\t--backupdir=/path/to/backup: Use an alternate backup dir. The directory must exists and be writable. " . "The default is /var/lib/libvirt/backup\n\n" . "\t--keep-lock: Let the lock file present. This prevent another " . "dump to run while an third party backup software (BackupPC for example) saves the dumped files.\n\n"; } # Dump the domain description as XML sub save_drbd_res{ my $res = shift; print "\nSaving XML description for $vm to $backupdir/$vm.res\n" if ($opts{debug}); open(XML, ">$backupdir/$vm" . ".res") || die $!; print XML $res; close XML; } # Create an LVM snapshot # Pass the original logical volume and the suffix # to be added to the snapshot name as arguments sub create_snapshot{ my ($blk,$suffix) = @_; my $ret = 0; print "Running: $opts{lvcreate} -p r -s -n " . $blk . $suffix . " -L $opts{snapsize} $blk > /dev/null 2>&1\n" if $opts{debug}; if ( system("$opts{lvcreate} -s -n " . $blk . $suffix . " -L $opts{snapsize} $blk > /dev/null 2>&1") == 0 ) { $ret = 1; open SNAPLIST, ">>$backupdir.meta/snapshots" or die "Error, couldn't open snapshot list file\n"; print SNAPLIST $blk.$suffix ."\n"; close SNAPLIST; } return $ret; } # Remove an LVM snapshot sub destroy_snapshot{ my $ret = 0; my ($snap) = @_; print "Removing snapshot $snap\n" if $opts{debug}; if (system ("$opts{lvremove} -f $snap > /dev/null 2>&1") == 0 ){ $ret = 1; } return $ret; } # Lock a VM backup dir # Just creates an empty lock file sub lock_vm{ print "Locking $vm\n" if $opts{debug}; open ( LOCK, ">$backupdir.meta/$vm.lock" ) || die $!; print LOCK ""; close LOCK; } # Unlock the VM backup dir # Just removes the lock file sub unlock_vm{ print "Removing lock file for $vm\n\n" if $opts{debug}; unlink <$backupdir.meta/$vm.lock>; } sub runcmd() { my $cmd = shift; my $quiet = shift; my $ignore = shift; ## ignore exit code 1 with greps -- not found is OK.. if ($cmd =~ /grep/) { $ignore = 1; } if (!$quiet) { print "exec: $cmd ... ";} my @output = `$cmd`; if ($?) { print $ignore . "\n"; my $e = sprintf("%d", $? >> 8); if ($ignore && $ignore == $e) { print "exit code = $e -- ignoring exit code $e\n"; } else { printf "\n******** command $cmd exited with value %d\n", $? >> 8; print @output; exit $? >> 8; } } if (!$quiet) { print "success\n"; } return @output; } ## get primative VirtualDomain sub GetPVD() { my $vm = shift; my $out = join("",&runcmd("crm resource show | grep $vm | grep VirtualDomain")); if ($out =~ /([\d\w\-\_]+)/) { return $1; } else { print "Could not locate Primative VirtualDomain for $vm\n"; } } </pre>
Summary:
Please note that all contributions to RARForge may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
RARForge:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Navigation menu
Personal tools
Not logged in
Talk
Contributions
Log in
Namespaces
Page
Discussion
English
Views
Read
Edit
View history
More
Search
Navigation
Home
All Pages
All Files
View Categories
Recent changes
Random page
Edit this menu
Tools
What links here
Related changes
Special pages
Page information