ee5eceba9625acf9a1cbd86f97e08bfe82ad3869
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * demo
4  * ================================================================================
5  * Copyright (C) 2018-2019 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 com.google.gson.Gson;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.lang.StringBuilder;
34 import java.net.URLEncoder;
35 import java.nio.file.Files;
36 import java.nio.file.Paths;
37 import java.time.Instant;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Properties;
41 import java.util.UUID;
42
43 import org.junit.AfterClass;
44 import org.junit.BeforeClass;
45 import org.junit.Test;
46
47 import org.kie.api.runtime.KieSession;
48 import org.kie.api.runtime.rule.FactHandle;
49
50 import org.onap.policy.appclcm.LcmRequest;
51 import org.onap.policy.appclcm.LcmRequestWrapper;
52 import org.onap.policy.appclcm.LcmResponse;
53 import org.onap.policy.appclcm.LcmResponseWrapper;
54 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
55 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
56 import org.onap.policy.common.endpoints.event.comm.TopicListener;
57 import org.onap.policy.common.endpoints.event.comm.TopicSink;
58 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
59 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
60 import org.onap.policy.controlloop.ControlLoopEventStatus;
61 import org.onap.policy.controlloop.ControlLoopNotificationType;
62 import org.onap.policy.controlloop.ControlLoopTargetType;
63 import org.onap.policy.controlloop.VirtualControlLoopEvent;
64 import org.onap.policy.controlloop.VirtualControlLoopNotification;
65 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
66 import org.onap.policy.coordination.CoordinationDirective;
67 import org.onap.policy.coordination.Util;
68 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
69 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
70 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
71 import org.onap.policy.drools.system.PolicyController;
72 import org.onap.policy.drools.system.PolicyEngine;
73
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 public class ControlLoopCoordinationTest implements TopicListener {
78
79     private static final Logger logger = LoggerFactory.getLogger(ControlLoopCoordinationTest.class);
80
81     private static List<? extends TopicSink> noopTopics;
82
83     private static KieSession kieSession1;
84     private static KieSession kieSession2;
85     private static StringBuilder controlLoopOneName = new StringBuilder();
86     private static StringBuilder controlLoopTwoName = new StringBuilder();
87     private static String expectedDecision;
88
89     static {
90         /* Set environment properties */
91         SupportUtil.setAaiProps();
92         SupportUtil.setGuardPropsEmbedded();
93         SupportUtil.setPuProp();
94     }
95
96     /**
97      * Setup simulator.
98      *
99      * @throws IOException when thrown by buildAaiSim
100      */
101     @BeforeClass
102     public static void setUpSimulator()  throws Exception {
103         PolicyEngine.manager.configure(new Properties());
104         assertTrue(PolicyEngine.manager.start());
105         Properties noopSinkProperties = new Properties();
106         noopSinkProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-LCM-READ,POLICY-CL-MGT");
107         noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events", "org.onap.policy.appclcm.LcmRequestWrapper");
108         noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events.custom.gson",
109                 "org.onap.policy.appclcm.util.Serialization,gson");
110         noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events",
111                 "org.onap.policy.controlloop.VirtualControlLoopNotification");
112         noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson",
113                 "org.onap.policy.controlloop.util.Serialization,gsonPretty");
114         noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties);
115
116         EventProtocolCoder.manager.addEncoder(EventProtocolParams.builder()
117                 .groupId("junit.groupId")
118                 .artifactId("junit.artifactId")
119                 .topic("POLICY-CL-MGT")
120                 .eventClass("org.onap.policy.controlloop.VirtualControlLoopNotification")
121                 .protocolFilter(new JsonProtocolFilter())
122                 .modelClassLoaderHash(1111));
123         EventProtocolCoder.manager.addEncoder(EventProtocolParams.builder()
124                 .groupId("junit.groupId")
125                 .artifactId("junit.artifactId")
126                 .topic("APPC-LCM-READ")
127                 .eventClass("org.onap.policy.appclcm.LcmRequestWrapper")
128                 .protocolFilter(new JsonProtocolFilter())
129                 .modelClassLoaderHash(1111));
130         SupportUtil.buildAaiSim();
131         /*
132          * Apply the coordination directive
133          */
134         final String coordinationDir = "src/test/resources/coordination";
135         final String coordinationProtoDir = "src/main/resources/coordination/prototype";
136         final String coordinationDirectiveName = "synthetic_control_loop_one_blocks_synthetic_control_loop_two";
137         final String propertiesDir = "src/test/resources/properties";
138         final String propertiesProtoDir = "src/test/resources/properties/prototype";
139         final String xacmlDir      = "src/test/resources/xacml";
140         final String yamlDir = "src/test/resources/yaml";
141
142         String coordinationDirectiveFile = coordinationDir + File.separator + coordinationDirectiveName + ".yaml";
143         /*
144          * Load the coordination directive from the Yaml encoding
145          */
146         CoordinationDirective cd = Util.loadCoordinationDirectiveFromFile(coordinationDirectiveFile);
147         logger.info("CoordinationDirective={}", cd.toString());
148         /*
149          * Generate the Xacml policy from the coordination directive
150          */
151         String xacmlPolicy = Util.generateXacmlFromCoordinationDirective(cd, coordinationProtoDir);
152         /*
153          * Create directory for Xacml output file, if necessary
154          */
155         Files.createDirectories(Paths.get(xacmlDir));
156         /*
157          * Write Xacml policy to file
158          */
159         String xacmlFilename      = xacmlDir + File.separator
160             + cd.getCoordinationFunction()
161             + cd.getControlLoop(0)
162             + cd.getControlLoop(1)
163             + ".xml";
164         File xacmlFile = new File(xacmlFilename);
165         PrintWriter xacmlFileWriter = new PrintWriter(xacmlFile);
166         xacmlFileWriter.println(xacmlPolicy);
167         xacmlFileWriter.flush();
168         /*
169          * Remove Xacml file after test
170          */
171         xacmlFile.deleteOnExit();
172         /*
173          * Insert the Xacml policy file into the PDP
174          */
175         SupportUtil.insertXacmlPolicy(xacmlFilename, propertiesProtoDir, propertiesDir);
176         /*
177          * Start the kie sessions
178          */
179         kieSession1 = startSession(
180                     controlLoopOneName,
181                     "src/main/resources/__closedLoopControlName__.drl",
182                     yamlDir + "/policy_ControlLoop_SyntheticOne.yaml",
183                     "service=ServiceDemo;resource=Res1Demo;type=operational",
184                     "SyntheticControlLoopOnePolicy",
185                     "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
186         kieSession2 = startSession(
187                     controlLoopTwoName,
188                     "src/main/resources/__closedLoopControlName__.drl",
189                     yamlDir + "/policy_ControlLoop_SyntheticTwo.yaml",
190                     "service=ServiceDemo;resource=Res1Demo;type=operational",
191                     "SyntheticControlLoopTwoPolicy",
192                     "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
193     }
194
195     /**
196      * Tear down simulator.
197      */
198     @AfterClass
199     public static void tearDownSimulator() {
200         /*
201          * Gracefully shut down the kie session
202          */
203         kieSession1.dispose();
204         kieSession2.dispose();
205
206         PolicyEngine.manager.stop();
207         HttpServletServer.factory.destroy();
208         PolicyController.factory.shutdown();
209         TopicEndpoint.manager.shutdown();
210     }
211
212     /**
213      * Set expected decision.
214      *
215      * @param ed the expected decision ("PERMIT" or "DENY")
216      */
217     public void expectedDecisionIs(String ed) {
218         expectedDecision = ed;
219         logger.info("Expected decision is {}", ed);
220     }
221
222     /**
223      * This method is used to simulate event messages from DCAE
224      * that start the control loop (onset message) or end the
225      * control loop (abatement message).
226      *
227      * @param controlLoopName the control loop name
228      * @param requestId the requestId for this event
229      * @param status could be onset or abated
230      * @param target the target name
231      * @param kieSession the kieSession to which this event is being sent
232      */
233     protected void sendEvent(String controlLoopName,
234                              UUID requestId,
235                              ControlLoopEventStatus status,
236                              String target,
237                              KieSession kieSession) {
238         logger.debug("sendEvent controlLoopName={}", controlLoopName);
239         VirtualControlLoopEvent event = new VirtualControlLoopEvent();
240         event.setClosedLoopControlName(controlLoopName);
241         event.setRequestId(requestId);
242         event.setTarget("generic-vnf.vnf-name");
243         event.setTargetType(ControlLoopTargetType.VNF);
244         event.setClosedLoopAlarmStart(Instant.now());
245         event.setAai(new HashMap<>());
246         event.getAai().put("generic-vnf.vnf-name", target);
247         event.setClosedLoopEventStatus(status);
248
249         Gson gson = new Gson();
250         String json = gson.toJson(event);
251         logger.debug("sendEvent {}", json);
252
253         kieSession.insert(event);
254     }
255
256
257     /**
258      * Simulate an event by inserting into kieSession and firing rules as needed.
259      *
260      * @param cles the ControlLoopEventStatus
261      * @param rid the request ID
262      * @param controlLoopName the control loop name
263      * @param kieSession the kieSession to which this event is being sent
264      * @param expectedDecision the expected decision
265      */
266     protected void simulateEvent(ControlLoopEventStatus cles,
267                                  UUID rid,
268                                  String controlLoopName,
269                                  String target,
270                                  KieSession kieSession,
271                                  String expectedDecision) {
272         //
273         // if onset, set expected decision
274         //
275         if (cles == ControlLoopEventStatus.ONSET) {
276             expectedDecisionIs(expectedDecision);
277         }
278         //
279         // simulate sending event
280         //
281         sendEvent(controlLoopName, rid, cles, target, kieSession);
282         kieSession.fireUntilHalt();
283         //
284         // get dump of database entries and log
285         //
286         List<?> entries = SupportUtil.dumpDb();
287         assertNotNull(entries);
288         logger.debug("dumpDB, {} entries", entries.size());
289         for (Object entry : entries) {
290             logger.debug("{}", entry);
291         }
292         //
293         // we are done
294         //
295         logger.info("simulateEvent: done");
296     }
297
298     /**
299      * Simulate an onset event.
300      *
301      * @param rid the request ID
302      * @param controlLoopName the control loop name
303      * @param kieSession the kieSession to which this event is being sent
304      * @param expectedDecision the expected decision
305      */
306     public void simulateOnset(UUID rid,
307                               String controlLoopName,
308                               String target,
309                               KieSession kieSession,
310                               String expectedDecision) {
311         simulateEvent(ControlLoopEventStatus.ONSET, rid, controlLoopName, target, kieSession, expectedDecision);
312     }
313
314     /**
315      * Simulate an abated event.
316      *
317      * @param rid the request ID
318      * @param controlLoopName the control loop name
319      * @param kieSession the kieSession to which this event is being sent
320      */
321     public void simulateAbatement(UUID rid,
322                                   String controlLoopName,
323                                   String target,
324                                   KieSession kieSession) {
325         simulateEvent(ControlLoopEventStatus.ABATED, rid, controlLoopName, target, kieSession, null);
326     }
327
328     /**
329      * This method will start a kie session and instantiate the Policy Engine.
330      *
331      * @param droolsTemplate the DRL rules file
332      * @param yamlFile the yaml file containing the policies
333      * @param policyScope scope for policy
334      * @param policyName name of the policy
335      * @param policyVersion version of the policy
336      * @return the kieSession to be used to insert facts
337      * @throws IOException throws IO exception
338      */
339     private static KieSession startSession(StringBuilder controlLoopName,
340                                            String droolsTemplate,
341                                            String yamlFile,
342                                            String policyScope,
343                                            String policyName,
344                                            String policyVersion) throws IOException {
345
346         /*
347          * Load policies from yaml
348          */
349         SupportUtil.Pair<ControlLoopPolicy, String> pair = SupportUtil.loadYaml(yamlFile);
350         assertNotNull(pair);
351         assertNotNull(pair.first);
352         assertNotNull(pair.first.getControlLoop());
353         assertNotNull(pair.first.getControlLoop().getControlLoopName());
354         assertTrue(!pair.first.getControlLoop().getControlLoopName().isEmpty());
355
356         controlLoopName.append(pair.first.getControlLoop().getControlLoopName());
357         String yamlContents = pair.second;
358
359         /*
360          * Construct a kie session
361          */
362         final KieSession kieSession = SupportUtil.buildContainer(droolsTemplate,
363                                                           controlLoopName.toString(),
364                                                           policyScope,
365                                                           policyName,
366                                                           policyVersion,
367                                                           URLEncoder.encode(yamlContents, "UTF-8"));
368
369         /*
370          * Retrieve the Policy Engine
371          */
372
373         logger.debug("============");
374         logger.debug(URLEncoder.encode(yamlContents, "UTF-8"));
375         logger.debug("============");
376
377         return kieSession;
378     }
379
380     /*
381      * (non-Javadoc)
382      *
383      * @see org.onap.policy.drools.PolicyEngineListener#newEventNotification(java.lang.String)
384      */
385     @Override
386     public void onTopicEvent(CommInfrastructure commType, String topic, String event) {
387         /*
388          * Pull the object that was sent out to DMAAP and make sure it is a ControlLoopNoticiation
389          * of type active
390          */
391         Object obj = null;
392         if ("POLICY-CL-MGT".equals(topic)) {
393             obj = org.onap.policy.controlloop.util.Serialization.gsonJunit.fromJson(event,
394                     org.onap.policy.controlloop.VirtualControlLoopNotification.class);
395         } else if ("APPC-LCM-READ".equals(topic)) {
396             obj = org.onap.policy.appclcm.util.Serialization.gsonJunit.fromJson(event,
397                     org.onap.policy.appclcm.LcmRequestWrapper.class);
398         }
399         assertNotNull(obj);
400         if (obj instanceof VirtualControlLoopNotification) {
401             VirtualControlLoopNotification notification = (VirtualControlLoopNotification) obj;
402             String policyName = notification.getPolicyName();
403             if (policyName.endsWith("EVENT")) {
404                 logger.debug("Rule Fired: " + notification.getPolicyName());
405                 assertTrue(ControlLoopNotificationType.ACTIVE.equals(notification.getNotification()));
406             } else if (policyName.endsWith("GUARD_NOT_YET_QUERIED")) {
407                 logger.debug("Rule Fired: " + notification.getPolicyName());
408                 assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.getNotification()));
409                 assertNotNull(notification.getMessage());
410                 assertTrue(notification.getMessage().startsWith("Sending guard query"));
411             } else if (policyName.endsWith("GUARD.RESPONSE")) {
412                 logger.debug("Rule Fired: " + notification.getPolicyName());
413                 assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.getNotification()));
414                 assertNotNull(notification.getMessage());
415                 // THESE ARE THE MOST CRITICAL ASSERTS
416                 // TEST IF GUARD.RESPONSE IS CORRECT
417                 logger.debug("Testing whether decision was {} as expected", expectedDecision);
418                 assertTrue(notification.getMessage().toUpperCase().endsWith(expectedDecision));
419             } else if (policyName.endsWith("GUARD_PERMITTED")) {
420                 logger.debug("Rule Fired: " + notification.getPolicyName());
421                 assertEquals(ControlLoopNotificationType.OPERATION,notification.getNotification());
422                 assertNotNull(notification.getMessage());
423                 assertTrue(notification.getMessage().startsWith("actor=APPC"));
424             } else if (policyName.endsWith("OPERATION.TIMEOUT")) {
425                 logger.debug("Rule Fired: " + notification.getPolicyName());
426                 kieSession1.halt();
427                 kieSession2.halt();
428                 logger.debug("The operation timed out");
429                 fail("Operation Timed Out");
430             } else if (policyName.endsWith("APPC.LCM.RESPONSE")) {
431                 logger.debug("Rule Fired: " + notification.getPolicyName());
432                 assertTrue(ControlLoopNotificationType.OPERATION_SUCCESS.equals(notification.getNotification()));
433                 assertNotNull(notification.getMessage());
434                 assertTrue(notification.getMessage().startsWith("actor=APPC"));
435             } else if (policyName.endsWith("EVENT.MANAGER")) {
436                 logger.debug("Rule Fired: " + notification.getPolicyName());
437                 if (notification.getMessage().endsWith("Closing the control loop.")
438                     || notification.getMessage().equals("Waiting for abatement")) {
439                     if (policyName.startsWith(controlLoopOneName.toString())) {
440                         logger.debug("Halting kieSession1");
441                         kieSession1.halt();
442                     } else if (policyName.startsWith(controlLoopTwoName.toString())) {
443                         logger.debug("Halting kieSession2");
444                         kieSession2.halt();
445                     } else {
446                         fail("Unknown ControlLoop");
447                     }
448                 }
449             } else if (policyName.endsWith("EVENT.MANAGER.TIMEOUT")) {
450                 logger.debug("Rule Fired: " + notification.getPolicyName());
451                 kieSession1.halt();
452                 kieSession2.halt();
453                 logger.debug("The control loop timed out");
454                 fail("Control Loop Timed Out");
455             }
456         } else if (obj instanceof LcmRequestWrapper) {
457             /*
458              * The request should be of type LCMRequestWrapper and the subrequestid should be 1
459              */
460             LcmRequestWrapper dmaapRequest = (LcmRequestWrapper) obj;
461             LcmRequest appcRequest = dmaapRequest.getBody();
462             assertEquals(appcRequest.getCommonHeader().getSubRequestId(),"1");
463
464             logger.debug("\n============ APPC received the request!!! ===========\n");
465
466             /*
467              * Simulate a success response from APPC and insert the response into the working memory
468              */
469             LcmResponseWrapper dmaapResponse = new LcmResponseWrapper();
470             LcmResponse appcResponse = new LcmResponse(appcRequest);
471             appcResponse.getStatus().setCode(400);
472             appcResponse.getStatus().setMessage("AppC success");
473             dmaapResponse.setBody(appcResponse);
474             kieSession1.insert(dmaapResponse);
475             kieSession2.insert(dmaapResponse);
476         }
477     }
478
479     /**
480      * This method will dump all the facts in the working memory.
481      *
482      * @param kieSession the session containing the facts
483      */
484     public void dumpFacts(KieSession kieSession) {
485         logger.debug("Fact Count: {}", kieSession.getFactCount());
486         for (FactHandle handle : kieSession.getFactHandles()) {
487             logger.debug("FACT: {}", handle);
488         }
489     }
490
491     /**
492      * Test that SyntheticControlLoopOne blocks SyntheticControlLoopTwo
493      * is enforced correctly.
494      */
495     @Test
496     public void testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo() throws InterruptedException {
497         logger.info("Beginning testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo");
498         /*
499          * Allows the PolicyEngine to callback to this object to
500          * notify that there is an event ready to be pulled
501          * from the queue
502          */
503         for (TopicSink sink : noopTopics) {
504             assertTrue(sink.start());
505             sink.register(this);
506         }
507
508         /*
509          * Create unique requestIds
510          */
511         final UUID requestId1 = UUID.randomUUID();
512         final UUID requestId2 = UUID.randomUUID();
513         final UUID requestId3 = UUID.randomUUID();
514         final UUID requestId4 = UUID.randomUUID();
515         final UUID requestId5 = UUID.randomUUID();
516         final String cl1 = controlLoopOneName.toString();
517         final String cl2 = controlLoopTwoName.toString();
518         final String t1 = "TARGET_1";
519         final String t2 = "TARGET_2";
520
521         logger.info("@@@@@@@@@@ cl2 ONSET t1 (Success) @@@@@@@@@@");
522         simulateOnset(requestId1, cl2, t1, kieSession2,"PERMIT");
523
524         logger.info("@@@@@@@@@@ cl1 ONSET t1 @@@@@@@@@@");
525         simulateOnset(requestId2, cl1, t1, kieSession1,"PERMIT");
526
527         logger.info("@@@@@@@@@@ cl2 ABATED t1 @@@@@@@@@@");
528         simulateAbatement(requestId1, cl2, t1, kieSession2);
529
530         logger.info("@@@@@@@@@@ cl2 ONSET t1 (Fail) @@@@@@@@@@");
531         simulateOnset(requestId3, cl2, t1, kieSession2,"DENY");
532
533         logger.info("@@@@@@@@@@ cl2 ONSET t2 (Success) @@@@@@@@@@");
534         simulateOnset(requestId4, cl2, t2, kieSession2,"PERMIT");
535
536         logger.info("@@@@@@@@@@ cl2 ABATED t2 @@@@@@@@@@");
537         simulateAbatement(requestId4, cl2, t2, kieSession2);
538
539         logger.info("@@@@@@@@@@ cl1 ABATED t1  @@@@@@@@@@");
540         simulateAbatement(requestId2, cl1, t1, kieSession1);
541
542         logger.info("@@@@@@@@@@ cl2 ONSET t1 (Success) @@@@@@@@@@");
543         simulateOnset(requestId5, cl2, t1, kieSession2,"PERMIT");
544
545         logger.info("@@@@@@@@@@ cl2 ABATED t1 @@@@@@@@@@");
546         simulateAbatement(requestId5, cl2, t1, kieSession2);
547
548         /*
549          * Print what's left in memory
550          */
551         dumpFacts(kieSession1);
552         dumpFacts(kieSession2);
553     }
554 }