2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2021 Nordix Foundation.
6 * Modifications Copyright (C) 2019, 2021 AT&T Intellectual Property.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.pap.main.rest;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
29 import java.util.Objects;
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.ToscaConceptIdentifier;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifierOptVersion;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * Provider for PAP component to create or update PDP groups. The following items must be in the
57 * <li>PDP Modification Lock</li>
58 * <li>PDP Modify Request Map</li>
59 * <li>PAP DAO Factory</li>
62 public class PdpGroupCreateOrUpdateProvider extends ProviderBase {
63 private static final Logger logger = LoggerFactory.getLogger(PdpGroupCreateOrUpdateProvider.class);
66 * Constructs the object.
68 public PdpGroupCreateOrUpdateProvider() {
73 * Creates or updates PDP groups.
75 * @param groups PDP group configurations to be created or updated
76 * @throws PfModelException if an error occurred
78 public void createOrUpdateGroups(PdpGroups groups) throws PfModelException {
79 BeanValidationResult result = new BeanValidationResult("groups", groups);
80 groups.checkForDuplicateGroups(result);
81 if (!result.isValid()) {
82 String msg = result.getResult().trim();
84 throw new PfModelException(Status.BAD_REQUEST, msg);
86 // During PdpGroup create/update, policies are not supposed to be deployed/undeployed into the group.
87 // There is a separate API for this.
88 List<PdpSubGroup> subGroupsWithPolicies =
89 groups.getGroups().parallelStream().flatMap(group -> group.getPdpSubgroups().parallelStream())
90 .filter(subGroup -> null != subGroup.getPolicies() && !subGroup.getPolicies().isEmpty())
91 .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());
112 ValidationResult groupValidationResult;
113 if (dbgroup == null) {
115 groupValidationResult = group.validatePapRest(false);
116 if (groupValidationResult.isValid()) {
117 result.addResult(addGroup(data, group));
119 result.addResult(groupValidationResult);
124 groupValidationResult = group.validatePapRest(true);
125 if (groupValidationResult.isValid()) {
126 result.addResult(updateGroup(data, dbgroup, group));
128 result.addResult(groupValidationResult);
133 if (!result.isValid()) {
134 throw new PfModelException(Status.BAD_REQUEST, result.getResult().trim());
141 * @param data session data
142 * @param group the group to be added
143 * @return the validation result
144 * @throws PfModelException if an error occurred
146 private ValidationResult addGroup(SessionData data, PdpGroup group) throws PfModelException {
147 BeanValidationResult result = new BeanValidationResult(group.getName(), group);
149 validateGroupOnly(group, result);
150 if (!result.isValid()) {
155 if (group.getPdpGroupState() == null) {
156 group.setPdpGroupState(PdpState.ACTIVE);
159 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
160 result.addResult(addSubGroup(data, subgrp));
163 if (result.isValid()) {
171 * Performs additional validations of a group, but does not examine the subgroups.
173 * @param group the group to be validated
174 * @param result the validation result
176 private void validateGroupOnly(PdpGroup group, BeanValidationResult result) {
177 if (group.getPdpGroupState() == null) {
181 switch (group.getPdpGroupState()) {
187 result.addResult(new ObjectValidationResult("pdpGroupState", group.getPdpGroupState(),
188 ValidationStatus.INVALID, "must be null, ACTIVE, or PASSIVE"));
194 * Updates an existing group.
196 * @param data session data
197 * @param dbgroup the group, as it appears within the DB
198 * @param group the group to be added
199 * @return the validation result
200 * @throws PfModelException if an error occurred
202 private ValidationResult updateGroup(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
203 BeanValidationResult result = new BeanValidationResult(group.getName(), group);
205 if (!Objects.equals(dbgroup.getProperties(), group.getProperties())) {
207 new ObjectValidationResult("properties", "", ValidationStatus.INVALID, "cannot change properties"));
210 boolean updated = updateField(dbgroup.getDescription(), group.getDescription(), dbgroup::setDescription);
212 updateField(dbgroup.getPdpGroupState(), group.getPdpGroupState(), dbgroup::setPdpGroupState) || updated;
213 updated = notifyPdpsDelSubGroups(data, dbgroup, group) || updated;
214 updated = addOrUpdateSubGroups(data, dbgroup, group, result) || updated;
216 if (result.isValid() && updated) {
224 * Updates a field, if the new value is different than the old value.
226 * @param oldValue old value
227 * @param newValue new value
228 * @param setter function to set the field to the new value
229 * @return {@code true} if the field was updated, {@code false} if it already matched
232 private <T> boolean updateField(T oldValue, T newValue, Consumer<T> setter) {
233 if (oldValue == newValue) {
237 if (oldValue != null && oldValue.equals(newValue)) {
241 setter.accept(newValue);
246 * Adds or updates subgroups within the group.
248 * @param data session data
249 * @param dbgroup the group, as it appears within the DB
250 * @param group the group to be added
251 * @param result the validation result
252 * @return {@code true} if the DB group was modified, {@code false} otherwise
253 * @throws PfModelException if an error occurred
255 private boolean addOrUpdateSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group,
256 BeanValidationResult result) throws PfModelException {
258 // create a map of existing subgroups
259 Map<String, PdpSubGroup> type2sub = new HashMap<>();
260 dbgroup.getPdpSubgroups().forEach(subgrp -> type2sub.put(subgrp.getPdpType(), subgrp));
262 boolean updated = false;
264 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
265 PdpSubGroup dbsub = type2sub.get(subgrp.getPdpType());
266 BeanValidationResult subResult = new BeanValidationResult(subgrp.getPdpType(), subgrp);
270 subResult.addResult(addSubGroup(data, subgrp));
271 dbgroup.getPdpSubgroups().add(subgrp);
274 updated = updateSubGroup(dbsub, subgrp, subResult) || updated;
277 result.addResult(subResult);
284 * Notifies any PDPs whose subgroups are being removed.
286 * @param data session data
287 * @param dbgroup the group, as it appears within the DB
288 * @param group the group being updated
289 * @return {@code true} if a subgroup was removed, {@code false} otherwise
290 * @throws PfModelException if an error occurred
292 private boolean notifyPdpsDelSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
293 boolean updated = false;
295 // subgroups, as they appear within the updated group
296 Set<String> subgroups = new HashSet<>();
297 group.getPdpSubgroups().forEach(subgrp -> subgroups.add(subgrp.getPdpType()));
299 // loop through subgroups as they appear within the DB
300 for (PdpSubGroup subgrp : dbgroup.getPdpSubgroups()) {
302 if (!subgroups.contains(subgrp.getPdpType())) {
303 // this subgroup no longer appears - notify its PDPs
305 notifyPdpsDelSubGroup(data, subgrp);
306 trackPdpsDelSubGroup(data, dbgroup.getName(), subgrp);
314 * Notifies the PDPs that their subgroup is being removed.
316 * @param data session data
317 * @param subgrp subgroup that is being removed
319 private void notifyPdpsDelSubGroup(SessionData data, PdpSubGroup subgrp) {
320 for (Pdp pdp : subgrp.getPdpInstances()) {
321 String name = pdp.getInstanceId();
324 PdpStateChange change = new PdpStateChange();
325 change.setName(name);
326 change.setState(PdpState.PASSIVE);
328 // remove it from subgroup and undeploy all policies
329 PdpUpdate update = new PdpUpdate();
330 update.setName(name);
332 data.addRequests(update, change);
337 * Tracks PDP responses when their subgroup is removed.
339 * @param data session data
340 * @param pdpGroup PdpGroup name
341 * @param subgrp subgroup that is being removed
342 * @throws PfModelException if an error occurred
344 private void trackPdpsDelSubGroup(SessionData data, String pdpGroup, PdpSubGroup subgrp) throws PfModelException {
345 Set<String> pdps = subgrp.getPdpInstances().stream().map(Pdp::getInstanceId).collect(Collectors.toSet());
347 for (ToscaConceptIdentifier policyId : subgrp.getPolicies()) {
348 data.trackUndeploy(policyId, pdps, pdpGroup, subgrp.getPdpType());
353 * Adds a new subgroup.
355 * @param data session data
356 * @param subgrp the subgroup to be added, updated to fully qualified versions upon
358 * @return the validation result
359 * @throws PfModelException if an error occurred
361 private ValidationResult addSubGroup(SessionData data, PdpSubGroup subgrp) throws PfModelException {
362 subgrp.setCurrentInstanceCount(0);
363 subgrp.setPdpInstances(Collections.emptyList());
365 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
367 result.addResult(validateSupportedTypes(data, subgrp));
372 * Updates an existing subgroup.
374 * @param dbsub the subgroup, from the DB
375 * @param subgrp the subgroup to be updated, updated to fully qualified versions upon
377 * @param container container for additional validation results
378 * @return {@code true} if the subgroup content was changed, {@code false} if there
381 private boolean updateSubGroup(PdpSubGroup dbsub, PdpSubGroup subgrp, BeanValidationResult container) {
383 // perform additional validations first
384 if (!validateSubGroup(dbsub, subgrp, container)) {
388 if (null != subgrp.getSupportedPolicyTypes() && !new HashSet<>(dbsub.getSupportedPolicyTypes())
389 .equals(new HashSet<>(subgrp.getSupportedPolicyTypes()))) {
390 logger.warn("Supported policy types cannot be updated while updating PdpGroup. "
391 + "Hence, ignoring the new set of supported policy types.");
394 // while updating PdpGroup, list of policies (already deployed ones) and supported policies (the ones provided
395 // during PdpGroup creation) has to be retained
396 subgrp.setSupportedPolicyTypes(dbsub.getSupportedPolicyTypes());
397 subgrp.setPolicies(dbsub.getPolicies());
398 return updateField(dbsub.getDesiredInstanceCount(), subgrp.getDesiredInstanceCount(),
399 dbsub::setDesiredInstanceCount);
403 * Performs additional validations of a subgroup.
405 * @param dbsub the subgroup, from the DB
406 * @param subgrp the subgroup to be validated, updated to fully qualified versions
408 * @param container container for additional validation results
409 * @return {@code true} if the subgroup is valid, {@code false} otherwise
411 private boolean validateSubGroup(PdpSubGroup dbsub, PdpSubGroup subgrp, BeanValidationResult container) {
413 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
415 if (!Objects.equals(dbsub.getProperties(), subgrp.getProperties())) {
417 new ObjectValidationResult("properties", "", ValidationStatus.INVALID, "cannot change properties"));
420 container.addResult(result);
422 return result.isValid();
426 * Performs validations of the supported policy types within a subgroup.
428 * @param data session data
429 * @param subgrp the subgroup to be validated
430 * @param result the validation result
431 * @throws PfModelException if an error occurred
433 private ValidationResult validateSupportedTypes(SessionData data, PdpSubGroup subgrp) throws PfModelException {
434 BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
435 for (ToscaConceptIdentifier type : subgrp.getSupportedPolicyTypes()) {
436 if (!type.getName().endsWith(".*") && data.getPolicyType(type) == null) {
438 new ObjectValidationResult("policy type", type, ValidationStatus.INVALID, "unknown policy type"));
446 protected Updater makeUpdater(SessionData data, ToscaPolicy policy,
447 ToscaConceptIdentifierOptVersion desiredPolicy) {
448 throw new UnsupportedOperationException("makeUpdater should not be invoked");