Tengo este código:
#!/bin/sh
echo "--------- Backup config files -------------"
find /vmfs/volumes/datastore1/ \
-type f \
-regex '.*\.\(vmx\|nvram\|vmsd\|vmdk\)$' \
! -name '*-flat.vmdk' \
-exec sh -c "$(cat << 'EOF'
addrs=XX.XX.XX.XX
for pth; do
drctry="$(printf '%s' "${pth%/*}" | sed "s/'/'\"'\"'/g")"
ssh "root@$addrs" "mkdir -p '$drctry'" && scp -pr "$pth" "$addrs:'$drctry/'"
done
EOF
)" sh {} +
echo "----------- Backup VM disks ---------------"
vim-cmd vmsvc/getallvms | sed -e '1d' -e 's/ \[.*$//' | while read -r line; do
addrs=XX.XX.XX.XX
vmid="$(printf '%s' "${line%}" | awk '{print $1;}'| sed "s/'/'\"'\"'/g")"
vmname="$(printf '%s' "${line%}" | awk '{for (i=2; i<=NF; i++) print $i}'| sed "s/'/'\"'\"'/g")"
vim-cmd vmsvc/snapshot.create $vmid backup 'Snapshot created by Backup Script' 0 0
scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"/vmfs/volumes/datastore1/$vmname"
vim-cmd vmsvc/snapshot.removeall $vmid
done
la primera parte (archivos de configuración de respaldo) funciona perfectamente. la segunda parte (discos de VM de respaldo) no. La salida de la línea vim-cmd (que inicia el ciclo) es:
1 VM_1
10 VM_2 with_space
11 VM_3 with space
12 VM_4 with_space
14 VM_5
Necesito:
- asigne la primera palabra (la identificación numérica) a var $vmid
- asigne el resto de las palabras a var $vmname para poder concatenar la ruta del comando scp y ejecutarlo correctamente sin encontrar problemas generados por espacios en el nombre del directorio y el nombre del archivo.
En este momento obtengo éxito para todas las máquinas virtuales que no tienen espacio en el nombre. Para los demás obtengo:
Create Snapshot:
/vmfs/volumes/datastore1/VM_name
with_space/VM_name
with_space.vmdk: No such file or directory
sh: with_space: not found
Remove All Snapshots:
ACTUALIZACIÓN 1: Para aquellos que necesitan conocer el resultado de "vim-cmd vmsvc/getallvm s", ahí está:
Vmid Name File Guest OS Version Annotation
1 VM_1 [datastore1] VM_1/VM_1.vmx winXPProGuest vmx-13
10 VM_2 with_space [datastore1] VM_2 with_space/VM_2 with_space.vmx opensuse64Guest vmx-13
11 VM_3 with space [datastore1] VM_3 with space/VM_3 with space.vmx opensuse64Guest vmx-13
12 VM_4 with_space [datastore1] VM_4 with_space/VM_4 with_space.vmx opensuse64Guest vmx-13
14 VM_5 [datastore1] VM_5/VM_5.vmx winXPProGuest vmx-13
ACTUALIZACIÓN 2:
Gracias a @Sorin, logré reducir este monstruo en más de un 60% a 8 líneas. Entonces, esta es la solución de clonación del pobre de un host ESXi 6.5 gratuito a otro host:
#!/bin/sh
addrs=XXX.XXX.XXX.XXX
IFS="\t" vim-cmd vmsvc/getallvms | sed -e '1d' -e 's/ \[.*//g' -e "s/\s\+/\t/" | while read id name; do
echo "----- Backup "$name" --------"
scp -pr "/vmfs/volumes/datastore1/$name/$name.vmx" "/vmfs/volumes/datastore1/$name/$name.nvram" "/vmfs/volumes/datastore1/$name/$name.vmsd" "/vmfs/volumes/datastore1/$name/$name.vmdk" $addrs:"'/vmfs/volumes/datastore1/$name'"
vim-cmd vmsvc/snapshot.create $id backup 'Snapshot created by Backup Script' 0 0
scp -pr "/vmfs/volumes/datastore1/$name/$name-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$name'"
vim-cmd vmsvc/snapshot.removeall $id
done
Gracias !!
Respuesta1
Puedes reemplazar el primer conjunto de espacios con una pestaña y configurar el IFS en "\t" y leer tanto el id como el nombre con read, algo como esto:
IFS="\t" cat test | sed -e '1d' -e 's/ \[.*//g' -e "s/\s\+/\t/" |\
while read id name; do echo -e "id:$id\nname:$name";done
id:1
name:VM_1
id:10
name:VM_2 with_space
id:11
name:VM_3 with space
id:12
name:VM_4 with_space
id:14
name:VM_5
Sin embargo, según el resultado que tiene de getallvms, no creo que sea seguro. La salida parece tener un ancho fijo, pero no está claro si ese ancho es el mismo o depende de la longitud del nombre de la VM, o si el nombre se trunca si es demasiado largo. Busqué un manual para vim-cmd para ver si puedes controlar el formato, pero no hay documentación en línea que cubra las opciones de la línea de comandos. Compruebe si vim-cmd tiene alguna opción para controlar la salida (tal vez pueda establecer un separador explícito para las columnas, o puede exportar xml/json...)
Si eso no es posible, mi sugerencia es obtener solo el vmid de getallvms y usarlo vim-cmd vmsvc/get.summary $vmid
para obtener el nombre.
Necesitaría una muestra del resultado de get.summary para darle el código exacto, pero principalmente se vería así:
vim-cmd vmsvc/getallvms | sed -e '1d' | awk "{print $1} | while read id; do
vmname=$(vim-cmd vmsvc/get.summary $vmid | \
sed '/ guest =/,/}/d' | egrep name | sed 's/.*= //;s/,//;s/ *$//')
...
done
Esto no está probado. La mayor parte lo obtuve de:http://unixetc.co.uk/2015/03/28/list-virtual-machines-on-esxi/
Ah, ahora veo que su problema es en realidad el comando SCP, las direcciones scp remotas deben tener doble escape. Intentar:
scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$vmname'"
O si tiene bash, puede usar printf para citar el nombre del archivo:
SRC=$(printf /vmfs/volumes/datastore1/%q/%q-flat.vmdk "$VMNAME" "$VMNAME")
DEST=$(printf %s:'/vmfs/volumes/datastore1/%q' $addr "$VMNAME")
y úsalo en tus comandos scp
Respuesta2
Tal vez no sea relevante, pero usando Perl es bastante fácil de lograr.
Dado test.out:
Vmid Name File Guest OS Version Annotation
1 VM_1 [datastore1] VM_1/VM_1.vmx winXPProGuest vmx-13
10 VM_2 with_space [datastore1] VM_2 with_space/VM_2 with_space.vmx opensuse64Guest vmx-13
11 VM_3 with space [datastore1] VM_3 with space/VM_3 with space.vmx opensuse64Guest vmx-13
12 VM_4 with_space [datastore1] VM_4 with_space/VM_4 with_space.vmx opensuse64Guest vmx-13
14 VM_5 [datastore1] VM_5/VM_5.vmx winXPProGuest vmx-13
El trabajo se realiza utilizando la siguiente línea de comando:
$ cat test.out |perl -ne 'if (/(\d+)[ \t]*([^[]+)/) { print "id is $1, name is $2\n"; }'
id is 1, name is VM_1
id is 10, name is VM_2 with_space
id is 11, name is VM_3 with space
id is 12, name is VM_4 with_space
id is 14, name is VM_5
Puede adaptar fácilmente la salida de Perl para que sea más utilizable con Shell...
NOTA: Este oneliner memoriza el nombre hasta que se cumple la cadena "[almacén de datos]"... si la columna del archivo difiere de "[almacén de datos]", el script no funciona...