/**
* Copyright (c) 2016 Inria
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* - Christophe Gourdin <christophe.gourdin@inria.fr>
*
*/
package org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Calendar;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.AttachDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.CreateDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.DatacenterNotFoundException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.DatastoreNotFoundException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.DeleteDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.DetachDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.LoadVolumeException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.RenameDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.addons.exceptions.ResizeDiskException;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.utils.DatacenterHelper;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.utils.DatastoreHelper;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.utils.VCenterClient;
import org.occiware.clouddesigner.occi.infrastructure.connector.vmware.utils.VMHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vmware.vim25.ArrayOfHostDatastoreBrowserSearchResults;
import com.vmware.vim25.FileBackedVirtualDiskSpec;
import com.vmware.vim25.FileInfo;
import com.vmware.vim25.FileQuery;
import com.vmware.vim25.FileQueryFlags;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.MethodFault;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecFileOperation;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDeviceConnectInfo;
import com.vmware.vim25.VirtualDeviceFileBackingInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskAdapterType;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualDiskType;
import com.vmware.vim25.VirtualMachineConfigInfo;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualMachinePowerState;
import com.vmware.vim25.VirtualSCSIController;
import com.vmware.vim25.VmDiskFileQuery;
import com.vmware.vim25.VmDiskFileQueryFilter;
import com.vmware.vim25.mo.Datacenter;
import com.vmware.vim25.mo.Datastore;
import com.vmware.vim25.mo.FileManager;
import com.vmware.vim25.mo.Folder;
import com.vmware.vim25.mo.HostDatastoreBrowser;
import com.vmware.vim25.mo.Task;
import com.vmware.vim25.mo.VirtualDiskManager;
import com.vmware.vim25.mo.VirtualMachine;
/**
* This class define a virtual volumes as virtual disk AND as vmdk file.
*
* @author Christophe Gourdin - Inria
*
*/
public class Volume {
private static Logger LOGGER = LoggerFactory.getLogger(Volume.class);
public static final String VIRTUAL_DISK = "VirtualDisk";
public static final String CONTROLLER_IDE = "VirtualIDEController";
public static final String CONTROLLER_SCSI = "VirtualSCSIController";
public static final String CONTROLLER_SATA = "VirtualSATAController";
private String dcName;
private String dsName;
private String volumeName;
/**
* Linked virtual machine name (null if none).
*/
private String vmName;
/**
* VI java data object.
*/
private VirtualDisk vdisk = null;
/**
* Size is in GB.
*/
private Float size = 0.0f;
/**
* Physical full path on VMWare infra.
*/
private String fullPath = null;
/**
* Mount point like : /home/user/ .
*/
private String mountPoint = null;
/**
* Define if this volume is the main on a compute or not.
*/
private boolean mainVolume = false;
/**
* VMWare state of this volume.
*/
private String volumeState = null;
/**
* VMWare controller type of this volume like : VirtualSCSIController,
* VirtualIDEController, VirtualSATAController.
*/
private String controllerType = CONTROLLER_SCSI; // Default to scsi
// controller.
/**
* Define if this volume exist on vmware vcenter.
*/
private boolean exist = false;
private boolean attached = false;
private Calendar modifiedDate;
/**
* Build a volume object from vcenter server information.
*
* @param volumeName
* @param ds
* @param dc
*/
public Volume(final String volumeName, final String dsName, final String dcName, final String vmName) {
this.volumeName = volumeName;
this.dsName = dsName;
this.dcName = dcName;
this.vmName = vmName;
}
/**
* Load all the attributes of this object from vcenter. If none found, this
* volume doesnt exist anymore.
* @throws LoadVolumeException
*/
public void loadVolume() throws LoadVolumeException {
if (dsName == null) {
exist = false;
throw new LoadVolumeException("Cant load the disk information without datastore reference.");
}
if (dcName == null) {
exist = false;
throw new LoadVolumeException("Cant load the disk information without datacenter reference");
}
// Load datastore info.
Datastore ds;
try {
ds = loadDatastore();
} catch (DatastoreNotFoundException ex) {
exist = false;
throw new LoadVolumeException(ex.getMessage(), ex.getCause());
}
if (fullPath == null) {
// Load the fullPath and check if it exist.
try {
fullPath = findVolumeVMDKPathForName(ds);
} catch (Exception ex) {
exist = false;
throw new LoadVolumeException(ex.getMessage(), ex.getCause());
}
}
if (fullPath == null) {
exist = false;
throw new LoadVolumeException("No vmware path found on this volume, cant load the volume informations.");
}
exist = exists(fullPath, ds);
if (!exist) {
throw new LoadVolumeException("The disk doesnt exist for path : " + fullPath + " on datastore : " + ds.getName());
}
// Load its information.
if (vmName != null) {
// Load the corresponding virtualDisk.
loadVirtualDisk();
}
if (vdisk != null) {
// Get the size of this disk.
Long sizeCapaKB = vdisk.getCapacityInKB();
size = sizeCapaKB.floatValue() / (1024 * 1024);
// Disk is attached.
attached = true;
// Determine if this volume is a main disk.
Integer unitNumber = vdisk.getUnitNumber();
// TODO : Check if there are other ways to know about a system disk.
if (unitNumber == 0) {
mainVolume = true;
} else {
mainVolume = false;
}
if (attached) {
volumeState = "attached";
}
} else {
// Disk is not attached to a vm.
attached = false;
// this volume is not the main...
mainVolume = false;
// The disk size has been set on findVolumeVMDKPathForName...
volumeState = "detached";
}
}
/**
* Load the corresponding virtual disk object if this volume is attached on
* vm instance.
*/
public void loadVirtualDisk() {
VirtualMachine vm = VMHelper.loadVirtualMachine(vmName);
if (vm == null) {
LOGGER.warn("The attached virtual machine doesnt exist, cant load virtual disk information.");
vdisk = null;
attached = false;
return;
}
VirtualDevice[] devices = vm.getConfig().getHardware().getDevice();
if (devices == null) {
LOGGER.warn("No attached devices on this virtual machine : " + vmName
+ " --> can't load virtual disk information");
vdisk = null;
attached = false;
return;
}
String diskName;
// Search on devices.
for (VirtualDevice device : devices) {
diskName = null;
if (device == null) {
continue;
} else if (device instanceof VirtualDisk) {
VirtualDisk disk = (VirtualDisk) device;
VirtualDeviceBackingInfo vdbi = device.getBacking();
if (vdbi instanceof VirtualDeviceFileBackingInfo) {
diskName = ((VirtualDeviceFileBackingInfo) vdbi).getFileName();
}
if (diskName != null && diskName.equals(fullPath)) {
// The disk has been found on the virtual machine.
vdisk = disk;
attached = true;
exist = true;
break;
}
}
}
}
/**
* Create an empty volume on vmware vcenter, attached to a datastore but not
* attached on a compute. We create here a new VMDK file on the datastore
* with the given path.
*
* @throws CreateDiskException
*/
public void createEmptyVolume() throws CreateDiskException {
Datacenter dc;
Datastore ds;
try {
ds = loadDatastore();
dc = loadDatacenter();
} catch (DatacenterNotFoundException ex) {
throw new CreateDiskException(ex);
} catch (DatastoreNotFoundException ex) {
throw new CreateDiskException(ex);
}
if (fullPath == null) {
try {
fullPath = findVolumeVMDKPathForName(ds);
} catch (Exception ex) {
throw new CreateDiskException(ex.getMessage(), ex.getCause());
}
}
// Check if this volume already exist in the datastore.
if (fullPath == null) {
// The volume doesnt exist, we create it in a directory that is the name of the volume (without extension vmdk).
try {
mkdir(dc, ds, "/" + volumeName);
fullPath = "[" + dsName + "] " + volumeName + "/" + volumeName + ".vmdk";
} catch (IOException ex) {
throw new CreateDiskException("Error IO : " + ex.getMessage(), ex.getCause());
}
} else {
throw new CreateDiskException("Cant create the disk, it already exist for this path : " + fullPath);
}
if (size.longValue() <= 0L) {
throw new CreateDiskException(
"Cant create the disk, the size is not setted correctly, size must be superior or equal to 1 GB");
}
// Create an empty, unformatted and unpartitionned VMDK virtual disk
// file.
try {
VirtualDiskManager diskManager = VCenterClient.getServiceInstance().getVirtualDiskManager();
FileBackedVirtualDiskSpec fbvspec = new FileBackedVirtualDiskSpec();
fbvspec.setAdapterType(VirtualDiskAdapterType.lsiLogic.name());
fbvspec.setCapacityKb(size.longValue() * (1024 * 1024));
fbvspec.setDiskType(VirtualDiskType.preallocated.name());
Task task = diskManager.createVirtualDisk_Task(fullPath, dc, fbvspec);
task.waitForTask();
TaskInfo taskInfo;
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new CreateDiskException("Error while creating an empty detached disk : " + volumeName + "--> "
+ fault.detail + " \n Fault message: " + fault.getMessage());
} else {
attached = false;
mainVolume = false;
exist = true;
volumeState = "undefined";
LOGGER.info("The disk : " + volumeName + " has been created (empty volume).");
}
} catch (RemoteException | InterruptedException ex) {
throw new CreateDiskException("Error while creating an empty detached disk : " + volumeName, ex.getCause());
}
}
/**
* Create a new volume attached on a vm compute, after, load it's
* information on this object attribute. We use the method of the
* virtualDevice from vijava SDK.
*/
public void createAttachedVolume() throws CreateDiskException {
VirtualDisk disk = null;
VirtualSCSIController scsiController;
Datastore ds;
try {
ds = loadDatastore();
} catch (DatastoreNotFoundException ex) {
LOGGER.error(ex.getMessage());
return;
}
if (dsName == null) {
throw new CreateDiskException("Cant load the disk information without datastore reference.");
}
if (dcName == null) {
throw new CreateDiskException("Cant load the disk information without datacenter reference");
}
if (volumeName == null) {
throw new CreateDiskException(
"The disk name is not setted, please defined it on title attribute, cant create an attached disk.");
}
if (vmName == null) {
throw new CreateDiskException("The virtual machine is not defined, cant create an attached disk.");
}
if (size == 0.0f) {
throw new CreateDiskException("The size must be set (in GB), cant create the attached disk.");
}
VirtualMachine vm = VMHelper.loadVirtualMachine(vmName);
if (vm == null) {
throw new CreateDiskException(
"The virtual machine : " + vmName + " doesnt exist anymore, cant create the attached disk.");
}
scsiController = getSCSIController(vm);
if (scsiController == null) {
throw new CreateDiskException("No scsi controller found on virtual machine : " + vm.getName()
+ " , cant create the attached disk.");
}
// Define the full filename path.
fullPath = "[" + dsName + "] " + vm.getName() + "/" + volumeName + ".vmdk";
// Check if the disk already exist on this path.
if (exists(fullPath, ds)) {
LOGGER.warn("The disk " + volumeName + " already exist, cant create.");
exist = true;
throw new CreateDiskException("The disk " + volumeName + " already exist, cant create.");
}
VirtualDisk mainDisk = findVirtualDiskForScsiId(vm, 0);
VirtualDiskFlatVer2BackingInfo vdMainBackingInfo = (VirtualDiskFlatVer2BackingInfo) mainDisk.getBacking();
disk = new VirtualDisk();
Integer scsiId = getFreeUnitNumber(vm);
if (scsiId > 14 || scsiId == 0) {
throw new CreateDiskException(
"No available scsi unit number, please check your vm configuration, cant create the attached disk.");
}
disk.setUnitNumber(scsiId);
disk.setCapacityInKB(size.longValue() * 1024L * 1024L);
disk.setControllerKey(scsiController.getKey());
// VirtualDiskFlatVer2BackingInfo
VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo();
backingInfo.setDatastore(null);
backingInfo.setFileName(fullPath);
// TODO : Attribute on this object, valid values are : persistent,
// independent_persistent, independent_nonpersistent, nonpersistent,
// undoable,append
backingInfo.setDiskMode("persistent");
backingInfo.setSplit(false);
backingInfo.setEagerlyScrub(false);
backingInfo.setThinProvisioned(vdMainBackingInfo.getThinProvisioned());
backingInfo.setWriteThrough(false);
disk.setBacking(backingInfo);
// VirtualDeviceConnectInfo
VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo();
connectInfo.setAllowGuestControl(false);
connectInfo.setStartConnected(true);
connectInfo.setConnected(true);
disk.setConnectable(connectInfo);
// Prepare the configuration.
VirtualDeviceConfigSpec diskSpec = new VirtualDeviceConfigSpec();
diskSpec.setOperation(VirtualDeviceConfigSpecOperation.add);
diskSpec.setFileOperation(VirtualDeviceConfigSpecFileOperation.create);
diskSpec.setDevice(disk);
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
configSpec.setDeviceChange(new VirtualDeviceConfigSpec[] { diskSpec });
// Launch the task.
Task task;
try {
task = vm.reconfigVM_Task(configSpec);
task.waitForTask();
} catch (RemoteException | InterruptedException e) {
LOGGER.error("Error while creating an attached disk : " + volumeName + " --< to vm : " + vm.getName(), e.getCause());
throw new CreateDiskException(
"Error while creating an attached disk : " + volumeName + " --< to vm : " + vm.getName(), e.getCause());
}
TaskInfo taskInfo;
try {
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new CreateDiskException("Error while creating an attached disk : " + volumeName + " --< to vm : "
+ vm.getName() + " \n " + fault.detail + "Fault message: " + fault.getMessage() + " --> "
+ fault.getClass().getName());
}
} catch (RemoteException e) {
throw new CreateDiskException(
"Error while creating an attached disk : " + volumeName + " --< to vm : " + vm.getName(), e.getCause());
}
// Update the vdisk infos.
vdisk = findVirtualDiskForScsiId(vm, scsiId);
if (vdisk != null) {
attached = true;
exist = true;
mainVolume = false;
// Connectable object is null here, custom status....
volumeState = "connected";
LOGGER.info("The disk is created and attached.");
} else {
LOGGER.warn("Cant reload the disk. Check on vcenter (or with retrieve operation) if the disk is created.");
volumeState = "error";
attached = false;
mainVolume = false;
exist = false;
}
}
/**
* Destroy definitively this volume from datastore and vcenter.
*/
public void destroyVolume() throws DeleteDiskException {
Datacenter dc;
Datastore ds;
try {
ds = loadDatastore();
dc = loadDatacenter();
} catch (DatacenterNotFoundException ex) {
throw new DeleteDiskException(ex);
} catch (DatastoreNotFoundException ex) {
throw new DeleteDiskException(ex);
}
FileManager fileManager = VCenterClient.getServiceInstance().getFileManager();
if (fileManager == null) {
throw new DeleteDiskException(
"File manager is not supported or you have not the rights to use it, cant destroy the disk "
+ volumeName);
}
if (fullPath == null) {
try {
fullPath = findVolumeVMDKPathForName(ds);
} catch (Exception ex) {
throw new DeleteDiskException(ex.getMessage(), ex.getCause());
}
}
if (fullPath == null) {
throw new DeleteDiskException("The disk path on vcenter is not found, cant destroy the disk " + volumeName);
}
if (!exists(fullPath, ds)) {
LOGGER.warn("The disk doesnt exist anymore, cant destroy the disk " + volumeName);
throw new DeleteDiskException("The disk doesnt exist anymore, cant destroy the disk " + volumeName);
}
Task task;
try {
task = fileManager.deleteDatastoreFile_Task(fullPath, dc);
task.waitForTask();
} catch (RemoteException | InterruptedException e) {
throw new DeleteDiskException("Error while destroying a disk : " + volumeName, e);
}
TaskInfo taskInfo;
try {
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new DeleteDiskException("Error while destroying a disk : " + volumeName + " --> " + fault.detail
+ " \n Fault message: " + fault.getMessage() + " fault: " + fault.getClass().getName());
} else {
// OK, now destroy the hiding file flat version.
String flatName = fullPath.substring(0, volumeName.length() - 5) + "-flat.vmdk";
task = fileManager.deleteDatastoreFile_Task(flatName, dc);
task.waitForTask();
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new DeleteDiskException("Error while destroying a flat disk : " + volumeName + fault.detail
+ " \n Fault message: " + fault.getMessage() + " fault : " + fault.getClass().getName());
} else {
LOGGER.info("The disk " + volumeName + " has been destroyed.");
volumeState = "deleted";
exist = false;
attached = false;
mainVolume = false;
}
}
} catch (RemoteException | InterruptedException e) {
throw new DeleteDiskException("Error while destroying a disk : " + volumeName, e);
}
LOGGER.info("The disk : " + volumeName + " has been destroyed.");
}
/**
* Attach the volume to an instance. Dont forget to load virtualDisk object
* from vcenter and update vmdk path. if this volume is already attached,
* this method does nothing.
*
* @throws AttachDiskException
*/
public void attachVolume() throws AttachDiskException {
Datastore ds;
try {
ds = loadDatastore();
} catch (DatastoreNotFoundException ex) {
throw new AttachDiskException(ex.getMessage(), ex.getCause());
}
if (vmName == null) {
throw new AttachDiskException("No virtual machine name is given for the attachment. Cant attach the disk.");
}
if (fullPath == null) {
try {
fullPath = findVolumeVMDKPathForName(ds);
} catch (Exception ex) {
throw new AttachDiskException(ex.getCause());
}
}
loadVirtualDisk();
if (attached) {
throw new AttachDiskException("The disk : " + volumeName + " is already attached on the vm : " + vmName);
}
if (fullPath == null) {
exist = false;
// Create the disk
try {
this.createAttachedVolume();
} catch (CreateDiskException ex) {
throw new AttachDiskException(ex.getCause());
}
} else {
// Reuse the disk.
// Load the virtual machine.
VirtualMachine vm = VMHelper.loadVirtualMachine(vmName);
if (vm == null) {
throw new AttachDiskException("The virtual machine doesnt exist anymore. Cant attach the disk "
+ volumeName + " on the vm: " + vmName);
}
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec diskSpec = new VirtualDeviceConfigSpec();
VirtualDeviceConfigSpec[] vdiskSpecArray = { diskSpec };
vmConfigSpec.setDeviceChange(vdiskSpecArray);
VirtualDiskFlatVer2BackingInfo diskfileBacking = new VirtualDiskFlatVer2BackingInfo();
diskfileBacking.setFileName(fullPath);
diskfileBacking.setDiskMode("persistent");
VirtualSCSIController scsiController = getSCSIController(vm);
int unitNumber = getFreeUnitNumber(vm);
VirtualDisk disk = new VirtualDisk();
disk.setControllerKey(scsiController.getKey());
disk.setUnitNumber(unitNumber);
disk.setBacking(diskfileBacking);
// Unlike required by API ref, the capacityKB is optional. So skip
// setCapacityInKB() method.
disk.setKey(-100);
diskSpec.setOperation(VirtualDeviceConfigSpecOperation.add);
diskSpec.setDevice(disk);
// Launch the service task.
Task task;
try {
task = vm.reconfigVM_Task(vmConfigSpec);
task.waitForTask();
TaskInfo taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new AttachDiskException("Error while attaching a disk : " + volumeName + " --> " + fault.detail + " \n Fault message: " + fault.getMessage() + " --< " + fault.getClass().getName());
} else {
LOGGER.info("The disk " + volumeName + " is attached.");
attached = true;
exist = true;
}
} catch (RemoteException | InterruptedException e) {
throw new AttachDiskException(
"Error while attaching a disk : " + volumeName + " on virtual machine: " + vmName, e.getCause());
}
}
}
/**
* Detach this volume from the instance, don't forget to set virtualDisk
* object to null and update vmdk path accordingly. If this volume is not
* attached, this method does nothing. If this volume is a main volume,
* can't detach this volume.
*/
public void detachVolume() throws DetachDiskException {
loadVirtualDisk();
if (!attached) {
throw new DetachDiskException("The disk is already detached.");
}
// VirtualMachine
if (vmName == null) {
attached = false;
throw new DetachDiskException("The disk is already detached, no vm found for this disk.");
}
Folder rootFolder = VCenterClient.getServiceInstance().getRootFolder();
VirtualMachine machine = VMHelper.findVMForName(rootFolder, vmName);
loadVirtualDisk();
if (vdisk == null) {
attached = false;
throw new DetachDiskException("The disk is already detached, no virtual disk found for name: " + volumeName
+ " on virtual machine: " + vmName);
}
VirtualDeviceConfigSpec diskSpec = new VirtualDeviceConfigSpec();
diskSpec.setOperation(VirtualDeviceConfigSpecOperation.remove);
// diskSpec.setFileOperation(VirtualDeviceConfigSpecFileOperation.destroy);
diskSpec.setDevice(vdisk);
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
configSpec.setDeviceChange(new VirtualDeviceConfigSpec[] { diskSpec });
Task task;
try {
task = machine.reconfigVM_Task(configSpec);
task.waitForTask();
TaskInfo taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new DetachDiskException("Error while detaching a disk : " + volumeName + " --> " + fault.detail + " \n Fault message: " + fault.getMessage());
} else {
LOGGER.info("The disk " + volumeName + " has been detached.");
}
} catch (RemoteException | InterruptedException e) {
throw new DetachDiskException(
"Error while detaching a disk : " + volumeName + " from virtual machine: " + vmName, e.getCause());
}
}
/**
* Migrate a volume from one datastore to another.
*/
public void MigrateVolumeOnAnotherDatastore() {
// TODO : migrate a volume from origin ds to destination ds. in V2.
}
/**
* Migrate a volume from one instance vm to another instance vm.
*/
public void MigrateVolumeInstanceToAnother() {
// TODO : migrate a volume from origin instance to destination instance.
// in V2.
}
/**
* Extend a disk to a new size capacity.
*
* @param newSize
* (in GB).
* @return true if extend disk is success. false otherwise.
*/
public void resize(Float newSize) throws ResizeDiskException {
Datacenter dc;
Datastore ds;
try {
ds = loadDatastore();
dc = loadDatacenter();
} catch (DatacenterNotFoundException | DatastoreNotFoundException ex) {
throw new ResizeDiskException(ex.getMessage(), ex.getCause());
}
Long sizeKB = newSize.longValue() * 1024 * 1024;
LOGGER.info("Resizing volume " + volumeName + " to " + sizeKB);
if (fullPath == null) {
try {
fullPath = findVolumeVMDKPathForName(ds);
} catch (Exception ex) {
throw new ResizeDiskException(ex.getMessage(), ex.getCause());
}
if (fullPath == null) {
throw new ResizeDiskException("No vmware path found on this volume, cant resize the volume");
}
} else {
LOGGER.debug("Full path: " + fullPath);
}
VirtualDiskManager vdiskManager = VCenterClient.getServiceInstance().getVirtualDiskManager();
if (vdiskManager != null) {
// Launch the task.
Task task;
try {
task = vdiskManager.extendVirtualDisk_Task(fullPath, dc, sizeKB);
task.waitForTask();
} catch (RemoteException | InterruptedException e) {
throw new ResizeDiskException("Error while resizing a disk : " + volumeName, e.getCause());
}
TaskInfo taskInfo;
try {
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new ResizeDiskException("Error while resizing a disk : " + volumeName + " --> " + fault.detail + " \n Fault message: " + fault.getMessage() + " --< " + fault.getClass().getName());
} else {
size = newSize;
}
} catch (RemoteException e) {
throw new ResizeDiskException("Error while resizing a disk : " + volumeName, e.getCause());
}
} else {
// Cant use virtualDisk manager.
loadVirtualDisk();
if (isAttached() && vdisk != null) {
vdisk.setCapacityInKB(sizeKB);
// Load the virtual machine
Folder rootFolder = VCenterClient.getServiceInstance().getRootFolder();
VirtualMachine vm = VMHelper.findVMForName(rootFolder, vmName);
VirtualDeviceConfigSpec vdcs = new VirtualDeviceConfigSpec();
vdcs.setDevice(vdisk);
vdcs.setOperation(VirtualDeviceConfigSpecOperation.edit);
VirtualMachineConfigSpec vmcs = new VirtualMachineConfigSpec();
vmcs.setDeviceChange(new VirtualDeviceConfigSpec[] { vdcs });
Task task;
try {
task = vm.reconfigVM_Task(vmcs);
task.waitForTask();
} catch (RemoteException | InterruptedException e) {
throw new ResizeDiskException("Error while resizing a disk : " + volumeName, e.getCause());
}
TaskInfo taskInfo;
try {
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new ResizeDiskException("Error while resizing a disk : " + volumeName + " --> " + fault.detail + " \n Fault message: " + fault.getMessage() + " --< " + fault.getClass().getName());
} else {
size = newSize;
}
} catch (RemoteException e) {
throw new ResizeDiskException("Error while resizing a disk : " + volumeName, e.getCause());
}
} else {
// Volume not attached and cant use virtual disk manager.
throw new ResizeDiskException(
"Cant resize the disk, the disk must be attached to a virtual machine or you must have the rights to VirtualDiskManager file operation.");
}
}
LOGGER.info("The disk : " + volumeName + " has been resized to : " + size);
}
/**
* Rename a disk from volumeName to newVolumeName.
*
* @param newVolumeName
* @return true if rename operation has succeed.
*/
public void renameDisk(final String newVolumeName) throws RenameDiskException {
Datacenter dc;
Datastore ds;
try {
ds = loadDatastore();
dc = loadDatacenter();
} catch (DatacenterNotFoundException | DatastoreNotFoundException e) {
throw new RenameDiskException(e.getMessage(), e.getCause());
}
// Check if we must power off the vm, only if the disk is attached..
if (isAttached()) {
// Load the vm.
Folder rootFolder = VCenterClient.getServiceInstance().getRootFolder();
VirtualMachine vm = VMHelper.findVMForName(rootFolder, vmName);
if (vm != null) {
if (!vm.getRuntime().getPowerState().equals(VirtualMachinePowerState.poweredOff)) {
// Power off before.
try {
VMHelper.powerOff(vm);
} catch (RemoteException | InterruptedException ex) {
throw new RenameDiskException(
"Cant power off the virtual machine : " + vm.getName() + " to rename a virtual disk.", ex.getCause());
}
}
}
if (vdisk == null) {
loadVirtualDisk();
}
} // end if isAttached.
String newPath = "[" + dsName + "] " + vmName + "/" + newVolumeName + ".vmdk";
LOGGER.debug("Moving disk vmdk : " + fullPath + " --< to: " + newPath);
Task task;
try {
VirtualDiskManager vdiskManager = VCenterClient.getServiceInstance().getVirtualDiskManager();
task = vdiskManager.moveVirtualDisk_Task(fullPath, dc, newPath, dc, true);
task.waitForTask();
} catch (RemoteException | InterruptedException ex) {
throw new RenameDiskException("Error while renaming a disk : " + volumeName + " to: " + newVolumeName, ex.getCause());
}
TaskInfo taskInfo;
try {
taskInfo = task.getTaskInfo();
if (taskInfo.getState() != TaskInfoState.success) {
MethodFault fault = taskInfo.getError().getFault();
throw new RenameDiskException("Error while renaming a disk : " + volumeName + " to: " + newVolumeName + " --> " + fault.detail + " \n Fault message: " + fault.getMessage() + " fault:" + fault.getClass().getName());
} else {
volumeName = newVolumeName;
fullPath = newPath;
}
} catch (RemoteException e) {
throw new RenameDiskException("Error while renaming a disk : " + volumeName, e.getCause());
}
LOGGER.info("The disk : " + volumeName + " has been renamed.");
}
/**
* Get the uuid of this disk (vmware value).
*
* @return
*/
public String getUUID() throws RemoteException {
String uuid = "unknwown";
Datacenter dc;
try {
dc = loadDatacenter();
} catch (DatacenterNotFoundException ex) {
LOGGER.error(ex.getMessage());
return null;
}
VirtualDiskManager virtDiskMgr = VCenterClient.getServiceInstance().getVirtualDiskManager();
uuid = virtDiskMgr.queryVirtualDiskUuid(fullPath, dc);
return uuid;
}
// Utility methods.
/**
* Create a directory for the volume management.
*
* @param dc
* @param ds
* @param destFolder
* @return
* @throws IOException
*/
private static boolean mkdir(final Datacenter dc, final Datastore ds, String destFolder) throws IOException {
String dsName = ds.getName();
destFolder = "[" + dsName + "]" + destFolder;
FileManager fileManager = VCenterClient.getServiceInstance().getFileManager();
if (fileManager == null) {
LOGGER.warn("File manager is not available on this vcenter server !");
LOGGER.warn("Cant create the directory " + destFolder + " on datastore : " + dsName);
return false;
}
if (!exists(destFolder, ds)) {
LOGGER.info("Creating directory : " + destFolder);
fileManager.makeDirectory(destFolder, dc, true);
return true;
}
LOGGER.warn("Directory : " + destFolder + " could not be created because it already exists");
return false;
}
/**
* Find a volume full path for the volume name in datastore.
*
* @param ds
* @param volumeName
* (ex: data1)
* @return the full path like "[datastore1] /datavm1/data1.vmdk", may return
* null if no volume is found.
*/
private String findVolumeVMDKPathForName(Datastore ds) throws RemoteException, InterruptedException {
String fullPath = null;
String basePath = null;
VmDiskFileQueryFilter vdiskFilter = new VmDiskFileQueryFilter();
// default is SCSI.
vdiskFilter.setControllerType(new String[] { controllerType });
VmDiskFileQuery fQuery = new VmDiskFileQuery();
fQuery.setFilter(vdiskFilter);
HostDatastoreBrowserSearchSpec searchSpec = new HostDatastoreBrowserSearchSpec();
searchSpec.setQuery(new FileQuery[] { fQuery });
searchSpec.setMatchPattern(new String[] { volumeName + ".vmdk" }); // ".*"
FileQueryFlags fqf = new FileQueryFlags();
fqf.setFileSize(true);
fqf.setModification(true);
fqf.setFileOwner(true);
fqf.setFileType(true);
searchSpec.setDetails(fqf);
Task subFolderTask = ds.getBrowser().searchDatastoreSubFolders_Task("[" + dsName + "]", searchSpec);
subFolderTask.waitForTask();
TaskInfo tInfo = subFolderTask.getTaskInfo();
ArrayOfHostDatastoreBrowserSearchResults searchResult = (ArrayOfHostDatastoreBrowserSearchResults) tInfo
.getResult();
HostDatastoreBrowserSearchResults[] results = null;
if (searchResult == null) {
return null;
}
results = searchResult.getHostDatastoreBrowserSearchResults();
if (results == null) {
return null;
}
int len = searchResult.getHostDatastoreBrowserSearchResults().length;
for (int j = 0; j < len; j++) {
HostDatastoreBrowserSearchResults sres = searchResult.HostDatastoreBrowserSearchResults[j];
basePath = sres.getFolderPath();
FileInfo[] fileArray = sres.getFile();
if (fileArray == null) {
continue;
}
for (FileInfo fileInfo : fileArray) {
fullPath = basePath + fileInfo.getPath();
// Real size on datastore.
// File info is in bytes not kilo !!!
size = fileInfo.getFileSize().floatValue() / (1024 * 1024 * 1024);
modifiedDate = fileInfo.getModification();
break;
}
}
return fullPath;
}
/**
* Utility method, check if a folder exist or a path on the datastore.
*
* @param path
* @param ds
* @return
* @throws IOException
*/
private static boolean exists(String path, Datastore ds) {
// works for both files and folders
String basePath = "[" + ds.getName() + "]";
if (!path.contains(basePath)) {
path = "[" + ds.getName() + "]" + path;
}
HostDatastoreBrowser hdb = ds.getBrowser();
String[] splitPath = path.split("/");
String fileName = splitPath[splitPath.length - 1];
String folder = path.substring(0, path.length() - fileName.length());
HostDatastoreBrowserSearchSpec fileSearchSpec = new HostDatastoreBrowserSearchSpec();
fileSearchSpec.setMatchPattern(new String[] { fileName });
try {
Task task = hdb.searchDatastore_Task(folder, fileSearchSpec);
task.waitForTask();
HostDatastoreBrowserSearchResults searchResults = (HostDatastoreBrowserSearchResults) task.getTaskInfo()
.getResult();
if (searchResults == null) {
return false;
}
FileInfo[] fileInfo = searchResults.getFile();
return (fileInfo != null && fileInfo.length > 0);
} catch (com.vmware.vim25.FileNotFound ex) {
// normal case
} catch (Exception ex) {
LOGGER.error("Exception while testing if " + path + " exists ", ex);
}
return false;
}
/**
* Find a virtual disk for a scsiId.
*
* @param vm
* @param scsiId
* @return a virtual disk for this scsiId, if none found return null.
*/
private VirtualDisk findVirtualDiskForScsiId(VirtualMachine vm, Integer scsiId) {
VirtualSCSIController scsiController = getSCSIController(vm);
VirtualDisk disk = null;
for (VirtualDevice device : vm.getConfig().getHardware().getDevice()) {
if (device instanceof VirtualDisk) {
VirtualDisk tmpDisk = (VirtualDisk) device;
if (tmpDisk.getControllerKey() != null && tmpDisk.getControllerKey().equals(scsiController.getKey())) {
if (tmpDisk.getUnitNumber() != null && tmpDisk.getUnitNumber().equals(scsiId)) {
disk = tmpDisk;
break;
}
}
}
}
return disk;
}
/**
* Get the first scsi controller on bus 0.
*
* @param vm
* @return a virtual scsi controller (bus 0), if none, return null.
*/
private VirtualSCSIController getSCSIController(VirtualMachine vm) {
// Search for the first scsi controller bus unit = 0.
VirtualSCSIController scsiController = null;
for (VirtualDevice device : vm.getConfig().getHardware().getDevice()) {
if (device instanceof VirtualSCSIController) {
VirtualSCSIController scsiController2 = (VirtualSCSIController) device;
if (scsiController2.getBusNumber() == 0) {
scsiController = scsiController2;
break;
}
}
}
if (scsiController == null) {
LOGGER.warn("No scsi controller found on this instance : " + vm.getName());
}
return scsiController;
}
/**
* Get a free unit number on the vm scsi controller.
*
* @param vm
* @return
*/
private Integer getFreeUnitNumber(VirtualMachine vm) {
Integer scsiId = 0;
VirtualSCSIController scsiController = getSCSIController(vm);
VirtualDisk disk = null;
for (VirtualDevice device : vm.getConfig().getHardware().getDevice()) {
if (device instanceof VirtualDisk) {
VirtualDisk tmpDisk = (VirtualDisk) device;
if (tmpDisk.getControllerKey() != null && tmpDisk.getControllerKey().equals(scsiController.getKey())) {
// Search for a unit number free, get the last one and
// increment. (max 13).
if (tmpDisk.getUnitNumber() > scsiId) {
scsiId = tmpDisk.getUnitNumber();
}
}
}
}
scsiId = scsiId + 1;
if (scsiId > 13) {
LOGGER.warn("No scsi unit number is available !!!");
}
return scsiId;
}
/**
* Load a datastore from rootFolder.
*
* @return a datastore object, if not found a
* {@link DatastoreNotFoundException} is thrown.
* @throws DatastoreNotFoundException
*/
private Datastore loadDatastore() throws DatastoreNotFoundException {
if (dsName == null) {
throw new DatastoreNotFoundException("Datastore name is not set, cant locate a datastore by its name.");
}
Folder rootFolder = VCenterClient.getServiceInstance().getRootFolder();
Datastore ds = DatastoreHelper.findDatastoreForName(rootFolder, dsName);
if (ds == null) {
throw new DatastoreNotFoundException();
}
return ds;
}
/**
* Load a datacenter object from rootFolder and datastore name.
*
* @return a datacenter object, if not found a
* {@link DatacenterNotFoundException} is thrown (or if datastore
* name is null).
* @throws DatacenterNotFoundException
*/
private Datacenter loadDatacenter() throws DatacenterNotFoundException {
if (dsName == null) {
throw new DatacenterNotFoundException(
"Datastore name is not set, cant locate datacenter from datastore name.");
}
Folder rootFolder = VCenterClient.getServiceInstance().getRootFolder();
Datacenter dc = DatacenterHelper.findDatacenterFromDatastore(rootFolder, dsName);
if (dc == null) {
throw new DatacenterNotFoundException("Cant locate datacenter from datastore name: " + dsName);
}
return dc;
}
// Getters and setters.
public String getVolumeName() {
return volumeName;
}
public void setVolumeName(String volumeName) {
this.volumeName = volumeName;
}
public Float getSize() {
return size;
}
public void setSize(Float size) {
this.size = size;
}
public String getFullPath() {
return fullPath;
}
public void setFullPath(String fullPath) {
this.fullPath = fullPath;
}
public String getMountPoint() {
return mountPoint;
}
public void setMountPoint(String mountPoint) {
this.mountPoint = mountPoint;
}
public boolean isMainVolume() {
return mainVolume;
}
public void setMainVolume(boolean mainVolume) {
this.mainVolume = mainVolume;
}
public String getVolumeState() {
return volumeState;
}
public void setVolumeState(String volumeState) {
this.volumeState = volumeState;
}
public boolean isExist() {
return exist;
}
public void setExist(boolean exist) {
this.exist = exist;
}
public boolean isAttached() {
return attached;
}
public void setAttached(boolean attached) {
this.attached = attached;
}
public String getVmName() {
return vmName;
}
public void setVmName(String vmName) {
this.vmName = vmName;
}
public Calendar getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Calendar modifiedDate) {
this.modifiedDate = modifiedDate;
}
}