/***************************************************************************
* Copyright (c) 2012-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.aurora.vc.vcservice;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import com.vmware.aurora.exception.VcException;
import com.vmware.aurora.global.Configuration;
import com.vmware.aurora.security.CmsKeyStore;
import com.vmware.aurora.util.AuAssert;
import com.vmware.aurora.util.HttpsConnectionUtil;
import com.vmware.aurora.vc.vcservice.VcService.MyThreadPoolExecutor.MyBlockingQueue;
import com.vmware.vim.binding.impl.vim.DescriptionImpl;
import com.vmware.vim.binding.impl.vim.ExtensionImpl;
import com.vmware.vim.binding.impl.vim.KeyValueImpl;
import com.vmware.vim.binding.impl.vim.ext.ExtendedProductInfoImpl;
import com.vmware.vim.binding.impl.vim.ext.ManagedEntityInfoImpl;
import com.vmware.vim.binding.impl.vim.ext.SolutionManagerInfoImpl;
import com.vmware.vim.binding.vim.Description;
import com.vmware.vim.binding.vim.Extension;
import com.vmware.vim.binding.vim.ExtensionManager;
import com.vmware.vim.binding.vim.FileManager;
import com.vmware.vim.binding.vim.KeyValue;
import com.vmware.vim.binding.vim.OvfManager;
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.TaskManager;
import com.vmware.vim.binding.vim.VirtualDiskManager;
import com.vmware.vim.binding.vim.alarm.AlarmManager;
import com.vmware.vim.binding.vim.event.Event;
import com.vmware.vim.binding.vim.event.Event.EventSeverity;
import com.vmware.vim.binding.vim.ext.ExtendedProductInfo;
import com.vmware.vim.binding.vim.ext.ManagedEntityInfo;
import com.vmware.vim.binding.vim.ext.SolutionManagerInfo;
import com.vmware.vim.binding.vim.option.OptionManager;
import com.vmware.vim.binding.vim.option.OptionValue;
import com.vmware.vim.binding.vim.version.version8;
import com.vmware.vim.binding.vmodl.ManagedObject;
import com.vmware.vim.binding.vmodl.ManagedObjectReference;
import com.vmware.vim.binding.vmodl.query.PropertyCollector;
import com.vmware.vim.vmomi.client.Client;
import com.vmware.vim.vmomi.client.http.HttpClientConfiguration;
import com.vmware.vim.vmomi.client.http.HttpConfiguration;
import com.vmware.vim.vmomi.client.http.ThumbprintVerifier;
import com.vmware.vim.vmomi.client.http.impl.HttpConfigurationImpl;
import com.vmware.vim.vmomi.core.exception.CertificateValidationException;
import com.vmware.vim.vmomi.core.exception.InternalException;
import com.vmware.vim.vmomi.core.types.VmodlTypeMap;
/**
* <code>VcService</code> maintains connection and session with VC server.
*/
public class VcService {
private static final Logger logger = Logger.getLogger(VcService.class);
private static final String SERENGETI_EXTENSION_REGISTERED = "serengeti.extension.registered";
private static final Class<?> version = version8.class;
private static final int SESSION_TIME_OUT = Configuration.getInt(
"vc.session_time_out", 120000);
/*
* The following fields are VC login info, initialized once.
*/
static private boolean configured = false;
static private boolean vcExtensionRegistered = false;
static private String vcHost;
static private int vcPort;
static private String evsURL;
static private String evsToken;
static private String vcThumbprint;
static private String extKey;
static private String userName;
static private String password;
static private String locale;
private final String serviceName; // Internally used session name.
private final boolean useExecutor; // Whether this VcService uses ThreadPoolExecutor.
private final int timeoutMillis; // HTTP timeout
private ThumbprintVerifier getThumbprintVerifier() {
return new ThumbprintVerifier() {
@Override
public Result verify(String thumbprint) {
if (thumbprint.equalsIgnoreCase(vcThumbprint)) {
return Result.MATCH;
} else {
return Result.MISMATCH;
}
}
@Override
public void onSuccess(X509Certificate[] chain, String thumbprint,
Result verifyResult, boolean trustedChain,
boolean verifiedAssertions) throws SSLException {
}
};
}
/**
* XXX This class is created to debug PR 848988.
* @author mchen
*/
static class MyThreadPoolExecutor extends ThreadPoolExecutor {
static class MyRejectHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
RejectedExecutionException e = new RejectedExecutionException();
logger.info("rejecting runnable " + r, e);
logger.info((executor.isShutdown()? " shutdown " : " running ") +
",tasks=" + executor.getTaskCount() +
",poolSize=" + executor.getPoolSize() +
",maxPoolSize=" + executor.getMaximumPoolSize() +
",active=" + executor.getActiveCount() +
",queue=" + executor.getQueue() +
",qSize=" + executor.getQueue().size() +
",qCapacity=" + executor.getQueue().remainingCapacity());
throw e;
}
static public MyRejectHandler getInstance() {
return new MyRejectHandler();
}
}
@SuppressWarnings("serial")
static class MyBlockingQueue<E> extends LinkedBlockingQueue<E> {
public MyBlockingQueue() {
super();
}
@Override
public boolean offer(E e) {
int oldSize = size();
int oldRemainingCapacity = remainingCapacity();
boolean retVal = super.offer(e);
if (!retVal) {
logger.info("offer failed: size=" + size() +
",capacity=" + remainingCapacity() +
",oldSize=" + oldSize +
",oldRemainingCapacity=" + oldRemainingCapacity);
}
return retVal;
}
}
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, MyBlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, MyRejectHandler.getInstance());
}
}
/*
* References to VC service objects.
*/
private class ServiceContents {
private final long genCount; // generation count of this service instance
private Client vmomiClient;
private ThreadPoolExecutor executor;
private HttpConfiguration httpConfig;
private ServiceInstance instance = null;
private ServiceInstanceContent instanceContent = null;
private SessionManager sessionManager = null;
private FileManager fileManager = null;
private VirtualDiskManager vmdkManager = null;
private OvfManager ovfManager = null;
private PerformanceManager perfManager = null;
private TaskManager taskManager = null;
private OptionManager optionManager = null;
private PropertyCollector propertyCollector = null;
private ExtensionManager extensionManager = null;
private AlarmManager alarmManager = null;
/*
* A map that caches all managed object proxy objects.
*/
private final Map<ManagedObjectReference, ManagedObject> moMap =
new HashMap<ManagedObjectReference, ManagedObject>();
/*
* Login VC session and get service objects.
*/
private ServiceContents(long genCount) throws Exception
{
this.genCount = genCount;
long startNanos = System.nanoTime();
String sessionTicket = loginAndGetSessionTicket();
ManagedObjectReference svcRef = new ManagedObjectReference();
boolean done = false;
svcRef.setType("ServiceInstance");
svcRef.setValue("ServiceInstance");
try {
initVmomiClient();
instance = vmomiClient.createStub(ServiceInstance.class, svcRef);
/*
* Establish session.
*/
instanceContent = instance.retrieveContent();
sessionManager = vmomiClient.createStub(SessionManager.class,
instanceContent.getSessionManager());
/*
* login for the user
*/
if (sessionTicket != null) {
try {
sessionManager.loginBySessionTicket(sessionTicket);
} catch (Exception e) {
logger.error("failed to use VC session ticket", e);
throw e;
}
} else if (userName != null) {
logger.info("try to login to VC using username and password");
sessionManager.login(userName, password, locale);
} else {
throw VcException.LOGIN_ERROR();
}
fileManager = getManagedObject(instanceContent.getFileManager());
vmdkManager = getManagedObject(instanceContent.getVirtualDiskManager());
taskManager = getManagedObject(instanceContent.getTaskManager());
ovfManager = getManagedObject(instanceContent.getOvfManager());
perfManager = getManagedObject(instanceContent.getPerfManager());
optionManager = getManagedObject(instanceContent.getSetting());
propertyCollector = getManagedObject(instanceContent.getPropertyCollector());
extensionManager = getManagedObject(instanceContent.getExtensionManager());
alarmManager = getManagedObject(instanceContent.getAlarmManager());
logger.info("VC login on behalf of {" + Thread.currentThread().getName() +
":" + serviceName + "{" + genCount + "}} " +
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos) + "ms");
done = true;
} finally {
// Clean up if we cannot proceed.
if (!done) {
cleanup();
}
}
}
/**
* Initialize a new vmomiClient, including all required resources, thread
* pool, etc. Thread names will include service name, generation count and
* a thread number in the pool as in "Thread[vcService{2}-1]".
* @throws Exception
*/
private void initVmomiClient() throws URISyntaxException {
String threadNamePrefix = serviceName + "{" + genCount + "}-";
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory(threadNamePrefix);
threadFactory.setDaemon(true);
if (useExecutor) {
executor = new MyThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS,
new MyBlockingQueue<Runnable>(), threadFactory);
} else {
executor = null;
}
URI uri;
String serviceUrl = getServiceUrl();
try {
uri = new URI(serviceUrl);
} catch (URISyntaxException e) {
logger.error("Bad VC URL " + serviceUrl + e);
AuAssert.check(false);
throw e;
}
httpConfig = new HttpConfigurationImpl();
httpConfig.setTimeoutMs(timeoutMillis);
httpConfig.setThumbprintVerifier(getThumbprintVerifier());
if (useExecutor) {
vmomiClient = Client.Factory.createClient(uri, version, executor, httpConfig);
} else {
HttpClientConfiguration clientConfig = HttpClientConfiguration.Factory.newInstance();
clientConfig.setHttpConfiguration(httpConfig);
vmomiClient = Client.Factory.createClient(uri, version, clientConfig);
}
}
/*
* Get & insert a managed object proxy from moMap cache.
*/
private synchronized <T extends ManagedObject> T
getManagedObjectInt(ManagedObjectReference moRef) {
@SuppressWarnings("unchecked")
T obj = (T)moMap.get(moRef);
if (obj != null) {
return obj;
}
// The VmodlTypeMap.getVmodlType() is applicable for any vmodl type and is
// not specific to ManagedObject. We need to cast to Class<T> because VLSI
// core has changed the interface.
@SuppressWarnings({ "unchecked"})
Class<T> clazz = (Class<T>) VmodlTypeMap.Factory.getTypeMap()
.getVmodlType(moRef.getType()).getTypeClass();
obj = vmomiClient.<T>createStub(clazz, moRef);
moMap.put(moRef, obj);
return obj;
}
/*
* Get a managed object proxy from moMap cache.
*/
private <T extends ManagedObject> T
getManagedObject(ManagedObjectReference moRef) {
@SuppressWarnings("unchecked")
T obj = (T)moMap.get(moRef);
if (obj != null) {
return obj;
}
return this.<T>getManagedObjectInt(moRef);
}
/**
* Logout out of vc and tear down all consumed resources.
*/
private void cleanup() {
moMap.clear();
VcContext.getVcCleaner().logout(serviceName, vmomiClient, sessionManager,
executor, httpConfig);
}
public int getInstanceId() {
OptionValue[] options = optionManager.getSetting();
for (OptionValue option : options) {
if (option.getKey().equals("instance.id")) {
return (Integer)option.getValue();
}
}
throw VcException.SETTING_ERROR();
}
void dumpStatus() {
logger.info(serviceName + "{" + genCount + "}:" +
(executor.isShutdown()? " shutdown " : " running ") +
"tasks=" + executor.getTaskCount() +
",qSize=" + executor.getQueue().size() +
",qCapacity=" + executor.getQueue().remainingCapacity());
}
}
/*
* Because VC sessions are shared by multiple threads,
* we use a generation counter to keep track of the current
* login-session to prevent multiple logout calls on
* the same session. There will be exactly one VC session manager
* logout and clearing of service contents per VC login.
*/
private volatile long curGenCount = 0;
/*
* VC connection objects. If a connection failed or was closed,
* always establish new instances.
*/
ServiceContents service = null;
private void setService(ServiceContents service, VcConnectionStatusChangeEvent eventType) {
AuAssert.check(Thread.holdsLock(this));
/*
* Always log an event for an initial attempt: both VcContext is
* initialized for the first time and this session gets its first life.
* Otherwise, log an an event only if a service state change has been
* detected to avoid event storms when VC is persistently down.
*/
if (eventType != null &&
((VcContext.getGenCount() == 1 && curGenCount == 1) ||
this.service != service)) {
VcContext.triggerEvent(eventType, serviceName);
}
this.service = service;
}
private static void initVcConfig() {
/*
* The following configs should be set in
* aurora-cms-transient.properties by evs_init.py
* on every CMS boot.
*/
vcHost = Configuration.getString("vim.host");
vcPort = Configuration.getInt("vim.port", 443);
evsURL = Configuration.getString("vim.evs_url");
evsToken = Configuration.getString("vim.evs_token");
vcThumbprint = Configuration.getString("vim.thumbprint", null);
/*
* Extension key is based on CMS instance identifier.
*/
extKey = "com.vmware.aurora.vcext.instance-" + Configuration.getCmsInstanceId();
// represent if extension service is already registered
vcExtensionRegistered = Configuration.getBoolean(SERENGETI_EXTENSION_REGISTERED, false);
/*
* The following are not set in config files by default.
* They can be hard coded manually.
*/
userName = Configuration.getString("vim.username", null);
password = Configuration.getString("vim.password", null);
locale = Configuration.getString("vim.locale", "en");
HttpsConnectionUtil.init(vcThumbprint);
configured = true;
}
/**
* Create a VC service instance. The following is done:
* - Load the VLSI vmodl context.
* - Create a VC client object with VLSI with VC URL,
* HTTP configurations and a thread pool.
*
* After initialization, VC session will be established.
*
* @param serviceName internal name
*/
public VcService(String serviceName, boolean useExecutor, int timeoutMillis)
throws VcException {
if (!configured) {
initVcConfig();
}
this.serviceName = serviceName;
this.useExecutor = useExecutor;
this.timeoutMillis = timeoutMillis;
synchronized(this) {
// Connect to VC.
try {
initVcSession();
} catch (Exception e) {
logger.error("Cannot establish session to VC " +
vcHost + ":" + vcPort + " on startup,", e);
}
}
}
public VcService(String serviceName) {
this(serviceName, true, SESSION_TIME_OUT);
}
public String getServiceName() {
return serviceName;
}
public String getServiceUrl() {
return "https://" + vcHost + ":" + vcPort + "/sdk";
}
public String getClientSessionId() {
return getServiceContents().vmomiClient.getBinding().getSession().getId();
}
/**
* Returns true if a connection to VC has already been established
*/
public boolean isConnected() {
return service != null;
}
/*
* Login once using VC extension key (stored in cms keystore)
* and retrieve the session ticket.
*/
private String loginAndGetSessionTicket() {
URI sdkUri = null;
if (!vcExtensionRegistered) {
return null;
}
try {
sdkUri = new URI("https://sdkTunnel:8089/sdk/vimService");
} catch (URISyntaxException e) {
logger.error(e);
return null;
}
HttpConfigurationImpl httpConfig = new HttpConfigurationImpl();
httpConfig.setTimeoutMs(SESSION_TIME_OUT);
httpConfig.setKeyStore(CmsKeyStore.getKeyStore());
httpConfig.setDefaultProxy(vcHost, 80, "http");
httpConfig.getKeyStoreConfig().setKeyAlias(CmsKeyStore.VC_EXT_KEY);
httpConfig.getKeyStoreConfig().setKeyPassword(CmsKeyStore.getVCExtPassword());
httpConfig.setThumbprintVerifier(getThumbprintVerifier());
HttpClientConfiguration clientConfig = HttpClientConfiguration.Factory.newInstance();
clientConfig.setHttpConfiguration(httpConfig);
Client client = Client.Factory.createClient(sdkUri, version, clientConfig);
SessionManager sm = null;
try {
ManagedObjectReference svcRef = new ManagedObjectReference();
svcRef.setType("ServiceInstance");
svcRef.setValue("ServiceInstance");
ServiceInstance si = client.createStub(ServiceInstance.class, svcRef);
sm = client.createStub(SessionManager.class,
si.getContent().getSessionManager());
sm.loginExtensionByCertificate(extKey, "en");
String ticket = sm.acquireSessionTicket(null);
logger.info("got session ticket using extension");
return ticket;
} catch (Exception e) {
logger.error("failed to get session ticket using extension", e);
return null;
} finally {
VcContext.getVcCleaner().logout("VcExtensionLogin", client, sm,
null, httpConfig);
}
}
/**
* Initializes the VC session. Each session gets a separate vmomi client
* (http session cookie is kept in vmomi client).
*
* Result:
* _serviceInstance & other fields initialized if no exception is thrown.
*/
private void initVcSession() throws VcException {
try {
/*
* Make sure our VC extension has been registered.
*/
boolean justRegistered = false;
if (!vcExtensionRegistered) {
registerExtensionVService();
justRegistered = true;
}
if (vcExtensionRegistered) {
String ticket = null;
try {
ticket = loginAndGetSessionTicket();
} catch (Exception e) {
logger.debug("Got exception during login");
}
if (ticket == null) {
logger.info("Failed to login using certificate, try to regist extension once again");
vcExtensionRegistered = false;
}
}
if (!vcExtensionRegistered) {
registerExtensionVService();
justRegistered = true;
}
/*
* attach to the new VC session
*/
setService(new ServiceContents(++curGenCount), VcConnectionStatusChangeEvent.VC_SESSION_CREATED);
/* Context callback to communicate session reset. */
VcContext.serviceReset(this);
/*
* Now that we have a valid VMOMI connection, set properties for our extension
*/
if (vcExtensionRegistered && justRegistered) {
configureExtensionVService();
}
} catch (Exception ex) {
if (service != null) {
service.cleanup();
}
setService(null, VcConnectionStatusChangeEvent.VC_SESSION_CREATION_FAILURE);
if (ex instanceof VcException) {
throw (VcException)ex;
} else if (ex instanceof UndeclaredThrowableException) {
UndeclaredThrowableException e = (UndeclaredThrowableException)ex;
if (e.getUndeclaredThrowable() instanceof CertificateValidationException) {
throw VcException.LOGIN_ERROR(e.getUndeclaredThrowable());
}
} else if (ex instanceof InternalException) {
InternalException e = (InternalException)ex;
if (e.getCause() instanceof CertificateValidationException) {
throw VcException.LOGIN_ERROR(e.getCause());
}
}
throw VcException.LOGIN_ERROR(ex);
}
}
/**
* Returns a PEM representation of a certificate
*
* @param cert A non-null certificate
* @return The PEM representation
* @throws CertificateEncodingException, if certificate is malformed
*/
private static String CertificateToPem(Certificate cert) throws CertificateEncodingException {
byte[] base64 = Base64.encodeBase64Chunked(cert.getEncoded());
StringBuffer sb = new StringBuffer();
sb.append("-----BEGIN CERTIFICATE-----\n");
sb.append(new String(base64));
sb.append("-----END CERTIFICATE-----");
return sb.toString();
}
/**
* This sends a URL POST request to the Extension vService guest API to
* register a new extension. Upon success, set vcExtensionRegistered to true.
* Note that the extension will not be fully configured until we log in to VC
* as this extension and make some VMODL calls to finish the job.
*
* Note also that we only need to do this once per CMS install, not once per
* CMS startup, but it doesn't seem to hurt to do it every time.
*
* @synchronized for preventing concurrent call to register EVS.
*/
private static synchronized void registerExtensionVService() {
if (vcExtensionRegistered) {
return;
}
logger.debug("Register extension vService at: " + evsURL + " token=" + evsToken);
Writer output = null;
BufferedReader input = null;
try {
Certificate cert = CmsKeyStore.getCertificate(CmsKeyStore.VC_EXT_KEY);
URL url = new URL(evsURL);
URLConnection connection = url.openConnection();
connection.setRequestProperty("evs-token", evsToken);
connection.setDoInput(true);
connection.setDoOutput(true); // POST
connection.setUseCaches(false);
output = new OutputStreamWriter(connection.getOutputStream());
String evsSchema = "http://www.vmware.com/schema/vservice/ExtensionVService";
String payload =
"<RegisterExtension xmlns=\"" + evsSchema + "\">\n" +
" <Key>" + extKey + "</Key>\n" +
" <Certificate>\n" +
CertificateToPem(cert) + "\n" +
" </Certificate>\n" +
"</RegisterExtension>\n";
output.write(payload);
output.flush();
connection.connect();
// Read response headers
Map<String, List<String>> headers = connection.getHeaderFields();
for (Map.Entry<String, List<String>> e: headers.entrySet()) {
for (String val: e.getValue()) {
logger.info("Response Header: " + e.getKey() + " :" + val);
}
}
// Read response
input = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
for (String str = input.readLine(); str != null; str = input.readLine()) {
logger.debug("Response: " + str);
}
vcExtensionRegistered = true;
logger.debug("Extension registration request sent successfully");
} catch (Exception e) {
logger.error("Failed Extension registration to " + evsURL, e);
} finally {
Configuration.setBoolean(SERENGETI_EXTENSION_REGISTERED, true);
Configuration.save();
if (output != null) {
try {
output.close();
} catch (IOException e) {
logger.error("Failed to close output Writer", e);
}
}
if (input != null) {
try {
input.close();
} catch (IOException e) {
logger.error("Failed to close input Reader", e);
}
}
}
}
private void configureExtensionVService() throws Exception {
ExtensionManager em = service.extensionManager;
Extension us = em.findExtension(extKey);
AuAssert.check(us != null);
// Describe Aurora itself
Description desc = new DescriptionImpl();
desc.setLabel("VMware Serengeti Management Server");
desc.setSummary("VMware Serengeti Management Server, instance " + Configuration.getCmsInstanceId());
us.setDescription(desc);
us.setCompany("VMware, Inc.");
us.setVersion(Configuration.getNonEmptyString("serengeti.version"));
us.setShownInSolutionManager(true);
ExtendedProductInfo extInfo = new ExtendedProductInfoImpl();
extInfo.setCompanyUrl("http://www.vmware.com");
us.setExtendedProductInfo(extInfo);
// XXX: Set health info, any other fields?
// Describe the entities we manage (DBVM)
ManagedEntityInfo info = new ManagedEntityInfoImpl();
info.setType("hadoop node");
info.setDescription("VMware Serengeti - Node Template");
//info.setSmallIconUrl("https://*:443/some-16x16.png");
ManagedEntityInfo[] infos = new ManagedEntityInfo[1];
infos[0] = info;
us.setManagedEntityInfo(infos);
// Generate ResourceInfo
Extension.ResourceInfo extensionResourceInfo = new ExtensionImpl.ResourceInfoImpl();
extensionResourceInfo.setLocale("en");
extensionResourceInfo.setModule("extension");
KeyValue localizedExt[] = new KeyValue[2];
localizedExt[0] = new KeyValueImpl();
localizedExt[0].setKey(us.getKey() + ".label");
localizedExt[0].setValue(us.getDescription().getLabel());
localizedExt[1] = new KeyValueImpl();
localizedExt[1].setKey(us.getKey() + ".summary");
localizedExt[1].setValue(us.getDescription().getSummary());
extensionResourceInfo.setData(localizedExt);
// Generate event type specifications
Extension.ResourceInfo eventResourceInfo = new ExtensionImpl.ResourceInfoImpl();
eventResourceInfo.setLocale("en");
eventResourceInfo.setModule("event");
class KeyValueList extends ArrayList<KeyValue> {
public void add(String key, String value) {
KeyValue pair = new KeyValueImpl();
pair.setKey(key);
pair.setValue(value);
super.add(pair);
}
};
KeyValueList resourceInfo = new KeyValueList();
ArrayList<Extension.EventTypeInfo> eventTypes = new ArrayList<Extension.EventTypeInfo>();
for (EventSeverity severity : Event.EventSeverity.values()) {
resourceInfo.add("com.vmware.vhadoop.vhm.vc.events."+severity.name()+".label", "BDE notification");
resourceInfo.add("com.vmware.vhadoop.vhm.vc.events."+severity.name()+".summary", "BDE notification");
resourceInfo.add("com.vmware.vhadoop.vhm.vc.events."+severity.name()+".category", severity.name());
resourceInfo.add("com.vmware.vhadoop.vhm.vc.events."+severity.name()+".fullFormat", "{message}");
resourceInfo.add("com.vmware.vhadoop.vhm.vc.events."+severity.name()+".formatOnVm", "BDE notification");
Extension.EventTypeInfo event = new ExtensionImpl.EventTypeInfoImpl();
event.setEventID("com.vmware.vhadoop.vhm.vc.events."+severity.name());
event.setEventTypeSchema("<EventType><eventTypeID>com.vmware.vhadoop.vhm.vc.events."+severity.name()+"</eventTypeID><description>Status update for a Big Data Extensions compute VM</description><arguments/></EventType>");
eventTypes.add(event);
}
eventResourceInfo.setData(resourceInfo.toArray(new KeyValue[0]));
us.setResourceList(new Extension.ResourceInfo[] {extensionResourceInfo, eventResourceInfo});
us.setEventList(eventTypes.toArray(new Extension.EventTypeInfo[0]));
us.setShownInSolutionManager(true);
SolutionManagerInfo sm = new SolutionManagerInfoImpl();
sm.setSmallIconUrl("http://www.vmware.com");
us.setSolutionManagerInfo(sm);
// Push this info into VC
em.updateExtension(us);
}
/**
* Synchronized creation of new VC session.
* @return the VC service object.
* @throws Exception
*/
private synchronized ServiceContents
getServiceContentsLocked() throws VcException {
if (!isConnected()) {
initVcSession();
}
AuAssert.check(service != null);
return service;
}
/*
* If already logged into VC, do nothing.
* Otherwise, try to instantiate a new {\ServiceContents} object
* and start a new login session.
*
* This function speculatively fetches the session without locks
* and fall back on synchronized method.
*/
private ServiceContents getServiceContents() throws VcException {
ServiceContents svc = service;
if (svc == null) {
return getServiceContentsLocked();
}
return svc;
}
/**
* Dump debug info of the current service.
*/
public void dumpServiceStatus() {
ServiceContents svc = service;
if (svc != null) {
svc.dumpStatus();
} else {
logger.info("vcservice not connected last genCount=" + curGenCount);
}
}
/**
* Get the generation count of the current service context, if any.
* @return Generation count, or null.
*/
public Long getServiceGenCount() {
ServiceContents svc = service;
if (svc != null) {
return svc.genCount;
} else {
return null;
}
}
/**
* Clean up of the current VC service session. Physical vc logout
* is deferred to VcCleanup thread. This call cannot fail.
* @param generation count to match the session.
* @param forced True if we always logout regardless of generation count.
*/
private synchronized void clearServiceContents(long generation,
boolean forced, VcConnectionStatusChangeEvent eventType) {
if (isConnected()) {
if (forced || service.genCount == generation) {
/* Report disconnect event before async logout to avoid event reorder. */
service.cleanup();
setService(null, eventType);
AuAssert.check(!isConnected());
}
}
}
/**
* Drop connection on the floor without cleaning up.
* Used for TESTING ONLY to drop VC connection.
*/
public synchronized void dropConnection() {
try {
if(isConnected()) {
/* For testing, this is synchronous, no VcCleaner. */
VcContext.triggerEvent(VcConnectionStatusChangeEvent.VC_SESSION_DISCONNECTED, serviceName);
service.sessionManager.logout();
}
} catch (Exception e) {
logger.info("Got exception trying to drop connection" + e);
}
}
/**
* Connect to VC if a session has not been established.
* @return generation number for the VC session
*/
public long login() throws VcException {
return getServiceContents().genCount;
}
/**
* Force logout of the current VC session.
*/
public void logout() {
clearServiceContents(-1, true, VcConnectionStatusChangeEvent.VC_SESSION_DISCONNECTED);
}
/**
* Same as above, but allows to trigger a custom event. Used, for example,
* during CMS shutdown.
* @param eventType
*/
public void logout(VcConnectionStatusChangeEvent eventType) {
clearServiceContents(-1, true, eventType);
}
/**
* Logs out the session from VC server iff the genCount of the
* current VC service matches the login generation of the caller.
* @param generation Last known VC login generation count by the caller.
*/
public void logout(long generation) {
clearServiceContents(generation, false, VcConnectionStatusChangeEvent.VC_SESSION_DISCONNECTED);
}
/**
* Reestablish connection by logging in and out.
* @param generation Last known VC login generation count
* @return new VC login generation count
* @throws VcException
*/
public long reconnect(long generation) throws VcException {
logout(generation);
return login();
}
public ServiceInstance getServiceInstance() throws VcException {
return getServiceContents().instance;
}
public ServiceInstanceContent getServiceInstanceContent() throws VcException {
return getServiceContents().instanceContent;
}
public FileManager getFileManager() throws VcException {
return getServiceContents().fileManager;
}
public VirtualDiskManager getVirtualDiskManager() throws VcException {
return getServiceContents().vmdkManager;
}
public TaskManager getTaskManager() throws VcException {
return getServiceContents().taskManager;
}
public OvfManager getOvfManager() throws VcException {
return getServiceContents().ovfManager;
}
public PerformanceManager getPerfManager() throws VcException {
return getServiceContents().perfManager;
}
public AlarmManager getAlarmManager() throws VcException {
return getServiceContents().alarmManager;
}
public int getInstanceId() {
return getServiceContents().getInstanceId();
}
/**
* @return default property collector for this client.
* @throws VcException
*/
public PropertyCollector getPropertyCollector() throws VcException {
return getServiceContents().propertyCollector;
}
/**
* Given a <code>ManagedObjectReference</code> instance, uses it to fetch the
* mapping <code>ManagedObject</code>
*
* @param moRef
* The <code>ManagedObjectReference</code> instance.
* @return ManagedObject instance
*/
public <T extends ManagedObject> T getManagedObject(ManagedObjectReference moRef) {
return getServiceContents().<T>getManagedObject(moRef);
}
public String getExtensionKey() {
return extKey;
}
}