Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
tuto:virtualisation:virt_scripts [03/04/2010 00:26] dani |
tuto:virtualisation:virt_scripts [12/07/2012 23:32] (Version actuelle) dani Page moved from virt_scripts to tuto:virtualisation:virt_scripts |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | FIXME: cette page est obsolète, et n'a été utilisé que pour la création du paquet virt-stack | ||
+ | |||
Contenue du paquet: | Contenue du paquet: | ||
* script de sauvegarde virt-backup.pl | * script de sauvegarde virt-backup.pl | ||
Ligne 6: | Ligne 8: | ||
====== Script d' | ====== Script d' | ||
+ | |||
+ | Ce script ne devrait plus être nécessaire à partit de libvirt-0.8.0 (qui intègre de façon native une fonction " | ||
<code bash> | <code bash> | ||
Ligne 164: | Ligne 168: | ||
===== Script de configuration des permissions qui vont bien ===== | ===== Script de configuration des permissions qui vont bien ===== | ||
+ | Hook script pour qemu (/ | ||
+ | <code bash> | ||
+ | #!/bin/bash | ||
+ | |||
+ | VM=shift | ||
+ | OP=shift | ||
+ | SUBOP=shift | ||
+ | |||
+ | VOLS=(cat /dev/stdin | xmlstarlet sel -t -m \ | ||
+ | "/ | ||
+ | |||
+ | if [ $OP == " | ||
+ | for VOL in $VOLS; | ||
+ | chcon -t virt_image_t $VOL | ||
+ | done | ||
+ | fi | ||
+ | |||
+ | exit 0 | ||
+ | </ | ||
+ | |||
+ | Hook script pour le démon (/ | ||
+ | |||
+ | <code bash> | ||
+ | #!/bin/bash | ||
+ | |||
+ | OBJ=shift | ||
+ | OP=shift | ||
+ | |||
+ | if [ $OP == " | ||
+ | for DIR in / | ||
+ | [ -d $DIR ] || mkdir -p $DIR | ||
+ | chown qemu:qemu $DIR | ||
+ | done | ||
+ | fi | ||
+ | |||
+ | exit 0 | ||
+ | </ | ||
Un chcon tout les supports utilisés par une VM: | Un chcon tout les supports utilisés par une VM: | ||
Ligne 197: | Ligne 238: | ||
* Dommage qu'on ait pas les stats de KSM disponibles dans /sys, ça permettrait d' | * Dommage qu'on ait pas les stats de KSM disponibles dans /sys, ça permettrait d' | ||
- | ====== Script de sauvegarde ====== | ||
- | |||
- | <code perl> | ||
- | # | ||
- | |||
- | # AUTHOR | ||
- | # | ||
- | # | ||
- | # COPYRIGHT | ||
- | # | ||
- | # | ||
- | # This program is free software; you can redistribute it and/or modify | ||
- | # it under the terms of the GNU General Public License as published by | ||
- | # the Free Software Foundation; either version 2 of the License, or | ||
- | # (at your option) any later version. | ||
- | # | ||
- | # This program is distributed in the hope that it will be useful, | ||
- | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
- | # | ||
- | # GNU General Public License for more details. | ||
- | # | ||
- | # You should have received a copy of the GNU General Public License | ||
- | # along with this program; if not, write to the Free Software | ||
- | # | ||
- | |||
- | |||
- | |||
- | # This script allows you to backup Virtual Machines managed by libvirt. | ||
- | # It has only be tested with KVM based VM | ||
- | # This script will dump: | ||
- | # * each block devices | ||
- | # * optionnally the memory (if --state flag is given) | ||
- | # * the XML description of the VM | ||
- | |||
- | # These files are writen in a temporary backup dir. Everything is done | ||
- | # in order to minimize donwtime of the guest. For example, it takes | ||
- | # a snapshot of the block devices (if backed with LVM) so the guest is | ||
- | # just paused for a couple of seconds. Once this is done, the guest is | ||
- | # resumed, and the script starts to dump the snapshot. | ||
- | |||
- | # Once a backup is finished, you'll have several files in the backup | ||
- | # directory. Let's take an example with a VM called my_vm which has | ||
- | # two virtual disks: hda and hdb. You have passed the --state flag: | ||
- | # * my_vm.lock: lock file to prevent another backup to run at the same time | ||
- | # * my_vm.xml: this file is the XML description of the VM (for libvirt configuraiton) | ||
- | # * my_vm_hda.img: | ||
- | # * my_vm_hdb.img: | ||
- | # * my_vm.state: | ||
- | |||
- | # This script was made to be ran with BackupPC pre/post commands. | ||
- | # In the pre-backup phase, you dump everything then, backuppc backups, | ||
- | # compress, pools etc... the dumped file. Eventually, when the backup is finished | ||
- | # The script is called with the --cleanup flag, which cleanups everything. | ||
- | |||
- | # Some examples: | ||
- | # | ||
- | # Backup the VM named mail01 and devsrv. Also dump the memory. | ||
- | # Exclude any virtual disk attached as vdb or hdb and on the fly | ||
- | # compress the dumped disks (uses gzip by default) | ||
- | # virt-backup.pl --dump --vm=mail01, | ||
- | |||
- | # Remove all the files related to mail01 VM in the backup directory | ||
- | # virt-backup.pl --cleanup --vm=mail01 | ||
- | |||
- | # Backup devsrv, use 10G for LVM snapshots (if available), do not dump the memory | ||
- | # (the guest will just be paused while we take a snapshot) | ||
- | # Keep the lock file present after the dump | ||
- | # virt-backup.pl --dump --vm=devsrv --snapsize=10G --keep-lock | ||
- | |||
- | # Backup devsrv, and disable LVM snapshots | ||
- | # virt-backup.pl --dump --vm=devsrv --no-snapshot | ||
- | |||
- | # Backup mail01, and enable debug (verbose output) | ||
- | # virt-backup.pl --dump --vm=mail01 --debug | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | ### TODO: | ||
- | # - Make it more robust (script crash sometime while trying to restore | ||
- | # Probably a bug somewhere between libvirt and Sys::Virt) | ||
- | # - Test with images as backend. Should just work, but without the snapshot function | ||
- | # - Add snapshot (LVM) support for image based disk ? (should we detect the mount moint, and block device | ||
- | # of the storage or let the user specify it with a --logical ?) | ||
- | # - Additionnal check that the vm is available after a restore (via $dom-> | ||
- | # - Check if compression utilies are available | ||
- | # - Support per vm excludes in one run | ||
- | |||
- | |||
- | |||
- | ### CHANGES | ||
- | # * 26/03/2010 | ||
- | # - Initial packaged version | ||
- | |||
- | use XML:: | ||
- | use Sys::Virt; | ||
- | use Getopt:: | ||
- | |||
- | # Some constant | ||
- | |||
- | our %opts = (); | ||
- | our @vms = (); | ||
- | our @excludes = (); | ||
- | |||
- | # Sets some defaults values | ||
- | $opts{backupdir} = '/ | ||
- | $opts{snapsize} = ' | ||
- | $opts{state} = 0; | ||
- | $opts{debug} = 0; | ||
- | $opts{keeplock} = 0; | ||
- | $opts{snapshot} = 1; | ||
- | $opts{connect} = " | ||
- | $opts{compress} = ' | ||
- | $opts{lvcreate} = '/ | ||
- | $opts{lvremove} = '/ | ||
- | $opts{nice} = 'nice -n 19'; | ||
- | $opts{ionice} = ' | ||
- | $opts{livebackup} = 1; | ||
- | $opts{wasrunning} = 1; | ||
- | $opts{bs} = " | ||
- | |||
- | # get command line arguments | ||
- | GetOptions( | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | ); | ||
- | |||
- | |||
- | # Set compression settings | ||
- | if ($opts{compress} eq ' | ||
- | $opts{compext} = " | ||
- | $opts{compcmd} = "lzop -c"; | ||
- | } | ||
- | elsif ($opts{compress} eq ' | ||
- | $opts{compext} = " | ||
- | $opts{compcmd} = "bzip2 -c"; | ||
- | } | ||
- | elsif ($opts{compress} eq ' | ||
- | $opts{compext} = " | ||
- | $opts{compcmd} = " | ||
- | } | ||
- | elsif ($opts{compress} eq ' | ||
- | $opts{compext} = " | ||
- | $opts{compcmd} = "xz -c"; | ||
- | } | ||
- | # Default is gzip | ||
- | elsif (($opts{compress} eq ' | ||
- | $opts{compext} = " | ||
- | $opts{compcmd} = "gzip -c"; | ||
- | } | ||
- | else{ | ||
- | $opts{compext} = ""; | ||
- | $opts{compcmd} = " | ||
- | } | ||
- | |||
- | # Allow comma separated multi-argument | ||
- | @vms = split(/,/, | ||
- | @excludes = split(/,/, | ||
- | |||
- | |||
- | |||
- | # Stop here if we either have dump and cleanup, no dump and no cleanup, no vm | ||
- | # Or the help flag is present | ||
- | if ( | ||
- | ($opts{dump} && $opts{cleanup}) || | ||
- | (!$opts{dump} && !$opts{cleanup}) || | ||
- | (!@vms) || | ||
- | ($opts{help}) | ||
- | ){ | ||
- | usage(); | ||
- | exit 1; | ||
- | } | ||
- | |||
- | if (! -d $opts{backupdir} ){ | ||
- | print " | ||
- | exit 1; | ||
- | } | ||
- | |||
- | # Connect to libvirt | ||
- | print " | ||
- | our $libvirt = Sys:: | ||
- | die "Error connecting to libvirt on URI: $opts{connect}"; | ||
- | |||
- | |||
- | |||
- | print " | ||
- | |||
- | |||
- | foreach our $vm (@vms){ | ||
- | # Create a new object representing the VM | ||
- | print " | ||
- | our $dom = $libvirt-> | ||
- | die "Error opening $vm object"; | ||
- | if ($opts{dump}){ | ||
- | print " | ||
- | $opts{backupdir} .= '/' | ||
- | mkdir $opts{backupdir} || die $!; | ||
- | run_dump(); | ||
- | } | ||
- | elsif ($opts{cleanup}){ | ||
- | print " | ||
- | run_cleanup(); | ||
- | } | ||
- | else { | ||
- | usage(); | ||
- | exit 1; | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | |||
- | |||
- | ############################################################################ | ||
- | ############## | ||
- | ############################################################################ | ||
- | |||
- | |||
- | sub run_dump{ | ||
- | # Create a new XML object | ||
- | my $xml = new XML::Simple (); | ||
- | my $data = $xml-> | ||
- | |||
- | # STop here if the lock file is present, another dump might be running | ||
- | die " | ||
- | |||
- | # Lock VM: Create a lock file so only one dump process can run | ||
- | lock_vm(); | ||
- | |||
- | # Save the XML description | ||
- | save_xml(); | ||
- | |||
- | # Save the VM state if it's running and --state is present | ||
- | # (else, just suspend the VM) | ||
- | $opts{wasrunning} = 0 unless (is_active()); | ||
- | |||
- | if ($opts{wasrunning}){ | ||
- | if ($opts{state}){ | ||
- | save_vm_state(); | ||
- | } | ||
- | else{ | ||
- | suspend_vm(); | ||
- | } | ||
- | } | ||
- | |||
- | my @disks; | ||
- | |||
- | # Create a list of disks used by the VM | ||
- | foreach $disk (@{$data-> | ||
- | |||
- | my $source; | ||
- | if ($disk-> | ||
- | $source = $disk-> | ||
- | } | ||
- | elsif ($disk-> | ||
- | $source = $disk-> | ||
- | } | ||
- | else{ | ||
- | print " | ||
- | " and only block and file are supported" | ||
- | next; | ||
- | } | ||
- | my $target = $disk-> | ||
- | |||
- | # Check if the current disk is not excluded | ||
- | if (!!grep { $_ eq " | ||
- | print " | ||
- | join(",", | ||
- | next; | ||
- | } | ||
- | |||
- | # If the device is a disk (and not a cdrom) and the source dev exists | ||
- | if (($disk-> | ||
- | |||
- | print " | ||
- | |||
- | # If it's a block device | ||
- | if ($disk-> | ||
- | | ||
- | my $time = " | ||
- | # Try to snapshot the source if snapshot is enabled | ||
- | if ( ($opts{snapshot}) && (create_snapshot($source, | ||
- | print " | ||
- | $source . $time ." | ||
- | $source = $source.$time; | ||
- | push (@disks, {source => $source, target => $target, type => ' | ||
- | } | ||
- | # Snapshot failed, or disabled: disabling live backups | ||
- | else{ | ||
- | if ($opts{snapshot}){ | ||
- | print " | ||
- | ", live backup will be disabled\n" | ||
- | } | ||
- | else{ | ||
- | print "Not using LVM snapshots, live backups will be disabled\n" | ||
- | } | ||
- | $opts{livebackup} = 0; | ||
- | push (@disks, {source => $source, target => $target, type => ' | ||
- | } | ||
- | } | ||
- | elsif ($disk-> | ||
- | $opts{livebackup} = 0; | ||
- | push (@disks, {source => $source, target => $target, type => ' | ||
- | } | ||
- | print " | ||
- | } | ||
- | } | ||
- | |||
- | # Summarize the list of disk to be dumped | ||
- | if ($opts{debug}){ | ||
- | print " | ||
- | foreach $disk (@disks){ | ||
- | print " | ||
- | " | ||
- | } | ||
- | print " | ||
- | } | ||
- | |||
- | # If livebackup is possible (every block devices can be snapshoted) | ||
- | # We can restore the VM now, in order to minimize the downtime | ||
- | if ($opts{livebackup}){ | ||
- | print "\nWe can run a live backup\n" | ||
- | if ($opts{wasrunning}){ | ||
- | if ($opts{state}){ | ||
- | restore_vm(); | ||
- | } | ||
- | else{ | ||
- | resume_vm(); | ||
- | } | ||
- | } | ||
- | } | ||
- | |||
- | # Now, it's time to actually dump the disks | ||
- | foreach $disk (@disks){ | ||
- | |||
- | my $source = $disk-> | ||
- | my $dest = " | ||
- | |||
- | print " | ||
- | my $ddcmd = " | ||
- | unless( system(" | ||
- | die " | ||
- | } | ||
- | # Remove the snapshot if the current dumped disk is a snapshot | ||
- | destroy_snapshot($source) if ($disk-> | ||
- | } | ||
- | |||
- | # If the VM was running before the dump, restore (or resume) it | ||
- | if ($opts{wasrunning}){ | ||
- | if ($opts{state}){ | ||
- | restore_vm(); | ||
- | } | ||
- | else{ | ||
- | resume_vm(); | ||
- | } | ||
- | } | ||
- | # And remove the lock file, unless the --keep-lock flag is present | ||
- | unlock_vm() unless ($opts{keeplock}); | ||
- | } | ||
- | |||
- | # Remove the dumps | ||
- | sub run_cleanup{ | ||
- | print " | ||
- | my $cnt = 0; | ||
- | $cnt= unlink < | ||
- | rmdir " | ||
- | print "$cnt file(s) removed\n" | ||
- | } | ||
- | |||
- | sub usage{ | ||
- | print " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | "if you separate them with comma, or with multiple --vm argument. You have to use the name of the domain, ". | ||
- | "ID and UUID is not supported at the moment\n\n" | ||
- | " | ||
- | " | ||
- | "the VM (if running) instead of just suspending it. With this you should be able to restore the VM at " . | ||
- | "the exact state it was when the backup started. The reason this flag is optional is that some guests " . | ||
- | " | ||
- | "your environnement before using this flag on production\n\n" | ||
- | " | ||
- | "of each disk of type ' | ||
- | "on the --state flag) immediatly after the snapshots have been taken, resulting in almost no downtime. " . | ||
- | "This is called a \"live backup\" | ||
- | "If at least one disk cannot be snapshoted, the VM is suspended (or stoped) for the time the disks are " . | ||
- | " | ||
- | "or RAID10)\n\n" | ||
- | " | ||
- | "eg: --snapsize=15G. Default is 5G\n\n" | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | "disk of a VM, but not the data one which can be backed up separatly, at the files level.\n\n" | ||
- | " | ||
- | "The default is / | ||
- | " | ||
- | "The default is qemu:/// | ||
- | " | ||
- | "dump to run while an third party backup software (BackupPC for example) saves the dumped files.\n\n" | ||
- | " | ||
- | "bs option of dd\n\n" | ||
- | } | ||
- | |||
- | # Save a running VM, if it's running | ||
- | sub save_vm_state{ | ||
- | if (is_active()){ | ||
- | print "$vm is running, saving state....\n" | ||
- | $dom-> | ||
- | print "$vm state saved as $opts{backupdir}/ | ||
- | } | ||
- | else{ | ||
- | print "$vm is not running, nothing to do\n" if ($opts{debug}); | ||
- | } | ||
- | } | ||
- | |||
- | # Restore the state of a VM | ||
- | sub restore_vm{ | ||
- | if (! is_active()){ | ||
- | if (-e " | ||
- | print " | ||
- | $libvirt-> | ||
- | print " | ||
- | my $i = 0; | ||
- | while ((!is_active()) && ($i < 120)){ | ||
- | sleep(5); | ||
- | $i = $i+5; | ||
- | } | ||
- | print " | ||
- | if (($i > 120) && ($opts{debug})); | ||
- | } | ||
- | else{ | ||
- | print " | ||
- | } | ||
- | } | ||
- | else{ | ||
- | print " | ||
- | if ($opts{debug}); | ||
- | } | ||
- | } | ||
- | |||
- | # Suspend a VM | ||
- | sub suspend_vm(){ | ||
- | if (is_active()){ | ||
- | print "$vm is running, suspending\n" | ||
- | $dom-> | ||
- | print "$vm now suspended\n" | ||
- | } | ||
- | else{ | ||
- | print "$vm is not running, nothing to do\n" if ($opts{debug}); | ||
- | } | ||
- | } | ||
- | |||
- | # Resume a VM if it's paused | ||
- | sub resume_vm(){ | ||
- | if ($dom-> | ||
- | print "$vm is suspended, resuming\n" | ||
- | $dom-> | ||
- | print "$vm now resumed\n" | ||
- | } | ||
- | else{ | ||
- | print "$vm is not suspended, nothing to do\n" if ($opts{debug}); | ||
- | } | ||
- | } | ||
- | |||
- | # Dump the domain description as XML | ||
- | sub save_xml{ | ||
- | print " | ||
- | open(XML, "> | ||
- | print XML $dom-> | ||
- | 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, | ||
- | my $ret = 0; | ||
- | print " | ||
- | " -L $opts{snapsize} $blk > /dev/null 2>& | ||
- | if ( system(" | ||
- | " -L $opts{snapsize} $blk > /dev/null 2>& | ||
- | $ret = 1; | ||
- | } | ||
- | return $ret; | ||
- | } | ||
- | |||
- | # Remove an LVM snapshot | ||
- | sub destroy_snapshot{ | ||
- | my $ret = 0; | ||
- | my ($snap) = @_; | ||
- | print " | ||
- | if (system (" | ||
- | $ret = 1; | ||
- | } | ||
- | return $ret; | ||
- | } | ||
- | |||
- | # Lock a VM backup dir | ||
- | # Just creates an empty lock file | ||
- | sub lock_vm{ | ||
- | print " | ||
- | open ( LOCK, "> | ||
- | print LOCK ""; | ||
- | close LOCK; | ||
- | } | ||
- | |||
- | # Unlock the VM backup dir | ||
- | # Just removes the lock file | ||
- | sub unlock_vm{ | ||
- | print " | ||
- | unlink < | ||
- | } | ||
- | |||
- | </ | ||
- | |||
- | |||
- | ====== Dépendances ====== | ||
- | à mettre dans le rpm: | ||
- | |||
- | * perl-Sys-Virt | ||
- | * perl-XML-Simple | ||
- | * xmlstarlet | ||
- | * lzop | ||
- | * pbzip2 | ||
- | * xz | ||