From 3a50557405e66759277ab71d85963d0943cd08d2 Mon Sep 17 00:00:00 2001 From: "Haddox, Anthony" Date: Wed, 17 Apr 2019 09:07:36 -0700 Subject: [CCSDK-1241] Increase GRToolkit Unit Test Coverage Add unit tests for GRToolkit. Slight changes to make code testable. Change-Id: Ib435da58b62e7b8edda4876f0f3a262cbc41a8ca Issue-ID: CCSDK-1241 Signed-off-by: Haddox, Anthony --- grToolkit/provider/pom.xml | 6 + .../onap/ccsdk/sli/plugins/GrToolkitProvider.java | 958 -------------------- .../onap/ccsdk/sli/plugins/data/ClusterActor.java | 215 ----- .../onap/ccsdk/sli/plugins/data/MemberBuilder.java | 80 -- .../sli/plugins/grtoolkit/GrToolkitProvider.java | 965 +++++++++++++++++++++ .../sli/plugins/grtoolkit/data/ClusterActor.java | 215 +++++ .../sli/plugins/grtoolkit/data/MemberBuilder.java | 80 ++ .../src/main/resources/gr-toolkit.properties | 21 +- .../org/opendaylight/blueprint/GrToolkit.xml | 2 +- .../plugins/grtoolkit/GrToolkitProviderTest.java | 344 ++++++++ .../plugins/grtoolkit/data/MemberBuilderTest.java | 41 + grToolkit/provider/src/test/resources/akka.conf | 49 ++ grToolkit/provider/src/test/resources/akka6.conf | 49 ++ .../src/test/resources/gr-toolkit.properties | 34 + 14 files changed, 1804 insertions(+), 1255 deletions(-) delete mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/GrToolkitProvider.java delete mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/ClusterActor.java delete mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/MemberBuilder.java create mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java create mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java create mode 100755 grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java create mode 100644 grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java create mode 100644 grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java create mode 100644 grToolkit/provider/src/test/resources/akka.conf create mode 100644 grToolkit/provider/src/test/resources/akka6.conf create mode 100755 grToolkit/provider/src/test/resources/gr-toolkit.properties diff --git a/grToolkit/provider/pom.xml b/grToolkit/provider/pom.xml index 951d2da6..51a850b8 100755 --- a/grToolkit/provider/pom.xml +++ b/grToolkit/provider/pom.xml @@ -98,5 +98,11 @@ ${akka.version} provided + + com.github.stefanbirkner + system-rules + 1.19.0 + test + diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/GrToolkitProvider.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/GrToolkitProvider.java deleted file mode 100755 index dc8534be..00000000 --- a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/GrToolkitProvider.java +++ /dev/null @@ -1,958 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * Copyright (C) 2018 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.plugins; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.annotation.Nonnull; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - -import org.onap.ccsdk.sli.core.dblib.DbLibService; -import org.onap.ccsdk.sli.plugins.data.ClusterActor; -import org.onap.ccsdk.sli.plugins.data.MemberBuilder; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; -import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; -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.plugins.gr.toolkit.rev180926.AdminHealthInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.GrToolkitService; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Member; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Site; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierInput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutput; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.site.health.output.SitesBuilder; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.common.RpcResultBuilder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GrToolkitProvider implements AutoCloseable, GrToolkitService, DataTreeChangeListener { - private static final String APP_NAME = "gr-toolkit"; - private static final String PROPERTIES_FILE = System.getenv("SDNC_CONFIG_DIR") + "/gr-toolkit.properties"; - private static final String HEALTHY = "HEALTHY"; - private static final String FAULTY = "FAULTY"; - private static final String VALUE = "value"; - private String akkaConfig; - private String jolokiaClusterPath; - private String shardManagerPath; - private String shardPathTemplate; - private String credentials; - private String httpProtocol; - private String siteIdentifier = System.getenv("SITE_NAME"); - private final Logger log = LoggerFactory.getLogger(GrToolkitProvider.class); - private final ExecutorService executor; - protected DataBroker dataBroker; - protected NotificationPublishService notificationService; - protected RpcProviderRegistry rpcRegistry; - protected BindingAwareBroker.RpcRegistration rpcRegistration; - protected DbLibService dbLib; - private String member; - private ClusterActor self; - private HashMap memberMap; - private SiteConfiguration siteConfiguration; - private Properties properties; - private DistributedDataStoreInterface configDatastore; - public GrToolkitProvider(DataBroker dataBroker, - NotificationPublishService notificationProviderService, - RpcProviderRegistry rpcProviderRegistry, - DistributedDataStoreInterface configDatastore, - DbLibService dbLibService) { - this.log.info("Creating provider for {}", APP_NAME); - this.executor = Executors.newFixedThreadPool(1); - this.dataBroker = dataBroker; - this.notificationService = notificationProviderService; - this.rpcRegistry = rpcProviderRegistry; - this.configDatastore = configDatastore; - this.dbLib = dbLibService; - initialize(); - } - - private void initialize() { - log.info("Initializing provider for {}", APP_NAME); - // Create the top level containers - createContainers(); - setProperties(); - defineMembers(); - - rpcRegistration = rpcRegistry.addRpcImplementation(GrToolkitService.class, this); - log.info("Initialization complete for {}", APP_NAME); - } - - private void setProperties() { - log.info("Loading properties from {}", PROPERTIES_FILE); - properties = new Properties(); - File propertiesFile = new File(PROPERTIES_FILE); - if(!propertiesFile.exists()) { - log.warn("Properties file not found."); - return; - } - try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) { - properties.load(fileInputStream); - if(!properties.containsKey(PropertyKeys.SITE_IDENTIFIER)) { - properties.put(PropertyKeys.SITE_IDENTIFIER, "Unknown Site"); - } - String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL).trim() : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP).trim(); - httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://"; - akkaConfig = properties.getProperty(PropertyKeys.AKKA_CONF_LOCATION).trim(); - jolokiaClusterPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_CLUSTER).trim(); - shardManagerPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_MANAGER).trim(); - shardPathTemplate = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_CONFIG).trim(); - if(siteIdentifier == null || siteIdentifier.isEmpty()) { - siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim(); - } - credentials = properties.getProperty(PropertyKeys.CONTROLLER_CREDENTIALS).trim(); - log.info("Loaded properties."); - } catch(IOException e) { - log.error("Error loading properties.", e); - } - } - - private void defineMembers() { - member = configDatastore.getActorContext().getCurrentMemberName().getName(); - log.info("Cluster member: {}", member); - - log.info("Parsing akka.conf for cluster memberMap..."); - try { - File akkaConfigFile = new File(this.akkaConfig); - try(FileReader fileReader = new FileReader(akkaConfigFile); - BufferedReader bufferedReader = new BufferedReader(fileReader)) { - String line; - while((line = bufferedReader.readLine()) != null) { - if(line.contains("seed-nodes =")) { - parseSeedNodes(line); - break; - } - } - } - } catch(IOException e) { - log.error("Couldn't load akka", e); - } - log.info("self:\n{}", self); - } - - private void createContainers() { - // Replace with MD-SAL write for FailoverStatus - } - - protected void initializeChild() { - // Override if you have custom initialization intelligence - } - - @Override - public void close() throws Exception { - log.info("Closing provider for {}", APP_NAME); - executor.shutdown(); - rpcRegistration.close(); - log.info("Successfully closed provider for {}", APP_NAME); - } - - @Override - public void onDataTreeChanged(@Nonnull Collection changes) { - log.info("onDataTreeChanged() called. but there is no change here"); - } - - @Override - public ListenableFuture> clusterHealth(ClusterHealthInput input) { - log.info("{}:cluster-health invoked.", APP_NAME); - getControllerHealth(); - return buildClusterHealthOutput("200"); - } - - @Override - public ListenableFuture> siteHealth(SiteHealthInput input) { - log.info("{}:site-health invoked.", APP_NAME); - getControllerHealth(); - return buildSiteHealthOutput("200", getAdminHealth(), getDatabaseHealth()); - } - - @Override - public ListenableFuture> databaseHealth(DatabaseHealthInput input) { - log.info("{}:database-health invoked.", APP_NAME); - DatabaseHealthOutputBuilder outputBuilder = new DatabaseHealthOutputBuilder(); - outputBuilder.setStatus("200"); - outputBuilder.setHealth(getDatabaseHealth()); - - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - @Override - public ListenableFuture> adminHealth(AdminHealthInput input) { - log.info("{}:admin-health invoked.", APP_NAME); - AdminHealthOutputBuilder outputBuilder = new AdminHealthOutputBuilder(); - outputBuilder.setStatus("200"); - outputBuilder.setHealth(getAdminHealth()); - - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - @Override - public ListenableFuture> haltAkkaTraffic(HaltAkkaTrafficInput input) { - log.info("{}:halt-akka-traffic invoked.", APP_NAME); - HaltAkkaTrafficOutputBuilder outputBuilder = new HaltAkkaTrafficOutputBuilder(); - outputBuilder.setStatus("200"); - modifyIpTables(IpTables.ADD, input.getNodeInfo().toArray()); - - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - @Override - public ListenableFuture> resumeAkkaTraffic(ResumeAkkaTrafficInput input) { - log.info("{}:resume-akka-traffic invoked.", APP_NAME); - ResumeAkkaTrafficOutputBuilder outputBuilder = new ResumeAkkaTrafficOutputBuilder(); - outputBuilder.setStatus("200"); - modifyIpTables(IpTables.DELETE, input.getNodeInfo().toArray()); - - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - @Override - public ListenableFuture> siteIdentifier(SiteIdentifierInput input) { - log.info("{}:site-identifier invoked.", APP_NAME); - SiteIdentifierOutputBuilder outputBuilder = new SiteIdentifierOutputBuilder(); - outputBuilder.setStatus("200"); - outputBuilder.setId(siteIdentifier); - - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - @Override - public ListenableFuture> failover(FailoverInput input) { - log.info("{}:failover invoked.", APP_NAME); - FailoverOutputBuilder outputBuilder = new FailoverOutputBuilder(); - if(siteConfiguration != SiteConfiguration.GEO) { - log.info("Cannot failover non-GEO site."); - outputBuilder.setMessage("Failover aborted. This is not a GEO configuration."); - outputBuilder.setStatus("400"); - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - ArrayList activeSite = new ArrayList<>(); - ArrayList standbySite = new ArrayList<>(); - - log.info("Performing preliminary cluster health check..."); - // Necessary to populate all member info. Health is not used for judgement calls. - getControllerHealth(); - - log.info("Determining active site..."); - for(Map.Entry entry : memberMap.entrySet()) { - String key = entry.getKey(); - ClusterActor clusterActor = entry.getValue(); - if(clusterActor.isVoting()) { - activeSite.add(clusterActor); - log.debug("Active Site member: {}", key); - } - else { - standbySite.add(clusterActor); - log.debug("Standby Site member: {}", key); - } - } - - String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL)) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL) : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP); - - if(Boolean.parseBoolean(input.getBackupData())) { - backupMdSal(activeSite, port); - } - - if(!changeClusterVoting(outputBuilder, activeSite, standbySite, port)) - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - - if(Boolean.parseBoolean(input.getIsolate())) { - isolateSiteFromCluster(activeSite, standbySite, port); - - if(Boolean.parseBoolean(input.getDownUnreachable())) { - downUnreachableNodes(activeSite, standbySite, port); - } - } - - log.info("{}:failover complete.", APP_NAME); - - outputBuilder.setMessage("Failover complete."); - outputBuilder.setStatus("200"); - return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); - } - - private void isolateSiteFromCluster(ArrayList activeSite, ArrayList standbySite, String port) { - log.info("Halting Akka traffic..."); - for(ClusterActor actor : standbySite) { - try { - log.info("Halting Akka traffic for: {}", actor.getNode()); - // Build JSON with activeSite actor Node and actor AkkaPort - JSONObject akkaInput = new JSONObject(); - JSONObject inputBlock = new JSONObject(); - JSONArray votingStateArray = new JSONArray(); - JSONObject nodeInfo; - for(ClusterActor node : activeSite) { - nodeInfo = new JSONObject(); - nodeInfo.put("node", node.getNode()); - nodeInfo.put("port", node.getAkkaPort()); - votingStateArray.put(nodeInfo); - } - inputBlock.put("node-info", votingStateArray); - akkaInput.put("input", inputBlock); - getRequestContent(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:halt-akka-traffic", HttpMethod.POST, akkaInput.toString()); - } catch(IOException e) { - log.error("Could not halt Akka traffic for: " + actor.getNode(), e); - } - } - } - - private void downUnreachableNodes(ArrayList activeSite, ArrayList standbySite, String port) { - log.info("Setting site unreachable..."); - JSONObject jolokiaInput = new JSONObject(); - jolokiaInput.put("type", "EXEC"); - jolokiaInput.put("mbean", "akka:type=Cluster"); - jolokiaInput.put("operation", "down"); - JSONArray arguments = new JSONArray(); - for(ClusterActor actor : activeSite) { - // Build Jolokia input - // May need to change from akka port to actor.getAkkaPort() - arguments.put("akka.tcp://opendaylight-cluster-data@" + actor.getNode() + ":" + properties.getProperty(PropertyKeys.CONTROLLER_PORT_AKKA)); - } - jolokiaInput.put("arguments", arguments); - log.debug("{}", jolokiaInput); - try { - log.info("Setting nodes unreachable"); - getRequestContent(httpProtocol + standbySite.get(0).getNode() + ":" + port + "/jolokia", HttpMethod.POST, jolokiaInput.toString()); - } catch(IOException e) { - log.error("Error setting nodes unreachable", e); - } - } - - private boolean changeClusterVoting(FailoverOutputBuilder outputBuilder, ArrayList activeSite, ArrayList standbySite, String port) { - log.info("Changing voting for all shards to standby site..."); - try { - JSONObject votingInput = new JSONObject(); - JSONObject inputBlock = new JSONObject(); - JSONArray votingStateArray = new JSONArray(); - JSONObject memberVotingState; - for(ClusterActor actor : activeSite) { - memberVotingState = new JSONObject(); - memberVotingState.put("member-name", actor.getMember()); - memberVotingState.put("voting", false); - votingStateArray.put(memberVotingState); - } - for(ClusterActor actor : standbySite) { - memberVotingState = new JSONObject(); - memberVotingState.put("member-name", actor.getMember()); - memberVotingState.put("voting", true); - votingStateArray.put(memberVotingState); - } - inputBlock.put("member-voting-state", votingStateArray); - votingInput.put("input", inputBlock); - log.debug("{}", votingInput); - // Change voting all shards - getRequestContent(httpProtocol + self.getNode() + ":" + port + "/restconf/operations/cluster-admin:change-member-voting-states-for-all-shards", HttpMethod.POST, votingInput.toString()); - } catch(IOException e) { - log.error("Changing voting", e); - outputBuilder.setMessage("Failover aborted. Failed to change voting."); - outputBuilder.setStatus("500"); - return false; - } - return true; - } - - private void backupMdSal(ArrayList activeSite, String port) { - log.info("Backing up data..."); - try { - log.info("Scheduling backup for: {}", activeSite.get(0).getNode()); - getRequestContent(httpProtocol + activeSite.get(0).getNode() + ":" + port + "/restconf/operations/data-export-import:schedule-export", HttpMethod.POST, "{ \"input\": { \"run-at\": \"30\" } }"); - } catch(IOException e) { - log.error("Error backing up MD-SAL", e); - } - for(ClusterActor actor : activeSite) { - try { - // Move data offsite - log.info("Backing up data for: {}", actor.getNode()); - getRequestContent(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/daexim-offsite-backup:backup-data", HttpMethod.POST); - } catch(IOException e) { - log.error("Error backing up data.", e); - } - } - } - - private ListenableFuture> buildClusterHealthOutput(String statusCode) { - ClusterHealthOutputBuilder outputBuilder = new ClusterHealthOutputBuilder(); - outputBuilder.setStatus(statusCode); - outputBuilder.setMembers((List) new ArrayList()); - int site1Health = 0; - int site2Health = 0; - - for(Map.Entry entry : memberMap.entrySet()) { - ClusterActor clusterActor = entry.getValue(); - if(clusterActor.isUp() && !clusterActor.isUnreachable()) { - if(ClusterActor.SITE_1.equals(clusterActor.getSite())) - site1Health++; - else if(ClusterActor.SITE_2.equals(clusterActor.getSite())) - site2Health++; - } - outputBuilder.getMembers().add(new MemberBuilder(clusterActor).build()); - } - if(siteConfiguration == SiteConfiguration.SOLO) { - outputBuilder.setSite1Health(HEALTHY); - } - else if(site1Health > 1) { - outputBuilder.setSite1Health(HEALTHY); - } - else { - outputBuilder.setSite1Health(FAULTY); - } - - if(siteConfiguration == SiteConfiguration.GEO && site2Health > 1) { - outputBuilder.setSite2Health(HEALTHY); - } - else if(siteConfiguration == SiteConfiguration.GEO) { - outputBuilder.setSite2Health(FAULTY); - } - - RpcResult rpcResult = RpcResultBuilder.status(true).withResult(outputBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - private ListenableFuture> buildSiteHealthOutput(String statusCode, String adminHealth, String databaseHealth) { - SiteHealthOutputBuilder outputBuilder = new SiteHealthOutputBuilder(); - outputBuilder.setStatus(statusCode); - outputBuilder.setSites((List) new ArrayList()); - - if(siteConfiguration != SiteConfiguration.GEO) { - int healthyODLs = 0; - SitesBuilder builder = new SitesBuilder(); - for(Map.Entry entry : memberMap.entrySet()) { - ClusterActor clusterActor = entry.getValue(); - if(clusterActor.isUp() && !clusterActor.isUnreachable()) { - healthyODLs++; - } - } - if(siteConfiguration != SiteConfiguration.SOLO) { - builder.setHealth(HEALTHY); - builder.setRole("ACTIVE"); - builder.setId(siteIdentifier); - } - else { - builder = getSitesBuilder(healthyODLs, true, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); - } - outputBuilder.getSites().add(builder.build()); - } - else { - int site1HealthyODLs = 0; - int site2HealthyODLs = 0; - boolean site1Voting = false; - boolean site2Voting = false; - boolean performedCrossSiteHealthCheck = false; - boolean crossSiteAdminHealthy = false; - boolean crossSiteDbHealthy = false; - String crossSiteIdentifier = "UNKNOWN_SITE"; - String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL)) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL) : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP); - if(isSite1()) { - // Make calls over to site 2 healthchecks - for(Map.Entry entry : memberMap.entrySet()) { - ClusterActor clusterActor = entry.getValue(); - if(clusterActor.isUp() && !clusterActor.isUnreachable()) { - if(ClusterActor.SITE_1.equals(clusterActor.getSite())) { - site1HealthyODLs++; - if(clusterActor.isVoting()) { - site1Voting = true; - } - } - else { - site2HealthyODLs++; - if(clusterActor.isVoting()) { - site2Voting = true; - } - if(!performedCrossSiteHealthCheck) { - try { - String content = getRequestContent(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:site-identifier", HttpMethod.POST); - crossSiteIdentifier = new JSONObject(content).getJSONObject("output").getString("id"); - crossSiteDbHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:database-health"); - crossSiteAdminHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:admin-health"); - performedCrossSiteHealthCheck = true; - } catch(Exception e) { - log.info("Cannot get site identifier from {}", clusterActor.getNode()); - log.error("Site Health Error", e); - } - } - } - } - } - SitesBuilder builder = getSitesBuilder(site1HealthyODLs, site1Voting, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); - outputBuilder.getSites().add(builder.build()); - builder = getSitesBuilder(site2HealthyODLs, site2Voting, crossSiteAdminHealthy, crossSiteDbHealthy, crossSiteIdentifier); - outputBuilder.getSites().add(builder.build()); - } - else { - // Make calls over to site 1 healthchecks - for(Map.Entry entry : memberMap.entrySet()) { - ClusterActor clusterActor = entry.getValue(); - if(clusterActor.isUp() && !clusterActor.isUnreachable()) { - if(ClusterActor.SITE_1.equals(clusterActor.getSite())) { - site1HealthyODLs++; - if(clusterActor.isVoting()) { - site1Voting = true; - } - if(!performedCrossSiteHealthCheck) { - try { - String content = getRequestContent(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:site-identifier", HttpMethod.POST); - crossSiteIdentifier = new JSONObject(content).getJSONObject("output").getString("id"); - crossSiteDbHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:database-health"); - crossSiteAdminHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:admin-health"); - performedCrossSiteHealthCheck = true; - } catch(Exception e) { - log.info("Cannot get site identifier from {}", clusterActor.getNode()); - log.error("Site Health Error", e); - } - } - } - else { - site2HealthyODLs++; - if(clusterActor.isVoting()) { - site2Voting = true; - } - } - } - } - // Build Output - SitesBuilder builder = getSitesBuilder(site1HealthyODLs, site1Voting, crossSiteAdminHealthy, crossSiteDbHealthy, crossSiteIdentifier); - outputBuilder.getSites().add(builder.build()); - builder = getSitesBuilder(site2HealthyODLs, site2Voting, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); - outputBuilder.getSites().add(builder.build()); - } - } - - RpcResult rpcResult = RpcResultBuilder.status(true).withResult(outputBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - private SitesBuilder getSitesBuilder(int siteHealthyODLs, boolean siteVoting, boolean adminHealthy, boolean dbHealthy, String siteIdentifier) { - SitesBuilder builder = new SitesBuilder(); - if(siteHealthyODLs > 1) { - builder.setHealth(HEALTHY); - } - else { - log.warn("{} Healthy ODLs: {}", siteIdentifier, siteHealthyODLs); - builder.setHealth(FAULTY); - } - if(!adminHealthy) { - log.warn("{} Admin Health: {}", siteIdentifier, FAULTY); - builder.setHealth(FAULTY); - } - if(!dbHealthy) { - log.warn("{} Database Health: {}", siteIdentifier, FAULTY); - builder.setHealth(FAULTY); - } - if(siteVoting) { - builder.setRole("ACTIVE"); - } - else { - builder.setRole("STANDBY"); - } - builder.setId(siteIdentifier); - return builder; - } - - private boolean isSite1() { - int memberNumber = Integer.parseInt(member.split("-")[1]); - boolean isSite1 = memberNumber < 4; - log.info("isSite1(): {}", isSite1); - return isSite1; - } - - private void parseSeedNodes(String line) { - memberMap = new HashMap<>(); - line = line.substring(line.indexOf("[\""), line.indexOf(']')); - String[] splits = line.split(","); - - for(int ndx = 0; ndx < splits.length; ndx++) { - String nodeName = splits[ndx]; - int delimLocation = nodeName.indexOf('@'); - String port = nodeName.substring(splits[ndx].indexOf(':', delimLocation) + 1, splits[ndx].indexOf('"', splits[ndx].indexOf(':'))); - splits[ndx] = nodeName.substring(delimLocation + 1, splits[ndx].indexOf(':', delimLocation)); - log.info("Adding node: {}:{}", splits[ndx], port); - ClusterActor clusterActor = new ClusterActor(); - clusterActor.setNode(splits[ndx]); - clusterActor.setAkkaPort(port); - clusterActor.setMember("member-" + (ndx + 1)); - if(ndx < 3) { - clusterActor.setSite(ClusterActor.SITE_1); - } - else { - clusterActor.setSite(ClusterActor.SITE_2); - } - - if(member.equals(clusterActor.getMember())) { - self = clusterActor; - } - memberMap.put(clusterActor.getNode(), clusterActor); - log.info("{}", clusterActor); - } - - if(memberMap.size() == 1) { - log.info("1 member found. This is a solo environment."); - siteConfiguration = SiteConfiguration.SOLO; - } - else if(memberMap.size() == 3) { - log.info("This is a single site."); - siteConfiguration = SiteConfiguration.SINGLE; - } - else if(memberMap.size() == 6) { - log.info("This is a georedundant site."); - siteConfiguration = SiteConfiguration.GEO; - } - } - - private void getMemberStatus(ClusterActor clusterActor) throws IOException { - log.info("Getting member status for {}", clusterActor.getNode()); - String content = getRequestContent(httpProtocol + clusterActor.getNode() + jolokiaClusterPath, HttpMethod.GET); - try { - JSONObject responseJson = new JSONObject(content); - JSONObject responseValue = responseJson.getJSONObject(VALUE); - clusterActor.setUp("Up".equals(responseValue.getString("MemberStatus"))); - clusterActor.setUnreachable(false); - } catch(JSONException e) { - log.error("Error parsing response from {}", clusterActor.getNode(), e); - clusterActor.setUp(false); - clusterActor.setUnreachable(true); - } - } - - private void getShardStatus(ClusterActor clusterActor) throws IOException { - log.info("Getting shard status for {}", clusterActor.getNode()); - String content = getRequestContent(httpProtocol + clusterActor.getNode() + shardManagerPath, HttpMethod.GET); - try { - JSONObject responseValue = new JSONObject(content).getJSONObject(VALUE); - JSONArray shardList = responseValue.getJSONArray("LocalShards"); - - String pattern = "-config$"; - Pattern r = Pattern.compile(pattern); - Matcher m; - for(int ndx = 0; ndx < shardList.length(); ndx++) { - String configShardName = shardList.getString(ndx); - m = r.matcher(configShardName); - String operationalShardName = m.replaceFirst("-operational"); - String shardConfigPath = String.format(shardPathTemplate, configShardName); - String shardOperationalPath = String.format(shardPathTemplate, operationalShardName).replace("Config", "Operational"); - extractShardInfo(clusterActor, configShardName, shardConfigPath); - extractShardInfo(clusterActor, operationalShardName, shardOperationalPath); - } - } catch(JSONException e) { - log.error("Error parsing response from " + clusterActor.getNode(), e); - } - } - - private void extractShardInfo(ClusterActor clusterActor, String shardName, String shardPath) throws IOException { - log.info("Extracting shard info for {}", shardName); - log.debug("Pulling config info for {} from: {}", shardName, shardPath); - String content = getRequestContent(httpProtocol + clusterActor.getNode() + shardPath, HttpMethod.GET); - log.debug("Response: {}", content); - - try { - JSONObject shardValue = new JSONObject(content).getJSONObject(VALUE); - clusterActor.setVoting(shardValue.getBoolean("Voting")); - if(shardValue.getString("PeerAddresses").length() > 0) { - clusterActor.getReplicaShards().add(shardName); - if(shardValue.getString("Leader").startsWith(clusterActor.getMember())) { - clusterActor.getShardLeader().add(shardName); - } - } - else { - clusterActor.getNonReplicaShards().add(shardName); - } - JSONArray followerInfo = shardValue.getJSONArray("FollowerInfo"); - for(int followerNdx = 0; followerNdx < followerInfo.length(); followerNdx++) { - int commitIndex = shardValue.getInt("CommitIndex"); - int matchIndex = followerInfo.getJSONObject(followerNdx).getInt("matchIndex"); - if(commitIndex != -1 && matchIndex != -1) { - int commitsBehind = commitIndex - matchIndex; - clusterActor.getCommits().put(followerInfo.getJSONObject(followerNdx).getString("id"), commitsBehind); - } - } - } catch(JSONException e) { - log.error("Error parsing response from " + clusterActor.getNode(), e); - } - } - - private void getControllerHealth() { - for(Map.Entry entry : memberMap.entrySet()) { - ClusterActor clusterActor = entry.getValue(); - String key = entry.getKey(); - try { - // First flush out the old values - clusterActor.flush(); - log.info("Gathering info for {}", clusterActor.getNode()); - getMemberStatus(clusterActor); - getShardStatus(clusterActor); - log.info("MemberInfo:\n{}", clusterActor); - } catch(IOException e) { - log.error("Connection Error", e); - memberMap.get(key).setUnreachable(true); - memberMap.get(key).setUp(false); - log.info("MemberInfo:\n{}", memberMap.get(key)); - } - } - } - - private void modifyIpTables(IpTables task, Object[] nodeInfo) { - log.info("Modifying IPTables rules..."); - if(task == IpTables.ADD) { - for(Object node : nodeInfo) { - org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo n = - (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo) node; - log.info("Isolating {}", n.getNode()); - executeCommand(String.format("sudo /sbin/iptables -A INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); - executeCommand(String.format("sudo /sbin/iptables -A OUTPUT -p tcp --destination-port %s -j DROP -s %s", n.getPort(), n.getNode())); - } - - } else if(task == IpTables.DELETE) { - for(Object node : nodeInfo) { - org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo n = - (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo) node; - log.info("De-isolating {}", n.getNode()); - executeCommand(String.format("sudo /sbin/iptables -D INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); - executeCommand(String.format("sudo /sbin/iptables -D OUTPUT -p tcp --destination-port %s -j DROP -s %s", n.getPort(), n.getNode())); - } - - } - executeCommand("sudo /sbin/iptables -L"); - } - - private void executeCommand(String command) { - log.info("Executing command: {}", command); - String[] cmd = command.split(" "); - try { - Process p = Runtime.getRuntime().exec(cmd); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String inputLine; - StringBuilder content = new StringBuilder(); - while((inputLine = bufferedReader.readLine()) != null) { - content.append(inputLine); - } - bufferedReader.close(); - log.info("{}", content); - } catch(IOException e) { - log.error("Error executing command", e); - } - } - - private boolean crossSiteHealthRequest(String path) throws IOException { - String content = getRequestContent(path, HttpMethod.POST); - try { - JSONObject responseJson = new JSONObject(content); - JSONObject responseValue = responseJson.getJSONObject(VALUE); - return HEALTHY.equals(responseValue.getString("health")); - } catch(JSONException e) { - log.error("Error parsing JSON", e); - throw new IOException(); - } - } - - private String getAdminHealth() { - String protocol = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? "https://" : "http://"; - String port = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? properties.getProperty(PropertyKeys.ADM_PORT_SSL) : properties.getProperty(PropertyKeys.ADM_PORT_HTTP); - String path = protocol + properties.getProperty(PropertyKeys.ADM_FQDN) + ":" + port + properties.getProperty(PropertyKeys.ADM_HEALTHCHECK); - log.info("Requesting healthcheck from {}", path); - try { - int response = getRequestStatus(path, HttpMethod.GET); - log.info("Response: {}", response); - if(response == 200) - return HEALTHY; - return FAULTY; - } catch(IOException e) { - log.error("Problem getting ADM health.", e); - return FAULTY; - } - } - - private String getDatabaseHealth() { - log.info("Determining database health..."); - try { - log.info("DBLib isActive(): {}", dbLib.isActive()); - log.info("DBLib isReadOnly(): {}", dbLib.getConnection().isReadOnly()); - log.info("DBLib isClosed(): {}", dbLib.getConnection().isClosed()); - if(!dbLib.isActive() || dbLib.getConnection().isClosed() || dbLib.getConnection().isReadOnly()) { - log.warn("Database is FAULTY"); - return FAULTY; - } - log.info("Database is HEALTHY"); - } catch(SQLException e) { - log.error("Database is FAULTY"); - log.error("Error", e); - return FAULTY; - } - - return HEALTHY; - } - - private String getRequestContent(String path, HttpMethod method) throws IOException { - return getRequestContent(path, method, null); - } - - private String getRequestContent(String path, HttpMethod method, String input) throws IOException { - HttpURLConnection connection = getConnection(path); - connection.setRequestMethod(method.getMethod()); - connection.setDoInput(true); - - if(input != null) { - sendPayload(input, connection); - } - - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - String inputLine; - StringBuilder content = new StringBuilder(); - while((inputLine = bufferedReader.readLine()) != null) { - content.append(inputLine); - } - bufferedReader.close(); - connection.disconnect(); - return content.toString(); - } - - private int getRequestStatus(String path, HttpMethod method) throws IOException { - return getRequestStatus(path, method, null); - } - - private int getRequestStatus(String path, HttpMethod method, String input) throws IOException { - HttpURLConnection connection = getConnection(path); - connection.setRequestMethod(method.getMethod()); - connection.setDoInput(true); - - if(input != null) { - sendPayload(input, connection); - } - int response = connection.getResponseCode(); - log.info("Received {} response code from {}", response, path); - connection.disconnect(); - return response; - } - - private void sendPayload(String input, HttpURLConnection connection) throws IOException { - byte[] out = input.getBytes(StandardCharsets.UTF_8); - int length = out.length; - - connection.setFixedLengthStreamingMode(length); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setDoOutput(true); - connection.connect(); - try(OutputStream os = connection.getOutputStream()) { - os.write(out); - } - } - - private HttpURLConnection getConnection(String host) throws IOException { - log.info("Getting connection to: {}", host); - URL url = new URL(host); - String auth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(credentials.getBytes()); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.addRequestProperty("Authorization", auth); - connection.setRequestProperty("Connection", "keep-alive"); - connection.setRequestProperty("Proxy-Connection", "keep-alive"); - return connection; - } - - private enum IpTables { - ADD, - DELETE - } - - private enum SiteConfiguration { - SOLO, - SINGLE, - GEO - } - - private enum HttpMethod { - GET("GET"), - POST("POST"); - - private String method; - HttpMethod(String method) { - this.method = method; - } - public String getMethod() { - return method; - } - } - - private class PropertyKeys { - static final String SITE_IDENTIFIER = "site.identifier"; - static final String CONTROLLER_USE_SSL = "controller.useSsl"; - static final String CONTROLLER_PORT_SSL = "controller.port.ssl"; - static final String CONTROLLER_PORT_HTTP = "controller.port.http"; - static final String CONTROLLER_PORT_AKKA = "controller.port.akka"; - static final String CONTROLLER_CREDENTIALS = "controller.credentials"; - static final String AKKA_CONF_LOCATION = "akka.conf.location"; - static final String MBEAN_CLUSTER = "mbean.cluster"; - static final String MBEAN_SHARD_MANAGER = "mbean.shardManager"; - static final String MBEAN_SHARD_CONFIG = "mbean.shard.config"; - static final String ADM_USE_SSL = "adm.useSsl"; - static final String ADM_PORT_SSL = "adm.port.ssl"; - static final String ADM_PORT_HTTP = "adm.port.http"; - static final String ADM_FQDN = "adm.fqdn"; - static final String ADM_HEALTHCHECK= "adm.healthcheck"; - } -} \ No newline at end of file diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/ClusterActor.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/ClusterActor.java deleted file mode 100755 index 1c8faac0..00000000 --- a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/ClusterActor.java +++ /dev/null @@ -1,215 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * Copyright (C) 2018 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.plugins.data; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ClusterActor { - private String node; - private String member; - private String site; - private String akkaPort; - private boolean voting; - private boolean up; - private boolean unreachable; - private ArrayList shardLeader; - private ArrayList replicaShards; - private ArrayList nonReplicaShards; - private HashMap commits; - - public static final String SITE_1 = "Site 1"; - public static final String SITE_2 = "Site 2"; - - public ClusterActor() { - node = ""; - member = ""; - site = ""; - voting = false; - up = false; - unreachable = false; - shardLeader = new ArrayList<>(); - replicaShards = new ArrayList<>(); - nonReplicaShards = new ArrayList<>(); - commits = new HashMap<>(); - } - - public String getNode() { - return node; - } - - public void setNode(String node) { - this.node = node; - } - - public String getMember() { - return member; - } - - public void setMember(String member) { - this.member = member; - } - - public String getSite() { - return site; - } - - public void setSite(String site) { - this.site = site; - } - - public String getAkkaPort() { - return akkaPort; - } - - public void setAkkaPort(String akkaPort) { - this.akkaPort = akkaPort; - } - - public boolean isVoting() { - return voting; - } - - public void setVoting(boolean voting) { - this.voting = voting; - } - - public boolean isUp() { - return up; - } - - public void setUp(boolean up) { - this.up = up; - } - - public boolean isUnreachable() { - return unreachable; - } - - public void setUnreachable(boolean unreachable) { - this.unreachable = unreachable; - } - - public List getShardLeader() { - return shardLeader; - } - - public void setShardLeader(List shardLeader) { - this.shardLeader = (ArrayList) shardLeader; - } - - public List getReplicaShards() { - return replicaShards; - } - - public void setReplicaShards(List replicaShards) { - this.replicaShards = (ArrayList) replicaShards; - } - - public List getNonReplicaShards() { - return nonReplicaShards; - } - - public void setNonReplicaShards(List nonReplicaShards) { - this.nonReplicaShards = (ArrayList) nonReplicaShards; - } - - public Map getCommits() { - return commits; - } - - public void setCommits(Map commits) { - this.commits = (HashMap) commits; - } - - public void flush() { - shardLeader.clear(); - replicaShards.clear(); - nonReplicaShards.clear(); - commits.clear(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("[ "); - builder.append(this.member); - builder.append(" ] "); - - builder.append(this.node); - builder.append(":"); - builder.append(this.akkaPort); - builder.append(" is"); - if(up) - builder.append(" Up"); - else - builder.append(" Down"); - if(unreachable) - builder.append(" [ UNREACHABLE ]"); - - if(voting) - builder.append(" (Voting)"); - - builder.append("\n"); - - for(String l : this.shardLeader) { - builder.append("\tLeader: "); - builder.append(l); - builder.append("\n"); - } - - for(String r : this.replicaShards) { - builder.append("\tReplicating: "); - builder.append(r); - builder.append("\n"); - } - - for(String n : this.nonReplicaShards) { - builder.append("\tNot replicating: "); - builder.append(n); - builder.append("\n"); - } - - for(Map.Entry entry : commits.entrySet()) { - String key = entry.getKey(); - int value = entry.getValue(); - if(value > 0) { - builder.append("\t"); - builder.append(value); - builder.append(" commits ahead of "); - builder.append(key); - builder.append("\n"); - } - else if(value < 0) { - builder.append("\t"); - builder.append(value); - builder.append(" commits behind "); - builder.append(key); - builder.append("\n"); - } - } - - return builder.toString(); - } -} diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/MemberBuilder.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/MemberBuilder.java deleted file mode 100755 index 96dfa64a..00000000 --- a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/data/MemberBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * openECOMP : SDN-C - * ================================================================================ - * Copyright (C) 2018 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.plugins.data; - -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.cluster.health.output.MembersBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.CommitStatusBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.ReplicasBuilder; -import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.LeaderBuilder; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class MemberBuilder extends MembersBuilder { - public MemberBuilder(ClusterActor actor) { - super(); - this.setAddress(actor.getNode()); - this.setRole(actor.getMember()); - this.setVoting(actor.isVoting()); - this.setUp(actor.isUp()); - this.setUnreachable(actor.isUnreachable()); - populateReplicas(actor.getReplicaShards()); - populateCommits(actor.getCommits()); - populateLeader(actor.getShardLeader()); - } - - private void populateLeader(List shardLeader) { - LeaderBuilder builder; - this.setLeader(new ArrayList<>()); - for(String leader : shardLeader) { - builder = new LeaderBuilder(); - builder.setShard(leader); - this.getLeader().add(builder.build()); - } - } - - private void populateCommits(Map commits) { - CommitStatusBuilder builder; - this.setCommitStatus(new ArrayList<>()); - for(Map.Entry entry : commits.entrySet()) { - String key = entry.getKey(); - Integer value = entry.getValue(); - if(value != 0) { - builder = new CommitStatusBuilder(); - builder.setShard(key); - builder.setDelta(value); - this.getCommitStatus().add(builder.build()); - } - } - } - - private void populateReplicas(List replicaShards) { - ReplicasBuilder builder; - this.setReplicas(new ArrayList<>()); - for(String shard : replicaShards) { - builder = new ReplicasBuilder(); - builder.setShard(shard); - this.getReplicas().add(builder.build()); - } - } -} diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java new file mode 100755 index 00000000..4e121e34 --- /dev/null +++ b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java @@ -0,0 +1,965 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 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.plugins.grtoolkit; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.MemberBuilder; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +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.plugins.gr.toolkit.rev180926.AdminHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.GrToolkitService; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Member; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Site; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.site.health.output.SitesBuilder; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GrToolkitProvider implements AutoCloseable, GrToolkitService, DataTreeChangeListener { + private static final String APP_NAME = "gr-toolkit"; + private static final String PROPERTIES_FILE = System.getenv("SDNC_CONFIG_DIR") + "/gr-toolkit.properties"; + private static final String HEALTHY = "HEALTHY"; + private static final String FAULTY = "FAULTY"; + private static final String VALUE = "value"; + private String akkaConfig; + private String jolokiaClusterPath; + private String shardManagerPath; + private String shardPathTemplate; + private String credentials; + private String httpProtocol; + private String siteIdentifier = System.getenv("SITE_NAME"); + private final Logger log = LoggerFactory.getLogger(GrToolkitProvider.class); + private final ExecutorService executor; + protected DataBroker dataBroker; + protected NotificationPublishService notificationService; + protected RpcProviderRegistry rpcRegistry; + protected BindingAwareBroker.RpcRegistration rpcRegistration; + protected DbLibService dbLib; + private String member; + private ClusterActor self; + private HashMap memberMap; + private SiteConfiguration siteConfiguration; + private Properties properties; + private DistributedDataStoreInterface configDatastore; + public GrToolkitProvider(DataBroker dataBroker, + NotificationPublishService notificationProviderService, + RpcProviderRegistry rpcProviderRegistry, + DistributedDataStoreInterface configDatastore, + DbLibService dbLibService) { + this.log.info("Creating provider for {}", APP_NAME); + this.executor = Executors.newFixedThreadPool(1); + this.dataBroker = dataBroker; + this.notificationService = notificationProviderService; + this.rpcRegistry = rpcProviderRegistry; + this.configDatastore = configDatastore; + this.dbLib = dbLibService; + initialize(); + } + + private void initialize() { + log.info("Initializing provider for {}", APP_NAME); + // Create the top level containers + createContainers(); + setProperties(); + defineMembers(); + + rpcRegistration = rpcRegistry.addRpcImplementation(GrToolkitService.class, this); + log.info("Initialization complete for {}", APP_NAME); + } + + private void setProperties() { + log.info("Loading properties from {}", PROPERTIES_FILE); + properties = new Properties(); + File propertiesFile = new File(PROPERTIES_FILE); + if(!propertiesFile.exists()) { + log.warn("Properties file not found."); + return; + } + try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) { + properties.load(fileInputStream); + if(!properties.containsKey(PropertyKeys.SITE_IDENTIFIER)) { + properties.put(PropertyKeys.SITE_IDENTIFIER, "Unknown Site"); + } + String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL).trim() : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP).trim(); + httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://"; + akkaConfig = properties.getProperty(PropertyKeys.AKKA_CONF_LOCATION).trim(); + jolokiaClusterPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_CLUSTER).trim(); + shardManagerPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_MANAGER).trim(); + shardPathTemplate = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_CONFIG).trim(); + if(siteIdentifier == null || siteIdentifier.isEmpty()) { + siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim(); + } + credentials = properties.getProperty(PropertyKeys.CONTROLLER_CREDENTIALS).trim(); + log.info("Loaded properties."); + } catch(IOException e) { + log.error("Error loading properties.", e); + } + } + + private void defineMembers() { + member = configDatastore.getActorContext().getCurrentMemberName().getName(); + log.info("Cluster member: {}", member); + + log.info("Parsing akka.conf for cluster memberMap..."); + try { + File akkaConfigFile = new File(this.akkaConfig); + try(FileReader fileReader = new FileReader(akkaConfigFile); + BufferedReader bufferedReader = new BufferedReader(fileReader)) { + String line; + while((line = bufferedReader.readLine()) != null) { + if(line.contains("seed-nodes =")) { + parseSeedNodes(line); + break; + } + } + } + } catch(IOException e) { + log.error("Couldn't load akka", e); + } catch(NullPointerException e) { + log.error("akkaConfig is null. Check properties file and restart {} bundle.", APP_NAME); + } + log.info("self:\n{}", self); + } + + private void createContainers() { + // Replace with MD-SAL write for FailoverStatus + } + + protected void initializeChild() { + // Override if you have custom initialization intelligence + } + + @Override + public void close() throws Exception { + log.info("Closing provider for {}", APP_NAME); + executor.shutdown(); + rpcRegistration.close(); + log.info("Successfully closed provider for {}", APP_NAME); + } + + @Override + public void onDataTreeChanged(@Nonnull Collection changes) { + log.info("onDataTreeChanged() called. but there is no change here"); + } + + @Override + public ListenableFuture> clusterHealth(ClusterHealthInput input) { + log.info("{}:cluster-health invoked.", APP_NAME); + getControllerHealth(); + return buildClusterHealthOutput("200"); + } + + @Override + public ListenableFuture> siteHealth(SiteHealthInput input) { + log.info("{}:site-health invoked.", APP_NAME); + getControllerHealth(); + return buildSiteHealthOutput("200", getAdminHealth(), getDatabaseHealth()); + } + + @Override + public ListenableFuture> databaseHealth(DatabaseHealthInput input) { + log.info("{}:database-health invoked.", APP_NAME); + DatabaseHealthOutputBuilder outputBuilder = new DatabaseHealthOutputBuilder(); + outputBuilder.setStatus("200"); + outputBuilder.setHealth(getDatabaseHealth()); + + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + @Override + public ListenableFuture> adminHealth(AdminHealthInput input) { + log.info("{}:admin-health invoked.", APP_NAME); + AdminHealthOutputBuilder outputBuilder = new AdminHealthOutputBuilder(); + outputBuilder.setStatus("200"); + outputBuilder.setHealth(getAdminHealth()); + + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + @Override + public ListenableFuture> haltAkkaTraffic(HaltAkkaTrafficInput input) { + log.info("{}:halt-akka-traffic invoked.", APP_NAME); + HaltAkkaTrafficOutputBuilder outputBuilder = new HaltAkkaTrafficOutputBuilder(); + outputBuilder.setStatus("200"); + modifyIpTables(IpTables.ADD, input.getNodeInfo().toArray()); + + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + @Override + public ListenableFuture> resumeAkkaTraffic(ResumeAkkaTrafficInput input) { + log.info("{}:resume-akka-traffic invoked.", APP_NAME); + ResumeAkkaTrafficOutputBuilder outputBuilder = new ResumeAkkaTrafficOutputBuilder(); + outputBuilder.setStatus("200"); + modifyIpTables(IpTables.DELETE, input.getNodeInfo().toArray()); + + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + @Override + public ListenableFuture> siteIdentifier(SiteIdentifierInput input) { + log.info("{}:site-identifier invoked.", APP_NAME); + SiteIdentifierOutputBuilder outputBuilder = new SiteIdentifierOutputBuilder(); + outputBuilder.setStatus("200"); + outputBuilder.setId(siteIdentifier); + + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + @Override + public ListenableFuture> failover(FailoverInput input) { + log.info("{}:failover invoked.", APP_NAME); + FailoverOutputBuilder outputBuilder = new FailoverOutputBuilder(); + if(siteConfiguration != SiteConfiguration.GEO) { + log.info("Cannot failover non-GEO site."); + outputBuilder.setMessage("Failover aborted. This is not a GEO configuration."); + outputBuilder.setStatus("400"); + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + ArrayList activeSite = new ArrayList<>(); + ArrayList standbySite = new ArrayList<>(); + + log.info("Performing preliminary cluster health check..."); + // Necessary to populate all member info. Health is not used for judgement calls. + getControllerHealth(); + + log.info("Determining active site..."); + for(Map.Entry entry : memberMap.entrySet()) { + String key = entry.getKey(); + ClusterActor clusterActor = entry.getValue(); + if(clusterActor.isVoting()) { + activeSite.add(clusterActor); + log.debug("Active Site member: {}", key); + } + else { + standbySite.add(clusterActor); + log.debug("Standby Site member: {}", key); + } + } + + String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL)) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL) : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP); + + if(Boolean.parseBoolean(input.getBackupData())) { + backupMdSal(activeSite, port); + } + + if(!changeClusterVoting(outputBuilder, activeSite, standbySite, port)) + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + + if(Boolean.parseBoolean(input.getIsolate())) { + isolateSiteFromCluster(activeSite, standbySite, port); + + if(Boolean.parseBoolean(input.getDownUnreachable())) { + downUnreachableNodes(activeSite, standbySite, port); + } + } + + log.info("{}:failover complete.", APP_NAME); + + outputBuilder.setMessage("Failover complete."); + outputBuilder.setStatus("200"); + return Futures.immediateFuture(RpcResultBuilder.status(true).withResult(outputBuilder.build()).build()); + } + + private void isolateSiteFromCluster(ArrayList activeSite, ArrayList standbySite, String port) { + log.info("Halting Akka traffic..."); + for(ClusterActor actor : standbySite) { + try { + log.info("Halting Akka traffic for: {}", actor.getNode()); + // Build JSON with activeSite actor Node and actor AkkaPort + JSONObject akkaInput = new JSONObject(); + JSONObject inputBlock = new JSONObject(); + JSONArray votingStateArray = new JSONArray(); + JSONObject nodeInfo; + for(ClusterActor node : activeSite) { + nodeInfo = new JSONObject(); + nodeInfo.put("node", node.getNode()); + nodeInfo.put("port", node.getAkkaPort()); + votingStateArray.put(nodeInfo); + } + inputBlock.put("node-info", votingStateArray); + akkaInput.put("input", inputBlock); + getRequestContent(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:halt-akka-traffic", HttpMethod.POST, akkaInput.toString()); + } catch(IOException e) { + log.error("Could not halt Akka traffic for: " + actor.getNode(), e); + } + } + } + + private void downUnreachableNodes(ArrayList activeSite, ArrayList standbySite, String port) { + log.info("Setting site unreachable..."); + JSONObject jolokiaInput = new JSONObject(); + jolokiaInput.put("type", "EXEC"); + jolokiaInput.put("mbean", "akka:type=Cluster"); + jolokiaInput.put("operation", "down"); + JSONArray arguments = new JSONArray(); + for(ClusterActor actor : activeSite) { + // Build Jolokia input + // May need to change from akka port to actor.getAkkaPort() + arguments.put("akka.tcp://opendaylight-cluster-data@" + actor.getNode() + ":" + properties.getProperty(PropertyKeys.CONTROLLER_PORT_AKKA)); + } + jolokiaInput.put("arguments", arguments); + log.debug("{}", jolokiaInput); + try { + log.info("Setting nodes unreachable"); + getRequestContent(httpProtocol + standbySite.get(0).getNode() + ":" + port + "/jolokia", HttpMethod.POST, jolokiaInput.toString()); + } catch(IOException e) { + log.error("Error setting nodes unreachable", e); + } + } + + private boolean changeClusterVoting(FailoverOutputBuilder outputBuilder, ArrayList activeSite, ArrayList standbySite, String port) { + log.info("Changing voting for all shards to standby site..."); + try { + JSONObject votingInput = new JSONObject(); + JSONObject inputBlock = new JSONObject(); + JSONArray votingStateArray = new JSONArray(); + JSONObject memberVotingState; + for(ClusterActor actor : activeSite) { + memberVotingState = new JSONObject(); + memberVotingState.put("member-name", actor.getMember()); + memberVotingState.put("voting", false); + votingStateArray.put(memberVotingState); + } + for(ClusterActor actor : standbySite) { + memberVotingState = new JSONObject(); + memberVotingState.put("member-name", actor.getMember()); + memberVotingState.put("voting", true); + votingStateArray.put(memberVotingState); + } + inputBlock.put("member-voting-state", votingStateArray); + votingInput.put("input", inputBlock); + log.debug("{}", votingInput); + // Change voting all shards + getRequestContent(httpProtocol + self.getNode() + ":" + port + "/restconf/operations/cluster-admin:change-member-voting-states-for-all-shards", HttpMethod.POST, votingInput.toString()); + } catch(IOException e) { + log.error("Changing voting", e); + outputBuilder.setMessage("Failover aborted. Failed to change voting."); + outputBuilder.setStatus("500"); + return false; + } + return true; + } + + private void backupMdSal(ArrayList activeSite, String port) { + log.info("Backing up data..."); + try { + log.info("Scheduling backup for: {}", activeSite.get(0).getNode()); + getRequestContent(httpProtocol + activeSite.get(0).getNode() + ":" + port + "/restconf/operations/data-export-import:schedule-export", HttpMethod.POST, "{ \"input\": { \"run-at\": \"30\" } }"); + } catch(IOException e) { + log.error("Error backing up MD-SAL", e); + } + for(ClusterActor actor : activeSite) { + try { + // Move data offsite + log.info("Backing up data for: {}", actor.getNode()); + getRequestContent(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/daexim-offsite-backup:backup-data", HttpMethod.POST); + } catch(IOException e) { + log.error("Error backing up data.", e); + } + } + } + + private ListenableFuture> buildClusterHealthOutput(String statusCode) { + ClusterHealthOutputBuilder outputBuilder = new ClusterHealthOutputBuilder(); + outputBuilder.setStatus(statusCode); + outputBuilder.setMembers((List) new ArrayList()); + int site1Health = 0; + int site2Health = 0; + + for(Map.Entry entry : memberMap.entrySet()) { + ClusterActor clusterActor = entry.getValue(); + if(clusterActor.isUp() && !clusterActor.isUnreachable()) { + if(ClusterActor.SITE_1.equals(clusterActor.getSite())) + site1Health++; + else if(ClusterActor.SITE_2.equals(clusterActor.getSite())) + site2Health++; + } + outputBuilder.getMembers().add(new MemberBuilder(clusterActor).build()); + } + if(siteConfiguration == SiteConfiguration.SOLO) { + outputBuilder.setSite1Health(HEALTHY); + } + else if(site1Health > 1) { + outputBuilder.setSite1Health(HEALTHY); + } + else { + outputBuilder.setSite1Health(FAULTY); + } + + if(siteConfiguration == SiteConfiguration.GEO && site2Health > 1) { + outputBuilder.setSite2Health(HEALTHY); + } + else if(siteConfiguration == SiteConfiguration.GEO) { + outputBuilder.setSite2Health(FAULTY); + } + + RpcResult rpcResult = RpcResultBuilder.status(true).withResult(outputBuilder.build()).build(); + return Futures.immediateFuture(rpcResult); + } + + private ListenableFuture> buildSiteHealthOutput(String statusCode, String adminHealth, String databaseHealth) { + SiteHealthOutputBuilder outputBuilder = new SiteHealthOutputBuilder(); + outputBuilder.setStatus(statusCode); + outputBuilder.setSites((List) new ArrayList()); + + if(siteConfiguration != SiteConfiguration.GEO) { + int healthyODLs = 0; + SitesBuilder builder = new SitesBuilder(); + for(Map.Entry entry : memberMap.entrySet()) { + ClusterActor clusterActor = entry.getValue(); + if(clusterActor.isUp() && !clusterActor.isUnreachable()) { + healthyODLs++; + } + } + if(siteConfiguration != SiteConfiguration.SOLO) { + builder.setHealth(HEALTHY); + builder.setRole("ACTIVE"); + builder.setId(siteIdentifier); + } + else { + builder = getSitesBuilder(healthyODLs, true, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); + } + outputBuilder.getSites().add(builder.build()); + } + else { + int site1HealthyODLs = 0; + int site2HealthyODLs = 0; + boolean site1Voting = false; + boolean site2Voting = false; + boolean performedCrossSiteHealthCheck = false; + boolean crossSiteAdminHealthy = false; + boolean crossSiteDbHealthy = false; + String crossSiteIdentifier = "UNKNOWN_SITE"; + String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL)) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL) : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP); + if(isSite1()) { + // Make calls over to site 2 healthchecks + for(Map.Entry entry : memberMap.entrySet()) { + ClusterActor clusterActor = entry.getValue(); + if(clusterActor.isUp() && !clusterActor.isUnreachable()) { + if(ClusterActor.SITE_1.equals(clusterActor.getSite())) { + site1HealthyODLs++; + if(clusterActor.isVoting()) { + site1Voting = true; + } + } + else { + site2HealthyODLs++; + if(clusterActor.isVoting()) { + site2Voting = true; + } + if(!performedCrossSiteHealthCheck) { + try { + String content = getRequestContent(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:site-identifier", HttpMethod.POST); + crossSiteIdentifier = new JSONObject(content).getJSONObject("output").getString("id"); + crossSiteDbHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:database-health"); + crossSiteAdminHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:admin-health"); + performedCrossSiteHealthCheck = true; + } catch(Exception e) { + log.info("Cannot get site identifier from {}", clusterActor.getNode()); + log.error("Site Health Error", e); + } + } + } + } + } + SitesBuilder builder = getSitesBuilder(site1HealthyODLs, site1Voting, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); + outputBuilder.getSites().add(builder.build()); + builder = getSitesBuilder(site2HealthyODLs, site2Voting, crossSiteAdminHealthy, crossSiteDbHealthy, crossSiteIdentifier); + outputBuilder.getSites().add(builder.build()); + } + else { + // Make calls over to site 1 healthchecks + for(Map.Entry entry : memberMap.entrySet()) { + ClusterActor clusterActor = entry.getValue(); + if(clusterActor.isUp() && !clusterActor.isUnreachable()) { + if(ClusterActor.SITE_1.equals(clusterActor.getSite())) { + site1HealthyODLs++; + if(clusterActor.isVoting()) { + site1Voting = true; + } + if(!performedCrossSiteHealthCheck) { + try { + String content = getRequestContent(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:site-identifier", HttpMethod.POST); + crossSiteIdentifier = new JSONObject(content).getJSONObject("output").getString("id"); + crossSiteDbHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:database-health"); + crossSiteAdminHealthy = crossSiteHealthRequest(httpProtocol + clusterActor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:admin-health"); + performedCrossSiteHealthCheck = true; + } catch(Exception e) { + log.info("Cannot get site identifier from {}", clusterActor.getNode()); + log.error("Site Health Error", e); + } + } + } + else { + site2HealthyODLs++; + if(clusterActor.isVoting()) { + site2Voting = true; + } + } + } + } + // Build Output + SitesBuilder builder = getSitesBuilder(site1HealthyODLs, site1Voting, crossSiteAdminHealthy, crossSiteDbHealthy, crossSiteIdentifier); + outputBuilder.getSites().add(builder.build()); + builder = getSitesBuilder(site2HealthyODLs, site2Voting, HEALTHY.equals(adminHealth), HEALTHY.equals(databaseHealth), siteIdentifier); + outputBuilder.getSites().add(builder.build()); + } + } + + RpcResult rpcResult = RpcResultBuilder.status(true).withResult(outputBuilder.build()).build(); + return Futures.immediateFuture(rpcResult); + } + + private SitesBuilder getSitesBuilder(int siteHealthyODLs, boolean siteVoting, boolean adminHealthy, boolean dbHealthy, String siteIdentifier) { + SitesBuilder builder = new SitesBuilder(); + if(siteHealthyODLs > 1) { + builder.setHealth(HEALTHY); + } + else { + log.warn("{} Healthy ODLs: {}", siteIdentifier, siteHealthyODLs); + builder.setHealth(FAULTY); + } + if(!adminHealthy) { + log.warn("{} Admin Health: {}", siteIdentifier, FAULTY); + builder.setHealth(FAULTY); + } + if(!dbHealthy) { + log.warn("{} Database Health: {}", siteIdentifier, FAULTY); + builder.setHealth(FAULTY); + } + if(siteVoting) { + builder.setRole("ACTIVE"); + } + else { + builder.setRole("STANDBY"); + } + builder.setId(siteIdentifier); + return builder; + } + + private boolean isSite1() { + int memberNumber = Integer.parseInt(member.split("-")[1]); + boolean isSite1 = memberNumber < 4; + log.info("isSite1(): {}", isSite1); + return isSite1; + } + + private void parseSeedNodes(String line) { + memberMap = new HashMap<>(); + line = line.substring(line.indexOf("[\""), line.indexOf(']')); + String[] splits = line.split(","); + + for(int ndx = 0; ndx < splits.length; ndx++) { + String nodeName = splits[ndx]; + int delimLocation = nodeName.indexOf('@'); + String port = nodeName.substring(splits[ndx].indexOf(':', delimLocation) + 1, splits[ndx].indexOf('"', splits[ndx].indexOf(':'))); + splits[ndx] = nodeName.substring(delimLocation + 1, splits[ndx].indexOf(':', delimLocation)); + log.info("Adding node: {}:{}", splits[ndx], port); + ClusterActor clusterActor = new ClusterActor(); + clusterActor.setNode(splits[ndx]); + clusterActor.setAkkaPort(port); + clusterActor.setMember("member-" + (ndx + 1)); + if(ndx < 3) { + clusterActor.setSite(ClusterActor.SITE_1); + } + else { + clusterActor.setSite(ClusterActor.SITE_2); + } + + if(member.equals(clusterActor.getMember())) { + self = clusterActor; + } + memberMap.put(clusterActor.getNode(), clusterActor); + log.info("{}", clusterActor); + } + + if(memberMap.size() == 1) { + log.info("1 member found. This is a solo environment."); + siteConfiguration = SiteConfiguration.SOLO; + } + else if(memberMap.size() == 3) { + log.info("This is a single site."); + siteConfiguration = SiteConfiguration.SINGLE; + } + else if(memberMap.size() == 6) { + log.info("This is a georedundant site."); + siteConfiguration = SiteConfiguration.GEO; + } + } + + private void getMemberStatus(ClusterActor clusterActor) throws IOException { + log.info("Getting member status for {}", clusterActor.getNode()); + String content = getRequestContent(httpProtocol + clusterActor.getNode() + jolokiaClusterPath, HttpMethod.GET); + try { + JSONObject responseJson = new JSONObject(content); + JSONObject responseValue = responseJson.getJSONObject(VALUE); + clusterActor.setUp("Up".equals(responseValue.getString("MemberStatus"))); + clusterActor.setUnreachable(false); + } catch(JSONException e) { + log.error("Error parsing response from {}", clusterActor.getNode(), e); + clusterActor.setUp(false); + clusterActor.setUnreachable(true); + } + } + + private void getShardStatus(ClusterActor clusterActor) throws IOException { + log.info("Getting shard status for {}", clusterActor.getNode()); + String content = getRequestContent(httpProtocol + clusterActor.getNode() + shardManagerPath, HttpMethod.GET); + try { + JSONObject responseValue = new JSONObject(content).getJSONObject(VALUE); + JSONArray shardList = responseValue.getJSONArray("LocalShards"); + + String pattern = "-config$"; + Pattern r = Pattern.compile(pattern); + Matcher m; + for(int ndx = 0; ndx < shardList.length(); ndx++) { + String configShardName = shardList.getString(ndx); + m = r.matcher(configShardName); + String operationalShardName = m.replaceFirst("-operational"); + String shardConfigPath = String.format(shardPathTemplate, configShardName); + String shardOperationalPath = String.format(shardPathTemplate, operationalShardName).replace("Config", "Operational"); + extractShardInfo(clusterActor, configShardName, shardConfigPath); + extractShardInfo(clusterActor, operationalShardName, shardOperationalPath); + } + } catch(JSONException e) { + log.error("Error parsing response from " + clusterActor.getNode(), e); + } + } + + private void extractShardInfo(ClusterActor clusterActor, String shardName, String shardPath) throws IOException { + log.info("Extracting shard info for {}", shardName); + log.debug("Pulling config info for {} from: {}", shardName, shardPath); + String content = getRequestContent(httpProtocol + clusterActor.getNode() + shardPath, HttpMethod.GET); + log.debug("Response: {}", content); + + try { + JSONObject shardValue = new JSONObject(content).getJSONObject(VALUE); + clusterActor.setVoting(shardValue.getBoolean("Voting")); + if(shardValue.getString("PeerAddresses").length() > 0) { + clusterActor.getReplicaShards().add(shardName); + if(shardValue.getString("Leader").startsWith(clusterActor.getMember())) { + clusterActor.getShardLeader().add(shardName); + } + } + else { + clusterActor.getNonReplicaShards().add(shardName); + } + JSONArray followerInfo = shardValue.getJSONArray("FollowerInfo"); + for(int followerNdx = 0; followerNdx < followerInfo.length(); followerNdx++) { + int commitIndex = shardValue.getInt("CommitIndex"); + int matchIndex = followerInfo.getJSONObject(followerNdx).getInt("matchIndex"); + if(commitIndex != -1 && matchIndex != -1) { + int commitsBehind = commitIndex - matchIndex; + clusterActor.getCommits().put(followerInfo.getJSONObject(followerNdx).getString("id"), commitsBehind); + } + } + } catch(JSONException e) { + log.error("Error parsing response from " + clusterActor.getNode(), e); + } + } + + private void getControllerHealth() { + for(Map.Entry entry : memberMap.entrySet()) { + ClusterActor clusterActor = entry.getValue(); + String key = entry.getKey(); + try { + // First flush out the old values + clusterActor.flush(); + log.info("Gathering info for {}", clusterActor.getNode()); + getMemberStatus(clusterActor); + getShardStatus(clusterActor); + log.info("MemberInfo:\n{}", clusterActor); + } catch(IOException e) { + log.error("Connection Error", e); + memberMap.get(key).setUnreachable(true); + memberMap.get(key).setUp(false); + log.info("MemberInfo:\n{}", memberMap.get(key)); + } + } + } + + private void modifyIpTables(IpTables task, Object[] nodeInfo) { + log.info("Modifying IPTables rules..."); + if(task == IpTables.ADD) { + for(Object node : nodeInfo) { + org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo n = + (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo) node; + log.info("Isolating {}", n.getNode()); + executeCommand(String.format("sudo /sbin/iptables -A INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); + executeCommand(String.format("sudo /sbin/iptables -A OUTPUT -p tcp --destination-port %s -j DROP -s %s", n.getPort(), n.getNode())); + } + + } else if(task == IpTables.DELETE) { + for(Object node : nodeInfo) { + org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo n = + (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo) node; + log.info("De-isolating {}", n.getNode()); + executeCommand(String.format("sudo /sbin/iptables -D INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); + executeCommand(String.format("sudo /sbin/iptables -D OUTPUT -p tcp --destination-port %s -j DROP -s %s", n.getPort(), n.getNode())); + } + + } + executeCommand("sudo /sbin/iptables -L"); + } + + private void executeCommand(String command) { + log.info("Executing command: {}", command); + String[] cmd = command.split(" "); + try { + Process p = Runtime.getRuntime().exec(cmd); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + while((inputLine = bufferedReader.readLine()) != null) { + content.append(inputLine); + } + bufferedReader.close(); + log.info("{}", content); + } catch(IOException e) { + log.error("Error executing command", e); + } + } + + private boolean crossSiteHealthRequest(String path) throws IOException { + String content = getRequestContent(path, HttpMethod.POST); + try { + JSONObject responseJson = new JSONObject(content); + JSONObject responseValue = responseJson.getJSONObject(VALUE); + return HEALTHY.equals(responseValue.getString("health")); + } catch(JSONException e) { + log.error("Error parsing JSON", e); + throw new IOException(); + } + } + + private String getAdminHealth() { + String protocol = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? "https://" : "http://"; + String port = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? properties.getProperty(PropertyKeys.ADM_PORT_SSL) : properties.getProperty(PropertyKeys.ADM_PORT_HTTP); + String path = protocol + properties.getProperty(PropertyKeys.ADM_FQDN) + ":" + port + properties.getProperty(PropertyKeys.ADM_HEALTHCHECK); + log.info("Requesting healthcheck from {}", path); + try { + int response = getRequestStatus(path, HttpMethod.GET); + log.info("Response: {}", response); + if(response == 200) + return HEALTHY; + return FAULTY; + } catch(IOException e) { + log.error("Problem getting ADM health.", e); + return FAULTY; + } + } + + private String getDatabaseHealth() { + log.info("Determining database health..."); + try { + Connection connection = dbLib.getConnection(); + log.info("DBLib isActive(): {}", dbLib.isActive()); + log.info("DBLib isReadOnly(): {}", connection.isReadOnly()); + log.info("DBLib isClosed(): {}", connection.isClosed()); + if(!dbLib.isActive() || connection.isClosed() || connection.isReadOnly()) { + log.warn("Database is FAULTY"); + connection.close(); + return FAULTY; + } + connection.close(); + log.info("Database is HEALTHY"); + } catch(SQLException e) { + log.error("Database is FAULTY"); + log.error("Error", e); + return FAULTY; + } + + return HEALTHY; + } + + private String getRequestContent(String path, HttpMethod method) throws IOException { + return getRequestContent(path, method, null); + } + + private String getRequestContent(String path, HttpMethod method, String input) throws IOException { + HttpURLConnection connection = getConnection(path); + connection.setRequestMethod(method.getMethod()); + connection.setDoInput(true); + + if(input != null) { + sendPayload(input, connection); + } + + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + while((inputLine = bufferedReader.readLine()) != null) { + content.append(inputLine); + } + bufferedReader.close(); + connection.disconnect(); + return content.toString(); + } + + private int getRequestStatus(String path, HttpMethod method) throws IOException { + return getRequestStatus(path, method, null); + } + + private int getRequestStatus(String path, HttpMethod method, String input) throws IOException { + HttpURLConnection connection = getConnection(path); + connection.setRequestMethod(method.getMethod()); + connection.setDoInput(true); + + if(input != null) { + sendPayload(input, connection); + } + int response = connection.getResponseCode(); + log.info("Received {} response code from {}", response, path); + connection.disconnect(); + return response; + } + + private void sendPayload(String input, HttpURLConnection connection) throws IOException { + byte[] out = input.getBytes(StandardCharsets.UTF_8); + int length = out.length; + + connection.setFixedLengthStreamingMode(length); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + connection.connect(); + try(OutputStream os = connection.getOutputStream()) { + os.write(out); + } + } + + private HttpURLConnection getConnection(String host) throws IOException { + log.info("Getting connection to: {}", host); + URL url = new URL(host); + String auth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(credentials.getBytes()); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.addRequestProperty("Authorization", auth); + connection.setRequestProperty("Connection", "keep-alive"); + connection.setRequestProperty("Proxy-Connection", "keep-alive"); + return connection; + } + + enum IpTables { + ADD, + DELETE + } + + enum SiteConfiguration { + SOLO, + SINGLE, + GEO + } + + enum HttpMethod { + GET("GET"), + POST("POST"); + + private String method; + HttpMethod(String method) { + this.method = method; + } + public String getMethod() { + return method; + } + } + + class PropertyKeys { + static final String SITE_IDENTIFIER = "site.identifier"; + static final String CONTROLLER_USE_SSL = "controller.useSsl"; + static final String CONTROLLER_PORT_SSL = "controller.port.ssl"; + static final String CONTROLLER_PORT_HTTP = "controller.port.http"; + static final String CONTROLLER_PORT_AKKA = "controller.port.akka"; + static final String CONTROLLER_CREDENTIALS = "controller.credentials"; + static final String AKKA_CONF_LOCATION = "akka.conf.location"; + static final String MBEAN_CLUSTER = "mbean.cluster"; + static final String MBEAN_SHARD_MANAGER = "mbean.shardManager"; + static final String MBEAN_SHARD_CONFIG = "mbean.shard.config"; + static final String ADM_USE_SSL = "adm.useSsl"; + static final String ADM_PORT_SSL = "adm.port.ssl"; + static final String ADM_PORT_HTTP = "adm.port.http"; + static final String ADM_FQDN = "adm.fqdn"; + static final String ADM_HEALTHCHECK= "adm.healthcheck"; + } +} \ No newline at end of file diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java new file mode 100755 index 00000000..7cd503a9 --- /dev/null +++ b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java @@ -0,0 +1,215 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 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.plugins.grtoolkit.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ClusterActor { + private String node; + private String member; + private String site; + private String akkaPort; + private boolean voting; + private boolean up; + private boolean unreachable; + private ArrayList shardLeader; + private ArrayList replicaShards; + private ArrayList nonReplicaShards; + private HashMap commits; + + public static final String SITE_1 = "Site 1"; + public static final String SITE_2 = "Site 2"; + + public ClusterActor() { + node = ""; + member = ""; + site = ""; + voting = false; + up = false; + unreachable = false; + shardLeader = new ArrayList<>(); + replicaShards = new ArrayList<>(); + nonReplicaShards = new ArrayList<>(); + commits = new HashMap<>(); + } + + public String getNode() { + return node; + } + + public void setNode(String node) { + this.node = node; + } + + public String getMember() { + return member; + } + + public void setMember(String member) { + this.member = member; + } + + public String getSite() { + return site; + } + + public void setSite(String site) { + this.site = site; + } + + public String getAkkaPort() { + return akkaPort; + } + + public void setAkkaPort(String akkaPort) { + this.akkaPort = akkaPort; + } + + public boolean isVoting() { + return voting; + } + + public void setVoting(boolean voting) { + this.voting = voting; + } + + public boolean isUp() { + return up; + } + + public void setUp(boolean up) { + this.up = up; + } + + public boolean isUnreachable() { + return unreachable; + } + + public void setUnreachable(boolean unreachable) { + this.unreachable = unreachable; + } + + public List getShardLeader() { + return shardLeader; + } + + public void setShardLeader(List shardLeader) { + this.shardLeader = (ArrayList) shardLeader; + } + + public List getReplicaShards() { + return replicaShards; + } + + public void setReplicaShards(List replicaShards) { + this.replicaShards = (ArrayList) replicaShards; + } + + public List getNonReplicaShards() { + return nonReplicaShards; + } + + public void setNonReplicaShards(List nonReplicaShards) { + this.nonReplicaShards = (ArrayList) nonReplicaShards; + } + + public Map getCommits() { + return commits; + } + + public void setCommits(Map commits) { + this.commits = (HashMap) commits; + } + + public void flush() { + shardLeader.clear(); + replicaShards.clear(); + nonReplicaShards.clear(); + commits.clear(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("[ "); + builder.append(this.member); + builder.append(" ] "); + + builder.append(this.node); + builder.append(":"); + builder.append(this.akkaPort); + builder.append(" is"); + if(up) + builder.append(" Up"); + else + builder.append(" Down"); + if(unreachable) + builder.append(" [ UNREACHABLE ]"); + + if(voting) + builder.append(" (Voting)"); + + builder.append("\n"); + + for(String l : this.shardLeader) { + builder.append("\tLeader: "); + builder.append(l); + builder.append("\n"); + } + + for(String r : this.replicaShards) { + builder.append("\tReplicating: "); + builder.append(r); + builder.append("\n"); + } + + for(String n : this.nonReplicaShards) { + builder.append("\tNot replicating: "); + builder.append(n); + builder.append("\n"); + } + + for(Map.Entry entry : commits.entrySet()) { + String key = entry.getKey(); + int value = entry.getValue(); + if(value > 0) { + builder.append("\t"); + builder.append(value); + builder.append(" commits ahead of "); + builder.append(key); + builder.append("\n"); + } + else if(value < 0) { + builder.append("\t"); + builder.append(value); + builder.append(" commits behind "); + builder.append(key); + builder.append("\n"); + } + } + + return builder.toString(); + } +} diff --git a/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java new file mode 100755 index 00000000..8bbf574d --- /dev/null +++ b/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 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.plugins.grtoolkit.data; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.cluster.health.output.MembersBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.CommitStatusBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.ReplicasBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.LeaderBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class MemberBuilder extends MembersBuilder { + public MemberBuilder(ClusterActor actor) { + super(); + this.setAddress(actor.getNode()); + this.setRole(actor.getMember()); + this.setVoting(actor.isVoting()); + this.setUp(actor.isUp()); + this.setUnreachable(actor.isUnreachable()); + populateReplicas(actor.getReplicaShards()); + populateCommits(actor.getCommits()); + populateLeader(actor.getShardLeader()); + } + + private void populateLeader(List shardLeader) { + LeaderBuilder builder; + this.setLeader(new ArrayList<>()); + for(String leader : shardLeader) { + builder = new LeaderBuilder(); + builder.setShard(leader); + this.getLeader().add(builder.build()); + } + } + + private void populateCommits(Map commits) { + CommitStatusBuilder builder; + this.setCommitStatus(new ArrayList<>()); + for(Map.Entry entry : commits.entrySet()) { + String key = entry.getKey(); + Integer value = entry.getValue(); + if(value != 0) { + builder = new CommitStatusBuilder(); + builder.setShard(key); + builder.setDelta(value); + this.getCommitStatus().add(builder.build()); + } + } + } + + private void populateReplicas(List replicaShards) { + ReplicasBuilder builder; + this.setReplicas(new ArrayList<>()); + for(String shard : replicaShards) { + builder = new ReplicasBuilder(); + builder.setShard(shard); + this.getReplicas().add(builder.build()); + } + } +} diff --git a/grToolkit/provider/src/main/resources/gr-toolkit.properties b/grToolkit/provider/src/main/resources/gr-toolkit.properties index 2ddaa9a4..c008eea9 100755 --- a/grToolkit/provider/src/main/resources/gr-toolkit.properties +++ b/grToolkit/provider/src/main/resources/gr-toolkit.properties @@ -1,6 +1,25 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 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========================================================= + akka.conf.location=/opt/opendaylight/current/controller/configuration/initial/akka.conf adm.useSsl=true -adm.fqdn=sdnmlcadm-conexus-it.ecomp.cci.att.com +adm.fqdn= adm.healthcheck=/healthcheck adm.port.http=8181 adm.port.ssl=8443 diff --git a/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml b/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml index 606ce277..5a4492c5 100755 --- a/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml +++ b/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml @@ -21,7 +21,7 @@ - + diff --git a/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java b/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java new file mode 100644 index 00000000..dabdf206 --- /dev/null +++ b/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java @@ -0,0 +1,344 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 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.plugins.grtoolkit; +import com.google.common.util.concurrent.ListenableFuture; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.opendaylight.controller.cluster.access.concepts.MemberName; +import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; +import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +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.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutput; +import org.opendaylight.yangtools.yang.common.RpcResult; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class GrToolkitProviderTest { + GrToolkitProvider provider; + GrToolkitProvider providerSpy; + DataBroker dataBroker; + NotificationPublishService notificationProviderService; + RpcProviderRegistry rpcProviderRegistry; + DistributedDataStoreInterface configDatastore; + DbLibService dbLibService; + DBLibConnection connection; + + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + @Before + public void setup() { + environmentVariables.set("SDNC_CONFIG_DIR","src/test/resources/"); + dataBroker = mock(DataBroker.class); + notificationProviderService = mock(NotificationPublishService.class); + rpcProviderRegistry = mock(RpcProviderRegistry.class); + configDatastore = mock(DistributedDataStoreInterface.class); + dbLibService = mock(DbLibService.class); + connection = mock(DBLibConnection.class); + + ActorContext actorContext = mock(ActorContext.class); + MemberName memberName = MemberName.forName("Test"); + + when(actorContext.getCurrentMemberName()).thenReturn(memberName); + when(configDatastore.getActorContext()).thenReturn(actorContext); + + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + + provider = new GrToolkitProvider(dataBroker, notificationProviderService, + rpcProviderRegistry, configDatastore, dbLibService); + providerSpy = spy(provider); + } + + @Test + public void closeTest() { + try { + provider.close(); + } + catch(Exception e) { + // Exception expected + } + } + + @Test + public void onDataTreeChangedTest() { + provider.onDataTreeChanged(new ArrayList()); + // onDataTreeChanged is an empty stub + } + + @Test + public void clusterHealthTest() { + ListenableFuture> result = provider.clusterHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void siteHealthTest() { + ListenableFuture> result = provider.siteHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void siteHealth6NodeTest() { + Map memberMap = new HashMap<>(); + ClusterActor actor; + for(int ndx = 0; ndx < 6; ndx++) { + actor = new ClusterActor(); + actor.setNode("member-" + (ndx + 1)); + actor.setUp(true); + actor.setUnreachable(false); + + memberMap.put(actor.getNode(), actor); + } + + try { + Field field = provider.getClass().getDeclaredField("siteConfiguration"); + field.setAccessible(true); + field.set(provider, GrToolkitProvider.SiteConfiguration.GEO); + + field = provider.getClass().getDeclaredField("memberMap"); + field.setAccessible(true); + field.set(provider, memberMap); + + + actor = new ClusterActor(); + actor.setNode("member-1"); + field = provider.getClass().getDeclaredField("self"); + field.setAccessible(true); + field.set(provider, actor); + + field = provider.getClass().getDeclaredField("member"); + field.setAccessible(true); + field.set(provider, actor.getNode()); + } + catch(IllegalAccessException | NoSuchFieldException e) { + fail(); + } + + ListenableFuture> result = provider.siteHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthTest() { + ListenableFuture> result = provider.databaseHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthWhenROTest() { + try { + when(connection.isReadOnly()).thenReturn(true); + } catch(SQLException e) { + fail(); + } + ListenableFuture> result = provider.databaseHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthWhenExceptionTest() { + try { + when(connection.isReadOnly()).thenThrow(new SQLException()); + } catch(SQLException e) { + //expected + } + ListenableFuture> result = provider.databaseHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void adminHealthTest() { + ListenableFuture> result = provider.adminHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void siteIdentifierTest() { + ListenableFuture> result = provider.siteIdentifier(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void failoverTest() { + ListenableFuture> result = provider.failover(null); + try { + assertEquals("400", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void executeCommandTest() { + try { + Method method = provider.getClass().getDeclaredMethod("executeCommand", String.class); + method.setAccessible(true); + method.invoke(provider, "ls"); + } + catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void isolateSiteFromClusterTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-node"); + actor.setAkkaPort("2550"); + ArrayList activeList = new ArrayList<>(); + activeList.add(actor); + ArrayList standbyList = new ArrayList<>(); + standbyList.add(actor); + Method method = provider.getClass().getDeclaredMethod("isolateSiteFromCluster", ArrayList.class, ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, standbyList, "80"); + } + catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void downUnreachableNodesTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-node"); + actor.setAkkaPort("2550"); + ArrayList activeList = new ArrayList<>(); + activeList.add(actor); + ArrayList standbyList = new ArrayList<>(); + standbyList.add(actor); + Method method = provider.getClass().getDeclaredMethod("downUnreachableNodes", ArrayList.class, ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, standbyList, "80"); + } + catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void changeClusterVotingTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setMember("some-member"); + actor.setNode("some-Node"); + ArrayList activeList = new ArrayList<>(); + activeList.add(actor); + ArrayList standbyList = new ArrayList<>(); + standbyList.add(actor); + Field field = provider.getClass().getDeclaredField("self"); + field.setAccessible(true); + field.set(provider, actor); + Method method = provider.getClass().getDeclaredMethod("changeClusterVoting", FailoverOutputBuilder.class, ArrayList.class, ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, new FailoverOutputBuilder(), activeList, standbyList, "80"); + } + catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { + fail(); + } + } + + @Test + public void backupMdSalTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-Node"); + actor.setAkkaPort("2550"); + ArrayList activeList = new ArrayList<>(); + activeList.add(actor); + Method method = provider.getClass().getDeclaredMethod("backupMdSal", ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, "80"); + } + catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + +} diff --git a/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java b/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java new file mode 100644 index 00000000..4b657cf0 --- /dev/null +++ b/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java @@ -0,0 +1,41 @@ +package org.onap.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; + +import static org.junit.Assert.*; + +public class MemberBuilderTest { + MemberBuilder builder; + ClusterActor actor; + + @Before + public void setUp() { + actor = new ClusterActor(); + actor.setUp(true); + actor.setVoting(true); + } + + @Test + public void constructorTest() { + ArrayList actorList = new ArrayList<>(); + ArrayList shardList = new ArrayList<>(); + HashMap commitMap = new HashMap<>(); + actorList.add("Some-Actor"); + shardList.add("Some-shard"); + commitMap.put("Some-shard", 4); + commitMap.put("Some-other-shard", -4); + actor.setShardLeader(actorList); + actor.setReplicaShards(shardList); + actor.setNonReplicaShards(shardList); + actor.setCommits(commitMap); + assertNotNull(actor.toString()); + assertEquals("", actor.getSite()); + assertEquals(1, actor.getNonReplicaShards().size()); + builder = new MemberBuilder(actor); + assertNotNull(builder.build()); + } +} \ No newline at end of file diff --git a/grToolkit/provider/src/test/resources/akka.conf b/grToolkit/provider/src/test/resources/akka.conf new file mode 100644 index 00000000..cbb73d54 --- /dev/null +++ b/grToolkit/provider/src/test/resources/akka.conf @@ -0,0 +1,49 @@ + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/grToolkit/provider/src/test/resources/akka6.conf b/grToolkit/provider/src/test/resources/akka6.conf new file mode 100644 index 00000000..358218d9 --- /dev/null +++ b/grToolkit/provider/src/test/resources/akka6.conf @@ -0,0 +1,49 @@ + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.2:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.3:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.4:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.5:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.6:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/grToolkit/provider/src/test/resources/gr-toolkit.properties b/grToolkit/provider/src/test/resources/gr-toolkit.properties new file mode 100755 index 00000000..d9bc66dc --- /dev/null +++ b/grToolkit/provider/src/test/resources/gr-toolkit.properties @@ -0,0 +1,34 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 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========================================================= + +akka.conf.location=src/test/resources/akka.conf +adm.useSsl=false +adm.fqdn=wiki.onap.org +adm.healthcheck= +adm.port.http=80 +adm.port.ssl=443 +controller.credentials=admin:admin +controller.useSsl=false +controller.port.http=8181 +controller.port.ssl=8443 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +site.identifier=TestODL -- cgit 1.2.3-korg