711a61738e1c389ec41927f059d0b90235abe68d
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
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.controlloop.common.rules.test;
22
23 import static org.junit.Assert.assertEquals;
24
25 import java.util.List;
26 import java.util.UUID;
27 import java.util.function.Function;
28 import java.util.function.Supplier;
29 import java.util.stream.Collectors;
30 import lombok.AccessLevel;
31 import lombok.Getter;
32 import org.junit.Test;
33 import org.onap.policy.appc.Request;
34 import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
35 import org.onap.policy.common.utils.coder.Coder;
36 import org.onap.policy.common.utils.coder.StandardCoder;
37 import org.onap.policy.common.utils.coder.StandardCoderInstantAsMillis;
38 import org.onap.policy.controlloop.ControlLoopNotificationType;
39 import org.onap.policy.controlloop.VirtualControlLoopNotification;
40 import org.onap.policy.drools.system.PolicyController;
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
42
43 /**
44  * Superclass used for rule tests.
45  */
46 public abstract class BaseRuleTest {
47     /*
48      * Canonical Topic Names.
49      */
50     protected static final String DCAE_TOPIC = "DCAE_TOPIC";
51     protected static final String APPC_LCM_WRITE_TOPIC = "APPC-LCM-WRITE";
52     protected static final String POLICY_CL_MGT_TOPIC = "POLICY-CL-MGT";
53     protected static final String APPC_LCM_READ_TOPIC = "APPC-LCM-READ";
54     protected static final String APPC_CL_TOPIC = "APPC-CL";
55
56     /*
57      * Constants for each test case.
58      */
59
60     // service123 (i.e., multi-operation policy)
61     private static final String SERVICE123_TOSCA_COMPLIANT_POLICY = "service123/tosca-compliant-service123.json";
62     private static final String SERVICE123_ONSET = "service123/service123.onset.json";
63     private static final String SERVICE123_APPC_RESTART_FAILURE = "service123/service123.appc.restart.failure.json";
64     private static final String SERVICE123_APPC_REBUILD_FAILURE = "service123/service123.appc.rebuild.failure.json";
65     private static final String SERVICE123_APPC_MIGRATE_SUCCESS = "service123/service123.appc.migrate.success.json";
66
67     // duplicates (i.e., mutliple events in the engine at the same time)
68     private static final String DUPLICATES_TOSCA_COMPLIANT_POLICY = "duplicates/tosca-compliant-duplicates.json";
69     private static final String DUPLICATES_ONSET_1 = "duplicates/duplicates.onset.1.json";
70     private static final String DUPLICATES_ONSET_2 = "duplicates/duplicates.onset.2.json";
71     private static final String DUPLICATES_APPC_SUCCESS = "duplicates/duplicates.appc.success.json";
72
73     // VCPE
74     private static final String VCPE_TOSCA_LEGACY_POLICY = "vcpe/tosca-legacy-vcpe.json";
75     private static final String VCPE_TOSCA_COMPLIANT_POLICY = "vcpe/tosca-compliant-vcpe.json";
76     private static final String VCPE_ONSET_1 = "vcpe/vcpe.onset.1.json";
77     private static final String VCPE_ONSET_2 = "vcpe/vcpe.onset.2.json";
78     private static final String VCPE_ONSET_3 = "vcpe/vcpe.onset.3.json";
79     private static final String VCPE_APPC_SUCCESS = "vcpe/vcpe.appc.success.json";
80
81     // VDNS
82     private static final String VDNS_TOSCA_COMPLIANT_POLICY = "vdns/tosca-compliant-vdns.json";
83     private static final String VDNS_ONSET = "vdns/vdns.onset.json";
84
85     // VFW
86     private static final String VFW_TOSCA_LEGACY_POLICY = "vfw/tosca-vfw.json";
87     private static final String VFW_TOSCA_COMPLIANT_POLICY = "vfw/tosca-compliant-vfw.json";
88     private static final String VFW_ONSET = "vfw/vfw.onset.json";
89     private static final String VFW_APPC_SUCCESS = "vfw/vfw.appc.success.json";
90
91     // VLB
92     private static final String VLB_TOSCA_LEGACY_POLICY = "vlb/tosca-vlb.json";
93     private static final String VLB_TOSCA_COMPLIANT_POLICY = "vlb/tosca-compliant-vlb.json";
94     private static final String VLB_ONSET = "vlb/vlb.onset.json";
95
96     /*
97      * Coders used to decode requests and responses.
98      */
99     private static final Coder APPC_LEGACY_CODER = new StandardCoderInstantAsMillis();
100     private static final Coder APPC_LCM_CODER = new StandardCoder();
101
102     // these may be overridden by junit tests
103     private static Function<String, Rules> ruleMaker = Rules::new;
104     private static Supplier<HttpClients> httpClientMaker = HttpClients::new;
105     private static Supplier<Simulators> simMaker = Simulators::new;
106     private static Supplier<Topics> topicMaker = Topics::new;
107
108     protected static Rules rules;
109     protected static HttpClients httpClients;
110     protected static Simulators simulators;
111
112
113     // used to inject and wait for messages
114     @Getter(AccessLevel.PROTECTED)
115     private Topics topics;
116
117     // used to wait for messages on SINK topics
118     protected Listener<VirtualControlLoopNotification> policyClMgt;
119     protected Listener<Request> appcClSink;
120     protected Listener<AppcLcmDmaapWrapper> appcLcmRead;
121
122     protected PolicyController controller;
123
124     /*
125      * Tosca Policy that was loaded.
126      */
127     protected ToscaPolicy policy;
128
129
130     /**
131      * Initializes {@link #rules}, {@link #httpClients}, and {@link #simulators}.
132      *
133      * @param controllerName the rule controller name
134      */
135     public static void initStatics(String controllerName) {
136         rules = ruleMaker.apply(controllerName);
137         httpClients = httpClientMaker.get();
138         simulators = simMaker.get();
139     }
140
141     /**
142      * Destroys {@link #httpClients}, {@link #simulators}, and {@link #rules}.
143      */
144     public static void finishStatics() {
145         httpClients.destroy();
146         simulators.destroy();
147         rules.destroy();
148     }
149
150     /**
151      * Initializes {@link #topics} and {@link #controller}.
152      */
153     public void init() {
154         topics = topicMaker.get();
155         controller = rules.getController();
156     }
157
158     /**
159      * Destroys {@link #topics} and resets the rule facts.
160      */
161     public void finish() {
162         topics.destroy();
163         rules.resetFacts();
164     }
165
166     // Service123 (i.e., Policy with multiple operations)
167
168     /**
169      * Service123 with Tosca Compliant Policy.
170      */
171     @Test
172     public void testService123Compliant() {
173         policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
174         appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
175
176         assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
177         policy = rules.setupPolicyFromFile(SERVICE123_TOSCA_COMPLIANT_POLICY);
178         assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
179
180         // inject an ONSET event over the DCAE topic
181         topics.inject(DCAE_TOPIC, SERVICE123_ONSET);
182
183         /* Wait to acquire a LOCK and a PDP-X PERMIT */
184         waitForLockAndPermit(policy, policyClMgt);
185
186         // restart request should be sent and fail four times (i.e., because retry=3)
187         for (int count = 0; count < 4; ++count) {
188             AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName()));
189
190             topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_RESTART_FAILURE,
191                             appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
192         }
193
194         // rebuild request should be sent and fail once
195         AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "rebuild".equals(req.getRpcName()));
196
197         topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_REBUILD_FAILURE,
198                         appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
199
200         // migrate request should be sent and succeed
201         appcreq = appcLcmRead.await(req -> "migrate".equals(req.getRpcName()));
202
203         topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_MIGRATE_SUCCESS,
204                         appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
205
206         /* --- Operation Completed --- */
207
208         waitForOperationSuccess();
209
210         /* --- Transaction Completed --- */
211         waitForFinalSuccess(policy, policyClMgt);
212     }
213
214     // Duplicate events
215
216     /**
217      * This test case tests the scenario where 3 events occur and 2 of the requests refer
218      * to the same target entity while the 3rd is for another entity. The expected result
219      * is that the event with the duplicate target entity will have a final success result
220      * for one of the events, and a rejected message for the one that was unable to obtain
221      * the lock. The event that is referring to a different target entity should be able
222      * to obtain a lock since it is a different target. After processing of all events
223      * there should only be the policy and params objects left in memory.
224      */
225     @Test
226     public void testDuplicatesEvents() {
227         policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
228         appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
229
230         assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
231         policy = rules.setupPolicyFromFile(DUPLICATES_TOSCA_COMPLIANT_POLICY);
232         assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
233
234         /*
235          * Inject ONSET events over the DCAE topic. First and last have the same target
236          * entity, but different request IDs - only one should succeed. The middle one is
237          * for a different target entity, so it should succeed.
238          */
239         topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString());
240         topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_2);
241         topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString());
242
243         // one should immediately generate a FINAL failure
244         waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_FAILURE);
245
246         // should see two restarts
247         for (int count = 0; count < 2; ++count) {
248             AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName()));
249
250             // indicate success
251             topics.inject(APPC_LCM_WRITE_TOPIC, DUPLICATES_APPC_SUCCESS,
252                             appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
253         }
254
255         // should see two FINAL successes
256         VirtualControlLoopNotification notif1 = waitForFinalSuccess(policy, policyClMgt);
257         VirtualControlLoopNotification notif2 = waitForFinalSuccess(policy, policyClMgt);
258
259         // get the list of target names so we can ensure there's one of each
260         List<String> actual = List.of(notif1, notif2).stream().map(notif -> notif.getAai().get("generic-vnf.vnf-id"))
261                         .sorted().collect(Collectors.toList());
262
263         assertEquals(List.of("duplicate-VNF", "vCPE_Infrastructure_vGMUX_demo_app").toString(), actual.toString());
264     }
265
266     // VCPE
267
268     /**
269      * Sunny Day with Legacy Tosca Policy.
270      */
271     @Test
272     public void testVcpeSunnyDayLegacy() {
273         appcLcmSunnyDay(VCPE_TOSCA_LEGACY_POLICY, VCPE_ONSET_1, "restart");
274     }
275
276     /**
277      * Sunny Day with Tosca Compliant Policy.
278      */
279     @Test
280     public void testVcpeSunnyDayCompliant() {
281         appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, VCPE_ONSET_1, "restart");
282     }
283
284     /**
285      * An ONSET flood prevention test that injects a few ONSETs at once. It attempts to
286      * simulate the flooding behavior of the DCAE TCA microservice. TCA could blast tens
287      * or hundreds of ONSETs within sub-second intervals.
288      */
289     @Test
290     public void testVcpeOnsetFloodPrevention() {
291         appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, List.of(VCPE_ONSET_1, VCPE_ONSET_2, VCPE_ONSET_3), "restart");
292     }
293
294     // VDNS
295
296     /**
297      * Sunny Day with Tosca Compliant Policy.
298      */
299     @Test
300     public void testVdnsSunnyDayCompliant() {
301         httpSunnyDay(VDNS_TOSCA_COMPLIANT_POLICY, VDNS_ONSET);
302     }
303
304     // VFW
305
306     /**
307      * VFW Sunny Day with Legacy Tosca Policy.
308      */
309     @Test
310     public void testVfwSunnyDayLegacy() {
311         appcLegacySunnyDay(VFW_TOSCA_LEGACY_POLICY, VFW_ONSET, "ModifyConfig");
312     }
313
314     /**
315      * VFW Sunny Day with Tosca Compliant Policy.
316      */
317     @Test
318     public void testVfwSunnyDayCompliant() {
319         appcLegacySunnyDay(VFW_TOSCA_COMPLIANT_POLICY, VFW_ONSET, "ModifyConfig");
320     }
321
322     // VLB
323
324     /**
325      * Sunny Day with Legacy Tosca Policy.
326      */
327     @Test
328     public void testVlbSunnyDayLegacy() {
329         httpSunnyDay(VLB_TOSCA_LEGACY_POLICY, VLB_ONSET);
330     }
331
332     /**
333      * Sunny Day with Tosca Compliant Policy.
334      */
335     @Test
336     public void testVlbSunnyDayCompliant() {
337         httpSunnyDay(VLB_TOSCA_COMPLIANT_POLICY, VLB_ONSET);
338     }
339
340     /**
341      * Sunny day scenario for use cases that use APPC-LCM.
342      *
343      * @param policyFile file containing the ToscaPolicy to be loaded
344      * @param onsetFile file containing the ONSET to be injected
345      * @param operation expected APPC operation request
346      */
347     protected void appcLcmSunnyDay(String policyFile, String onsetFile, String operation) {
348         appcLcmSunnyDay(policyFile, List.of(onsetFile), operation);
349     }
350
351     /**
352      * Sunny day scenario for use cases that use APPC-LCM.
353      *
354      * @param policyFile file containing the ToscaPolicy to be loaded
355      * @param onsetFiles list of files containing the ONSET to be injected
356      * @param operation expected APPC operation request
357      */
358     protected void appcLcmSunnyDay(String policyFile, List<String> onsetFiles, String operation) {
359         policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
360         appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER);
361
362         assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
363         policy = rules.setupPolicyFromFile(policyFile);
364         assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
365
366         // inject several ONSET events over the DCAE topic
367         for (String onsetFile : onsetFiles) {
368             topics.inject(DCAE_TOPIC, onsetFile);
369         }
370
371         /* Wait to acquire a LOCK and a PDP-X PERMIT */
372         waitForLockAndPermit(policy, policyClMgt);
373
374         /*
375          * Ensure that an APPC RESTART request was sent in response to the matching ONSET
376          */
377         AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> operation.equals(req.getRpcName()));
378
379         /*
380          * Inject a 400 APPC Response Return over the APPC topic, with appropriate
381          * subRequestId
382          */
383         topics.inject(APPC_LCM_WRITE_TOPIC, VCPE_APPC_SUCCESS,
384                         appcreq.getBody().getInput().getCommonHeader().getSubRequestId());
385
386         /* --- Operation Completed --- */
387
388         waitForOperationSuccess();
389
390         /* --- Transaction Completed --- */
391         waitForFinalSuccess(policy, policyClMgt);
392     }
393
394     /**
395      * Sunny day scenario for use cases that use Legacy APPC.
396      *
397      * @param policyFile file containing the ToscaPolicy to be loaded
398      * @param onsetFile file containing the ONSET to be injected
399      * @param operation expected APPC operation request
400      */
401     protected void appcLegacySunnyDay(String policyFile, String onsetFile, String operation) {
402         policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
403         appcClSink = topics.createListener(APPC_CL_TOPIC, Request.class, APPC_LEGACY_CODER);
404
405         assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
406         policy = rules.setupPolicyFromFile(policyFile);
407         assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
408
409         /* Inject an ONSET event over the DCAE topic */
410         topics.inject(DCAE_TOPIC, onsetFile);
411
412         /* Wait to acquire a LOCK and a PDP-X PERMIT */
413         waitForLockAndPermit(policy, policyClMgt);
414
415         /*
416          * Ensure that an APPC RESTART request was sent in response to the matching ONSET
417          */
418         Request appcreq = appcClSink.await(req -> operation.equals(req.getAction()));
419
420         /*
421          * Inject a 400 APPC Response Return over the APPC topic, with appropriate
422          * subRequestId
423          */
424         topics.inject(APPC_CL_TOPIC, VFW_APPC_SUCCESS, appcreq.getCommonHeader().getSubRequestId());
425
426         /* --- Operation Completed --- */
427
428         waitForOperationSuccess();
429
430         /* --- Transaction Completed --- */
431         waitForFinalSuccess(policy, policyClMgt);
432     }
433
434     /**
435      * Sunny day scenario for use cases that use an HTTP simulator.
436      *
437      * @param policyFile file containing the ToscaPolicy to be loaded
438      * @param onsetFile file containing the ONSET to be injected
439      * @param operation expected APPC operation request
440      */
441     protected void httpSunnyDay(String policyFile, String onsetFile) {
442         policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller);
443
444         assertEquals(0, controller.getDrools().factCount(rules.getControllerName()));
445         policy = rules.setupPolicyFromFile(policyFile);
446         assertEquals(2, controller.getDrools().factCount(rules.getControllerName()));
447
448         /* Inject an ONSET event over the DCAE topic */
449         topics.inject(DCAE_TOPIC, onsetFile);
450
451         /* Wait to acquire a LOCK and a PDP-X PERMIT */
452         waitForLockAndPermit(policy, policyClMgt);
453
454         /* --- Operation Completed --- */
455
456         waitForOperationSuccess();
457
458         /* --- Transaction Completed --- */
459         waitForFinalSuccess(policy, policyClMgt);
460     }
461
462     /**
463      * Waits for a OPERATION SUCCESS transaction notification.
464      */
465     protected void waitForOperationSuccess() {
466         policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION_SUCCESS);
467     }
468
469     /**
470      * Waits for a FINAL SUCCESS transaction notification.
471      *
472      * @return the FINAL SUCCESS notification
473      */
474     protected VirtualControlLoopNotification waitForFinalSuccess(ToscaPolicy policy,
475                     Listener<VirtualControlLoopNotification> policyClMgt) {
476
477         return this.waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_SUCCESS);
478     }
479
480     /**
481      * Waits for notifications for LOCK acquisition and GUARD Permit so that event
482      * processing may proceed.
483      */
484     protected abstract void waitForLockAndPermit(ToscaPolicy policy,
485                     Listener<VirtualControlLoopNotification> policyClMgt);
486
487     /**
488      * Waits for a FINAL transaction notification.
489      *
490      * @param finalType FINAL_xxx type for which to wait
491      *
492      * @return the FINAL notification
493      */
494     protected abstract VirtualControlLoopNotification waitForFinal(ToscaPolicy policy,
495                     Listener<VirtualControlLoopNotification> policyClMgt, ControlLoopNotificationType finalType);
496 }