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