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.drools.persistence.SystemPersistence;
67 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
68 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
69 import org.onap.policy.drools.system.PolicyController;
70 import org.onap.policy.drools.system.PolicyControllerConstants;
71 import org.onap.policy.drools.system.PolicyEngine;
72 import org.onap.policy.drools.system.PolicyEngineConstants;
73 import org.onap.policy.drools.util.KieUtils;
74 import org.onap.policy.drools.utils.logging.LoggerUtil;
75 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
76 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
77 import org.onap.policy.simulators.Util;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
82 * Use Cases Tests Framework.
84 public abstract class FrankfurtBase {
86 private static final Logger logger = LoggerFactory.getLogger(FrankfurtBase.class);
87 private static final StandardCoder coder = new StandardCoder();
92 protected static final PolicyEngine pdpD = PolicyEngineConstants.getManager();
95 * PDP-D Configuration Repository.
97 protected static final SystemPersistence repo = SystemPersistenceConstants.getManager();
100 * Frankfurt controller and session name.
102 protected static final String CONTROLLER_NAME = "frankfurt";
105 * Frankfurt controller.
107 protected static PolicyController controller;
110 * Canonical Topic Names.
112 protected static final String DCAE_TOPIC = "DCAE_TOPIC";
113 protected static final String APPC_LCM_WRITE_TOPIC = "APPC-LCM-WRITE";
114 protected static final String POLICY_CL_MGT_TOPIC = "POLICY-CL-MGT";
115 protected static final String APPC_LCM_READ_TOPIC = "APPC-LCM-READ";
116 protected static final String APPC_CL_TOPIC = "APPC-CL";
118 protected static void initConfigDir() {
119 SystemPersistenceConstants.getManager().setConfigurationDir("src/test/resources/config");
123 * Sets up overall logging.
125 protected static void setupLogging() {
126 LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "WARN");
127 LoggerUtil.setLevel("org.eclipse.jetty", "WARN");
128 LoggerUtil.setLevel("org.onap.policy.controlloop", "INFO");
129 LoggerUtil.setLevel("network", "INFO");
133 * Sets up Drools Logging for events of interest.
135 protected static void setupDroolsLogging() {
136 KieSession session = PolicyControllerConstants.getFactory().get(CONTROLLER_NAME).getDrools().getContainer()
137 .getPolicySession(CONTROLLER_NAME).getKieSession();
139 session.addEventListener(new RuleListenerLogger());
140 session.addEventListener(new AgendaListenerLogger());
144 * Sets up Http Clients specified in the property file.
146 protected static void setUpHttpClients() {
148 HttpClientFactoryInstance.getClientFactory().build(
149 SystemPersistenceConstants.getManager().getHttpClientProperties("frankfurt"));
150 } catch (HttpClientConfigException e) {
151 throw new IllegalArgumentException("cannot initialize HTTP clients", e);
156 * Sets up Simulators for use case testing.
158 protected static void setupSimulators() throws InterruptedException {
162 Util.buildGuardSim();
167 * Returns the runtime Control Loop Parameters associated with a Tosca Policy.
169 protected ControlLoopParams clParameters(ToscaPolicy policy) {
170 return controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
171 .filter((params) -> params.getToscaPolicy() == policy).findFirst().get();
174 protected ToscaPolicy getPolicyFromResource(String resourcePath, String policyName) throws CoderException {
175 String policyJson = ResourceUtils.getResourceAsString(resourcePath);
176 ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
177 ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
178 assertNotNull(policy);
181 * name and version are used within a drl. api component and drools core will
182 * ensure that these are populated.
184 if (StringUtils.isBlank(policy.getName())) {
185 policy.setName(policyName);
188 if (StringUtils.isBlank(policy.getVersion())) {
189 policy.setVersion(policy.getTypeVersion());
192 return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
195 protected ToscaPolicy getPolicyFromFile(String policyPath) throws IOException, CoderException {
196 String rawPolicy = new String(Files.readAllBytes(Paths.get(policyPath)));
197 return coder.decode(rawPolicy, ToscaPolicy.class);
200 private ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
201 final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
202 final KieObjectExpectedCallback<?> paramsTracker =
203 new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
205 controller.getDrools().offer(policy);
207 assertTrue(policyTracker.isNotified());
208 assertTrue(paramsTracker.isNotified());
210 assertEquals(1, controller.getDrools().facts(CONTROLLER_NAME, ToscaPolicy.class).stream()
211 .filter((anotherPolicy) -> anotherPolicy == policy).count());
213 assertEquals(1, controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
214 .filter((params) -> params.getToscaPolicy() == policy).count());
219 * Installs a policy from policy/models (examples) repo.
221 protected ToscaPolicy setupPolicyFromResource(String resourcePath, String policyName)
222 throws CoderException, InterruptedException {
223 return setupPolicy(getPolicyFromResource(resourcePath, policyName));
228 * Installs a given policy.
230 protected ToscaPolicy setupPolicyFromFile(String policyPath)
231 throws IOException, CoderException, InterruptedException {
232 return setupPolicy(getPolicyFromFile(policyPath));
238 protected void deletePolicy(ToscaPolicy policy) throws InterruptedException {
239 ControlLoopParams clParams = clParameters(policy);
240 assertNotNull(clParams);
242 final KieObjectExpectedCallback<?> policyTracker = new KieObjectDeletedExpectedCallback<>(policy);
243 final KieObjectExpectedCallback<?> clParamsTracker = new KieObjectDeletedExpectedCallback<>(clParams);
245 controller.getDrools().delete(CONTROLLER_NAME, policy);
246 assertTrue(policyTracker.isNotified());
247 assertTrue(clParamsTracker.isNotified());
249 assertEquals(0, controller.getDrools().facts(CONTROLLER_NAME, ToscaPolicy.class).stream()
250 .filter((anotherPolicy) -> anotherPolicy == policy).count());
252 assertEquals(0, controller.getDrools().facts(CONTROLLER_NAME, ControlLoopParams.class).stream()
253 .filter((params) -> params.getPolicyName() == policy.getName()).count());
257 * Prepare a PDP-D to test the Use Cases.
259 protected static void preparePdpD() throws IOException {
260 KieUtils.installArtifact(Paths.get("src/main/resources/META-INF/kmodule.xml").toFile(),
261 Paths.get("src/test/resources/frankfurt.pom").toFile(),
262 "src/main/resources/org/onap/policy/controlloop/",
263 Collections.singletonList(Paths.get("src/main/resources/frankfurt.drl").toFile()));
265 repo.setConfigurationDir("src/test/resources/config");
266 pdpD.configure(new Properties());
268 controller = pdpD.createPolicyController(CONTROLLER_NAME, repo.getControllerProperties(CONTROLLER_NAME));
271 setupDroolsLogging();
277 protected static void stopPdpD() {
278 PolicyControllerConstants.getFactory().shutdown(CONTROLLER_NAME);
283 * Stops the http clients.
285 protected static void stopHttpClients() {
286 HttpClientFactoryInstance.getClientFactory().destroy();
292 protected static void stopSimulators() {
293 HttpServletServerFactoryInstance.getServerFactory().destroy();
297 * Creates a Topic Sink Callback tracker.
299 protected <T> TopicCallback<T> createTopicSinkCallback(String topicName, Class<T> clazz) {
300 return new TopicCallback<>(TopicEndpointManager.getManager().getNoopTopicSink(topicName), clazz);
304 * Creates a Topic Sink Callback tracker.
306 protected <T> TopicCallback<T> createTopicSinkCallbackPlain(String topicName, Class<T> clazz, Coder coder) {
307 return new TopicCallbackCoder<>(TopicEndpointManager.getManager().getNoopTopicSink(topicName), clazz, coder);
311 * Creates a Topic Source Callback tracker.
313 protected <T> TopicCallback<T> createTopicSourceCallback(String topicName, Class<T> clazz) {
314 return new TopicCallback<>(TopicEndpointManager.getManager().getNoopTopicSource(topicName), clazz);
318 * Injects a message on a Topic Source.
320 protected void injectOnTopic(String topicName, Path onsetPath) throws IOException {
321 TopicEndpointManager.getManager().getNoopTopicSource(topicName)
322 .offer(new String(Files.readAllBytes(onsetPath)));
326 * Injects a message on a Topic Source, with the given substitution..
328 protected void injectOnTopic(String topicName, Path path, String newText) throws IOException {
329 String text = IOUtils.toString(path.toUri(), StandardCharsets.UTF_8);
330 text = text.replace("${replaceMe}", newText);
331 TopicEndpointManager.getManager().getNoopTopicSource(topicName).offer(text);
335 * Waits for LOCK acquisition and getting a Permit from PDP-X to proceed.
337 protected void waitForLockAndPermit(ToscaPolicy policy, TopicCallback<VirtualControlLoopNotification> policyClMgt) {
338 String policyName = policy.getIdentifier().getName();
340 // TODO register a topic listener instead of using await() ?
342 await().until(() -> !policyClMgt.getMessages().isEmpty());
343 VirtualControlLoopNotification notif = policyClMgt.getMessages().remove();
344 assertEquals(ControlLoopNotificationType.ACTIVE, notif.getNotification());
345 assertEquals(policyName + ".EVENT", notif.getPolicyName());
347 await().until(() -> !policyClMgt.getMessages().isEmpty());
348 notif = policyClMgt.getMessages().remove();
349 assertEquals(ControlLoopNotificationType.OPERATION, notif.getNotification());
350 assertEquals(policyName + ".EVENT.MANAGER.PROCESSING", notif.getPolicyName());
351 assertThat(notif.getMessage()).startsWith("Sending guard query");
353 await().until(() -> !policyClMgt.getMessages().isEmpty());
354 notif = policyClMgt.getMessages().remove();
355 assertEquals(ControlLoopNotificationType.OPERATION, notif.getNotification());
356 assertEquals(policyName + ".EVENT.MANAGER.PROCESSING", notif.getPolicyName());
357 assertThat(notif.getMessage()).startsWith("Guard result").endsWith("Permit");
361 * Waits for a FINAL SUCCESS transaction notification.
363 protected void waitForFinalSuccess(ToscaPolicy policy, TopicCallback<VirtualControlLoopNotification> policyClMgt) {
364 await().until(() -> !policyClMgt.getMessages().isEmpty());
365 assertEquals(ControlLoopNotificationType.FINAL_SUCCESS, policyClMgt.getMessages().peek().getNotification());
366 assertEquals(policy.getIdentifier().getName() + ".EVENT.MANAGER.FINAL",
367 policyClMgt.getMessages().remove().getPolicyName());
371 * Logs Modifications to Working Memory.
373 static class RuleListenerLogger implements RuleRuntimeEventListener {
375 public void objectInserted(ObjectInsertedEvent event) {
376 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
377 logger.info("RULE {}: inserted {}", ruleName, event.getObject());
381 public void objectUpdated(ObjectUpdatedEvent event) {
382 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
383 logger.info("RULE {}: updated {}", ruleName, event.getObject());
388 public void objectDeleted(ObjectDeletedEvent event) {
389 String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
390 logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
397 static class AgendaListenerLogger extends DefaultAgendaEventListener {
399 public void matchCreated(MatchCreatedEvent event) {
400 logger.info("RULE {}: match created", event.getMatch().getRule().getName());
404 public void matchCancelled(MatchCancelledEvent event) {
405 logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
409 public void beforeMatchFired(BeforeMatchFiredEvent event) {
410 logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
414 public void afterMatchFired(AfterMatchFiredEvent event) {
415 logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
420 * Base Class to track Working Memory updates for objects of type T.
422 abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
425 protected CountDownLatch countDownLatch = new CountDownLatch(1);
427 public KieObjectExpectedCallback(T affected) {
432 public boolean isNotified() throws InterruptedException {
433 return countDownLatch.await(9L, TimeUnit.SECONDS);
436 protected void callbacked() {
438 countDownLatch.countDown();
441 public KieObjectExpectedCallback<T> register() {
442 controller.getDrools().getContainer().getPolicySession(CONTROLLER_NAME).getKieSession()
443 .addEventListener(this);
447 public KieObjectExpectedCallback<T> unregister() {
448 controller.getDrools().getContainer().getPolicySession(CONTROLLER_NAME).getKieSession()
449 .removeEventListener(this);
455 * Tracks inserts in Working Memory for an object of type T.
457 class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
458 public KieObjectInsertedExpectedCallback(T affected) {
463 public void objectInserted(ObjectInsertedEvent event) {
464 if (subject == event.getObject()) {
471 * Tracks deletes in Working Memory of an object of type T.
473 class KieObjectDeletedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
474 public KieObjectDeletedExpectedCallback(T affected) {
479 public void objectDeleted(ObjectDeletedEvent event) {
480 if (subject == event.getOldObject()) {
487 * Tracks inserts in Working Memory for any object of class T.
489 class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
491 public KieClassInsertedExpectedCallback(T affected) {
495 public void objectInserted(ObjectInsertedEvent event) {
496 if (subject == event.getObject().getClass()) {
503 * Tracks callbacks from topics.
505 class TopicCallback<T> implements TopicListener {
506 protected final Topic topic;
507 protected final Class<T> expectedClass;
510 protected Queue<T> messages = new LinkedList<>();
512 public TopicCallback(Topic topic, Class<T> expectedClass) {
514 this.expectedClass = expectedClass;
515 this.topic.register(this);
518 public TopicCallback<T> register() {
519 this.topic.register(this);
523 public TopicCallback<T> unregister() {
524 this.topic.unregister(this);
529 @SuppressWarnings("unchecked")
530 public void onTopicEvent(CommInfrastructure comm, String topic, String event) {
532 messages.add((T) EventProtocolCoderConstants.getManager().decode(controller.getDrools().getGroupId(),
533 controller.getDrools().getArtifactId(), topic, event));
534 } catch (Exception e) {
535 logger.warn("invalid mapping in topic {} for event {}", topic, event, e);
540 class TopicCallbackCoder<T> extends TopicCallback<T> {
541 private final Coder coder;
543 public TopicCallbackCoder(Topic topic, Class<T> expectedClass, Coder coder) {
544 super(topic, expectedClass);
549 public void onTopicEvent(CommInfrastructure comm, String topic, String event) {
551 messages.add((T) coder.decode(event, expectedClass));
552 } catch (Exception e) {
553 logger.warn("invalid mapping in topic {} for event {}", topic, event, e);