Removing deprecated DMAAP library
[policy/drools-pdp.git] / feature-lifecycle / src / main / java / org / onap / policy / drools / lifecycle / LifecycleStateRunning.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019 Bell Canada.
7  * Modifications Copyright (C) 2021, 2023 Nordix Foundation.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.drools.lifecycle;
24
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.function.BiPredicate;
30 import lombok.NonNull;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.onap.policy.common.utils.coder.CoderException;
33 import org.onap.policy.drools.domain.models.artifact.NativeArtifactPolicy;
34 import org.onap.policy.models.pdp.concepts.PdpResponseDetails;
35 import org.onap.policy.models.pdp.concepts.PdpStateChange;
36 import org.onap.policy.models.pdp.concepts.PdpUpdate;
37 import org.onap.policy.models.pdp.enums.PdpResponseStatus;
38 import org.onap.policy.models.pdp.enums.PdpState;
39 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
40 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Support class for default functionality for running states.
46  */
47 public abstract class LifecycleStateRunning extends LifecycleStateDefault {
48
49     private static final Logger logger = LoggerFactory.getLogger(LifecycleStateRunning.class);
50
51     protected LifecycleStateRunning(LifecycleFsm manager) {
52         super(manager);
53     }
54
55     protected abstract boolean stateChangeToPassive(@NonNull PdpStateChange change);
56
57     protected abstract boolean stateChangeToActive(@NonNull PdpStateChange change);
58
59     protected abstract boolean deployPolicy(@NonNull PolicyTypeController controller, @NonNull ToscaPolicy policy);
60
61     protected abstract boolean undeployPolicy(@NonNull PolicyTypeController controller, @NonNull ToscaPolicy policy);
62
63     @Override
64     public boolean start() {
65         logger.warn("{}: start", this);
66         return false;
67     }
68
69     @Override
70     public boolean stop() {
71         synchronized (fsm) {
72             boolean success = fsm.statusAction(PdpState.TERMINATED, null);
73             success = fsm.stopAction() && success;
74             return transitionToState(new LifecycleStateTerminated(fsm)) && success;
75         }
76     }
77
78     @Override
79     public void shutdown() {
80         synchronized (fsm) {
81             stop();
82             fsm.shutdownAction();
83         }
84     }
85
86     @Override
87     public boolean status() {
88         synchronized (fsm) {
89             if (!fsm.isMandatoryPolicyTypesCompliant()) {
90                 logger.info("Not all expected policy types are registered yet, current={}, expected={}",
91                         fsm.getCurrentPolicyTypes(), fsm.getMandatoryPolicyTypes());
92                 return false;
93             }
94
95             return fsm.statusAction();
96         }
97     }
98
99     @Override
100     public boolean isAlive() {
101         return true;
102     }
103
104     @Override
105     public boolean stateChange(@NonNull PdpStateChange change) {
106         synchronized (fsm) {
107             if (change.getState() == PdpState.PASSIVE) {
108                 return stateChangeToPassive(change);
109             }
110
111             if (change.getState() == PdpState.ACTIVE) {
112                 return stateChangeToActive(change);
113             }
114
115             logger.warn("{}: state-change: {}", this, change);
116
117             invalidStateChange(change);
118             return false;
119         }
120     }
121
122     @Override
123     public boolean update(@NonNull PdpUpdate update) {
124         synchronized (fsm) {
125             if (update.getPdpHeartbeatIntervalMs() != null
126                     && !fsm.setStatusIntervalAction(update.getPdpHeartbeatIntervalMs() / 1000)) {
127                 fsm.statusAction(response(update.getRequestId(), PdpResponseStatus.FAIL,
128                     "invalid interval: " + update.getPdpHeartbeatIntervalMs() + " seconds"));
129                 return false;
130             }
131
132             // update subgroup if applicable per update message
133
134             fsm.setSubGroup(update.getPdpSubgroup());
135
136             // Compute the desired final policy set after processing this update.
137             // Delta policies allows for the PAP to send us just the policies to deploy and undeploy
138             // Note that in this mode of operation, there may be dependent policies in the
139             // active inventory.  For example a request to remove a controller policy in a
140             // delta request, may affect operational or artifact policies in use.
141
142             List<ToscaPolicy> desiredPolicyInventory =
143                 fsm.mergePolicies(update.getPoliciesToBeDeployed(), update.getPoliciesToBeUndeployed());
144
145             // snapshot the active policies previous to apply the new set of active
146             // policies as given by the PAP in the update message
147
148             List<ToscaPolicy> activePoliciesPreUpdate = fsm.getActivePolicies();
149             Map<String, List<ToscaPolicy>> activePoliciesPreUpdateMap =
150                     fsm.groupPoliciesByPolicyType(activePoliciesPreUpdate);
151
152             Pair<List<ToscaPolicy>, List<ToscaPolicy>> results = updatePoliciesWithResults(desiredPolicyInventory);
153
154             // summary message to return in the update response to the PAP
155
156             List<ToscaPolicy> failedPolicies = new ArrayList<>(results.getLeft());
157             failedPolicies.addAll(results.getRight());
158
159             // If there are *new* native controller policies deployed, there may
160             // be existing native artifact policies (previous to the update event
161             // processing) that would need to be reapplied.   This requires
162             // going through the list of those native artifact policies that
163             // were neither deployed nor undeployed and re-apply them on top
164             // of the controllers.
165
166             failedPolicies.addAll(reApplyNativeArtifactPolicies(activePoliciesPreUpdate, activePoliciesPreUpdateMap));
167
168             // If there are *new* native artifact policies deployed, there may be existing
169             // non-native policies (previous to the update event processing)
170             // that will need to be reapplied as the new controllers don't know about them.
171             // This requires going through the list of those non-native policies
172             // which neither were undeployed nor deployed and re-apply them on top of the
173             // new "brained" controllers.
174
175             failedPolicies.addAll(reApplyNonNativePolicies(activePoliciesPreUpdateMap));
176
177             PdpResponseDetails response =
178                 response(update.getRequestId(),
179                         (failedPolicies.isEmpty()) ? PdpResponseStatus.SUCCESS : PdpResponseStatus.FAIL,
180                         fsm.getPolicyIdsMessage(failedPolicies));
181
182             return fsm.statusAction(response) && failedPolicies.isEmpty();
183         }
184     }
185
186     @Override
187     public boolean updatePolicies(List<ToscaPolicy> policies) {
188         Pair<List<ToscaPolicy>, List<ToscaPolicy>> results = updatePoliciesWithResults(policies);
189         return results.getLeft().isEmpty() && results.getRight().isEmpty();
190     }
191
192     protected Pair<List<ToscaPolicy>, List<ToscaPolicy>> updatePoliciesWithResults(List<ToscaPolicy> policies) {
193         if (policies == null) {
194             return Pair.of(Collections.emptyList(), Collections.emptyList());
195         }
196
197         // Note that PAP sends the list of all ACTIVE policies with every
198         // UPDATE message.   First, we will undeploy all policies that are
199         // running but are not present in this list.  This will include
200         // policies that are overridden by a different version.   Second,
201         // we will deploy those policies that are not installed but
202         // included in this list.
203
204         List<ToscaPolicy> failedUndeployPolicies = undeployPolicies(policies);
205         if (!failedUndeployPolicies.isEmpty()) {
206             logger.warn("update-policies: undeployment failures: {}", fsm.getPolicyIdsMessage(failedUndeployPolicies));
207             failedUndeployPolicies.forEach(fsm::failedUndeployPolicyAction);
208         }
209
210         List<ToscaPolicy> failedDeployPolicies = deployPolicies(policies);
211         if (!failedDeployPolicies.isEmpty()) {
212             logger.warn("update-policies: deployment failures: {}", fsm.getPolicyIdsMessage(failedDeployPolicies));
213             failedDeployPolicies.forEach(fsm::failedDeployPolicyAction);
214         }
215
216         return Pair.of(failedUndeployPolicies, failedDeployPolicies);
217     }
218
219     protected List<ToscaPolicy> reApplyNonNativePolicies(Map<String, List<ToscaPolicy>> preActivePoliciesMap) {
220         // only need to re-apply non-native policies if there are new native artifact policies
221
222         Map<String, List<ToscaPolicy>> activePoliciesByType = fsm.groupPoliciesByPolicyType(fsm.getActivePolicies());
223         List<ToscaPolicy> activeNativeArtifactPolicies = fsm.getNativeArtifactPolicies(activePoliciesByType);
224
225         activeNativeArtifactPolicies.removeAll(fsm.getNativeArtifactPolicies(preActivePoliciesMap));
226         if (activeNativeArtifactPolicies.isEmpty()) {
227             logger.info("reapply-non-native-policies: nothing to reapply, no new native artifact policies");
228             return Collections.emptyList();
229         }
230
231         // need to re-apply non-native policies
232
233         // get the non-native policies to be reapplied, this is just the intersection of
234         // the original active set, and the new active set (i.e. policies that have not changed,
235         // or in other words, have not been deployed or undeployed).
236
237         List<ToscaPolicy> preNonNativePolicies = fsm.getNonNativePolicies(preActivePoliciesMap);
238         preNonNativePolicies.retainAll(fsm.getNonNativePolicies(activePoliciesByType));
239
240         logger.info("re-applying non-native policies {} because new native artifact policies have been found: {}",
241                 fsm.getPolicyIdsMessage(preNonNativePolicies), fsm.getPolicyIdsMessage(activeNativeArtifactPolicies));
242
243         List<ToscaPolicy> failedPolicies = syncPolicies(preNonNativePolicies, this::deployPolicy);
244         logger.info("re-applying non-native policies failures: {}", fsm.getPolicyIdsMessage(failedPolicies));
245
246         return failedPolicies;
247     }
248
249     protected List<ToscaPolicy> reApplyNativeArtifactPolicies(List<ToscaPolicy> preActivePolicies,
250             Map<String, List<ToscaPolicy>> preActivePoliciesMap) {
251         // only need to re-apply native artifact policies if there are new native controller policies
252
253         Map<String, List<ToscaPolicy>> activePoliciesByType = fsm.groupPoliciesByPolicyType(fsm.getActivePolicies());
254         List<ToscaPolicy> activeNativeControllerPolicies = fsm.getNativeControllerPolicies(activePoliciesByType);
255
256         activeNativeControllerPolicies.removeAll(fsm.getNativeControllerPolicies(preActivePoliciesMap));
257         if (activeNativeControllerPolicies.isEmpty()) {
258             logger.info("reapply-native-artifact-policies: nothing to reapply, no new native controller policies");
259             return Collections.emptyList();
260         }
261
262         // need to re-apply native artifact policies
263
264         // get the native artifact policies to be reapplied, this is just the intersection of
265         // the original active set, and the new active set (i.e. policies that have not changed,
266         // or in other words, have not been deployed or undeployed).
267
268         List<ToscaPolicy> preNativeArtifactPolicies = fsm.getNativeArtifactPolicies(preActivePoliciesMap);
269         preNativeArtifactPolicies.retainAll(fsm.getNativeArtifactPolicies(activePoliciesByType));
270
271         logger.info("reapply candidate native artifact policies {} as new native controller policies {} were found",
272                 fsm.getPolicyIdsMessage(preNativeArtifactPolicies),
273                 fsm.getPolicyIdsMessage(activeNativeControllerPolicies));
274
275         // from the intersection, only need to reapply those for which there is a new native
276         // controller policy
277
278         List<ToscaPolicy> preNativeArtifactPoliciesToApply = new ArrayList<>();
279         for (ToscaPolicy preNativeArtifactPolicy : preNativeArtifactPolicies) {
280             NativeArtifactPolicy nativeArtifactPolicy;
281             try {
282                 nativeArtifactPolicy =
283                     fsm.getDomainMaker().convertTo(preNativeArtifactPolicy, NativeArtifactPolicy.class);
284             } catch (CoderException | RuntimeException ex) {
285                 logger.warn("reapply-native-artifact-policy {}: (unexpected) non conformant: ignoring",
286                         preNativeArtifactPolicy.getIdentifier(), ex);
287                 continue;
288             }
289
290             String controllerName = nativeArtifactPolicy.getProperties().getController().getName();
291             for (ToscaPolicy policy : activeNativeControllerPolicies) {
292                 if (controllerName.equals(policy.getProperties().get("controllerName"))) {
293                     preNativeArtifactPoliciesToApply.add(preNativeArtifactPolicy);
294                 }
295             }
296         }
297
298         logger.info("reapply set of native artifact policies {} as new native controller policies {} were found",
299                 fsm.getPolicyIdsMessage(preNativeArtifactPoliciesToApply),
300                 fsm.getPolicyIdsMessage(activeNativeControllerPolicies));
301
302         List<ToscaPolicy> failedPolicies = syncPolicies(preNativeArtifactPoliciesToApply, this::deployPolicy);
303         logger.info("re-applying native artifact policies failures: {}", fsm.getPolicyIdsMessage(failedPolicies));
304
305         // since we want non-native policies to be reapplied when a new native artifact policy has been
306         // reapplied here, remove it from the preActivePolicies, so it is detected as new.
307
308         if (!preNativeArtifactPoliciesToApply.isEmpty()) {
309             preActivePolicies.removeAll(preNativeArtifactPoliciesToApply);
310             preActivePoliciesMap
311                     .getOrDefault(LifecycleFsm.POLICY_TYPE_DROOLS_NATIVE_RULES.getName(), Collections.emptyList())
312                     .removeAll(preNativeArtifactPoliciesToApply);
313         }
314         return failedPolicies;
315     }
316
317     protected List<ToscaPolicy> deployPolicies(List<ToscaPolicy> policies) {
318         return syncPolicies(fsm.getDeployablePoliciesAction(policies), this::deployPolicy);
319     }
320
321     protected List<ToscaPolicy> undeployPolicies(List<ToscaPolicy> policies) {
322         return syncPolicies(fsm.getUndeployablePoliciesAction(policies), this::undeployPolicy);
323     }
324
325     protected List<ToscaPolicy> syncPolicies(List<ToscaPolicy> policies,
326                                    BiPredicate<PolicyTypeController, ToscaPolicy> sync) {
327         List<ToscaPolicy> failedPolicies = new ArrayList<>();
328         var domain = fsm.getDomainMaker();
329         for (ToscaPolicy policy : policies) {
330             ToscaConceptIdentifier policyType = policy.getTypeIdentifier();
331             PolicyTypeController controller = fsm.getController(policyType);
332             if (controller == null) {
333                 logger.warn("no controller found for {}", policyType);
334                 failedPolicies.add(policy);
335                 continue;
336             }
337
338             if (domain.isRegistered(policy.getTypeIdentifier())) {
339                 if (!domain.isConformant(policy) || !sync.test(controller, policy)) {
340                     failedPolicies.add(policy);
341                 }
342             } else {
343                 logger.info("no validator registered for policy type {}", policy.getTypeIdentifier());
344                 if (!sync.test(controller, policy)) {
345                     failedPolicies.add(policy);
346                 }
347             }
348         }
349         return failedPolicies;
350     }
351
352     private void invalidStateChange(PdpStateChange change) {
353         logger.warn("{}: state-change: {}", this, change);
354         fsm.statusAction(response(change.getRequestId(), PdpResponseStatus.FAIL,
355                  "invalid state change to " + change.getState()));
356     }
357
358     protected PdpResponseDetails response(String requestId, PdpResponseStatus responseStatus, String message) {
359         var response = new PdpResponseDetails();
360         response.setResponseTo(requestId);
361         response.setResponseStatus(responseStatus);
362         if (message != null) {
363             response.setResponseMessage(message);
364         }
365
366         return response;
367     }
368 }