/*************************************************************************** * Copyright (c) 2013 VMware, Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package com.vmware.vhadoop.vhm.vc; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import com.vmware.vhadoop.api.vhm.VCActions; import com.vmware.vhadoop.util.CompoundStatus; import com.vmware.vhadoop.util.ExternalizedParameters; import com.vmware.vhadoop.util.ThreadLocalCompoundStatus; import com.vmware.vhadoop.vhm.vc.VcClientFactory.VcClientKey; import com.vmware.vim.binding.vim.PerformanceManager; import com.vmware.vim.binding.vim.Task; import com.vmware.vim.binding.vim.event.Event.EventSeverity; import com.vmware.vim.vmomi.client.Client; public class VcAdapter implements VCActions { private static final Logger _log = Logger.getLogger(VcAdapter.class.getName()); private final long SLEEP_RETRY_LOOKING_FOR_VALID_CLUSTERS = ExternalizedParameters.get().getLong("SLEEP_RETRY_LOOKING_FOR_VALID_CLUSTERS"); /* If no clusters are installed, we should be pretty much dormant */ private final Long CUSTOM_RESET_CLIENT_TIMEOUT = 10000L; private final VcClientFactory _clientFactory; /* THREADING: Thread-safe lazy access to VC clients */ private final VcVlsi _vcVlsi; /* THREADING: Thread-safe singleton */ private final String _rootFolderName; // root folder for this VHM instance private ThreadLocalCompoundStatus _threadLocalStatus; public void setThreadLocalCompoundStatus(ThreadLocalCompoundStatus tlcs) { _threadLocalStatus = tlcs; _vcVlsi.setThreadLocalCompoundStatus(tlcs); } /* TODO: CompoundStatus is currently unused in VC functions, but we can get richer failure info if/when it's used */ @SuppressWarnings("unused") private CompoundStatus getCompoundStatus() { if (_threadLocalStatus == null) { if (_log.isLoggable(Level.FINER)) { _log.finer("Returning dummy status compound status for thread "+Thread.currentThread()); } return new CompoundStatus("DUMMY_STATUS"); } return _threadLocalStatus.get(); } public VcAdapter(VcCredentials vcCreds, String rootFolderName) { _rootFolderName = rootFolderName; _vcVlsi = new VcVlsi(); _clientFactory = new VcClientFactory(_vcVlsi, vcCreds); } /* THREADING: Can be called by multiple threads */ @Override public Map<String, Future<Boolean>> changeVMPowerState(Set<String> vmMoRefs, boolean powerOn) { Client client = _clientFactory.getAndValidateClient(VcClientKey.CONTROL_CLIENT); if (client == null) { return null; } Map<String, Task> taskList = null; if (powerOn) { taskList = _vcVlsi.powerOnVMs(client, vmMoRefs); } else { taskList = _vcVlsi.powerOffVMs(client, vmMoRefs); } return convertTaskListToFutures(client, taskList); } private Map<String, Future<Boolean>> convertTaskListToFutures(final Client client, Map<String, Task> taskList) { Map<String, Future<Boolean>> result = new HashMap<String, Future<Boolean>>(); for (String moRef : taskList.keySet()) { final Task task = taskList.get(moRef); result.put(moRef, new Future<Boolean>() { @Override public boolean cancel(boolean arg0) { return false; } @Override public Boolean get() throws InterruptedException, ExecutionException { return _vcVlsi.waitForTask(client, task); } @Override public Boolean get(long arg0, TimeUnit arg1) throws InterruptedException, ExecutionException, TimeoutException { return null; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return false; }}); } return result; } /* THREADING: Single-threaded. This is only ever called by the ClusterStateChangeListener thread */ @Override public List<VMEventData> waitForPropertyChange(String folderName) throws InterruptedException { Client client = _clientFactory.getAndValidateClient(VcClientKey.WAIT_FOR_UPDATE_CLIENT); if (client == null) { return null; } List<VMEventData> result = new ArrayList<VMEventData>(); for (int i=0; i<2; i++) { String waitForUpdatesVersion = _clientFactory.getWaitForUpdatesVersion(); String versionStatus = _vcVlsi.waitForUpdates(client, folderName, waitForUpdatesVersion, result); if (versionStatus.equals(VcVlsi.WAIT_FOR_UPDATES_CANCELED_STATUS)) { _log.fine("WaitForUpdates cancelled - throwing InterruptedException"); throw new InterruptedException(); } else if (versionStatus.equals(VcVlsi.WAIT_FOR_UPDATES_INVALID_COLLECTOR_VERSION_STATUS)) { client = _clientFactory.resetClient(VcClientKey.WAIT_FOR_UPDATE_CLIENT, CUSTOM_RESET_CLIENT_TIMEOUT); if (client != null) { _log.fine("Invalid property collector version - successfully reset VC client"); continue; } else { _log.fine("Invalid property collector version - could not reset VC client"); break; } } else if (versionStatus.equals(VcVlsi.WAIT_FOR_UPDATES_INVALID_PROPERTY_STATUS)) { client = _clientFactory.resetClient(VcClientKey.WAIT_FOR_UPDATE_CLIENT, CUSTOM_RESET_CLIENT_TIMEOUT); if (client != null) { _log.fine("Invalid property status - successfully reset VC client"); continue; } else { _log.fine("Invalid property status - could not reset VC client"); break; } } else if (versionStatus.equals(VcVlsi.WAIT_FOR_UPDATES_NO_CLUSTERS)) { /* If no clusters are yet created, VHM should be pretty much dormant */ _log.fine("Sleeping for "+SLEEP_RETRY_LOOKING_FOR_VALID_CLUSTERS+"ms"); Thread.sleep(SLEEP_RETRY_LOOKING_FOR_VALID_CLUSTERS); } else if (!waitForUpdatesVersion.equals(versionStatus)) { _log.fine("Updating waitForUpdates version to "+versionStatus); _clientFactory.updateWaitForUpdatesVersion(versionStatus); } break; } _log.finest("Returning result "+result); return result; } /* THREADING: Can be called by multiple threads */ @Override public PerformanceManager getPerformanceManager() { Client client = _clientFactory.getAndValidateClient(VcClientKey.STATS_POLL_CLIENT); if (client == null) { return null; } return _vcVlsi.getPerformanceManager(client); } /* THREADING: Can be called by multiple threads */ @Override public List<String> listVMsInFolder(String folderName) { Client client = _clientFactory.getAndValidateClient(VcClientKey.CONTROL_CLIENT); if (client == null) { return null; } return _vcVlsi.getVMsInFolder(client, _rootFolderName, folderName); } @Override public void interruptWait() { _vcVlsi.cancelWaitForUpdates(); } /** * This logs an event with VC for the specified VM. * * @param level error, warning or info * @param vm the managed object reference of the VM the message applies to * @param message the message to display in VC and serengeti cluster detail * * @return true if successful, false otherwise */ /* THREADING: Can be called by multiple threads */ @Override public boolean logEventForVM(EventSeverity level, String vmMoRef, String message) { Client client = _clientFactory.getAndValidateClient(VcClientKey.CONTROL_CLIENT); if (client == null) { return false; } return _vcVlsi.postEventForVM(client, vmMoRef, level, message); } @Override /* THREADING: Can be called by multiple threads */ public void raiseAlarm(String vmMoRef, String message) { logEventForVM(EventSeverity.warning, vmMoRef, message); } @Override /* THREADING: Can be called by multiple threads */ public void clearAlarm(String vmMoRef) { /* switch the VM back to green */ Client client = _clientFactory.getAndValidateClient(VcClientKey.CONTROL_CLIENT); if (client == null) { return; } _vcVlsi.postEventForVM(client, vmMoRef, EventSeverity.info, "all health issues previously reported by Big Data Extensions are in remission"); _vcVlsi.acknowledgeAlarm(client, _rootFolderName, vmMoRef); } }