Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CP-46179 backport for deterministic backup VDI #5552

Merged
merged 4 commits into from
Apr 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ocaml/xapi-aux/dune
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
(name xapi_aux)

(libraries
uuidm
xapi-types
stdext
)
13 changes: 13 additions & 0 deletions ocaml/xapi-aux/uuidx.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
include Uuidm

module Hash = struct
(** Derive a deterministic UUID from a string: the same
string maps to the same UUID. We are using our own namespace; the
namespace is not a secret *)

let namespace =
let ns = "e93e0639-2bdb-4a59-8b46-352b3f408c19" in
Uuidm.(of_string ns |> Option.get)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Uuidx library is available as Uuid in yangtze. Patching it seems more involved than doing it here, so I think this change makes more sense than backporting it to https://github.com/xapi-project/xen-api-libs-transitional/blob/2.25-lcm/uuid/uuid.ml


let string str = Uuidm.v5 namespace str
end
11 changes: 11 additions & 0 deletions ocaml/xapi-aux/uuidx.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type t

val to_string : ?upper:bool -> t -> string

module Hash : sig
(** hash a string (deterministically) into a UUID. This uses
namespace UUID e93e0639-2bdb-4a59-8b46-352b3f408c19. *)

(* UUID Version 5 derived from argument string and namespace UUID *)
val string : string -> t
end
6 changes: 3 additions & 3 deletions ocaml/xapi/sm.ml
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ let sr_update dconf driver sr =
let call = Sm_exec.make_call ~sr_ref:sr dconf "sr_update" [] in
Sm_exec.parse_unit (Sm_exec.exec_xmlrpc (driver_filename driver) call)

let vdi_create dconf driver sr sm_config vdi_type size name_label
let vdi_create ?vdi_uuid dconf driver sr sm_config vdi_type size name_label
name_description metadata_of_pool is_a_snapshot snapshot_time snapshot_of
read_only =
debug "vdi_create" driver
@@ -144,8 +144,8 @@ let vdi_create dconf driver sr sm_config vdi_type size name_label
) ;
srmaster_only dconf ;
let call =
Sm_exec.make_call ~sr_ref:sr ~vdi_sm_config:sm_config ~vdi_type dconf
"vdi_create"
Sm_exec.make_call ?vdi_uuid ~sr_ref:sr ~vdi_sm_config:sm_config ~vdi_type
dconf "vdi_create"
[
sprintf "%Lu" size
; name_label
22 changes: 19 additions & 3 deletions ocaml/xapi/sm_exec.ml
Original file line number Diff line number Diff line change
@@ -65,8 +65,8 @@ type call = {
}

let make_call ?driver_params ?sr_sm_config ?vdi_sm_config ?vdi_type
?vdi_location ?new_uuid ?sr_ref ?vdi_ref (subtask_of, device_config) cmd
args =
?vdi_location ?new_uuid ?sr_ref ?vdi_ref ?vdi_uuid
(subtask_of, device_config) cmd args =
Server_helpers.exec_with_new_task "sm_exec" (fun __context ->
(* Only allow a subset of calls if the SR has been introduced by a DR task. *)
Option.iter
@@ -113,7 +113,23 @@ let make_call ?driver_params ?sr_sm_config ?vdi_sm_config ?vdi_type
may (fun self -> Db.VDI.get_location ~__context ~self) vdi_ref
in
let vdi_uuid =
may (fun self -> Db.VDI.get_uuid ~__context ~self) vdi_ref
let __FUNCTION__ = "Sm_exec.make_call" in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why redefining __FUNCTION__?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not defined in the compiler version on Yangtze. So now I don't have to change the code in the back port.

match (cmd, vdi_ref, vdi_uuid) with
| "vdi_create", None, (Some x as uuid) ->
debug "%s: cmd=%s vdi_uuid=%s" __FUNCTION__ cmd x ;
uuid
(* when creating a VDI we sometimes want to provide the UUID
rather than letting the backend pick one. This is to
support backup VDIs CP-46179. So in that case, use the
provided UUID but not for other commands *)
| _, None, Some uuid ->
warn "%s: cmd=%s vdi_uuid=%s - should not happen" __FUNCTION__ cmd
uuid ;
None
| _, Some self, _ ->
Db.VDI.get_uuid ~__context ~self |> Option.some
| _, None, None ->
None
in
let vdi_on_boot =
may
25 changes: 20 additions & 5 deletions ocaml/xapi/storage_access.ml
Original file line number Diff line number Diff line change
@@ -688,16 +688,31 @@ module SMAPIv1 = struct
vdi_info_from_db ~__context (Db.VDI.get_by_uuid ~__context ~uuid)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this storage_access.ml file corresponds to storage_smapiv1.ml in master, no smapiv3 back then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

let create context ~dbg ~sr ~vdi_info =
let __FUNCTION__ = "Storage_access.create" in
try
Server_helpers.exec_with_new_task "VDI.create"
~subtask_of:(Ref.of_string dbg) (fun __context ->
let sr = Db.SR.get_by_uuid ~__context ~uuid:(s_of_sr sr) in
let sr_uuid = s_of_sr sr in
let sr = Db.SR.get_by_uuid ~__context ~uuid:sr_uuid in
let vi =
(* we want to set vdi_uuid when creating a backup VDI with
a specific UUID. SM picks up vdi_uuid instead of creating
a new random UUID; Cf. Xapi_vdi.create *)
let vdi_uuid =
match vdi_info.uuid with
| Some uuid when uuid = Uuidx.(Hash.string sr_uuid |> to_string)
->
info "%s: creating a backup VDI %s" __FUNCTION__ uuid ;
vdi_info.uuid
| _ ->
None
in
Sm.call_sm_functions ~__context ~sR:sr (fun device_config _type ->
Sm.vdi_create device_config _type sr vdi_info.sm_config
vdi_info.ty vdi_info.virtual_size vdi_info.name_label
vdi_info.name_description vdi_info.metadata_of_pool
vdi_info.is_a_snapshot vdi_info.snapshot_time
Sm.vdi_create ?vdi_uuid device_config _type sr
vdi_info.sm_config vdi_info.ty vdi_info.virtual_size
vdi_info.name_label vdi_info.name_description
vdi_info.metadata_of_pool vdi_info.is_a_snapshot
vdi_info.snapshot_time
(s_of_vdi vdi_info.snapshot_of)
vdi_info.read_only
)
17 changes: 16 additions & 1 deletion ocaml/xapi/xapi_vdi.ml
Original file line number Diff line number Diff line change
@@ -595,6 +595,7 @@ let update_vdi_db ~__context ~sr newvdi =

let create ~__context ~name_label ~name_description ~sR ~virtual_size ~_type
~sharable ~read_only ~other_config ~xenstore_data ~sm_config ~tags =
let __FUNCTION__ = "Xapi_vdi.create" in
if _type = `cbt_metadata then (
error
"VDI.create: creation of VDIs with type cbt_metadata is not allowed (at \
@@ -634,13 +635,27 @@ let create ~__context ~name_label ~name_description ~sR ~virtual_size ~_type
| `cbt_metadata ->
"cbt_metadata"
in
(* special case: we want to use a specific UUID for Pool Meta Data
Backup *)
let uuid_ =
match (_type, name_label) with
| `user, "Pool Metadata Backup" ->
let sr = Db.SR.get_uuid ~__context ~self:sR in
let uuid = Uuidx.(Hash.string sr |> to_string) in
info "%s: using deterministic UUID for '%s' VDI: %s" __FUNCTION__
name_label uuid ;
Some uuid
| _ ->
None
in
let open Storage_access in
let task = Context.get_task_id __context in
let open Storage_interface in
let vdi_info =
{
Storage_interface.default_vdi_info with
name_label
uuid= uuid_
; name_label
; name_description
; ty= vdi_type
; read_only
155 changes: 97 additions & 58 deletions scripts/xe-backup-metadata
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ function usage {
echo " -k: Number of older backups to preserve (default: ${history_kept})"
echo " -n: Just try to find a backup VDI and stop the script after that"
echo " -f Force backup even when less than 10% free capacity is left on the backup VDI"
echo " -y: Assume non-interactive mode and yes to all questions"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing hash of original commit in the commit message: 4fe8ffa

echo " -v: Verbose output"
echo
echo
@@ -48,10 +49,33 @@ function usage {
exit 1
}

function uuid5 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing hash of original commit in the commit message: 084c6a9

# could use a modern uuidgen but it's not on XS 8
# should work with Python 2 and 3
python -c "import uuid; print (uuid.uuid5(uuid.UUID('$1'), '$2'))"
}

function validate_vdi_uuid {
# we check that vdi has the expected UUID which depends on the UUID of
# the SR. This is a deterministic hash of the SR UUID and the
# namespace UUID $NS. This UUID must match what Xapi's Uuidx module is using.
local NS="e93e0639-2bdb-4a59-8b46-352b3f408c19"
local sr="$1"
local vdi="$2"
local uuid

uuid=$(uuid5 "$NS" "$sr")
if [ "$vdi" != "$uuid" ]; then
return 1
else
return 0
fi
}

function test_sr {
sr_uuid_found=$(${XE} sr-list uuid="$1" --minimal)
if [ "${sr_uuid_found}" != "$1" ]; then
echo Invalid SR UUID specified: $1
echo "Invalid SR UUID specified: $1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing hash of original commit in the commit message: f88fa12

usage
fi
}
@@ -63,7 +87,8 @@ just_find_vdi=0
fs_uninitialised=0
usage_alert=90
force_backup=0
while getopts "hvink:u:dcf" opt ; do
yes=0
while getopts "yhvink:u:dcf" opt ; do
case $opt in
h) usage ;;
c) create_vdi=1 ; fs_uninitialised=1 ;;
@@ -73,6 +98,7 @@ while getopts "hvink:u:dcf" opt ; do
d) leave_mounted=1 ;;
n) just_find_vdi=1 ;;
v) debug="" ;;
y) yes=1 ;;
f) force_backup=1 ;;
*) echo "Invalid option"; usage ;;
esac
@@ -89,32 +115,32 @@ fi
# determine if the SR UUID is vaid
if [ -z "${sr_uuid}" ]; then
# use the default-SR from the pool
sr_uuid=$(${XE} pool-param-get uuid=${pool_uuid} param-name=default-SR)
sr_uuid=$(${XE} pool-param-get uuid="${pool_uuid}" param-name=default-SR)
fi
test_sr "${sr_uuid}"

sr_name=$(${XE} sr-param-get uuid=${sr_uuid} param-name=name-label)
sr_name=$(${XE} sr-param-get uuid="${sr_uuid}" param-name=name-label)
# see if a backup VDI already exists on the selected SR
vdi_uuid=$(${XE} vdi-list other-config:ctxs-pool-backup=true sr-uuid=${sr_uuid} params=uuid --minimal)
vdi_uuid=$(${XE} vdi-list other-config:ctxs-pool-backup=true sr-uuid="${sr_uuid}" params=uuid --minimal)

mnt=
function cleanup {
trap "" TERM INT
cd /
if [ ! -z "${mnt}" ]; then
umount ${mnt} >/dev/null 2>&1
rmdir ${mnt}
umount "${mnt}" >/dev/null 2>&1
rmdir "${mnt}"
fi

if [ ! -z "${vbd_uuid}" ]; then
${debug} echo -n "Unplugging VBD: "
${XE} vbd-unplug uuid=${vbd_uuid} timeout=20
${XE} vbd-unplug uuid="${vbd_uuid}" timeout=20
# poll for the device to go away if we know its name
if [ "${device}" != "" ]; then
device_gone=0
for ((i=0; i<10; i++)); do
${debug} echo -n "."
if [ ! -b ${device} ]; then
if [ ! -b "${device}" ]; then
${debug} echo " done"
device_gone=1
break
@@ -123,22 +149,35 @@ function cleanup {
done
if [ ${device_gone} -eq 0 ]; then
${debug} echo " failed"
echo Please destroy VBD ${vbd_uuid} manually.
echo "Please destroy VBD ${vbd_uuid} manually."
else
${XE} vbd-destroy uuid=${vbd_uuid}
${XE} vbd-destroy uuid="${vbd_uuid}"
fi
fi
fi
if [ ${fs_uninitialised} -eq 1 -a -n "${vdi_uuid}" ] ; then
${XE} vdi-destroy uuid=${vdi_uuid}
${XE} vdi-destroy uuid="${vdi_uuid}"
fi
}

echo Using SR: ${sr_name}
# if we can't validate the UUID of the VDI, prompt the user
if [ -n "${vdi_uuid}" ]; then
if ! validate_vdi_uuid "${sr_uuid}" "${vdi_uuid}" && [ "$yes" -eq 0 ]; then
echo "Backup VDI $vdi_uuid was most likley create by an earlier"
echo "version of this code. Make sure this is a VDI that you"
echo "created as we can't validate it without mounting it."
read -p "Continue? [Y/N]" -n 1 -r; echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
fi
fi

echo "Using SR: ${sr_name}"
if [ -z "${vdi_uuid}" ]; then
if [ "${create_vdi}" -gt 0 ]; then
echo -n "Creating new backup VDI: "
vdi_uuid=$(${XE} vdi-create virtual-size=500MiB sr-uuid=${sr_uuid} type=user name-label="Pool Metadata Backup")
label="Pool Metadata Backup"
# the label must match what xapi_vdi.ml is using for backup VDIs
vdi_uuid=$(${XE} vdi-create virtual-size=500MiB sr-uuid="${sr_uuid}" type=user name-label="${label}")
init_fs=1
if [ $? -ne 0 ]; then
echo failed
@@ -148,8 +187,8 @@ if [ -z "${vdi_uuid}" ]; then
echo "Backup VDI not found, aborting. You can initialise one using the '-c' flag."
exit 3
fi
echo ${vdi_uuid}
${XE} vdi-param-set uuid=${vdi_uuid} other-config:ctxs-pool-backup=true
echo "${vdi_uuid}"
${XE} vdi-param-set uuid="${vdi_uuid}" other-config:ctxs-pool-backup=true
else
${debug} echo "Using existing backup VDI: ${vdi_uuid}"
fs_uninitialised=0
@@ -160,124 +199,124 @@ if [ ${just_find_vdi} -gt 0 ]; then
fi

${debug} echo -n "Creating VBD: "
vbd_uuid=$(${XE} vbd-create vm-uuid=${CONTROL_DOMAIN_UUID} vdi-uuid=${vdi_uuid} device=autodetect)
${debug} echo ${vbd_uuid}
vbd_uuid=$(${XE} vbd-create vm-uuid="${CONTROL_DOMAIN_UUID}" vdi-uuid="${vdi_uuid}" device=autodetect)
${debug} echo "${vbd_uuid}"


if [ $? -ne 0 -o -z "${vbd_uuid}" ]; then
echo error creating VBD
echo "error creating VBD"
cleanup
exit 1
fi

${debug} echo -n "Plugging VBD: "
${XE} vbd-plug uuid=${vbd_uuid}
device=/dev/$(${XE} vbd-param-get uuid=${vbd_uuid} param-name=device)
${XE} vbd-plug uuid="${vbd_uuid}"
device=/dev/$(${XE} vbd-param-get uuid="${vbd_uuid}" param-name=device)

if [ ! -b ${device} ]; then
${debug} echo ${device}: not a block special
if [ ! -b "${device}" ]; then
${debug} echo "${device}: not a block special"
cleanup
exit 1
fi

${debug} echo ${device}
${debug} echo "${device}"

if [ $init_fs -eq 1 ]; then
if [ "$init_fs" -eq 1 ]; then
${debug} echo -n "Creating filesystem: "
mkfs.ext3 -j -F ${device} > /dev/null 2>&1
mkfs.ext3 -j -F "${device}" > /dev/null 2>&1
${debug} echo "done"
fs_uninitialised=0
fi

${debug} echo -n "Mounting filesystem: "
mnt=/var/run/pool-backup-${vdi_uuid}
mkdir -p ${mnt}
mnt="/var/run/pool-backup-${vdi_uuid}"
mkdir -p "${mnt}"

/sbin/fsck -a ${device} >/dev/null 2>&1
/sbin/fsck -a "${device}" >/dev/null 2>&1
if [ $? -ne 0 ]; then
${debug} fsck failed. Please correct manually
cleanup
exit 1
fi

mount ${device} ${mnt} > /dev/null 2>&1
mount "${device}" "${mnt}" > /dev/null 2>&1
if [ $? -ne 0 ]; then
${debug} echo failed
cleanup
exit 1
fi
${debug} echo ${mnt}
${debug} echo "${mnt}"

if [ ${leave_mounted} -eq 0 ]; then
lrconf=${mnt}/conf/${vdi_uuid}
if [ ! -f ${lrconf} ]; then
lrconf="${mnt}/conf/${vdi_uuid}"
if [ ! -f "${lrconf}" ]; then
${debug} echo -n "Initialising rotation: "
mkdir -p ${mnt}/conf/
echo "${mnt}/${pool_uuid}.db {" >> ${lrconf}
echo " rotate ${history_kept}" >> ${lrconf}
echo " missingok" >> ${lrconf}
echo "}" >> ${lrconf}
echo done
echo ${metadata_version} >> ${mnt}/.ctxs-metadata-backup
mkdir -p "${mnt}/conf/"
echo "${mnt}/${pool_uuid}.db {" >> "${lrconf}"
echo " rotate ${history_kept}" >> "${lrconf}"
echo " missingok" >> "${lrconf}"
echo "}" >> "${lrconf}"
echo "done"
echo "${metadata_version}" >> "${mnt}/.ctxs-metadata-backup"
fi

# check the usage of the backup VDI
usage=`cd ${mnt} && df . | sed -n "2p" | awk '{ print $5 }' | tr -d '%'`
usage=`cd "${mnt}" && df . | sed -n "2p" | awk '{ print $5 }' | tr -d '%'`
echo "Checking backup VDI space usage: $usage%"
if [ $usage -gt $usage_alert ] && [ ${force_backup} -eq 0 ]; then
echo "Running out of space, you can use "-d" option to attach VDI and free more space, exit now."
if [ "$usage" -gt "$usage_alert" ] && [ "${force_backup}" -eq 0 ]; then
echo "Running out of space, you can use '-d' option to attach VDI and free more space, exit now."
cleanup
exit 1
fi

# invoke logrotate to rotate over old pool db backups
echo -n "Rotating old backups: "
logrotate -f ${lrconf}
num_found=$(find ${mnt} -name \*.db\.* | wc -l)
echo found ${num_found}
logrotate -f "${lrconf}"
num_found=$(find "${mnt}" -name '*.db.*' | wc -l)
echo "found ${num_found}"

# perform the pool database dump
echo -n "Backing up pool database: "
${XE} pool-dump-database file-name=${mnt}/${pool_uuid}.db
${XE} pool-dump-database file-name="${mnt}/${pool_uuid}.db"
echo done

# backup the VM metadata for each VM in the pool into a dated directory
datetime=$(date +%F-%H-%M-%S)
metadir=${mnt}/metadata/${datetime}
mkdir -p ${metadir}
metadir="${mnt}/metadata/${datetime}"
mkdir -p "${metadir}"
echo -n "Cleaning old VM metadata: "
IFS=" "
todelete=$(cd ${mnt}/metadata && ls -1 |sort -n | head -n -${history_kept} | xargs echo)
todelete=$(cd "${mnt}/metadata" && ls -1 |sort -n | head -n -${history_kept} | xargs echo)
for dir in ${todelete}; do
rm -rf ${mnt}/metadata/${dir}
rm -rf "${mnt}/metadata/${dir}"
done
echo done
IFS=","
echo -n "Backing up SR metadata: "
mkdir -p ${metadir}
"@LIBEXECDIR@/backup-sr-metadata.py" -f ${metadir}/SRMETA.xml
mkdir -p "${metadir}"
"@LIBEXECDIR@/backup-sr-metadata.py" -f "${metadir}/SRMETA.xml"
echo "done"

echo -n "Backing up VM metadata: "
${debug} echo ""
mkdir -p ${metadir}/all
mkdir -p "${metadir}/all"
for vmuuid in $(${XE} vm-list params=uuid is-control-domain=false --minimal); do
${debug} echo -n .
${XE} vm-export --metadata uuid=${vmuuid} filename=${metadir}/all/${vmuuid}.vmmeta >/dev/null 2>&1
${XE} vm-export --metadata uuid="${vmuuid}" filename="${metadir}/all/${vmuuid}.vmmeta" >/dev/null 2>&1
done
echo "done"
echo -n "Backing up Template metadata: "
${debug} echo ""
template_uuids=$("@LIBEXECDIR@/print-custom-templates")
if [ $? -eq 0 ]; then
for tmpl_uuid in ${template_uuids}; do
${XE} template-export --metadata template-uuid=${tmpl_uuid} filename=${metadir}/all/${tmpl_uuid}.vmmeta >/dev/null 2>&1
${XE} template-export --metadata template-uuid="${tmpl_uuid}" filename="${metadir}/all/${tmpl_uuid}.vmmeta" >/dev/null 2>&1
done
fi
echo "done"
"@LIBEXECDIR@/link-vms-by-sr.py" -d ${metadir}
"@LIBEXECDIR@/link-vms-by-sr.py" -d "${metadir}"
else
cd ${mnt}
cd "${mnt}"
env PS1="Mounted backup VDI on: ${mnt}\nPress ^D to exit shell and safely detach it.\n\n[\u@\h \W]\$ " bash
fi

133 changes: 77 additions & 56 deletions scripts/xe-restore-metadata
Original file line number Diff line number Diff line change
@@ -55,11 +55,18 @@ function usage {
function test_sr {
sr_uuid_found=$(${XE} sr-list uuid="$1" --minimal)
if [ "${sr_uuid_found}" != "$1" ]; then
echo Invalid SR UUID specified: $1
echo "Invalid SR UUID specified: $1"
usage
fi
}

# name space to hash SRs for a deterministic VDI UUID
NS="e93e0639-2bdb-4a59-8b46-352b3f408c19"
function uuid5 {
# could use a modern uuidgen but it's not on XS 8
python -c "import uuid; print (uuid.uuid5(uuid.UUID('$1'), '$2'))"
}

dry_run=0
sr_uuid=
yes=0
@@ -102,35 +109,42 @@ fi
# determine if the SR UUID is vaid
if [ -z "${sr_uuid}" ]; then
# use the default-SR from the pool
sr_uuid=$(${XE} pool-param-get uuid=${pool_uuid} param-name=default-SR)
sr_uuid=$(${XE} pool-param-get uuid="${pool_uuid}" param-name=default-SR)
fi
test_sr "${sr_uuid}"

sr_name=$(${XE} sr-param-get uuid=${sr_uuid} param-name=name-label)
sr_name=$(${XE} sr-param-get uuid="${sr_uuid}" param-name=name-label)

# probe first for a VDI with known UUID derived from the SR to avoid
# scanning for a VDI
backup_vdi=$(uuid5 "${NS}" "${sr_uuid}")
if [ -z "${vdis}" ]; then
vdis=$(${XE} vdi-list uuid="${backup_vdi}" sr-uuid="${sr_uuid}" read-only=false --minimal)
fi

# get a list of all VDIs if an override has not been provided on the cmd line
if [ -z "${vdis}" ]; then
vdis=$(${XE} vdi-list params=uuid sr-uuid=${sr_uuid} read-only=false --minimal)
vdis=$(${XE} vdi-list params=uuid sr-uuid="${sr_uuid}" read-only=false --minimal)
fi

mnt=
function cleanup {
cd /
if [ ! -z "${mnt}" ]; then
umount ${mnt} >/dev/null 2>&1
rmdir ${mnt}
umount "${mnt}" >/dev/null 2>&1
rmdir "${mnt}"
mnt=""
fi

if [ ! -z "${vbd_uuid}" ]; then
${debug} echo -n "Unplugging VBD: " >&2
${XE} vbd-unplug uuid=${vbd_uuid} timeout=20
${XE} vbd-unplug uuid="${vbd_uuid}" timeout=20
# poll for the device to go away if we know its name
if [ "${device}" != "" ]; then
device_gone=0
for ((i=0; i<10; i++)); do
${debug} echo -n "." >&2
if [ ! -b ${device} ]; then
if [ ! -b "${device}" ]; then
${debug} echo " done" >&2
device_gone=1
break
@@ -139,9 +153,9 @@ function cleanup {
done
if [ ${device_gone} -eq 0 ]; then
${debug} echo " failed" >&2
${debug} echo Please destroy VBD ${vbd_uuid} manually. >&2
${debug} echo "Please destroy VBD ${vbd_uuid} manually." >&2
else
${XE} vbd-destroy uuid=${vbd_uuid}
${XE} vbd-destroy uuid="${vbd_uuid}"
vbd_uuid=""
fi
fi
@@ -150,46 +164,53 @@ function cleanup {
}

if [ -z "${vdis}" ]; then
echo No VDIs found on SR. >&2
echo "No VDIs found on SR." >&2
exit 0
fi

trap cleanup SIGINT ERR

for vdi_uuid in ${vdis}; do
if [ "${vdi_uuid}" != "${backup_vdi}" ] && [ "$yes" -eq 0 ]; then
echo "Probing VDI ${vdi_uuid}."
echo "This VDI was created with a prior version of this code."
echo "Its validity can't be checked without mounting it first."
read -p "Continue? [Y/N]" -n 1 -r; echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
fi

${debug} echo -n "Creating VBD: " >&2
vbd_uuid=$(${XE} vbd-create vm-uuid=${CONTROL_DOMAIN_UUID} vdi-uuid=${vdi_uuid} device=autodetect 2>/dev/null)
vbd_uuid=$(${XE} vbd-create vm-uuid="${CONTROL_DOMAIN_UUID}" vdi-uuid="${vdi_uuid}" device=autodetect 2>/dev/null)

if [ $? -ne 0 -o -z "${vbd_uuid}" ]; then
${debug} echo error creating VBD for VDI ${vdi_uuid} >&2
${debug} echo "error creating VBD for VDI ${vdi_uuid}" >&2
cleanup
continue
fi

${debug} echo ${vbd_uuid} >&2
${debug} echo "${vbd_uuid}" >&2

${debug} echo -n "Plugging VBD: " >&2
${XE} vbd-plug uuid=${vbd_uuid}
device=/dev/$(${XE} vbd-param-get uuid=${vbd_uuid} param-name=device)
${XE} vbd-plug uuid="${vbd_uuid}"
device=/dev/$(${XE} vbd-param-get uuid="${vbd_uuid}" param-name=device)

if [ ! -b ${device} ]; then
${debug} echo ${device}: not a block special >&2
if [ ! -b "${device}" ]; then
${debug} echo "${device}: not a block special" >&2
cleanup
continue
fi

${debug} echo ${device} >&2
${debug} echo "${device}" >&2

${debug} echo -n "Probing device: " >&2
mnt=
if [ "$(file_exists "${device}" "/.ctxs-metadata-backup")" = y ]; then
${debug} echo found metadata backup >&2
${debug} echo -n "Mounting filesystem: " >&2
mnt=/var/run/pool-backup-${vdi_uuid}
mkdir -p ${mnt}
${debug} echo "found metadata backup" >&2
mnt="/var/run/pool-backup-${vdi_uuid}"
mkdir -p "${mnt}"
/sbin/e2fsck -p -f "${device}" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo File system integrity error. Please correct manually. >&2
echo "File system integrity error. Please correct manually." >&2
cleanup
continue
fi
@@ -199,91 +220,91 @@ for vdi_uuid in ${vdis}; do
cleanup
else
if [ -e "${mnt}/.ctxs-metadata-backup" ]; then
${debug} echo Found backup metadata on VDI: ${vdi_uuid} >&2
xe vdi-param-set uuid=${vdi_uuid} other-config:ctxs-pool-backup=true
${debug} echo "Found backup metadata on VDI: ${vdi_uuid}" >&2
xe vdi-param-set uuid="${vdi_uuid}" other-config:ctxs-pool-backup=true
break
fi
fi
else
${debug} echo backup metadata not found >&2
${debug} echo "backup metadata not found" >&2
fi
cleanup
done

if [ $just_probe -gt 0 ]; then
echo ${vdi_uuid}
echo "${vdi_uuid}"
cleanup
exit 0
fi

cd ${mnt}
cd "${mnt}"
${debug} echo "" >&2

if [ ! -d ${mnt}/metadata ]; then
echo Metadata backups not found. >&2
if [ ! -d "${mnt}/metadata" ]; then
echo "Metadata backups not found." >&2
cleanup
exit 1
fi

cd ${mnt}/metadata
cd "${mnt}/metadata"

if [ $just_list_dates -gt 0 ]; then
ls -1r ${mnt}/metadata
if [ "$just_list_dates" -gt 0 ]; then
ls -1r "${mnt}/metadata"
cleanup
exit 0
fi

if [ -z "${chosen_date}" ]; then
chosen_metadata_dir=$(ls | sort -n | tail -1)
if [ -z "${chosen_metadata_dir}" ]; then
echo No VM metadata backups found in ${mnt}/metadata >&2
echo "No VM metadata backups found in ${mnt}/metadata" >&2
cleanup
exit 1
fi
else
if [ ! -d ${mnt}/metadata/${chosen_date} ]; then
echo Date directory "${chosen_date}" not found >&2
if [ ! -d "${mnt}/metadata/${chosen_date}" ]; then
echo "Date directory ${chosen_date} not found" >&2
cleanup
exit 1
fi
chosen_metadata_dir=${chosen_date}
chosen_metadata_dir="${chosen_date}"
fi

case ${restore_mode} in
sr)
full_dir=${mnt}/metadata/${chosen_metadata_dir}/by-sr/${sr_uuid}
full_dir="${mnt}/metadata/${chosen_metadata_dir}/by-sr/${sr_uuid}"
;;
all)
full_dir=${mnt}/metadata/${chosen_metadata_dir}/all
full_dir="${mnt}/metadata/${chosen_metadata_dir}/all"
;;
esac

if [ ! -d ${full_dir} ]; then
echo No VM metadata exports were found for the selected SR >&2
if [ ! -d "${full_dir}" ]; then
echo "No VM metadata exports were found for the selected SR" >&2
cleanup
exit 1
fi

${debug} echo Selected: ${full_dir}
${debug} echo "Selected: ${full_dir}"

cd ${full_dir}
cd "${full_dir}"
${debug} echo "" >&2
${debug} echo Latest VM metadata found is: >&2
${debug} echo "Latest VM metadata found is": >&2
${debug} ls >&2

if [ $yes -eq 0 ]; then
if [ "$yes" -eq 0 ]; then
echo "Do you wish to reimport all VM metadata?"
echo "Please type in "yes" and <enter> to continue."
echo "Please type in 'yes' and <enter> to continue."
read response
if [ "$response" != "yes" ]; then
echo Aborting metadata restore.
echo "Aborting metadata restore."
cleanup
exit 1
fi
fi

${debug} echo "" >&2
${debug} echo Restoring VM metadata: >&2
${debug} echo "Restoring VM metadata:" >&2

trap - ERR

@@ -302,30 +323,30 @@ else
fi
shopt -s nullglob
for meta in *.vmmeta; do
echo xe vm-import filename=${meta} sr-uuid=${sr_uuid} --metadata --preserve${force_flag}${dry_run_flag}
"@BINDIR@/xe" vm-import filename="${full_dir}/${meta}" sr-uuid=${sr_uuid} --metadata --preserve${force_flag}${dry_run_flag}
echo xe vm-import filename="${meta}" sr-uuid="${sr_uuid}" --metadata --preserve"${force_flag}""${dry_run_flag}"
"@BINDIR@/bin/xe" vm-import filename="${full_dir}/${meta}" sr-uuid="${sr_uuid}" --preserve"${force_flag}""${dry_run_flag}"
if [ $? -gt 0 ]; then
error_count=$(( $error_count + 1 ))
else
good_count=$(( $good_count + 1 ))
fi
done

smmeta_file=${mnt}/metadata/${chosen_metadata_dir}/SRMETA.xml
smmeta_file="${mnt}/metadata/${chosen_metadata_dir}/SRMETA.xml"
if [ "$restore_mode" == "all" ]; then
cmd="@LIBEXECDIR@/restore-sr-metadata.py -f ${smmeta_file}"
else
cmd="@LIBEXECDIR@/restore-sr-metadata.py -u ${sr_uuid} -f ${smmeta_file}"
fi

if [ -e ${smmeta_file} ]; then
if [ ${dry_run} -gt 0 ]; then
echo ${cmd}
if [ -e "${smmeta_file}" ]; then
if [ "${dry_run}" -gt 0 ]; then
echo "${cmd}"
else
${cmd}
fi
fi

echo "Restored ${good_count} VM(s), and ${error_count} error(s)"
cleanup
exit ${error_count}
exit "${error_count}"