mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-25 02:50:08 +03:00
F OpenNebula/one#6265: Delete of snapshots in the middle of the chain
- Add suport for delete snapshots in the middle of the chain in qcow2 (ALLOW_ORPHANS=NO). Example, snapshot 2 below: 0 <- 1 <- 2 <- 3 - It supports removing the last snapshot and active (blockpull) and first one (snapshot 0). The first snapshot cannot be deleted in persistent images because of the underlying link setting. - Snapshots are deleted by blockcommit. For example to delete 2, 3 is committed into 2: 0 <- 1 <- 2: (2+3) A new file 3.current is created in the filesystem to look for the actual file name supporting snapshot 3. - qcow2/shared operations snap_delete, snap_revert and cpds (disk-saveas) has been updated to react to the .current files - The kvm/deploy action has been updated to resolve links in source file attributes. This is needed so the <backingStore> of the libvirt domain does not contain the sysmlinks that changes on the revert/create operations. - The kvm/migrate action also includes a minor modification to resolv the VM disk - Snapshots with more than 1 child (or their relatives) cannot be deleted to not break backing chains. - It has been found that the snap-delete operation in suspend state is insecure as the guest caches may not be updated accordingly. The operation is now not allowed in this state. - The associated state has been removed from oned. It needs to be removed in CLI/Sunstone and APIs - The changes are for VM disk-snapshots. A similar update can be made for images in the datastore/fs snap_delete action. This operation is not implemented in this commit. Note: Output of xmllint may or may not include new lines when multiple matches of an xpath expression occurs. Parsing of xmllint output adds additional new lines to deal with different versions.
This commit is contained in:
parent
1606a1c53e
commit
156cd04ba5
@ -139,12 +139,16 @@ public:
|
||||
int create_snapshot(const std::string& name, long long size_mb);
|
||||
|
||||
/**
|
||||
* Check if an snapshot can be deleted (no children, no active)
|
||||
* Check if an snapshot can be deleted, for the snap_delete operation on
|
||||
* VMs
|
||||
* @param id of the snapshot
|
||||
* @param error if any
|
||||
* @return true if can be deleted, false otherwise
|
||||
*/
|
||||
bool test_delete(int id, std::string& error) const;
|
||||
bool test_delete(int id, bool persistent, std::string& error) const;
|
||||
|
||||
// Version for the snap_delete operation on images
|
||||
bool test_delete_image(int id, std::string& error) const;
|
||||
|
||||
/**
|
||||
* Removes the snapshot from the list
|
||||
@ -173,8 +177,8 @@ public:
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
active = -1;
|
||||
disk_id = -1;
|
||||
active = -1;
|
||||
_disk_id = -1;
|
||||
|
||||
snapshot_template.clear();
|
||||
snapshot_pool.clear();
|
||||
@ -183,15 +187,25 @@ public:
|
||||
/**
|
||||
* Return the disk_id of the snapshot list
|
||||
*/
|
||||
int get_disk_id() const
|
||||
int disk_id() const
|
||||
{
|
||||
return disk_id;
|
||||
return _disk_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disk id for this snapshot list
|
||||
* @param did the id
|
||||
*/
|
||||
void set_disk_id(int did)
|
||||
{
|
||||
_disk_id = did;
|
||||
snapshot_template.replace("DISK_ID", did);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the active snapshot id
|
||||
*/
|
||||
int get_active_id() const
|
||||
int active_id() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
@ -201,20 +215,10 @@ public:
|
||||
*/
|
||||
void clear_disk_id()
|
||||
{
|
||||
disk_id = -1;
|
||||
_disk_id = -1;
|
||||
snapshot_template.erase("DISK_ID");
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the disk id for this snapshot list
|
||||
* @param did the id
|
||||
*/
|
||||
void set_disk_id(int did)
|
||||
{
|
||||
disk_id = did;
|
||||
snapshot_template.replace("DISK_ID", did);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return number of snapshots in the list
|
||||
*/
|
||||
@ -223,6 +227,11 @@ public:
|
||||
return snapshot_pool.size();
|
||||
};
|
||||
|
||||
unsigned int next() const
|
||||
{
|
||||
return next_snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if snapshot_pool is empty
|
||||
*/
|
||||
@ -246,14 +255,25 @@ public:
|
||||
/**
|
||||
* @return total snapshot size (virtual) in mb
|
||||
*/
|
||||
long long get_total_size() const;
|
||||
long long total_size() const;
|
||||
|
||||
/**
|
||||
* Get the size (virtual) in mb of the given snapshot
|
||||
* @param id of the snapshot
|
||||
* @return size or 0 if not found
|
||||
*/
|
||||
long long get_snapshot_size(int id) const;
|
||||
long long snapshot_size(int id) const;
|
||||
|
||||
/**
|
||||
* Return Snapshot children
|
||||
* @param if of the snapshot
|
||||
* @param children the attribute string
|
||||
* @return the number of children
|
||||
* -1 No snapshot
|
||||
* 0 CHILDREN not defined
|
||||
* N number of children in "0,2,3,5" ---> 4
|
||||
*/
|
||||
int children(int id, std::string& children) const;
|
||||
|
||||
/**
|
||||
* Get Attribute from the given snapshot
|
||||
@ -262,7 +282,7 @@ public:
|
||||
*
|
||||
* @return value or empty if not found
|
||||
*/
|
||||
std::string get_snapshot_attribute(int id, const char* name) const;
|
||||
std::string snapshot_attribute(int id, const char* name) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -317,7 +337,7 @@ private:
|
||||
/**
|
||||
* Id of the disk associated with this snapshot list
|
||||
*/
|
||||
int disk_id;
|
||||
int _disk_id;
|
||||
|
||||
/**
|
||||
* Allow to remove parent snapshots and active one
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
DISK_SNAPSHOT_REVERT_POWEROFF = 52,
|
||||
DISK_SNAPSHOT_DELETE_POWEROFF = 53,
|
||||
DISK_SNAPSHOT_SUSPENDED = 54,
|
||||
DISK_SNAPSHOT_REVERT_SUSPENDED = 55,
|
||||
//DISK_SNAPSHOT_REVERT_SUSPENDED = 55,
|
||||
DISK_SNAPSHOT_DELETE_SUSPENDED = 56,
|
||||
DISK_SNAPSHOT = 57,
|
||||
//DISK_SNAPSHOT_REVERT = 58,
|
||||
|
@ -210,7 +210,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return snapshots->get_total_size();
|
||||
return snapshots->total_size();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,7 +225,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return snapshots->get_snapshot_size(snap_id);
|
||||
return snapshots->snapshot_size(snap_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2202,8 +2202,7 @@ int DispatchManager::disk_snapshot_revert(int vid, int did, int snap_id,
|
||||
VirtualMachine::VmState state = vm->get_state();
|
||||
VirtualMachine::LcmState lstate = vm->get_lcm_state();
|
||||
|
||||
if ((state !=VirtualMachine::POWEROFF || lstate !=VirtualMachine::LCM_INIT)&&
|
||||
(state !=VirtualMachine::SUSPENDED|| lstate !=VirtualMachine::LCM_INIT))
|
||||
if (state != VirtualMachine::POWEROFF || lstate != VirtualMachine::LCM_INIT)
|
||||
{
|
||||
oss << "Could not revert to disk snapshot for VM " << vid
|
||||
<< ", wrong state " << vm->state_str() << ".";
|
||||
@ -2236,20 +2235,8 @@ int DispatchManager::disk_snapshot_revert(int vid, int did, int snap_id,
|
||||
|
||||
close_cp_history(vmpool, vm.get(), VMActions::DISK_SNAPSHOT_REVERT_ACTION, ra);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case VirtualMachine::POWEROFF:
|
||||
vm->set_state(VirtualMachine::ACTIVE);
|
||||
vm->set_state(VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF);
|
||||
break;
|
||||
|
||||
case VirtualMachine::SUSPENDED:
|
||||
vm->set_state(VirtualMachine::ACTIVE);
|
||||
vm->set_state(VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
vm->set_state(VirtualMachine::ACTIVE);
|
||||
vm->set_state(VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF);
|
||||
|
||||
vmpool->update(vm.get());
|
||||
|
||||
@ -2305,7 +2292,14 @@ int DispatchManager::disk_snapshot_delete(int vid, int did, int snap_id,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!snaps->test_delete(snap_id, error_str))
|
||||
const VirtualMachineDisk * disk = vm->get_disk(did);
|
||||
|
||||
if (disk == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!snaps->test_delete(snap_id, disk->is_persistent(), error_str))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ void DispatchManager::trigger_suspend_success(int vid)
|
||||
vm->get_lcm_state() == VirtualMachine::PROLOG_MIGRATE_SUSPEND ||
|
||||
vm->get_lcm_state() == VirtualMachine::PROLOG_MIGRATE_SUSPEND_FAILURE||
|
||||
vm->get_lcm_state() == VirtualMachine::DISK_SNAPSHOT_SUSPENDED ||
|
||||
vm->get_lcm_state() == VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED||
|
||||
vm->get_lcm_state() == VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED))
|
||||
{
|
||||
VirtualMachineTemplate quota_tmpl;
|
||||
|
@ -607,7 +607,7 @@ void Image::disk_attribute(VirtualMachineDisk * disk,
|
||||
disk->replace("SIZE", size_mb);
|
||||
}
|
||||
|
||||
snap_size = snapshots.get_total_size();
|
||||
snap_size = snapshots.total_size();
|
||||
disk->replace("DISK_SNAPSHOT_TOTAL_SIZE", snap_size);
|
||||
|
||||
// Force FORMAT and DRIVER from image
|
||||
|
@ -601,7 +601,7 @@ int ImageManager::delete_image(int iid, string& error_str)
|
||||
|
||||
int uid = img->get_uid();
|
||||
int gid = img->get_gid();
|
||||
long long size = img->get_size() + img->get_snapshots().get_total_size();
|
||||
long long size = img->get_size() + img->get_snapshots().total_size();
|
||||
|
||||
img.reset();
|
||||
|
||||
@ -1200,7 +1200,7 @@ int ImageManager::delete_snapshot(int iid, int sid, string& error)
|
||||
|
||||
const Snapshots& snaps = img->get_snapshots();
|
||||
|
||||
if (!snaps.test_delete(sid, error))
|
||||
if (!snaps.test_delete_image(sid, error))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ void ImageManager::_rm(unique_ptr<image_msg_t> msg)
|
||||
gid = image->get_gid();
|
||||
source = image->get_source();
|
||||
size = image->get_size();
|
||||
snap_size = (image->get_snapshots()).get_total_size();
|
||||
snap_size = (image->get_snapshots()).total_size();
|
||||
|
||||
if ( image->get_type() == Image::BACKUP )
|
||||
{
|
||||
@ -680,7 +680,7 @@ void ImageManager::_snap_delete(unique_ptr<image_msg_t> msg)
|
||||
ds_id = image->get_ds_id();
|
||||
uid = image->get_uid();
|
||||
gid = image->get_gid();
|
||||
snap_size = (image->get_snapshots()).get_snapshot_size(snap_id);
|
||||
snap_size = (image->get_snapshots()).snapshot_size(snap_id);
|
||||
|
||||
image->delete_snapshot(snap_id);
|
||||
}
|
||||
@ -795,7 +795,7 @@ void ImageManager::_snap_flatten(unique_ptr<image_msg_t> msg)
|
||||
ds_id = image->get_ds_id();
|
||||
uid = image->get_uid();
|
||||
gid = image->get_gid();
|
||||
snap_size = (image->get_snapshots()).get_total_size();
|
||||
snap_size = (image->get_snapshots()).total_size();
|
||||
|
||||
image->clear_snapshots();
|
||||
}
|
||||
|
@ -1135,7 +1135,6 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose,
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE:
|
||||
vm->clear_snapshot_disk();
|
||||
@ -1509,7 +1508,6 @@ void LifeCycleManager::recover(VirtualMachine * vm, bool success,
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE:
|
||||
@ -1747,7 +1745,6 @@ void LifeCycleManager::retry(VirtualMachine * vm)
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE:
|
||||
@ -1867,7 +1864,6 @@ void LifeCycleManager::trigger_updatesg(int sgid)
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::HOTPLUG_SAVEAS_POWEROFF:
|
||||
case VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED:
|
||||
|
@ -1961,7 +1961,6 @@ void LifeCycleManager::trigger_disk_snapshot_success(int vid)
|
||||
break;
|
||||
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
vm->log("LCM", Log::INFO, "VM disk snapshot operation completed.");
|
||||
vm->revert_disk_snapshot(disk_id, snap_id, true);
|
||||
break;
|
||||
@ -2046,7 +2045,6 @@ void LifeCycleManager::trigger_disk_snapshot_success(int vid)
|
||||
break;
|
||||
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
dm->trigger_suspend_success(vid);
|
||||
break;
|
||||
@ -2117,7 +2115,6 @@ void LifeCycleManager::trigger_disk_snapshot_failure(int vid)
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
vm->log("LCM", Log::ERROR, "VM disk snapshot operation failed.");
|
||||
break;
|
||||
|
||||
@ -2191,7 +2188,6 @@ void LifeCycleManager::trigger_disk_snapshot_failure(int vid)
|
||||
break;
|
||||
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
dm->trigger_suspend_success(vid);
|
||||
break;
|
||||
|
@ -94,7 +94,7 @@ unique_ptr<PoolObjectSQL> RequestManagerChown::get_and_quota(
|
||||
auto tmpl = make_unique<Template>();
|
||||
|
||||
tmpl->add("DATASTORE", img->get_ds_id());
|
||||
tmpl->add("SIZE",img->get_size()+img->get_snapshots().get_total_size());
|
||||
tmpl->add("SIZE",img->get_size()+img->get_snapshots().total_size());
|
||||
|
||||
quota_map.insert(make_pair(Quotas::DATASTORE, move(tmpl)));
|
||||
}
|
||||
|
@ -104,7 +104,6 @@ void TransferManager::_transfer(unique_ptr<transfer_msg_t> msg)
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE:
|
||||
lcm->trigger_disk_snapshot_success(id);
|
||||
@ -175,7 +174,6 @@ void TransferManager::_transfer(unique_ptr<transfer_msg_t> msg)
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF:
|
||||
case VirtualMachine::DISK_SNAPSHOT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
case VirtualMachine::DISK_SNAPSHOT_DELETE:
|
||||
lcm->trigger_disk_snapshot_failure(id);
|
||||
|
@ -98,7 +98,7 @@ if [ "${LCM_STATE}" = '26' ] && [ "${VM_MAD}" != "kvm" ]; then
|
||||
fi
|
||||
|
||||
if [ "$FORMAT" = "qcow2" ]; then
|
||||
CP_CMD="$QEMU_IMG convert $SRC_PATH -O qcow2 $DST_PATH"
|
||||
CP_CMD="$QEMU_IMG convert \${SRC_PATH} -O qcow2 $DST_PATH"
|
||||
else
|
||||
CP_CMD="cp -f $SRC_PATH $DST_PATH"
|
||||
fi
|
||||
@ -131,10 +131,16 @@ fi
|
||||
CPDS_CMD=$(cat <<EOF
|
||||
set -e -o pipefail
|
||||
|
||||
SRC_READLN=\$($READLINK -f $SRC_PATH)
|
||||
if [ -f "${SRC_PATH}.current" ]; then
|
||||
SRC_PATH=\$(cat ${SRC_PATH}.current)
|
||||
else
|
||||
SRC_PATH="${SRC_PATH}"
|
||||
fi
|
||||
|
||||
SRC_READLN=\$($READLINK -f \${SRC_PATH})
|
||||
DST_READLN=\$($READLINK -f $DST_PATH)
|
||||
|
||||
if [ \( -L $SRC_PATH \) -a \( "\$SRC_READLN" = "\$DST_READLN" \) ] ; then
|
||||
if [ \( -L \${SRC_PATH} \) -a \( "\$SRC_READLN" = "\$DST_READLN" \) ] ; then
|
||||
echo "Not moving files to image repo, they are the same"
|
||||
else
|
||||
${FREEZE_CP_CMD}
|
||||
|
@ -92,7 +92,7 @@ if [ "${LCM_STATE}" = '26' ] && [ "${VM_MAD}" != "kvm" ]; then
|
||||
fi
|
||||
|
||||
if [ "$FORMAT" = "qcow2" ]; then
|
||||
CP_CMD="$QEMU_IMG convert $SRC_PATH -O qcow2 $DST_PATH"
|
||||
CP_CMD="$QEMU_IMG convert \${SRC_PATH} -O qcow2 $DST_PATH"
|
||||
else
|
||||
CP_CMD="cp -f $SRC_PATH $DST_PATH"
|
||||
fi
|
||||
@ -124,10 +124,16 @@ fi
|
||||
CPDS_CMD=$(cat <<EOF
|
||||
set -e -o pipefail
|
||||
|
||||
SRC_READLN=\$($READLINK -f $SRC_PATH)
|
||||
if [ -f "${SRC_PATH}.current" ]; then
|
||||
SRC_PATH=\$(cat ${SRC_PATH}.current)
|
||||
else
|
||||
SRC_PATH="${SRC_PATH}"
|
||||
fi
|
||||
|
||||
SRC_READLN=\$($READLINK -f \${SRC_PATH})
|
||||
DST_READLN=\$($READLINK -f $DST_PATH)
|
||||
|
||||
if [ \( -L $SRC_PATH \) -a \( "\$SRC_READLN" = "\$DST_READLN" \) ] ; then
|
||||
if [ \( -L \${SRC_PATH} \) -a \( "\$SRC_READLN" = "\$DST_READLN" \) ] ; then
|
||||
echo "Not moving files to image repo, they are the same"
|
||||
else
|
||||
${FREEZE_CP_CMD}
|
||||
|
@ -77,7 +77,7 @@ mkdir -p "${SNAP_DIR}"
|
||||
|
||||
if [ ! -L "${SNAP_DIR}/${SNAP_NAME}" ]; then
|
||||
# backing file traversar link
|
||||
ln -s . "${SNAP_DIR}/${SNAP_NAME}" # backing file traversar link
|
||||
ln -s . "${SNAP_DIR}/${SNAP_NAME}"
|
||||
fi
|
||||
|
||||
SNAP="\$(ls ${SNAP_DIR} | grep '^[[:digit:]]*$' | sort -n | tail -n 1 || :)"
|
||||
|
@ -30,11 +30,225 @@ fi
|
||||
|
||||
DRIVER_PATH=$(dirname $0)
|
||||
|
||||
. $TMCOMMON
|
||||
source $TMCOMMON
|
||||
source ${DRIVER_PATH}/../../etc/vmm/kvm/kvmrc
|
||||
|
||||
SRC_PATH=$(arg_path $SRC)
|
||||
SRC_HOST=$(arg_host $SRC)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Backing File managment functions:
|
||||
# - scan_backing_files creates an associative map with snap_id -> backing file
|
||||
# - is_parent returns the child (if any) of a given snapshot (uses the map)
|
||||
# - do_live checks if virsh or qemu-img should be use (a running VM can delete
|
||||
# snapshots outside of current backingstore, e.g. because a revert)
|
||||
# - delete removes the snapshot from the backing chain using qemu-img rebase
|
||||
# virsh blockcommit or blockpull as needed.
|
||||
# ------------------------------------------------------------------------------
|
||||
scan_backing_files() {
|
||||
local -n bfs=$1
|
||||
local snap_dir=$2
|
||||
|
||||
for i in $(ls "${snap_dir}"); do
|
||||
f=$(realpath -s "${snap_dir}/${i}")
|
||||
|
||||
[ ! -f ${f} ] && continue
|
||||
[[ "${f}" =~ \.current ]] && continue
|
||||
|
||||
bf=$(qemu-img info -U --output json "${f}" | jq -r '."backing-filename"')
|
||||
ky="${f##*/}"
|
||||
|
||||
if [ "${bf:0:1}" = "/" ] ; then
|
||||
bfs["${ky}"]="${bf}"
|
||||
elif [ "${bf}" = "null" ]; then
|
||||
bfs["${ky}"]="null"
|
||||
else
|
||||
bfs["${ky}"]="${snap_dir}/${bf##*/}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
is_parent() {
|
||||
local -n bfs=$1
|
||||
local snap=$(echo "$2/$3" | tr -s '/')
|
||||
|
||||
local child=""
|
||||
|
||||
for file in "${!bfs[@]}"; do
|
||||
bfile=$(echo "${bfs[$file]}" | tr -s '/')
|
||||
|
||||
if [ "${snap}" = "${bfile}" -o "${snap}" = $(realpath ${bfile}) ]; then
|
||||
child="${file}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${child}"
|
||||
}
|
||||
|
||||
do_live() {
|
||||
local domid=$1
|
||||
local state=$2
|
||||
|
||||
local snap_id=$3
|
||||
local luri=$4
|
||||
|
||||
local found=""
|
||||
local dbs=()
|
||||
|
||||
if [ "${state}" != "59" ]; then
|
||||
echo "${found}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
bs=$(virsh -c "${luri}" dumpxml "${domid}" | \
|
||||
xmllint --xpath '//backingStore/source/@file' - | \
|
||||
sed -e 's/file=/\n/g' | tr -d '"')
|
||||
|
||||
while IFS= read -r f; do
|
||||
f=$(echo ${f} | sed 's/[[:space:]]*$//')
|
||||
|
||||
id="${f##*/}"
|
||||
dbs+=("${id}")
|
||||
|
||||
if [ "${id}" = "${snap_id}" ]; then
|
||||
found="${snap_id}"
|
||||
fi
|
||||
done <<< "${bs}"
|
||||
|
||||
echo "${found}"
|
||||
}
|
||||
|
||||
get_current_path() {
|
||||
local snap_dir=$1
|
||||
local snap_id=$2
|
||||
|
||||
local current=""
|
||||
|
||||
for i in $(ls ${snap_dir}/*.current); do
|
||||
current=$(cat "${i}")
|
||||
|
||||
#This snapshot represent another one (is in a .current file)
|
||||
if [ "${current##*/}" = "${snap_id}" ]; then
|
||||
echo "${i}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${snap_dir}/${snap_id}.current"
|
||||
}
|
||||
|
||||
delete_snapshot() {
|
||||
declare -gA backing_files
|
||||
|
||||
local dpath=$1
|
||||
local target=$2
|
||||
local snap_dir=$3
|
||||
local snap_id=$4
|
||||
local vm_id=$5
|
||||
local vm_st=$6
|
||||
local active=$7
|
||||
local luri=$8
|
||||
local qemu=$9
|
||||
|
||||
scan_backing_files backing_files "${snap_dir}"
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Re-define snap_id if current exists and set snap_path
|
||||
# --------------------------------------------------------------------------
|
||||
local current=""
|
||||
local cmd=""
|
||||
local rm_current=""
|
||||
|
||||
if [ -f "${snap_dir}/${snap_id}.current" ]; then
|
||||
rm_current="rm ${snap_dir}/${snap_id}.current"
|
||||
|
||||
current=$(cat "${snap_dir}/${snap_id}.current")
|
||||
snap_id="${current##*/}"
|
||||
fi
|
||||
|
||||
local snap_path="${snap_dir}/${snap_id}"
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Set base snapshot for the delete operation
|
||||
# --------------------------------------------------------------------------
|
||||
local base="${backing_files[$snap_id]}"
|
||||
|
||||
local vbase=""
|
||||
local qbase=""
|
||||
|
||||
if [ "${base}" = "null" ]; then
|
||||
vbase=""
|
||||
qbase='-b ""'
|
||||
else
|
||||
vbase="--base ${base}"
|
||||
qbase="-b ${base}"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Set child snapshot & live mode for the delete operation
|
||||
# --------------------------------------------------------------------------
|
||||
local child=$(is_parent backing_files "${snap_dir}" "${snap_id}")
|
||||
|
||||
local live=$(do_live "${vm_id}" "${vm_st}" "${snap_id}" "${luri}")
|
||||
|
||||
if [ "${active}" = "YES" ]; then
|
||||
# ----------------------------------------------------------------------
|
||||
# Active snapshot (last known by OpenNebula). Example, delete n:
|
||||
# n-1 <-- n <-- next
|
||||
# n-1 <-- next
|
||||
#
|
||||
# next = next + n. Remove n
|
||||
# ----------------------------------------------------------------------
|
||||
if [ -n "${live}" ]; then
|
||||
cmd="virsh -c ${luri} blockpull ${vm_id} ${target} ${vbase} --wait"
|
||||
else
|
||||
cmd="${qemu} rebase -q -U -F qcow2 ${qbase} ${dpath}"
|
||||
fi
|
||||
|
||||
cmd="${cmd}; rm ${snap_path}"
|
||||
elif [ -n "${child}" ]; then
|
||||
# ----------------------------------------------------------------------
|
||||
# Snapshot in the middle of a chain. Example, delete n:
|
||||
# n-1 <-- n <-- n+1
|
||||
# (live: commit n+1 into n) n-1 <-- n'
|
||||
# (poff: rebase n+1 to n-1) n-1 <-- n+1
|
||||
#
|
||||
# (live) n' = n + (n+1). (n+1).current = n. Remove n+1
|
||||
# (poff) Remove n
|
||||
# ----------------------------------------------------------------------
|
||||
if [ -n "${live}" ]; then
|
||||
local current_path=$(get_current_path ${snap_dir} ${child})
|
||||
|
||||
cmd="virsh -c ${luri} blockcommit ${vm_id} ${target} --top ${child} \
|
||||
--base ${snap_path} --wait"
|
||||
cmd="${cmd}; echo "${snap_path}" > ${current_path}"
|
||||
cmd="${cmd}; rm ${snap_dir}/${child}"
|
||||
else
|
||||
cmd="${qemu} rebase -q -U -F qcow2 ${qbase} ${snap_dir}/${child}"
|
||||
cmd="${cmd}; rm ${snap_path}"
|
||||
fi
|
||||
else
|
||||
# ----------------------------------------------------------------------
|
||||
# Snapshot has no children and not active. Example, delete n+1:
|
||||
# n-1 <-- n <-- n+1
|
||||
# \_ n+2 <-- next
|
||||
#
|
||||
# Remove n+1
|
||||
# ----------------------------------------------------------------------
|
||||
if [ -f "${snap_path}.current" ]; then
|
||||
cmd="rm ${current}"
|
||||
else
|
||||
cmd="rm ${snap_path}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Remove current pointer if exists
|
||||
cmd="${cmd}; ${rm_current}"
|
||||
|
||||
echo "${cmd}"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Get Image information
|
||||
#-------------------------------------------------------------------------------
|
||||
@ -49,14 +263,24 @@ while IFS= read -r -d '' element; do
|
||||
XPATH_ELEMENTS[i++]="$element"
|
||||
done < <(onevm show -x $VMID| $XPATH \
|
||||
/VM/TEMPLATE/DISK\[DISK_ID=$DISK_ID\]/SOURCE \
|
||||
/VM/TEMPLATE/DISK\[DISK_ID=$DISK_ID\]/CLONE)
|
||||
/VM/TEMPLATE/DISK\[DISK_ID=$DISK_ID\]/CLONE \
|
||||
/VM/TEMPLATE/DISK\[DISK_ID=$DISK_ID\]/TARGET \
|
||||
/VM/TEMPLATE/DISK\[DISK_ID=$DISK_ID\]/FORMAT \
|
||||
/VM/SNAPSHOTS/SNAPSHOT\[ID=$SNAP_ID\]/ACTIVE \
|
||||
/VM/DEPLOY_ID \
|
||||
/VM/LCM_STATE)
|
||||
|
||||
DISK_SRC="${XPATH_ELEMENTS[j++]}"
|
||||
CLONE="${XPATH_ELEMENTS[j++]}"
|
||||
TARGET="${XPATH_ELEMENTS[j++]}"
|
||||
FORMAT="${XPATH_ELEMENTS[j++]}"
|
||||
ACTIVE="${XPATH_ELEMENTS[j++]}"
|
||||
DEPLOY_ID="${XPATH_ELEMENTS[j++]}"
|
||||
LCM_STATE="${XPATH_ELEMENTS[j++]}"
|
||||
|
||||
SYSTEM_DS_PATH=$(dirname ${SRC_PATH})
|
||||
|
||||
if [ "$CLONE" = "YES" ]; then
|
||||
if [ "$CLONE" = "YES" -o ${0: -4} = ".ssh" ]; then
|
||||
DISK_PATH="${SYSTEM_DS_PATH}/disk.${DISK_ID}"
|
||||
else
|
||||
DISK_PATH=$DISK_SRC
|
||||
@ -65,6 +289,38 @@ fi
|
||||
SNAP_DIR="${DISK_PATH}.snap"
|
||||
SNAP_PATH="${SNAP_DIR}/${SNAP_ID}"
|
||||
|
||||
ssh_exec_and_log "${SRC_HOST}" "rm ${SNAP_PATH}" \
|
||||
DELETE_CMD=$(cat <<EOT
|
||||
set -e -o pipefail
|
||||
|
||||
$(type scan_backing_files | grep -v 'is a function')
|
||||
|
||||
$(type is_parent | grep -v 'is a function')
|
||||
|
||||
$(type do_live | grep -v 'is a function')
|
||||
|
||||
$(type get_current_path | grep -v 'is a function')
|
||||
|
||||
$(type delete_snapshot | grep -v 'is a function')
|
||||
|
||||
# DISK_RDLN is the path of the active file
|
||||
DISK_RDLN="\$(readlink ${SYSTEM_DS_PATH}/disk.${DISK_ID})"
|
||||
|
||||
if [ "\${DISK_RDLN:0:1}" != "/" ] ; then
|
||||
DISK_RDLN="${SYSTEM_DS_PATH}/\${DISK_RDLN}"
|
||||
fi
|
||||
|
||||
cmd=\$(delete_snapshot "\${DISK_RDLN}" "${TARGET}" "${SNAP_DIR}" "${SNAP_ID}" \
|
||||
"${DEPLOY_ID}" "${LCM_STATE}" "${ACTIVE}" "${LIBVIRT_URI}" "${QEMU_IMG}")
|
||||
|
||||
eval "\${cmd}"
|
||||
EOT
|
||||
)
|
||||
|
||||
# For disks using raw format just remove the snaphost file
|
||||
if [ "$FORMAT" = "raw" ]; then
|
||||
DELETE_CMD="rm \"${SNAP_PATH}\""
|
||||
fi
|
||||
|
||||
ssh_exec_and_log "${SRC_HOST}" "${DELETE_CMD}" \
|
||||
"Error deleting snapshot ${SNAP_PATH}"
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2023, OpenNebula Project, OpenNebula Systems #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
||||
# not use this file except in compliance with the License. You may obtain #
|
||||
# a copy of the License at #
|
||||
# #
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 #
|
||||
# #
|
||||
# Unless required by applicable law or agreed to in writing, software #
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, #
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
||||
# See the License for the specific language governing permissions and #
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
# snap_delete host:parent_image snap_id vmid ds_id
|
||||
|
||||
SRC=$1
|
||||
SNAP_ID=$2
|
||||
|
||||
if [ -z "${ONE_LOCATION}" ]; then
|
||||
TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh
|
||||
else
|
||||
TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh
|
||||
fi
|
||||
|
||||
|
||||
. $TMCOMMON
|
||||
|
||||
SRC_PATH=$(arg_path $SRC)
|
||||
SRC_HOST=$(arg_host $SRC)
|
||||
|
||||
DISK_ID=$(basename ${SRC} | cut -d. -f2)
|
||||
SYSTEM_DS_PATH=$(dirname ${SRC_PATH})
|
||||
|
||||
DISK_PATH="${SYSTEM_DS_PATH}/disk.${DISK_ID}"
|
||||
|
||||
SNAP_DIR="${DISK_PATH}.snap"
|
||||
SNAP_PATH="${SNAP_DIR}/${SNAP_ID}"
|
||||
|
||||
ssh_exec_and_log "${SRC_HOST}" "rm ${SNAP_PATH}" \
|
||||
"Error deleting snapshot ${SNAP_PATH}"
|
||||
|
1
src/tm_mad/qcow2/snap_delete.ssh
Symbolic link
1
src/tm_mad/qcow2/snap_delete.ssh
Symbolic link
@ -0,0 +1 @@
|
||||
snap_delete
|
@ -69,15 +69,23 @@ else
|
||||
fi
|
||||
|
||||
SNAP_PATH="${SNAP_DIR}/${SNAP_ID}"
|
||||
SNAP_PATH_SHORT="${SNAP_DIR_SHORT}/${SNAP_ID}"
|
||||
|
||||
if [ "$FORMAT" = "qcow2" ]; then
|
||||
SNAP_CMD=$(cat <<EOF
|
||||
set -e -o pipefail
|
||||
|
||||
if [ -f "${SNAP_PATH}.current" ]; then
|
||||
CURRENT_PATH=\$(cat "${SNAP_PATH}.current")
|
||||
CURRENT_ID="\${CURRENT_PATH##*/}"
|
||||
|
||||
SNAP_PATH_SHORT="${SNAP_DIR_SHORT}/\${CURRENT_ID}"
|
||||
else
|
||||
SNAP_PATH_SHORT="${SNAP_DIR_SHORT}/${SNAP_ID}"
|
||||
fi
|
||||
|
||||
cd "${SNAP_DIR}"
|
||||
qemu-img create -f qcow2 -o backing_fmt=qcow2 \
|
||||
-b "${SNAP_PATH_SHORT}" "\$(readlink -f ${DISK_PATH})"
|
||||
-b "\${SNAP_PATH_SHORT}" "\$(readlink -f ${DISK_PATH})"
|
||||
EOF
|
||||
)
|
||||
else
|
||||
|
@ -66,9 +66,18 @@ if [ "$FORMAT" = "qcow2" ]; then
|
||||
SNAP_CMD=$(cat <<EOF
|
||||
set -e -o pipefail
|
||||
|
||||
if [ -f "${SNAP_PATH}.current" ]; then
|
||||
CURRENT_PATH=\$(cat "${SNAP_PATH}.current")
|
||||
CURRENT_ID="\${CURRENT_PATH##*/}"
|
||||
|
||||
SNAP_PATH_SHORT="${SNAP_DIR_SHORT}/\${CURRENT_ID}"
|
||||
else
|
||||
SNAP_PATH_SHORT="${SNAP_DIR_SHORT}/${SNAP_ID}"
|
||||
fi
|
||||
|
||||
cd "${SNAP_DIR}"
|
||||
qemu-img create -f qcow2 -o backing_fmt=qcow2 \
|
||||
-b "${SNAP_PATH_SHORT}" "\$(readlink -f ${DISK_PATH})"
|
||||
-b "\${SNAP_PATH_SHORT}" "\$(readlink -f ${DISK_PATH})"
|
||||
EOF
|
||||
)
|
||||
else
|
||||
|
@ -13,6 +13,7 @@
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#include <algorithm>
|
||||
|
||||
#include "Snapshots.h"
|
||||
#include "NebulaUtil.h"
|
||||
@ -22,17 +23,17 @@ using namespace std;
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
Snapshots::Snapshots(int _disk_id, AllowOrphansMode _orphans):
|
||||
Snapshots::Snapshots(int __disk_id, AllowOrphansMode _orphans):
|
||||
snapshot_template(false,'=',"SNAPSHOTS"),
|
||||
next_snapshot(0),
|
||||
active(-1),
|
||||
disk_id(_disk_id),
|
||||
_disk_id(__disk_id),
|
||||
orphans(_orphans),
|
||||
current_base(-1)
|
||||
{
|
||||
if (_disk_id != -1)
|
||||
if (__disk_id != -1)
|
||||
{
|
||||
snapshot_template.add("DISK_ID", _disk_id);
|
||||
snapshot_template.add("DISK_ID", __disk_id);
|
||||
}
|
||||
|
||||
snapshot_template.add("ALLOW_ORPHANS", allow_orphans_mode_to_str(_orphans));
|
||||
@ -46,7 +47,7 @@ Snapshots::Snapshots(const Snapshots& s):
|
||||
snapshot_template(s.snapshot_template),
|
||||
next_snapshot(0),
|
||||
active(-1),
|
||||
disk_id(-1),
|
||||
_disk_id(-1),
|
||||
orphans(DENY),
|
||||
current_base(-1)
|
||||
{
|
||||
@ -59,7 +60,7 @@ Snapshots& Snapshots::operator= (const Snapshots& s)
|
||||
{
|
||||
next_snapshot = s.next_snapshot;
|
||||
active = s.active;
|
||||
disk_id = s.disk_id;
|
||||
_disk_id = s._disk_id;
|
||||
orphans = s.orphans;
|
||||
current_base = s.current_base;
|
||||
|
||||
@ -119,7 +120,7 @@ void Snapshots::init()
|
||||
|
||||
if (snapshot_template.get("DISK_ID", did))
|
||||
{
|
||||
disk_id = did;
|
||||
_disk_id = did;
|
||||
}
|
||||
|
||||
snapshot_template.get("NEXT_SNAPSHOT", next_snapshot);
|
||||
@ -255,8 +256,6 @@ int Snapshots::add_child_deny(VectorAttribute *snapshot)
|
||||
|
||||
void Snapshots::delete_snapshot(int id)
|
||||
{
|
||||
int parent_id;
|
||||
|
||||
VectorAttribute * snapshot = get_snapshot(id);
|
||||
|
||||
if (snapshot == nullptr)
|
||||
@ -264,37 +263,92 @@ void Snapshots::delete_snapshot(int id)
|
||||
return;
|
||||
}
|
||||
|
||||
if (orphans == DENY || orphans == MIXED)
|
||||
switch(orphans)
|
||||
{
|
||||
snapshot->vector_value("PARENT", parent_id);
|
||||
|
||||
//Remove this snapshot from parent's children
|
||||
if (parent_id != -1)
|
||||
case DENY:
|
||||
case MIXED:
|
||||
{
|
||||
set<int> child_set;
|
||||
int parent_id;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Remove this snapshot from parent's children
|
||||
// -----------------------------------------------------------------
|
||||
snapshot->vector_value("PARENT", parent_id);
|
||||
|
||||
if (parent_id == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
VectorAttribute * parent = get_snapshot(parent_id);
|
||||
|
||||
if (parent != nullptr)
|
||||
if (parent == nullptr)
|
||||
{
|
||||
string children = parent->vector_value("CHILDREN");
|
||||
break;
|
||||
}
|
||||
|
||||
one_util::split_unique(children, ',', child_set);
|
||||
string children = parent->vector_value("CHILDREN");
|
||||
|
||||
child_set.erase(id);
|
||||
set<int> child_set;
|
||||
|
||||
children = one_util::join(child_set.begin(), child_set.end(), ',');
|
||||
one_util::split_unique(children, ',', child_set);
|
||||
|
||||
if (children.empty())
|
||||
child_set.erase(id);
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Add snapshot child to parent's children
|
||||
//
|
||||
// Only DENY for in-line chain removal of snapshots (1 child)
|
||||
// -----------------------------------------------------------------
|
||||
string my_children = snapshot->vector_value("CHILDREN");
|
||||
|
||||
set<int> my_child_set;
|
||||
|
||||
one_util::split_unique(my_children, ',', my_child_set);
|
||||
|
||||
if ( my_child_set.size() == 1 )
|
||||
{
|
||||
int child_id = *my_child_set.begin();
|
||||
|
||||
child_set.insert(child_id);
|
||||
|
||||
VectorAttribute * child = get_snapshot(child_id);
|
||||
|
||||
if ( child != nullptr )
|
||||
{
|
||||
parent->remove("CHILDREN");
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->replace("CHILDREN", children);
|
||||
child->replace("PARENT", parent_id);
|
||||
}
|
||||
}
|
||||
|
||||
children = one_util::join(child_set.begin(), child_set.end(), ',');
|
||||
|
||||
if (children.empty())
|
||||
{
|
||||
parent->remove("CHILDREN");
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->replace("CHILDREN", children);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Set parent to ACTIVE if this snapshot id the active one
|
||||
//
|
||||
// Only DENY when deleting the active snapshot
|
||||
// -----------------------------------------------------------------
|
||||
if (active == id)
|
||||
{
|
||||
active = parent_id;
|
||||
|
||||
parent->replace("ACTIVE", true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ALLOW:
|
||||
case FORMAT: //At this point allow orpahn should be mapped to DENY/ALLOW
|
||||
break;
|
||||
}
|
||||
|
||||
snapshot_template.remove(snapshot);
|
||||
@ -382,7 +436,7 @@ const VectorAttribute * Snapshots::get_snapshot(int id) const
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
string Snapshots::get_snapshot_attribute(int id, const char * name) const
|
||||
string Snapshots::snapshot_attribute(int id, const char * name) const
|
||||
{
|
||||
const VectorAttribute * snapshot = get_snapshot(id);
|
||||
|
||||
@ -396,7 +450,7 @@ string Snapshots::get_snapshot_attribute(int id, const char * name) const
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
long long Snapshots::get_snapshot_size(int id) const
|
||||
long long Snapshots::snapshot_size(int id) const
|
||||
{
|
||||
long long snap_size = 0;
|
||||
|
||||
@ -410,10 +464,39 @@ long long Snapshots::get_snapshot_size(int id) const
|
||||
return snap_size;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int children_count(const VectorAttribute *snap, std::string& children)
|
||||
{
|
||||
if (snap == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
children.clear();
|
||||
|
||||
if (snap->vector_value("CHILDREN", children) == -1 || children.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::count(children.begin(), children.end(), ',') + 1;
|
||||
}
|
||||
|
||||
int Snapshots::children(int id, std::string& children) const
|
||||
{
|
||||
if (id <= -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return children_count(get_snapshot(id), children);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool Snapshots::test_delete(int id, string& error) const
|
||||
bool Snapshots::test_delete_image(int id, string& error) const
|
||||
{
|
||||
bool current;
|
||||
string children;
|
||||
@ -448,11 +531,111 @@ bool Snapshots::test_delete(int id, string& error) const
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool Snapshots::test_delete(int id, bool persistent, string& error) const
|
||||
{
|
||||
string childs;
|
||||
int ccount, p_ccount, c_ccount, p_id, c_id;
|
||||
|
||||
const VectorAttribute * snapshot = get_snapshot(id);
|
||||
|
||||
if (snapshot == nullptr)
|
||||
{
|
||||
error = "Snapshot does not exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(orphans)
|
||||
{
|
||||
case DENY:
|
||||
if ( persistent && id == 0 )
|
||||
{
|
||||
error = "Cannot delete snapshot 0 for persistent disk images";
|
||||
return false;
|
||||
}
|
||||
|
||||
ccount = children_count(snapshot, childs);
|
||||
|
||||
if ( ccount > 1 )
|
||||
{
|
||||
error = "Cannot delete snapshot with more than one children";
|
||||
return false;
|
||||
}
|
||||
else if ( ccount == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ( ccount == 1 && active == id)
|
||||
{
|
||||
error = "Cannot delete the active snapshot with children";
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Check the child (ccount == 1)
|
||||
// -------------------------------------------------------------
|
||||
if (snapshot->vector_value("CHILDREN", c_id) == 0)
|
||||
{
|
||||
c_ccount = children(c_id, childs);
|
||||
|
||||
if ( c_ccount > 1 || ( c_ccount == 1 && active == c_id ) )
|
||||
{
|
||||
error = "Cannot delete snapshot if child has children";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Check the parent
|
||||
// -----------------------------------------------------------------
|
||||
if ( snapshot->vector_value("PARENT", p_id) == 0 && p_id != -1)
|
||||
{
|
||||
p_ccount = children(p_id, childs);
|
||||
|
||||
if ( p_ccount > 1 )
|
||||
{
|
||||
error = "Cannot delete snapshot if parent has more than one child";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MIXED:
|
||||
if (id == active)
|
||||
{
|
||||
error = "Cannot delete the active snapshot";
|
||||
return false;
|
||||
}
|
||||
|
||||
ccount = children_count(snapshot, childs);
|
||||
|
||||
if (ccount != 0)
|
||||
{
|
||||
error = "Cannot delete snapshot with children";
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ALLOW:
|
||||
break;
|
||||
|
||||
case FORMAT:
|
||||
//At this point allow orpahn should be mapped to DENY/ALLOW
|
||||
error = "Inconsistent snapshot orphan mode";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
long long Snapshots::get_total_size() const
|
||||
long long Snapshots::total_size() const
|
||||
{
|
||||
long long size_mb, total_mb = 0;
|
||||
|
||||
|
@ -271,8 +271,6 @@ int VirtualMachine::lcm_state_from_str(string& st, LcmState& state)
|
||||
state = DISK_SNAPSHOT_DELETE_POWEROFF;
|
||||
} else if ( st == "DISK_SNAPSHOT_SUSPENDED" ) {
|
||||
state = DISK_SNAPSHOT_SUSPENDED;
|
||||
} else if ( st == "DISK_SNAPSHOT_REVERT_SUSPENDED" ) {
|
||||
state = DISK_SNAPSHOT_REVERT_SUSPENDED;
|
||||
} else if ( st == "DISK_SNAPSHOT_DELETE_SUSPENDED" ) {
|
||||
state = DISK_SNAPSHOT_DELETE_SUSPENDED;
|
||||
} else if ( st == "DISK_SNAPSHOT" ) {
|
||||
@ -420,8 +418,6 @@ string& VirtualMachine::lcm_state_to_str(string& st, LcmState state)
|
||||
st = "DISK_SNAPSHOT_DELETE_POWEROFF"; break;
|
||||
case DISK_SNAPSHOT_SUSPENDED:
|
||||
st = "DISK_SNAPSHOT_SUSPENDED"; break;
|
||||
case DISK_SNAPSHOT_REVERT_SUSPENDED:
|
||||
st = "DISK_SNAPSHOT_REVERT_SUSPENDED"; break;
|
||||
case DISK_SNAPSHOT_DELETE_SUSPENDED:
|
||||
st = "DISK_SNAPSHOT_DELETE_SUSPENDED"; break;
|
||||
case DISK_SNAPSHOT:
|
||||
@ -2734,7 +2730,7 @@ int VirtualMachine::from_xml(const string &xml_str)
|
||||
break;
|
||||
}
|
||||
|
||||
disks.set_snapshots(snap->get_disk_id(), snap);
|
||||
disks.set_snapshots(snap->disk_id(), snap);
|
||||
}
|
||||
|
||||
if (!content.empty())
|
||||
@ -3934,7 +3930,6 @@ void VirtualMachine::get_quota_template(VirtualMachineTemplate& quota_tmpl,
|
||||
lcm_state != VirtualMachine::DISK_SNAPSHOT_REVERT_POWEROFF &&
|
||||
lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_POWEROFF &&
|
||||
lcm_state != VirtualMachine::DISK_SNAPSHOT_SUSPENDED &&
|
||||
lcm_state != VirtualMachine::DISK_SNAPSHOT_REVERT_SUSPENDED &&
|
||||
lcm_state != VirtualMachine::DISK_SNAPSHOT_DELETE_SUSPENDED &&
|
||||
lcm_state != VirtualMachine::DISK_RESIZE_POWEROFF &&
|
||||
lcm_state != VirtualMachine::DISK_RESIZE_UNDEPLOYED &&
|
||||
|
@ -284,7 +284,7 @@ int VirtualMachineDisk::create_snapshot(const string& name, string& error)
|
||||
else
|
||||
{
|
||||
snap_id = snapshots->create_snapshot(name, size_mb);
|
||||
snap_size = snapshots->get_total_size();
|
||||
snap_size = snapshots->total_size();
|
||||
}
|
||||
|
||||
if (snap_id != -1)
|
||||
@ -334,21 +334,14 @@ void VirtualMachineDisk::delete_snapshot(int snap_id, Template **ds_quotas,
|
||||
return;
|
||||
}
|
||||
|
||||
long long ssize = snapshots->get_snapshot_size(snap_id);
|
||||
long long ssize = snapshots->snapshot_size(snap_id);
|
||||
|
||||
snapshots->delete_snapshot(snap_id);
|
||||
|
||||
long long snap_size = snapshots->get_total_size();
|
||||
long long snap_size = snapshots->total_size();
|
||||
|
||||
replace("DISK_SNAPSHOT_TOTAL_SIZE", snap_size);
|
||||
|
||||
if (snapshots->size() == 0)
|
||||
{
|
||||
delete snapshots;
|
||||
|
||||
snapshots = 0;
|
||||
}
|
||||
|
||||
string tm_target = get_tm_target();
|
||||
|
||||
vm_owner = tm_target == "SELF";
|
||||
|
@ -25,41 +25,76 @@ DEP_FILE_LOCATION=$(dirname $DEP_FILE)
|
||||
mkdir -p $DEP_FILE_LOCATION
|
||||
cat > $DEP_FILE
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Resolve disk.<disk_id> links for file-based disks
|
||||
#-------------------------------------------------------------------------------
|
||||
FILES=$(xmllint --xpath '//disk[@type="file" and @device="disk"]/source/@file' \
|
||||
"${DEP_FILE}" | sed -e 's/file=/\n/g' | tr -d '"')
|
||||
|
||||
SED_SCRIPT=""
|
||||
|
||||
while IFS= read -r f; do
|
||||
f=$(echo ${f} | sed 's/[[:space:]]*$//')
|
||||
|
||||
if ! [[ "${f}" =~ .*/disk\.[0-9]+$ ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
[ ! -L "${f}" ] && continue
|
||||
|
||||
rl=$(readlink ${f})
|
||||
|
||||
if [ "${rl:0:1}" != "/" ]; then
|
||||
rl="$(dirname ${f})/${rl}"
|
||||
fi
|
||||
|
||||
[ ! -e "${rl}" ] && continue
|
||||
|
||||
SED_SCRIPT="s#source file='${f}'#source file='${rl}'#g;${SED_SCRIPT}"
|
||||
done <<< ${FILES}
|
||||
|
||||
sed -i "${SED_SCRIPT}" "${DEP_FILE}"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Compact memory
|
||||
#-------------------------------------------------------------------------------
|
||||
if [ "x$CLEANUP_MEMORY_ON_START" = "xyes" ]; then
|
||||
(sudo -l | grep -q sysctl) && sudo -n sysctl vm.drop_caches=3 vm.compact_memory=1 >/dev/null
|
||||
fi
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Create non-volatile memory to store firmware variables if needed
|
||||
#-------------------------------------------------------------------------------
|
||||
nvram="$(xmllint --xpath '/domain/os/nvram/text()' $DEP_FILE 2>/dev/null)"
|
||||
if [ -n "${nvram}" ]; then
|
||||
cp -n "${OVMF_NVRAM}" "${nvram}"
|
||||
fi
|
||||
|
||||
# Create vGPU following NVIDIA official guide: https://docs.nvidia.com/grid/latest/pdf/grid-vgpu-user-guide.pdf
|
||||
# Only if supported by node
|
||||
#-------------------------------------------------------------------------------
|
||||
# Create vGPU following NVIDIA official guide:
|
||||
# https://docs.nvidia.com/grid/latest/pdf/grid-vgpu-user-guide.pdf
|
||||
#-------------------------------------------------------------------------------
|
||||
(sudo -l | grep -q vgpu) && sudo /var/tmp/one/vgpu "CREATE" "$DEP_FILE_LOCATION/vm.xml"
|
||||
|
||||
DATA=`virsh --connect $LIBVIRT_URI create $DEP_FILE`
|
||||
|
||||
if [ "x$?" = "x0" ]; then
|
||||
|
||||
DOMAIN_ID=$(xmllint --xpath '/domain/name/text()' "$DEP_FILE")
|
||||
UUID=$(virsh --connect $LIBVIRT_URI dominfo $DOMAIN_ID | awk '/UUID:/ {print $2}')
|
||||
echo $UUID
|
||||
|
||||
# redefine potential snapshots
|
||||
for SNAPSHOT_MD_XML in $(ls -v ${DEP_FILE_LOCATION}/snap-*.xml 2>/dev/null); do
|
||||
|
||||
|
||||
# replace uuid in the snapshot metadata xml
|
||||
sed -i "s%<uuid>[[:alnum:]-]*</uuid>%<uuid>$UUID</uuid>%" $SNAPSHOT_MD_XML
|
||||
|
||||
# redefine the snapshot using the xml metadata file
|
||||
virsh --connect $LIBVIRT_URI snapshot-create $DOMAIN_ID $SNAPSHOT_MD_XML --redefine > /dev/null || true
|
||||
done
|
||||
|
||||
else
|
||||
if [ "x$?" != "x0" ]; then
|
||||
error_message "Could not create domain from $DEP_FILE"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
DOMAIN_ID=$(xmllint --xpath '/domain/name/text()' "$DEP_FILE")
|
||||
UUID=$(virsh --connect $LIBVIRT_URI dominfo $DOMAIN_ID | awk '/UUID:/ {print $2}')
|
||||
|
||||
echo $UUID
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# redefine potential snapshots
|
||||
#---------------------------------------------------------------------------
|
||||
for SNAPSHOT_MD_XML in $(ls -v ${DEP_FILE_LOCATION}/snap-*.xml 2>/dev/null); do
|
||||
# replace uuid in the snapshot metadata xml
|
||||
sed -i "s%<uuid>[[:alnum:]-]*</uuid>%<uuid>$UUID</uuid>%" $SNAPSHOT_MD_XML
|
||||
|
||||
# redefine the snapshot using the xml metadata file
|
||||
virsh --connect $LIBVIRT_URI snapshot-create $DOMAIN_ID $SNAPSHOT_MD_XML --redefine > /dev/null || true
|
||||
done
|
||||
|
@ -258,6 +258,10 @@ if [ "$SHARED" = "YES" ] && [ -n "$SNAP_CUR" ]; then
|
||||
DISK_PATH=$(virsh --connect $QEMU_PROTOCOL://$DEST_HOST/system domblklist $DEPLOY_ID | awk '/disk.0/ {print $2}')
|
||||
DISK_DIR=$(dirname $DISK_PATH)
|
||||
|
||||
if [[ "${DISK_DIR}" =~ .*/disk\.[0-9]+.snap$ ]]; then
|
||||
DISK_DIR=$(dirname $DISK_DIR)
|
||||
fi
|
||||
|
||||
for SNAPSHOT_MD_XML in $(ls -v ${DISK_DIR}/snap-*.xml 2>/dev/null); do
|
||||
# replace uuid in the snapshot metadata xml
|
||||
sed -i "s%<uuid>[[:alnum:]-]*</uuid>%<uuid>$UUID</uuid>%" $SNAPSHOT_MD_XML
|
||||
|
Loading…
x
Reference in New Issue
Block a user