2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 AT&T Intellectual Property. All rights
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.ccsdk.sli.plugins.grtoolkit.resolver;
24 import org.json.JSONException;
25 import org.json.JSONObject;
27 import org.onap.ccsdk.sli.core.dblib.DbLibService;
28 import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionManager;
29 import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionResponse;
30 import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth;
31 import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor;
32 import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth;
33 import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth;
34 import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus;
35 import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health;
36 import org.onap.ccsdk.sli.plugins.grtoolkit.data.PropertyKeys;
37 import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth;
39 import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import java.io.IOException;
45 import java.sql.Connection;
46 import java.sql.SQLException;
47 import java.util.List;
49 import java.util.Properties;
51 import static org.onap.ccsdk.sli.plugins.grtoolkit.data.Health.HEALTHY;
54 * Abstract class for the Health Resolver system, which allows for custom logic
55 * to be implemented, while leaving inputs/outputs generic and architecture
56 * agnostic. This class provides some simple implementations of both Admin and
57 * Database health checking, but leaves cluster and site health determinations
58 * up to the implementer. Useful implementation examples can be found in the
59 * {@code SingleNodeHealthResolver}, {@code ThreeNodeHealthResolver}, and
60 * {@code SixNodeHealthResolver} classes.
62 * @author Anthony Haddox
63 * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider
64 * @see SingleNodeHealthResolver
65 * @see ThreeNodeHealthResolver
66 * @see SixNodeHealthResolver
68 public abstract class HealthResolver {
69 private final Logger log = LoggerFactory.getLogger(HealthResolver.class);
70 static final String OUTPUT = "output";
71 final String httpProtocol;
72 final String controllerPort;
73 final String credentials;
74 final Map<String, ClusterActor> memberMap;
75 private DbLibService dbLib;
76 final ShardResolver shardResolver;
77 private String adminPath;
78 private String siteIdentifier;
81 * Constructs the health resolver used by the {@code GrToolkitProvider} to
82 * determine the health of the application components.
84 * @param map a HashMap containing all of the nodes in the akka cluster
85 * @param properties the properties passed ino the provider
86 * @param dbLib a reference to the {@code DbLibService} of the provider
87 * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider
89 HealthResolver(Map<String, ClusterActor> map, Properties properties, DbLibService dbLib) {
90 log.info("Creating {}", this.getClass().getCanonicalName());
93 shardResolver = ShardResolver.getInstance(properties);
95 String adminProtocol = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? "https://" : "http://";
96 String adminPort = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? properties.getProperty(PropertyKeys.ADM_PORT_SSL) : properties.getProperty(PropertyKeys.ADM_PORT_HTTP);
97 adminPath = adminProtocol + properties.getProperty(PropertyKeys.ADM_FQDN) + ":" + adminPort + properties.getProperty(PropertyKeys.ADM_HEALTHCHECK);
98 siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim();
100 controllerPort = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL).trim() : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP).trim();
101 httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://";
102 if(siteIdentifier == null || siteIdentifier.isEmpty()) {
103 siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim();
105 credentials = properties.getProperty(PropertyKeys.CONTROLLER_CREDENTIALS).trim();
108 public abstract ClusterHealth getClusterHealth();
109 public abstract List<SiteHealth> getSiteHealth();
110 public abstract FailoverStatus tryFailover(FailoverInput input);
111 public abstract void resolveSites();
114 * Gets a connection to the admin portal. If the status code is 200, the
115 * admin portal is assumed to be healthy.
117 * @return an {@code AdminHealth} object with health of the admin portal
118 * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider
121 public AdminHealth getAdminHealth() {
122 log.info("getAdminHealth(): Requesting health check from {}", adminPath);
124 ConnectionResponse response = ConnectionManager.getConnectionResponse(adminPath, ConnectionManager.HttpMethod.GET, null, null);
125 Health health = (response.statusCode == 200) ? HEALTHY : Health.FAULTY;
126 AdminHealth adminHealth = new AdminHealth(health, response.statusCode);
127 log.info("getAdminHealth(): Response: {}", response);
129 } catch(IOException e) {
130 log.error("getAdminHealth(): Problem getting ADM health.", e);
131 return new AdminHealth(Health.FAULTY, 500);
136 * Uses {@code DbLibService} to get a connection to the database. If
137 * {@code DbLibService} is active and the connection it returns is not read
138 * only, the database(s) is assumed to be healthy.
140 * @return an {@code DatabaseHealth} object with health of the database
141 * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider
142 * @see DatabaseHealth
144 public DatabaseHealth getDatabaseHealth() {
145 log.info("getDatabaseHealth(): Determining database health...");
146 try (Connection connection = dbLib.getConnection()){
147 log.debug("getDatabaseHealth(): DBLib isActive(): {}", dbLib.isActive());
148 log.debug("getDatabaseHealth(): DBLib isReadOnly(): {}", connection.isReadOnly());
149 log.debug("getDatabaseHealth(): DBLib isClosed(): {}", connection.isClosed());
150 if(!dbLib.isActive() || connection.isClosed() || connection.isReadOnly()) {
151 log.warn("getDatabaseHealth(): Database is FAULTY");
152 return new DatabaseHealth(Health.FAULTY);
154 log.info("getDatabaseHealth(): Database is HEALTHY");
155 } catch(SQLException e) {
156 log.error("getDatabaseHealth(): Database is FAULTY");
157 log.error("getDatabaseHealth(): Error", e);
158 return new DatabaseHealth(Health.FAULTY);
161 return new DatabaseHealth(HEALTHY);
165 * Utility method to see if an input is healthy.
167 * @return true if the input is healthy
170 boolean isHealthy(Health h) {
174 public String getSiteIdentifier() {
175 return siteIdentifier;
178 public void setSiteIdentifier(String siteIdentifier) {
179 this.siteIdentifier = siteIdentifier;
183 * Used to invoke the admin-health or database-health RPC to check if that
184 * component is healthy.
186 * @param path the path to the admin-health or database-health RPCs
187 * @return true if the component is healthy
188 * @throws IOException if a connection cannot be obtained
190 boolean isRemoteComponentHealthy(String path) throws IOException {
191 String content = ConnectionManager.getConnectionResponse(path, ConnectionManager.HttpMethod.POST, null, credentials).content;
193 JSONObject responseJson = new JSONObject(content);
194 JSONObject responseValue = responseJson.getJSONObject(OUTPUT);
195 return HEALTHY.toString().equals(responseValue.getString("health"));
196 } catch(JSONException e) {
197 log.error("Error parsing JSON", e);
198 throw new IOException();
203 * Checks a {@code ClusterActor} object to see if the node is healthy.
205 * @param controller the controller to check
206 * @return true if the controller is up and reachable
209 public boolean isControllerHealthy(ClusterActor controller) {
210 return (controller.isUp() && ! controller.isUnreachable());