Fix sonars in policy-pap
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / notification / DeploymentTracker.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.Collection;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import org.onap.policy.models.pap.concepts.PolicyNotification;
29 import org.onap.policy.models.pap.concepts.PolicyStatus;
30 import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
31 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
32
33 /**
34  * Tracks policy status so that notifications can be generated.
35  */
36 public class DeploymentTracker {
37     public final Map<ToscaConceptIdentifier, PolicyStatus> deployMap = new HashMap<>();
38     public final Map<ToscaConceptIdentifier, PolicyStatus> undeployMap = new HashMap<>();
39
40
41     /**
42      * Gets the status of all deployments.
43      *
44      * @return the status of all deployments
45      */
46     public Collection<PolicyStatus> getDeploymentStatus() {
47         return deployMap.values();
48     }
49
50     /**
51      * Gets the status of all undeployments.
52      *
53      * @return the status of all undeployments
54      */
55     public Collection<PolicyStatus> getUndeploymentStatus() {
56         return undeployMap.values();
57     }
58
59     /**
60      * Compares this tracking data with new tracking data, adding new policy status to a
61      * notification based on the differences.
62      *
63      * @param notif notification to which to add policy status
64      * @param newTracker new tracking data
65      */
66     public void addNotifications(PolicyNotification notif, DeploymentTracker newTracker) {
67         merge(notif.getAdded(), deployMap, newTracker.deployMap);
68         merge(notif.getDeleted(), undeployMap, newTracker.undeployMap);
69
70         // include those that no longer exist
71         addMissing(notif, newTracker);
72     }
73
74     /**
75      * Merges original tracking data with new tracking data, adding new policy status to
76      * the given list.
77      *
78      * @param list list to which to add policy status
79      * @param originalMap original tracking data
80      * @param newMap new tracking data
81      */
82     private void merge(List<PolicyStatus> list, Map<ToscaConceptIdentifier, PolicyStatus> originalMap,
83                     Map<ToscaConceptIdentifier, PolicyStatus> newMap) {
84
85         for (Entry<ToscaConceptIdentifier, PolicyStatus> entry : newMap.entrySet()) {
86             ToscaConceptIdentifier policy = entry.getKey();
87             PolicyStatus newStat = entry.getValue();
88             PolicyStatus oldStat = originalMap.get(policy);
89
90             if (needNotification(oldStat, newStat)) {
91                 list.add(newStat);
92             }
93         }
94     }
95
96     /**
97      * Determines if a notification is needed.
98      *
99      * @param oldStat original status, or {@code null}
100      * @param newStat new status
101      * @return {@code true} if a notification is needed for the policy, {@code false}
102      *         otherwise
103      */
104     protected boolean needNotification(PolicyStatus oldStat, PolicyStatus newStat) {
105         if (oldStat == null) {
106             // new policy - need notification if it's complete now
107             return (newStat.getIncompleteCount() == 0);
108         }
109
110         if (newStat.getIncompleteCount() == 0) {
111             // don't care if the success count changes (i.e., new PDPs can be added
112             // without requiring a notification)
113             return (oldStat.getIncompleteCount() > 0 || newStat.getFailureCount() != oldStat.getFailureCount());
114         }
115
116         // something is incomplete - only notify if it was previously complete
117         return (oldStat.getIncompleteCount() == 0);
118     }
119
120     /**
121      * Adds notifications for previously deployed policies that are missing from the new
122      * tracker.
123      *
124      * @param notif notification to which to add policy status
125      * @param newTracker new tracking data
126      */
127     private void addMissing(PolicyNotification notif, DeploymentTracker newTracker) {
128         Map<ToscaConceptIdentifier, PolicyStatus> newDeployMap = newTracker.deployMap;
129
130         for (Entry<ToscaConceptIdentifier, PolicyStatus> entry : deployMap.entrySet()) {
131             if (entry.getValue().getIncompleteCount() != 0 || newDeployMap.containsKey(entry.getKey())) {
132                 /*
133                  * This policy deployment was previously incomplete, or it still exists in
134                  * the new tracker. Either way, it needs no notification.
135                  */
136                 continue;
137             }
138
139             // no longer deployed
140             PolicyStatus status = entry.getValue();
141
142             // create a status with counts that are all zero
143             var newStatus = new PolicyStatus();
144             newStatus.setPolicyId(status.getPolicyId());
145             newStatus.setPolicyVersion(status.getPolicyVersion());
146             newStatus.setPolicyTypeId(status.getPolicyTypeId());
147             newStatus.setPolicyTypeVersion(status.getPolicyTypeVersion());
148
149             /*
150              * Adding the status to the "added" set may be a bit unexpected, but when all
151              * status records are deleted from the group, we don't actually undeploy the
152              * policy from the subgroup. Instead, we leave it in the subgroup so that as
153              * soon as a PDP registers, we immediately deploy the policy to the PDP and
154              * continue on; the client can always undeploy the policy when it receives the
155              * notification, if so desired.
156              */
157             notif.getAdded().add(newStatus);
158         }
159     }
160
161     /**
162      * Adds status to the tracking data. Assumes the associated PDP/policy pair has not be
163      * added before.
164      *
165      * @param status status to be added
166      */
167     public void add(StatusAction status) {
168         if (status.getAction() == StatusAction.Action.DELETED) {
169             return;
170         }
171
172         add(status.getStatus());
173     }
174
175     /**
176      * Adds status to the tracking data. Assumes the associated PDP/policy pair has not be
177      * added before.
178      *
179      * @param status status to be added
180      */
181     public void add(PdpPolicyStatus status) {
182
183         ToscaConceptIdentifier policy = status.getPolicy();
184
185         // get the entry from the relevant map, creating an entry if necessary
186         Map<ToscaConceptIdentifier, PolicyStatus> map = (status.isDeploy() ? deployMap : undeployMap);
187
188         PolicyStatus newStat = map.computeIfAbsent(policy, key -> {
189             var value = new PolicyStatus();
190             value.setPolicyId(policy.getName());
191             value.setPolicyVersion(policy.getVersion());
192             value.setPolicyTypeId(status.getPolicyType().getName());
193             value.setPolicyTypeVersion(status.getPolicyType().getVersion());
194             return value;
195         });
196
197         // bump the relevant count
198         switch (status.getState()) {
199             case SUCCESS:
200                 newStat.setSuccessCount(newStat.getSuccessCount() + 1);
201                 break;
202             case FAILURE:
203                 newStat.setFailureCount(newStat.getFailureCount() + 1);
204                 break;
205             default:
206                 newStat.setIncompleteCount(newStat.getIncompleteCount() + 1);
207                 break;
208         }
209     }
210 }