680abf3fab6beadc3f846322edbf0b7e5fa6c1bc
[policy/drools-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * feature-state-management
4  * ================================================================================
5  * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.statemanagement;
22
23 import java.io.IOException;
24 import java.util.List;
25 import java.util.Properties;
26 import org.onap.policy.common.capabilities.Startable;
27 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
28 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
29 import org.onap.policy.common.im.IntegrityMonitor;
30 import org.onap.policy.common.im.IntegrityMonitorException;
31 import org.onap.policy.drools.utils.PropertyUtil;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * This class extends 'IntegrityMonitor' for use in the 'Drools PDP' virtual machine. The included
37  * audits are 'Database' and 'Repository'.
38  */
39 public class DroolsPdpIntegrityMonitor extends IntegrityMonitor {
40
41     private static final String INVALID_PROPERTY_VALUE = "init: property {} does not have the expected value of {}";
42
43     // get an instance of logger
44     private static final Logger logger = LoggerFactory.getLogger(DroolsPdpIntegrityMonitor.class);
45
46     // static global instance
47     private static DroolsPdpIntegrityMonitor im = null;
48
49     // list of audits to run
50     private static AuditBase[] audits = new AuditBase[] {DbAudit.getInstance(), RepositoryAudit.getInstance()};
51
52     private static Properties subsystemTestProperties = null;
53
54     private static final String PROPERTIES_NAME = "feature-state-management.properties";
55
56     /**
57      * Constructor - pass arguments to superclass, but remember properties.
58      *
59      * @param resourceName unique name of this Integrity Monitor
60      * @param url the JMX URL of the MBean server
61      * @param properties properties used locally, as well as by 'IntegrityMonitor'
62      * @throws IntegrityMonitorException (passed from superclass)
63      */
64     private DroolsPdpIntegrityMonitor(String resourceName, Properties consolidatedProperties)
65             throws IntegrityMonitorException {
66         super(resourceName, consolidatedProperties);
67     }
68
69     private static void missingProperty(String prop) throws IntegrityMonitorException {
70         String msg = "init: missing IntegrityMonitor property: ".concat(prop);
71         logger.error(msg);
72         throw new IntegrityMonitorException(msg);
73     }
74
75     private static void logPropertyValue(String prop, String val) {
76         logger.info("\n\n    init: property: {} = {}\n", prop, val);
77     }
78
79     /**
80      * Static initialization -- create Drools Integrity Monitor, and an HTTP server to handle REST
81      * 'test' requests.
82      *
83      * @throws IntegrityMonitorException exception
84      */
85     public static DroolsPdpIntegrityMonitor init(String configDir) throws IntegrityMonitorException {
86
87         logger.info("init: Entering and invoking PropertyUtil.getProperties() on '{}'", configDir);
88
89         // read in properties
90         Properties stateManagementProperties = getProperties(configDir);
91
92         // fetch and verify definitions of some properties, adding defaults where
93         // appropriate
94         // (the 'IntegrityMonitor' constructor does some additional verification)
95
96         checkPropError(stateManagementProperties, StateManagementProperties.TEST_HOST);
97         checkPropError(stateManagementProperties, StateManagementProperties.TEST_PORT);
98
99         addDefaultPropError(stateManagementProperties, StateManagementProperties.TEST_SERVICES,
100                 StateManagementProperties.TEST_SERVICES_DEFAULT);
101
102         addDefaultPropError(stateManagementProperties, StateManagementProperties.TEST_REST_CLASSES,
103                 StateManagementProperties.TEST_REST_CLASSES_DEFAULT);
104
105         addDefaultPropWarn(stateManagementProperties, StateManagementProperties.TEST_MANAGED,
106                 StateManagementProperties.TEST_MANAGED_DEFAULT);
107
108         addDefaultPropWarn(stateManagementProperties, StateManagementProperties.TEST_SWAGGER,
109                 StateManagementProperties.TEST_SWAGGER_DEFAULT);
110
111         checkPropError(stateManagementProperties, StateManagementProperties.RESOURCE_NAME);
112         checkPropError(stateManagementProperties, StateManagementProperties.FP_MONITOR_INTERVAL);
113         checkPropError(stateManagementProperties, StateManagementProperties.FAILED_COUNTER_THRESHOLD);
114         checkPropError(stateManagementProperties, StateManagementProperties.TEST_TRANS_INTERVAL);
115         checkPropError(stateManagementProperties, StateManagementProperties.WRITE_FPC_INTERVAL);
116         checkPropError(stateManagementProperties, StateManagementProperties.SITE_NAME);
117         checkPropError(stateManagementProperties, StateManagementProperties.NODE_TYPE);
118         checkPropError(stateManagementProperties, StateManagementProperties.DEPENDENCY_GROUPS);
119         checkPropError(stateManagementProperties, StateManagementProperties.DB_TYPE);
120         checkPropError(stateManagementProperties, StateManagementProperties.DB_DRIVER);
121         checkPropError(stateManagementProperties, StateManagementProperties.DB_URL);
122         checkPropError(stateManagementProperties, StateManagementProperties.DB_USER);
123         checkPropError(stateManagementProperties, StateManagementProperties.DB_PWD);
124
125         final String testHost = stateManagementProperties.getProperty(StateManagementProperties.TEST_HOST);
126         final String testPort = stateManagementProperties.getProperty(StateManagementProperties.TEST_PORT);
127         final String resourceName = stateManagementProperties.getProperty(StateManagementProperties.RESOURCE_NAME);
128
129         subsystemTestProperties = stateManagementProperties;
130
131         // Now that we've validated the properties, create Drools Integrity Monitor
132         // with these properties.
133         im = makeMonitor(resourceName, stateManagementProperties);
134         logger.info("init: New DroolsPDPIntegrityMonitor instantiated, resourceName = {}", resourceName);
135
136         // create http server
137         makeRestServer(testHost, testPort, stateManagementProperties);
138         logger.info("init: Exiting and returning DroolsPDPIntegrityMonitor");
139
140         return im;
141     }
142
143     /**
144      * Makes an Integrity Monitor.
145      *
146      * @param resourceName unique name of this Integrity Monitor
147      * @param properties properties used to configure the Integrity Monitor
148      * @return monitor object
149      * @throws IntegrityMonitorException exception
150      */
151     private static DroolsPdpIntegrityMonitor makeMonitor(String resourceName, Properties properties)
152             throws IntegrityMonitorException {
153
154         try {
155             return new DroolsPdpIntegrityMonitor(resourceName, properties);
156
157         } catch (Exception e) {
158             throw new IntegrityMonitorException(e);
159         }
160     }
161
162     /**
163      * Makes a rest server for the Integrity Monitor.
164      *
165      * @param testHost host name
166      * @param testPort port
167      * @param properties properties used to configure the rest server
168      * @throws IntegrityMonitorException exception
169      */
170     private static void makeRestServer(String testHost, String testPort, Properties properties)
171             throws IntegrityMonitorException {
172
173         try {
174             logger.info("init: Starting HTTP server, addr= {}:{}", testHost, testPort);
175
176             new IntegrityMonitorRestServer(properties);
177
178         } catch (Exception e) {
179             logger.error("init: Caught Exception attempting to start server on testPort={}", testPort);
180             throw new IntegrityMonitorException(e);
181         }
182     }
183
184     /**
185      * Gets the properties from the property file.
186      *
187      * @param configDir directory containing the property file
188      * @return the properties
189      * @throws IntegrityMonitorException exception
190      */
191     private static Properties getProperties(String configDir) throws IntegrityMonitorException {
192         try {
193             return PropertyUtil.getProperties(configDir + "/" + PROPERTIES_NAME);
194
195         } catch (IOException e) {
196             throw new IntegrityMonitorException(e);
197         }
198     }
199
200     /**
201      * Checks that a property is defined.
202      *
203      * @param props set of properties
204      * @param name name of the property to check
205      * @throws IntegrityMonitorException exception
206      */
207     private static void checkPropError(Properties props, String name) throws IntegrityMonitorException {
208         String val = props.getProperty(name);
209         if (val == null) {
210             missingProperty(name);
211         }
212
213         logPropertyValue(name, val);
214     }
215
216     /**
217      * Checks a property's value to verify that it matches the expected value. If the property is
218      * not defined, then it is added to the property set, with the expected value. Logs an error if
219      * the property is defined, but does not have the expected value.
220      *
221      * @param props set of properties
222      * @param name name of the property to check
223      * @param expected expected/default value
224      */
225     private static void addDefaultPropError(Properties props, String name, String expected) {
226         String val = props.getProperty(name);
227         if (val == null) {
228             props.setProperty(name, expected);
229
230         } else if (!val.equals(expected)) {
231             logger.error(INVALID_PROPERTY_VALUE, name, expected);
232         }
233
234         logPropertyValue(name, val);
235     }
236
237     /**
238      * Checks a property's value to verify that it matches the expected value. If the property is
239      * not defined, then it is added to the property set, with the expected value. Logs a warning if
240      * the property is defined, but does not have the expected value.
241      *
242      * @param props set of properties
243      * @param name name of the property to check
244      * @param expected expected/default value
245      */
246     private static void addDefaultPropWarn(Properties props, String name, String dflt) {
247         String val = props.getProperty(name);
248         if (val == null) {
249             props.setProperty(name, dflt);
250
251         } else if (!val.equals(dflt)) {
252             logger.warn(INVALID_PROPERTY_VALUE, name, dflt);
253         }
254
255         logPropertyValue(name, val);
256     }
257
258     /**
259      * Run tests (audits) unique to Drools PDP VM (Database + Repository).
260      */
261     @Override
262     public void subsystemTest() throws IntegrityMonitorException {
263         logger.info("DroolsPDPIntegrityMonitor.subsystemTest called");
264
265         // clear all responses (non-null values indicate an error)
266         for (AuditBase audit : audits) {
267             audit.setResponse(null);
268         }
269
270         // invoke all of the audits
271         for (AuditBase audit : audits) {
272             try {
273                 // invoke the audit (responses are stored within the audit object)
274                 audit.invoke(subsystemTestProperties);
275             } catch (Exception e) {
276                 logger.error("{} audit error", audit.getName(), e);
277                 if (audit.getResponse() == null) {
278                     // if there is no current response, use the exception message
279                     audit.setResponse(e.getMessage());
280                 }
281             }
282         }
283
284         // will contain list of subsystems where the audit failed
285         String responseMsg = "";
286
287         // Loop through all of the audits, and see which ones have failed.
288         // NOTE: response information is stored within the audit objects
289         // themselves -- only one can run at a time.
290         for (AuditBase audit : audits) {
291             String response = audit.getResponse();
292             if (response != null) {
293                 // the audit has failed -- add subsystem and
294                 // and 'responseValue' with the new information
295                 responseMsg = responseMsg.concat("\n" + audit.getName() + ": " + response);
296             }
297         }
298
299         if (!responseMsg.isEmpty()) {
300             throw new IntegrityMonitorException(responseMsg);
301         }
302     }
303
304     /* ============================================================ */
305
306     /**
307      * This is the base class for audits invoked in 'subsystemTest'.
308      */
309     public abstract static class AuditBase {
310         // name of the audit
311         protected String name;
312
313         // non-null indicates the error response
314         protected String response;
315
316         /**
317          * Constructor - initialize the name, and clear the initial response.
318          *
319          * @param name name of the audit
320          */
321         protected AuditBase(String name) {
322             this.name = name;
323             this.response = null;
324         }
325
326         /**
327          * Get the name.
328          *
329          * @return the name of this audit
330          */
331         public String getName() {
332             return name;
333         }
334
335         /**
336          * Get the response.
337          *
338          * @return the response String (non-null indicates the error message)
339          */
340         public String getResponse() {
341             return response;
342         }
343
344         /**
345          * Set the response string to the specified value.
346          *
347          * @param value the new value of the response string (null = no errors)
348          */
349         public void setResponse(String value) {
350             response = value;
351         }
352
353         /**
354          * Abstract method to invoke the audit.
355          *
356          * @param persistenceProperties Used for DB access
357          * @throws Exception passed in by the audit
358          */
359         abstract void invoke(Properties persistenceProperties) throws IntegrityMonitorException;
360     }
361
362     public static class IntegrityMonitorRestServer implements Startable {
363         protected HttpServletServer server = null;
364         protected final Properties integrityMonitorRestServerProperties;
365
366         public IntegrityMonitorRestServer(Properties props) {
367             this.integrityMonitorRestServerProperties = props;
368             this.start();
369         }
370
371         @Override
372         public boolean start() {
373             try {
374                 List<HttpServletServer> servers = HttpServletServerFactoryInstance.getServerFactory()
375                                 .build(integrityMonitorRestServerProperties);
376
377                 if (!servers.isEmpty()) {
378                     server = servers.get(0);
379
380                     waitServerStart();
381                 }
382             } catch (Exception e) {
383                 logger.error("Exception building servers", e);
384                 return false;
385             }
386
387             return true;
388         }
389
390         private void waitServerStart() {
391             try {
392                 server.waitedStart(5);
393             } catch (Exception e) {
394                 logger.error("Exception waiting for servers to start: ", e);
395             }
396         }
397
398         @Override
399         public boolean stop() {
400             try {
401                 server.stop();
402             } catch (Exception e) {
403                 logger.error("Exception during stop", e);
404             }
405
406             return true;
407         }
408
409         @Override
410         public void shutdown() {
411             this.stop();
412         }
413
414         @Override
415         public synchronized boolean isAlive() {
416             return this.integrityMonitorRestServerProperties != null;
417         }
418     }
419
420     /**
421      * Returns the instance.
422      *
423      * @return DroolsPDPIntegrityMonitor object
424      * @throws IntegrityMonitorException exception
425      */
426     public static DroolsPdpIntegrityMonitor getInstance() throws IntegrityMonitorException {
427         logger.debug("getInstance() called");
428         if (im == null) {
429             String msg = "No DroolsPDPIntegrityMonitor instance exists."
430                     + " Please use the method DroolsPDPIntegrityMonitor init(String configDir)";
431             throw new IntegrityMonitorException(msg);
432         } else {
433             return im;
434         }
435     }
436 }