2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 Nordix Foundation.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.pap.main.rest;
23 import com.att.aft.dme2.internal.apache.commons.lang.ObjectUtils;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
31 import java.util.function.Consumer;
32 import java.util.stream.Collectors;
33 import javax.ws.rs.core.Response.Status;
34 import org.onap.policy.common.parameters.BeanValidationResult;
35 import org.onap.policy.common.parameters.ObjectValidationResult;
36 import org.onap.policy.common.parameters.ValidationResult;
37 import org.onap.policy.common.parameters.ValidationStatus;
38 import org.onap.policy.common.utils.services.Registry;
39 import org.onap.policy.models.base.PfModelException;
40 import org.onap.policy.models.pdp.concepts.Pdp;
41 import org.onap.policy.models.pdp.concepts.PdpGroup;
42 import org.onap.policy.models.pdp.concepts.PdpGroups;
43 import org.onap.policy.models.pdp.concepts.PdpStateChange;
44 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
45 import org.onap.policy.models.pdp.concepts.PdpUpdate;
46 import org.onap.policy.models.pdp.enums.PdpState;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * Provider for PAP component to create or update PDP groups. The following items must be in the
58 * <li>PDP Modification Lock</li>
59 * <li>PDP Modify Request Map</li>
60 * <li>PAP DAO Factory</li>
63 public class PdpGroupCreateOrUpdateProvider extends ProviderBase {
64 private static final Logger logger = LoggerFactory.getLogger(PdpGroupCreateOrUpdateProvider.class);
67 * Constructs the object.
69 public PdpGroupCreateOrUpdateProvider() {
74 * Creates or updates PDP groups.
76 * @param groups PDP group configurations to be created or updated
77 * @throws PfModelException if an error occurred
79 public void createOrUpdateGroups(PdpGroups groups) throws PfModelException {
80 ValidationResult result = groups.validatePapRest();
82 if (!result.isValid()) {
83 String msg = result.getResult().trim();
85 throw new PfModelException(Status.BAD_REQUEST, msg);
87 // During PdpGroup create/update, policies are not supposed to be deployed/undeployed into the group.
88 // There is a separate API for this.
89 List<PdpSubGroup> subGroupsWithPolicies =
90 groups.getGroups().parallelStream().flatMap(group -> group.getPdpSubgroups().parallelStream())
91 .filter(subGroup -> !subGroup.getPolicies().isEmpty()).collect(Collectors.toList());
92 if (!subGroupsWithPolicies.isEmpty()) {
94 "Policies cannot be deployed during PdpGroup Create/Update operation. Ignoring the list of policies");
95 subGroupsWithPolicies.forEach(subGroup -> subGroup.setPolicies(Collections.emptyList()));
97 process(groups, this::createOrUpdate);
101 * Creates or updates PDP groups. This is the method that does the actual work.
103 * @param data session data
104 * @param groups PDP group configurations
105 * @throws PfModelException if an error occurred
107 private void createOrUpdate(SessionData data, PdpGroups groups) throws PfModelException {
108 BeanValidationResult result = new BeanValidationResult("groups", groups);
110 for (PdpGroup group : groups.getGroups()) {
111 PdpGroup dbgroup = data.getGroup(group.getName());
113 if (dbgroup == null) {
114 result.addResult(addGroup(data, group));
117 result.addResult(updateGroup(data, dbgroup, group));
121 if (!result.isValid()) {
122 throw new PfModelException(Status.BAD_REQUEST, result.getResult().trim());
129 * @param data session data
130 * @param group the group to be added
131 * @return the validation result
132 * @throws PfModelException if an error occurred
134 private ValidationResult addGroup(SessionData data, PdpGroup group) throws PfModelException {
135 BeanValidationResult result = new BeanValidationResult(group.getName(), group);
137 validateGroupOnly(group, result);
138 if (!result.isValid()) {
143 if (group.getPdpGroupState() == null) {
144 group.setPdpGroupState(PdpState.ACTIVE);
147 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
148 result.addResult(addSubGroup(data, subgrp));
151 if (result.isValid()) {
159 * Performs additional validations of a group, but does not examine the subgroups.
161 * @param group the group to be validated
162 * @param result the validation result
164 private void validateGroupOnly(PdpGroup group, BeanValidationResult result) {
165 if (group.getPdpGroupState() == null) {
169 switch (group.getPdpGroupState()) {
175 result.addResult(new ObjectValidationResult("pdpGroupState", group.getPdpGroupState(),
176 ValidationStatus.INVALID, "must be null, ACTIVE, or PASSIVE"));
182 * Updates an existing group.
184 * @param data session data
185 * @param dbgroup the group, as it appears within the DB
186 * @param group the group to be added
187 * @return the validation result
188 * @throws PfModelException if an error occurred
190 private ValidationResult updateGroup(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
191 BeanValidationResult result = new BeanValidationResult(group.getName(), group);
193 if (!ObjectUtils.equals(dbgroup.getProperties(), group.getProperties())) {
195 new ObjectValidationResult("properties", "", ValidationStatus.INVALID, "cannot change properties"));
198 boolean updated = updateField(dbgroup.getDescription(), group.getDescription(), dbgroup::setDescription);
200 updateField(dbgroup.getPdpGroupState(), group.getPdpGroupState(), dbgroup::setPdpGroupState) || updated;
201 updated = notifyPdpsDelSubGroups(data, dbgroup, group) || updated;
202 updated = addOrUpdateSubGroups(data, dbgroup, group, result) || updated;
204 if (result.isValid() && updated) {
212 * Updates a field, if the new value is different than the old value.
214 * @param oldValue old value
215 * @param newValue new value
216 * @param setter function to set the field to the new value
217 * @return {@code true} if the field was updated, {@code false} if it already matched
220 private <T> boolean updateField(T oldValue, T newValue, Consumer<T> setter) {
221 if (oldValue == newValue) {
225 if (oldValue != null && oldValue.equals(newValue)) {
229 setter.accept(newValue);
234 * Adds or updates subgroups within the group.
236 * @param data session data
237 * @param dbgroup the group, as it appears within the DB
238 * @param group the group to be added
239 * @param result the validation result
240 * @return {@code true} if the DB group was modified, {@code false} otherwise
241 * @throws PfModelException if an error occurred
243 private boolean addOrUpdateSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group,
244 BeanValidationResult result) throws PfModelException {
246 // create a map of existing subgroups
247 Map<String, PdpSubGroup> type2sub = new HashMap<>();
248 dbgroup.getPdpSubgroups().forEach(subgrp -> type2sub.put(subgrp.getPdpType(), subgrp));
250 boolean updated = false;
252 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
253 PdpSubGroup dbsub = type2sub.get(subgrp.getPdpType());
254 BeanValidationResult subResult = new BeanValidationResult(subgrp.getPdpType(), subgrp);
258 subResult.addResult(addSubGroup(data, subgrp));
259 dbgroup.getPdpSubgroups().add(subgrp);
262 updated = updateSubGroup(data, dbsub, subgrp, subResult) || updated;
265 result.addResult(subResult);
272 * Notifies any PDPs whose subgroups are being removed.
274 * @param data session data
275 * @param dbgroup the group, as it appears within the DB
276 * @param group the group being updated
277 * @return {@code true} if a subgroup was removed, {@code false} otherwise
278 * @throws PfModelException if an error occurred
280 private boolean notifyPdpsDelSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
281 boolean updated = false;
283 // subgroups, as they appear within the updated group
284 Set<String> subgroups = new HashSet<>();
285 group.getPdpSubgroups().forEach(subgrp -> subgroups.add(subgrp.getPdpType()));
287 // loop through subgroups as they appear within the DB
288 for (PdpSubGroup subgrp : dbgroup.getPdpSubgroups()) {
290 if (!subgroups.contains(subgrp.getPdpType())) {
291 // this subgroup no longer appears - notify its PDPs
293 notifyPdpsDelSubGroup(data, subgrp);
294 trackPdpsDelSubGroup(data, subgrp);
302 * Notifies the PDPs that their subgroup is being removed.
304 * @param data session data
305 * @param subgrp subgroup that is being removed
307 private void notifyPdpsDelSubGroup(SessionData data, PdpSubGroup subgrp) {
308 for (Pdp pdp : subgrp.getPdpInstances()) {
309 String name = pdp.getInstanceId();
312 PdpStateChange change = new PdpStateChange();
313 change.setName(name);
314 change.setState(PdpState.PASSIVE);
316 // remove it from subgroup and undeploy all policies
317 PdpUpdate update = new PdpUpdate();
318 update.setName(name);
320 data.addRequests(update, change);
325 * Tracks PDP responses when their subgroup is removed.
327 * @param data session data
328 * @param subgrp subgroup that is being removed
329 * @throws PfModelException if an error occurred
331 private void trackPdpsDelSubGroup(SessionData data, PdpSubGroup subgrp) throws PfModelException {
332 Set<String> pdps = subgrp.getPdpInstances().stream().map(Pdp::getInstanceId).collect(Collectors.toSet());
334 for (ToscaPolicyIdentifier policyId : subgrp.getPolicies()) {
335 data.trackUndeploy(policyId, pdps);
340 * Adds a new subgroup.
342 * @param data session data
343 * @param subgrp the subgroup to be added, updated to fully qualified versions upon
345 * @return the validation result
346 * @throws PfModelException if an error occurred
348 private ValidationResult addSubGroup(SessionData data, PdpSubGroup subgrp) throws PfModelException {
349 subgrp.setCurrentInstanceCount(0);
350 subgrp.setPdpInstances(Collections.emptyList());
352 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
354 result.addResult(validateSupportedTypes(data, subgrp));
359 * Updates an existing subgroup.
361 * @param data session data
362 * @param dbsub the subgroup, from the DB
363 * @param subgrp the subgroup to be updated, updated to fully qualified versions upon
365 * @param container container for additional validation results
366 * @return {@code true} if the subgroup content was changed, {@code false} if there
368 * @throws PfModelException if an error occurred
370 private boolean updateSubGroup(SessionData data, PdpSubGroup dbsub, PdpSubGroup subgrp,
371 BeanValidationResult container) throws PfModelException {
373 // perform additional validations first
374 if (!validateSubGroup(data, dbsub, subgrp, container)) {
378 boolean updated = updateList(dbsub.getSupportedPolicyTypes(), subgrp.getSupportedPolicyTypes(),
379 dbsub::setSupportedPolicyTypes);
381 return updateField(dbsub.getDesiredInstanceCount(), subgrp.getDesiredInstanceCount(),
382 dbsub::setDesiredInstanceCount) || updated;
386 * Performs additional validations of a subgroup.
388 * @param data session data
389 * @param dbsub the subgroup, from the DB
390 * @param subgrp the subgroup to be validated, updated to fully qualified versions
392 * @param container container for additional validation results
393 * @return {@code true} if the subgroup is valid, {@code false} otherwise
394 * @throws PfModelException if an error occurred
396 private boolean validateSubGroup(SessionData data, PdpSubGroup dbsub, PdpSubGroup subgrp,
397 BeanValidationResult container) throws PfModelException {
399 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
401 if (!ObjectUtils.equals(dbsub.getProperties(), subgrp.getProperties())) {
403 new ObjectValidationResult("properties", "", ValidationStatus.INVALID, "cannot change properties"));
406 result.addResult(validateSupportedTypes(data, subgrp));
407 container.addResult(result);
409 return result.isValid();
413 * Updates a DB list with items from a new list.
415 * @param dblist the list from the DB
416 * @param newList the new list
417 * @param setter function to set the new list
418 * @return {@code true} if the list changed, {@code false} if the lists were the same
420 private <T> boolean updateList(List<T> dblist, List<T> newList, Consumer<List<T>> setter) {
422 Set<T> dbTypes = new HashSet<>(dblist);
423 Set<T> newTypes = new HashSet<>(newList);
425 if (dbTypes.equals(newTypes)) {
429 setter.accept(new ArrayList<>(newTypes));
435 * Performs additional validations of the supported policy types within a subgroup.
437 * @param data session data
438 * @param subgrp the subgroup to be validated
439 * @param result the validation result
440 * @throws PfModelException if an error occurred
442 private ValidationResult validateSupportedTypes(SessionData data, PdpSubGroup subgrp) throws PfModelException {
443 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
445 for (ToscaPolicyTypeIdentifier type : subgrp.getSupportedPolicyTypes()) {
446 if (!type.getName().endsWith(".*") && data.getPolicyType(type) == null) {
448 new ObjectValidationResult("policy type", type, ValidationStatus.INVALID, "unknown policy type"));
456 protected Updater makeUpdater(SessionData data, ToscaPolicy policy, ToscaPolicyIdentifierOptVersion desiredPolicy) {
457 throw new UnsupportedOperationException("makeUpdater should not be invoked");