/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.nodecollector.domain.collectors;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.abiquo.model.enumerator.HypervisorType;
import com.abiquo.nodecollector.constants.MessageValues;
import com.abiquo.nodecollector.domain.Collector;
import com.abiquo.nodecollector.exception.CannotExecuteException;
import com.abiquo.nodecollector.exception.CollectorException;
import com.abiquo.nodecollector.exception.ConnectionException;
import com.abiquo.nodecollector.exception.LoginException;
import com.abiquo.nodecollector.exception.NoManagedException;
import com.abiquo.nodecollector.exception.NodecollectorException;
import com.abiquo.nodecollector.utils.ResourceComparator;
import com.abiquo.server.core.infrastructure.nodecollector.HostDto;
import com.abiquo.server.core.infrastructure.nodecollector.HostStatusEnumType;
import com.abiquo.server.core.infrastructure.nodecollector.ResourceEnumType;
import com.abiquo.server.core.infrastructure.nodecollector.ResourceType;
import com.abiquo.server.core.infrastructure.nodecollector.VirtualDiskEnumType;
import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemCollectionDto;
import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemDto;
import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemStatusEnumType;
import com.vmware.vim25.ArrayOfHostHostBusAdapter;
import com.vmware.vim25.DatastoreSummary;
import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.FileFault;
import com.vmware.vim25.FileInfo;
import com.vmware.vim25.FileQuery;
import com.vmware.vim25.FolderFileQuery;
import com.vmware.vim25.HostConfigInfo;
import com.vmware.vim25.HostConnectInfo;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.HostDatastoreConnectInfo;
import com.vmware.vim25.HostHardwareInfo;
import com.vmware.vim25.HostHostBusAdapter;
import com.vmware.vim25.HostInternetScsiHba;
import com.vmware.vim25.HostListSummary;
import com.vmware.vim25.HostNetworkInfo;
import com.vmware.vim25.HostProxySwitch;
import com.vmware.vim25.HostVirtualSwitch;
import com.vmware.vim25.InvalidLogin;
import com.vmware.vim25.KeyAnyValue;
import com.vmware.vim25.LicenseManagerLicenseInfo;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec;
import com.vmware.vim25.OptionValue;
import com.vmware.vim25.PhysicalNic;
import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.RuntimeFault;
import com.vmware.vim25.SelectionSpec;
import com.vmware.vim25.ServiceContent;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.VimPortType;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceFileBackingInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualDiskRawDiskMappingVer1BackingInfo;
import com.vmware.vim25.VirtualDiskRawDiskVer2BackingInfo;
import com.vmware.vim25.VirtualDiskSparseVer2BackingInfo;
import com.vmware.vim25.VirtualMachineConfigInfo;
import com.vmware.vim25.VirtualMachineRuntimeInfo;
import com.vmware.vim25.mo.Datacenter;
import com.vmware.vim25.mo.HostDatastoreBrowser;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.InventoryNavigator;
import com.vmware.vim25.mo.ManagedEntity;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.Task;
/**
* Collector implementation for ESXi hypervisors.
*
* @author jdevesa@abiquo.com
*/
@Collector(type = HypervisorType.VMX_04, order = 0)
public class ESXiCollector extends AbstractCollector
{
/** Folder mark perfix. */
private static String DATASTORE_UUID_MARK = "datastoreuuid.";
/** Pattern to match with the mark folder. */
private static String DATASTORE_UUID_MARK_PATTERN = DATASTORE_UUID_MARK + "*";
private static PropertySpec[] hostSystemSpec;
private static final Integer KBYTE = 1024;
/** The logger. */
private static final Logger LOGGER = LoggerFactory.getLogger(ESXiCollector.class);
private static final Integer MEGABYTE = 1048576;
private static final String POWERED_OFF = "poweredOff";
private static final String POWERED_ON = "poweredOn";
private static SelectionSpec[] selectionSpecs;
private static ManagedObjectReference siMoref;
private static final String NAME = "name";
private static final String HARDWARE = "hardware";
private static final String HOST_SUMMARY = "summary";
private static PropertySpec[] virtualMachineSpec;
static
{
// Create an instance of the ServiceInstance managed object
siMoref = new ManagedObjectReference();
siMoref.setType("ServiceInstance");
siMoref.set_value("ServiceInstance");
// Set the transactionSpec route to Host. We build the whole inventory tree
// to search any 'managedObjectType'
selectionSpecs = buildFullTraversal();
// Set the property spec to specify which object I want to retrieve
hostSystemSpec = new PropertySpec[] {new PropertySpec()};
hostSystemSpec[0].setType("HostSystem");
hostSystemSpec[0].setAll(true);
virtualMachineSpec = new PropertySpec[] {new PropertySpec()};
virtualMachineSpec[0].setType("VirtualMachine");
virtualMachineSpec[0].setAll(true);
}
/**
* This method creates a SelectionSpec[] to traverses the entire inventory tree starting at a
* Folder.
*
* @return The SelectionSpec[]
*/
private static SelectionSpec[] buildFullTraversal()
{
SelectionSpec rpToVmSpec = createSelectionSpec("rpToVm");
SelectionSpec rpToRpSpec = createSelectionSpec("rpToRp");
// ResourcePool to itself
TraversalSpec rpToRp =
createTraversalSpec("rpToRp", "ResourcePool", "resourcePool", rpToRpSpec, rpToVmSpec);
// ResourcePool to Vm
TraversalSpec rpToVm =
createTraversalSpec("rpToVm", "ResourcePool", "vm", new SelectionSpec[] {});
// ComputerResource to ResourcePool
TraversalSpec crToRp =
createTraversalSpec("crToRp", "ComputeResource", "resourcePool", rpToRpSpec, rpToVmSpec);
// ComputerResource to Host
TraversalSpec crToH =
createTraversalSpec("crToH", "ComputeResource", "host", new SelectionSpec[] {});
SelectionSpec visitFoldersSpec = createSelectionSpec("visitFolders");
// Datacenter to hostFolder
TraversalSpec dcToHf =
createTraversalSpec("dcToHf", "Datacenter", "hostFolder", visitFoldersSpec);
// Datacenter to vm Folder
TraversalSpec dcToVmf =
createTraversalSpec("dcToVmf", "Datacenter", "vmFolder", visitFoldersSpec);
// Host to Vm
TraversalSpec hToVm = createTraversalSpec("HToVm", "HostSystem", "vm", visitFoldersSpec);
// Root folder to others
TraversalSpec visitFolders =
createTraversalSpec("visitFolders", "Folder", "childEntity", visitFoldersSpec,
createSelectionSpec("dcToHf"), createSelectionSpec("dcToVmf"),
createSelectionSpec("crToH"), createSelectionSpec("crToRp"),
createSelectionSpec("HToVm"), rpToVmSpec);
return new SelectionSpec[] {visitFolders, dcToVmf, dcToHf, crToH, crToRp, rpToRp, hToVm,
rpToVm};
}
private static SelectionSpec createSelectionSpec(final String name)
{
SelectionSpec s = new SelectionSpec();
s.setName(name);
return s;
}
private static TraversalSpec createTraversalSpec(final String name, final String type,
final String path, final SelectionSpec... selectSet)
{
TraversalSpec traversalSpec = new TraversalSpec();
traversalSpec.setName(name);
traversalSpec.setType(type);
traversalSpec.setPath(path);
traversalSpec.setSkip(false);
traversalSpec.setSelectSet(selectSet);
return traversalSpec;
}
/**
* The api version of the Hypervisor.
*/
private String apiVersion;
private ManagedObjectReference rootFolder;
private ObjectSpec[] rootObjSpecs;
private ServiceInstance serviceInstance;
/**
* Default constructor.
*
* @param ipAddress address where to locate the Hypervisor.
*/
public ESXiCollector()
{
// ignore certs
System.setProperty("org.apache.axis.components.net.SecureSocketFactory",
"org.apache.axis.components.net.SunFakeTrustSocketFactory");
}
@Override
public void connect(final String user, final String password) throws ConnectionException,
LoginException
{
try
{
serviceInstance =
new ServiceInstance(new URL("https://" + getIpAddress() + "/sdk"),
user,
password,
true);
}
catch (InvalidLogin e)
{
LOGGER.warn("Invalid credentials for hypervisor {} at cloud node ", this
.getHypervisorType().name(), getIpAddress());
throw new LoginException(MessageValues.LOG_EXCP, e);
}
catch (Exception e)
{
LOGGER.warn("Could not connect at hypervisor {} at cloud node {}", this
.getHypervisorType().name(), getIpAddress());
throw new ConnectionException(MessageValues.CONN_EXCP_I, e);
}
}
@Override
public void disconnect() throws CollectorException
{
if (serviceInstance != null && serviceInstance.getServerConnection() != null)
{
serviceInstance.getServerConnection().logout();
}
}
/**
* Retrieve a single object.
*
* @param mor Managed Object Reference to get contents for
* @param propertyName of the object to retrieve
* @return retrieved object
* @throws Exception any encapsulated exception.
*/
public Object getDynamicProperty(final ManagedObjectReference mor, final String propertyName)
throws Exception
{
ObjectContent[] objContent = getObjectProperties(null, mor, new String[] {propertyName});
Object propertyValue = null;
if (objContent != null)
{
DynamicProperty[] dynamicProperty = objContent[0].getPropSet();
if (dynamicProperty != null)
{
// Check the dynamic propery for ArrayOfXXX object
Object dynamicPropertyVal = dynamicProperty[0].getVal();
String dynamicPropertyName = dynamicPropertyVal.getClass().getName();
if (dynamicPropertyName.indexOf("ArrayOf") != -1)
{
String methodName =
dynamicPropertyName.substring(dynamicPropertyName.indexOf("ArrayOf")
+ "ArrayOf".length(), dynamicPropertyName.length());
/*
* If object is ArrayOfX object, then get the X by invoking getX() on the
* object. For Ex: ArrayOfManagedObjectReference.getManagedObjectReference()
* returns ManagedObjectReference[] array.
*/
if (methodExists(dynamicPropertyVal, "get" + methodName, null))
{
methodName = "get" + methodName;
}
else
{
/*
* Construct methodName for ArrayOf primitive types Ex: For ArrayOfInt,
* methodName is get_int
*/
methodName = "get_" + methodName.toLowerCase();
}
Method getMorMethod =
dynamicPropertyVal.getClass().getDeclaredMethod(methodName, (Class[]) null);
propertyValue = getMorMethod.invoke(dynamicPropertyVal, (Object[]) null);
}
else
{
propertyValue = dynamicPropertyVal;
}
}
}
return propertyValue;
}
@Override
public HostDto getHostInfo() throws NodecollectorException
{
LOGGER.debug("Getting information for host at: {}", getIpAddress());
final HostHardwareInfo hardwareInfo;
final HostDto physicalInfo = new HostDto();
// Check the license of the ESXi
hasValidLicense();
// We take the first one because we use ESXi not VirtualCenter, and there is only one host
// managed
ObjectContent hostSystem;
try
{
LOGGER.debug("Getting Managed Object information for the host from inventory...");
hostSystem = getManagedObjectReferencesFromInventory(hostSystemSpec)[0];
LOGGER.debug("Got the information of the Managed Object");
}
catch (RemoteException e)
{
LOGGER.error("Unexpected exception:", e);
throw new CollectorException(MessageValues.COLL_EXCP_PH, e);
}
if (isInMaintenanceMode(hostSystem))
{
throw new NoManagedException(MessageValues.NOMAN_ESXI_LIC);
}
// Please check the following url to understand the DynamicProperty indexation in this code
// http://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/
// The order of the properties in the web, are the same here.
try
{
hardwareInfo = (HostHardwareInfo) getDynamicProperty(hostSystem, HARDWARE);
physicalInfo.setName((String) getDynamicProperty(hostSystem, NAME));
physicalInfo.setCpu(Long.valueOf(((Short) hardwareInfo.getCpuInfo().getNumCpuCores())
.toString()));
physicalInfo.setRam(hardwareInfo.getMemorySize());
physicalInfo.setHypervisor(getHypervisorType().getValue());
physicalInfo.setVersion(getApiVersion());
physicalInfo.getResources().addAll(getHostResources(hostSystem));
}
catch (Exception e1)
{
LOGGER.error(MessageValues.UNP_EXCP);
throw new CollectorException(MessageValues.UNP_EXCP);
}
try
{
LOGGER.debug("Getting the storage system information for the host...");
ManagedObjectReference storageSystemMor = getStorageSystem(hostSystem);
LOGGER.debug("Storage information retrieved...");
LOGGER.debug("Getting the iSCSI initiator...");
final String initiatorIQN = getInternetSCSIInitiatorIQN(hostSystem, storageSystemMor);
LOGGER.debug("iSCSI initiator retrieved");
physicalInfo.setInitiatorIQN(initiatorIQN);
}
catch (CollectorException e)
{
// XXX add the cause ??
LOGGER.warn(MessageValues.WARN_INITIATOR_IQN);
}
physicalInfo.setStatus(HostStatusEnumType.MANAGED);
LOGGER.debug("Information retreived for host at: {}", getIpAddress());
return physicalInfo;
}
/**
* @return the myConn
*/
public VimPortType getMyConn()
{
return serviceInstance.getServerConnection().getVimService();
}
/**
* Retrieve contents for a single object based on the property collector registered with the
* service.
*
* @param collector Property collector registered with service.
* @param mobj Managed Object Reference to get contents for.
* @param properties names of properties of object to retrieve.
* @return retrieved object contents.
* @throws Exception for any encapsulated exception.
*/
public ObjectContent[] getObjectProperties(final ManagedObjectReference collector,
final ManagedObjectReference mobj, final String[] properties) throws Exception
{
if (mobj == null)
{
return null;
}
ManagedObjectReference usecoll = collector;
if (usecoll == null)
{
usecoll = getServiceContent().getPropertyCollector();
}
PropertyFilterSpec filter = new PropertyFilterSpec();
PropertySpec propertySpec = new PropertySpec();
propertySpec.setAll(Boolean.valueOf(properties == null || properties.length == 0));
propertySpec.setType(mobj.getType());
propertySpec.setPathSet(properties);
filter.setPropSet(new PropertySpec[] {propertySpec});
ObjectSpec objectSpec = new ObjectSpec();
objectSpec.setObj(mobj);
objectSpec.setSkip(Boolean.FALSE);
filter.setObjectSet(new ObjectSpec[] {objectSpec});
return getMyConn().retrieveProperties(usecoll, new PropertyFilterSpec[] {filter});
}
/**
* @return the serviceContent
*/
public ServiceContent getServiceContent()
{
return serviceInstance.getServiceContent();
}
@Override
public VirtualSystemCollectionDto getVirtualMachines() throws CollectorException
{
final VirtualSystemCollectionDto virtualMachines = new VirtualSystemCollectionDto();
ObjectContent[] esxiMachines;
try
{
esxiMachines = getManagedObjectReferencesFromInventory(virtualMachineSpec);
}
catch (RemoteException e)
{
LOGGER.error("Unexpected exception:", e);
throw new CollectorException(MessageValues.COLL_EXCP_VM, e);
}
if (esxiMachines != null)
{
for (ObjectContent esxiMachine : esxiMachines)
{
String machineName = null;
try
{
// get the machine name for logging.
machineName = (String) getDynamicProperty(esxiMachine, "name");
machineName = decodeURLRawString(machineName);
// Get the virtual machine configuration
Object obj = getDynamicProperty(esxiMachine, "config");
if (obj != null)
{
VirtualMachineConfigInfo vmConfig = (VirtualMachineConfigInfo) obj;
VirtualSystemDto vSys = new VirtualSystemDto();
vSys.setName(decodeURLRawString(vmConfig.getName()));
vSys.setStatus(getStateFromESXiMachine(esxiMachine));
vSys.setUuid(vmConfig.getUuid());
vSys.setCpu(Long.valueOf(vmConfig.getHardware().getNumCPU()));
vSys.setRam(Long.valueOf(vmConfig.getHardware().getMemoryMB()) * MEGABYTE);
vSys.setVport(getVPortFromExtraConfig(vmConfig.getExtraConfig()));
// Recover the list of disks for each virtual system
HashMap<Integer, VirtualDevice> deviceHashMap =
buildDeviceMap(vmConfig.getHardware().getDevice());
for (VirtualDevice device : vmConfig.getHardware().getDevice())
{
if (device instanceof VirtualDisk)
{
ResourceType diskResource = createDiskFromVirtualDevice(device);
// add the controller key.. like IDE 0:0
VirtualDevice controller =
deviceHashMap.get(device.getControllerKey());
diskResource.setAttachment(controller.getDeviceInfo().label + ":"
+ device.getUnitNumber());
vSys.getResources().add(diskResource);
}
}
virtualMachines.getVirtualSystems().add(vSys);
}
else
{
LOGGER.warn("Could not retrieve virtual machine " + machineName
+ " because it is inaccessible");
}
}
catch (Exception e)
{
LOGGER.error("Could not retrieve virtual machine " + machineName
+ " information. Cause: ", e);
}
}
}
return virtualMachines;
}
/**
* @param apiVersion the apiVersion to set
*/
public void setApiVersion(final String apiVersion)
{
this.apiVersion = apiVersion;
}
/**
* Gets the internet SCSI controller (Host Bus Adapter -- config.storageDevice.scsiLun)
* initiator IQN.
*/
protected String getInternetSCSIInitiatorIQN(final ObjectContent hostSystemOc,
final ManagedObjectReference storageSystemMor) throws CollectorException
{
String[] hbasPropDesc = new String[] {"config.storageDevice.hostBusAdapter"};
ManagedObjectReference hostSystemMor = hostSystemOc.getObj();
HostInternetScsiHba iscsi = null;
HostHostBusAdapter[] hbas;
if (!isInternetSCSIEnable(hostSystemOc, storageSystemMor))
{
final String cause = "Can not enable the software iSCSI controller"; // internal message
throw new CollectorException(cause);
}
try
{
ManagedObjectReference collector = null; // obtain?
ObjectContent[] hostBusAdapters =
getObjectProperties(collector, hostSystemMor, hbasPropDesc);
if (hostBusAdapters == null || hostBusAdapters.length != 1)
{
final String cause =
"Can not retrieve avaiable Host Bus Adapters on the Storage Device";// internal
// message
LOGGER.error(cause);
throw new CollectorException(cause);
}
// TODO propSet at 0 -- check by type
ArrayOfHostHostBusAdapter arrHbas =
(ArrayOfHostHostBusAdapter) hostBusAdapters[0].getPropSet()[0].getVal();
hbas = arrHbas.getHostHostBusAdapter();
}
catch (Exception e)
{
final String cause =
"Can not retrieve avaiable Host Bus Adapters on the Storage Device";// internal
// message
LOGGER.error(cause);
throw new CollectorException(cause, e);
}
for (HostHostBusAdapter hba : hbas)
{
if (hba instanceof HostInternetScsiHba)
{
HostInternetScsiHba iscsicurrent = (HostInternetScsiHba) hba;
LOGGER.debug(String.format(
"[iscsi] Device:%s Driver:%s Model:%s\n\tAlias:%s Name:%s Software:%s",
iscsicurrent.getDevice(), iscsicurrent.getDriver(), iscsicurrent.getModel(),
iscsicurrent.getIScsiAlias(), iscsicurrent.getIScsiName(),
String.valueOf(iscsicurrent.isIsSoftwareBased())));
if (iscsicurrent.isIsSoftwareBased()
&& iscsicurrent.getModel().equalsIgnoreCase("iSCSI Software Adapter"))
{
iscsi = iscsicurrent;
}
}
}
if (iscsi == null)
{
final String cause = "Can not find the iSCSI Host Bus Adapter";
LOGGER.error(cause);
throw new CollectorException(cause); // internal
// message
}
LOGGER.debug(String.format(
"[iscsi] SELECTED :\n Device:%s Driver:%s Model:%s\n\tAlias:%s Name:%s Software:%s",
iscsi.getDevice(), iscsi.getDriver(), iscsi.getModel(), iscsi.getIScsiAlias(),
iscsi.getIScsiName(), String.valueOf(iscsi.isIsSoftwareBased())));
return iscsi.getIScsiName();
}
/**
* Determines of a method 'methodName' exists for the Object 'obj'.
*
* @param obj The Object to check
* @param methodName The method name
* @param parameterTypes Array of Class objects for the parameter types
* @return true if the method exists, false otherwise
*/
boolean methodExists(final Object obj, final String methodName,
final Class< ? >[] parameterTypes)
{
boolean exists = false;
try
{
Method method = obj.getClass().getMethod(methodName, parameterTypes);
if (method != null)
{
exists = true;
}
}
catch (Exception e)
{
return exists;
}
return exists;
}
/**
* Builds a hasmap from list of devices.
*
* @param devices list of virtual devices
* @return hash map based on key devices.
*/
private HashMap<Integer, VirtualDevice> buildDeviceMap(final VirtualDevice[] devices)
{
HashMap<Integer, VirtualDevice> deviceMap = new HashMap<Integer, VirtualDevice>();
for (VirtualDevice device : devices)
{
deviceMap.put(device.getKey(), device);
}
return deviceMap;
}
/**
* Create a new datastore folder mark.
*
* @return the just created UUID for the folder mark
*/
private String createDatastoreFolderMark(final Datacenter dc, final String dsName)
throws CollectorException
{
String folderUuidMark = UUID.randomUUID().toString();
String directoryOnDatastore =
String.format("%s %s%s", dsName, DATASTORE_UUID_MARK, folderUuidMark);
try
{
// do not create parent folders (is on the root)
serviceInstance.getFileManager().makeDirectory(directoryOnDatastore, dc, false);
}
catch (FileFault e)
{
LOGGER.error("Can not create the folder mark at [{}], caused by file fault {}", dsName,
e);
throw new CollectorException(MessageValues.DATASTRORE_MARK, e);
}
catch (Exception e)
{
LOGGER.error("Can not create the folder mark at [{}]\n{}", dsName, e);
throw new CollectorException(MessageValues.DATASTRORE_MARK, e);
}
return folderUuidMark;
}
/**
* Create a {@link Disk} object from a virtual device.
*
* @param device device of remote virtual system
* @return a built object.
* @throws CollectorException
* @throws Exception
*/
private ResourceType createDiskFromVirtualDevice(final VirtualDevice device)
throws CollectorException
{
try
{
VirtualDisk diskDevice = (VirtualDisk) device;
ResourceType hardDisk = new ResourceType();
hardDisk.setUnits(diskDevice.getCapacityInKB() * KBYTE);
// Check if we can say it is the system disk or the extra disk.
if (diskDevice.getDeviceInfo().getLabel().equals("Hard disk 1"))
{
hardDisk.setLabel("SYSTEM DISK");
}
else
{
hardDisk.setLabel("EXTRA DISK");
}
diskDevice.getControllerKey();
// get the attachment
if (diskDevice.getBacking() instanceof VirtualDeviceFileBackingInfo)
{
VirtualDeviceFileBackingInfo backing =
(VirtualDeviceFileBackingInfo) diskDevice.getBacking();
String fileName = backing.getFileName();
if (diskDevice.getBacking() instanceof VirtualDiskRawDiskMappingVer1BackingInfo)
{
hardDisk.setResourceType(ResourceEnumType.VOLUME_DISK);
}
else
{
hardDisk.setResourceType(ResourceEnumType.HARD_DISK);
}
VirtualDiskEnumType vdet = resolveDiskFileType(backing);
hardDisk.setResourceSubType(vdet.value());
if (vdet == VirtualDiskEnumType.VMDK_FLAT)
{
hardDisk.setAddress(parseImagePathAddingFlat(fileName));
}
else
{
hardDisk.setAddress(fileName);
}
ManagedObjectReference datastore = backing.getDatastore();
String datastoreName = (String) getDynamicProperty(datastore, "name");
hardDisk.setConnection(datastoreName);
}
// The disk belong to a device. The correct statement here would be
// 'diskDevice.getBacking() instanceof VirtualDeviceDeviceBackingInfo' because
// in the hierarchy has
// the same level of VirtualDeviceFileBackingInfo, but the only subclass of
// VirtualDevicefileBackingInfo we can treat is
// VirtualDiskRawDiskVer2BackingInfo. So, in order to
// have a clearer code, we put it directly.
// In the future, if we can recover disk information from Floppy or CDRoom, we
// should change this
// condition
else if (diskDevice.getBacking() instanceof VirtualDiskRawDiskVer2BackingInfo)
{
VirtualDiskRawDiskVer2BackingInfo backing =
(VirtualDiskRawDiskVer2BackingInfo) diskDevice.getBacking();
hardDisk.setAddress(backing.getDescriptorFileName());
hardDisk.setResourceSubType(VirtualDiskEnumType.RAW);
hardDisk.setResourceType(ResourceEnumType.VOLUME_DISK);
hardDisk.setConnection("unknown");
}
// Other disk types not used.
else
{
hardDisk.setAddress("unknown");
hardDisk.setResourceSubType(VirtualDiskEnumType.VMDK_FLAT);
hardDisk.setResourceType(ResourceEnumType.HARD_DISK);
hardDisk.setConnection("unknown");
}
return hardDisk;
}
catch (Exception e)
{
throw new CollectorException(MessageValues.COLL_EXCP_VM, e);
}
}
/** Reused Query specification to locate the folder mark (UUID) */
private HostDatastoreBrowserSearchSpec createQueryDatastoreFolderMark()
{
HostDatastoreBrowserSearchSpec querySpec = new HostDatastoreBrowserSearchSpec();
querySpec.setQuery(new FileQuery[] {new FolderFileQuery()}); // VmDiskFileQuery() FileQuery
querySpec.setSearchCaseInsensitive(false);
querySpec.setMatchPattern(new String[] {DATASTORE_UUID_MARK_PATTERN});
// FileQueryFlags fqf = new FileQueryFlags();
// fqf.setFileSize(true);
// fqf.setModification(true);
// querySpec.setDetails(fqf);
return querySpec;
}
/**
* @return the api version of the esxi hypervisor.
*/
private String getApiVersion()
{
return apiVersion;
}
/**
* Gets the vSpherer Datatacenter. Assume its named ''ha-datacenter''.
*/
private Datacenter getDatacenter() throws CollectorException
{
Datacenter dc;
try
{
dc =
(Datacenter) new InventoryNavigator(serviceInstance.getRootFolder())
.searchManagedEntity("Datacenter", "ha-datacenter");
}
catch (Exception e)
{
throw new CollectorException(MessageValues.CONN_EXCP_I);
}
if (dc == null)
{
LOGGER.error("Datacenter ''ha-datacenter'' not found.");
throw new CollectorException(MessageValues.DATACENTER_NOT_FOUND);
}
return dc;
}
/**
* Locate or create the datastore folder mark to determine if the datastore is being shared
* across hypervisors.
*
* @return a Datastore UUID
* @throws CollectorException
*/
private String getDatastoreUuidMark(final String dsName, final HostDatastoreBrowser dsBrowser,
final Datacenter dc) throws CollectorException
{
String uuid = null;
Task searchtask;
try
{
// search on the top of the filesystem (do not navegate subfolders)
searchtask = dsBrowser.searchDatastore_Task(dsName, createQueryDatastoreFolderMark()); // TODO
// static
searchtask.waitForTask();
if (searchtask.getTaskInfo().state == TaskInfoState.success)
{
Object objres = searchtask.getTaskInfo().getResult();
HostDatastoreBrowserSearchResults result =
(HostDatastoreBrowserSearchResults) objres;
if (result.getFile() != null)
{
if (result.getFile().length != 1)
{
throw new CollectorException(MessageValues.DATASTRORE_MULTIPLE_MARKS);
}
FileInfo fi = result.getFile()[0];
String foldername = fi.getPath(); // path = name in the root
LOGGER.debug("Datastore folder mark found [{}]", foldername);
if (!foldername.startsWith(DATASTORE_UUID_MARK))
{
LOGGER.error("The datastore folder mark isn't the expected [{}].",
foldername);
throw new CollectorException(MessageValues.DATASTRORE_MARK);
}
uuid = foldername.substring(DATASTORE_UUID_MARK.length());
}
else
{
// any folder mark found
}
}
else
{
// folder mark not found
}
}
catch (Exception e)
{
e.printStackTrace(); // delme
LOGGER.error("Can't identify the datastore [{}] uuid", dsName);
throw new CollectorException(MessageValues.DATASTRORE_MARK, e);
}
if (uuid == null)
{
LOGGER.debug(String.format(
"Datastore %s on Host [%s] hasn't any folder mark, creating it.", dsName,
getIpAddress()));
uuid = createDatastoreFolderMark(dc, dsName);
}
LOGGER.debug("Datastore {} UUID [{}]", dsName, uuid);
return uuid;
}
/**
* Return the property of the ObjectContent with name 'propertyName'
*
* @param obj object content to get its properties
* @param propertyName property name to retrieve
* @return
* @throws Exception
*/
private Object getDynamicProperty(final ObjectContent obj, final String propertyName)
throws Exception
{
if (obj != null)
{
DynamicProperty[] dynamicProperties = obj.getPropSet();
if (dynamicProperties != null)
{
for (DynamicProperty currentProp : dynamicProperties)
{
if (currentProp.getName().equalsIgnoreCase(propertyName))
{
return currentProp.getVal();
}
}
}
}
return null;
}
/**
* Retrieves the list of resources of the host.
*
* @param hostSystem {@link ObjectContent} object. It is enough to reach all the information we
* need.
* @param repositoryLocation the repository location is the Abiquo image repository and we don't
* want to retrieve it.
* @return a list of Resources
* @throws CollectorException
* @throws RemoteException
* @throws RuntimeFault
*/
private List<ResourceType> getHostResources(final ObjectContent hostSystem)
throws CollectorException
{
List<ResourceType> resources = new ArrayList<ResourceType>();
HostConfigInfo config = (HostConfigInfo) hostSystem.getPropSet()[2].getVal();
String repositoryLocation =
System.getProperty("abiquo.appliancemanager.repositoryLocation");
// Network resouces
HostNetworkInfo network = config.getNetwork();
if (network.getVswitch() != null)
{
for (HostVirtualSwitch vswitch : network.getVswitch())
{
if (vswitch.pnic != null)
{
ResourceType resource = new ResourceType();
resource.setResourceType(ResourceEnumType.NETWORK_INTERFACE);
String[] pnics = vswitch.getPnic();
String address = "";
if (pnics.length > 0)
{
for (PhysicalNic nic : network.getPnic())
{
if (nic.getKey().equalsIgnoreCase(pnics[0]))
{
address = nic.getMac();
}
}
}
resource.setAddress(address);
resource.setElementName(vswitch.getName());
resources.add(resource);
}
}
}
// If the dvs is enabled, retrieve the list of Distributed Virtual Switch
Boolean dvsEnabled = Boolean.valueOf(System.getProperty("abiquo.dvs.enabled"));
if (dvsEnabled && network.getProxySwitch() != null)
{
for (HostProxySwitch dvswitch : network.getProxySwitch())
{
// Add it only if it has any physical NIC attached and its name starts with 'dvs'
if (dvswitch.dvsName.toLowerCase().startsWith("dvs") && dvswitch.pnic != null)
{
ResourceType resource = new ResourceType();
resource.setResourceType(ResourceEnumType.NETWORK_INTERFACE);
String[] pnics = dvswitch.getPnic();
String address = "";
if (pnics.length > 0)
{
for (PhysicalNic nic : network.getPnic())
{
if (nic.getKey().equalsIgnoreCase(pnics[0]))
{
address = nic.getMac();
}
}
}
resource.setAddress(address);
resource.setElementName(dvswitch.getDvsName());
resources.add(resource);
}
}
}
// Datastores resources
HostConnectInfo hostInfo;
HostDatastoreBrowser dsBrowser;
Datacenter dc;
try
{
hostInfo = getMyConn().queryHostConnectionInfo(hostSystem.getObj());
dsBrowser = getHostSystem().getDatastoreBrowser();
dc = getDatacenter();
}
catch (RuntimeFault e1)
{
LOGGER.error("Unexpected exception:", e1);
throw new CollectorException(MessageValues.COLL_EXCP_PH);
}
catch (RemoteException e1)
{
LOGGER.error("Unexpected exception:", e1);
throw new CollectorException(MessageValues.COLL_EXCP_PH);
}
for (HostDatastoreConnectInfo datastoreConnect : hostInfo.getDatastore())
{
Long size;
Long freeSize;
DatastoreSummary datastoreSummary = datastoreConnect.getSummary();
if (datastoreSummary.isAccessible())
{
if (datastoreSummary.getType().equalsIgnoreCase("NFS"))
{
if (checkNFS(repositoryLocation, datastoreSummary.getDatastore().get_value()))
{
continue;
}
}
// datastoreSummary.getDatastore()
String dsName = String.format("[%s]", datastoreSummary.getName());
String datastoreUuidMark = getDatastoreUuidMark(dsName, dsBrowser, dc);
size = datastoreSummary.getCapacity();
freeSize = datastoreSummary.getFreeSpace();
ResourceType resource = new ResourceType();
resource.setResourceType(ResourceEnumType.HARD_DISK);
resource.setAddress(datastoreSummary.getName());
resource.setElementName(datastoreSummary.getName());
resource.setUnits(size);
resource.setAvailableUnits(freeSize);
resource.setConnection(datastoreUuidMark);
resources.add(resource);
} // accessible
}// datastores
Collections.sort(resources, new ResourceComparator());
return resources;
}
/**
* Gets the Host System on the Root Folder. Assume single Host System and named using its IP.
*/
private HostSystem getHostSystem() throws CollectorException
{
// hostname match the ip address
String hostname = getIpAddress();
HostSystem host = null;
ManagedEntity[] hosts;
try
{
hosts =
new InventoryNavigator(serviceInstance.getRootFolder())
.searchManagedEntities("HostSystem");
// .searchManagedEntity("HostSystem", hostname);
}
catch (Exception e)
{
throw new CollectorException(MessageValues.CONN_EXCP_I);
}
if (hosts == null || hosts.length != 1)
{
LOGGER.error("Host System {} not found.", hostname);
throw new CollectorException(MessageValues.HOST_SYSTEM_NOT_FOUND);
}
host = (HostSystem) hosts[0];
return host;
}
/**
* This auxiliar method return a list of ManagedObjectReference encapsulated into an
* ObjectContent instance. Informing the name of the managed object reference as a parameter,
* will return the list of MORs whatever is its place into the inventory hierarchy of the VI
* SDK.
*
* @param managedObjectType name of the MOR we want to retrieve
* @return list of ObjectContent with the result of the search.
* @throws RemoteException if there is any problem working with the remote objects
*/
private ObjectContent[] getManagedObjectReferencesFromInventory(final PropertySpec[] propSpec)
throws RemoteException
{
// Set the PropertyFilter Spec previous to put toghether the property Spec
// and the objectSpec
PropertyFilterSpec propertyFilter = new PropertyFilterSpec();
propertyFilter.setPropSet(propSpec);
propertyFilter.setObjectSet(getRootObjectSpec());
// Do the search! This search will return all the matching MOR of the
// 'managedObjectType' type
ObjectContent[] objectContent =
getMyConn().retrieveProperties(getServiceContent().getPropertyCollector(),
new PropertyFilterSpec[] {propertyFilter});
return objectContent;
}
private ManagedObjectReference getRootFolder()
{
if (rootFolder == null)
{
rootFolder = getServiceContent().getRootFolder();
}
return rootFolder;
}
private ObjectSpec[] getRootObjectSpec()
{
if (rootObjSpecs == null)
{
rootObjSpecs = new ObjectSpec[] {new ObjectSpec()};
rootObjSpecs[0].setObj(getRootFolder());
rootObjSpecs[0].setSkip(Boolean.FALSE);
rootObjSpecs[0].setSelectSet(selectionSpecs);
}
return rootObjSpecs;
}
/**
* Return the state of deployed machine.
*
* @param esxiMachine machine deployed
* @return VirtualMachineStateType representing the state.
* @throws CollectorException, if can not obtain the VirtualMachineRuntimeInfo property
*/
private VirtualSystemStatusEnumType getStateFromESXiMachine(final ObjectContent esxiMachine)
throws CollectorException
{
// TODO revise for [ABICLOUDPREMIUM-324]
// final Integer vmRuntimeProperty;
// if (getApiVersion().startsWith("2.5"))
// {
// vmRuntimeProperty = 22;
// }
// else
// {
// vmRuntimeProperty = 23;
// }
//
// String stateString =
// ((VirtualMachineRuntimeInfo) esxiMachine.getPropSet(vmRuntimeProperty).getVal())
// .getPowerState().getValue();
// Solve [ABICLOUDPREMIUM-321]
VirtualMachineRuntimeInfo runInfo = null;
boolean found = false;
DynamicProperty[] dps = esxiMachine.getPropSet();
for (int i = 0; i < dps.length && !found; i++)
{
Object dp = dps[i].getVal();
if (dp instanceof VirtualMachineRuntimeInfo)
{
runInfo = (VirtualMachineRuntimeInfo) dp;
found = true;
}
}
if (runInfo == null)
{
final String cause = "Can not retrieve VirtualMachineRuntimeInfo of machine";
LOGGER.error(cause);
throw new CollectorException(cause);
}
String stateString = runInfo.getPowerState().name();
VirtualSystemStatusEnumType vmStateType;
// parametrize the state
if (stateString.equalsIgnoreCase(POWERED_OFF))
{
vmStateType = VirtualSystemStatusEnumType.OFF;
}
else if (stateString.equalsIgnoreCase(POWERED_ON))
{
vmStateType = VirtualSystemStatusEnumType.ON;
}
else
{
vmStateType = VirtualSystemStatusEnumType.PAUSED;
}
return vmStateType;
}
/**
* Gets the storage system reference from the host system's configuration manager.
*/
private ManagedObjectReference getStorageSystem(final ObjectContent hostSystemOc)
throws CollectorException
{
String[] storageSystemPropDesc = new String[] {"configManager.storageSystem"};
ManagedObjectReference hostSystemMor = hostSystemOc.getObj();
ManagedObjectReference storageSystem;
try
{
ManagedObjectReference collector = null; // obtain?
ObjectContent[] storages =
this.getObjectProperties(collector, hostSystemMor, storageSystemPropDesc);
// TODO storages.length == 1;
storageSystem = storages[0].getObj();
}
catch (Exception e1)
{
final String cause = "Can not get Configuration Manager on the Host System";
LOGGER.error(cause, e1);
throw new CollectorException(cause, e1);// internal
// message
}
return storageSystem;
}
/**
* Returns the virtual remote port if its enabled. 0 otherwise.
*
* @param opts extra configuration key-value array.
* @return a Long object with the remote session port
*/
private Long getVPortFromExtraConfig(final OptionValue[] opts)
{
// default values
boolean portIsEnabled = false;
Long rdPort = 0L;
// check first if the port is enabled.
OptionValue optionValue;
for (OptionValue opt : opts)
{
optionValue = opt;
if (optionValue.getKey().equalsIgnoreCase("RemoteDisplay.vnc.enabled"))
{
portIsEnabled = Boolean.valueOf(optionValue.getValue().toString());
break;
}
}
// If it is enabled, search for the remote display port.
if (portIsEnabled)
{
for (OptionValue opt : opts)
{
optionValue = opt;
if (optionValue.getKey().equalsIgnoreCase("RemoteDisplay.vnc.port"))
{
rdPort = Long.valueOf(optionValue.getValue().toString());
break;
}
}
}
return rdPort;
}
/**
* Checks if the Host is in maintenance mode
*/
private boolean isInMaintenanceMode(final ObjectContent hostSystem) throws CollectorException
{
try
{
HostListSummary summary =
(HostListSummary) getDynamicProperty(hostSystem, HOST_SUMMARY);
if (summary.rebootRequired)
{
LOGGER.warn("ESXi host requires reboot");
}
return summary.getRuntime().isInMaintenanceMode();
}
catch (Exception e)
{
LOGGER.error(MessageValues.UNP_EXCP, e.getMessage());
throw new CollectorException(MessageValues.UNP_EXCP);
}
}
/**
* Private helper to check if vmware license is not FREE basic.
*
* @throws NoManagedException if the license is not valid.
*/
private synchronized void hasValidLicense() throws NodecollectorException
{
LOGGER.debug("Checking if host {} has a valid license...", getIpAddress());
ManagedObjectReference licenseManager = getServiceContent().getLicenseManager();
LicenseManagerLicenseInfo[] licenseInfo;
try
{
licenseInfo =
(LicenseManagerLicenseInfo[]) getDynamicProperty(licenseManager, "licenses");
for (LicenseManagerLicenseInfo licenseManagerLicenseInfo : licenseInfo)
{
if (licenseManagerLicenseInfo.getEditionKey().equals("esxBasic"))
{
LOGGER.debug("Invalid license found!");
throw new CannotExecuteException(MessageValues.NOMAN_ESXI_LIC);
}
KeyAnyValue[] properties = licenseManagerLicenseInfo.getProperties();
Long expirationHours = new Long(0);
Long expirationMinutes = new Long(0);
boolean neverExpires = true;
for (KeyAnyValue keyAnyValue : properties)
{
if ("expirationHours".equals(keyAnyValue.getKey()))
{
expirationHours = (Long) keyAnyValue.getValue();
neverExpires = false;
}
else if ("expirationMinutes".equals(keyAnyValue.getKey()))
{
expirationMinutes = (Long) keyAnyValue.getValue();
neverExpires = false;
}
}
if (!neverExpires)
{
if (expirationHours.intValue() == 0 && expirationMinutes.intValue() == 0)
{
LOGGER.debug("Expired license found!");
throw new CannotExecuteException(MessageValues.NOMAN_ESXI_LIC);
}
}
}
}
catch (Exception e)
{
LOGGER.error("An error was occurred when checking the license: {}", e);
throw new CannotExecuteException(MessageValues.NOMAN_ESXI_LIC);
}
LOGGER.debug("Valid license found");
}
/**
* Adds the value '-flat' to disk imagePath.
*
* @param imagePath the image path
* @return modified image path
*/
private String parseImagePathAddingFlat(final String imagePath)
{
return imagePath.substring(0, imagePath.lastIndexOf(".vmdk")) + "-flat.vmdk";
}
/**
* Depending on the kind of disk, we convert it into nodecollector's standard type.
*
* @param diskFile file which contains a Virtual System disk
* @return a {@link DiskType} object.
*/
private VirtualDiskEnumType resolveDiskFileType(final VirtualDeviceFileBackingInfo diskFile)
{
VirtualDiskEnumType diskType;
// first define the incompatible types which are the following:
// · VirtualCdromIsoBackingInfo
// · VirtualDiskFlatVer1BackingInfo
// ·
// check if the 'backing' variable belongs to a kind of supported disk types
if (diskFile instanceof VirtualDiskFlatVer2BackingInfo)
{
// Check if its monolithic or use extents
if (((VirtualDiskFlatVer2BackingInfo) diskFile).getSplit())
{
// Abicloud can not support extent files
diskType = VirtualDiskEnumType.INCOMPATIBLE;
}
else
{
diskType = VirtualDiskEnumType.VMDK_FLAT;
}
}
else if (diskFile instanceof VirtualDiskSparseVer2BackingInfo)
{
// Check if its monolithic or use extents
if (((VirtualDiskSparseVer2BackingInfo) diskFile).getSplit())
{
// Abicloud can not support extent files
diskType = VirtualDiskEnumType.INCOMPATIBLE;
}
else
{
diskType = VirtualDiskEnumType.VMDK_MONOLITHIC_SPARSE;
}
}
else if (diskFile instanceof VirtualDiskRawDiskMappingVer1BackingInfo)
{
// Stateful image
diskType = VirtualDiskEnumType.STATEFUL;
}
else
{
diskType = VirtualDiskEnumType.UNKNOWN;
}
return diskType;
}
/**
* Check if host system has the internetSCSI software controller enable, if not try to enabling
* it. TODO only look for software controller, add hardware support
*/
private Boolean isInternetSCSIEnable(final ObjectContent hostSystemOc,
final ManagedObjectReference storageSystemMor) throws CollectorException
{
Object objIscsiEnable;
Boolean isIscsiEnable;
ManagedObjectReference hostSystemMor = hostSystemOc.getObj();
String iscsiEnableProp = "config.storageDevice.softwareInternetScsiEnabled";
try
{
objIscsiEnable = getDynamicProperty(hostSystemMor, iscsiEnableProp);
// propISCSIEnable = utils.getProperties(hostSystemMOR, props);
}
catch (Exception e)
{
final String cause =
"Can not get the ''config.storageDevice.softwareInternetScsiEnabled'' property ";
LOGGER.error(cause, e);
throw new CollectorException(cause, e);// internal
// message
}
if (objIscsiEnable == null)
{
final String cause = "No such softwareInternetScsiEnabled property";
LOGGER.equals(cause);
throw new CollectorException(cause); // internal
// message
}
isIscsiEnable = (Boolean) objIscsiEnable;
if (!isIscsiEnable)
{
LOGGER.debug("iSCSI software initiator is not enabled, try to setting up");
try
{
getMyConn().updateSoftwareInternetScsiEnabled(storageSystemMor, true);
// TODO check realy is enableenableInternetSCSI();
isIscsiEnable = true;
LOGGER.debug("iSCSI software initiator enabled");
}
catch (Exception e)
{
LOGGER.error("Can not enable the software iSCSI initiator.\n {}", e);
}
}
return isIscsiEnable;
}
protected String decodeURLRawString(final String value) throws CollectorException
{
try
{
return URLDecoder.decode(value, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
LOGGER.error("Can not decode {} from URL raw encoding. {}", value, e);
throw new CollectorException(MessageValues.COLL_EXCP_DECODE);
}
}
}