Move PAP database provider to spring boot default
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / notification / DeploymentStatus.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.pap.main.notification;
23
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.function.BiPredicate;
33 import java.util.stream.Collectors;
34 import lombok.AccessLevel;
35 import lombok.Getter;
36 import org.onap.policy.models.pap.concepts.PolicyNotification;
37 import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
38 import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.State;
39 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
40 import org.onap.policy.pap.main.notification.StatusAction.Action;
41 import org.onap.policy.pap.main.service.PolicyStatusService;
42
43 /**
44  * Collection of Policy Deployment Status records. The sequence of method invocations
45  * should be as follows:
46  * <ol>
47  * <li>{@link #loadByGroup(String)}</li>
48  * <li>various other methods</li>
49  * <li>repeat the previous steps as appropriate</li>
50  * <li>{@link #flush(PolicyNotification)}</li>
51  * </ol>
52  */
53 public class DeploymentStatus {
54     /**
55      * Tracks the groups that have been loaded.
56      */
57     private final Set<String> pdpGroupLoaded = new HashSet<>();
58
59     /**
60      * Records, mapped by PDP/Policy pair.
61      */
62     @Getter(AccessLevel.PROTECTED)
63     private final Map<StatusKey, StatusAction> recordMap = new HashMap<>();
64
65     /**
66      * Records the policy status so that notifications can be generated. When
67      * {@link #loadByGroup(String)} is invoked, records are added to this. Other than
68      * that, this is not updated until {@link #addNotifications(PolicyNotification)} is
69      * invoked.
70      */
71     private DeploymentTracker tracker = new DeploymentTracker();
72
73     private PolicyStatusService policyStatusService;
74
75
76     /**
77      * Constructs the object.
78      *
79      * @param policyStatusService the policyStatusService
80      */
81     public DeploymentStatus(PolicyStatusService policyStatusService) {
82         this.policyStatusService = policyStatusService;
83     }
84
85     /**
86      * Adds new policy status to a notification.
87      *
88      * @param notif notification to which to add policy status
89      */
90     protected void addNotifications(PolicyNotification notif) {
91         var newTracker = new DeploymentTracker();
92         recordMap.values().forEach(newTracker::add);
93
94         tracker.addNotifications(notif, newTracker);
95
96         tracker = newTracker;
97     }
98
99     /**
100      * Loads policy deployment status associated with the given PDP group.
101      *
102      * @param pdpGroup group whose records are to be loaded
103      */
104     public void loadByGroup(String pdpGroup) {
105         if (pdpGroupLoaded.contains(pdpGroup)) {
106             return;
107         }
108
109         pdpGroupLoaded.add(pdpGroup);
110
111         for (PdpPolicyStatus status : policyStatusService.getGroupPolicyStatus(pdpGroup)) {
112             var status2 = new StatusAction(Action.UNCHANGED, status);
113             recordMap.put(new StatusKey(status), status2);
114             tracker.add(status2);
115         }
116     }
117
118     /**
119      * Flushes changes to the DB, adding policy status to the notification.
120      *
121      * @param notif notification to which to add policy status
122      */
123     public void flush(PolicyNotification notif) {
124         // must add notifications BEFORE deleting undeployments
125         addNotifications(notif);
126         deleteUndeployments();
127         flush();
128     }
129
130     /**
131      * Flushes changes to the DB.
132      */
133     protected void flush() {
134         // categorize the records
135         List<PdpPolicyStatus> created = new ArrayList<>();
136         List<PdpPolicyStatus> updated = new ArrayList<>();
137         List<PdpPolicyStatus> deleted = new ArrayList<>();
138
139         for (StatusAction status : recordMap.values()) {
140             switch (status.getAction()) {
141                 case CREATED:
142                     created.add(status.getStatus());
143                     break;
144                 case UPDATED:
145                     updated.add(status.getStatus());
146                     break;
147                 case DELETED:
148                     deleted.add(status.getStatus());
149                     break;
150                 default:
151                     break;
152             }
153         }
154
155         policyStatusService.cudPolicyStatus(created, updated, deleted);
156
157         /*
158          * update the records to indicate everything is now unchanged (i.e., matches what
159          * is in the DB)
160          */
161
162         Iterator<StatusAction> iter = recordMap.values().iterator();
163         while (iter.hasNext()) {
164             StatusAction status = iter.next();
165
166             if (status.getAction() == Action.DELETED) {
167                 iter.remove();
168             } else {
169                 status.setAction(Action.UNCHANGED);
170             }
171         }
172     }
173
174     /**
175      * Deletes records for any policies that have been completely undeployed.
176      */
177     protected void deleteUndeployments() {
178         // identify the incomplete policies
179
180         // @formatter:off
181         Set<ToscaConceptIdentifier> incomplete = recordMap.values().stream()
182             .filter(status -> status.getAction() != Action.DELETED)
183             .map(StatusAction::getStatus)
184             .filter(status -> status.getState() == State.WAITING)
185             .map(PdpPolicyStatus::getPolicy)
186             .collect(Collectors.toSet());
187         // @formatter:on
188
189         // delete if UNDEPLOYED and not incomplete
190         deleteDeployment((key, status) -> !status.getStatus().isDeploy() && !incomplete.contains(key.getPolicy()));
191     }
192
193     /**
194      * Delete deployment records for a PDP.
195      *
196      * @param pdpId PDP whose records are to be deleted
197      */
198     public void deleteDeployment(String pdpId) {
199         deleteDeployment((key, status) -> key.getPdpId().equals(pdpId));
200     }
201
202     /**
203      * Delete deployment records for a policy.
204      *
205      * @param policy policy whose records are to be deleted
206      * @param deploy {@code true} to delete deployment records, {@code false} to delete
207      *        undeployment records
208      */
209     public void deleteDeployment(ToscaConceptIdentifier policy, boolean deploy) {
210         deleteDeployment((key, status) -> status.getStatus().isDeploy() == deploy && key.getPolicy().equals(policy));
211     }
212
213     /**
214      * Delete deployment records for a policy.
215      *
216      * @param filter filter to identify records to be deleted
217      */
218     private void deleteDeployment(BiPredicate<StatusKey, StatusAction> filter) {
219         Iterator<Entry<StatusKey, StatusAction>> iter = recordMap.entrySet().iterator();
220         while (iter.hasNext()) {
221             Entry<StatusKey, StatusAction> entry = iter.next();
222             StatusKey key = entry.getKey();
223             StatusAction value = entry.getValue();
224
225             if (filter.test(key, value)) {
226                 if (value.getAction() == Action.CREATED) {
227                     // it's a new record - just remove it
228                     iter.remove();
229                 } else {
230                     // it's an existing record - mark it for deletion
231                     value.setAction(Action.DELETED);
232                 }
233             }
234         }
235     }
236
237     /**
238      * Deploys/undeploys a policy to a PDP. Assumes that
239      * {@link #deleteDeployment(ToscaConceptIdentifier, boolean)} has already been invoked
240      * to delete any records having the wrong "deploy" value.
241      *
242      * @param pdpId PDP to which the policy is to be deployed
243      * @param policy policy to be deployed
244      * @param policyType policy's type
245      * @param pdpGroup PdpGroup containing the PDP of interest
246      * @param pdpType PDP type (i.e., PdpSubGroup) containing the PDP of interest
247      * @param deploy {@code true} if the policy is being deployed, {@code false} if
248      *        undeployed
249      */
250     public void deploy(String pdpId, ToscaConceptIdentifier policy, ToscaConceptIdentifier policyType, String pdpGroup,
251                     String pdpType, boolean deploy) {
252
253         recordMap.compute(new StatusKey(pdpId, policy), (key, status) -> {
254
255             if (status == null) {
256                 // no record yet - create one
257
258                 // @formatter:off
259                 return new StatusAction(Action.CREATED, PdpPolicyStatus.builder()
260                                     .pdpGroup(pdpGroup)
261                                     .pdpId(pdpId)
262                                     .pdpType(pdpType)
263                                     .policy(policy)
264                                     .policyType(policyType)
265                                     .deploy(deploy)
266                                     .state(State.WAITING)
267                                     .build());
268                 // @formatter:on
269             }
270
271             PdpPolicyStatus status2 = status.getStatus();
272
273             // record already exists - see if the deployment flag should be changed
274
275             if (status2.isDeploy() != deploy) {
276                 // deployment flag has changed
277                 status.setChanged();
278                 status2.setDeploy(deploy);
279                 status2.setState(State.WAITING);
280
281
282             } else if (status.getAction() == Action.DELETED) {
283                 // deployment flag is unchanged
284                 status.setAction(Action.UPDATED);
285             }
286
287             return status;
288         });
289     }
290
291     /**
292      * Indicates the deployment/undeployment of a set of policies to a PDP has completed.
293      *
294      * @param pdpId PDP of interest
295      * @param expectedPolicies policies that we expected to be deployed to the PDP
296      * @param actualPolicies policies that were actually deployed to the PDP
297      */
298     public void completeDeploy(String pdpId, Set<ToscaConceptIdentifier> expectedPolicies,
299                     Set<ToscaConceptIdentifier> actualPolicies) {
300
301         for (StatusAction status : recordMap.values()) {
302             PdpPolicyStatus status2 = status.getStatus();
303
304             if (!status.getStatus().getPdpId().equals(pdpId)
305                             || expectedPolicies.contains(status2.getPolicy()) != status2.isDeploy()) {
306                 /*
307                  * The policy is "expected" to be deployed, but the record is not marked
308                  * for deployment (or vice versa), which means the expected policy is out
309                  * of date with the DB, thus we'll ignore this policy for now.
310                  */
311                 continue;
312             }
313
314             State state;
315             if (actualPolicies.contains(status2.getPolicy())) {
316                 state = (status.getStatus().isDeploy() ? State.SUCCESS : State.FAILURE);
317             } else {
318                 state = (status.getStatus().isDeploy() ? State.FAILURE : State.SUCCESS);
319             }
320
321             if (status2.getState() != state) {
322                 status.setChanged();
323                 status2.setState(state);
324             }
325         }
326     }
327 }