da9b2a85bd338af1141fe3a94013286f28979a33
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * demo
4  * ================================================================================
5  * Copyright (C) 2018 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.template.demo.clc;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27
28 import java.io.IOException;
29 import java.io.UnsupportedEncodingException;
30 import java.net.URLEncoder;
31 import java.time.Instant;
32 import java.util.HashMap;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Properties;
36 import java.util.UUID;
37 import org.junit.AfterClass;
38 import org.junit.BeforeClass;
39 import org.junit.Test;
40 import org.kie.api.runtime.KieSession;
41 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
42 import org.onap.policy.common.endpoints.event.comm.TopicSink;
43 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
44 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
45 import org.onap.policy.controlloop.ControlLoopEventStatus;
46 import org.onap.policy.controlloop.VirtualControlLoopEvent;
47 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
48 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
49 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
50 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
51 import org.onap.policy.drools.system.PolicyController;
52 import org.onap.policy.drools.system.PolicyEngine;
53 import org.onap.policy.drools.utils.logging.LoggerUtil;
54 import org.onap.policy.template.demo.clc.Util.Pair;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Verifies that event objects are cleaned up when rules are updated. This loads
60  * <b>two</b> copies of the rule set into a single policy to ensure that the two copies
61  * interact appropriately with each other's event objects.
62  */
63 public class ControlLoopEventCleanupTest {
64     private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventCleanupTest.class);
65
66     /**
67      * Number of objects per control loop, including the Params object.
68      */
69     private static int CL_OBJECTS = 7;
70
71     private static final String YAML = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test.yaml";
72
73     /**
74      * YAML to be used when the first rule set is updated.
75      */
76     private static final String YAML2 = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test2.yaml";
77
78     private static final String POLICY_VERSION = "v2.0";
79
80     private static final String POLICY_NAME = "CL_CleanupTest";
81
82     private static final String POLICY_SCOPE = "type=operational";
83
84     private static final String CONTROL_LOOP_NAME = "ControlLoop-Event-Cleanup-Test";
85
86     private static final String DROOLS_TEMPLATE = "src/main/resources/__closedLoopControlName__.drl";
87
88     // values specific to the second copy of the rules
89
90     private static final String YAML_B = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test-B.yaml";
91     private static final String POLICY_NAME_B = "CL_CleanupTest_B";
92     private static final String CONTROL_LOOP_NAME_B = "ControlLoop-Event-Cleanup-Test-B";
93
94     private static final String GUARD_DISABLED = "guard.disabled";
95
96     private static String saveGuardFlag;
97
98     private static KieSession kieSession;
99     private static Util.RuleSpec[] specifications;
100
101     /**
102      * Setup the simulator.
103      */
104     @BeforeClass
105     public static void setUpSimulator() {
106         LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO");
107
108         saveGuardFlag = PolicyEngine.manager.getEnvironmentProperty(GUARD_DISABLED);
109         PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, "true");
110
111         Util.setAaiProps();
112
113         PolicyEngine.manager.configure(new Properties());
114         assertTrue(PolicyEngine.manager.start());
115         Properties noopSinkProperties = new Properties();
116         noopSinkProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-CL,POLICY-CL-MGT");
117         noopSinkProperties.put("noop.sink.topics.APPC-CL.events", "org.onap.policy.appc.Response");
118         noopSinkProperties.put("noop.sink.topics.APPC-CL.events.custom.gson",
119                         "org.onap.policy.appc.util.Serialization,gsonPretty");
120         noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events",
121                         "org.onap.policy.controlloop.VirtualControlLoopNotification");
122         noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson",
123                         "org.onap.policy.controlloop.util.Serialization,gsonPretty");
124         final List<TopicSink> noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties);
125
126         EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT",
127                         "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null,
128                         null, 1111);
129         EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-CL",
130                         "org.onap.policy.appc.Request", new JsonProtocolFilter(), null, null, 1111);
131
132         try {
133             Util.buildAaiSim();
134
135         } catch (Exception e) {
136             logger.error("Could not create simulator", e);
137             fail("Could not create simulator");
138         }
139
140         for (TopicSink sink : noopTopics) {
141             assertTrue(sink.start());
142         }
143
144         try {
145             specifications = new Util.RuleSpec[2];
146
147             specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
148                             POLICY_VERSION, loadYaml(YAML));
149
150             specifications[1] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B,
151                             POLICY_VERSION, loadYaml(YAML_B));
152
153             kieSession = Util.buildContainer(POLICY_VERSION, specifications);
154
155         } catch (IOException e) {
156             logger.error("Could not create kieSession", e);
157             fail("Could not create kieSession");
158         }
159     }
160
161     /**
162      * Tear down.
163      */
164     @AfterClass
165     public static void tearDown() {
166         kieSession.dispose();
167
168         PolicyEngine.manager.stop();
169         HttpServletServer.factory.destroy();
170         PolicyController.factory.shutdown();
171         TopicEndpoint.manager.shutdown();
172
173         if (saveGuardFlag == null) {
174             PolicyEngine.manager.getEnvironment().remove(GUARD_DISABLED);
175
176         } else {
177             PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, saveGuardFlag);
178         }
179     }
180
181     @Test
182     public void test() throws IOException {
183
184         /*
185          * Let rules create Params objects.
186          */
187         kieSession.fireAllRules();
188
189         injectEvent(CONTROL_LOOP_NAME);
190         injectEvent(CONTROL_LOOP_NAME_B);
191
192         kieSession.fireAllRules();
193         List<Object> facts = getSessionObjects();
194         
195         // should have events for both control loops
196         assertEquals(2 * CL_OBJECTS, facts.size());
197         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
198         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
199
200         logger.info("UPDATING VERSION TO v3.0");
201         updatePolicy(YAML2, "v3.0");
202
203         /*
204          * Let rules update Params objects. The Params for the first set of rules should
205          * now be deleted and replaced with a new one, while the Params for the second set
206          * should be unchanged.
207          */
208         kieSession.fireAllRules();
209         facts = getSessionObjects();
210
211         // should only have event for second control loop + 1 Params for first control loop
212         assertEquals(CL_OBJECTS + 1, facts.size());
213         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
214
215         // add event for first control loop again
216         injectEvent(CONTROL_LOOP_NAME);
217         kieSession.fireAllRules();
218
219         logger.info("UPDATING VERSION TO v4.0");
220         updatePolicy(YAML, "v4.0");
221
222         /*
223          * Let rules update Params objects. The Params for the first set of rules should
224          * now be deleted and replaced with a new one, while the Params for the second set
225          * should be unchanged.
226          */
227         kieSession.fireAllRules();
228         facts = getSessionObjects();
229
230         // should only have event for second control loop + 1 Params for first control loop
231         assertEquals(CL_OBJECTS + 1, facts.size());
232         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
233
234         // add event for first control loop again
235         injectEvent(CONTROL_LOOP_NAME);
236         kieSession.fireAllRules();
237
238         logger.info("UPDATING VERSION TO v4.0 (i.e., unchanged)");
239         updatePolicy(YAML, "v4.0");
240
241         /*
242          * Let rules update Params objects. As the version (and YAML) are unchanged for
243          * either rule set, both Params objects should be unchanged.
244          */
245         kieSession.fireAllRules();
246         facts = getSessionObjects();
247
248         // should have events for both control loops
249         assertEquals(2 * CL_OBJECTS, facts.size());
250         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
251         assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
252
253         /*
254          * Now we'll delete the first rule set. That won't actually have any immediate
255          * effect, so then we'll update the second rule set, which should trigger a
256          * clean-up of both.
257          */
258         Util.RuleSpec[] specs = new Util.RuleSpec[1];
259         specs[0] = specifications[1];
260
261         logger.info("UPDATING VERSION TO v5.0 - DELETED RULE SET");
262         Util.updateContainer("v5.0", specs);
263
264         specs[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B, POLICY_VERSION,
265                         loadYaml(YAML));
266
267         logger.info("UPDATING VERSION TO v6.0 - UPDATED SECOND RULE SET");
268         Util.updateContainer("v6.0", specs);
269
270         kieSession.fireAllRules();
271         facts = getSessionObjects();
272
273         // only 1 Params should remain, for second rule set, but events should be gone
274         assertEquals(1, facts.size());
275         assertTrue(facts.stream().anyMatch(obj -> obj.toString().startsWith("Params( ")));
276     }
277
278     /**
279      * Updates the policy, changing the YAML associated with the first rule set.
280      *
281      * @param yamlFile name of the YAML file
282      * @param policyVersion policy version
283      * @throws IOException if an error occurs
284      */
285     private static void updatePolicy(String yamlFile, String policyVersion) throws IOException {
286
287         specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
288                         policyVersion, loadYaml(yamlFile));
289
290         /*
291          * Update the policy within the container.
292          */
293         Util.updateContainer(policyVersion, specifications);
294     }
295
296     /**
297      * Loads a YAML file and URL-encodes it.
298      *
299      * @param yamlFile name of the YAML file
300      * @return the contents of the specified file, URL-encoded
301      * @throws UnsupportedEncodingException if an error occurs
302      */
303     private static String loadYaml(String yamlFile) throws UnsupportedEncodingException {
304         Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile);
305         assertNotNull(pair);
306         assertNotNull(pair.first);
307         assertNotNull(pair.first.getControlLoop());
308         assertNotNull(pair.first.getControlLoop().getControlLoopName());
309         assertTrue(pair.first.getControlLoop().getControlLoopName().length() > 0);
310
311         return URLEncoder.encode(pair.second, "UTF-8");
312     }
313
314     /**
315      * Gets the session objects.
316      *
317      * @return the session objects
318      */
319     private static List<Object> getSessionObjects() {
320         // sort the objects so we know the order
321         LinkedList<Object> lst = new LinkedList<>(kieSession.getObjects());
322         lst.sort((left, right) -> left.toString().compareTo(right.toString()));
323
324         lst.forEach(obj -> logger.info("obj={}", obj));
325
326         return lst;
327     }
328
329     /**
330      * Injects an ONSET event into the rule engine.
331      *
332      * @param controlLoopName the control loop name
333      */
334     private void injectEvent(String controlLoopName) {
335         VirtualControlLoopEvent event = new VirtualControlLoopEvent();
336
337         event.setClosedLoopControlName(controlLoopName);
338
339         UUID reqid = UUID.randomUUID();
340         event.setRequestId(reqid);
341
342         event.setTarget("generic-vnf.vnf-id");
343         event.setClosedLoopAlarmStart(Instant.now());
344         event.setAai(new HashMap<>());
345         event.getAai().put("generic-vnf.vnf-id", "vnf-" + reqid.toString());
346         event.getAai().put(ControlLoopEventManager.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "false");
347         event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
348
349         kieSession.insert(event);
350     }
351
352     /**
353      * Determines if the facts contain an event for the given control loop.
354      * 
355      * @param facts session facts to be checked
356      * @param controlLoopName name of the control loop of interest
357      * @return {@code true} if the facts contain an event for the given control loop,
358      *         {@code false} otherwise
359      */
360     private boolean hasEvent(List<Object> facts, String controlLoopName) {
361         return (facts.stream().anyMatch(obj -> obj instanceof VirtualControlLoopEvent
362                         && controlLoopName.equals(((VirtualControlLoopEvent) obj).getClosedLoopControlName())));
363     }
364 }