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