/***************************************************************************
* Copyright (c) 2012-2013 VMware, Inc. All Rights Reserved.
* 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.
***************************************************************************/
package com.vmware.aurora.vc;
import java.util.HashMap;
import java.util.Map;
import com.vmware.aurora.global.DiskSize;
import com.vmware.aurora.util.AuAssert;
import com.vmware.aurora.vc.VcTask.TaskType;
import com.vmware.aurora.vc.VcTaskMgr.IVcTaskBody;
import com.vmware.aurora.vc.VcVirtualMachineImpl.VmOp;
import com.vmware.aurora.vc.vcservice.VcContext;
import com.vmware.vim.binding.vim.vm.ConfigInfo;
import com.vmware.vim.binding.vim.vm.FileLayoutEx;
import com.vmware.vim.binding.vim.vm.FileLayoutEx.DiskLayout;
import com.vmware.vim.binding.vim.vm.FileLayoutEx.DiskUnit;
import com.vmware.vim.binding.vim.vm.FileLayoutEx.FileInfo;
import com.vmware.vim.binding.vim.vm.FileLayoutEx.SnapshotLayout;
import com.vmware.vim.binding.vim.vm.Snapshot;
import com.vmware.vim.binding.vim.vm.device.VirtualDevice;
import com.vmware.vim.binding.vim.vm.device.VirtualDisk;
import com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode;
import com.vmware.vim.binding.vmodl.ManagedObject;
import com.vmware.vim.binding.vmodl.ManagedObjectReference;
import com.vmware.vim.binding.vmodl.fault.ManagedObjectNotFound;
public interface VcSnapshot extends VcVmBase {
/**
* Remove a snapshot asynchronously.
* @throws Exception
* @return the task
*/
public VcTask remove(final IVcTaskCallback callback) throws Exception;
/**
* Remove a snapshot.
* @throws Exception
*/
public void remove() throws Exception;
/**
* Return parent Vm
*/
public VcVirtualMachine getVm();
/**
* @return the name of the snapshot
*/
public String getName();
/**
* Gets the size of the snapshot on data storage.
* @return The size.
*/
public DiskSize getSizeOnData(DiskSchema disSchema);
/**
* Get the size details of each disks in the snapshot.
* @return The size.
*/
public Map<DeviceId, DiskSize> getSizeDetail();
/**
* Gets the size of the snapshot on all storages.
* @return The size.
*/
public DiskSize getSize();
/**
* Get the vmsn file size of the snapshot.
*/
public DiskSize getVmsnFileSize();
/**
* Revert to a snapshot.
* @param callback (optional) call-back function for the task
* @return the task for revert to the snapshot
* @throws Exception
*/
VcTask revert(final IVcTaskCallback callback) throws Exception;
void revert() throws Exception;
}
@SuppressWarnings("serial")
class VcSnapshotImpl extends VcVmBaseImpl implements VcSnapshot {
private final VcVirtualMachineImpl vm;
private final String name;
private ConfigInfo config;
private DiskSize size;
private Map<DeviceId, DiskSize> sizeDetail;
private DiskSize vmsnFileSize;
@Override
protected void update(ManagedObject mo) {
final Snapshot snap = (Snapshot)mo;
config = snap.getConfig();
updateSize();
}
/*
* There is no way to find out the VM from VC's Snapshot moref,
* so we add it during initialization.
*/
protected VcSnapshotImpl(Snapshot mo, VcVirtualMachineImpl vm, String name) {
super(mo);
AuAssert.check(vm != null);
this.vm = vm;
this.name = name;
update(mo);
}
@Override
protected ConfigInfo getConfig() {
return config;
}
@Override
public String getName() {
return MoUtil.fromURLString(name);
}
public VcVirtualMachine getVm() {
return vm;
}
@Override
public VcDatacenter getDatacenter() {
return vm.getDatacenter();
}
@Override
public VcTask remove(final IVcTaskCallback callback) throws Exception {
// remove child snaps=false, consolidate=true
VcTask task = VcContext.getTaskMgr().execute(new IVcTaskBody() {
@Override
public VcTask body() throws Exception {
Snapshot snap = getManagedObject();
return new VcTask(TaskType.RemoveSnap, snap.remove(false, true), callback);
}
});
return task;
}
void removeInt() throws Exception {
try {
VcTask task = remove(VcCache.getRefreshVcTaskCB(vm));
task.waitForCompletion();
} catch (ManagedObjectNotFound e) {
// The object was gone. Nothing to do.
logger.info("cannot destroy " + this + ", not found.");
}
}
@Override
public void remove() throws Exception {
vm.safeExecVmOp(new VmOp<Void>() {
public Void exec() throws Exception {
removeInt();
return null;
}
});
}
private VirtualDevice findDevice(VirtualDevice[] devices, int key) {
for (VirtualDevice device : devices) {
if (device.getKey() == key) {
return device;
}
}
return null;
}
/**
* Calculate size of snapshot files.
*
* Note: This is done at configuration time,
* so it won't get updated if file size ever changes.
*/
private void updateSize() {
FileLayoutEx fileLayout = vm.getFileLayout();
// create file map to avoid unnecessary loops
Map<Integer, FileInfo> fileMap = new HashMap<Integer, FileInfo>();
if (fileLayout.getFile() != null) {
for (FileInfo fileInfo : fileLayout.getFile()) {
fileMap.put(fileInfo.getKey(), fileInfo);
}
}
sizeDetail = new HashMap<DeviceId, DiskSize>();
size = new DiskSize(0);
if (fileLayout.getSnapshot() == null) {
logger.info("missing snapshot layout in " + vm);
return;
}
// search for the snapshot's files
for (SnapshotLayout snapshotLayout : fileLayout.getSnapshot()) {
if (getMoRef().equals(snapshotLayout.getKey())) {
// loop through each disk and count the last diskunit in the chain only
// because the previous ones are shared with the other snapshots so don't double counting
VirtualDevice[] devices = getConfig().getHardware().getDevice();
for(DiskLayout disk : snapshotLayout.getDisk()) {
VirtualDisk vdisk = (VirtualDisk)findDevice(devices, disk.getKey());
String diskMode = ((VirtualDisk.FlatVer2BackingInfo)vdisk.getBacking()).getDiskMode();
// independent disks are not included in snapshot
if (!diskMode.equals(DiskMode.independent_nonpersistent.name()) &&
!diskMode.equals(DiskMode.independent_persistent.name())) {
DiskUnit[] diskUnits = disk.getChain();
int[] fileKeys = diskUnits[diskUnits.length - 1].getFileKey();
// count the total of each file's size
long total = 0;
for (int fileKey : fileKeys) {
FileInfo fileInfo = fileMap.get(fileKey);
if (fileInfo != null) {
total += fileInfo.getSize();
}
}
// add up
size.add(total);
DeviceId deviceId =
new DeviceId(findDevice(devices, vdisk.getControllerKey()), vdisk);
sizeDetail.put(deviceId, new DiskSize(total));
}
}
// add vmsn file size which is always on data storage
FileInfo fileInfo = fileMap.get(snapshotLayout.getDataKey());
if (fileInfo != null) {
size.add(fileInfo.getSize());
vmsnFileSize = new DiskSize(fileInfo.getSize());
}
return;
}
}
// no matching file layout for this snapshot?
}
@Override
public DiskSize getSize() {
return size;
}
@Override
public DiskSize getSizeOnData(DiskSchema diskSchema) {
DiskSize dataSize = new DiskSize(0);
Map<DeviceId, DiskSize> sizeDetail = getSizeDetail();
for (DiskSpec diskSpec : diskSchema.getDiskSpecs()) {
// only the snapshot of the archive disk is on backup storage since V1.0
if (!diskSpec.getDiskType().equals(DiskType.Archive)) {
DiskSize size = sizeDetail.get(diskSpec.getDeviceId());
if (size != null) {
dataSize.add(size);
}
}
}
dataSize.add(getVmsnFileSize());
return dataSize;
}
@Override
public Map<DeviceId, DiskSize> getSizeDetail() {
return sizeDetail;
}
@Override
public DiskSize getVmsnFileSize() {
return vmsnFileSize;
}
@Override
public VcTask revert(final IVcTaskCallback callback) throws Exception {
// revert to snapshot: host=null, suppressPowerOn=false
VcTask task = VcContext.getTaskMgr().execute(new IVcTaskBody() {
@Override
public VcTask body() throws Exception {
final Snapshot snap = getManagedObject();
return new VcTask(TaskType.RevertSanp, snap.revert(null, false), callback);
}
});
return task;
}
@Override
public void revert() throws Exception {
try {
VcTask task = revert(VcCache.getRefreshVcTaskCB(vm));
task.waitForCompletion();
} catch (ManagedObjectNotFound e) {
// The object was gone. Nothing to do.
logger.info("cannot revert to " + this + ", not found.");
}
}
/**
* Extends VcObject serialization proxy with extra parameters.
*/
protected static class SerializationProxy extends VcObjectImpl.SerializationProxy
{
private final ManagedObjectReference vmMoref;
private SerializationProxy(VcSnapshotImpl snapshot) {
super(snapshot);
vmMoref = snapshot.vm.getMoRef();
}
@Override
protected final VcObject getCachedObject() {
VcVirtualMachineImpl vm = VcCache.get(vmMoref);
return vm.getSnapshot(moRef);
}
}
@Override
protected Object writeReplace() {
return new SerializationProxy(this);
}
}