miércoles, 21 de septiembre de 2011

Script para hacer backup de maquinas virtuales vmware

   Bueno, aunque últimamente me siento mucho más cómodo haciendo scripts y trabajando con Linux, hay veces que tienes que hacer cosas en Windows. Ahora le ha tocado el turno a la Powershell de Windows. Digan lo que digan, es una consola de comandos al estilo Linux, siempre se copia lo bueno.

   He creado este script, copiando un poco de aquí y poco de allí, para hacer copias de maquinas virtuales de VMWare que estan arrancadas. La idea es generar un Snapshot de la máquina, luego se copia a un datastore destino y luego se elimina el snapshot. Además de esa funcionalidad, queríamos una programación rudimentaria de los backups (fecha de lanzamiento y número de copias).

   El script se lanza todos los días. Se comprueba un fichero csv y si el fichero indica que se debe hacer backup de una maquina se lanza. Por último, la máquina se mueve a una carpeta y se dejan sólo un número finito de copias, también indicadas por el fichero csv.

   El fichero csv tiene el siguiente formato:

VMMaquina,DestinoBackup,n_copias,planificaciones
XXXXY,vmbackup,2,1;20
YYYYX,vmbackup,1,2

   El primer parámetro se corresponde con el nombre de la maquina a clonar. El segundo es el datastore destino de la copia. El tercero es el número de copias a conservar. El último parámetro separa por ";" las fechas en que lanzar el backup.

   El Script:

# Nos aseguramos de que la PowerShell sepa que tiene que cargar
add-pssnapin VMware.VimAutomation.Core

##########  Funciones
# VMMaquina --> Maquina virtual a clonar
# DestinoBackup --> Datastore destino para la maquina clonada

function clonarMaquinaVirtual($VMMaquina, $DestinoBackup) {

    if ( (($VMMaquina -ne $null) -and ($DestinoBackup -ne $null)) ) {
        Echo "Copiar ($VMMaquina) en ($DestinoBackup)"

        # Formato de la fecha
        $date = Get-Date -Format "yyyyMMdd"

        #Connect to vCenter - Instalado en XXXXX
        Connect-VIServer "XXXXXXXX"

        $vm = get-VM $VMMaquina
       
        if ($vm -eq $null) {
            Echo "No se encontro la maquina $VMMaquina en el VC."
        } else {
            #Generamos el Snapshot
            $cloneSnap = $vm | New-Snapshot -Name "Snapshot de Backup"

            # Obtenemos la maquina a  clonar
            $vmView = $vm | Get-View
           
            # Desde donde clonamos
            $cloneFolder = $vmView.parent
           
            # Parametros de la maquina clonada
            $cloneSpec = new-object Vmware.Vim.VirtualMachineCloneSpec
            $cloneSpec.Snapshot = $vmView.Snapshot.CurrentSnapshot

            # Donde la clonamos
            $cloneSpec.Location = new-object Vmware.Vim.VirtualMachineRelocateSpec
            $cloneSpec.Location.Datastore = (Get-Datastore -Name $DestinoBackup | Get-View).MoRef
            $cloneSpec.Location.Transform =  [Vmware.Vim.VirtualMachineRelocateTransformation]::sparse

            #Nombre de la copia
            $cloneName = "$vm-$date"
           
            ############# Clonamos la Maquina ###############
            $vmView.CloneVM( $cloneFolder, $cloneName, $cloneSpec )
           
            # Nombre la maquina clonada
           Get-VM $cloneName

            # Quitamos el Snapshot
            Get-Snapshot -VM (Get-VM -Name $VMMaquina) -Name $cloneSnap | Remove-Snapshot -confirm:$False

            # Quitar del inventario la maquina clonada
            Remove-VM -VM $cloneName -confirm:$False
        }
       
        # Desconectamos
        Disconnect-VIServer -Confirm:$false
    }

}

# Reorganizamos en carpetas los backups
#

# vm --> Nombre de la maquina virtual
# copies --> Numero de copias a mantener de la maquina clonada

function reorganizarDS($vm, $copies) {
    # Conectamos la Unidad Z: a \\xxxxxxxxxxx\vmbackup (samba)
    $net = new-object -ComObject WScript.Network
    $net.MapNetworkDrive("z:", "\\xxxxxxxxxx\vmbackup", $false, "USER", "PASS")
   
    $rutaMaquina = "z:\" + $vm
    $origenRutaMaquina = "z:\" + $vm + "-*"
   
    # Reorganizamos la maquina
    if (-not (Test-Path $rutaMaquina)) {
        Echo "Creamos una carpeta para la maquina en ($rutaMaquina)."
        New-Item $rutaMaquina -type directory
    }
    # Movemos las maquinas al directorio
    Echo "Movemos desde ($origenRutaMaquina) a ($rutaMaquina)."
   
    Move-Item $origenRutaMaquina $rutaMaquina

    Echo "Realizamos limpieza de ($vm) y dejamos ($copies) copias"

    # Cuantas maquinas hay almacenadas
    $numCopias = $(Get-Item "$rutaMaquina\*").count
    Echo "Hay ($numCopias) copias de la maquina"
   
    if ($numCopias -gt $copies) {
        Echo "Borrando maquinas"
        do {
            dir $rutaMaquina | sort lastwritetime | select -first 1 | remove-item -recurse -force
            $numCopias = $(Get-Item "$rutaMaquina\*").count
            Echo "Ahora quedan ($numCopias) maquinas."
        } until ($numCopias -eq $copies)
    }

    Echo "Limpieza terminada"
   
    # Desconectamos la unidad Z:
    $net.RemoveNetworkDrive("z:")
}   

########## Fin funciones


# Import Backup CSV
$backupinfo =  Import-Csv C:\scripts\MaquinasBackups.csv

# Recorremos la lista de maquinas
foreach ($datosBackup in $backupinfo) {
    if ($($datosBackup.VMMaquina) -eq "") {
        Echo "Hay una maquina en blanco..."
    } else {
        # Buscamos que dia del mes es hoy
        $todayIs = Get-Date -Format "dd"
        # Cambiamos los dias 0X por X
        $todayIs = [int] $todayIs
       
        Echo "Hoy es ($todayIs)."
       
        Echo "Comprobando maquina ($($datosBackup.VMMaquina)) que debe quedar con ($($datosBackup.n_copias)) copias."
        Echo "Se debe lanzar los dias..."
        # Parseamos el ultimo parametro. Los dias se separan por ;
        $backupDates = ($($datosBackup.planificaciones)).split(";")
        foreach ($backupDate in $backupDates) {
            Echo "El dia ($backupDate) se tiene que hacer backup."
            if ($todayIs -eq $backupDate) {
                $backupVM = $($datosBackup.VMMaquina)
                $backupDest = $($datosBackup.DestinoBackup)
                Echo "Lanzamos el backup de ($backupVM) en ($backupDest)"
               
                # Llamamos al script c:\scripts\BackupVms.ps1
                Echo "Lanzando la copia"
                clonarMaquinaVirtual $backupVM $backupDest
                Echo "Copia terminada"
               
                # Como hemos hecho backup es necesario limpiar lo viejo
                reorganizarDS $backupVM $($datosBackup.n_copias)
            }
           
        }
    }
}