Generate notifications when policies change
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / rest / depundep / ProviderBase.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP PAP
4  * ================================================================================
5  * Copyright (C) 2019 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.rest.depundep;
22
23 import java.util.Collection;
24 import java.util.stream.Collectors;
25 import javax.ws.rs.core.Response.Status;
26 import org.onap.policy.common.utils.services.Registry;
27 import org.onap.policy.models.base.PfModelException;
28 import org.onap.policy.models.base.PfModelRuntimeException;
29 import org.onap.policy.models.pdp.concepts.Pdp;
30 import org.onap.policy.models.pdp.concepts.PdpGroup;
31 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
32 import org.onap.policy.models.pdp.concepts.PdpUpdate;
33 import org.onap.policy.models.provider.PolicyModelsProvider;
34 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
35 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion;
36 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
37 import org.onap.policy.pap.main.PapConstants;
38 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
39 import org.onap.policy.pap.main.comm.PdpModifyRequestMap;
40 import org.onap.policy.pap.main.notification.PolicyNotifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Super class of providers that deploy and undeploy PDP groups. The following items must
46  * be in the {@link Registry}:
47  * <ul>
48  * <li>PDP Modification Lock</li>
49  * <li>PDP Modify Request Map</li>
50  * <li>PAP DAO Factory</li>
51  * </ul>
52  */
53 public abstract class ProviderBase {
54     private static final String DEPLOY_FAILED = "failed to deploy/undeploy policies";
55     public static final String DB_ERROR_MSG = "DB error";
56
57     private static final Logger logger = LoggerFactory.getLogger(ProviderBase.class);
58
59     /**
60      * Lock used when updating PDPs.
61      */
62     private final Object updateLock;
63
64     /**
65      * Used to send UPDATE and STATE-CHANGE requests to the PDPs.
66      */
67     private final PdpModifyRequestMap requestMap;
68
69     /**
70      * Generates policy notifications based on responses from PDPs.
71      */
72     private final PolicyNotifier notifier;
73
74     /**
75      * Factory for PAP DAO.
76      */
77     private final PolicyModelsProviderFactoryWrapper daoFactory;
78
79
80     /**
81      * Constructs the object.
82      */
83     public ProviderBase() {
84         this.updateLock = Registry.get(PapConstants.REG_PDP_MODIFY_LOCK, Object.class);
85         this.requestMap = Registry.get(PapConstants.REG_PDP_MODIFY_MAP, PdpModifyRequestMap.class);
86         this.daoFactory = Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
87         this.notifier = Registry.get(PapConstants.REG_POLICY_NOTIFIER, PolicyNotifier.class);
88     }
89
90     /**
91      * Processes a policy request.
92      *
93      * @param request PDP policy request
94      * @param processor function that processes the request
95      * @throws PfModelException if an error occurred
96      */
97     protected <T> void process(T request, BiConsumerWithEx<SessionData, T> processor) throws PfModelException {
98
99         synchronized (updateLock) {
100             SessionData data;
101
102             try (PolicyModelsProvider dao = daoFactory.create()) {
103
104                 data = new SessionData(dao);
105                 processor.accept(data, request);
106
107                 // make all of the DB updates
108                 data.updateDb();
109
110             } catch (PfModelException | PfModelRuntimeException e) {
111                 logger.warn(DEPLOY_FAILED, e);
112                 throw e;
113
114             } catch (RuntimeException e) {
115                 logger.warn(DEPLOY_FAILED, e);
116                 throw new PfModelException(Status.INTERNAL_SERVER_ERROR, "request failed", e);
117             }
118
119             // track responses for notification purposes
120             data.getDeployData().forEach(notifier::addDeploymentData);
121             data.getUndeployData().forEach(notifier::addUndeploymentData);
122
123             // publish the requests
124             data.getPdpRequests().forEach(pair -> requestMap.addRequest(pair.getLeft(), pair.getRight()));
125         }
126     }
127
128     /**
129      * Process a single policy from the request.
130      *
131      * @param data session data
132      * @param desiredPolicy request policy
133      * @throws PfModelException if an error occurred
134      */
135     protected void processPolicy(SessionData data, ToscaPolicyIdentifierOptVersion desiredPolicy)
136                     throws PfModelException {
137
138         ToscaPolicy policy = getPolicy(data, desiredPolicy);
139
140         Collection<PdpGroup> groups = getGroups(data, policy.getTypeIdentifier());
141         if (groups.isEmpty()) {
142             throw new PfModelException(Status.BAD_REQUEST, "policy not supported by any PDP group: "
143                             + desiredPolicy.getName() + " " + desiredPolicy.getVersion());
144         }
145
146         Updater updater = makeUpdater(data, policy, desiredPolicy);
147
148         for (PdpGroup group : groups) {
149             upgradeGroup(data, group, updater);
150         }
151     }
152
153     /**
154      * Makes a function to update a subgroup. The function is expected to return
155      * {@code true} if the subgroup was updated, {@code false} if no update was
156      * necessary/appropriate.
157      *
158      * @param data session data
159      * @param policy policy to be added to or removed from each subgroup
160      * @param desiredPolicy request policy
161      * @return a function to update a subgroup
162      */
163     protected abstract Updater makeUpdater(SessionData data, ToscaPolicy policy,
164                     ToscaPolicyIdentifierOptVersion desiredPolicy);
165
166     /**
167      * Finds the active PDP group(s) that supports the given policy type.
168      *
169      * @param data session data
170      * @param policyType the policy type of interest
171      * @return the matching PDP group, or {@code null} if no active group supports the
172      *         given PDP types
173      * @throws PfModelException if an error occurred
174      */
175     private Collection<PdpGroup> getGroups(SessionData data, ToscaPolicyTypeIdentifier policyType)
176                     throws PfModelException {
177
178         return data.getActivePdpGroupsByPolicyType(policyType);
179     }
180
181     /**
182      * Updates a group, assigning a new version number, if it actually changes.
183      *
184      * @param data session data
185      * @param group the original group, to be updated
186      * @param updater function to update a group
187      * @throws PfModelException if an error occurred
188      */
189     private void upgradeGroup(SessionData data, PdpGroup group, Updater updater) throws PfModelException {
190
191         boolean updated = false;
192
193         for (PdpSubGroup subgroup : group.getPdpSubgroups()) {
194
195             if (!updater.apply(group, subgroup)) {
196                 continue;
197             }
198
199             updated = true;
200
201             makeUpdates(data, group, subgroup);
202         }
203
204
205         if (updated) {
206             // something changed
207             data.update(group);
208         }
209     }
210
211     /**
212      * Makes UPDATE messages for each PDP in a subgroup.
213      *
214      * @param data session data
215      * @param group group containing the subgroup
216      * @param subgroup subgroup whose PDPs should receive messages
217      */
218     protected void makeUpdates(SessionData data, PdpGroup group, PdpSubGroup subgroup) {
219         for (Pdp pdp : subgroup.getPdpInstances()) {
220             data.addUpdate(makeUpdate(data, group, subgroup, pdp));
221         }
222     }
223
224     /**
225      * Makes an UPDATE message for a particular PDP.
226      *
227      * @param data session data
228      * @param group group to which the PDP should belong
229      * @param subgroup subgroup to which the PDP should belong
230      * @param pdp the PDP of interest
231      * @return a new UPDATE message
232      */
233     private PdpUpdate makeUpdate(SessionData data, PdpGroup group, PdpSubGroup subgroup, Pdp pdp) {
234
235         PdpUpdate update = new PdpUpdate();
236
237         update.setName(pdp.getInstanceId());
238         update.setDescription(group.getDescription());
239         update.setPdpGroup(group.getName());
240         update.setPdpSubgroup(subgroup.getPdpType());
241         update.setPolicies(subgroup.getPolicies().stream().map(ToscaPolicyIdentifierOptVersion::new)
242                         .map(ident -> getPolicy(data, ident)).collect(Collectors.toList()));
243
244         return update;
245     }
246
247     /**
248      * Gets the specified policy.
249      *
250      * @param data session data
251      * @param ident policy identifier, with an optional version
252      * @return the policy of interest
253      * @throws PfModelRuntimeException if an error occurred or the policy was not found
254      */
255     private ToscaPolicy getPolicy(SessionData data, ToscaPolicyIdentifierOptVersion ident) {
256         try {
257             ToscaPolicy policy = data.getPolicy(ident);
258             if (policy == null) {
259                 throw new PfModelRuntimeException(Status.NOT_FOUND,
260                                 "cannot find policy: " + ident.getName() + " " + ident.getVersion());
261             }
262
263             return policy;
264
265         } catch (PfModelException e) {
266             throw new PfModelRuntimeException(e.getErrorResponse().getResponseCode(),
267                             e.getErrorResponse().getErrorMessage(), e);
268         }
269     }
270
271     @FunctionalInterface
272     public static interface BiConsumerWithEx<F, S> {
273         /**
274          * Performs this operation on the given arguments.
275          *
276          * @param firstArg the first input argument
277          * @param secondArg the second input argument
278          * @throws PfModelException if an error occurred
279          */
280         void accept(F firstArg, S secondArg) throws PfModelException;
281     }
282
283     @FunctionalInterface
284     public static interface Updater {
285         boolean apply(PdpGroup group, PdpSubGroup subgroup) throws PfModelException;
286     }
287 }