Remove PDP from all active groups
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / comm / PdpModifyRequestMap.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.comm;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import org.onap.policy.models.base.PfModelException;
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.PdpGroupFilter;
32 import org.onap.policy.models.pdp.concepts.PdpMessage;
33 import org.onap.policy.models.pdp.concepts.PdpStateChange;
34 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
35 import org.onap.policy.models.pdp.concepts.PdpUpdate;
36 import org.onap.policy.models.pdp.enums.PdpState;
37 import org.onap.policy.models.provider.PolicyModelsProvider;
38 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
39 import org.onap.policy.pap.main.comm.msgdata.Request;
40 import org.onap.policy.pap.main.comm.msgdata.RequestListener;
41 import org.onap.policy.pap.main.comm.msgdata.StateChangeReq;
42 import org.onap.policy.pap.main.comm.msgdata.UpdateReq;
43 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
44 import org.onap.policy.pap.main.parameters.RequestParams;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * Maps a PDP name to requests that modify PDPs.
50  */
51 public class PdpModifyRequestMap {
52     private static final Logger logger = LoggerFactory.getLogger(PdpModifyRequestMap.class);
53
54     private static final String UNEXPECTED_BROADCAST = "unexpected broadcast message: ";
55
56     /**
57      * Maps a PDP name to its outstanding requests.
58      */
59     private final Map<String, PdpRequests> pdp2requests = new HashMap<>();
60
61     /**
62      * PDP modification lock.
63      */
64     private final Object modifyLock;
65
66     /**
67      * The configuration parameters.
68      */
69     private final PdpModifyRequestMapParams params;
70
71     /**
72      * Factory for PAP DAO.
73      */
74     private final PolicyModelsProviderFactoryWrapper daoFactory;
75
76
77     /**
78      * Constructs the object.
79      *
80      * @param params configuration parameters
81      *
82      * @throws IllegalArgumentException if a required parameter is not set
83      */
84     public PdpModifyRequestMap(PdpModifyRequestMapParams params) {
85         params.validate();
86
87         this.params = params;
88         this.modifyLock = params.getModifyLock();
89         this.daoFactory = params.getDaoFactory();
90     }
91
92     /**
93      * Stops publishing requests to the given PDP.
94      *
95      * @param pdpName PDP name
96      */
97     public void stopPublishing(String pdpName) {
98         synchronized (modifyLock) {
99             PdpRequests requests = pdp2requests.remove(pdpName);
100             if (requests != null) {
101                 requests.stopPublishing();
102             }
103         }
104     }
105
106     /**
107      * Adds a pair of requests to the map.
108      *
109      * @param update the UPDATE request or {@code null}
110      * @param stateChange the STATE-CHANGE request or {@code null}
111      */
112     public void addRequest(PdpUpdate update, PdpStateChange stateChange) {
113         if (update == null) {
114             addRequest(stateChange);
115
116         } else {
117             synchronized (modifyLock) {
118                 addRequest(update);
119                 addRequest(stateChange);
120             }
121         }
122     }
123
124     /**
125      * Adds an UPDATE request to the map.
126      *
127      * @param update the UPDATE request or {@code null}
128      */
129     public void addRequest(PdpUpdate update) {
130         if (update == null) {
131             return;
132         }
133
134         if (isBroadcast(update)) {
135             throw new IllegalArgumentException(UNEXPECTED_BROADCAST + update);
136         }
137
138         // @formatter:off
139         RequestParams reqparams = new RequestParams()
140             .setMaxRetryCount(params.getParams().getUpdateParameters().getMaxRetryCount())
141             .setTimers(params.getUpdateTimers())
142             .setModifyLock(params.getModifyLock())
143             .setPublisher(params.getPublisher())
144             .setResponseDispatcher(params.getResponseDispatcher());
145         // @formatter:on
146
147         String name = update.getName() + " " + PdpUpdate.class.getSimpleName();
148         UpdateReq request = new UpdateReq(reqparams, name, update);
149
150         addSingleton(request);
151     }
152
153     /**
154      * Adds a STATE-CHANGE request to the map.
155      *
156      * @param stateChange the STATE-CHANGE request or {@code null}
157      */
158     public void addRequest(PdpStateChange stateChange) {
159         if (stateChange == null) {
160             return;
161         }
162
163         if (isBroadcast(stateChange)) {
164             throw new IllegalArgumentException(UNEXPECTED_BROADCAST + stateChange);
165         }
166
167         // @formatter:off
168         RequestParams reqparams = new RequestParams()
169             .setMaxRetryCount(params.getParams().getStateChangeParameters().getMaxRetryCount())
170             .setTimers(params.getStateChangeTimers())
171             .setModifyLock(params.getModifyLock())
172             .setPublisher(params.getPublisher())
173             .setResponseDispatcher(params.getResponseDispatcher());
174         // @formatter:on
175
176         String name = stateChange.getName() + " " + PdpStateChange.class.getSimpleName();
177         StateChangeReq request = new StateChangeReq(reqparams, name, stateChange);
178
179         addSingleton(request);
180     }
181
182     /**
183      * Determines if a message is a broadcast message.
184      *
185      * @param message the message to examine
186      * @return {@code true} if the message is a broadcast message, {@code false} if
187      *         destined for a single PDP
188      */
189     private boolean isBroadcast(PdpMessage message) {
190         return (message.getName() == null);
191     }
192
193     /**
194      * Configures and adds a request to the map.
195      *
196      * @param request the request to be added
197      */
198     private void addSingleton(Request request) {
199
200         synchronized (modifyLock) {
201             PdpRequests requests = pdp2requests.computeIfAbsent(request.getMessage().getName(), this::makePdpRequests);
202
203             request.setListener(new SingletonListener(requests, request));
204             requests.addSingleton(request);
205         }
206     }
207
208     /**
209      * Starts the next request associated with a PDP.
210      *
211      * @param requests current set of requests
212      * @param request the request that just completed
213      */
214     private void startNextRequest(PdpRequests requests, Request request) {
215         if (!requests.startNextRequest(request)) {
216             pdp2requests.remove(requests.getPdpName(), requests);
217         }
218     }
219
220     /**
221      * Disables a PDP by removing it from its subgroup and then sending it a PASSIVE
222      * request.
223      *
224      * @param requests the requests associated with the PDP to be disabled
225      */
226     private void disablePdp(PdpRequests requests) {
227
228         // remove the requests from the map
229         if (!pdp2requests.remove(requests.getPdpName(), requests)) {
230             // don't have the info we need to disable it
231             logger.warn("no requests with which to disable {}", requests.getPdpName());
232             return;
233         }
234
235         logger.warn("disabling {}", requests.getPdpName());
236
237         requests.stopPublishing();
238
239         // remove the PDP from all groups
240         try {
241             removeFromGroups(requests.getPdpName());
242         } catch (PfModelException e) {
243             logger.info("unable to remove PDP {} from subgroup", requests.getPdpName(), e);
244         }
245
246         // send the state change
247         PdpStateChange change = new PdpStateChange();
248         change.setName(requests.getPdpName());
249         change.setState(PdpState.PASSIVE);
250         addRequest(change);
251     }
252
253     /**
254      * Removes a PDP from all active groups.
255      *
256      * @param pdpName name of the PDP to be removed
257      * @throws PfModelException if an error occurs
258      */
259     public void removeFromGroups(String pdpName) throws PfModelException {
260
261         try (PolicyModelsProvider dao = daoFactory.create()) {
262
263             PdpGroupFilter filter = PdpGroupFilter.builder().groupState(PdpState.ACTIVE).build();
264             List<PdpGroup> groups = dao.getFilteredPdpGroups(filter);
265             List<PdpGroup> updates = new ArrayList<>(1);
266
267             for (PdpGroup group : groups) {
268                 if (removeFromGroup(pdpName, group)) {
269                     updates.add(group);
270                 }
271             }
272
273             if (!updates.isEmpty()) {
274                 dao.updatePdpGroups(updates);
275             }
276         }
277     }
278
279     /**
280      * Removes a PDP from a group.
281      *
282      * @param pdpName name of the PDP to be removed
283      * @param group group from which it should be removed
284      * @return {@code true} if the PDP was removed from the, {@code false} if it was not
285      *         assigned to the group
286      */
287     private boolean removeFromGroup(String pdpName, PdpGroup group) {
288         for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
289             if (removeFromSubgroup(pdpName, group, subgrp)) {
290                 return true;
291             }
292         }
293
294         return false;
295     }
296
297     /**
298      * Removes a PDP from a subgroup.
299      *
300      * @param pdpName name of the PDP to be removed
301      * @param group group from which to attempt to remove the PDP
302      * @param subgrp subgroup from which to attempt to remove the PDP
303      * @return {@code true} if the PDP was removed, {@code false} if the PDP was not in
304      *         the group
305      */
306     private boolean removeFromSubgroup(String pdpName, PdpGroup group, PdpSubGroup subgrp) {
307
308         Iterator<Pdp> iter = subgrp.getPdpInstances().iterator();
309
310         while (iter.hasNext()) {
311             Pdp instance = iter.next();
312
313             if (pdpName.equals(instance.getInstanceId())) {
314                 logger.info("removed {} from group={} version={} subgroup={}", pdpName, group.getName(),
315                                 group.getVersion(), subgrp.getPdpType());
316                 iter.remove();
317                 subgrp.setCurrentInstanceCount(subgrp.getPdpInstances().size());
318                 return true;
319             }
320         }
321
322         return false;
323     }
324
325     /**
326      * Creates a new set of requests for a PDP. May be overridden by junit tests.
327      *
328      * @param pdpName PDP name
329      * @return a new set of requests
330      */
331     protected PdpRequests makePdpRequests(String pdpName) {
332         return new PdpRequests(pdpName);
333     }
334
335     /**
336      * Listener for singleton request events.
337      */
338     private class SingletonListener implements RequestListener {
339         private final PdpRequests requests;
340         private final Request request;
341
342         public SingletonListener(PdpRequests requests, Request request) {
343             this.requests = requests;
344             this.request = request;
345         }
346
347         @Override
348         public void failure(String pdpName, String reason) {
349             if (requests.getPdpName().equals(pdpName)) {
350                 disablePdp(requests);
351             }
352         }
353
354         @Override
355         public void success(String pdpName) {
356             if (requests.getPdpName().equals(pdpName)) {
357                 if (pdp2requests.get(requests.getPdpName()) == requests) {
358                     startNextRequest(requests, request);
359
360                 } else {
361                     logger.info("discard old requests for {}", pdpName);
362                     requests.stopPublishing();
363                 }
364             }
365         }
366
367         @Override
368         public void retryCountExhausted() {
369             disablePdp(requests);
370         }
371     }
372 }