// // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. // package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CreateVMSnapshotAnswer; import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.vm.snapshot.VMSnapshot; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Pool; import com.xensource.xenapi.SR; import com.xensource.xenapi.Task; import com.xensource.xenapi.Types; import com.xensource.xenapi.Types.VmPowerState; import com.xensource.xenapi.VBD; import com.xensource.xenapi.VDI; import com.xensource.xenapi.VM; @ResourceWrapper(handles = CreateVMSnapshotCommand.class) public final class CitrixCreateVMSnapshotCommandWrapper extends CommandWrapper<CreateVMSnapshotCommand, Answer, CitrixResourceBase> { private static final Logger s_logger = Logger.getLogger(CitrixCreateVMSnapshotCommandWrapper.class); @Override public Answer execute(final CreateVMSnapshotCommand command, final CitrixResourceBase citrixResourceBase) { final String vmName = command.getVmName(); final String vmSnapshotName = command.getTarget().getSnapshotName(); final List<VolumeObjectTO> listVolumeTo = command.getVolumeTOs(); VmPowerState vmState = VmPowerState.HALTED; final String guestOSType = command.getGuestOSType(); final String platformEmulator = command.getPlatformEmulator(); final boolean snapshotMemory = command.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; final long timeout = command.getWait(); final Connection conn = citrixResourceBase.getConnection(); VM vm = null; VM vmSnapshot = null; boolean success = false; try { // check if VM snapshot already exists final Set<VM> vmSnapshots = VM.getByNameLabel(conn, command.getTarget().getSnapshotName()); if (vmSnapshots == null || vmSnapshots.size() > 0) { return new CreateVMSnapshotAnswer(command, command.getTarget(), command.getVolumeTOs()); } // check if there is already a task for this VM snapshot Task task = null; Set<Task> tasks = Task.getByNameLabel(conn, "Async.VM.snapshot"); if(tasks == null) { tasks = new LinkedHashSet<>(); } final Set<Task> tasksByName = Task.getByNameLabel(conn, "Async.VM.checkpoint"); if(tasksByName != null) { tasks.addAll(tasksByName); } for (final Task taskItem : tasks) { if (taskItem.getOtherConfig(conn).containsKey("CS_VM_SNAPSHOT_KEY")) { final String vmSnapshotTaskName = taskItem.getOtherConfig(conn).get("CS_VM_SNAPSHOT_KEY"); if (vmSnapshotTaskName != null && vmSnapshotTaskName.equals(command.getTarget().getSnapshotName())) { task = taskItem; } } } // create a new task if there is no existing task for this VM snapshot if (task == null) { try { vm = citrixResourceBase.getVM(conn, vmName); vmState = vm.getPowerState(conn); } catch (final Exception e) { if (!snapshotMemory) { vm = citrixResourceBase.createWorkingVM(conn, vmName, guestOSType, platformEmulator, listVolumeTo); } } if (vm == null) { return new CreateVMSnapshotAnswer(command, false, "Creating VM Snapshot Failed due to can not find vm: " + vmName); } // call Xenserver API if (!snapshotMemory) { task = vm.snapshotAsync(conn, vmSnapshotName); } else { final Set<VBD> vbds = vm.getVBDs(conn); final Pool pool = Pool.getByUuid(conn, citrixResourceBase.getHost().getPool()); for (final VBD vbd : vbds) { final VBD.Record vbdr = vbd.getRecord(conn); if (vbdr.userdevice.equals("0")) { final VDI vdi = vbdr.VDI; final SR sr = vdi.getSR(conn); // store memory image on the same SR with ROOT volume pool.setSuspendImageSR(conn, sr); } } task = vm.checkpointAsync(conn, vmSnapshotName); } task.addToOtherConfig(conn, "CS_VM_SNAPSHOT_KEY", vmSnapshotName); } citrixResourceBase.waitForTask(conn, task, 1000, timeout * 1000); citrixResourceBase.checkForSuccess(conn, task); final String result = task.getResult(conn); // extract VM snapshot ref from result final String ref = result.substring("<value>".length(), result.length() - "</value>".length()); vmSnapshot = Types.toVM(ref); try { Thread.sleep(5000); } catch (final InterruptedException ex) { } // calculate used capacity for this VM snapshot for (final VolumeObjectTO volumeTo : command.getVolumeTOs()) { final long size = citrixResourceBase.getVMSnapshotChainSize(conn, volumeTo, command.getVmName()); volumeTo.setSize(size); } success = true; return new CreateVMSnapshotAnswer(command, command.getTarget(), command.getVolumeTOs()); } catch (final Exception e) { String msg = ""; if (e instanceof Types.BadAsyncResult) { final String licenseKeyWord = "LICENCE_RESTRICTION"; final Types.BadAsyncResult errorResult = (Types.BadAsyncResult)e; if (errorResult.shortDescription != null && errorResult.shortDescription.contains(licenseKeyWord)) { msg = licenseKeyWord; } } else { msg = e.toString(); } s_logger.warn("Creating VM Snapshot " + command.getTarget().getSnapshotName() + " failed due to: " + msg, e); return new CreateVMSnapshotAnswer(command, false, msg); } finally { try { if (!success) { if (vmSnapshot != null) { s_logger.debug("Delete exsisting VM Snapshot " + vmSnapshotName + " after making VolumeTO failed"); final Set<VBD> vbds = vmSnapshot.getVBDs(conn); for (final VBD vbd : vbds) { final VBD.Record vbdr = vbd.getRecord(conn); if (vbdr.type == Types.VbdType.DISK) { final VDI vdi = vbdr.VDI; vdi.destroy(conn); } } vmSnapshot.destroy(conn); } } if (vmState == VmPowerState.HALTED) { if (vm != null) { vm.destroy(conn); } } } catch (final Exception e2) { s_logger.error("delete snapshot error due to " + e2.getMessage()); } } } }