/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.vasa;
import java.io.File;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import org.apache.log4j.Logger;
import com.emc.storageos.vasa.util.FaultUtil;
import com.emc.storageos.vasa.util.SSLUtil;
import com.emc.storageos.vasa.util.SessionContext;
import com.emc.storageos.vasa.util.internal.SimpleTimeCounter;
import com.vmware.vim.vasa._1_0.InvalidArgument;
import com.vmware.vim.vasa._1_0.InvalidCertificate;
import com.vmware.vim.vasa._1_0.InvalidLogin;
import com.vmware.vim.vasa._1_0.InvalidSession;
import com.vmware.vim.vasa._1_0.StorageFault;
import com.vmware.vim.vasa._1_0.data.xsd.HostInitiatorInfo;
import com.vmware.vim.vasa._1_0.data.xsd.MessageCatalog;
import com.vmware.vim.vasa._1_0.data.xsd.MountInfo;
import com.vmware.vim.vasa._1_0.data.xsd.StorageCatalogEnum;
import com.vmware.vim.vasa._1_0.data.xsd.UsageContext;
import com.vmware.vim.vasa._1_0.data.xsd.VasaProviderInfo;
import com.vmware.vim.vasa._1_0.data.xsd.VendorModel;
public class ContextManagerImpl implements ContextManager {
/** Class logger */
private static Logger log = Logger.getLogger(ContextManagerImpl.class);
/** Singleton instance */
private static ContextManagerImpl _instance;
private VasaProviderInfo _vpInfo;
private SSLUtil _sslUtil;
private SOSManager _sosManager;
private final String CATALOG_CONTEXT_URI = "/storageos-vasasvc/catalog/"; // NOSONAR
// ("Suppressing Sonar violation of Lazy initialization of static fields should be synchronized")
public static synchronized ContextManagerImpl getInstance() {
if (_instance == null) {
_instance = new ContextManagerImpl();
}
return _instance;
}
/** Constructor */
private ContextManagerImpl() {
}
@Override
public void init(SSLUtil sslUtil) {
this._sslUtil = sslUtil;
}
@Override
public MessageCatalog[] queryCatalog() throws StorageFault, InvalidSession {
final String methodName = "queryCatalog(): ";
log.debug(methodName + "Entry");
// verify valid SSL and VASA Sessions.
List<MessageCatalog> mcList = new ArrayList<MessageCatalog>();
String fs = System.getProperty("file.separator");
final String productHome = System.getProperty("product.home");
final String catalogDirPath = productHome + fs + "lib" + fs
+ "storageos-vasasvc" + fs + "catalog";
// Determine base Catalog directory
String catalogDirStr = "";
String catalinaHome = System.getProperty("server.home");
if (catalinaHome != null) {
// Ex: O:\Program Files\Apache\Tomcat 6.0
catalogDirStr = catalogDirPath;
} else {
try {
// Get the base dir of the running code
URL url = getClass().getProtectionDomain().getCodeSource()
.getLocation();
File catalogDir = new File(url.getPath());
catalogDirStr = catalogDir.getCanonicalPath();
if (catalogDirStr.contains("%20")) {
catalogDirStr = catalogDirStr.replace("%20", " ");
}
} catch (Exception e) {
log.error(methodName
+ "Exception attempting to locate catalog files", e);
throw FaultUtil.StorageFault("runtime", e);
}
}
if (catalogDirStr != null && catalogDirStr.length() > 0) {
File catalogDir = new File(catalogDirStr);
mcList = compileCatalogInformation(catalogDir, mcList);
}
log.debug(methodName + "Exit returning message catalog list of size["
+ mcList.size() + "]");
return (MessageCatalog[]) mcList.toArray(new MessageCatalog[0]);
}
private List<MessageCatalog> compileCatalogInformation(File catalog,
List<MessageCatalog> mcList) throws StorageFault {
final String methodName = "compileCatalogInformation(): ";
log.trace(methodName + "Entry with input(s) catalog[" + catalog
+ "] mcList of size[" + mcList.size() + "]");
if (catalog != null && catalog.exists()) {
if (catalog.isDirectory()) {
String[] children = catalog.list();
for (int i = 0; i < children.length; i++) {
compileCatalogInformation(new File(catalog, children[i]),
mcList);
}
} else {
try {
MessageCatalog mc = new MessageCatalog();
mc.setModuleName(Constants.VASA_BOURNE_PROVIDER_CATAGLOG_NAME);
mc.setCatalogVersion(Constants.VASA_BOURNE_PROVIDER_CATAGLOG_VERSION);
// Catalog Locale
String catalogLocale = catalog.toURI().toString();
catalogLocale = catalogLocale.substring(
catalogLocale.indexOf("catalog") + 8,
catalogLocale.lastIndexOf("/"));
mc.setLocale(catalogLocale);
// NOTE:: catalogName must match constants defined in
// StorageCatalogEnum
// only event and alarm catalogs are required.
if (catalog.getName().equalsIgnoreCase("event.vmsg")) {
mc.setCatalogName(StorageCatalogEnum.Event.getValue());
} else if (catalog.getName().equalsIgnoreCase("alarm.vmsg")) {
mc.setCatalogName(StorageCatalogEnum.Alarm.getValue());
} else if (catalog.getName().equalsIgnoreCase("fault.vmsg")) {
mc.setCatalogName(StorageCatalogEnum.Fault.getValue());
} else {
log.warn(methodName + "catalog: " + catalog.getName()
+ " is not supported!");
log.trace(methodName
+ "Exit returning message catalog list of size["
+ mcList.size() + "]");
return mcList;
}
// Catalog URI
String catalogURI = CATALOG_CONTEXT_URI + catalogLocale
+ "/" + catalog.getName();
mc.setCatalogUri(catalogURI);
// Catalog Last Modified
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(catalog.lastModified());
mc.setLastModified(calendar);
mcList.add(mc);
} catch (Exception e) {
log.error(methodName
+ "Exception attempting to locate catalog files", e);
throw FaultUtil.StorageFault("runtime", e);
}
}
}
log.trace(methodName + "Exit returning message catalog list of size["
+ mcList.size() + "]");
return mcList;
}
/**
* vasaService interface
*/
@Override
public VasaProviderInfo registerVASACertificate(String username,
String password, String certificateStr) throws InvalidCertificate,
InvalidLogin, InvalidSession, StorageFault {
final String methodName = "registerVASACertificate() :";
log.debug(methodName + "Entry with inputs username[" + username
+ "] password[" + (password != null ? "****" : null)
+ "] certificateStr["
+ (certificateStr != null ? "****" : null) + "]");
try {
_sosManager = new SOSManager();
/*
* Verify username/password before verifying certificate. This means
* that if both username/password and certificate are invalid
* InvalidLogin exception will be thrown.
*/
_sosManager.verifyLoginCredentials(username, password);
log.debug(methodName
+ "Valid username and password. User credentials accepted.");
String clientAddress = _sslUtil.checkHttpRequest(false, false);
X509Certificate x509Cert = (X509Certificate) _sslUtil
.buildCertificate(certificateStr);
x509Cert.checkValidity();
if (!_sslUtil.certificateIsTrusted((Certificate) x509Cert)) {
_sslUtil.addCertificateToTrustStore(clientAddress,
(Certificate) x509Cert);
log.trace(methodName + "new certificate added as trusted");
_sslUtil.refreshTrustStore();
invalidateSession();
} else {
log.trace(methodName + "certificate was already trusted");
}
log.trace(methodName + "vpInfo: defaultNameSpace["
+ _vpInfo.getDefaultNamespace() + "] name["
+ _vpInfo.getName() + "] sessionId["
+ _vpInfo.getSessionId() + " vasaApiVersion["
+ _vpInfo.getVasaApiVersion() + "] vasaProviderVersion["
+ _vpInfo.getVasaProviderVersion() + "]");
log.debug(methodName + "Exit returning vpInfo");
return _vpInfo;
} catch (InvalidSession is) {
// thrown by sslUtil.checkHttpRequest()
log.error(methodName + "Session is invalid", is);
throw is;
} catch (InvalidCertificate ic) {
// thrown by sslUtil.buildCertificate()
log.error(methodName + "Certificate is invalid", ic);
throw ic;
} catch (CertificateExpiredException e) {
// thrown by x509Cert.checkValidity()
log.error(methodName + "Certificate is expired", e);
throw FaultUtil.InvalidCertificate(e);
} catch (CertificateNotYetValidException e) {
// thrown by x509Cert.checkValidity()
log.error(methodName + "Certificate is not in validity period ", e);
throw FaultUtil.InvalidCertificate(e);
} catch (InvalidLogin il) {
// thrown by verifyPassword();
log.error(methodName + "Invalid login", il);
throw il;
} catch (Exception e) {
log.error(methodName + "registration failed: ", e);
throw FaultUtil.StorageFault(methodName + "registration failed: ",
e);
}
}
/**
* vasaService interface
*/
@Override
public VasaProviderInfo setContext(UsageContext uc) throws InvalidArgument,
InvalidSession, StorageFault {
final String methodName = "setContext(): ";
log.debug(methodName + "Entry");
if (log.isDebugEnabled()) {
HostInitiatorInfo[] hostInitiators = uc.getHostInitiator();
if (hostInitiators != null && hostInitiators.length > 0) {
log.debug(methodName + "list of host initiators (HBA):");
for (HostInitiatorInfo initiator : hostInitiators) {
log.debug(methodName + "id["
+ initiator.getUniqueIdentifier() + "] iSCSIId["
+ initiator.getIscsiIdentifier() + "] nodeWWN["
+ initiator.getNodeWwn() + "] portWWN["
+ initiator.getPortWwn() + "]");
}
}
String[] iscsiIpAddresseses = uc.getIscsiInitiatorIpAddress();
if (iscsiIpAddresseses != null) {
log.debug(methodName + "list of iSCSI initiator IP addresses:");
log.debug(methodName + Arrays.asList(iscsiIpAddresseses));
}
MountInfo[] mountPoints = uc.getMountPoint();
if (mountPoints != null && mountPoints.length > 0) {
log.debug(methodName + "List of file share mount paths:");
for (MountInfo mountPoint : mountPoints) {
log.debug(methodName + "filePath["
+ mountPoint.getFilePath() + "] serverName["
+ mountPoint.getServerName() + "]");
}
}
log.debug(methodName + "vSphere service instance Id["
+ uc.getVcGuid() + "]");
}
String clientAddress = "unknown";
try {
// _sslUtil.checkForUniqueVASASessionId();
SessionContext sc = null;
clientAddress = _sslUtil.checkHttpRequest(true, false);
// Tear down any existing session
log.debug(methodName + "Tear down any existing session");
invalidateSession();
// Create new session.
sc = SessionContext.createSession(uc, clientAddress);
log.trace(methodName + "Created a new session: "
+ sc.getSessionId());
sc.setSosManager(_sosManager);
log.trace(methodName + "SOS manager is set in session context: "
+ sc.getSessionId());
_vpInfo.setSessionId(sc.getSessionId());
_sslUtil.setHttpResponse(sc);
log.debug(methodName
+ "Exit returning VasaProviderInfo with new session Id["
+ _vpInfo.getSessionId() + "]");
return _vpInfo;
} catch (StorageFault sf) {
log.error(methodName + " unknown exception occured", sf);
throw sf;
} catch (InvalidSession is) {
log.error(methodName + "Invalid session exception", is);
throw is;
} catch (Exception e) {
log.error("unknown exception occured", e);
throw FaultUtil.StorageFault("runtime ", e);
}
}
/**
* remove the current Session context and VASA_SESSION_ID cookie
*/
private void invalidateSession() throws InvalidSession {
final String methodName = "invalidateSession(): ";
log.trace(methodName + "Entry");
try {
String sessionId = _sslUtil.getCookie(SSLUtil.VASA_SESSIONID_STR);
log.debug(methodName + "Got existing session ID from cookie["
+ sessionId + "]");
if (sessionId != null) {
SessionContext.removeSession(sessionId);
}
log.trace(methodName + "Removed session ID [" + sessionId
+ "] from session ID list");
_sslUtil.setCookie(SSLUtil.VASA_SESSIONID_STR,
SessionContext.INVALID_SESSION_ID);
log.trace(methodName + "Set value of current session ID as invalid");
_vpInfo.setSessionId(SessionContext.INVALID_SESSION_ID);
} catch (Exception e) {
log.error(methodName + "Could not find session context", e);
throw FaultUtil.InvalidSession("Could not find session context "
+ e);
}
log.trace(methodName + "Exit");
}
/**
* internal routine to perform Certificate unregister operation
*
* @throws InvalidSession
*/
private void unregisterCertificate(X509Certificate x509Cert)
throws InvalidCertificate, StorageFault, InvalidSession {
final String methodName = "unregisterCertificate(): ";
log.trace(methodName + "Entry with input x509Cert["
+ (x509Cert != null ? "***" : null) + "]");
try {
if (_sslUtil.certificateIsTrusted(x509Cert)) {
log.debug(methodName + "certificate removed from trusted");
_sslUtil.removeCertificateFromTrustStore((Certificate) x509Cert);
_sslUtil.refreshTrustStore();
if (_sslUtil.certificateIsTrusted(x509Cert)) {
throw FaultUtil
.StorageFault("Certificate could not be removed from the trustStore.");
}
invalidateSession();
} else {
throw FaultUtil.InvalidCertificate("Certificate not registered.");
}
} catch (InvalidCertificate ic) {
log.error(methodName + "Invalid certificate exception", ic);
throw ic;
} catch (InvalidSession is) {
log.error(methodName + "Invalid session exception", is);
throw is;
} catch (Exception e) {
log.error(methodName + "unknown exception occured", e);
throw FaultUtil.StorageFault("runtime ", e);
}
log.trace(methodName + "Exit");
}
/**
* vasaService interface
*/
@Override
public void unregisterVASACertificate(String existingCertificate)
throws InvalidCertificate, InvalidSession, StorageFault {
final String methodName = "unregisterVASACertificate(): ";
log.debug(methodName + "Entry with input existingCertificate["
+ (existingCertificate != null ? "***" : null) + "]");
try {
/*
* Need to have a valid SSL session, but VASA session not required
*/
_sslUtil.checkHttpRequest(true, true);
X509Certificate x509Cert = (X509Certificate) _sslUtil
.buildCertificate(existingCertificate);
SimpleTimeCounter counter = new SimpleTimeCounter(
"unregisterVASACertificate");
unregisterCertificate(x509Cert);
counter.stop();
} catch (InvalidSession is) {
// thrown by unregisterCertificate()
log.error(methodName + "invalid session", is);
throw is;
} catch (InvalidCertificate ic) {
// thrown by sslUtil.buildCertificate()
// thrown by unregisterCertificate()
log.error(methodName + "invalid certificate", ic);
throw ic;
} catch (StorageFault sf) {
log.error(methodName + "storage fault occured ", sf);
throw sf;
} catch (Exception e) {
log.error(methodName + "unknown exception", e);
throw FaultUtil.StorageFault("runtime ", e);
}
log.debug(methodName + "Exit");
}
/**
* Craft the VASAProvider info
*/
public VasaProviderInfo initializeVasaProviderInfo() {
final String methodName = "initializeVasaProviderInfo(): ";
log.debug(methodName + "Entry");
VendorModel[] vms = new VendorModel[1];
vms[0] = new VendorModel();
vms[0].setVendorId(Constants.VASA_BOURNE_PROVIDER_VENDOR_NAME);
vms[0].setModelId(Constants.VASA_BOURNE_PROVIDER_VENDOR_MODEL);
_vpInfo = new VasaProviderInfo();
_vpInfo.setVasaApiVersion(FaultUtil.getVasaApiVersion());
log.debug(methodName + "vasa api version: "
+ _vpInfo.getVasaApiVersion());
_vpInfo.setName(Constants.VASA_BOURNE_PROVIDER_NAME);
log.debug(methodName + "vasa provider name: " + _vpInfo.getName());
_vpInfo.setVasaProviderVersion(Constants.VASA_BOURNE_PROVIDER_VERSION);
log.debug(methodName + "vasa provider version: "
+ _vpInfo.getVasaProviderVersion());
_vpInfo.setDefaultSessionTimeoutInSeconds(SessionContext.DEFAULT_SESSION_TIMEOUT);
log.debug(methodName + "default session time out (in seconds): "
+ _vpInfo.getDefaultSessionTimeoutInSeconds());
String namespace = Constants.VASA_BOURNE_PROVIDER_NAMESPACE;
_vpInfo.setDefaultNamespace(namespace);
log.debug(methodName + "defaultNameSpace: "
+ _vpInfo.getDefaultNamespace());
_vpInfo.setSupportedVendorModel(vms);
log.debug(methodName + "Exit");
return _vpInfo;
}
/**
* called by vasaService APIs to verify the connection and get the
* UsageContext.
*/
public UsageContext getUsageContext() throws InvalidSession, StorageFault {
final String methodName = "getUsageContext(): ";
log.debug(methodName + "Entry");
try {
// verify valid SSL and VASA Sessions.
String sessionId = _sslUtil.getCookie(SSLUtil.VASA_SESSIONID_STR);
if (sessionId == null) {
// this should "never happen" if checkHttpRequest does not
// throw an exception
throw FaultUtil.StorageFault("getUsageContext internal error.");
}
log.trace(methodName
+ "Looking for sessin context by session id...");
SessionContext sc = SessionContext
.lookupSessionContextBySessionId(sessionId);
if (sc == null) {
// this should "never happen" if checkHttpRequest does not
// throw an exception
throw FaultUtil.StorageFault("getUsageContext internal error.");
}
_sslUtil.setHttpResponse(sc);
UsageContext uc = sc.getUsageContext();
if (uc == null) {
throw FaultUtil.StorageFault("UsageContext is not set");
}
// validateUsageContext(uc);
return uc;
} catch (StorageFault sf) {
// thrown by this function
log.error(methodName + "storage fault occured", sf);
throw sf;
} catch (InvalidSession is) {
log.error(methodName + "invalid session", is);
throw is;
} catch (Exception e) {
log.error(methodName + "unexpected error", e);
throw FaultUtil.InvalidSession("runtime", e);
}
}
public SOSManager getSOSManager() throws StorageFault, InvalidSession {
final String methodName = "getSOSManager(): ";
log.debug(methodName + "Entry");
try {
// verify valid SSL and VASA Sessions.
String sessionId = _sslUtil.getCookie(SSLUtil.VASA_SESSIONID_STR);
if (sessionId == null) {
// this should "never happen" if checkHttpRequest does not
// throw an exception
throw FaultUtil.StorageFault(methodName + "internal error");
}
log.trace(methodName
+ "Looking for session context by session id...");
SessionContext sc = SessionContext
.lookupSessionContextBySessionId(sessionId);
if (sc == null) {
// this should "never happen" if checkHttpRequest does not
// throw an exception
throw FaultUtil.StorageFault("getUsageContext internal error.");
}
_sslUtil.setHttpResponse(sc);
SOSManager sosManager = sc.getSosManager();
if (sosManager == null) {
throw FaultUtil.StorageFault("SOSManager is not set");
}
// validateUsageContext(uc);
return sosManager;
} catch (StorageFault sf) {
// thrown by this function
log.error(methodName + "storage fault occured", sf);
throw sf;
} catch (InvalidSession is) {
log.error(methodName + "invalid session", is);
throw is;
} catch (Exception e) {
log.error(methodName + "unexpected error", e);
throw FaultUtil.InvalidSession("runtime", e);
}
}
}