2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 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.controlloop;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.awaitility.Awaitility.await;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertTrue;
29 import java.io.IOException;
30 import java.nio.charset.StandardCharsets;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.Collections;
35 import java.util.LinkedList;
36 import java.util.Properties;
37 import java.util.Queue;
38 import java.util.concurrent.CountDownLatch;
39 import java.util.concurrent.TimeUnit;
41 import org.apache.commons.io.IOUtils;
42 import org.apache.commons.lang3.StringUtils;
43 import org.kie.api.event.rule.AfterMatchFiredEvent;
44 import org.kie.api.event.rule.BeforeMatchFiredEvent;
45 import org.kie.api.event.rule.DefaultAgendaEventListener;
46 import org.kie.api.event.rule.DefaultRuleRuntimeEventListener;
47 import org.kie.api.event.rule.MatchCancelledEvent;
48 import org.kie.api.event.rule.MatchCreatedEvent;
49 import org.kie.api.event.rule.ObjectDeletedEvent;
50 import org.kie.api.event.rule.ObjectInsertedEvent;
51 import org.kie.api.event.rule.ObjectUpdatedEvent;
52 import org.kie.api.event.rule.RuleRuntimeEventListener;
53 import org.kie.api.runtime.KieSession;
54 import org.onap.policy.common.endpoints.event.comm.Topic;
55 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
56 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
57 import org.onap.policy.common.endpoints.event.comm.TopicListener;
58 import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
59 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
60 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
61 import org.onap.policy.common.utils.coder.Coder;
62 import org.onap.policy.common.utils.coder.CoderException;
63 import org.onap.policy.common.utils.coder.StandardCoder;
64 import org.onap.policy.common.utils.resources.ResourceUtils;
65 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
66 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
67 import org.onap.policy.drools.controller.DroolsController;
68 import org.onap.policy.drools.persistence.SystemPersistence;
69 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
70 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
71 import org.onap.policy.drools.system.PolicyController;
72 import org.onap.policy.drools.system.PolicyControllerConstants;
73 import org.onap.policy.drools.system.PolicyEngine;
74 import org.onap.policy.drools.system.PolicyEngineConstants;
75 import org.onap.policy.drools.util.KieUtils;
76 import org.onap.policy.drools.utils.logging.LoggerUtil;
77 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
78 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
79 import org.onap.policy.simulators.Util;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
84 * Use Cases Tests Framework.
86 public abstract class FrankfurtBase {
88 private static final Logger logger = LoggerFactory.getLogger(FrankfurtBase.class);
89 private static final StandardCoder coder = new StandardCoder();
94 protected static final PolicyEngine pdpD = PolicyEngineConstants.getManager();
97 * PDP-D Configuration Repository.
99 protected static final SystemPersistence repo = SystemPersistenceConstants.getManager();
102 * Frankfurt controller and session name.
104 protected static final String CONTROLLER_NAME = "frankfurt";
107 * Frankfurt controller.
109 protected static PolicyController controller;
112 * Canonical Topic Names.
114 protected static final String DCAE_TOPIC = "DCAE_TOPIC";
115 protected static final String APPC_LCM_WRITE_TOPIC = "APPC-LCM-WRITE";
116 protected static final String POLICY_CL_MGT_TOPIC = "POLICY-CL-MGT";
117 protected static final String APPC_LCM_READ_TOPIC = "APPC-LCM-READ";
118 protected static final String APPC_CL_TOPIC = "APPC-CL";
120 protected static void initConfigDir() {
121 SystemPersistenceConstants.getManager().setConfigurationDir("src/test/resources/config");
124 protected void resetFacts() {
125 DroolsController drools = controller.getDrools();
126 drools.delete(ToscaPolicy.class);
127 drools.delete(ControlLoopParams.class);
128 drools.delete(ControlLoopEventManager2.class);
129 drools.delete(VirtualControlLoopEvent.class);
133 * Sets up overall logging.
135 protected static void setupLogging() {
136 LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "WARN");
137 LoggerUtil.setLevel("org.eclipse.jetty", "WARN");
138 LoggerUtil.setLevel("org.onap.policy.controlloop", "INFO");
139 LoggerUtil.setLevel("network", "INFO");
143 * Sets up Drools Logging for events of interest.
145 protected static void setupDroolsLogging() {
146 KieSession session = PolicyControllerConstants.getFactory().get(CONTROLLER_NAME).getDrools().getContainer()
147 .getPolicySession(CONTROLLER_NAME).getKieSession();
149 session.addEventListener(new RuleListenerLogger());
150 session.addEventListener(new AgendaListenerLogger());
154 * Sets up Http Clients specified in the property file.
156 protected static void setUpHttpClients() {
158 HttpClientFactoryInstance.getClientFactory().build(
159 SystemPersistenceConstants.getManager().getHttpClientProperties("frankfurt"));
160 } catch (HttpClientConfigException e) {
161 throw new IllegalArgumentException("cannot initialize HTTP clients", e);
166 * Sets up Simulators for use case testing.
168 protected static void setupSimulators() throws InterruptedException {
172 Util.buildGuardSim();
177 * Returns the runtime Control Loop Parameters associated with a Tosca Policy.
179 protected ControlLoopParams clParameters(ToscaPolicy policy) {
180 return controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
181 .filter((params) -> params.getToscaPolicy() == policy).findFirst().get();
184 protected ToscaPolicy getPolicyFromResource(String resourcePath, String policyName) throws CoderException {
185 String policyJson = ResourceUtils.getResourceAsString(resourcePath);
186 ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
187 ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
188 assertNotNull(policy);
191 * name and version are used within a drl. api component and drools core will
192 * ensure that these are populated.
194 if (StringUtils.isBlank(policy.getName())) {
195 policy.setName(policyName);
198 if (StringUtils.isBlank(policy.getVersion())) {
199 policy.setVersion(policy.getTypeVersion());
202 return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
205 protected ToscaPolicy getPolicyFromFile(String policyPath) throws IOException, CoderException {
206 String rawPolicy = new String(Files.readAllBytes(Paths.get(policyPath)));
207 return coder.decode(rawPolicy, ToscaPolicy.class);
210 private ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
211 final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
212 final KieObjectExpectedCallback<?> paramsTracker =
213 new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
215 controller.getDrools().offer(policy);
217 assertTrue(policyTracker.isNotified());
218 assertTrue(paramsTracker.isNotified());
220 assertEquals(1, controller.getDrools().facts(CONTROLLER_NAME, ToscaPolicy.class).stream()
221 .filter((anotherPolicy) -> anotherPolicy == policy).count());
223 assertEquals(1, controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
224 .filter((params) -> params.getToscaPolicy() == policy).count());
229 * Installs a policy from policy/models (examples) repo.
231 protected ToscaPolicy setupPolicyFromResource(String resourcePath, String policyName)
232 throws CoderException, InterruptedException {
233 return setupPolicy(getPolicyFromResource(resourcePath, policyName));
238 * Installs a given policy.
240 protected ToscaPolicy setupPolicyFromFile(String policyPath)
241 throws IOException, CoderException, InterruptedException {
242 return setupPolicy(getPolicyFromFile(policyPath));
248 protected void deletePolicy(ToscaPolicy policy) throws InterruptedException {
249 ControlLoopParams clParams = clParameters(policy);
250 assertNotNull(clParams);
252 final KieObjectExpectedCallback<?> policyTracker = new KieObjectDeletedExpectedCallback<>(policy);
253 final KieObjectExpectedCallback<?> clParamsTracker = new KieObjectDeletedExpectedCallback<>(clParams);
255 controller.getDrools().delete(CONTROLLER_NAME, policy);
256 assertTrue(policyTracker.isNotified());
257 assertTrue(clParamsTracker.isNotified());
259 assertEquals(0, controller.getDrools().facts(CONTROLLER_NAME, ToscaPolicy.class).stream()
260 .filter((anotherPolicy) -> anotherPolicy == policy).count());
262 assertEquals(0, controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
263 .filter((params) -> params.getPolicyName() == policy.getName()).count());
267 * Prepare a PDP-D to test the Use Cases.
269 protected static void preparePdpD() throws IOException {
270 KieUtils.installArtifact(Paths.get("src/main/resources/META-INF/kmodule.xml").toFile(),
271 Paths.get("src/test/resources/frankfurt.pom").toFile(),
272 "src/main/resources/org/onap/policy/controlloop/",
273 Collections.singletonList(Paths.get("src/main/resources/frankfurt.drl").toFile()));
275 repo.setConfigurationDir("src/test/resources/config");
276 pdpD.configure(new Properties());
278 controller = pdpD.createPolicyController(CONTROLLER_NAME, repo.getControllerProperties(CONTROLLER_NAME));
281 setupDroolsLogging();
287 protected static void stopPdpD() {
288 PolicyControllerConstants.getFactory().shutdown(CONTROLLER_NAME);
293 * Stops the http clients.
295 protected static void stopHttpClients() {
296 HttpClientFactoryInstance.getClientFactory().destroy();
302 protected static void stopSimulators() {
303 HttpServletServerFactoryInstance.getServerFactory().destroy();
307 * Creates a Topic Sink Callback tracker.
309 protected <T> TopicCallback<T> createTopicSinkCallback(String topicName, Class<T> clazz) {
310 return new TopicCallback<>(TopicEndpointManager.getManager().getNoopTopicSink(topicName), clazz);
314 * Creates a Topic Sink Callback tracker.
316 protected <T> TopicCallback<T> createTopicSinkCallbackPlain(String topicName, Class<T> clazz, Coder coder) {
317 return new TopicCallbackCoder<>(TopicEndpointManager.getManager().getNoopTopicSink(topicName), clazz, coder);
321 * Creates a Topic Source Callback tracker.
323 protected <T> TopicCallback<T> createTopicSourceCallback(String topicName, Class<T> clazz) {
324 return new TopicCallback<>(TopicEndpointManager.getManager().getNoopTopicSource(topicName), clazz);
328 * Injects a message on a Topic Source.
330 protected void injectOnTopic(String topicName, Path onsetPath) throws IOException {
331 TopicEndpointManager.getManager().getNoopTopicSource(topicName)
332 .offer(new String(Files.readAllBytes(onsetPath)));
336 * Injects a message on a Topic Source, with the given substitution..
338 protected void injectOnTopic(String topicName, Path path, String newText) throws IOException {
339 String text = IOUtils.toString(path.toUri(), StandardCharsets.UTF_8);
340 text = text.replace("${replaceMe}", newText);
341 TopicEndpointManager.getManager().getNoopTopicSource(topicName).offer(text);
345 * Waits for LOCK acquisition and getting a Permit from PDP-X to proceed.
347 protected void waitForLockAndPermit(ToscaPolicy policy, TopicCallback<VirtualControlLoopNotification> policyClMgt) {
348 String policyName = policy.getIdentifier().getName();
350 // TODO register a topic listener instead of using await() ?
352 await().until(() -> !policyClMgt.getMessages().isEmpty());
353 VirtualControlLoopNotification notif = policyClMgt.getMessages().remove();
354 assertEquals(ControlLoopNotificationType.ACTIVE, notif.getNotification());
355 assertEquals(policyName + ".EVENT", notif.getPolicyName());
357 await().until(() -> !policyClMgt.getMessages().isEmpty());
358 notif = policyClMgt.getMessages().remove();
359 assertEquals(ControlLoopNotificationType.OPERATION, notif.getNotification());
360 assertEquals(policyName + ".EVENT.MANAGER.PROCESSING", notif.getPolicyName());
361 assertThat(notif.getMessage()).startsWith("Sending guard query");
363 await().until(() -> !policyClMgt.getMessages().isEmpty());
364 notif = policyClMgt.getMessages().remove();
365 assertEquals(ControlLoopNotificationType.OPERATION, notif.getNotification());
366 assertEquals(policyName + ".EVENT.MANAGER.PROCESSING", notif.getPolicyName());
367 assertThat(notif.getMessage()).startsWith("Guard result").endsWith("Permit");
369 await().until(() -> !policyClMgt.getMessages().isEmpty());
370 notif = policyClMgt.getMessages().remove();
371 assertEquals(ControlLoopNotificationType.OPERATION, notif.getNotification());
372 assertEquals(policyName + ".EVENT.MANAGER.PROCESSING", notif.getPolicyName());
373 assertThat(notif.getMessage()).startsWith("actor=");
377 * Waits for a FINAL SUCCESS transaction notification.
379 protected void waitForFinalSuccess(ToscaPolicy policy, TopicCallback<VirtualControlLoopNotification> policyClMgt) {
380 await().until(() -> !policyClMgt.getMessages().isEmpty());
381 assertEquals(ControlLoopNotificationType.FINAL_SUCCESS, policyClMgt.getMessages().peek().getNotification());
382 assertEquals(policy.getIdentifier().getName() + ".EVENT.MANAGER.FINAL",
383 policyClMgt.getMessages().remove().getPolicyName());
387 * Logs Modifications to Working Memory.
389 static class RuleListenerLogger implements RuleRuntimeEventListener {
391 public void objectInserted(ObjectInsertedEvent event) {
392 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
393 logger.info("RULE {}: inserted {}", ruleName, event.getObject());
397 public void objectUpdated(ObjectUpdatedEvent event) {
398 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
399 logger.info("RULE {}: updated {}", ruleName, event.getObject());
404 public void objectDeleted(ObjectDeletedEvent event) {
405 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
406 logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
413 static class AgendaListenerLogger extends DefaultAgendaEventListener {
415 public void matchCreated(MatchCreatedEvent event) {
416 logger.info("RULE {}: match created", event.getMatch().getRule().getName());
420 public void matchCancelled(MatchCancelledEvent event) {
421 logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
425 public void beforeMatchFired(BeforeMatchFiredEvent event) {
426 logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
430 public void afterMatchFired(AfterMatchFiredEvent event) {
431 logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
436 * Base Class to track Working Memory updates for objects of type T.
438 abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
441 protected CountDownLatch countDownLatch = new CountDownLatch(1);
443 public KieObjectExpectedCallback(T affected) {
448 public boolean isNotified() throws InterruptedException {
449 return countDownLatch.await(9L, TimeUnit.SECONDS);
452 protected void callbacked() {
454 countDownLatch.countDown();
457 public KieObjectExpectedCallback<T> register() {
458 controller.getDrools().getContainer().getPolicySession(CONTROLLER_NAME).getKieSession()
459 .addEventListener(this);
463 public KieObjectExpectedCallback<T> unregister() {
464 controller.getDrools().getContainer().getPolicySession(CONTROLLER_NAME).getKieSession()
465 .removeEventListener(this);
471 * Tracks inserts in Working Memory for an object of type T.
473 class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
474 public KieObjectInsertedExpectedCallback(T affected) {
479 public void objectInserted(ObjectInsertedEvent event) {
480 if (subject == event.getObject()) {
487 * Tracks deletes in Working Memory of an object of type T.
489 class KieObjectDeletedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
490 public KieObjectDeletedExpectedCallback(T affected) {
495 public void objectDeleted(ObjectDeletedEvent event) {
496 if (subject == event.getOldObject()) {
503 * Tracks inserts in Working Memory for any object of class T.
505 class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
507 public KieClassInsertedExpectedCallback(T affected) {
511 public void objectInserted(ObjectInsertedEvent event) {
512 if (subject == event.getObject().getClass()) {
519 * Tracks callbacks from topics.
521 class TopicCallback<T> implements TopicListener {
522 protected final Topic topic;
523 protected final Class<T> expectedClass;
526 protected Queue<T> messages = new LinkedList<>();
528 public TopicCallback(Topic topic, Class<T> expectedClass) {
530 this.expectedClass = expectedClass;
531 this.topic.register(this);
534 public TopicCallback<T> register() {
535 this.topic.register(this);
539 public TopicCallback<T> unregister() {
540 this.topic.unregister(this);
545 @SuppressWarnings("unchecked")
546 public void onTopicEvent(CommInfrastructure comm, String topic, String event) {
548 messages.add((T) EventProtocolCoderConstants.getManager().decode(controller.getDrools().getGroupId(),
549 controller.getDrools().getArtifactId(), topic, event));
550 } catch (Exception e) {
551 logger.warn("invalid mapping in topic {} for event {}", topic, event, e);
556 class TopicCallbackCoder<T> extends TopicCallback<T> {
557 private final Coder coder;
559 public TopicCallbackCoder(Topic topic, Class<T> expectedClass, Coder coder) {
560 super(topic, expectedClass);
565 public void onTopicEvent(CommInfrastructure comm, String topic, String event) {
567 messages.add((T) coder.decode(event, expectedClass));
568 } catch (Exception e) {
569 logger.warn("invalid mapping in topic {} for event {}", topic, event, e);