diff options
25 files changed, 2918 insertions, 0 deletions
diff --git a/dblib/lighty/pom.xml b/dblib/lighty/pom.xml new file mode 100755 index 00000000..e1ba85f9 --- /dev/null +++ b/dblib/lighty/pom.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>dblib-lighty</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>ccsdk-sli-core :: dblib :: ${project.artifactId}</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-lighty-dependency-versions</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.lighty.core</groupId> + <artifactId>lighty-controller</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>dblib-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa</groupId> + <artifactId>aaa-encrypt-service</artifactId> + </dependency> + </dependencies> +</project> diff --git a/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProviderLighty.java b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProviderLighty.java new file mode 100644 index 00000000..82579d7e --- /dev/null +++ b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProviderLighty.java @@ -0,0 +1,174 @@ +/*- + * ============LICENSE_START======================================================= + * onap + * ================================================================================ + * Copyright (C) 2016 - 2017 ONAP + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.dblib; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Optional; +import java.util.Properties; +import java.util.Vector; +import org.onap.ccsdk.sli.core.utils.KarafRootFileResolver; +import org.onap.ccsdk.sli.core.utils.PropertiesFileResolver; +import org.onap.ccsdk.sli.core.utils.common.CoreDefaultFileResolver; +import org.onap.ccsdk.sli.core.utils.common.SdncConfigEnvVarFileResolver; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link DBLIBResourceProvider} WITH REMOVED OSGi DEPENDENCIES + */ +public class DBLIBResourceProviderLighty { + + private static final Logger LOG = LoggerFactory.getLogger(DBLIBResourceProviderLighty.class); + + /** + * The name of the properties file for database configuration + */ + private static final String DBLIB_PROP_FILE_NAME = "dblib.properties"; + + private static final String DBLIB_PROPERTY_NAME = "org.onap.ccsdk.sli.jdbc.password"; + private final AAAEncryptionService aaaEncryptionService; + + /** + * A prioritized list of strategies for resolving dblib properties files. + */ + private Vector<PropertiesFileResolver> dblibPropertiesFileResolvers = new Vector<>(); + + /** + * The configuration properties for the db connection. + */ + private Properties properties; + + /** + * Set up the prioritized list of strategies for resolving dblib properties files. + */ + public DBLIBResourceProviderLighty(AAAEncryptionService aaaEncryptionService) { + this.aaaEncryptionService = aaaEncryptionService; + + dblibPropertiesFileResolvers.add(new SdncConfigEnvVarFileResolver( + "Using property file (1) from environment variable" + )); + dblibPropertiesFileResolvers.add(new CoreDefaultFileResolver( + "Using property file (2) from default directory" + )); + dblibPropertiesFileResolvers.add(new KarafRootFileResolver( + "Using property file (4) from karaf root", this)); + + // determines properties file as according to the priority described in the class header comment + final File propertiesFile = determinePropertiesFile(this); + if (propertiesFile != null) { + try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) { + properties = new Properties(); + properties.load(fileInputStream); + + if(properties.containsKey(DBLIB_PROPERTY_NAME)) { + String sensitive = properties.getProperty(DBLIB_PROPERTY_NAME); + if(sensitive != null && sensitive.startsWith("ENC:")) { + try { + sensitive = sensitive.substring(4); + String postsense = decrypt(sensitive); + properties.setProperty(DBLIB_PROPERTY_NAME, postsense); + } catch(Exception exc) { + LOG.error("Failed to translate property", exc); + } + } + } + + } catch (final IOException e) { + LOG.error("Failed to load properties for file: {}", propertiesFile.toString(), + new DblibConfigurationException("Failed to load properties for file: " + + propertiesFile.toString(), e)); + } + } + } + + /** + * + * @param value + * @return decrypted string if successful or the original value if unsuccessful + */ + private String decrypt(String value) { + return aaaEncryptionService.decrypt(value); + } + + /** + * Extract db config properties. + * + * @return the db config properties + */ + public Properties getProperties() { + return properties; + } + + /** + * Reports the method chosen for properties resolution to the <code>Logger</code>. + * + * @param message Some user friendly message + * @param fileOptional The file location of the chosen properties file + * @return the file location of the chosen properties file + */ + private static File reportSuccess(final String message, final Optional<File> fileOptional) { + if(fileOptional.isPresent()) { + final File file = fileOptional.get(); + LOG.info("{} {}", message, file.getPath()); + return file; + } + return null; + } + + /** + * Reports fatal errors. This is the case in which no properties file could be found. + * + * @param message An appropriate fatal error message + * @param dblibConfigurationException An exception describing what went wrong during resolution + */ + private static void reportFailure(final String message, + final DblibConfigurationException dblibConfigurationException) { + + LOG.error("{}", message, dblibConfigurationException); + } + + /** + * Determines the dblib properties file to use based on the following priority: + * <ol> + * <li>A directory identified by the system environment variable <code>SDNC_CONFIG_DIR</code></li> + * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li> + * <li>A directory identified by the JRE argument <code>dblib.properties</code></li> + * <li>A <code>dblib.properties</code> file located in the karaf root directory</li> + * </ol> + */ + File determinePropertiesFile(final DBLIBResourceProviderLighty dblibResourceProvider) { + + for (final PropertiesFileResolver dblibPropertiesFileResolver : dblibPropertiesFileResolvers) { + final Optional<File> fileOptional = dblibPropertiesFileResolver.resolveFile(DBLIB_PROP_FILE_NAME); + if (fileOptional.isPresent()) { + return reportSuccess(dblibPropertiesFileResolver.getSuccessfulResolutionMessage(), fileOptional); + } + } + + reportFailure("Missing configuration properties resource(3)", + new DblibConfigurationException("Missing configuration properties resource(3): " + + DBLIB_PROP_FILE_NAME)); + return null; + } +} diff --git a/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManagerLighty.java b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManagerLighty.java new file mode 100644 index 00000000..9b474a5e --- /dev/null +++ b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManagerLighty.java @@ -0,0 +1,977 @@ +/*- + * ============LICENSE_START======================================================= + * onap + * ================================================================================ + * Copyright (C) 2016 - 2017 ONAP + * ================================================================================ + * Modifications Copyright (C) 2018 IBM. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.dblib; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.SQLDataException; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLIntegrityConstraintViolationException; +import java.sql.SQLNonTransientConnectionException; +import java.sql.SQLSyntaxErrorException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Observable; +import java.util.Properties; +import java.util.Set; +import java.util.SortedSet; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.stream.Collectors; +import javax.sql.DataSource; +import javax.sql.rowset.CachedRowSet; +import org.apache.tomcat.jdbc.pool.PoolExhaustedException; +import org.onap.ccsdk.sli.core.dblib.config.DbConfigPool; +import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration; +import org.onap.ccsdk.sli.core.dblib.config.TerminatingConfiguration; +import org.onap.ccsdk.sli.core.dblib.factory.DBConfigFactory; +import org.onap.ccsdk.sli.core.dblib.pm.PollingWorker; +import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link DBResourceManager} WITH REMOVED OSGi DEPENDENCIES + */ +public class DBResourceManagerLighty implements DataSource, DataAccessor, DBResourceObserver, DbLibService { + private static final Logger LOGGER = LoggerFactory.getLogger(DBResourceManagerLighty.class); + private static final String DATABASE_URL = "org.onap.ccsdk.sli.jdbc.url"; + + transient boolean terminating = false; + transient protected long retryInterval = 10000L; + transient boolean recoveryMode = true; + + SortedSet<CachedDataSource> dsQueue = new ConcurrentSkipListSet<>(new DataSourceComparator()); + protected final Set<CachedDataSource> broken = Collections.synchronizedSet(new HashSet<CachedDataSource>()); + protected final Object monitor = new Object(); + protected final Properties configProps; + protected final Thread worker; + + protected final long terminationTimeOut; + protected final boolean monitorDbResponse; + protected final long monitoringInterval; + protected final long monitoringInitialDelay; + protected final long expectedCompletionTime; + protected final long unprocessedFailoverThreshold; + private static final String LOGGER_ALARM_MSG="Generated alarm: DBResourceManager.getData - No active DB connection pools are available."; + private static final String EXCEPTION_MSG= "No active DB connection pools are available in RequestDataNoRecovery call."; + + public DBResourceManagerLighty(final DBLIBResourceProviderLighty configuration) { + this(configuration.getProperties()); + } + + public DBResourceManagerLighty(final Properties properties) { + this.configProps = processSystemVariables(properties); + + // TODO : hack to force classloader to cache mariadb driver. This shouldnt be necessary, + // but for some reason it is (without this, dblib throws ClassNotFound on mariadb driver + // and fails to load). + LOGGER.info("Creating dummy instance of org.mariadb.jdbc.Driver"); + Driver dvr = new org.mariadb.jdbc.Driver(); + dvr = null; + + // get retry interval value + retryInterval = getLongFromProperties(configProps, "org.onap.dblib.connection.retry", 10000L); + + // get recovery mode flag + recoveryMode = getBooleanFromProperties(configProps, "org.onap.dblib.connection.recovery", true); + if(!recoveryMode) + { + recoveryMode = false; + LOGGER.info("Recovery Mode disabled"); + } + // get time out value for thread cleanup + terminationTimeOut = getLongFromProperties(configProps, "org.onap.dblib.termination.timeout", 300000L); + // get properties for monitoring + monitorDbResponse = getBooleanFromProperties(configProps, "org.onap.dblib.connection.monitor", false); + monitoringInterval = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.interval", 1000L); + monitoringInitialDelay = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.startdelay", 5000L); + expectedCompletionTime = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.expectedcompletiontime", 5000L); + unprocessedFailoverThreshold = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.unprocessedfailoverthreshold", 3L); + + // initialize performance monitor + PollingWorker.createInistance(configProps); + + // initialize recovery thread + worker = new RecoveryMgr(); + worker.setName("DBResourcemanagerWatchThread"); + worker.setDaemon(true); + worker.start(); + + try { + this.config(configProps); + } catch (final Exception e) { + // TODO: config throws <code>Exception</code> which is poor practice. Eliminate this in a separate patch. + LOGGER.error("Fatal Exception encountered while configuring DBResourceManager", e); + } + } + + public static Properties processSystemVariables(Properties properties) { + Map<Object, Object> hmap = new Properties(); + hmap.putAll(properties); + + Map<Object, Object> result = hmap.entrySet().stream() + .filter(map -> map.getValue().toString().startsWith("${")) + .filter(map -> map.getValue().toString().endsWith("}")) + .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); + + result.forEach((name, propEntries) -> { + hmap.put(name, replace(propEntries.toString())); + }); + + if(hmap.containsKey(DATABASE_URL) && hmap.get(DATABASE_URL).toString().contains("${")) { + String url = hmap.get(DATABASE_URL).toString(); + String[] innerChunks = url.split("\\$\\{"); + for(String chunk : innerChunks) { + if(chunk.contains("}")) { + String subChunk = chunk.substring(0, chunk.indexOf("}")); + String varValue = System.getenv(subChunk); + url = url.replace("${"+subChunk+"}", varValue); + } + } + hmap.put(DATABASE_URL, url); + } + return Properties.class.cast(hmap); + } + + + private static String replace(String value) { + String globalVariable = value.substring(2, value.length() -1); + String varValue = System.getenv(globalVariable); + return (varValue != null) ? varValue : value; + } + + + private void config(Properties configProps) throws Exception { + final ConcurrentLinkedQueue<CachedDataSource> semaphore = new ConcurrentLinkedQueue<>(); + final DbConfigPool dbConfig = DBConfigFactory.createConfig(configProps); + + long startTime = System.currentTimeMillis(); + + try { + JDBCConfiguration[] config = dbConfig.getJDBCbSourceArray(); + CachedDataSource[] cachedDS = new CachedDataSource[config.length]; + if (cachedDS == null || cachedDS.length == 0) { + LOGGER.error("Initialization of CachedDataSources failed. No instance was created."); + throw new Exception("Failed to initialize DB Library. No data source was created."); + } + + for(int i = 0; i < config.length; i++) { + cachedDS[i] = CachedDataSourceFactory.createDataSource(config[i]); + if(cachedDS[i] == null) + continue; + semaphore.add(cachedDS[i]); + cachedDS[i].setInterval(monitoringInterval); + cachedDS[i].setInitialDelay(monitoringInitialDelay); + cachedDS[i].setExpectedCompletionTime(expectedCompletionTime); + cachedDS[i].setUnprocessedFailoverThreshold(unprocessedFailoverThreshold); + cachedDS[i].addObserver(DBResourceManagerLighty.this); + } + + DataSourceTester[] tester = new DataSourceTester[config.length]; + + for(int i=0; i<tester.length; i++){ + tester[i] = new DataSourceTester(cachedDS[i], DBResourceManagerLighty.this, semaphore); + tester[i].start(); + } + + // the timeout param is set is seconds. + long timeout = ((dbConfig.getTimeout() <= 0) ? 60L : dbConfig.getTimeout()); + LOGGER.debug("Timeout set to {} seconds", timeout); + timeout *= 1000; + + + synchronized (semaphore) { + semaphore.wait(timeout); + } + } catch(Exception exc){ + LOGGER.warn("DBResourceManager.initWorker", exc); + } finally { + startTime = System.currentTimeMillis() - startTime; + LOGGER.info("Completed wait with {} active datasource(s) in {} ms", dsQueue.size(), startTime); + } + } + + + private final class DataSourceComparator implements Comparator<CachedDataSource> { + @Override + public int compare(CachedDataSource left, CachedDataSource right) { + if(LOGGER.isTraceEnabled()) + LOGGER.trace("----------SORTING-------- () : ()", left.getDbConnectionName(), right.getDbConnectionName()); + try { + if(left == right) { + return 0; + } + if(left == null){ + return 1; + } + if(right == null){ + return -1; + } + + boolean leftMaster = !left.isSlave(); + if(leftMaster) { + if(left.getIndex() <= right.getIndex()) + return -1; + else { + boolean rightMaster = !right.isSlave(); + if(rightMaster) { + if(left.getIndex() <= right.getIndex()) + return -1; +// if(left.getIndex() > right.getIndex()) + else { + return 1; + } + } else { + return -1; + } + } + } + if(!right.isSlave()) + return 1; + + if(left.getIndex() <= right.getIndex()) + return -1; + if(left.getIndex() > right.getIndex()) + return 1; + + + } catch (Throwable e) { + LOGGER.warn("", e); + } + return -1; + } + } + + class DataSourceTester extends Thread { + + private final CachedDataSource ds; + private final DBResourceManagerLighty manager; + private final ConcurrentLinkedQueue<CachedDataSource> semaphoreQ; + + public DataSourceTester(CachedDataSource ds, DBResourceManagerLighty manager, ConcurrentLinkedQueue<CachedDataSource> semaphore) { + this.ds = ds; + this.manager = manager; + this.semaphoreQ = semaphore; + } + + @Override + public void run() { + manager.setDataSource(ds); + boolean slave = true; + if(ds != null) { + try { + slave = ds.isSlave(); + } catch (Exception exc) { + LOGGER.warn("", exc); + } + } + if(!slave) { + LOGGER.info("Adding MASTER {} to active queue", ds.getDbConnectionName()); + try { + synchronized (semaphoreQ) { + semaphoreQ.notifyAll(); + } + } catch(Exception exc) { + LOGGER.warn("", exc); + } + } + try { + synchronized (semaphoreQ) { + semaphoreQ.remove(ds); + } + if(semaphoreQ.isEmpty()) { + synchronized (semaphoreQ) { + semaphoreQ.notifyAll(); + } + } + } catch(Exception exc) { + LOGGER.warn("", exc); + } + if(ds != null) + LOGGER.info("Thread DataSourceTester terminated {} for {}", this.getName(), ds.getDbConnectionName()); + } + + } + + + private long getLongFromProperties(Properties props, String property, long defaultValue) + { + String value = null; + long tmpLongValue = defaultValue; + try { + value = props.getProperty(property); + if(value != null) + tmpLongValue = Long.parseLong(value); + + } catch(NumberFormatException exc) { + if(LOGGER.isWarnEnabled()){ + LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a numeric value"); + } + } catch(Exception exc) { + } + return tmpLongValue; + + } + + private boolean getBooleanFromProperties(Properties props, String property, boolean defaultValue) + { + boolean tmpValue = defaultValue; + String value = null; + + try { + value = props.getProperty(property); + if(value != null) + tmpValue = Boolean.parseBoolean(value); + + } catch(NumberFormatException exc) { + if(LOGGER.isWarnEnabled()){ + LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a boolean value"); + } + } catch(Exception exc) { + } + return tmpValue; + + } + + + @Override + public void update(Observable observable, Object data) { + // if observable is active and there is a standby available, switch + if(observable instanceof SQLExecutionMonitor) + { + SQLExecutionMonitor monitor = (SQLExecutionMonitor)observable; + if(monitor.getParent() instanceof CachedDataSource) + { + CachedDataSource dataSource = (CachedDataSource)monitor.getParent(); + if(dataSource == dsQueue.first()) + { + if(recoveryMode && dsQueue.size() > 1){ + handleGetConnectionException(dataSource, new Exception(data.toString())); + } + } + } + } + } + + public void testForceRecovery() + { + CachedDataSource active = this.dsQueue.first(); + handleGetConnectionException(active, new Exception("test")); + } + + class RecoveryMgr extends Thread { + + @Override + public void run() { + while(!terminating) + { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException e1) { } + CachedDataSource brokenSource = null; + try { + if (!broken.isEmpty()) { + CachedDataSource[] sourceArray = broken.toArray(new CachedDataSource[0]); + for (int i = 0; i < sourceArray.length; i++) + { + brokenSource = sourceArray[i]; + if (brokenSource instanceof TerminatingCachedDataSource) + break; + if (resetConnectionPool(brokenSource)) { + broken.remove(brokenSource); + brokenSource.blockImmediateOffLine(); + dsQueue.add(brokenSource); + LOGGER.info("DataSource <" + + brokenSource.getDbConnectionName() + + "> recovered."); + } + brokenSource = null; + } + } + } catch (Exception exc) { + LOGGER.warn(exc.getMessage()); + if(brokenSource != null){ + try { + if(!broken.contains(brokenSource)) + broken.add(brokenSource); + brokenSource = null; + } catch (Exception e1) { } + } + } + } + LOGGER.info("DBResourceManager.RecoveryMgr <"+this.toString() +"> terminated." ); + } + + private boolean resetConnectionPool(CachedDataSource dataSource){ + try { + return dataSource.testConnection(); + } catch (Exception exc) { + LOGGER.info("DataSource <" + dataSource.getDbConnectionName() + "> resetCache failed with error: "+ exc.getMessage()); + return false; + } + } + } + + /* (non-Javadoc) + * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#getData(java.lang.String, java.util.ArrayList, java.lang.String) + */ + @Override + public CachedRowSet getData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException { + ArrayList<Object> newList= new ArrayList<>(); + if(arguments != null && !arguments.isEmpty()) { + newList.addAll(arguments); + } + if(recoveryMode) + return requestDataWithRecovery(statement, newList, preferredDS); + else + return requestDataNoRecovery(statement, newList, preferredDS); + } + + private CachedRowSet requestDataWithRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException { + Throwable lastException = null; + + // test if there are any connection pools available + if(this.dsQueue.isEmpty()){ + LOGGER.error(LOGGER_ALARM_MSG); + throw new DBLibException("No active DB connection pools are available in RequestDataWithRecovery call."); + } + + // loop through available data sources to retrieve data. + for(int i=0; i< 2; i++) + { + CachedDataSource active = this.dsQueue.first(); + + long time = System.currentTimeMillis(); + try { + if(!active.isFabric()) { + if(this.dsQueue.size() > 1 && active.isSlave()) { + CachedDataSource master = findMaster(); + if(master != null) { + active = master; + } + } + } + + return active.getData(statement, arguments); + } catch(SQLDataException | SQLSyntaxErrorException | SQLIntegrityConstraintViolationException exc){ + throw exc; + } catch(Throwable exc){ + if(exc instanceof SQLException) { + SQLException sqlExc = (SQLException)exc; + int code = sqlExc.getErrorCode(); + String state = sqlExc.getSQLState(); + LOGGER.debug("SQLException code: {} state: {}", code, state); + if("07001".equals(sqlExc.getSQLState())) { + throw sqlExc; + } + } + lastException = exc; + LOGGER.error("Generated alarm: {}", active.getDbConnectionName(), exc); + handleGetConnectionException(active, exc); + } finally { + if(LOGGER.isDebugEnabled()){ + time = System.currentTimeMillis() - time; + LOGGER.debug("getData processing time : {} {} miliseconds.", active.getDbConnectionName(), time); + } + } + } + if(lastException instanceof SQLException){ + throw (SQLException)lastException; + } + // repackage the exception + // you are here because either you run out of available data sources + // or the last exception was not of SQLException type. + // repackage the exception + if(lastException == null) { + throw new DBLibException("The operation timed out while waiting to acquire a new connection." ); + } else { + SQLException exception = new DBLibException(lastException.getMessage()); + exception.setStackTrace(lastException.getStackTrace()); + if(lastException.getCause() instanceof SQLException) { + throw (SQLException)lastException.getCause(); + } + throw exception; + } + } + + private CachedRowSet requestDataNoRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException { + if(dsQueue.isEmpty()){ + LOGGER.error(LOGGER_ALARM_MSG); + throw new DBLibException(EXCEPTION_MSG); + } + CachedDataSource active = this.dsQueue.first(); + long time = System.currentTimeMillis(); + try { + if(!active.isFabric()) { + if(this.dsQueue.size() > 1 && active.isSlave()) { + CachedDataSource master = findMaster(); + if(master != null) { + active = master; + } + } + } + return active.getData(statement, arguments); + + } catch(Throwable exc){ + String message = exc.getMessage(); + if(message == null) + message = exc.getClass().getName(); + LOGGER.error("Generated alarm: {} - {}",active.getDbConnectionName(), message); + if(exc instanceof SQLException) + throw (SQLException)exc; + else { + DBLibException excptn = new DBLibException(exc.getMessage()); + excptn.setStackTrace(exc.getStackTrace()); + throw excptn; + } + } finally { + if(LOGGER.isDebugEnabled()){ + time = System.currentTimeMillis() - time; + LOGGER.debug(">> getData : {} {} miliseconds.", active.getDbConnectionName(), time); + } + } + } + + + /* (non-Javadoc) + * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#writeData(java.lang.String, java.util.ArrayList, java.lang.String) + */ + @Override + public boolean writeData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException + { + ArrayList<Object> newList= new ArrayList<>(); + if(arguments != null && !arguments.isEmpty()) { + newList.addAll(arguments); + } + + return writeDataNoRecovery(statement, newList, preferredDS); + } + + synchronized CachedDataSource findMaster() throws SQLException { + final CachedDataSource[] clone = this.dsQueue.toArray(new CachedDataSource[0]); + + for(final CachedDataSource dss : clone) { + if(!dss.isSlave()) { + final CachedDataSource first = this.dsQueue.first(); + if(first != dss) { + if(LOGGER.isDebugEnabled()) + LOGGER.debug("----------REODRERING--------"); + dsQueue.clear(); + if(!dsQueue.addAll(Arrays.asList(clone))) { + LOGGER.error("Failed adding datasources"); + } + } + return dss; + } + } + LOGGER.warn("MASTER not found."); + return null; + } + + + private boolean writeDataNoRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException { + if(dsQueue.isEmpty()){ + LOGGER.error(LOGGER_ALARM_MSG); + throw new DBLibException(EXCEPTION_MSG); + } + + boolean initialRequest = true; + boolean retryAllowed = true; + CachedDataSource active = this.dsQueue.first(); + long time = System.currentTimeMillis(); + while(initialRequest) { + initialRequest = false; + try { + if(!active.isFabric()) { + if(this.dsQueue.size() > 1 && active.isSlave()) { + CachedDataSource master = findMaster(); + if(master != null) { + active = master; + } + } + } + + return active.writeData(statement, arguments); + } catch(Throwable exc){ + String message = exc.getMessage(); + if(message == null) + message = exc.getClass().getName(); + LOGGER.error("Generated alarm: {} - {}", active.getDbConnectionName(), message); + if(exc instanceof SQLException) { + SQLException sqlExc = SQLException.class.cast(exc); + // handle read-only exception + if(sqlExc.getErrorCode() == 1290 && "HY000".equals(sqlExc.getSQLState())) { + LOGGER.warn("retrying due to: {}", sqlExc.getMessage()); + this.findMaster(); + if(retryAllowed){ + retryAllowed = false; + initialRequest = true; + continue; + } + } + throw (SQLException)exc; + } else { + DBLibException excptn = new DBLibException(exc.getMessage()); + excptn.setStackTrace(exc.getStackTrace()); + throw excptn; + } + } finally { + if(LOGGER.isDebugEnabled()){ + time = System.currentTimeMillis() - time; + LOGGER.debug("writeData processing time : {} {} miliseconds.", active.getDbConnectionName(), time); + } + } + } + return true; + } + + public void setDataSource(CachedDataSource dataSource) { + if(this.dsQueue.contains(dataSource)) + return; + if(this.broken.contains(dataSource)) + return; + + if(dataSource.testConnection(true)){ + this.dsQueue.add(dataSource); + } else { + this.broken.add(dataSource); + } + } + + @Override + public Connection getConnection() throws SQLException { + Throwable lastException = null; + CachedDataSource active = null; + + if(dsQueue.isEmpty()){ + throw new DBLibException("No active DB connection pools are available in GetConnection call."); + } + + try { + active = dsQueue.first(); + + if(!active.isFabric()) { + if(this.dsQueue.size() > 1 && active.isSlave()) { + LOGGER.debug("Forcing reorder on: {}", dsQueue.toString()); + CachedDataSource master = findMaster(); + if(master != null) { + active = master; + } + } + } + return new DBLibConnection(active.getConnection(), active); + } catch(javax.sql.rowset.spi.SyncFactoryException exc){ + LOGGER.debug("Free memory (bytes): " + Runtime.getRuntime().freeMemory()); + LOGGER.warn("CLASSPATH issue. Allowing retry", exc); + lastException = exc; + } catch(PoolExhaustedException exc) { + throw new NoAvailableConnectionsException(exc); + } catch(SQLNonTransientConnectionException exc){ + throw new NoAvailableConnectionsException(exc); + } catch(Exception exc){ + lastException = exc; + if(recoveryMode){ + handleGetConnectionException(active, exc); + } else { + if(exc instanceof SQLException) { + throw (SQLException)exc; + } else { + DBLibException excptn = new DBLibException(exc.getMessage()); + excptn.setStackTrace(exc.getStackTrace()); + throw excptn; + } + } + } catch (Throwable trwb) { + DBLibException excptn = new DBLibException(trwb.getMessage()); + excptn.setStackTrace(trwb.getStackTrace()); + throw excptn; + } finally { + if(LOGGER.isDebugEnabled()){ + displayState(); + } + } + + if(lastException instanceof SQLException){ + throw (SQLException)lastException; + } + // repackage the exception + if(lastException == null) { + throw new DBLibException("The operation timed out while waiting to acquire a new connection." ); + } else { + SQLException exception = new DBLibException(lastException.getMessage()); + exception.setStackTrace(lastException.getStackTrace()); + if(lastException.getCause() instanceof SQLException) { + + throw (SQLException)lastException.getCause(); + } + throw exception; + } + } + + @Override + public Connection getConnection(String username, String password) + throws SQLException { + CachedDataSource active = null; + + if(dsQueue.isEmpty()){ + throw new DBLibException("No active DB connection pools are available in GetConnection call."); + } + + + try { + active = dsQueue.first(); + if(!active.isFabric()) { + if(this.dsQueue.size() > 1 && active.isSlave()) { + CachedDataSource master = findMaster(); + if(master != null) { + active = master; + } + } + } + return active.getConnection(username, password); + } catch(Throwable exc){ + if(recoveryMode){ + handleGetConnectionException(active, exc); + } else { + if(exc instanceof SQLException) + throw (SQLException)exc; + else { + DBLibException excptn = new DBLibException(exc.getMessage()); + excptn.setStackTrace(exc.getStackTrace()); + throw excptn; + } + } + + } + + throw new DBLibException("No connections available in DBResourceManager in GetConnection call."); + } + + private void handleGetConnectionException(final CachedDataSource source, Throwable exc) { + try { + if(!source.canTakeOffLine()) + { + LOGGER.error("Could not switch due to blocking"); + return; + } + + boolean removed = dsQueue.remove(source); + if(!broken.contains(source)) + { + if(broken.add(source)) + { + LOGGER.warn("DB Recovery: DataSource <" + source.getDbConnectionName() + "> put in the recovery mode. Reason : " + exc.getMessage()); + } else { + LOGGER.warn("Error putting DataSource <" +source.getDbConnectionName()+ "> in recovery mode."); + } + } else { + LOGGER.info("DB Recovery: DataSource <" + source.getDbConnectionName() + "> already in recovery queue"); + } + if(removed) + { + if(!dsQueue.isEmpty()) + { + LOGGER.warn("DB DataSource <" + dsQueue.first().getDbConnectionName() + "> became active"); + } + } + } catch (Exception e) { + LOGGER.error("", e); + } + } + + public void cleanUp() { + for(Iterator<CachedDataSource> it=dsQueue.iterator();it.hasNext();){ + CachedDataSource cds = it.next(); + it.remove(); + cds.cleanUp(); + } + + try { + this.terminating = true; + if(broken != null) + { + try { + broken.add( new TerminatingCachedDataSource(new TerminatingConfiguration())); + } catch(Exception exc){ + LOGGER.error("Waiting for Worker to stop", exc); + } + } + worker.join(terminationTimeOut); + LOGGER.info("DBResourceManager.RecoveryMgr <"+worker.toString() +"> termination was successful: " + worker.getState()); + } catch(Exception exc){ + LOGGER.error("Waiting for Worker thread to terminate ", exc); + } + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return this.dsQueue.first().getLogWriter(); + } + + @Override + public int getLoginTimeout() throws SQLException { + return this.dsQueue.first().getLoginTimeout(); + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + this.dsQueue.first().setLogWriter(out); + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + this.dsQueue.first().setLoginTimeout(seconds); + } + + public void displayState(){ + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("POOLS : Active = "+dsQueue.size() + ";\t Broken = "+broken.size()); + CachedDataSource current = dsQueue.first(); + if(current != null) { + LOGGER.debug("POOL : Active name = \'"+current.getDbConnectionName()+ "\'"); + } + } + } + + /* (non-Javadoc) + * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#isActive() + */ + @Override + public boolean isActive() { + return this.dsQueue.size()>0; + } + + public String getActiveStatus(){ + return "Connected: " + dsQueue.size()+"\tIn-recovery: "+broken.size(); + } + + public String getDBStatus(boolean htmlFormat) { + StringBuilder buffer = new StringBuilder(); + + ArrayList<CachedDataSource> list = new ArrayList<>(); + list.addAll(dsQueue); + list.addAll(broken); + if (htmlFormat) + { + buffer.append("<tr class=\"headerRow\"><th id=\"header1\">") + .append("Name:").append("</th>"); + for (int i = 0; i < list.size(); i++) { + buffer.append("<th id=\"header").append(2 + i).append("\">"); + buffer.append(list.get(i).getDbConnectionName()).append("</th>"); + } + buffer.append("</tr>"); + + buffer.append("<tr><td>State:</td>"); + for (int i = 0; i < list.size(); i++) { + if (broken.contains(list.get(i))) { + buffer.append("<td>in recovery</td>"); + } + if (dsQueue.contains(list.get(i))) { + if (dsQueue.first() == list.get(i)) + buffer.append("<td>active</td>"); + else + buffer.append("<td>standby</td>"); + } + } + buffer.append("</tr>"); + + } else { + for (int i = 0; i < list.size(); i++) { + buffer.append("Name: ").append(list.get(i).getDbConnectionName()); + buffer.append("\tState: "); + if (broken.contains(list.get(i))) { + buffer.append("in recovery"); + } else + if (dsQueue.contains(list.get(i))) { + if (dsQueue.first() == list.get(i)) + buffer.append("active"); + else + buffer.append("standby"); + } + + buffer.append("\n"); + + } + } + return buffer.toString(); + } + + @Override + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return false; + } + + @Override + public <T> T unwrap(Class<T> iface) throws SQLException { + return null; + } + + /** + * @return the monitorDbResponse + */ + @Override + public final boolean isMonitorDbResponse() { + return recoveryMode && monitorDbResponse; + } + + public void test(){ + CachedDataSource obj = dsQueue.first(); + Exception ption = new Exception(); + try { + for(int i=0; i<5; i++) + { + handleGetConnectionException(obj, ption); + } + } catch(Throwable exc){ + LOGGER.warn("", exc); + } + } + + @Override + public java.util.logging.Logger getParentLogger() + throws SQLFeatureNotSupportedException { + return null; + } + + class RemindTask extends TimerTask { + @Override + public void run() { + CachedDataSource ds = dsQueue.first(); + if(ds != null) + ds.getPoolInfo(false); + } + } + + public int poolSize() { + return dsQueue.size(); + } +} diff --git a/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/lighty/DblibModule.java b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/lighty/DblibModule.java new file mode 100644 index 00000000..bce166df --- /dev/null +++ b/dblib/lighty/src/main/java/org/onap/ccsdk/sli/core/dblib/lighty/DblibModule.java @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ + +package org.onap.ccsdk.sli.core.dblib.lighty; + +import io.lighty.core.controller.api.AbstractLightyModule; +import org.onap.ccsdk.sli.core.dblib.DBLIBResourceProviderLighty; +import org.onap.ccsdk.sli.core.dblib.DBResourceManagerLighty; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; + +/** + * The implementation of the {@link io.lighty.core.controller.api.LightyModule} that manages and provides services from + * the dblib artifact. + */ +public class DblibModule extends AbstractLightyModule { + + private final AAAEncryptionService aaaEncryptionService; + + private DBLIBResourceProviderLighty dbLibResourceProvider; + private DBResourceManagerLighty dbResourceManager; + + public DblibModule(AAAEncryptionService aaaEncryptionService) { + this.aaaEncryptionService = aaaEncryptionService; + } + + @Override + protected boolean initProcedure() { + this.dbLibResourceProvider = new DBLIBResourceProviderLighty(aaaEncryptionService); + this.dbResourceManager = new DBResourceManagerLighty(this.dbLibResourceProvider); + return true; + } + + @Override + protected boolean stopProcedure() { + return true; + } + + public DbLibService getDbLibService() { + return dbResourceManager; + } +} diff --git a/dblib/pom.xml b/dblib/pom.xml index 258a4307..d5a986e6 100755 --- a/dblib/pom.xml +++ b/dblib/pom.xml @@ -24,5 +24,6 @@ <module>provider</module> <module>features</module> <module>installer</module> + <module>lighty</module> </modules> </project> diff --git a/lighty/ccsdk-core-lighty/pom.xml b/lighty/ccsdk-core-lighty/pom.xml new file mode 100755 index 00000000..24c03cc2 --- /dev/null +++ b/lighty/ccsdk-core-lighty/pom.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-core-lighty</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>dblib-lighty</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-lighty</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliapi-lighty</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliPluginUtil-lighty</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/CcsdkCoreLightyModule.java b/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/CcsdkCoreLightyModule.java new file mode 100644 index 00000000..578d1824 --- /dev/null +++ b/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/CcsdkCoreLightyModule.java @@ -0,0 +1,148 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ + +package org.onap.ccsdk.sli.core.lighty; + +import io.lighty.core.controller.api.AbstractLightyModule; +import org.onap.ccsdk.sli.core.dblib.lighty.DblibModule; +import org.onap.ccsdk.sli.core.lighty.common.CcsdkLightyUtils; +import org.onap.ccsdk.sli.core.sli.SvcLogicAdaptor; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.core.sli.SvcLogicRecorder; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource; +import org.onap.ccsdk.sli.core.sli.lighty.SliModule; +import org.onap.ccsdk.sli.core.sliapi.lighty.SliApiModule; +import org.onap.ccsdk.sli.core.slipluginutils.lighty.SliPluginUtilsModule; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The implementation of the {@link io.lighty.core.controller.api.LightyModule} that groups all other LightyModules + * from the ccsdk-sli-core repository so they can be all treated as one component (for example started/stopped at once). + * For more information about the lighty.io visit the website https://lighty.io. + */ +public class CcsdkCoreLightyModule extends AbstractLightyModule { + + private static final Logger LOG = LoggerFactory.getLogger(CcsdkCoreLightyModule.class); + + private final DataBroker dataBroker; + private final NotificationPublishService notificationPublishService; + private final RpcProviderRegistry rpcProviderRegistry; + private final AAAEncryptionService aaaEncryptionService; + private final SvcLogicResource svcLogicResource; + private final SvcLogicRecorder svcLogicRecorder; + private final SvcLogicJavaPlugin svcLogicJavaPlugin; + private final SvcLogicAdaptor svcLogicAdaptor; + + private DblibModule dblibModule; + private SliModule sliModule; + private SliApiModule sliApiModule; + private SliPluginUtilsModule sliPluginUtilsModule; + + // FIXME cyclic dependency - implementation of SvcLogicResource is located in adaptors and CcsdkAdaptorsLightyModule + // is depencent on DbLibService from core + public CcsdkCoreLightyModule(DataBroker dataBroker, NotificationPublishService notificationPublishService, + RpcProviderRegistry rpcProviderRegistry, AAAEncryptionService aaaEncryptionService, + SvcLogicResource svcLogicResource, SvcLogicRecorder svcLogicRecorder, SvcLogicJavaPlugin svcLogicJavaPlugin, + SvcLogicAdaptor svcLogicAdaptor) { + this.dataBroker = dataBroker; + this.notificationPublishService = notificationPublishService; + this.rpcProviderRegistry = rpcProviderRegistry; + this.aaaEncryptionService = aaaEncryptionService; + this.svcLogicResource = svcLogicResource; + this.svcLogicRecorder = svcLogicRecorder; + this.svcLogicJavaPlugin = svcLogicJavaPlugin; + this.svcLogicAdaptor = svcLogicAdaptor; + } + + protected boolean initProcedure() { + LOG.debug("Initializing CCSDK Core Lighty module..."); + + this.dblibModule = new DblibModule(aaaEncryptionService); + if (!CcsdkLightyUtils.startLightyModule(dblibModule)) { + return false; + } + + this.sliModule = new SliModule(dblibModule.getDbLibService(), svcLogicResource, svcLogicRecorder, + svcLogicJavaPlugin, svcLogicAdaptor); + if (!CcsdkLightyUtils.startLightyModule(sliModule)) { + return false; + } + + this.sliApiModule = new SliApiModule(dataBroker, notificationPublishService, rpcProviderRegistry, sliModule.getSvcLogicServiceImpl()); + if (!CcsdkLightyUtils.startLightyModule(sliApiModule)) { + return false; + } + + this.sliPluginUtilsModule = new SliPluginUtilsModule(); + if (!CcsdkLightyUtils.startLightyModule(sliPluginUtilsModule)) { + return false; + } + + LOG.debug("CCSDK Core Lighty module was initialized successfully"); + return true; + } + + protected boolean stopProcedure() { + LOG.debug("Stopping CCSDK Core Lighty module..."); + + boolean stopSuccessful = true; + + if (!CcsdkLightyUtils.stopLightyModule(sliPluginUtilsModule)) { + stopSuccessful = false; + } + + if (!CcsdkLightyUtils.stopLightyModule(sliApiModule)) { + stopSuccessful = false; + } + + if (!CcsdkLightyUtils.stopLightyModule(sliModule)) { + stopSuccessful = false; + } + + if (!CcsdkLightyUtils.stopLightyModule(dblibModule)) { + stopSuccessful = false; + } + + if (stopSuccessful) { + LOG.debug("CCSDK Core Lighty module was stopped successfully"); + } else { + LOG.error("CCSDK Core Lighty module was not stopped successfully!"); + } + return stopSuccessful; + } + + public DblibModule getDblibModule() { + return dblibModule; + } + + public SliModule getSliModule() { + return sliModule; + } + + public SliApiModule getSliApiModule() { + return sliApiModule; + } + + public SliPluginUtilsModule getSliPluginUtilsModule() { + return sliPluginUtilsModule; + } +} diff --git a/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/common/CcsdkLightyUtils.java b/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/common/CcsdkLightyUtils.java new file mode 100644 index 00000000..bfbcb135 --- /dev/null +++ b/lighty/ccsdk-core-lighty/src/main/java/org/onap/ccsdk/sli/core/lighty/common/CcsdkLightyUtils.java @@ -0,0 +1,78 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ + +package org.onap.ccsdk.sli.core.lighty.common; + +import io.lighty.core.controller.api.LightyModule; +import java.util.concurrent.ExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utils class containing methods to start/stop LightyModules easier. + */ +public class CcsdkLightyUtils { + + private static final Logger LOG = LoggerFactory.getLogger(CcsdkLightyUtils.class); + + private CcsdkLightyUtils() { + throw new IllegalStateException("This class should not be instantiated!"); + } + + /** + * Starts provided LightyModule + * @param lightyModule LightyModule to start + * @return true if start was successful; false otherwise + */ + public static boolean startLightyModule(LightyModule lightyModule) { + LOG.debug("Starting Lighty module: {} ...", lightyModule.getClass()); + try { + if (lightyModule.start().get()) { + LOG.debug("Lighty module: {} was started successfully", lightyModule.getClass()); + return true; + } else { + LOG.error("Unable to start Lighty Module: {}!", lightyModule.getClass()); + return false; + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Exception thrown while initializing Lighty Module: {}!", lightyModule.getClass(), e); + return false; + } + } + + /** + * Stops provided LightyModule + * @param lightyModule LightyModule to stop + * @return true if stop was successful; false otherwise + */ + public static boolean stopLightyModule(LightyModule lightyModule) { + LOG.debug("Stopping Lighty Module: {}...", lightyModule.getClass()); + try { + if (lightyModule.shutdown().get()) { + LOG.debug("Lighty Module: {} was stopped successfully", lightyModule.getClass()); + return true; + } else { + LOG.error("Unable to stop Lighty Module: {}!", lightyModule.getClass()); + return false; + } + } catch (Exception e) { + LOG.error("Exception thrown while shutting down {} in CCSDK Core Lighty module!", lightyModule.getClass(), + e); + return false; + } + } +} diff --git a/lighty/ccsdk-lighty-dependency-versions/pom.xml b/lighty/ccsdk-lighty-dependency-versions/pom.xml new file mode 100755 index 00000000..e30a7e61 --- /dev/null +++ b/lighty/ccsdk-lighty-dependency-versions/pom.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-lighty-dependency-versions</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>pom</packaging> + + <properties> + <lighty.version>10.1.0</lighty.version> + <odl.aaa.version>0.9.0</odl.aaa.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>io.lighty.core</groupId> + <artifactId>lighty-controller</artifactId> + <version>${lighty.version}</version> + </dependency> + <dependency> + <groupId>io.lighty.modules</groupId> + <artifactId>lighty-restconf-nb-community</artifactId> + <version>${lighty.version}</version> + </dependency> + <dependency> + <groupId>io.lighty.modules</groupId> + <artifactId>lighty-netconf-sb</artifactId> + <version>${lighty.version}</version> + </dependency> + <dependency> + <groupId>io.lighty.resources</groupId> + <artifactId>singlenode-configuration</artifactId> + <version>${lighty.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa</groupId> + <artifactId>aaa-artifacts</artifactId> + <version>${odl.aaa.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa</groupId> + <artifactId>aaa-encrypt-service</artifactId> + <version>${odl.aaa.version}</version> + </dependency> + </dependencies> + </dependencyManagement> + +</project> diff --git a/lighty/pom.xml b/lighty/pom.xml new file mode 100644 index 00000000..cd94259c --- /dev/null +++ b/lighty/pom.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-core-lighty-aggregator</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>pom</packaging> + + <modules> + <module>ccsdk-core-lighty</module> + <module>ccsdk-lighty-dependency-versions</module> + </modules> +</project> @@ -34,6 +34,7 @@ <module>sliapi</module> <module>features</module> <module>artifacts</module> + <module>lighty</module> </modules> <scm> diff --git a/sli/lighty/pom.xml b/sli/lighty/pom.xml new file mode 100755 index 00000000..c38ad286 --- /dev/null +++ b/sli/lighty/pom.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-lighty</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>ccsdk-sli-core :: sli :: ${project.artifactId}</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-lighty-dependency-versions</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.lighty.core</groupId> + <artifactId>lighty-controller</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/lighty/SliModule.java b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/lighty/SliModule.java new file mode 100644 index 00000000..d4846932 --- /dev/null +++ b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/lighty/SliModule.java @@ -0,0 +1,77 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ +package org.onap.ccsdk.sli.core.sli.lighty; + +import io.lighty.core.controller.api.AbstractLightyModule; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.core.sli.SvcLogicAdaptor; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.core.sli.SvcLogicRecorder; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicClassResolverLighty; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicPropertiesProviderImpl; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicServiceImplLighty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The implementation of the {@link io.lighty.core.controller.api.LightyModule} that manages and provides services from + * the sli-provider artifact. + */ +public class SliModule extends AbstractLightyModule { + + private static final Logger LOG = LoggerFactory.getLogger(SliModule.class); + + private final DbLibService dbLibService; + private final SvcLogicResource svcLogicResource; + private final SvcLogicRecorder svcLogicRecorder; + private final SvcLogicJavaPlugin svcLogicJavaPlugin; + private final SvcLogicAdaptor svcLogicAdaptor; + + private SvcLogicPropertiesProviderImpl svcLogicPropertiesImpl; + private SvcLogicServiceImplLighty svcLogicImpl; + private SvcLogicClassResolverLighty svcLogicClassResolver; + + public SliModule(DbLibService dbLibService, SvcLogicResource svcLogicResource, SvcLogicRecorder svcLogicRecorder, + SvcLogicJavaPlugin svcLogicJavaPlugin, SvcLogicAdaptor svcLogicAdaptor) { + this.dbLibService = dbLibService; + this.svcLogicResource = svcLogicResource; + this.svcLogicRecorder = svcLogicRecorder; + this.svcLogicJavaPlugin = svcLogicJavaPlugin; + this.svcLogicAdaptor = svcLogicAdaptor; + } + + @Override + protected boolean initProcedure() { + this.svcLogicPropertiesImpl = new SvcLogicPropertiesProviderImpl(); + this.svcLogicClassResolver = new SvcLogicClassResolverLighty(svcLogicResource, svcLogicRecorder, + svcLogicJavaPlugin, svcLogicAdaptor); + this.svcLogicImpl = new SvcLogicServiceImplLighty(this.svcLogicPropertiesImpl, dbLibService, + svcLogicClassResolver); + return true; + } + + @Override + protected boolean stopProcedure() { + return true; + } + + public SvcLogicService getSvcLogicServiceImpl() { + return this.svcLogicImpl; + } +} diff --git a/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicAdaptorFactoryLighty.java b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicAdaptorFactoryLighty.java new file mode 100644 index 00000000..d60e4311 --- /dev/null +++ b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicAdaptorFactoryLighty.java @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : CCSDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.sli.provider; + +import java.util.HashMap; +import org.onap.ccsdk.sli.core.sli.SvcLogicAdaptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link SvcLogicAdaptorFactory} WITH REMOVED OSGi DEPENDENCIES + */ +public class SvcLogicAdaptorFactoryLighty { + + private static final Logger LOG = LoggerFactory + .getLogger(SvcLogicAdaptorFactoryLighty.class); + + private static HashMap<String, SvcLogicAdaptor> adaptorMap = new HashMap<>(); + + public static void registerAdaptor(SvcLogicAdaptor adaptor) { + String name = adaptor.getClass().getName(); + LOG.info("Registering adaptor " + name); + adaptorMap.put(name, adaptor); + + } + + public static void unregisterAdaptor(String name) { + if (adaptorMap.containsKey(name)) { + LOG.info("Unregistering " + name); + adaptorMap.remove(name); + } + } + +} diff --git a/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicClassResolverLighty.java b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicClassResolverLighty.java new file mode 100644 index 00000000..7b730a8e --- /dev/null +++ b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicClassResolverLighty.java @@ -0,0 +1,51 @@ +package org.onap.ccsdk.sli.core.sli.provider; + +import org.onap.ccsdk.sli.core.sli.SvcLogicAdaptor; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.core.sli.SvcLogicRecorder; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource; +import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link SvcLogicClassResolver} WITH REMOVED OSGi DEPENDENCIES + */ +public class SvcLogicClassResolverLighty implements SvcLogicResolver { + + private static final Logger LOG = LoggerFactory.getLogger(SvcLogicClassResolverLighty.class); + + private final SvcLogicResource svcLogicResource; + private final SvcLogicRecorder svcLogicRecorder; + private final SvcLogicJavaPlugin svcLogicJavaPlugin; + private final SvcLogicAdaptor svcLogicAdaptor; + + public SvcLogicClassResolverLighty(SvcLogicResource svcLogicResource, SvcLogicRecorder svcLogicRecorder, + SvcLogicJavaPlugin svcLogicJavaPlugin, SvcLogicAdaptor svcLogicAdaptor) { + this.svcLogicResource = svcLogicResource; + this.svcLogicRecorder = svcLogicRecorder; + this.svcLogicJavaPlugin = svcLogicJavaPlugin; + this.svcLogicAdaptor = svcLogicAdaptor; + } + + @Override + public SvcLogicResource getSvcLogicResource(String resourceName) { + return svcLogicResource; + } + + @Override + public SvcLogicRecorder getSvcLogicRecorder(String recorderName) { + return svcLogicRecorder; + } + + @Override + public SvcLogicJavaPlugin getSvcLogicJavaPlugin(String pluginName) { + return svcLogicJavaPlugin; + } + + @Override + public SvcLogicAdaptor getSvcLogicAdaptor(String adaptorName) { + return svcLogicAdaptor; + } + +} diff --git a/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicPropertiesProviderImplLighty.java b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicPropertiesProviderImplLighty.java new file mode 100644 index 00000000..dfc5855f --- /dev/null +++ b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicPropertiesProviderImplLighty.java @@ -0,0 +1,172 @@ +/*- + * ============LICENSE_START======================================================= + * onap + * ================================================================================ + * Copyright (C) 2016 - 2017 ONAP + * Modifications Copyright (C) 2018 IBM. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.sli.provider; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.Vector; +import org.onap.ccsdk.sli.core.sli.ConfigurationException; +import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicPropertiesProvider; +import org.onap.ccsdk.sli.core.utils.KarafRootFileResolver; +import org.onap.ccsdk.sli.core.utils.PropertiesFileResolver; +import org.onap.ccsdk.sli.core.utils.common.CoreDefaultFileResolver; +import org.onap.ccsdk.sli.core.utils.common.SdncConfigEnvVarFileResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link SvcLogicPropertiesProviderImpl} WITH REMOVED OSGi DEPENDENCIES + */ +public class SvcLogicPropertiesProviderImplLighty implements SvcLogicPropertiesProvider { + + private static final Logger log = LoggerFactory.getLogger(SvcLogicPropertiesProviderImplLighty.class); + + /** + * The name of the properties file for database configuration + */ + private static final String SVCLOGIC_PROP_FILE_NAME = "svclogic.properties"; + + /** + * A prioritized list of strategies for resolving dblib properties files. + */ + private Vector<PropertiesFileResolver> sliPropertiesFileResolvers = new Vector<>(); + + /** + * The configuration properties for the db connection. + */ + private Properties properties; + + /** + * Set up the prioritized list of strategies for resolving dblib properties + * files. + */ + public SvcLogicPropertiesProviderImplLighty() { + sliPropertiesFileResolvers + .add(new SdncConfigEnvVarFileResolver("Using property file (1) from environment variable")); + sliPropertiesFileResolvers.add(new CoreDefaultFileResolver("Using property file (2) from default directory")); + sliPropertiesFileResolvers.add(new KarafRootFileResolver("Using property file (4) from karaf root", this)); + + // determines properties file as according to the priority described in the + // class header comment + final File propertiesFile = determinePropertiesFile(this); + if (propertiesFile != null) { + try (FileInputStream fileInputStream = new FileInputStream(propertiesFile)) { + properties = new Properties(); + properties.load(fileInputStream); + } catch (final IOException e) { + log.error("Failed to load properties for file: {}", propertiesFile.toString(), + new ConfigurationException("Failed to load properties for file: " + propertiesFile.toString(), + e)); + } + } else { + // Try to read properties as resource + + InputStream propStr = getClass().getResourceAsStream("/" + SVCLOGIC_PROP_FILE_NAME); + if (propStr != null) { + properties = new Properties(); + try { + properties.load(propStr); + propStr.close(); + } catch (IOException e) { + log.error("IO Exception",e); + properties = null; + } + } + + } + + if (properties == null) { + reportFailure("Missing configuration properties resource(3)", new ConfigurationException( + "Missing configuration properties resource(3): " + SVCLOGIC_PROP_FILE_NAME)); + } + } + + /** + * Extract svclogic config properties. + * + * @return the svclogic config properties + */ + public Properties getProperties() { + return properties; + } + + /** + * Reports the method chosen for properties resolution to the + * <code>Logger</code>. + * + * @param message + * Some user friendly message + * @param fileOptional + * The file location of the chosen properties file + * @return the file location of the chosen properties file + */ + private static File reportSuccess(final String message, final Optional<File> fileOptional) { + if (fileOptional.isPresent()) { + final File file = fileOptional.get(); + log.info("{} {}", message, file.getPath()); + return file; + } + return null; + } + + /** + * Reports fatal errors. This is the case in which no properties file could be + * found. + * + * @param message + * An appropriate fatal error message + * @param configurationException + * An exception describing what went wrong during resolution + */ + private static void reportFailure(final String message, final ConfigurationException configurationException) { + + log.error("{}", message, configurationException); + } + + /** + * Determines the dblib properties file to use based on the following priority: + * <ol> + * <li>A directory identified by the system environment variable + * <code>SDNC_CONFIG_DIR</code></li> + * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li> + * <li>A directory identified by the JRE argument + * <code>dblib.properties</code></li> + * <li>A <code>dblib.properties</code> file located in the karaf root + * directory</li> + * </ol> + */ + File determinePropertiesFile(final SvcLogicPropertiesProviderImplLighty resourceProvider) { + + for (final PropertiesFileResolver sliPropertiesFileResolver : sliPropertiesFileResolvers) { + final Optional<File> fileOptional = sliPropertiesFileResolver.resolveFile(SVCLOGIC_PROP_FILE_NAME); + if (fileOptional.isPresent()) { + return reportSuccess(sliPropertiesFileResolver.getSuccessfulResolutionMessage(), fileOptional); + } + } + + return null; + } +} diff --git a/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicServiceImplLighty.java b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicServiceImplLighty.java new file mode 100755 index 00000000..b9740c58 --- /dev/null +++ b/sli/lighty/src/main/java/org/onap/ccsdk/sli/core/sli/provider/SvcLogicServiceImplLighty.java @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : CCSDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Modifications Copyright (C) 2018 IBM. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.sli.provider; + +import java.util.Properties; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.core.sli.ConfigurationException; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicDblibStore; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicGraph; +import org.onap.ccsdk.sli.core.sli.SvcLogicStore; +import org.onap.ccsdk.sli.core.sli.SvcLogicStoreFactory; +import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicPropertiesProvider; +import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicResolver; +import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicServiceImplBase; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +/** + * THIS CLASS IS A COPY OF {@link SvcLogicServiceImpl} WITH REMOVED OSGi DEPENDENCIES + */ +public class SvcLogicServiceImplLighty extends SvcLogicServiceImplBase implements SvcLogicService { + + private static final Logger LOG = LoggerFactory.getLogger(SvcLogicServiceImplLighty.class); + + public SvcLogicServiceImplLighty(SvcLogicPropertiesProvider resourceProvider, DbLibService dbSvc, + SvcLogicResolver resolver) { + super(null); + this.resolver = resolver; + properties = resourceProvider.getProperties(); + this.store = new SvcLogicDblibStore(dbSvc); + } + + @Override + public Properties execute(String module, String rpc, String version, String mode, Properties props) + throws SvcLogicException { + return (execute(module, rpc, version, mode, props, null)); + } + + @Override + public Properties execute(String module, String rpc, String version, String mode, Properties props, + DOMDataBroker domDataBroker) throws SvcLogicException { + LOG.info("Fetching service logic from data store"); + SvcLogicGraph graph = store.fetch(module, rpc, version, mode); + + if (graph == null) { + Properties retProps = new Properties(); + retProps.setProperty("error-code", "401"); + retProps.setProperty("error-message", + "No service logic found for [" + module + "," + rpc + "," + version + "," + mode + "]"); + return (retProps); + } + + SvcLogicContext ctx = new SvcLogicContext(props); + ctx.setAttribute(CURRENT_GRAPH, graph.toString()); + ctx.setAttribute("X-ECOMP-RequestID", MDC.get("X-ECOMP-RequestID")); + ctx.setDomDataBroker(domDataBroker); + execute(graph, ctx); + return (ctx.toProperties()); + } + + @Override + public SvcLogicStore getStore() throws SvcLogicException { + // Create and initialize SvcLogicStore object - used to access + // saved service logic. + if (store != null) { + return store; + } + + try { + store = SvcLogicStoreFactory.getSvcLogicStore(properties); + } catch (Exception e) { + throw new ConfigurationException("Could not get service logic store", e); + + } + + try { + store.init(properties); + } catch (SvcLogicException e) { + throw new ConfigurationException("Could not get service logic store", e); + } + + return store; + } + +} diff --git a/sli/pom.xml b/sli/pom.xml index b9b60e9e..bf21cb63 100755 --- a/sli/pom.xml +++ b/sli/pom.xml @@ -28,5 +28,6 @@ <module>recording</module> <module>features</module> <module>installer</module> + <module>lighty</module> </modules> </project> diff --git a/sliPluginUtils/lighty/pom.xml b/sliPluginUtils/lighty/pom.xml new file mode 100755 index 00000000..53445daf --- /dev/null +++ b/sliPluginUtils/lighty/pom.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliPluginUtil-lighty</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>ccsdk-sli-core :: sliPluginUtil :: ${project.artifactId}</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-lighty-dependency-versions</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.lighty.core</groupId> + <artifactId>lighty-controller</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliPluginUtils-provider</artifactId> + <version>${project.version}</version> + <scope>compile</scope> + </dependency> + </dependencies> +</project> diff --git a/sliPluginUtils/lighty/src/main/java/org/onap/ccsdk/sli/core/slipluginutils/lighty/SliPluginUtilsModule.java b/sliPluginUtils/lighty/src/main/java/org/onap/ccsdk/sli/core/slipluginutils/lighty/SliPluginUtilsModule.java new file mode 100644 index 00000000..41b63f53 --- /dev/null +++ b/sliPluginUtils/lighty/src/main/java/org/onap/ccsdk/sli/core/slipluginutils/lighty/SliPluginUtilsModule.java @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ +package org.onap.ccsdk.sli.core.slipluginutils.lighty; + +import io.lighty.core.controller.api.AbstractLightyModule; +import org.onap.ccsdk.sli.core.slipluginutils.SliPluginUtils; +import org.onap.ccsdk.sli.core.slipluginutils.SliStringUtils; + +/** + * The implementation of the {@link io.lighty.core.controller.api.LightyModule} that manages and provides services from + * the sliPluginUtils-provider artifact. + */ +public class SliPluginUtilsModule extends AbstractLightyModule { + + private SliPluginUtils sliPluginUtils; + private SliStringUtils sliStringUtils; + + @Override + protected boolean initProcedure() { + this.sliPluginUtils = new SliPluginUtils(); + this.sliStringUtils = new SliStringUtils(); + return true; + } + + @Override + protected boolean stopProcedure() { + return true; + } + + public SliPluginUtils getSliPluginUtils() { + return this.sliPluginUtils; + } + + public SliStringUtils getSliStringUtils() { + return sliStringUtils; + } +} diff --git a/sliPluginUtils/pom.xml b/sliPluginUtils/pom.xml index f9cdcec2..48994d8e 100755 --- a/sliPluginUtils/pom.xml +++ b/sliPluginUtils/pom.xml @@ -21,5 +21,6 @@ <module>provider</module> <module>features</module> <module>installer</module> + <module>lighty</module> </modules> </project> diff --git a/sliapi/lighty/pom.xml b/sliapi/lighty/pom.xml new file mode 100755 index 00000000..2812d6ba --- /dev/null +++ b/sliapi/lighty/pom.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliapi-lighty</artifactId> + <version>0.6.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>ccsdk-sli-core :: sliapi :: ${project.artifactId}</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-lighty-dependency-versions</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.lighty.core</groupId> + <artifactId>lighty-controller</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sliapi-provider</artifactId> + <version>${project.version}</version> + <scope>compile</scope> + </dependency> + </dependencies> +</project> diff --git a/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/lighty/SliApiModule.java b/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/lighty/SliApiModule.java new file mode 100644 index 00000000..24d8958e --- /dev/null +++ b/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/lighty/SliApiModule.java @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START========================================== + * Copyright (c) 2019 PANTHEON.tech s.r.o. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + */ +package org.onap.ccsdk.sli.core.sliapi.lighty; + +import io.lighty.core.controller.api.AbstractLightyModule; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService; +import org.onap.ccsdk.sli.core.sliapi.sliapiProviderLighty; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The implementation of the {@link io.lighty.core.controller.api.LightyModule} that manages and provides services from + * the sliapi-provider artifact. + */ +public class SliApiModule extends AbstractLightyModule { + + private static final Logger LOG = LoggerFactory.getLogger(SliApiModule.class); + + private final DataBroker dataBroker; + private final NotificationPublishService notificationPublishService; + private final RpcProviderRegistry rpcRegistry; + private final SvcLogicService svcLogic; + + private sliapiProviderLighty sliapiProvider; + + public SliApiModule (DataBroker dataBroker, NotificationPublishService notificationPublishService, + RpcProviderRegistry rpcRegistry, SvcLogicService svcLogic) { + this.dataBroker = dataBroker; + this.notificationPublishService = notificationPublishService; + this.rpcRegistry = rpcRegistry; + this.svcLogic = svcLogic; + } + + @Override + protected boolean initProcedure() { + LOG.debug("Initializing Lighty module {}...", this.getClass()); + this.sliapiProvider = new sliapiProviderLighty(dataBroker, notificationPublishService, rpcRegistry, svcLogic); + LOG.debug("Lighty module {} initialized", this.getClass()); + return true; + } + + @Override + protected boolean stopProcedure() { + return true; + } + +} diff --git a/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/sliapiProviderLighty.java b/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/sliapiProviderLighty.java new file mode 100644 index 00000000..94698412 --- /dev/null +++ b/sliapi/lighty/src/main/java/org/onap/ccsdk/sli/core/sliapi/sliapiProviderLighty.java @@ -0,0 +1,606 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : CCSDK + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.ccsdk.sli.core.sliapi; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.Properties; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.binding.impl.AbstractForwardedDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.ExecuteGraphInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.ExecuteGraphInput.Mode; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.ExecuteGraphInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.ExecuteGraphOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.ExecuteGraphOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.HealthcheckInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.HealthcheckOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.HealthcheckOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.SLIAPIService; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.TestResults; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.VlbcheckInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.VlbcheckOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.VlbcheckOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.execute.graph.input.SliParameter; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.test.results.TestResult; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.core.sliapi.rev161110.test.results.TestResultBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * THIS CLASS IS A COPY OF {@link sliapiProvider} WITH REMOVED OSGi DEPENDENCIES + */ +public class sliapiProviderLighty implements AutoCloseable, SLIAPIService { + + private static final Logger LOG = LoggerFactory.getLogger(sliapiProviderLighty.class); + private static final String appName = "slitester"; + + protected DataBroker dataBroker; + protected DOMDataBroker domDataBroker; + protected NotificationPublishService notificationService; + protected RpcProviderRegistry rpcRegistry; + + private SvcLogicService svcLogic; + + protected BindingAwareBroker.RpcRegistration<SLIAPIService> rpcRegistration; + + private static String SLIAPI_NAMESPACE = "org:onap:ccsdk:sli:core:sliapi"; + private static String SLIAPI_REVISION = "2016-11-10"; + private static String SDNC_STATUS_FILE = "SDNC_STATUS_FILE"; + private static String sdncStatusFile = null; + + private static QName TEST_RESULTS_QNAME = null; + private static QName TEST_RESULT_QNAME = null; + private static QName TEST_ID_QNAME = null; + private static QName RESULTS_QNAME = null; + private static final String NON_NULL = "non-null"; + + static { + + TEST_RESULTS_QNAME = QName.create(SLIAPI_NAMESPACE, SLIAPI_REVISION, "test-results"); + TEST_RESULT_QNAME = QName.create(TEST_RESULTS_QNAME, "test-result"); + TEST_ID_QNAME = QName.create(TEST_RESULT_QNAME, "test-identifier"); + RESULTS_QNAME = QName.create(TEST_RESULT_QNAME, "results"); + } + + public sliapiProviderLighty(DataBroker dataBroker, NotificationPublishService notificationPublishService, + RpcProviderRegistry rpcProviderRegistry, SvcLogicService svcLogic) { + this.LOG.info("Creating provider for " + appName); + this.dataBroker = dataBroker; + this.notificationService = notificationPublishService; + this.rpcRegistry = rpcProviderRegistry; + this.svcLogic = svcLogic; + initialize(); + } + + public void initialize() { + LOG.info("Initializing provider for " + appName); + // initialization code goes here. + rpcRegistration = rpcRegistry.addRpcImplementation(SLIAPIService.class, this); + + sdncStatusFile = System.getenv(SDNC_STATUS_FILE); + LOG.info("SDNC STATUS FILE = " + sdncStatusFile); + LOG.info("Initialization complete for " + appName); + } + + protected void initializeChild() { + // Override if you have custom initialization intelligence + } + + @Override + public void close() throws Exception { + LOG.info("Closing provider for " + appName); + // closing code goes here + + rpcRegistration.close(); + LOG.info("Successfully closed provider for " + appName); + } + + public void setDataBroker(DataBroker dataBroker) { + this.dataBroker = dataBroker; + if (dataBroker instanceof AbstractForwardedDataBroker) { + domDataBroker = ((AbstractForwardedDataBroker) dataBroker).getDelegate(); + } + if (LOG.isDebugEnabled()) { + LOG.debug("DataBroker set to " + (dataBroker == null ? "null" : NON_NULL) + "."); + } + } + + public void setNotificationService(NotificationPublishService notificationService) { + this.notificationService = notificationService; + if (LOG.isDebugEnabled()) { + LOG.debug("Notification Service set to " + (notificationService == null ? "null" : NON_NULL) + "."); + } + } + + public void setRpcRegistry(RpcProviderRegistry rpcRegistry) { + this.rpcRegistry = rpcRegistry; + if (LOG.isDebugEnabled()) { + LOG.debug("RpcRegistry set to " + (rpcRegistry == null ? "null" : NON_NULL) + "."); + } + } + + @Override + public ListenableFuture<RpcResult<ExecuteGraphOutput>> executeGraph(ExecuteGraphInput input) { + RpcResult<ExecuteGraphOutput> rpcResult = null; + + ExecuteGraphOutputBuilder respBuilder = new ExecuteGraphOutputBuilder(); + + String calledModule = input.getModuleName(); + String calledRpc = input.getRpcName(); + Mode calledMode = input.getMode(); + String modeStr = "sync"; + + if (calledMode == Mode.Async) { + modeStr = "async"; + } + + if (svcLogic == null) { + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<ExecuteGraphOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + try { + if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) { + respBuilder.setResponseCode("404"); + respBuilder.setResponseMessage( + "Directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr + " not found"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<ExecuteGraphOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + } catch (Exception e) { + LOG.error( + "Caught exception looking for directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr, + e); + + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Internal error : could not determine if target graph exists"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<ExecuteGraphOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + // Load properties + Properties parms = new Properties(); + + // Pass properties using names from sli-parameters + for (SliParameter sliParm : input.getSliParameter()) { + + String propValue = ""; + + Boolean boolval = sliParm.isBooleanValue(); + + if (boolval != null) { + propValue = boolval.toString(); + } else { + Integer intval = sliParm.getIntValue(); + if (intval != null) { + propValue = intval.toString(); + } else { + propValue = sliParm.getStringValue(); + if (propValue == null) { + propValue = ""; + } + } + } + parms.setProperty(sliParm.getParameterName(), propValue); + } + + // Also, pass "meta" properties (i.e. pass SliParameter objects themselves) + ExecuteGraphInputBuilder inputBuilder = new ExecuteGraphInputBuilder(input); + + SliapiHelper.toProperties(parms, "input", inputBuilder); + + try { + LOG.info("Calling directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr); + + if (LOG.isTraceEnabled()) { + StringBuffer argList = new StringBuffer(); + argList.append("Parameters : {"); + Enumeration e = parms.propertyNames(); + while (e.hasMoreElements()) { + String propName = (String) e.nextElement(); + argList.append(" (" + propName + "," + parms.getProperty(propName) + ") "); + } + argList.append("}"); + LOG.trace(argList.toString()); + argList = null; + } + + Properties respProps = svcLogic.execute(calledModule, calledRpc, null, modeStr, parms, domDataBroker); + + StringBuilder sb = new StringBuilder("{"); + + for (Object key : respProps.keySet()) { + String keyValue = (String) key; + if (keyValue != null && !"".equals(keyValue) && !keyValue.contains("input.sli-parameter")) { + sb.append("\"").append(keyValue).append("\": \"").append(respProps.getProperty(keyValue)) + .append("\","); + } + } + + sb.setLength(sb.length() - 1); + sb.append("}"); + + respBuilder.setResponseCode(respProps.getProperty("error-code", "0")); + respBuilder.setResponseMessage(respProps.getProperty("error-message", ""));// TODO change response-text to + // response-message to match + // other BVC APIs + respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y")); + respBuilder.setContextMemoryJson(sb.toString()); + + TestResultBuilder testResultBuilder = new TestResultBuilder(); + + SliapiHelper.toBuilder(respProps, testResultBuilder); + + String testIdentifier = testResultBuilder.getTestIdentifier(); + + if ((testIdentifier != null) && (testIdentifier.length() > 0)) { + + // Add test results to config tree + LOG.debug("Saving test results for test id " + testIdentifier); + + DomSaveTestResult(testResultBuilder.build(), true, LogicalDatastoreType.CONFIGURATION); + + } + + } catch (Exception e) { + LOG.error("Caught exception executing directed graph for" + calledModule + ":" + calledRpc + "," + modeStr + + ">", e); + + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Internal error : caught exception executing directed graph " + calledModule + + "/" + calledRpc + "/" + modeStr); + respBuilder.setAckFinalIndicator("Y"); + + } + + rpcResult = RpcResultBuilder.<ExecuteGraphOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + @Override + public ListenableFuture<RpcResult<HealthcheckOutput>> healthcheck(HealthcheckInput healthcheckInput) { + + RpcResult<HealthcheckOutput> rpcResult = null; + + HealthcheckOutputBuilder respBuilder = new HealthcheckOutputBuilder(); + + String calledModule = "sli"; + String calledRpc = "healthcheck"; + String modeStr = "sync"; + + if (svcLogic == null) { + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<HealthcheckOutput>failed().withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + try { + if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) { + respBuilder.setResponseCode("404"); + respBuilder.setResponseMessage( + "Directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr + " not found"); + + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<HealthcheckOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + } catch (Exception e) { + LOG.error( + "Caught exception looking for directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr, + e); + + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Internal error : could not determine if target graph exists"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<HealthcheckOutput>failed().withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + try { + LOG.info("Calling directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr); + + Properties parms = new Properties(); + + Properties respProps = svcLogic.execute(calledModule, calledRpc, null, modeStr, parms); + + respBuilder.setResponseCode(respProps.getProperty("error-code", "0")); + respBuilder.setResponseMessage(respProps.getProperty("error-message", "")); + respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y")); + + } catch (Exception e) { + LOG.error("Caught exception executing directed graph for" + calledModule + ":" + calledRpc + "," + modeStr + + ">", e); + + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Internal error : caught exception executing directed graph " + calledModule + + "/" + calledRpc + "/" + modeStr); + respBuilder.setAckFinalIndicator("Y"); + + } + + rpcResult = RpcResultBuilder.<HealthcheckOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + public ListenableFuture<RpcResult<VlbcheckOutput>> vlbcheck(VlbcheckInput vlbInput) { + + RpcResult<VlbcheckOutput> rpcResult = null; + + VlbcheckOutputBuilder respBuilder = new VlbcheckOutputBuilder(); + + String calledModule = "sli"; + String calledRpc = "vlbcheck"; + String modeStr = "sync"; + + if (svcLogic == null) { + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service"); + respBuilder.setAckFinalIndicator("Y"); + + rpcResult = RpcResultBuilder.<VlbcheckOutput>failed().withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } + + boolean dgExists = true; + try { + if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) { + dgExists = false; + } + } catch (Exception e) { + LOG.warn( + "Caught exception looking for directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr, + e); + + dgExists = false; + } + + if (dgExists) { + try { + LOG.info("Calling directed graph for " + calledModule + "/" + calledRpc + "/" + modeStr); + + Properties parms = new Properties(); + + Properties respProps = svcLogic.execute(calledModule, calledRpc, null, modeStr, parms); + + respBuilder.setResponseCode(respProps.getProperty("error-code", "0")); + respBuilder.setResponseMessage(respProps.getProperty("error-message", "")); + respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y")); + + } catch (Exception e) { + LOG.error("Caught exception executing directed graph for" + calledModule + ":" + calledRpc + "," + + modeStr + ">", e); + + respBuilder.setResponseCode("500"); + respBuilder.setResponseMessage("Internal error : caught exception executing directed graph " + + calledModule + "/" + calledRpc + "/" + modeStr); + respBuilder.setAckFinalIndicator("Y"); + + } + + rpcResult = RpcResultBuilder.<VlbcheckOutput>status(true).withResult(respBuilder.build()).build(); + return (Futures.immediateFuture(rpcResult)); + } else { + // check the state based on the config file + + boolean suspended = false; + BufferedReader br = null; + String line = ""; + + if (sdncStatusFile != null) { + try { + br = new BufferedReader(new FileReader(sdncStatusFile)); + while ((line = br.readLine()) != null) { + if ("ODL_STATE=SUSPENDED".equals(line)) { + suspended = true; + LOG.debug("vlbcheck: server is suspended"); + } + } + br.close(); + } catch (FileNotFoundException e) { + LOG.trace("Caught File not found exception " + sdncStatusFile + "\n", e); + } catch (Exception e) { + LOG.trace("Failed to read status file " + sdncStatusFile + "\n", e); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + LOG.warn("Failed to close status file " + sdncStatusFile + "\n", e); + } + } + } + } + + if (suspended) { + rpcResult = RpcResultBuilder.<VlbcheckOutput>failed() + .withError(ErrorType.APPLICATION, "resource-denied", "Server Suspended").build(); + } else { + respBuilder.setResponseMessage("server is normal"); + rpcResult = RpcResultBuilder.<VlbcheckOutput>status(true).withResult(respBuilder.build()).build(); + } + return (Futures.immediateFuture(rpcResult)); + } + } + + private void DomSaveTestResult(final TestResult entry, boolean merge, LogicalDatastoreType storeType) { + + if (domDataBroker == null) { + LOG.error("domDataBroker unset - cannot save test result using DOMDataBroker"); + return; + } + + MapEntryNode resultNode = null; + + try { + resultNode = toMapEntryNode(entry); + } catch (Exception e) { + LOG.error("Caught exception trying to create map entry node", e); + } + + if (resultNode == null) { + LOG.error("Could not convert entry to MapEntryNode"); + return; + } + + YangInstanceIdentifier testResultsPid = YangInstanceIdentifier.builder().node(TEST_RESULTS_QNAME) + .node(QName.create(TEST_RESULTS_QNAME, "test-result")).build(); + YangInstanceIdentifier testResultPid = testResultsPid + .node(new NodeIdentifierWithPredicates(TEST_RESULT_QNAME, resultNode.getIdentifier().getKeyValues())); + + int tries = 2; + while (true) { + try { + DOMDataWriteTransaction wtx = domDataBroker.newWriteOnlyTransaction(); + if (merge) { + LOG.info("Merging test identifier " + entry.getTestIdentifier()); + wtx.merge(storeType, testResultPid, resultNode); + } else { + LOG.info("Putting test identifier " + entry.getTestIdentifier()); + wtx.put(storeType, testResultPid, resultNode); + } + wtx.submit().checkedGet(); + LOG.trace("Update DataStore succeeded"); + break; + } catch (final TransactionCommitFailedException e) { + if (e instanceof OptimisticLockFailedException) { + if (--tries <= 0) { + LOG.trace("Got OptimisticLockFailedException on last try - failing "); + throw new IllegalStateException(e); + } + LOG.trace("Got OptimisticLockFailedException - trying again "); + } else { + LOG.trace("Update DataStore failed"); + throw new IllegalStateException(e); + } + } + } + + } + + private void SaveTestResult(final TestResult entry, boolean merge, LogicalDatastoreType storeType) + throws IllegalStateException { + // Each entry will be identifiable by a unique key, we have to create that + // identifier + + InstanceIdentifier.InstanceIdentifierBuilder<TestResult> testResultIdBuilder = InstanceIdentifier + .<TestResults>builder(TestResults.class).child(TestResult.class, entry.key()); + InstanceIdentifier<TestResult> path = testResultIdBuilder.build(); + int tries = 2; + while (true) { + try { + WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); + if (merge) { + tx.merge(storeType, path, entry); + } else { + tx.put(storeType, path, entry); + } + tx.submit().checkedGet(); + LOG.trace("Update DataStore succeeded"); + break; + } catch (final TransactionCommitFailedException e) { + if (e instanceof OptimisticLockFailedException) { + if (--tries <= 0) { + LOG.trace("Got OptimisticLockFailedException on last try - failing "); + throw new IllegalStateException(e); + } + LOG.trace("Got OptimisticLockFailedException - trying again "); + } else { + LOG.trace("Update DataStore failed"); + throw new IllegalStateException(e); + } + } + } + } + + private MapEntryNode toMapEntryNode(TestResult testResult) { + + YangInstanceIdentifier testResultId = YangInstanceIdentifier.builder().node(TEST_RESULTS_QNAME) + .node(TEST_RESULT_QNAME).build(); + + // Construct results list + LinkedList<LeafSetEntryNode<Object>> entryList = new LinkedList<>(); + for (String result : testResult.getResults()) { + LeafSetEntryNode<Object> leafSetEntryNode = ImmutableLeafSetEntryNodeBuilder.create() + .withNodeIdentifier(new NodeWithValue(RESULTS_QNAME, result)).withValue(result).build(); + entryList.add(leafSetEntryNode); + } + // Construct results LeafSetNode + LeafSetNode<?> resultsNode = ImmutableLeafSetNodeBuilder.create() + .withNodeIdentifier(new NodeIdentifier(RESULTS_QNAME)).withValue(entryList).build(); + + // Construct test result ContainerNode with 2 children - test-identifier leaf + // and results leaf-set + MapEntryNode testResultNode = ImmutableNodes.mapEntryBuilder() + .withNodeIdentifier(new NodeIdentifierWithPredicates(TEST_RESULT_QNAME, TEST_ID_QNAME, + testResult.getTestIdentifier())) + .withChild(ImmutableNodes.leafNode(TEST_ID_QNAME, testResult.getTestIdentifier())) + .withChild(resultsNode).build(); + + return (testResultNode); + + } + +} diff --git a/sliapi/pom.xml b/sliapi/pom.xml index c9541ac2..62651d06 100755 --- a/sliapi/pom.xml +++ b/sliapi/pom.xml @@ -21,6 +21,7 @@ <module>features</module> <module>provider</module> <module>installer</module> + <module>lighty</module> </modules> <properties> |