forked from lamw/ghettoVCB
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ghettoVCB.sh
executable file
·1433 lines (1234 loc) · 60.5 KB
/
ghettoVCB.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Author: William Lam
# Created Date: 11/17/2008
# http://www.virtuallyghetto.com/
# http://communities.vmware.com/docs/DOC-8760
##################################################################
# directory that all VM backups should go (e.g. /vmfs/volumes/SAN_LUN1/mybackupdir)
VM_BACKUP_VOLUME=/vmfs/volumes/mini-local-datastore-2/backups
# Format output of VMDK backup
# zeroedthick
# 2gbsparse
# thin
# eagerzeroedthick
DISK_BACKUP_FORMAT=thin
# Number of backups for a given VM before deleting
VM_BACKUP_ROTATION_COUNT=3
# Shutdown guestOS prior to running backups and power them back on afterwards
# This feature assumes VMware Tools are installed, else they will not power down and loop forever
# 1=on, 0 =off
POWER_VM_DOWN_BEFORE_BACKUP=0
# enable shutdown code 1=on, 0 = off
ENABLE_HARD_POWER_OFF=0
# if the above flag "ENABLE_HARD_POWER_OFF "is set to 1, then will look at this flag which is the # of iterations
# the script will wait before executing a hard power off, this will be a multiple of 60seconds
# (e.g) = 3, which means this will wait up to 180seconds (3min) before it just powers off the VM
ITER_TO_WAIT_SHUTDOWN=3
# Number of iterations the script will wait before giving up on powering down the VM and ignoring it for backup
# this will be a multiple of 60 (e.g) = 5, which means this will wait up to 300secs (5min) before it gives up
POWER_DOWN_TIMEOUT=5
# enable compression with gzip+tar 1=on, 0=off
ENABLE_COMPRESSION=0
# Include VMs memory when taking snapshot
VM_SNAPSHOT_MEMORY=0
# Quiesce VM when taking snapshot (requires VMware Tools to be installed)
VM_SNAPSHOT_QUIESCE=0
# Allow VMs with snapshots to be backed up, this WILL CONSOLIDATE EXISTING SNAPSHOTS!
ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP=0
##########################################################
# NON-PERSISTENT NFS-BACKUP ONLY
#
# ENABLE NON PERSISTENT NFS BACKUP 1=on, 0=off
ENABLE_NON_PERSISTENT_NFS=0
# umount NFS datastore after backup is complete 1=yes, 0=no
UNMOUNT_NFS=0
# IP Address of NFS Server
NFS_SERVER=172.51.0.192
# Path of exported folder residing on NFS Server (e.g. /some/mount/point )
NFS_MOUNT=/upload
# Non-persistent NFS datastore display name of choice
NFS_LOCAL_NAME=backup
# Name of backup directory for VMs residing on the NFS volume
NFS_VM_BACKUP_DIR=mybackups
##########################################################
# EMAIL CONFIGURATIONS
#
# Email log 1=yes, 0=no
EMAIL_LOG=0
# Email Delay Interval from NC (netcat) - default 1
EMAIL_DELAY_INTERVAL=1
# Email SMTP server
EMAIL_SERVER=auroa.primp-industries.com
# Email SMTP server port
EMAIL_SERVER_PORT=25
# Email FROM
EMAIL_FROM=root@ghettoVCB
# Email RCPT
# Comma separated list of VM startup/shutdown ordering
VM_SHUTDOWN_ORDER=
VM_STARTUP_ORDER=
# DO NOT USE - UNTESTED CODE
# Path to another location that should have backups rotated,
# this is useful when your backups go to a temporary location
# then are rsync'd to a final destination. You can specify the final
# destination as the ADDITIONAL_ROTATION_PATH which will be rotated after
# all VMs have been restarted
ADDITIONAL_ROTATION_PATH=
############################
######### DEBUG ############
############################
# Do not remove workdir on exit: 1=yes, 0=no
WORKDIR_DEBUG=0
########################## DO NOT MODIFY PAST THIS LINE ##########################
# RSYNC LINK 1=yes, 0 = no
RSYNC_LINK=0
LOG_LEVEL="info"
VMDK_FILES_TO_BACKUP="all"
# default 15min timeout
SNAPSHOT_TIMEOUT=15
LAST_MODIFIED_DATE=2013_01_11
VERSION=0
VERSION_STRING=${LAST_MODIFIED_DATE}_${VERSION}
# Directory naming convention for backup rotations (please ensure there are no spaces!)
# If set to "0", VMs will be rotated via an index, beginning at 0, ending at
# VM_BACKUP_ROTATION_COUNT-1
VM_BACKUP_DIR_NAMING_CONVENTION="$(date +%F_%H-%M-%S)"
printUsage() {
echo "###############################################################################"
echo "#"
echo "# ghettoVCB for ESX/ESXi 3.5, 4.x+ and 5.x"
echo "# Author: William Lam"
echo "# http://www.virtuallyghetto.com/"
echo "# Documentation: http://communities.vmware.com/docs/DOC-8760"
echo "# Created: 11/17/2008"
echo "# Last modified: ${LAST_MODIFIED_DATE} Version ${VERSION}"
echo "#"
echo "###############################################################################"
echo
echo "Usage: $(basename $0) [options]"
echo
echo "OPTIONS:"
echo " -a Backup all VMs on host"
echo " -f List of VMs to backup"
echo " -m Name of VM to backup (overrides -f)"
echo " -c VM configuration directory for VM backups"
echo " -g Path to global ghettoVCB configuration file"
echo " -l File to output logging"
echo " -w ghettoVCB work directory (default: /tmp/ghettoVCB.work)"
echo " -d Debug level [info|debug|dryrun] (default: info)"
echo
echo "(e.g.)"
echo -e "\nBackup VMs stored in a list"
echo -e "\t$0 -f vms_to_backup"
echo -e "\nBackup a single VM"
echo -e "\t$0 -m vm_to_backup"
echo -e "\nBackup all VMs residing on this host"
echo -e "\t$0 -a"
echo -e "\nBackup all VMs residing on this host except for the VMs in the exclusion list"
echo -e "\t$0 -a -e vm_exclusion_list"
echo -e "\nBackup VMs based on specific configuration located in directory"
echo -e "\t$0 -f vms_to_backup -c vm_backup_configs"
echo -e "\nBackup VMs using global ghettoVCB configuration file"
echo -e "\t$0 -f vms_to_backup -g /global/ghettoVCB.conf"
echo -e "\nOutput will log to /tmp/ghettoVCB.log (consider logging to local or remote datastore to persist logs)"
echo -e "\t$0 -f vms_to_backup -l /vmfs/volume/local-storage/ghettoVCB.log"
echo -e "\nDry run (no backup will take place)"
echo -e "\t$0 -f vms_to_backup -d dryrun"
echo
}
logger() {
LOG_TYPE=$1
MSG=$2
if [[ "${LOG_LEVEL}" == "debug" ]] && [[ "${LOG_TYPE}" == "debug" ]] || [[ "${LOG_TYPE}" == "info" ]] || [[ "${LOG_TYPE}" == "dryrun" ]]; then
TIME=$(date +%F" "%H:%M:%S)
if [[ "${LOG_TO_STDOUT}" -eq 1 ]] ; then
echo -e "${TIME} -- ${LOG_TYPE}: ${MSG}"
fi
if [[ -n "${LOG_OUTPUT}" ]] ; then
echo -e "${TIME} -- ${LOG_TYPE}: ${MSG}" >> "${LOG_OUTPUT}"
fi
if [[ "${EMAIL_LOG}" -eq 1 ]] ; then
echo -ne "${TIME} -- ${LOG_TYPE}: ${MSG}\r\n" >> "${EMAIL_LOG_OUTPUT}"
fi
fi
}
sanityCheck() {
# ensure root user is running the script
if [ ! $(env | grep -e "^USER=" | awk -F = '{print $2}') == "root" ]; then
logger "info" "This script needs to be executed by \"root\"!"
echo "ERROR: This script needs to be executed by \"root\"!"
exit 1
fi
# use of global ghettoVCB configuration
if [[ "${USE_GLOBAL_CONF}" -eq 1 ]] ; then
reConfigureGhettoVCBConfiguration "${GLOBAL_CONF}"
fi
# always log to STDOUT, use "> /dev/null" to ignore output
LOG_TO_STDOUT=1
#if no logfile then provide default logfile in /tmp
if [[ -z "${LOG_OUTPUT}" ]] ; then
LOG_OUTPUT="/tmp/ghettoVCB-$(date +%F_%H-%M-%S)-$$.log"
echo "Logging output to \"${LOG_OUTPUT}\" ..."
fi
touch "${LOG_OUTPUT}"
# REDIRECT is used by the "tail" trick, use REDIRECT=/dev/null to redirect vmkfstool to STDOUT only
REDIRECT=${LOG_OUTPUT}
if [[ ! -f "${VM_FILE}" ]] && [[ "${USE_VM_CONF}" -eq 0 ]] && [[ "${BACKUP_ALL_VMS}" -eq 0 ]]; then
logger "info" "ERROR: \"${VM_FILE}\" is not valid VM input file!"
printUsage
fi
if [[ ! -f "${VM_EXCLUSION_FILE}" ]] && [[ "${EXCLUDE_SOME_VMS}" -eq 1 ]]; then
logger "info" "ERROR: \"${VM_EXCLUSION_FILE}\" is not valid VM exclusion input file!"
printUsage
fi
if [[ ! -d "${CONFIG_DIR}" ]] && [[ "${USE_VM_CONF}" -eq 1 ]]; then
logger "info" "ERROR: \"${CONFIG_DIR}\" is not valid directory!"
printUsage
fi
if [[ ! -f "${GLOBAL_CONF}" ]] && [[ "${USE_GLOBAL_CONF}" -eq 1 ]]; then
logger "info" "ERROR: \"${GLOBAL_CONF}\" is not valid global configuration file!"
printUsage
fi
if [[ -f /usr/bin/vmware-vim-cmd ]]; then
VMWARE_CMD=/usr/bin/vmware-vim-cmd
VMKFSTOOLS_CMD=/usr/sbin/vmkfstools
elif [[ -f /bin/vim-cmd ]]; then
VMWARE_CMD=/bin/vim-cmd
VMKFSTOOLS_CMD=/sbin/vmkfstools
else
logger "info" "ERROR: Unable to locate *vimsh*! You're not running ESX(i) 3.5+, 4.x+ or 5.0!"
echo "ERROR: Unable to locate *vimsh*! You're not running ESX(i) 3.5+, 4.x+ or 5.0!"
exit 1
fi
ESX_VERSION=$(vmware -v | awk '{print $3}')
case "${ESX_VERSION}" in
5.0.0|5.1.0) VER=5; break;;
4.0.0|4.1.0) VER=4; break;;
3.5.0|3i) VER=3; break;;
*) echo "You're not running ESX(i) 3.5, 4.x, 5.x!"; exit 1; break;;
esac
NEW_VIMCMD_SNAPSHOT="no"
${VMWARE_CMD} vmsvc/snapshot.remove 2>&1 | grep -q "snapshotId"
[[ $? -eq 0 ]] && NEW_VIMCMD_SNAPSHOT="yes"
if [[ "${EMAIL_LOG}" -eq 1 ]] && [[ -f /usr/bin/nc ]] || [[ -f /bin/nc ]]; then
if [[ -f /usr/bin/nc ]] ; then
NC_BIN=/usr/bin/nc
elif [[ -f /bin/nc ]] ; then
NC_BIN=/bin/nc
fi
else
EMAIL_LOG=0
fi
TAR="tar"
[[ ! -f /bin/tar ]] && TAR="busybox tar"
# Enable multiextent VMkernel module if disk format is 2gbsparse (disabled by default in 5.1)
if [[ "${VMDK_DISK_FORMAT}" == "2gbsparse" ]] && [[ "${VER}" -eq 5 ]]; then
esxcli system module list | grep multiextent > /dev/null 2>&1
if [ $? -eq 1 ]; then
logger "info" "multiextent VMkernel module is not loaded & is required for 2gbsparse, enabling ..."
esxcli system module load -m multiextent
fi
fi
}
startTimer() {
START_TIME=$(date)
S_TIME=$(date +%s)
}
endTimer() {
END_TIME=$(date)
E_TIME=$(date +%s)
DURATION=$(echo $((E_TIME - S_TIME)))
#calculate overall completion time
if [[ ${DURATION} -le 60 ]] ; then
logger "info" "Backup Duration: ${DURATION} Seconds"
else
logger "info" "Backup Duration: $(awk 'BEGIN{ printf "%.2f\n", '${DURATION}'/60}') Minutes"
fi
}
captureDefaultConfigurations() {
DEFAULT_VM_BACKUP_VOLUME="${VM_BACKUP_VOLUME}"
DEFAULT_DISK_BACKUP_FORMAT="${DISK_BACKUP_FORMAT}"
DEFAULT_VM_BACKUP_ROTATION_COUNT="${VM_BACKUP_ROTATION_COUNT}"
DEFAULT_POWER_VM_DOWN_BEFORE_BACKUP="${POWER_VM_DOWN_BEFORE_BACKUP}"
DEFAULT_ENABLE_HARD_POWER_OFF="${ENABLE_HARD_POWER_OFF}"
DEFAULT_ITER_TO_WAIT_SHUTDOWN="${ITER_TO_WAIT_SHUTDOWN}"
DEFAULT_POWER_DOWN_TIMEOUT="${POWER_DOWN_TIMEOUT}"
DEFAULT_SNAPSHOT_TIMEOUT="${SNAPSHOT_TIMEOUT}"
DEFAULT_ENABLE_COMPRESSION="${ENABLE_COMPRESSION}"
DEFAULT_VM_SNAPSHOT_MEMORY="${VM_SNAPSHOT_MEMORY}"
DEFAULT_VM_SNAPSHOT_QUIESCE="${VM_SNAPSHOT_QUIESCE}"
DEFAULT_ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP="${ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP}"
DEFAULT_VMDK_FILES_TO_BACKUP="${VMDK_FILES_TO_BACKUP}"
DEFAULT_EMAIL_LOG="${EMAIL_LOG}"
DEFAULT_WORKDIR_DEBUG="${WORKDIR_DEBUG}"
DEFAULT_VM_SHUTDOWN_ORDER="${VM_SHUTDOWN_ORDER}"
DEFAULT_VM_STARTUP_ORDER="${VM_STARTUP_ORDER}"
}
useDefaultConfigurations() {
VM_BACKUP_VOLUME="${DEFAULT_VM_BACKUP_VOLUME}"
DISK_BACKUP_FORMAT="${DEFAULT_DISK_BACKUP_FORMAT}"
VM_BACKUP_ROTATION_COUNT="${DEFAULT_VM_BACKUP_ROTATION_COUNT}"
POWER_VM_DOWN_BEFORE_BACKUP="${DEFAULT_POWER_VM_DOWN_BEFORE_BACKUP}"
ENABLE_HARD_POWER_OFF="${DEFAULT_ENABLE_HARD_POWER_OFF}"
ITER_TO_WAIT_SHUTDOWN="${DEFAULT_ITER_TO_WAIT_SHUTDOWN}"
POWER_DOWN_TIMEOUT="${DEFAULT_POWER_DOWN_TIMEOUT}"
SNAPSHOT_TIMEOUT="${DEFAULT_SNAPSHOT_TIMEOUT}"
ENABLE_COMPRESSION="${DEFAULT_ENABLE_COMPRESSION}"
VM_SNAPSHOT_MEMORY="${DEFAULT_VM_SNAPSHOT_MEMORY}"
VM_SNAPSHOT_QUIESCE="${DEFAULT_VM_SNAPSHOT_QUIESCE}"
ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP="${DEFAULT_ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP}"
VMDK_FILES_TO_BACKUP="${DEFAULT_VMDK_FILES_TO_BACKUP}"
EMAIL_LOG="${DEFAULT_EMAIL_LOG}"
WORKDIR_DEBUG="${DEFAULT_WORKDIR_DEBUG}"
VM_SHUTDOWN_ORDER="${DEFAULT_VM_SHUTDOWN_ORDER}"
VM_STARTUP_ORDER="${DEFAULT_VM_STARTUP_ORDER}"
}
reConfigureGhettoVCBConfiguration() {
GLOBAL_CONF=$1
if [[ -f "${GLOBAL_CONF}" ]]; then
source "${GLOBAL_CONF}"
else
useDefaultConfigurations
fi
}
reConfigureBackupParam() {
VM=$1
if [[ -e "${CONFIG_DIR}/${VM}" ]]; then
logger "info" "CONFIG - USING CONFIGURATION FILE = ${CONFIG_DIR}/${VM}"
source "${CONFIG_DIR}/${VM}"
else
useDefaultConfigurations
fi
}
dumpHostInfo() {
VERSION=$(vmware -v)
logger "debug" "HOST VERSION: ${VERSION}"
echo ${VERSION} | grep "Server 3i" > /dev/null 2>&1
[[ $? -eq 1 ]] && logger "debug" "HOST LEVEL: $(vmware -l)"
logger "debug" "HOSTNAME: $(hostname)\n"
}
findVMDK() {
VMDK_TO_SEARCH_FOR=$1
#if [[ "${USE_VM_CONF}" -eq 1 ]] ; then
logger "debug" "findVMDK() - Searching for VMDK: \"${VMDK_TO_SEARCH_FOR}\" to backup"
OLD_IFS2="${IFS}"
IFS=","
for k in ${VMDK_FILES_TO_BACKUP}; do
VMDK_FILE=$(echo $k | sed -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//')
if [[ "${VMDK_FILE}" == "${VMDK_TO_SEARCH_FOR}" ]] ; then
logger "debug" "findVMDK() - Found VMDK! - \"${VMDK_TO_SEARCH_FOR}\" to backup"
isVMDKFound=1
fi
done
IFS="${OLD_IFS2}"
#fi
}
getVMDKs() {
#get all VMDKs listed in .vmx file
VMDKS_FOUND=$(grep -iE '(^scsi|^ide)' "${VMX_PATH}" | grep -i fileName | awk -F " " '{print $1}')
TMP_IFS=${IFS}
IFS=${ORIG_IFS}
#loop through each disk and verify that it's currently present and create array of valid VMDKS
for DISK in ${VMDKS_FOUND}; do
#extract the SCSI ID and use it to check for valid vmdk disk
SCSI_ID=$(echo ${DISK%%.*})
grep -i "^${SCSI_ID}.present" "${VMX_PATH}" | grep -i "true" > /dev/null 2>&1
#if valid, then we use the vmdk file
if [[ $? -eq 0 ]]; then
#verify disk is not independent
grep -i "^${SCSI_ID}.mode" "${VMX_PATH}" | grep -i "independent" > /dev/null 2>&1
if [[ $? -eq 1 ]]; then
grep -i "^${SCSI_ID}.deviceType" "${VMX_PATH}" | grep -i "scsi-hardDisk" > /dev/null 2>&1
#if we find the device type is of scsi-disk, then proceed
if [[ $? -eq 0 ]]; then
DISK=$(grep -i "^${SCSI_ID}.fileName" "${VMX_PATH}" | awk -F "\"" '{print $2}')
echo "${DISK}" | grep "\/vmfs\/volumes" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
DISK_SIZE_IN_SECTORS=$(cat "${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
else
DISK_SIZE_IN_SECTORS=$(cat "${VMX_DIR}/${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
fi
DISK_SIZE=$(echo "${DISK_SIZE_IN_SECTORS}" | awk '{printf "%.0f\n",$1*512/1024/1024/1024}')
VMDKS="${DISK}###${DISK_SIZE}:${VMDKS}"
TOTAL_VM_SIZE=$((TOTAL_VM_SIZE+DISK_SIZE))
else
#if the deviceType is NULL for IDE which it is, thanks for the inconsistency VMware
#we'll do one more level of verification by checking to see if an ext. of .vmdk exists
#since we can not rely on the deviceType showing "ide-hardDisk"
grep -i "^${SCSI_ID}.fileName" "${VMX_PATH}" | grep -i ".vmdk" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
DISK=$(grep -i "^${SCSI_ID}.fileName" "${VMX_PATH}" | awk -F "\"" '{print $2}')
echo "${DISK}" | grep "\/vmfs\/volumes" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
DISK_SIZE_IN_SECTORS=$(cat "${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
else
DISK_SIZE_IN_SECTORS=$(cat "${VMX_DIR}/${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
fi
DISK_SIZE=$(echo "${DISK_SIZE_IN_SECTORS}" | awk '{printf "%.0f\n",$1*512/1024/1024/1024}')
VMDKS="${DISK}###${DISK_SIZE}:${VMDKS}"
TOTAL_VM_SIZE=$((TOTAL_VM_SIZE_IN+DISK_SIZE))
fi
fi
else
#independent disks are not affected by snapshots, hence they can not be backed up
DISK=$(grep -i "^${SCSI_ID}.fileName" "${VMX_PATH}" | awk -F "\"" '{print $2}')
echo "${DISK}" | grep "\/vmfs\/volumes" > /dev/null 2>&1
if [[ $? -eq 0 ]]; then
DISK_SIZE_IN_SECTORS=$(cat "${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
else
DISK_SIZE_IN_SECTORS=$(cat "${VMX_DIR}/${DISK}" | grep "VMFS" | grep ".vmdk" | awk '{print $2}')
fi
DISK_SIZE=$(echo "${DISK_SIZE_IN_SECTORS}" | awk '{printf "%.0f\n",$1*512/1024/1024/1024}')
INDEP_VMDKS="${DISK}###${DISK_SIZE}:${INDEP_VMDKS}"
fi
fi
done
IFS=${TMP_IFS}
logger "debug" "getVMDKs() - ${VMDKS}"
}
dumpVMConfigurations() {
logger "info" "CONFIG - VERSION = ${VERSION_STRING}"
logger "info" "CONFIG - GHETTOVCB_PID = ${GHETTOVCB_PID}"
logger "info" "CONFIG - VM_BACKUP_VOLUME = ${VM_BACKUP_VOLUME}"
if [[ "${ENABLE_NON_PERSISTENT_NFS}" -eq 1 ]]; then
logger "info" "CONFIG - ENABLE_NON_PERSISTENT_NFS = ${ENABLE_NON_PERSISTENT_NFS}"
logger "info" "CONFIG - UNMOUNT_NFS = ${UNMOUNT_NFS}"
logger "info" "CONFIG - NFS_SERVER = ${NFS_SERVER}"
logger "info" "CONFIG - NFS_MOUNT = ${NFS_MOUNT}"
fi
logger "info" "CONFIG - VM_BACKUP_ROTATION_COUNT = ${VM_BACKUP_ROTATION_COUNT}"
logger "info" "CONFIG - VM_BACKUP_DIR_NAMING_CONVENTION = ${VM_BACKUP_DIR_NAMING_CONVENTION}"
logger "info" "CONFIG - DISK_BACKUP_FORMAT = ${DISK_BACKUP_FORMAT}"
logger "info" "CONFIG - POWER_VM_DOWN_BEFORE_BACKUP = ${POWER_VM_DOWN_BEFORE_BACKUP}"
logger "info" "CONFIG - ENABLE_HARD_POWER_OFF = ${ENABLE_HARD_POWER_OFF}"
logger "info" "CONFIG - ITER_TO_WAIT_SHUTDOWN = ${ITER_TO_WAIT_SHUTDOWN}"
logger "info" "CONFIG - POWER_DOWN_TIMEOUT = ${POWER_DOWN_TIMEOUT}"
logger "info" "CONFIG - SNAPSHOT_TIMEOUT = ${SNAPSHOT_TIMEOUT}"
logger "info" "CONFIG - ENABLE_COMPRESSION = ${ENABLE_COMPRESSION}"
logger "info" "CONFIG - LOG_LEVEL = ${LOG_LEVEL}"
logger "info" "CONFIG - BACKUP_LOG_OUTPUT = ${LOG_OUTPUT}"
logger "info" "CONFIG - VM_SNAPSHOT_MEMORY = ${VM_SNAPSHOT_MEMORY}"
logger "info" "CONFIG - VM_SNAPSHOT_QUIESCE = ${VM_SNAPSHOT_QUIESCE}"
logger "info" "CONFIG - ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP = ${ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP}"
logger "info" "CONFIG - VMDK_FILES_TO_BACKUP = ${VMDK_FILES_TO_BACKUP}"
logger "info" "CONFIG - VM_SHUTDOWN_ORDER = ${VM_SHUTDOWN_ORDER}"
logger "info" "CONFIG - VM_STARTUP_ORDER = ${VM_STARTUP_ORDER}"
logger "info" "CONFIG - EMAIL_LOG = ${EMAIL_LOG}"
if [[ "${EMAIL_LOG}" -eq 1 ]]; then
logger "info" "CONFIG - EMAIL_SERVER = ${EMAIL_SERVER}"
logger "info" "CONFIG - EMAIL_SERVER_PORT = ${EMAIL_SERVER_PORT}"
logger "info" "CONFIG - EMAIL_DELAY_INTERVAL = ${EMAIL_DELAY_INTERVAL}"
logger "info" "CONFIG - EMAIL_FROM = ${EMAIL_FROM}"
logger "info" "CONFIG - EMAIL_TO = ${EMAIL_TO}"
logger "info" "CONFIG - WORKDIR_DEBUG = ${WORKDIR_DEBUG}"
fi
logger "info" ""
}
indexedRotate() {
local BACKUP_DIR_PATH=$1
local VM_TO_SEARCH_FOR=$2
#default rotation if variable is not defined
if [[ -z ${VM_BACKUP_ROTATION_COUNT} ]]; then
VM_BACKUP_ROTATION_COUNT=1
fi
#LIST_BACKUPS=$(ls -t "${BACKUP_DIR_PATH}" | grep "${VM_TO_SEARCH_FOR}-[0-9]*")
i=${VM_BACKUP_ROTATION_COUNT}
while [[ $i -ge 0 ]]; do
if [[ -f ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz ]]; then
if [[ $i -eq $((VM_BACKUP_ROTATION_COUNT-1)) ]]; then
rm -rf ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz
if [[ $? -eq 0 ]]; then
logger "info" "Deleted ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz"
else
logger "info" "Failure deleting ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz"
fi
else
mv -f ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1)).gz
if [[ $? -eq 0 ]]; then
logger "info" "Moved ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz to ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1)).gz"
else
logger "info" "Failure moving ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i.gz to ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1)).gz"
fi
fi
fi
if [[ -d ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i ]]; then
if [[ $i -eq $((VM_BACKUP_ROTATION_COUNT-1)) ]]; then
rm -rf ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i
if [[ $? -eq 0 ]]; then
logger "info" "Deleted ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i"
else
logger "info" "Failure deleting ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i"
fi
else
mv -f ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1))
if [[ $? -eq 0 ]]; then
logger "info" "Moved ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i to ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1))"
else
logger "info" "Failure moving ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i to ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$((i+1))"
fi
if [[ $i -eq 0 ]]; then
mkdir ${BACKUP_DIR_PATH}/${VM_TO_SEARCH_FOR}-$i
fi
fi
fi
i=$((i-1))
done
}
checkVMBackupRotation() {
local BACKUP_DIR_PATH=$1
local VM_TO_SEARCH_FOR=$2
#default rotation if variable is not defined
if [[ -z ${VM_BACKUP_ROTATION_COUNT} ]]; then
VM_BACKUP_ROTATION_COUNT=1
fi
LIST_BACKUPS=$(ls -t "${BACKUP_DIR_PATH}" | grep "${VM_TO_SEARCH_FOR}-[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}_[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}")
BACKUPS_TO_KEEP=$(ls -t "${BACKUP_DIR_PATH}" | grep "${VM_TO_SEARCH_FOR}-[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}_[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}" | head -"${VM_BACKUP_ROTATION_COUNT}")
ORIG_IFS=${IFS}
IFS='
'
for i in ${LIST_BACKUPS}; do
FOUND=0
for j in ${BACKUPS_TO_KEEP}; do
[[ $i == $j ]] && FOUND=1
done
if [[ $FOUND -eq 0 ]]; then
logger "debug" "Removing $BACKUP_DIR_PATH/$i"
rm -rf "$BACKUP_DIR_PATH/$i"
#NFS I/O error handling hack
if [[ $? -ne 0 ]] ; then
NFS_IO_HACK_COUNTER=0
NFS_IO_HACK_STATUS=0
NFS_IO_HACK_FILECHECK="$BACKUP_DIR_PATH/nfs_io.check"
while [[ "${NFS_IO_HACK_STATUS}" -eq 0 ]] && [[ "${NFS_IO_HACK_COUNTER}" -lt 60 ]]; do
sleep 1
NFS_IO_HACK_COUNTER=$((NFS_IO_HACK_COUNTER+1))
touch "${NFS_IO_HACK_FILECHECK}"
[[ $? -eq 0 ]] && NFS_IO_HACK_STATUS=1
done
rm -rf "${NFS_IO_HACK_FILECHECK}"
if [[ "${NFS_IO_HACK_STATUS}" -eq 1 ]] ; then
logger "info" "Slept ${NFS_IO_HACK_COUNTER} seconds to work around NFS I/O error"
else
logger "info" "Slept ${NFS_IO_HACK_COUNTER} seconds but failed work around for NFS I/O error"
fi
fi
fi
done
IFS=${ORIG_IFS}
}
storageInfo() {
SECTION=$1
#SOURCE DATASTORE
SRC_DATASTORE_CAPACITY=$($VMWARE_CMD hostsvc/datastore/info "${VMFS_VOLUME}" | grep -i "capacity" | awk '{print $3}' | sed 's/,//g')
SRC_DATASTORE_FREE=$($VMWARE_CMD hostsvc/datastore/info "${VMFS_VOLUME}" | grep -i "freespace" | awk '{print $3}' | sed 's/,//g')
SRC_DATASTORE_BLOCKSIZE=$($VMWARE_CMD hostsvc/datastore/info "${VMFS_VOLUME}" | grep -i blockSizeMb | awk '{print $3}' | sed 's/,//g')
if [[ -z ${SRC_DATASTORE_BLOCKSIZE} ]] ; then
SRC_DATASTORE_BLOCKSIZE="NA"
SRC_DATASTORE_MAX_FILE_SIZE="NA"
else
case ${SRC_DATASTORE_BLOCKSIZE} in
1)SRC_DATASTORE_MAX_FILE_SIZE="256 GB";;
2)SRC_DATASTORE_MAX_FILE_SIZE="512 GB";;
4)SRC_DATASTORE_MAX_FILE_SIZE="1024 GB";;
8)SRC_DATASTORE_MAX_FILE_SIZE="2048 GB";;
esac
fi
SRC_DATASTORE_CAPACITY_GB=$(echo "${SRC_DATASTORE_CAPACITY}" | awk '{printf "%.1f\n",$1/1024/1024/1024}')
SRC_DATASTORE_FREE_GB=$(echo "${SRC_DATASTORE_FREE}" | awk '{printf "%.1f\n",$1/1024/1024/1024}')
#DESTINATION DATASTORE
DST_VOL_1=$(echo "${VM_BACKUP_VOLUME#/*/*/}")
DST_DATASTORE=$(echo "${DST_VOL_1%%/*}")
DST_DATASTORE_CAPACITY=$($VMWARE_CMD hostsvc/datastore/info "${DST_DATASTORE}" | grep -i "capacity" | awk '{print $3}' | sed 's/,//g')
DST_DATASTORE_FREE=$($VMWARE_CMD hostsvc/datastore/info "${DST_DATASTORE}" | grep -i "freespace" | awk '{print $3}' | sed 's/,//g')
DST_DATASTORE_BLOCKSIZE=$($VMWARE_CMD hostsvc/datastore/info "${DST_DATASTORE}" | grep -i blockSizeMb | awk '{print $3}' | sed 's/,//g')
if [[ -z ${DST_DATASTORE_BLOCKSIZE} ]] ; then
DST_DATASTORE_BLOCKSIZE="NA"
DST_DATASTORE_MAX_FILE_SIZE="NA"
else
case ${DST_DATASTORE_BLOCKSIZE} in
1)DST_DATASTORE_MAX_FILE_SIZE="256 GB";;
2)DST_DATASTORE_MAX_FILE_SIZE="512 GB";;
4)DST_DATASTORE_MAX_FILE_SIZE="1024 GB";;
8)DST_DATASTORE_MAX_FILE_SIZE="2048 GB";;
esac
fi
DST_DATASTORE_CAPACITY_GB=$(echo "${DST_DATASTORE_CAPACITY}" | awk '{printf "%.1f\n",$1/1024/1024/1024}')
DST_DATASTORE_FREE_GB=$(echo "${DST_DATASTORE_FREE}" | awk '{printf "%.1f\n",$1/1024/1024/1024}')
logger "debug" "Storage Information ${SECTION} backup: "
logger "debug" "SRC_DATASTORE: ${VMFS_VOLUME}"
logger "debug" "SRC_DATASTORE_CAPACITY: ${SRC_DATASTORE_CAPACITY_GB} GB"
logger "debug" "SRC_DATASTORE_FREE: ${SRC_DATASTORE_FREE_GB} GB"
logger "debug" "SRC_DATASTORE_BLOCKSIZE: ${SRC_DATASTORE_BLOCKSIZE}"
logger "debug" "SRC_DATASTORE_MAX_FILE_SIZE: ${SRC_DATASTORE_MAX_FILE_SIZE}"
logger "debug" ""
logger "debug" "DST_DATASTORE: ${DST_DATASTORE}"
logger "debug" "DST_DATASTORE_CAPACITY: ${DST_DATASTORE_CAPACITY_GB} GB"
logger "debug" "DST_DATASTORE_FREE: ${DST_DATASTORE_FREE_GB} GB"
logger "debug" "DST_DATASTORE_BLOCKSIZE: ${DST_DATASTORE_BLOCKSIZE}"
logger "debug" "DST_DATASTORE_MAX_FILE_SIZE: ${DST_DATASTORE_MAX_FILE_SIZE}"
if [[ "${SRC_DATASTORE_BLOCKSIZE}" != "NA" ]] && [[ "${DST_DATASTORE_BLOCKSIZE}" != "NA" ]]; then
if [[ "${SRC_DATASTORE_BLOCKSIZE}" -lt "${DST_DATASTORE_BLOCKSIZE}" ]]; then
logger "debug" ""
logger "debug" "SRC VMFS blocksze of ${SRC_DATASTORE_BLOCKSIZE}MB is less than DST VMFS blocksize of ${DST_DATASTORE_BLOCKSIZE}MB which can be an issue for VM snapshots"
fi
fi
logger "debug" ""
}
powerOff() {
VM_NAME="$1"
VM_ID="$2"
POWER_OFF_EC=0
START_ITERATION=0
logger "info" "Powering off initiated for ${VM_NAME}, backup will not begin until VM is off..."
${VMWARE_CMD} vmsvc/power.shutdown ${VM_ID} > /dev/null 2>&1
while ${VMWARE_CMD} vmsvc/power.getstate ${VM_ID} | grep -i "Powered on" > /dev/null 2>&1; do
#enable hard power off code
if [[ ${ENABLE_HARD_POWER_OFF} -eq 1 ]] ; then
if [[ ${START_ITERATION} -ge ${ITER_TO_WAIT_SHUTDOWN} ]] ; then
logger "info" "Hard power off occured for ${VM_NAME}, waited for $((ITER_TO_WAIT_SHUTDOWN*60)) seconds"
${VMWARE_CMD} vmsvc/power.off ${VM_ID} > /dev/null 2>&1
#this is needed for ESXi, even the hard power off did not take affect right away
sleep 60
break
fi
fi
logger "info" "VM is still on - Iteration: ${START_ITERATION} - sleeping for 60secs (Duration: $((START_ITERATION*60)) seconds)"
sleep 60
#logic to not backup this VM if unable to shutdown
#after certain timeout period
if [[ ${START_ITERATION} -ge ${POWER_DOWN_TIMEOUT} ]] ; then
logger "info" "Unable to power off ${VM_NAME}, waited for $((POWER_DOWN_TIMEOUT*60)) seconds! Ignoring ${VM_NAME} for backup!"
POWER_OFF_EC=1
break
fi
START_ITERATION=$((START_ITERATION + 1))
done
if [[ ${POWER_OFF_EC} -eq 0 ]] ; then
logger "info" "VM is powerdOff"
fi
}
powerOn() {
VM_NAME="$1"
VM_ID="$2"
POWER_ON_EC=0
START_ITERATION=0
logger "info" "Powering on initiated for ${VM_NAME}"
${VMWARE_CMD} vmsvc/power.on ${VM_ID} > /dev/null 2>&1
while ${VMWARE_CMD} vmsvc/get.guest ${VM_ID} | grep -i "toolsNotRunning" > /dev/null 2>&1; do
logger "info" "VM is still not booted - Iteration: ${START_ITERATION} - sleeping for 60secs (Duration: $((START_ITERATION*60)) seconds)"
sleep 60
#logic to not backup this VM if unable to shutdown
#after certain timeout period
if [[ ${START_ITERATION} -ge ${POWER_DOWN_TIMEOUT} ]] ; then
logger "info" "Unable to detect started tools on ${VM_NAME}, waited for $((POWER_DOWN_TIMEOUT*60)) seconds!"
POWER_ON_EC=1
break
fi
START_ITERATION=$((START_ITERATION + 1))
done
if [[ ${POWER_ON_EC} -eq 0 ]] ; then
logger "info" "VM is powerdOn"
fi
}
ghettoVCB() {
VM_INPUT=$1
VM_OK=0
VM_FAILED=0
VMDK_FAILED=0
dumpHostInfo
if [[ ${ENABLE_NON_PERSISTENT_NFS} -eq 1 ]] ; then
VM_BACKUP_VOLUME="/vmfs/volumes/${NFS_LOCAL_NAME}/${NFS_VM_BACKUP_DIR}"
if [[ "${LOG_LEVEL}" != "dryrun" ]] ; then
#1 = readonly
#0 = readwrite
logger "debug" "Mounting NFS: ${NFS_SERVER}:${NFS_MOUNT} to /vmfs/volume/${NFS_LOCAL_NAME}"
${VMWARE_CMD} hostsvc/datastore/nas_create "${NFS_LOCAL_NAME}" "${NFS_SERVER}" "${NFS_MOUNT}" 0
fi
fi
captureDefaultConfigurations
if [[ "${USE_GLOBAL_CONF}" -eq 1 ]] ; then
logger "info" "CONFIG - USING GLOBAL GHETTOVCB CONFIGURATION FILE = ${GLOBAL_CONF}"
fi
if [[ "${USE_VM_CONF}" -eq 0 ]] ; then
dumpVMConfigurations
fi
#dump out all virtual machines allowing for spaces now
${VMWARE_CMD} vmsvc/getallvms | sed 's/[[:blank:]]\{3,\}/ /g' | fgrep "[" | fgrep "vmx-" | fgrep ".vmx" | fgrep "/" | awk -F' ' '{print "\""$1"\";\""$2"\";\""$3"\""}' | sed 's/\] /\]\";\"/g' > ${WORKDIR}/vms_list
if [[ "${BACKUP_ALL_VMS}" -eq 1 ]] ; then
${VMWARE_CMD} vmsvc/getallvms | sed 's/[[:blank:]]\{3,\}/ /g' | fgrep "[" | fgrep "vmx-" | fgrep ".vmx" | fgrep "/" | awk -F' ' '{print ""$2""}' | sed '/^$/d' > "${VM_INPUT}"
fi
ORIG_IFS=${IFS}
IFS='
'
if [[ ${#VM_SHUTDOWN_ORDER} -gt 0 ]] && [[ "${LOG_LEVEL}" != "dryrun" ]]; then
logger "debug" "VM Shutdown Order: ${VM_SHUTDOWN_ORDER}\n"
IFS2="${IFS}"
IFS=","
for VM_NAME in ${VM_SHUTDOWN_ORDER}; do
VM_ID=$(grep -E "\"${VM_NAME}\"" ${WORKDIR}/vms_list | awk -F ";" '{print $1}' | sed 's/"//g')
powerOff "${VM_NAME}" "${VM_ID}"
if [[ ${POWER_OFF_EC} -eq 1 ]]; then
logger "debug" "Error unable to shutdown VM ${VM_NAME}\n"
exit 1
fi
done
IFS="${IFS2}"
fi
for VM_NAME in $(cat "${VM_INPUT}" | grep -v "#" | sed '/^$/d' | sed -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//'); do
IGNORE_VM=0
if [[ "${EXCLUDE_SOME_VMS}" -eq 1 ]] ; then
grep -E "^${VM_NAME}" "${VM_EXCLUSION_FILE}" > /dev/null 2>&1
if [[ $? -eq 0 ]] ; then
IGNORE_VM=1
fi
fi
VM_ID=$(grep -E "\"${VM_NAME}\"" ${WORKDIR}/vms_list | awk -F ";" '{print $1}' | sed 's/"//g')
#ensure default value if one is not selected or variable is null
if [[ -z ${VM_BACKUP_DIR_NAMING_CONVENTION} ]] ; then
VM_BACKUP_DIR_NAMING_CONVENTION="$(date +%F_%k-%M-%S)"
fi
if [[ "${USE_VM_CONF}" -eq 1 ]] && [[ ! -z ${VM_ID} ]]; then
reConfigureBackupParam "${VM_NAME}"
dumpVMConfigurations
fi
VMFS_VOLUME=$(grep -E "\"${VM_NAME}\"" ${WORKDIR}/vms_list | awk -F ";" '{print $3}' | sed 's/\[//;s/\]//;s/"//g')
VMX_CONF=$(grep -E "\"${VM_NAME}\"" ${WORKDIR}/vms_list | awk -F ";" '{print $4}' | sed 's/\[//;s/\]//;s/"//g')
VMX_PATH="/vmfs/volumes/${VMFS_VOLUME}/${VMX_CONF}"
VMX_DIR=$(dirname "${VMX_PATH}")
#storage info
if [[ ! -z ${VM_ID} ]] && [[ "${LOG_LEVEL}" != "dryrun" ]]; then
storageInfo "before"
fi
#ignore VM as it's in the exclusion list
if [[ "${IGNORE_VM}" -eq 1 ]] ; then
logger "debug" "Ignoring ${VM_NAME} for backup since its located in exclusion list\n"
#checks to see if we can pull out the VM_ID
elif [[ -z ${VM_ID} ]] ; then
logger "info" "ERROR: failed to locate and extract VM_ID for ${VM_NAME}!\n"
VM_FAILED=1
elif [[ "${LOG_LEVEL}" == "dryrun" ]] ; then
logger "dryrun" "###############################################"
logger "dryrun" "Virtual Machine: $VM_NAME"
logger "dryrun" "VM_ID: $VM_ID"
logger "dryrun" "VMX_PATH: $VMX_PATH"
logger "dryrun" "VMX_DIR: $VMX_DIR"
logger "dryrun" "VMX_CONF: $VMX_CONF"
logger "dryrun" "VMFS_VOLUME: $VMFS_VOLUME"
logger "dryrun" "VMDK(s): "
TOTAL_VM_SIZE=0
getVMDKs
OLD_IFS="${IFS}"
IFS=":"
for j in ${VMDKS}; do
J_VMDK=$(echo "${j}" | awk -F "###" '{print $1}')
J_VMDK_SIZE=$(echo "${j}" | awk -F "###" '{print $2}')
logger "dryrun" "\t${J_VMDK}\t${J_VMDK_SIZE} GB"
done
HAS_INDEPENDENT_DISKS=0
logger "dryrun" "INDEPENDENT VMDK(s): "
for k in ${INDEP_VMDKS}; do
HAS_INDEPENDENT_DISKS=1
K_VMDK=$(echo "${k}" | awk -F "###" '{print $1}')
K_VMDK_SIZE=$(echo "${k}" | awk -F "###" '{print $2}')
logger "dryrun" "\t${K_VMDK}\t${K_VMDK_SIZE} GB"
done
IFS="${OLD_IFS}"
VMDKS=""
INDEP_VMDKS=""
logger "dryrun" "TOTAL_VM_SIZE_TO_BACKUP: ${TOTAL_VM_SIZE} GB"
if [[ ${HAS_INDEPENDENT_DISKS} -eq 1 ]] ; then
logger "dryrun" "Snapshots can not be taken for indepdenent disks!"
logger "dryrun" "THIS VIRTUAL MACHINE WILL NOT HAVE ALL ITS VMDKS BACKED UP!"
fi
ls "${VMX_DIR}" | grep -q "\-delta\.vmdk" > /dev/null 2>&1;
if [[ $? -eq 0 ]]; then
if [ ${ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP} -eq 0 ]; then
logger "dryrun" "Snapshots found for this VM, please commit all snapshots before continuing!"
logger "dryrun" "THIS VIRTUAL MACHINE WILL NOT BE BACKED UP DUE TO EXISTING SNAPSHOTS!"
else
logger "dryrun" "Snapshots found for this VM, ALL EXISTING SNAPSHOTS WILL BE CONSOLIDATED PRIOR TO BACKUP!"
fi
fi
if [[ ${TOTAL_VM_SIZE} -eq 0 ]] ; then
logger "dryrun" "THIS VIRTUAL MACHINE WILL NOT BE BACKED UP DUE TO EMPTY VMDK LIST!"
fi
logger "dryrun" "###############################################\n"
#checks to see if the VM has any snapshots to start with
#elif ls "${VMX_DIR}" | grep -q "\-delta\.vmdk" > /dev/null 2>&1; then
# logger "info" "Snapshot found for ${VM_NAME}, backup will not take place\n"
# VM_FAILED=1
elif [[ -f "${VMX_PATH}" ]] && [[ ! -z "${VMX_PATH}" ]]; then
if ls "${VMX_DIR}" | grep -q "\-delta\.vmdk" > /dev/null 2>&1; then
if [ ${ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP} -eq 0 ];then
logger "info" "Snapshot found for ${VM_NAME}, backup will not take place\n"
VM_FAILED=1
else
logger "info" "Snapshot found for ${VM_NAME}, consolidating ALL snapshots now (this can take awhile) ...\n"
$VMWARE_CMD vmsvc/snapshot.removeall ${VM_ID} > /dev/null 2>&1
fi
fi
#nfs case and backup to root path of your NFS mount
if [[ ${ENABLE_NON_PERSISTENT_NFS} -eq 1 ]] ; then
BACKUP_DIR="/vmfs/volumes/${NFS_LOCAL_NAME}/${NFS_VM_BACKUP_DIR}/${VM_NAME}"
if [[ -z ${VM_NAME} ]] || [[ -z ${NFS_LOCAL_NAME} ]] || [[ -z ${NFS_VM_BACKUP_DIR} ]]; then
logger "info" "ERROR: Variable BACKUP_DIR was not set properly, please ensure all required variables for non-persistent NFS backup option has been defined"
exit 1
fi
#non-nfs (SAN,LOCAL)
else
BACKUP_DIR="${VM_BACKUP_VOLUME}/${VM_NAME}"
if [[ -z ${VM_BACKUP_VOLUME} ]]; then
logger "info" "ERROR: Variable VM_BACKUP_VOLUME was not defined"
exit 1
fi
fi
#initial root VM backup directory
if [[ ! -d "${BACKUP_DIR}" ]] ; then
mkdir -p "${BACKUP_DIR}"
if [[ ! -d "${BACKUP_DIR}" ]] ; then
logger "info" "Unable to create \"${BACKUP_DIR}\"! - Ensure VM_BACKUP_VOLUME was defined correctly"
exit 1
fi
fi
# directory name of the individual Virtual Machine backup followed by naming convention followed by count
VM_BACKUP_DIR="${BACKUP_DIR}/${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}"
# Rsync relative path variable if needed
RSYNC_LINK_DIR="./${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}"
# Do indexed rotation if naming convention is set for it
if [[ ${VM_BACKUP_DIR_NAMING_CONVENTION} = "0" ]]; then
indexedRotate "${BACKUP_DIR}" "${VM_NAME}"
fi
mkdir -p "${VM_BACKUP_DIR}"
cp "${VMX_PATH}" "${VM_BACKUP_DIR}"
#new variable to keep track on whether VM has independent disks
VM_HAS_INDEPENDENT_DISKS=0
#extract all valid VMDK(s) from VM
getVMDKs
if [[ ! -z ${INDEP_VMDKS} ]] ; then
VM_HAS_INDEPENDENT_DISKS=1
fi
ORGINAL_VM_POWER_STATE=$(${VMWARE_CMD} vmsvc/power.getstate ${VM_ID} | tail -1)
CONTINUE_TO_BACKUP=1
#section that will power down a VM prior to taking a snapshot and backup and power it back on
if [[ ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]] ; then
powerOff "${VM_NAME}" "${VM_ID}"
if [[ ${POWER_OFF_EC} -eq 1 ]]; then
VM_FAILED=1
CONTINUE_TO_BACKUP=0
fi
fi
if [[ ${CONTINUE_TO_BACKUP} -eq 1 ]] ; then
logger "info" "Initiate backup for ${VM_NAME}"
startTimer
SNAP_SUCCESS=1
VM_VMDK_FAILED=0
#powered on VMs only
if [[ ! ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]] && [[ "${ORGINAL_VM_POWER_STATE}" != "Powered off" ]]; then
SNAPSHOT_NAME="ghettoVCB-snapshot-$(date +%F)"
logger "info" "Creating Snapshot \"${SNAPSHOT_NAME}\" for ${VM_NAME}"
${VMWARE_CMD} vmsvc/snapshot.create ${VM_ID} "${SNAPSHOT_NAME}" "${SNAPSHOT_NAME}" "${VM_SNAPSHOT_MEMORY}" "${VM_SNAPSHOT_QUIESCE}" > /dev/null 2>&1
logger "debug" "Waiting for snapshot \"${SNAPSHOT_NAME}\" to be created"
logger "debug" "Snapshot timeout set to: $((SNAPSHOT_TIMEOUT*60)) seconds"
START_ITERATION=0
while [[ $(${VMWARE_CMD} vmsvc/snapshot.get ${VM_ID} | wc -l) -eq 1 ]]; do
if [[ ${START_ITERATION} -ge ${SNAPSHOT_TIMEOUT} ]] ; then
logger "info" "Snapshot timed out, failed to create snapshot: \"${SNAPSHOT_NAME}\" for ${VM_NAME}"
SNAP_SUCCESS=0
echo "ERROR: Unable to backup ${VM_NAME} due to snapshot creation" >> ${VM_BACKUP_DIR}/STATUS.error
break
fi
logger "debug" "Waiting for snapshot creation to be completed - Iteration: ${START_ITERATION} - sleeping for 60secs (Duration: $((START_ITERATION*30)) seconds)"
sleep 60
START_ITERATION=$((START_ITERATION + 1))
done
fi
if [[ ${SNAP_SUCCESS} -eq 1 ]] ; then
OLD_IFS="${IFS}"
IFS=":"