blob: 209945fa4714807a700aca9e0b0b301f530f712c [file] [log] [blame]
// 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 org.apache.cloudstack.util.vmware;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceException;
import org.apache.cloudstack.util.LoginInfo;
import org.apache.log4j.Logger;
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.InvalidCollectorVersionFaultMsg;
import com.vmware.vim25.InvalidPropertyFaultMsg;
import com.vmware.vim25.LocalizedMethodFault;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec;
import com.vmware.vim25.ObjectUpdate;
import com.vmware.vim25.ObjectUpdateKind;
import com.vmware.vim25.PropertyChange;
import com.vmware.vim25.PropertyChangeOp;
import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertyFilterUpdate;
import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.RetrieveOptions;
import com.vmware.vim25.RetrieveResult;
import com.vmware.vim25.RuntimeFaultFaultMsg;
import com.vmware.vim25.SelectionSpec;
import com.vmware.vim25.ServiceContent;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.UpdateSet;
import com.vmware.vim25.VimPortType;
import com.vmware.vim25.VimService;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualIDEController;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualSCSIController;
public class VMwareUtil {
private static final Logger s_logger = Logger.getLogger(VMwareUtil.class);
private VMwareUtil() {}
public static class VMwareConnection {
private VimPortType _vimPortType;
private ServiceContent _serviceContent;
VMwareConnection(VimPortType vimPortType, ServiceContent serviceContent) {
_vimPortType = vimPortType;
_serviceContent = serviceContent;
}
VimPortType getVimPortType() {
return _vimPortType;
}
ServiceContent getServiceContent() {
return _serviceContent;
}
}
public static VMwareConnection getVMwareConnection(LoginInfo loginInfo) throws Exception {
trustAllHttpsCertificates();
HostnameVerifier hv = new HostnameVerifier() {
@Override
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
ManagedObjectReference serviceInstanceRef = new ManagedObjectReference();
final String serviceInstanceName = "ServiceInstance";
serviceInstanceRef.setType(serviceInstanceName);
serviceInstanceRef.setValue(serviceInstanceName);
VimService vimService = new VimService();
VimPortType vimPortType = vimService.getVimPort();
Map<String, Object> ctxt = ((BindingProvider)vimPortType).getRequestContext();
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://" + loginInfo.getHost() + "/sdk");
ctxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
ServiceContent serviceContent = vimPortType.retrieveServiceContent(serviceInstanceRef);
vimPortType.login(serviceContent.getSessionManager(), loginInfo.getUsername(), loginInfo.getPassword(), null);
return new VMwareConnection(vimPortType, serviceContent);
}
public static void closeVMwareConnection(VMwareConnection connection) throws Exception {
if (connection != null) {
connection.getVimPortType().logout(connection.getServiceContent().getSessionManager());
}
}
public static Map<String, ManagedObjectReference> getVms(VMwareConnection connection) throws Exception {
Map<String, ManagedObjectReference> nameToVm = new HashMap<>();
ManagedObjectReference rootFolder = connection.getServiceContent().getRootFolder();
TraversalSpec tSpec = getVMTraversalSpec();
PropertySpec propertySpec = new PropertySpec();
propertySpec.setAll(Boolean.FALSE);
propertySpec.getPathSet().add("name");
propertySpec.setType("VirtualMachine");
ObjectSpec objectSpec = new ObjectSpec();
objectSpec.setObj(rootFolder);
objectSpec.setSkip(Boolean.TRUE);
objectSpec.getSelectSet().add(tSpec);
PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec();
propertyFilterSpec.getPropSet().add(propertySpec);
propertyFilterSpec.getObjectSet().add(objectSpec);
List<PropertyFilterSpec> lstPfs = new ArrayList<>(1);
lstPfs.add(propertyFilterSpec);
VimPortType vimPortType = connection.getVimPortType();
ManagedObjectReference propertyCollector = connection.getServiceContent().getPropertyCollector();
List<ObjectContent> lstObjectContent = retrievePropertiesAllObjects(lstPfs, vimPortType, propertyCollector);
if (lstObjectContent != null) {
for (ObjectContent oc : lstObjectContent) {
ManagedObjectReference mor = oc.getObj();
List<DynamicProperty> dps = oc.getPropSet();
String vmName = null;
if (dps != null) {
for (DynamicProperty dp : dps) {
vmName = (String)dp.getVal();
}
}
if (vmName != null) {
nameToVm.put(vmName, mor);
}
}
}
return nameToVm;
}
public static Map<String, Object> getEntityProps(VMwareConnection connection, ManagedObjectReference entityMor, String[] props)
throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg {
Map<String, Object> retVal = new HashMap<>();
PropertySpec propertySpec = new PropertySpec();
propertySpec.setAll(Boolean.FALSE);
propertySpec.setType(entityMor.getType());
propertySpec.getPathSet().addAll(Arrays.asList(props));
ObjectSpec objectSpec = new ObjectSpec();
objectSpec.setObj(entityMor);
// Create PropertyFilterSpec using the PropertySpec and ObjectPec created above.
PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec();
propertyFilterSpec.getPropSet().add(propertySpec);
propertyFilterSpec.getObjectSet().add(objectSpec);
List<PropertyFilterSpec> propertyFilterSpecs = new ArrayList<>();
propertyFilterSpecs.add(propertyFilterSpec);
RetrieveResult rslts = connection.getVimPortType().retrievePropertiesEx(connection.getServiceContent().getPropertyCollector(),
propertyFilterSpecs, new RetrieveOptions());
List<ObjectContent> listobjcontent = new ArrayList<>();
if (rslts != null && rslts.getObjects() != null && !rslts.getObjects().isEmpty()) {
listobjcontent.addAll(rslts.getObjects());
}
String token = null;
if (rslts != null && rslts.getToken() != null) {
token = rslts.getToken();
}
while (token != null && !token.isEmpty()) {
rslts = connection.getVimPortType().continueRetrievePropertiesEx(connection.getServiceContent().getPropertyCollector(),
token);
token = null;
if (rslts != null) {
token = rslts.getToken();
if (rslts.getObjects() != null && !rslts.getObjects().isEmpty()) {
listobjcontent.addAll(rslts.getObjects());
}
}
}
for (ObjectContent oc : listobjcontent) {
List<DynamicProperty> dps = oc.getPropSet();
if (dps != null) {
for (DynamicProperty dp : dps) {
retVal.put(dp.getName(), dp.getVal());
}
}
}
return retVal;
}
public static ManagedObjectReference reconfigureVm(VMwareConnection connection, ManagedObjectReference morVm,
VirtualMachineConfigSpec vmcs) throws Exception {
return connection.getVimPortType().reconfigVMTask(morVm, vmcs);
}
public static VirtualMachineDiskInfoBuilder getDiskInfoBuilder(List<VirtualDevice> devices) throws Exception {
VirtualMachineDiskInfoBuilder builder = new VirtualMachineDiskInfoBuilder();
if (devices != null) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
VirtualDisk virtualDisk = (VirtualDisk)device;
VirtualDeviceBackingInfo backingInfo = virtualDisk.getBacking();
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
String deviceBusName = VMwareUtil.getDeviceBusName(devices, virtualDisk);
while (diskBackingInfo != null) {
builder.addDisk(deviceBusName, diskBackingInfo.getFileName());
diskBackingInfo = diskBackingInfo.getParent();
}
}
}
}
}
return builder;
}
public static String getDeviceBusName(List<VirtualDevice> allDevices, VirtualDisk disk) throws Exception {
for (VirtualDevice device : allDevices) {
if (device.getKey() == disk.getControllerKey()) {
if (device instanceof VirtualIDEController) {
return String.format("ide%d:%d", ((VirtualIDEController)device).getBusNumber(), disk.getUnitNumber());
} else if (device instanceof VirtualSCSIController) {
return String.format("scsi%d:%d", ((VirtualSCSIController)device).getBusNumber(), disk.getUnitNumber());
} else {
throw new Exception("The device controller is not supported.");
}
}
}
throw new Exception("The device controller could not be located.");
}
public static boolean waitForTask(VMwareConnection connection, ManagedObjectReference task) throws Exception {
try {
Object[] result = waitForValues(connection, task, new String[] { "info.state", "info.error" }, new String[] { "state" },
new Object[][] { new Object[] { TaskInfoState.SUCCESS, TaskInfoState.ERROR } });
if (result[0].equals(TaskInfoState.SUCCESS)) {
return true;
}
if (result[1] instanceof LocalizedMethodFault) {
throw new Exception(((LocalizedMethodFault)result[1]).getLocalizedMessage());
}
} catch (WebServiceException we) {
s_logger.debug("Cancelling vCenter task because the task failed with the following error: " + we.getLocalizedMessage());
connection.getVimPortType().cancelTask(task);
throw new Exception("The vCenter task failed due to the following error: " + we.getLocalizedMessage());
}
return false;
}
private static Object[] waitForValues(VMwareConnection connection, ManagedObjectReference morObj, String[] filterProps,
String[] endWaitProps, Object[][] expectedVals) throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg,
InvalidCollectorVersionFaultMsg {
String version = "";
Object[] endVals = new Object[endWaitProps.length];
Object[] filterVals = new Object[filterProps.length];
PropertyFilterSpec spec = new PropertyFilterSpec();
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(morObj);
oSpec.setSkip(Boolean.FALSE);
spec.getObjectSet().add(oSpec);
PropertySpec pSpec = new PropertySpec();
pSpec.getPathSet().addAll(Arrays.asList(filterProps));
pSpec.setType(morObj.getType());
spec.getPropSet().add(pSpec);
ManagedObjectReference propertyCollector = connection.getServiceContent().getPropertyCollector();
ManagedObjectReference filterSpecRef = connection.getVimPortType().createFilter(propertyCollector, spec, true);
boolean reached = false;
UpdateSet updateSet;
List<PropertyFilterUpdate> lstPropertyFilterUpdates;
List<ObjectUpdate> lstObjectUpdates;
List<PropertyChange> lstPropertyChanges;
while (!reached) {
updateSet = connection.getVimPortType().waitForUpdates(propertyCollector, version);
if (updateSet == null || updateSet.getFilterSet() == null) {
continue;
}
version = updateSet.getVersion();
lstPropertyFilterUpdates = updateSet.getFilterSet();
for (PropertyFilterUpdate propertyFilterUpdate : lstPropertyFilterUpdates) {
lstObjectUpdates = propertyFilterUpdate.getObjectSet();
for (ObjectUpdate objUpdate : lstObjectUpdates) {
if (objUpdate.getKind() == ObjectUpdateKind.MODIFY || objUpdate.getKind() == ObjectUpdateKind.ENTER ||
objUpdate.getKind() == ObjectUpdateKind.LEAVE) {
lstPropertyChanges = objUpdate.getChangeSet();
for (PropertyChange propchg : lstPropertyChanges) {
updateValues(endWaitProps, endVals, propchg);
updateValues(filterProps, filterVals, propchg);
}
}
}
}
Object expectedValue;
// Check if the expected values have been reached and exit the loop if done.
// Also, exit the WaitForUpdates loop if this is the case.
for (int chgi = 0; chgi < endVals.length && !reached; chgi++) {
for (int vali = 0; vali < expectedVals[chgi].length && !reached; vali++) {
expectedValue = expectedVals[chgi][vali];
reached = expectedValue.equals(endVals[chgi]) || reached;
}
}
}
// Destroy the filter when we are done.
connection.getVimPortType().destroyPropertyFilter(filterSpecRef);
return filterVals;
}
private static void updateValues(String[] props, Object[] vals, PropertyChange propertyChange) {
for (int findi = 0; findi < props.length; findi++) {
if (propertyChange.getName().lastIndexOf(props[findi]) >= 0) {
if (propertyChange.getOp() == PropertyChangeOp.REMOVE) {
vals[findi] = "";
} else {
vals[findi] = propertyChange.getVal();
}
}
}
}
private static List<ObjectContent> retrievePropertiesAllObjects(List<PropertyFilterSpec> lstPfs,
VimPortType vimPortType, ManagedObjectReference propCollectorRef) throws Exception {
List<ObjectContent> lstObjectContent = new ArrayList<>();
RetrieveOptions retrieveOptions = new RetrieveOptions();
RetrieveResult rslts = vimPortType.retrievePropertiesEx(propCollectorRef, lstPfs, retrieveOptions);
if (rslts != null && rslts.getObjects() != null && rslts.getObjects().size() > 0) {
List<ObjectContent> lstOc = new ArrayList<>();
for (ObjectContent oc : rslts.getObjects()) {
lstOc.add(oc);
}
lstObjectContent.addAll(lstOc);
}
String token = null;
if (rslts != null && rslts.getToken() != null) {
token = rslts.getToken();
}
while (token != null && !token.isEmpty()) {
rslts = vimPortType.continueRetrievePropertiesEx(propCollectorRef, token);
token = null;
if (rslts != null) {
token = rslts.getToken();
if (rslts.getObjects() != null && rslts.getObjects().size() > 0) {
List<ObjectContent> lstOc = new ArrayList<>();
for (ObjectContent oc : rslts.getObjects()) {
lstOc.add(oc);
}
lstObjectContent.addAll(lstOc);
}
}
}
return lstObjectContent;
}
private static TraversalSpec getVMTraversalSpec() {
// Create a TraversalSpec that starts from the 'root' objects
// and traverses the inventory tree to get to the VirtualMachines.
// Build the traversal specs bottoms up
// TraversalSpec to get to the VM in a vApp
TraversalSpec vAppToVM = new TraversalSpec();
vAppToVM.setName("vAppToVM");
vAppToVM.setType("VirtualApp");
vAppToVM.setPath("vm");
// TraversalSpec for vApp to vApp
TraversalSpec vAppToVApp = new TraversalSpec();
vAppToVApp.setName("vAppToVApp");
vAppToVApp.setType("VirtualApp");
vAppToVApp.setPath("resourcePool");
// SelectionSpec for vApp-to-vApp recursion
SelectionSpec vAppRecursion = new SelectionSpec();
vAppRecursion.setName("vAppToVApp");
// SelectionSpec to get to a VM in the vApp
SelectionSpec vmInVApp = new SelectionSpec();
vmInVApp.setName("vAppToVM");
// SelectionSpec for both vApp to vApp and vApp to VM
List<SelectionSpec> vAppToVMSS = new ArrayList<>();
vAppToVMSS.add(vAppRecursion);
vAppToVMSS.add(vmInVApp);
vAppToVApp.getSelectSet().addAll(vAppToVMSS);
// This SelectionSpec is used for recursion for Folder recursion
SelectionSpec sSpec = new SelectionSpec();
sSpec.setName("VisitFolders");
// Traversal to get to the vmFolder from DataCenter
TraversalSpec dataCenterToVMFolder = new TraversalSpec();
dataCenterToVMFolder.setName("DataCenterToVMFolder");
dataCenterToVMFolder.setType("Datacenter");
dataCenterToVMFolder.setPath("vmFolder");
dataCenterToVMFolder.setSkip(false);
dataCenterToVMFolder.getSelectSet().add(sSpec);
// TraversalSpec to get to the DataCenter from rootFolder
TraversalSpec traversalSpec = new TraversalSpec();
traversalSpec.setName("VisitFolders");
traversalSpec.setType("Folder");
traversalSpec.setPath("childEntity");
traversalSpec.setSkip(false);
List<SelectionSpec> sSpecArr = new ArrayList<>();
sSpecArr.add(sSpec);
sSpecArr.add(dataCenterToVMFolder);
sSpecArr.add(vAppToVM);
sSpecArr.add(vAppToVApp);
traversalSpec.getSelectSet().addAll(sSpecArr);
return traversalSpec;
}
private static void trustAllHttpsCertificates() throws Exception {
// Create a trust manager that does not validate certificate chains:
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new TrustAllTrustManager();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext.getInstance("SSL");
SSLSessionContext sslsc = sc.getServerSessionContext();
sslsc.setSessionTimeout(0);
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
private static class TrustAllTrustManager implements TrustManager, X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}
}