Fix sonars in policy-pap
[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  * ================================================================================
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.pap.main.notification;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Set;
31 import java.util.function.BiPredicate;
32 import java.util.stream.Collectors;
33 import lombok.AccessLevel;
34 import lombok.Getter;
35 import org.onap.policy.models.base.PfModelException;
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.provider.PolicyModelsProvider;
40 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
41 import org.onap.policy.pap.main.notification.StatusAction.Action;
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 PolicyModelsProvider provider;
74
75
76     /**
77      * Constructs the object.
78      *
79      * @param provider the provider to use to access the DB
80      */
81     public DeploymentStatus(PolicyModelsProvider provider) {
82         this.provider = provider;
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      * @throws PfModelException if an error occurs
104      */
105     public void loadByGroup(String pdpGroup) throws PfModelException {
106         if (pdpGroupLoaded.contains(pdpGroup)) {
107             return;
108         }
109
110         pdpGroupLoaded.add(pdpGroup);
111
112         for (PdpPolicyStatus status : provider.getGroupPolicyStatus(pdpGroup)) {
113             var status2 = new StatusAction(Action.UNCHANGED, status);
114             recordMap.put(new StatusKey(status), status2);
115             tracker.add(status2);
116         }
117     }
118
119     /**
120      * Flushes changes to the DB, adding policy status to the notification.
121      *
122      * @param notif notification to which to add policy status
123      */
124     public void flush(PolicyNotification notif) {
125         // must add notifications BEFORE deleting undeployments
126         addNotifications(notif);
127         deleteUndeployments();
128         flush();
129     }
130
131     /**
132      * Flushes changes to the DB.
133      */
134     protected void flush() {
135         // categorize the records
136         List<PdpPolicyStatus> created = new ArrayList<>();
137         List<PdpPolicyStatus> updated = new ArrayList<>();
138         List<PdpPolicyStatus> deleted = new ArrayList<>();
139
140         for (StatusAction status : recordMap.values()) {
141             switch (status.getAction()) {
142                 case CREATED:
143                     created.add(status.getStatus());
144                     break;
145                 case UPDATED:
146                     updated.add(status.getStatus());
147                     break;
148                 case DELETED:
149                     deleted.add(status.getStatus());
150                     break;
151                 default:
152                     break;
153             }
154         }
155
156         provider.cudPolicyStatus(created, updated, deleted);
157
158         /*
159          * update the records to indicate everything is now unchanged (i.e., matches what
160          * is in the DB)
161          */
162
163         Iterator<StatusAction> iter = recordMap.values().iterator();
164         while (iter.hasNext()) {
165             StatusAction status = iter.next();
166
167             if (status.getAction() == Action.DELETED) {
168                 iter.remove();
169             } else {
170                 status.setAction(Action.UNCHANGED);
171             }
172         }
173     }
174
175     /**
176      * Deletes records for any policies that have been completely undeployed.
177      */
178     protected void deleteUndeployments() {
179         // identify the incomplete policies
180
181         // @formatter:off
182         Set<ToscaConceptIdentifier> incomplete = recordMap.values().stream()
183             .filter(status -> status.getAction() != Action.DELETED)
184             .map(StatusAction::getStatus)
185             .filter(status -> status.getState() == State.WAITING)
186             .map(PdpPolicyStatus::getPolicy)
187             .collect(Collectors.toSet());
188         // @formatter:on
189
190         // delete if UNDEPLOYED and not incomplete
191         deleteDeployment((key, status) -> !status.getStatus().isDeploy() && !incomplete.contains(key.getPolicy()));
192     }
193
194     /**
195      * Delete deployment records for a PDP.
196      *
197      * @param pdpId PDP whose records are to be deleted
198      */
199     public void deleteDeployment(String pdpId) {
200         deleteDeployment((key, status) -> key.getPdpId().equals(pdpId));
201     }
202
203     /**
204      * Delete deployment records for a policy.
205      *
206      * @param policy policy whose records are to be deleted
207      * @param deploy {@code true} to delete deployment records, {@code false} to delete
208      *        undeployment records
209      */
210     public void deleteDeployment(ToscaConceptIdentifier policy, boolean deploy) {
211         deleteDeployment((key, status) -> status.getStatus().isDeploy() == deploy && key.getPolicy().equals(policy));
212     }
213
214     /**
215      * Delete deployment records for a policy.
216      *
217      * @param filter filter to identify records to be deleted
218      */
219     private void deleteDeployment(BiPredicate<StatusKey, StatusAction> filter) {
220         Iterator<Entry<StatusKey, StatusAction>> iter = recordMap.entrySet().iterator();
221         while (iter.hasNext()) {
222             Entry<StatusKey, StatusAction> entry = iter.next();
223             StatusKey key = entry.getKey();
224             StatusAction value = entry.getValue();
225
226             if (filter.test(key, value)) {
227                 if (value.getAction() == Action.CREATED) {
228                     // it's a new record - just remove it
229                     iter.remove();
230                 } else {
231                     // it's an existing record - mark it for deletion
232                     value.setAction(Action.DELETED);
233                 }
234             }
235         }
236     }
237
238     /**
239      * Deploys/undeploys a policy to a PDP. Assumes that
240      * {@link #deleteDeployment(ToscaConceptIdentifier, boolean)} has already been invoked
241      * to delete any records having the wrong "deploy" value.
242      *
243      * @param pdpId PDP to which the policy is to be deployed
244      * @param policy policy to be deployed
245      * @param policyType policy's type
246      * @param pdpGroup PdpGroup containing the PDP of interest
247      * @param pdpType PDP type (i.e., PdpSubGroup) containing the PDP of interest
248      * @param deploy {@code true} if the policy is being deployed, {@code false} if
249      *        undeployed
250      */
251     public void deploy(String pdpId, ToscaConceptIdentifier policy, ToscaConceptIdentifier policyType, String pdpGroup,
252                     String pdpType, boolean deploy) {
253
254         recordMap.compute(new StatusKey(pdpId, policy), (key, status) -> {
255
256             if (status == null) {
257                 // no record yet - create one
258
259                 // @formatter:off
260                 return new StatusAction(Action.CREATED, PdpPolicyStatus.builder()
261                                     .pdpGroup(pdpGroup)
262                                     .pdpId(pdpId)
263                                     .pdpType(pdpType)
264                                     .policy(policy)
265                                     .policyType(policyType)
266                                     .deploy(deploy)
267                                     .state(State.WAITING)
268                                     .build());
269                 // @formatter:on
270             }
271
272             PdpPolicyStatus status2 = status.getStatus();
273
274             // record already exists - see if the deployment flag should be changed
275
276             if (status2.isDeploy() != deploy) {
277                 // deployment flag has changed
278                 status.setChanged();
279                 status2.setDeploy(deploy);
280                 status2.setState(State.WAITING);
281
282
283             } else if (status.getAction() == Action.DELETED) {
284                 // deployment flag is unchanged
285                 status.setAction(Action.UPDATED);
286             }
287
288             return status;
289         });
290     }
291
292     /**
293      * Indicates the deployment/undeployment of a set of policies to a PDP has completed.
294      *
295      * @param pdpId PDP of interest
296      * @param expectedPolicies policies that we expected to be deployed to the PDP
297      * @param actualPolicies policies that were actually deployed to the PDP
298      */
299     public void completeDeploy(String pdpId, Set<ToscaConceptIdentifier> expectedPolicies,
300                     Set<ToscaConceptIdentifier> actualPolicies) {
301
302         for (StatusAction status : recordMap.values()) {
303             PdpPolicyStatus status2 = status.getStatus();
304
305             if (!status.getStatus().getPdpId().equals(pdpId)
306                             || expectedPolicies.contains(status2.getPolicy()) != status2.isDeploy()) {
307                 /*
308                  * The policy is "expected" to be deployed, but the record is not marked
309                  * for deployment (or vice versa), which means the expected policy is out
310                  * of date with the DB, thus we'll ignore this policy for now.
311                  */
312                 continue;
313             }
314
315             State state;
316             if (actualPolicies.contains(status2.getPolicy())) {
317                 state = (status.getStatus().isDeploy() ? State.SUCCESS : State.FAILURE);
318             } else {
319                 state = (status.getStatus().isDeploy() ? State.FAILURE : State.SUCCESS);
320             }
321
322             if (status2.getState() != state) {
323                 status.setChanged();
324                 status2.setState(state);
325             }
326         }
327     }
328 }