/*************************************************************************** * Copyright (c) 2013-2014 VMware, Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package com.vmware.vhadoop.vhm.vc; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLException; import com.vmware.vhadoop.api.vhm.VCActions; import com.vmware.vhadoop.api.vhm.VCActions.VMEventData; import com.vmware.vhadoop.util.CompoundStatus; import com.vmware.vhadoop.util.ExternalizedParameters; import com.vmware.vhadoop.util.LogFormatter; import com.vmware.vhadoop.util.ThreadLocalCompoundStatus; import com.vmware.vhadoop.util.VhmLevel; import com.vmware.vim.binding.impl.vim.event.EventExImpl; import com.vmware.vim.binding.impl.vmodl.TypeNameImpl; import com.vmware.vim.binding.vim.Folder; import com.vmware.vim.binding.vim.PerformanceManager; import com.vmware.vim.binding.vim.ServiceInstance; import com.vmware.vim.binding.vim.ServiceInstanceContent; import com.vmware.vim.binding.vim.SessionManager; import com.vmware.vim.binding.vim.Task; import com.vmware.vim.binding.vim.TaskInfo; import com.vmware.vim.binding.vim.VirtualMachine; import com.vmware.vim.binding.vim.VirtualMachine.PowerState; import com.vmware.vim.binding.vim.alarm.Alarm; import com.vmware.vim.binding.vim.alarm.AlarmManager; import com.vmware.vim.binding.vim.event.Event.EventSeverity; import com.vmware.vim.binding.vim.event.EventEx; import com.vmware.vim.binding.vim.event.EventManager; import com.vmware.vim.binding.vim.fault.HostConnectFault; import com.vmware.vim.binding.vim.fault.InvalidEvent; import com.vmware.vim.binding.vim.fault.VimFault; import com.vmware.vim.binding.vim.net.IpConfigInfo; import com.vmware.vim.binding.vim.net.IpConfigInfo.IpAddress; import com.vmware.vim.binding.vim.option.OptionValue; import com.vmware.vim.binding.vim.version.version8; import com.vmware.vim.binding.vim.view.ContainerView; import com.vmware.vim.binding.vim.view.ViewManager; import com.vmware.vim.binding.vim.vm.GuestInfo.NicInfo; import com.vmware.vim.binding.vmodl.DynamicProperty; import com.vmware.vim.binding.vmodl.ManagedObjectReference; import com.vmware.vim.binding.vmodl.TypeName; import com.vmware.vim.binding.vmodl.fault.RequestCanceled; import com.vmware.vim.binding.vmodl.query.InvalidCollectorVersion; import com.vmware.vim.binding.vmodl.query.InvalidProperty; import com.vmware.vim.binding.vmodl.query.PropertyCollector; import com.vmware.vim.binding.vmodl.query.PropertyCollector.Change; import com.vmware.vim.binding.vmodl.query.PropertyCollector.Filter; import com.vmware.vim.binding.vmodl.query.PropertyCollector.FilterSpec; import com.vmware.vim.binding.vmodl.query.PropertyCollector.FilterUpdate; import com.vmware.vim.binding.vmodl.query.PropertyCollector.ObjectContent; import com.vmware.vim.binding.vmodl.query.PropertyCollector.ObjectSpec; import com.vmware.vim.binding.vmodl.query.PropertyCollector.ObjectUpdate; import com.vmware.vim.binding.vmodl.query.PropertyCollector.ObjectUpdate.Kind; import com.vmware.vim.binding.vmodl.query.PropertyCollector.PropertySpec; import com.vmware.vim.binding.vmodl.query.PropertyCollector.RetrieveOptions; import com.vmware.vim.binding.vmodl.query.PropertyCollector.RetrieveResult; import com.vmware.vim.binding.vmodl.query.PropertyCollector.SelectionSpec; import com.vmware.vim.binding.vmodl.query.PropertyCollector.TraversalSpec; import com.vmware.vim.binding.vmodl.query.PropertyCollector.UpdateSet; import com.vmware.vim.binding.vmodl.query.PropertyCollector.WaitOptions; import com.vmware.vim.vmomi.client.Client; import com.vmware.vim.vmomi.client.exception.ConnectionException; import com.vmware.vim.vmomi.client.exception.TransportProtocolException; import com.vmware.vim.vmomi.client.http.HttpClientConfiguration; import com.vmware.vim.vmomi.client.http.ThumbprintVerifier; import com.vmware.vim.vmomi.client.http.impl.HttpConfigurationImpl; import com.vmware.vim.vmomi.core.types.VmodlContext; public class VcVlsi { public static final String SERENGETI_MASTERVM_NAME_POSTFIX = "-master-"; public static final String SERENGETI_MASTERVM_NAME_POSTFIX_GUI = "-ComputeMaster-"; private static final Logger _log = Logger.getLogger(VcVlsi.class.getName()); static final String VC_PROP_VM_NAME = "name"; static final String VC_PROP_VM_EXTRA_CONFIG = "config.extraConfig"; static final String VC_PROP_VM_UUID = "config.uuid"; static final String VC_PROP_VM_NUM_CPU = "config.hardware.numCPU"; static final String VC_PROP_VM_POWER_STATE = "runtime.powerState"; static final String VC_PROP_VM_HOST = "runtime.host"; static final String VC_PROP_VM_GUEST_NIC_INFO = "guest.net"; static final String VC_PROP_VM_GUEST_HOSTNAME = "guest.hostName"; static final String VC_MOREF_TYPE_TASK = "Task"; static final String VC_MOREF_TYPE_VM = "VirtualMachine"; static final String VC_MOREF_TYPE_FOLDER = "Folder"; static final String VC_MOREF_TYPE_CONTAINER_VIEW = "ContainerView"; private static final TypeNameImpl typeTask = new TypeNameImpl(VC_MOREF_TYPE_TASK); private static final TypeNameImpl typeVM = new TypeNameImpl(VC_MOREF_TYPE_VM); private static final TypeNameImpl typeFolder = new TypeNameImpl(VC_MOREF_TYPE_FOLDER); private static final TypeNameImpl typeContainerView = new TypeNameImpl(VC_MOREF_TYPE_CONTAINER_VIEW); private static final int propertyCollectorTimeout = ExternalizedParameters.get().getInt("VC_PROPERTY_COLLECTOR_TIMEOUT"); static final String VHM_EXTRA_CONFIG_PREFIX = "vhmInfo."; static final String VHM_EXTRA_CONFIG_UUID = "vhmInfo.serengeti.uuid"; static final String VHM_EXTRA_CONFIG_MASTER_UUID = "vhmInfo.masterVM.uuid"; static final String VHM_EXTRA_CONFIG_MASTER_MOREF = "vhmInfo.masterVM.moid"; static final String VHM_EXTRA_CONFIG_MASTER_CLUSTERNAME = "vhmInfo.masterVM.clusterName"; static final String VHM_EXTRA_CONFIG_ELASTIC = "vhmInfo.elastic"; static final String VHM_EXTRA_CONFIG_AUTOMATION_ENABLE = "vhmInfo.vhm.enable"; static final String VHM_EXTRA_CONFIG_AUTOMATION_MIN_INSTANCES = "vhmInfo.min.computeNodeNum"; static final String VHM_EXTRA_CONFIG_AUTOMATION_INSTANCE_RANGE = "vhmInfo.instanceRange.computeNodeNum"; static final String VHM_EXTRA_CONFIG_JOB_TRACKER_PORT = "vhmInfo.jobtracker.port"; private static final String TASK_INFO_STATE = "info.state"; static final String WAIT_FOR_UPDATES_CANCELED_STATUS = "VC_WAIT_FOR_UPDATES_CANCELED"; static final String WAIT_FOR_UPDATES_INVALID_COLLECTOR_VERSION_STATUS = "VC_WAIT_FOR_UPDATES_INVALID_COLLECTOR_VERSION"; static final String WAIT_FOR_UPDATES_INVALID_PROPERTY_STATUS = "VC_WAIT_FOR_UPDATES_INVALID_PROPERTY"; static final String WAIT_FOR_UPDATES_NO_CLUSTERS = "VC_WAIT_FOR_UPDATES_NO_CLUSTERS"; private final String VC_ALARM_NAME_BASE = ExternalizedParameters.get().getString("VC_ALARM_NAME_BASE"); private ThreadLocalCompoundStatus _threadLocalStatus; private PropertyCollector _blockedPropertyCollectorSingleton; /* Only one thread will ever be blocked on the PropertyCollector */ private Object _propertyCollectorLock = new Object(); private Alarm _alarmSingleton; private Object _alarmLock = new Object(); static { VmodlContext.initContext(new String[] { "com.vmware.vim.binding.vim" }); } void setThreadLocalCompoundStatus(ThreadLocalCompoundStatus tlcs) { _threadLocalStatus = tlcs; } private CompoundStatus getCompoundStatus() { if (_threadLocalStatus == null) { return new CompoundStatus("DUMMY_STATUS"); } return _threadLocalStatus.get(); } private ThumbprintVerifier getThumbprintVerifier(final String vcThumbprint) { return new ThumbprintVerifier() { @Override public Result verify(String thumbprint) { if (vcThumbprint == null) { return Result.MATCH; } else if (thumbprint.equalsIgnoreCase(vcThumbprint)) { return Result.MATCH; } else { _log.log(Level.SEVERE, "VHM: thumbprint from vhm.properties does not match thumbprint from vCenter - "+thumbprint); return Result.MISMATCH; } } @Override public void onSuccess(X509Certificate[] chain, String thumbprint, Result verifyResult, boolean trustedChain, boolean verifiedAssertions) throws SSLException { } }; } private ServiceInstance getServiceInstance(Client vcClient) { ManagedObjectReference svcRef = new ManagedObjectReference(); svcRef.setType("ServiceInstance"); svcRef.setValue("ServiceInstance"); return vcClient.createStub(ServiceInstance.class, svcRef); } private ServiceInstanceContent getServiceInstanceContent(Client vcClient) throws ConnectionException { ServiceInstance svc = getServiceInstance(vcClient); return svc.retrieveContent(); } /* * Create a temporary connection to VC to login using extension certificate via sdkTunnel, * and get the session ticket to use for the normal connection. */ private String getSessionTicket(String vcIP, String keyStoreFile, String keyStorePwd, String vcExtKey, String vcThumbprint, long timeoutMillis) throws ConnectionException, URISyntaxException, IOException, GeneralSecurityException, VimFault { URI uri = new URI("https://sdkTunnel:8089/sdk/vimService"); KeyStore keyStore = KeyStore.getInstance("JKS"); InputStream is = null; try { is = new FileInputStream(keyStoreFile); keyStore.load(is, keyStorePwd.toCharArray()); } finally { if (is != null) { try { is.close(); } catch (IOException e) {} } } HttpConfigurationImpl httpConfig = new HttpConfigurationImpl(); httpConfig.setKeyStore(keyStore); httpConfig.setDefaultProxy(vcIP, 80, "http"); httpConfig.getKeyStoreConfig().setKeyAlias(keyStore.aliases().nextElement()); httpConfig.getKeyStoreConfig().setKeyPassword(keyStorePwd); httpConfig.setTimeoutMs((int)timeoutMillis); httpConfig.setThumbprintVerifier(getThumbprintVerifier(vcThumbprint)); HttpClientConfiguration clientConfig = HttpClientConfiguration.Factory.newInstance(); clientConfig.setHttpConfiguration(httpConfig); Client newClient = Client.Factory.createClient(uri, version8.class, clientConfig); ServiceInstanceContent sic = getServiceInstanceContent(newClient); SessionManager sm = newClient.createStub(SessionManager.class, sic.getSessionManager()); sm.loginExtensionByCertificate(vcExtKey, null); String ticket = sm.acquireSessionTicket(null); return ticket; } public Client connect(VcCredentials credentials, boolean useKey, Client cloneClient, long timeoutMillis) throws ConnectionException, HostConnectFault, IOException, VimFault, GeneralSecurityException, URISyntaxException { String sessionTicket = null; if (cloneClient != null) { ServiceInstanceContent sic = getServiceInstanceContent(cloneClient); SessionManager sm = cloneClient.createStub(SessionManager.class, sic.getSessionManager()); sessionTicket = sm.acquireCloneTicket(); } else if (useKey) { sessionTicket = getSessionTicket(credentials.vcIP, credentials.keyStoreFile, credentials.keyStorePwd, credentials.vcExtKey, credentials.vcThumbprint, timeoutMillis); } URI uri = new URI("https", null, credentials.vcIP, 443, "/sdk", null, null); // each VLSI call consumes an executor thread for the duration of the blocking call ThreadPoolExecutor executor = new ThreadPoolExecutor(1, // core pool size 4, // max pool size 10, TimeUnit.SECONDS, // max thread idle time new LinkedBlockingQueue<Runnable>()); // work queue HttpConfigurationImpl httpConfig = new HttpConfigurationImpl(); httpConfig.setTimeoutMs((int)timeoutMillis); httpConfig.setThumbprintVerifier(getThumbprintVerifier(credentials.vcThumbprint)); HttpClientConfiguration clientConfig = HttpClientConfiguration.Factory.newInstance(); clientConfig.setHttpConfiguration(httpConfig); clientConfig.setExecutor(executor); Client newClient = Client.Factory.createClient(uri, version8.class, clientConfig); if (newClient != null) { ServiceInstanceContent sic = getServiceInstanceContent(newClient); SessionManager sm = newClient.createStub(SessionManager.class, sic.getSessionManager()); if (cloneClient != null) { sm.cloneSession(sessionTicket); } else { if (useKey) { sm.loginBySessionTicket(sessionTicket); } else { sm.login(credentials.user, credentials.password, null); } } } return newClient; } public boolean testConnection(Client vcClient) { if (vcClient == null) { return false; } // Test the operation of the current connection using the standard simple call for this purpose. Calendar vcTime = null; try { ServiceInstance si = getServiceInstance(vcClient); vcTime = si.currentTime(); } catch (Exception e) { /* We don't really care what the exception was, we just don't want it propogating */ } finally { if (vcTime == null) { _log.log(Level.INFO, "testConnection found VC connection dropped; caller will reconnect"); } } return (vcTime != null); } private Folder getRootFolder(Client client) throws ConnectionException { ServiceInstanceContent sic = getServiceInstanceContent(client); return client.createStub(Folder.class, sic.getRootFolder()); } /** * Property filters are used a lot when querying the JAX-WS API for information about VC entities * The code is pretty ugly, so it makes sense to encapsulate it in a utility class. * The class is dual-purpose - it can be created with either constructor depending on the need. * Properties can then be added to the filter and once that's completed, * retrieveProperties() or getPropertyCollector() can be called, depending on the requirement * */ public class PropertyFilter { PropertyCollector _propertyCollector; Filter _filter; PropertySpec _propertySpec; FilterSpec _propertyFilterSpec; boolean _initialized = false; ContainerView _containerView; Client _vcClient; public PropertyFilter(Client vcClient, ContainerView containerView, TypeName type) { _vcClient = vcClient; _containerView = containerView; TraversalSpec tSpec = getTraversalSpecFromView(); ObjectSpec objectSpec = new ObjectSpec(); objectSpec.setObj(containerView._getRef()); objectSpec.setSelectSet(new SelectionSpec[] {tSpec}); _propertyFilterSpec = new FilterSpec(); _propertyFilterSpec.setObjectSet(new ObjectSpec[] {objectSpec}); _propertySpec = new PropertySpec(); _propertySpec.setAll(Boolean.FALSE); _propertySpec.setType(type); } public PropertyFilter(Client vcClient, Task task) { _vcClient = vcClient; ObjectSpec objectSpec = new ObjectSpec(); objectSpec.setObj(task._getRef()); objectSpec.setSkip(Boolean.FALSE); _propertyFilterSpec = new FilterSpec(); _propertyFilterSpec.setObjectSet(new ObjectSpec[] {objectSpec}); _propertySpec = new PropertySpec(); _propertySpec.setType(typeTask); } private TraversalSpec getTraversalSpecFromView() { // Create a traversal spec that starts from the ListView object // and traverses to its "view" property containing the managed object references. TraversalSpec viewToObject = new TraversalSpec(); viewToObject.setName("viewToObject"); viewToObject.setType(typeContainerView); viewToObject.setPath("view"); viewToObject.setSkip(false); return viewToObject; } public void setPropToFilter(String property) throws InvalidProperty, ConnectionException { _propertySpec.setPathSet(new String[] {property}); init(); } public void setPropsToFilter(String[] properties) throws InvalidProperty, ConnectionException { _propertySpec.setPathSet(properties); init(); } private void init() throws InvalidProperty, ConnectionException { if (!_initialized) { _propertyFilterSpec.setPropSet(new PropertySpec [] {_propertySpec}); ServiceInstanceContent sic = getServiceInstanceContent(_vcClient); _propertyCollector = _vcClient.createStub(PropertyCollector.class, sic.getPropertyCollector()); _filter = _vcClient.createStub(Filter.class, _propertyCollector.createFilter(_propertyFilterSpec, true)); } _initialized = true; } public PropertyCollector getPropertyCollector() throws InvalidProperty, ConnectionException { init(); return _propertyCollector; } public RetrieveResult retrieveProperties() throws InvalidProperty, ConnectionException { init(); RetrieveOptions retrieveOptions = new RetrieveOptions(); return _propertyCollector.retrievePropertiesEx(new FilterSpec[] {_propertyFilterSpec}, retrieveOptions); } public RetrieveResult continueRetrieve(String token) throws InvalidProperty { return _propertyCollector.continueRetrievePropertiesEx(token); } public void cleanup() { if (_filter != null) { _filter.destroy(); } if (_containerView != null) { _containerView.destroy(); } } } private List<ManagedObjectReference> findObjectsInFolder(Client client, Folder baseFolder, TypeName type, String restrictToName) throws InvalidProperty, ConnectionException { if (baseFolder == null) { return null; } List<ManagedObjectReference> resultRefs = new ArrayList<ManagedObjectReference>(); ServiceInstanceContent sic = getServiceInstanceContent(client); ViewManager viewMgr = client.createStub(ViewManager.class, sic.getViewManager()); ContainerView cView = client.createStub(ContainerView.class, viewMgr.createContainerView(baseFolder._getRef(), new TypeName[] {type}, true)); PropertyFilter propFilter = new PropertyFilter(client, cView, type); propFilter.setPropToFilter("name"); RetrieveResult rr = propFilter.retrieveProperties(); boolean done = false; while ((rr != null) && !done) { ObjectContent[] oca = rr.getObjects(); for (ObjectContent oc : oca) { if (restrictToName == null) { resultRefs.add(oc.getObj()); } else { // filter out by name DynamicProperty[] dps = oc.getPropSet(); for (DynamicProperty dp : dps) { if (dp.getName().equals("name") && dp.getVal().equals(restrictToName)) { resultRefs.add(oc.getObj()); done = true; break; } } } } if (rr.getToken() == null) { done = true; } else if (!done) { // get the next batch of results from VC rr = propFilter.continueRetrieve(rr.getToken()); } } propFilter.cleanup(); return resultRefs; } Folder getFolderForName(Client client, Folder baseFolder, String restrictToName) throws InvalidProperty { if (baseFolder == null) { baseFolder = getRootFolder(client); } List<ManagedObjectReference> refs = findObjectsInFolder(client, baseFolder, typeFolder, restrictToName); if (refs.size() > 0) { return client.createStub(Folder.class, refs.get(0)); } return null; } private PropertyFilter setupWaitForUpdates(Client vcClient, Folder baseFolder, TypeName type, String[] statePropsToGet) throws InvalidProperty, ConnectionException { PropertyFilter propFilter = null; ServiceInstanceContent sic = getServiceInstanceContent(vcClient); ViewManager viewMgr = vcClient.createStub(ViewManager.class, sic.getViewManager()); ContainerView cView = vcClient.createStub(ContainerView.class, viewMgr.createContainerView(baseFolder._getRef(), new TypeName[] {type}, true)); propFilter = new PropertyFilter(vcClient, cView, type); propFilter.setPropsToFilter(statePropsToGet); return propFilter; } private UpdateSet callWaitForUpdates(PropertyCollector propCollector, String version) throws InvalidCollectorVersion { UpdateSet updateSet = null; if (version == null) { version = ""; } WaitOptions waitOptions = new WaitOptions(); waitOptions.setMaxWaitSeconds(propertyCollectorTimeout); synchronized(_propertyCollectorLock) { _blockedPropertyCollectorSingleton = propCollector; } try { updateSet = propCollector.waitForUpdatesEx(version, waitOptions); } finally { synchronized(_propertyCollectorLock) { _blockedPropertyCollectorSingleton = null; } } return updateSet; } /* TODO: Why is this not called anywhere? */ private void cleanupWaitForUpdates(PropertyFilter propFilter) { propFilter.cleanup(); } private static Map<String, Set<String>> getNicInfo(NicInfo[] nicInfoArray) { Map<String, Set<String>> nicAndIpAddressMap = new HashMap<String, Set<String>>(); if (nicInfoArray != null) { for (NicInfo nicInfo : nicInfoArray) { String networkName = nicInfo.getNetwork(); IpConfigInfo ipConfigInfo = nicInfo.getIpConfig(); if ((ipConfigInfo != null) && (networkName != null)) { IpAddress[] ipAddressObjects = ipConfigInfo.getIpAddress(); if (ipAddressObjects != null) { Set<String> ipAddressSet = new HashSet<String>(); for (IpAddress ipAddressObj : ipAddressObjects) { String ipAddress = ipAddressObj.getIpAddress(); if (ipAddress != null) { ipAddressSet.add(ipAddress); } } nicAndIpAddressMap.put(networkName, ipAddressSet); } } } } return nicAndIpAddressMap; } private static VMEventData parseObjUpdate(Logger logger, ObjectUpdate obj) { VMEventData vmData = new VMEventData(); vmData._vmMoRef = obj.getObj().getValue(); Kind kind = obj.getKind(); logger.log(Level.FINE, "Pobj kind= " + kind + " obj= " + obj.getObj().getValue()); if (kind == Kind.leave) { vmData._isLeaving = true; } else if (kind == Kind.modify || kind == Kind.enter) { vmData._isLeaving = false; for (Change pc : obj.getChangeSet()) { String pcName = pc.getName(); Object pcValue = pc.getVal(); logger.log(Level.FINE, "Pobj prop= " + pcName + " val= " + pcValue); if (pcValue != null) { if (pcName.equals(VC_PROP_VM_UUID)) { vmData._myUUID = (String)pcValue; } else if (pcName.equals(VC_PROP_VM_NUM_CPU)) { vmData._vCPUs = (Integer)pcValue; } else if (pcName.equals(VC_PROP_VM_NAME)) { vmData._myName = (String)pcValue; /* Update this as early as possible. Doesn't matter if the key already exists */ logger.log(Level.FINE, "Associating vmId "+vmData._vmMoRef+" with name "+vmData._myName); LogFormatter._vmIdToNameMapper.put(vmData._vmMoRef, vmData._myName); } else if (pcName.equals(VC_PROP_VM_POWER_STATE)) { PowerState ps = (PowerState)pcValue; if (ps == PowerState.poweredOn) { vmData._powerState = true; } else { vmData._powerState = false; } } else if (pcName.equals(VC_PROP_VM_HOST)) { vmData._hostMoRef = ((ManagedObjectReference)pcValue).getValue(); } else if (pcName.equals(VC_PROP_VM_GUEST_NIC_INFO)) { vmData._nicAndIpAddressMap = getNicInfo((NicInfo[])pcValue); } else if (pcName.equals(VC_PROP_VM_GUEST_HOSTNAME)) { vmData._dnsName = (String)pcValue; } else if (pcName.equals(VC_PROP_VM_EXTRA_CONFIG)) { // extraConfig updates can be returned as an array (pcName == config.extraConfig), or individual key (below) OptionValue[] ecl = (OptionValue[]) pcValue; for (OptionValue ec : ecl) { if (ec.getKey().startsWith(VHM_EXTRA_CONFIG_PREFIX)) { VcVlsiHelper.parseExtraConfig(vmData, ec.getKey(), (String)ec.getValue()); } } } else if (pcName.lastIndexOf(VC_PROP_VM_EXTRA_CONFIG) >= 0) { // individual extraConfig entries (pcName = config.extraConfig["xxx"].value) String [] parts = pcName.split("\"",3); if (parts.length > 1) { if (parts[1].startsWith(VHM_EXTRA_CONFIG_PREFIX)) { // sometimes pcValue is a String, and sometimes its OptionValue... String valueString; if (pcValue instanceof String) { valueString = (String)pcValue; } else { valueString = (String) ((OptionValue)pcValue).getValue(); } VcVlsiHelper.parseExtraConfig(vmData, parts[1], valueString); } } } else { logger.log(Level.WARNING, "Unexpected update: prop= " + pcName + " val= " + pcValue); } } } } return vmData; } private String pcVMsInFolder(Client vcClient, Folder folder, String version, List<VMEventData> vmDataList) throws ConnectionException, InvalidCollectorVersion, InvalidProperty { if (version == null) { version = ""; } if (version.equals("")) { String [] props = {VC_PROP_VM_NAME, VC_PROP_VM_EXTRA_CONFIG, VC_PROP_VM_UUID, VC_PROP_VM_NUM_CPU, VC_PROP_VM_POWER_STATE, VC_PROP_VM_HOST, VC_PROP_VM_GUEST_NIC_INFO, VC_PROP_VM_GUEST_HOSTNAME}; setupWaitForUpdates(vcClient, folder, typeVM, props); } ServiceInstanceContent sic = getServiceInstanceContent(vcClient); PropertyCollector propertyCollector = vcClient.createStub(PropertyCollector.class, sic.getPropertyCollector()); UpdateSet updateSet = null; try { updateSet = callWaitForUpdates(propertyCollector, version); } catch (ConnectionException e) { Throwable cause = e.getCause(); /* * SocketTimeoutException is caused when we hit SESSION_TIME_OUT * If that happens, hide the exception, and just return with no changes */ if ((cause != null) && (cause instanceof SocketTimeoutException)) { return version; } throw e; } if (updateSet != null) { version = updateSet.getVersion(); FilterUpdate[] updates = updateSet.getFilterSet(); //_log.log(Level.INFO, "WFU new version= " + version + " fs= " + updates); if (updates != null) { for (FilterUpdate pfu : updates) { ObjectUpdate[] objectSet = pfu.getObjectSet(); for (ObjectUpdate obj : objectSet) { VMEventData vmData = parseObjUpdate(_log, obj); if (vmData != null) { vmDataList.add(vmData); } } } } } return version; } boolean waitForTask(Client client, Task task) { CompoundStatus status = new CompoundStatus("waitForTask"); boolean result = false; PropertyFilter propFilter = new PropertyFilter(client, task); try { propFilter.setPropsToFilter(new String [] {TASK_INFO_STATE }); UpdateSet updateSet = null; String version = ""; WaitOptions waitOptions = new WaitOptions(); waitOptions.setMaxWaitSeconds(propertyCollectorTimeout); _mainLoop: while (true) { _log.log(Level.INFO, "WFT waiting vesrion=" + version); updateSet = propFilter.getPropertyCollector().waitForUpdatesEx(version, waitOptions); if (updateSet != null) { version = updateSet.getVersion(); FilterUpdate[] updates = updateSet.getFilterSet(); if (updates != null) { for (FilterUpdate pfu : updates) { ObjectUpdate[] objectSet = pfu.getObjectSet(); for (ObjectUpdate obj : objectSet) { Kind kind = obj.getKind(); if (kind == Kind.modify || kind == Kind.enter || kind == Kind.leave) { for (Change pc : obj.getChangeSet()) { String pcName = pc.getName(); Object pcValue = pc.getVal(); if (pcName.lastIndexOf(TASK_INFO_STATE) >= 0) { TaskInfo.State state = (TaskInfo.State)pcValue; if (state == TaskInfo.State.error) { result = false; break _mainLoop; } else if (state == TaskInfo.State.success) { result = true; break _mainLoop; } } } } } } } } } status.registerTaskSucceeded(); } catch (ConnectionException e) { reportException("Error connecting to vCenter: "+e.getMessage(), status); } catch (Exception e) { reportException("Unexpected exception waiting for task completion", e, status); } finally { propFilter.cleanup(); } getCompoundStatus().addStatus(status); return result; } public String waitForUpdates(Client client, String baseFolderName, String version, List<VMEventData> vmDataList) { CompoundStatus status = new CompoundStatus("waitForUpdates"); String newVersion = version; /* There is an expectation that this method should never return null */ if (newVersion == null) { newVersion = ""; } try { Folder f = getFolderForName(client, null, baseFolderName); if (f == null) { // This is normal state when user hasn't created any hadoop clusters yet _log.log(Level.INFO, "No found clusters for hadoop UUID " + baseFolderName); newVersion = WAIT_FOR_UPDATES_NO_CLUSTERS; } else { newVersion = pcVMsInFolder(client, f, version, vmDataList); } status.registerTaskSucceeded(); } catch (RequestCanceled e) { _log.info("waitForUpdates request has been canceled"); newVersion = WAIT_FOR_UPDATES_CANCELED_STATUS; } catch (InvalidCollectorVersion e) { _log.info("propertyCollector version has become stale"); newVersion = WAIT_FOR_UPDATES_INVALID_COLLECTOR_VERSION_STATUS; } catch (InvalidProperty e) { _log.info("propertyCollector property is invalid: "+e); newVersion = WAIT_FOR_UPDATES_INVALID_PROPERTY_STATUS; } catch (ConnectionException e) { reportException("Error connecting to vCenter: "+e.getMessage(), status); } catch (TransportProtocolException e) { reportException("Error connecting to vCenter: "+e.getMessage(), status); } catch (Exception e) { reportException("Unexpected exception waiting for updates", e, status); } getCompoundStatus().addStatus(status); return newVersion; } public List<String> getVMsInFolder(Client client, String baseFolderName, String folderName) { CompoundStatus status = new CompoundStatus("getVMsInFolder"); List<String> result = null; try { Folder baseFolder = getFolderForName(client, null, baseFolderName); Folder folder = getFolderForName(client, baseFolder, folderName); List<ManagedObjectReference> refs = findObjectsInFolder(client, folder, typeVM, null); if ((refs != null) && (refs.size() > 0)) { result = new ArrayList<String>(); for (ManagedObjectReference ref : refs) { result.add(ref.getValue()); } } status.registerTaskSucceeded(); } catch (com.vmware.vim.binding.vmodl.fault.RequestCanceled e) { _log.info("getVMsInFolder has been canceled"); } catch (ConnectionException e) { reportException("Error connecting to vCenter: "+e.getMessage(), status); } catch (Exception e) { reportException("Unexpected exception in getVMsInFolder", e, status); } getCompoundStatus().addStatus(status); return result; } private void reportException(String msg, CompoundStatus status) { reportException(msg, null, status); } private void reportException(String msg, Exception e, CompoundStatus status) { if (e != null) { /* Stack trace at INFO level to comply with PSP */ _log.log(Level.INFO, msg, e); msg += ": "+e.getMessage(); } _log.log(Level.WARNING, msg); status.registerTaskFailed(false, msg); } public Map<String, Task> powerOnVMs(Client client, Set<String> vmMoRefs) { CompoundStatus status = new CompoundStatus(VCActions.VC_POWER_ON_STATUS_KEY); Map<String, Task> result = new HashMap<String, Task>(); for (String moRef : vmMoRefs) { ManagedObjectReference ref = new ManagedObjectReference(); ref.setValue(moRef); VirtualMachine vm = client.createStub(VirtualMachine.class, ref); try { ManagedObjectReference taskRef = vm.powerOn(null); Task task = client.createStub(Task.class, taskRef); result.put(moRef, task); status.registerTaskSucceeded(); } catch (Exception e) { reportException("Error powering on VM: "+e.getMessage(), status); } } getCompoundStatus().addStatus(status); return result; } public Map<String, Task> powerOffVMs(Client client, Set<String> vmMoRefs) { CompoundStatus status = new CompoundStatus(VCActions.VC_POWER_OFF_STATUS_KEY); Map<String, Task> result = new HashMap<String, Task>(); for (String moRef : vmMoRefs) { ManagedObjectReference ref = new ManagedObjectReference(); ref.setValue(moRef); VirtualMachine vm = client.createStub(VirtualMachine.class, ref); try { ManagedObjectReference taskRef = vm.powerOff(); Task task = client.createStub(Task.class, taskRef); result.put(moRef, task); status.registerTaskSucceeded(); } catch (Exception e) { reportException("Error powering off VM: "+e.getMessage(), status); } } getCompoundStatus().addStatus(status); return result; } public void cancelWaitForUpdates() { synchronized(_propertyCollectorLock) { if (_blockedPropertyCollectorSingleton != null) { _blockedPropertyCollectorSingleton.cancelWaitForUpdates(); } } } public PerformanceManager getPerformanceManager(Client client) { try { ServiceInstanceContent sic = getServiceInstanceContent(client); return client.createStub(PerformanceManager.class, sic.getPerfManager()); } catch (Exception e) { _log.info("Cannot connect to VC performance manager"); return null; } } private EventManager getEventManager(Client client) { try { ServiceInstanceContent sic = getServiceInstanceContent(client); return client.createStub(EventManager.class, sic.getEventManager()); } catch (Exception e) { _log.info("Cannot connect to VC event manager"); return null; } } private AlarmManager getAlarmManager(Client client) { try { ServiceInstanceContent sic = getServiceInstanceContent(client); return client.createStub(AlarmManager.class, sic.getAlarmManager()); } catch (Exception e) { _log.info("Cannot connect to VC alarm manager"); return null; } } public boolean postEventForVM(Client client, String vmMoRef, EventSeverity level, String message) { EventManager eventManager = getEventManager(client); if (eventManager == null) { return false; } ManagedObjectReference ref = new ManagedObjectReference(); ref.setValue(vmMoRef); ref.setType("VirtualMachine"); EventEx event = new EventExImpl(); event.setCreatedTime(Calendar.getInstance()); event.setUserName("Big Data Extensions"); event.setEventTypeId("com.vmware.vhadoop.vhm.vc.events."+level.name()); event.setSeverity(level.name()); event.setMessage(message); event.setObjectId(ref.getValue()); event.setObjectType(new TypeNameImpl("VirtualMachine")); try { _log.log(VhmLevel.USER, "VHM: <%V"+vmMoRef+"%V> - "+message); eventManager.postEvent(event, null); return true; } catch (InvalidEvent e) { _log.log(Level.INFO, "VHM: <%V"+vmMoRef+"%V> - failed to log "+level.name()+" event with vCenter", e); } return false; } private Alarm getAlarm(Client client, String rootFolderName) { synchronized(_alarmLock) { if (_alarmSingleton != null) { return _alarmSingleton; } AlarmManager manager = getAlarmManager(client); if (manager == null) { return null; } Folder root; try { root = getFolderForName(client, null, rootFolderName); ManagedObjectReference[] existing = manager.getAlarm(root._getRef()); for (ManagedObjectReference m : existing) { Alarm a = client.createStub(Alarm.class, m); if (a.getInfo().getName().startsWith(VC_ALARM_NAME_BASE)) { _alarmSingleton = a; return _alarmSingleton; } } } catch (InvalidProperty e) { _log.info("VHM: unable to get reference to alarm "+VC_ALARM_NAME_BASE+" on vApp folder "+rootFolderName); _log.log(Level.FINER, "VHM: exception while getting reference to top level alarm", e); } catch (NullPointerException e) { /* almost any of the values returned from vlsi client or their subsequent calls could be null but * will not be most of the time. It's much clearer to just have one catch here than tests on * ever access given we do the same thing in response. */ _log.info("VHM: unable to get reference to alarm "+VC_ALARM_NAME_BASE+" on vApp folder "+rootFolderName); _log.log(Level.FINER, "VHM: exception while getting reference to top level alarm", e); } } return null; } void acknowledgeAlarm(Client client, String rootFolderName, String vmMoRef) { AlarmManager alarmMgr = getAlarmManager(client); if (alarmMgr == null) { return; } /* acknowledge the alarm */ Alarm alarm = getAlarm(client, rootFolderName); if (alarm != null) { ManagedObjectReference moRef = new ManagedObjectReference(); moRef.setValue(vmMoRef); moRef.setType("VirtualMachine"); alarmMgr.acknowledgeAlarm(alarm._getRef(), moRef); } } } /* VirtualMachine vm = getVMForName(f, "xxxxx"); _log.log(Level.INFO, "TPC vm.name = " + vm.getName()); _log.log(Level.INFO, "TPC vm = " + vm); PowerState ps = vm.getRuntime().getPowerState(); _log.log(Level.INFO, "TPC vm ps = " + ps); ManagedObjectReference taskRef; if (ps == PowerState.poweredOn) { taskRef = vm.powerOff(); } else { taskRef = vm.powerOn(null); } Task t = defaultClient.createStub(Task.class, taskRef); boolean success = waitForTask(t); _log.log(Level.INFO, "TPC task success=" + success); ps = vm.getRuntime().getPowerState(); _log.log(Level.INFO, "TPC vm new ps = " + ps); */