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