2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.policy.drools.lifecycle;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
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;
45 * Support class for default functionality for running states.
47 public abstract class LifecycleStateRunning extends LifecycleStateDefault {
49 private static final Logger logger = LoggerFactory.getLogger(LifecycleStateRunning.class);
51 protected LifecycleStateRunning(LifecycleFsm manager) {
55 protected abstract boolean stateChangeToPassive(@NonNull PdpStateChange change);
57 protected abstract boolean stateChangeToActive(@NonNull PdpStateChange change);
59 protected abstract boolean deployPolicy(@NonNull PolicyTypeController controller, @NonNull ToscaPolicy policy);
61 protected abstract boolean undeployPolicy(@NonNull PolicyTypeController controller, @NonNull ToscaPolicy policy);
64 public boolean start() {
65 logger.warn("{}: start", this);
70 public boolean stop() {
72 boolean success = fsm.statusAction(PdpState.TERMINATED, null);
73 success = fsm.stopAction() && success;
74 return transitionToState(new LifecycleStateTerminated(fsm)) && success;
79 public void shutdown() {
87 public boolean status() {
89 if (!fsm.isMandatoryPolicyTypesCompliant()) {
90 logger.info("Not all expected policy types are registered yet, current={}, expected={}",
91 fsm.getCurrentPolicyTypes(), fsm.getMandatoryPolicyTypes());
95 return fsm.statusAction();
100 public boolean isAlive() {
105 public boolean stateChange(@NonNull PdpStateChange change) {
107 if (change.getState() == PdpState.PASSIVE) {
108 return stateChangeToPassive(change);
111 if (change.getState() == PdpState.ACTIVE) {
112 return stateChangeToActive(change);
115 logger.warn("{}: state-change: {}", this, change);
117 invalidStateChange(change);
123 public boolean update(@NonNull PdpUpdate update) {
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"));
132 // update subgroup if applicable per update message
134 fsm.setSubGroup(update.getPdpSubgroup());
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.
142 List<ToscaPolicy> desiredPolicyInventory =
143 fsm.mergePolicies(update.getPoliciesToBeDeployed(), update.getPoliciesToBeUndeployed());
145 // snapshot the active policies previous to apply the new set of active
146 // policies as given by the PAP in the update message
148 List<ToscaPolicy> activePoliciesPreUpdate = fsm.getActivePolicies();
149 Map<String, List<ToscaPolicy>> activePoliciesPreUpdateMap =
150 fsm.groupPoliciesByPolicyType(activePoliciesPreUpdate);
152 Pair<List<ToscaPolicy>, List<ToscaPolicy>> results = updatePoliciesWithResults(desiredPolicyInventory);
154 // summary message to return in the update response to the PAP
156 List<ToscaPolicy> failedPolicies = new ArrayList<>(results.getLeft());
157 failedPolicies.addAll(results.getRight());
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.
166 failedPolicies.addAll(reApplyNativeArtifactPolicies(activePoliciesPreUpdate, activePoliciesPreUpdateMap));
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.
175 failedPolicies.addAll(reApplyNonNativePolicies(activePoliciesPreUpdateMap));
177 PdpResponseDetails response =
178 response(update.getRequestId(),
179 (failedPolicies.isEmpty()) ? PdpResponseStatus.SUCCESS : PdpResponseStatus.FAIL,
180 fsm.getPolicyIdsMessage(failedPolicies));
182 return fsm.statusAction(response) && failedPolicies.isEmpty();
187 public boolean updatePolicies(List<ToscaPolicy> policies) {
188 Pair<List<ToscaPolicy>, List<ToscaPolicy>> results = updatePoliciesWithResults(policies);
189 return results.getLeft().isEmpty() && results.getRight().isEmpty();
192 protected Pair<List<ToscaPolicy>, List<ToscaPolicy>> updatePoliciesWithResults(List<ToscaPolicy> policies) {
193 if (policies == null) {
194 return Pair.of(Collections.emptyList(), Collections.emptyList());
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.
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);
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);
216 return Pair.of(failedUndeployPolicies, failedDeployPolicies);
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
222 Map<String, List<ToscaPolicy>> activePoliciesByType = fsm.groupPoliciesByPolicyType(fsm.getActivePolicies());
223 List<ToscaPolicy> activeNativeArtifactPolicies = fsm.getNativeArtifactPolicies(activePoliciesByType);
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();
231 // need to re-apply non-native policies
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).
237 List<ToscaPolicy> preNonNativePolicies = fsm.getNonNativePolicies(preActivePoliciesMap);
238 preNonNativePolicies.retainAll(fsm.getNonNativePolicies(activePoliciesByType));
240 logger.info("re-applying non-native policies {} because new native artifact policies have been found: {}",
241 fsm.getPolicyIdsMessage(preNonNativePolicies), fsm.getPolicyIdsMessage(activeNativeArtifactPolicies));
243 List<ToscaPolicy> failedPolicies = syncPolicies(preNonNativePolicies, this::deployPolicy);
244 logger.info("re-applying non-native policies failures: {}", fsm.getPolicyIdsMessage(failedPolicies));
246 return failedPolicies;
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
253 Map<String, List<ToscaPolicy>> activePoliciesByType = fsm.groupPoliciesByPolicyType(fsm.getActivePolicies());
254 List<ToscaPolicy> activeNativeControllerPolicies = fsm.getNativeControllerPolicies(activePoliciesByType);
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();
262 // need to re-apply native artifact policies
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).
268 List<ToscaPolicy> preNativeArtifactPolicies = fsm.getNativeArtifactPolicies(preActivePoliciesMap);
269 preNativeArtifactPolicies.retainAll(fsm.getNativeArtifactPolicies(activePoliciesByType));
271 logger.info("reapply candidate native artifact policies {} as new native controller policies {} were found",
272 fsm.getPolicyIdsMessage(preNativeArtifactPolicies),
273 fsm.getPolicyIdsMessage(activeNativeControllerPolicies));
275 // from the intersection, only need to reapply those for which there is a new native
278 List<ToscaPolicy> preNativeArtifactPoliciesToApply = new ArrayList<>();
279 for (ToscaPolicy preNativeArtifactPolicy : preNativeArtifactPolicies) {
280 NativeArtifactPolicy nativeArtifactPolicy;
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);
290 String controllerName = nativeArtifactPolicy.getProperties().getController().getName();
291 for (ToscaPolicy policy : activeNativeControllerPolicies) {
292 if (controllerName.equals(policy.getProperties().get("controllerName"))) {
293 preNativeArtifactPoliciesToApply.add(preNativeArtifactPolicy);
298 logger.info("reapply set of native artifact policies {} as new native controller policies {} were found",
299 fsm.getPolicyIdsMessage(preNativeArtifactPoliciesToApply),
300 fsm.getPolicyIdsMessage(activeNativeControllerPolicies));
302 List<ToscaPolicy> failedPolicies = syncPolicies(preNativeArtifactPoliciesToApply, this::deployPolicy);
303 logger.info("re-applying native artifact policies failures: {}", fsm.getPolicyIdsMessage(failedPolicies));
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.
308 if (!preNativeArtifactPoliciesToApply.isEmpty()) {
309 preActivePolicies.removeAll(preNativeArtifactPoliciesToApply);
311 .getOrDefault(LifecycleFsm.POLICY_TYPE_DROOLS_NATIVE_RULES.getName(), Collections.emptyList())
312 .removeAll(preNativeArtifactPoliciesToApply);
314 return failedPolicies;
317 protected List<ToscaPolicy> deployPolicies(List<ToscaPolicy> policies) {
318 return syncPolicies(fsm.getDeployablePoliciesAction(policies), this::deployPolicy);
321 protected List<ToscaPolicy> undeployPolicies(List<ToscaPolicy> policies) {
322 return syncPolicies(fsm.getUndeployablePoliciesAction(policies), this::undeployPolicy);
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);
338 if (domain.isRegistered(policy.getTypeIdentifier())) {
339 if (!domain.isConformant(policy) || !sync.test(controller, policy)) {
340 failedPolicies.add(policy);
343 logger.info("no validator registered for policy type {}", policy.getTypeIdentifier());
344 if (!sync.test(controller, policy)) {
345 failedPolicies.add(policy);
349 return failedPolicies;
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()));
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);