2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.template.demo.clc;
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;
28 import com.att.research.xacml.util.XACMLProperties;
30 import com.google.gson.Gson;
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;
41 import org.junit.AfterClass;
42 import org.junit.BeforeClass;
43 import org.junit.Test;
45 import org.kie.api.runtime.KieSession;
46 import org.kie.api.runtime.rule.FactHandle;
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;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class ControlLoopCoordinationTest implements TopicListener {
74 private static final Logger logger = LoggerFactory.getLogger(ControlLoopCoordinationTest.class);
76 private static List<? extends TopicSink> noopTopics;
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;
85 /* Set environment properties */
87 Util.setGuardPropsEmbedded();
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);
109 EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT",
110 "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null, null,
112 EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-LCM-READ",
113 "org.onap.policy.appclcm.LcmRequestWrapper", new JsonProtocolFilter(), null, null, 1111);
116 } catch (Exception e) {
117 fail(e.getMessage());
121 * Start the kie sessions
124 kieSession1 = startSession(
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(
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");
145 * Tear down simulator.
148 public static void tearDownSimulator() {
150 * Gracefully shut down the kie session
152 kieSession1.dispose();
153 kieSession2.dispose();
155 PolicyEngine.manager.stop();
156 HttpServletServer.factory.destroy();
157 PolicyController.factory.shutdown();
158 TopicEndpoint.manager.shutdown();
162 * Set expected decision.
164 * @param ed the expected decision ("PERMIT" or "DENY")
166 public void expectedDecisionIs(String ed) {
167 expectedDecision = ed;
168 logger.info("Expected decision is {}", ed);
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).
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
182 protected void sendEvent(String controlLoopName,
184 ControlLoopEventStatus status,
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);
198 Gson gson = new Gson();
199 String json = gson.toJson(event);
200 logger.debug("sendEvent {}", json);
202 kieSession.insert(event);
207 * Simulate an event by inserting into kieSession and firing rules as needed.
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
215 protected void simulateEvent(ControlLoopEventStatus cles,
217 String controlLoopName,
219 KieSession kieSession,
220 String expectedDecision) {
221 int waitMillis = 5000;
223 // if onset, set expected decision
225 if (cles == ControlLoopEventStatus.ONSET) {
226 expectedDecisionIs(expectedDecision);
229 // simulate sending event
231 sendEvent(controlLoopName, rid, cles, target, kieSession);
232 kieSession.fireUntilHalt();
234 // get dump of database entries and log
236 List entries = Util.dumpDb();
237 assertNotNull(entries);
238 logger.debug("dumpDB, {} entries", entries.size());
239 for (Object entry : entries) {
240 logger.debug("{}", entry);
245 logger.info("simulateEvent: done");
249 * Simulate an onset event.
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
256 public void simulateOnset(UUID rid,
257 String controlLoopName,
259 KieSession kieSession,
260 String expectedDecision) {
261 simulateEvent(ControlLoopEventStatus.ONSET, rid, controlLoopName, target, kieSession, expectedDecision);
265 * Simulate an abated event.
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
271 public void simulateAbatement(UUID rid,
272 String controlLoopName,
274 KieSession kieSession) {
275 simulateEvent(ControlLoopEventStatus.ABATED, rid, controlLoopName, target, kieSession, null);
279 * This method will start a kie session and instantiate the Policy Engine.
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
289 private static KieSession startSession(StringBuilder controlLoopName,
290 String droolsTemplate,
294 String policyVersion) throws IOException {
297 * Load policies from yaml
299 Util.Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile);
301 assertNotNull(pair.first);
302 assertNotNull(pair.first.getControlLoop());
303 assertNotNull(pair.first.getControlLoop().getControlLoopName());
304 assertTrue(!pair.first.getControlLoop().getControlLoopName().isEmpty());
306 controlLoopName.append(pair.first.getControlLoop().getControlLoopName());
307 String yamlContents = pair.second;
310 * Construct a kie session
312 final KieSession kieSession = Util.buildContainer(droolsTemplate,
313 controlLoopName.toString(),
317 URLEncoder.encode(yamlContents, "UTF-8"));
320 * Retrieve the Policy Engine
323 logger.debug("============");
324 logger.debug(URLEncoder.encode(yamlContents, "UTF-8"));
325 logger.debug("============");
333 * @see org.onap.policy.drools.PolicyEngineListener#newEventNotification(java.lang.String)
336 public void onTopicEvent(CommInfrastructure commType, String topic, String event) {
338 * Pull the object that was sent out to DMAAP and make sure it is a ControlLoopNoticiation
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);
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());
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");
392 } else if (policyName.startsWith(controlLoopTwoName.toString())) {
393 logger.debug("Halting kieSession2");
396 fail("Unknown ControlLoop");
399 } else if (policyName.endsWith("EVENT.MANAGER.TIMEOUT")) {
400 logger.debug("Rule Fired: " + notification.getPolicyName());
403 logger.debug("The control loop timed out");
404 fail("Control Loop Timed Out");
406 } else if (obj instanceof LcmRequestWrapper) {
408 * The request should be of type LCMRequestWrapper and the subrequestid should be 1
410 LcmRequestWrapper dmaapRequest = (LcmRequestWrapper) obj;
411 LcmRequest appcRequest = dmaapRequest.getBody();
412 assertEquals(appcRequest.getCommonHeader().getSubRequestId(),"1");
414 logger.debug("\n============ APPC received the request!!! ===========\n");
417 * Simulate a success response from APPC and insert the response into the working memory
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);
430 * This method will dump all the facts in the working memory.
432 * @param kieSession the session containing the facts
434 public void dumpFacts(KieSession kieSession) {
435 logger.debug("Fact Count: {}", kieSession.getFactCount());
436 for (FactHandle handle : kieSession.getFactHandles()) {
437 logger.debug("FACT: {}", handle);
442 * Test that SyntheticControlLoopOne blocks SyntheticControlLoopTwo
443 * is enforced correctly.
446 public void testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo() throws InterruptedException {
447 logger.info("Beginning testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo");
449 * Allows the PolicyEngine to callback to this object to
450 * notify that there is an event ready to be pulled
453 for (TopicSink sink : noopTopics) {
454 assertTrue(sink.start());
459 * Create unique requestIds
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";
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);
491 * Print what's left in memory
493 dumpFacts(kieSession1);
494 dumpFacts(kieSession2);