2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2021, 2023 Nordix Foundation.
6 * Modifications Copyright (C) 2019, 2021 AT&T Intellectual Property.
7 * Modifications Copyright (C) 2021 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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.policy.pap.main.rest;
25 import jakarta.ws.rs.core.Response.Status;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
31 import java.util.Objects;
33 import java.util.function.Consumer;
34 import java.util.stream.Collectors;
35 import org.onap.policy.common.parameters.BeanValidationResult;
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.onap.policy.pap.main.PapConstants;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.springframework.stereotype.Service;
56 * Provider for PAP component to create or update PDP groups. The following items must be in the
59 * <li>PDP Modification Lock</li>
60 * <li>PDP Modify Request Map</li>
61 * <li>PAP DAO Factory</li>
65 public class PdpGroupCreateOrUpdateProvider extends ProviderBase {
66 private static final Logger logger = LoggerFactory.getLogger(PdpGroupCreateOrUpdateProvider.class);
69 * Creates or updates PDP groups.
71 * @param groups PDP group configurations to be created or updated
72 * @throws PfModelException if an error occurred
74 public void createOrUpdateGroups(PdpGroups groups) throws PfModelException {
75 var result = new BeanValidationResult("groups", groups);
76 groups.checkForDuplicateGroups(result);
77 if (!result.isValid()) {
78 String msg = result.getResult().trim();
80 throw new PfModelException(Status.BAD_REQUEST, msg);
82 // During PdpGroup create/update, policies are not supposed to be deployed/undeployed into the group.
83 // There is a separate API for this.
84 List<PdpSubGroup> subGroupsWithPolicies =
85 groups.getGroups().parallelStream().flatMap(group -> group.getPdpSubgroups().parallelStream())
86 .filter(subGroup -> null != subGroup.getPolicies() && !subGroup.getPolicies().isEmpty())
88 if (!subGroupsWithPolicies.isEmpty()) {
90 "Policies cannot be deployed during PdpGroup Create/Update operation. Ignoring the list of policies");
91 subGroupsWithPolicies.forEach(subGroup -> subGroup.setPolicies(Collections.emptyList()));
93 process(groups, this::createOrUpdate);
97 * Creates or updates PDP groups. This is the method that does the actual work.
99 * @param data session data
100 * @param groups PDP group configurations
101 * @throws PfModelException if an error occurred
103 private void createOrUpdate(SessionData data, PdpGroups groups) throws PfModelException {
104 var result = new BeanValidationResult("groups", groups);
106 for (PdpGroup group : groups.getGroups()) {
107 PdpGroup dbgroup = data.getGroup(group.getName());
108 ValidationResult groupValidationResult;
109 if (dbgroup == null) {
111 groupValidationResult = group.validatePapRest(false);
112 if (groupValidationResult.isValid()) {
113 result.addResult(addGroup(data, group));
115 result.addResult(groupValidationResult);
120 groupValidationResult = group.validatePapRest(true);
121 if (groupValidationResult.isValid()) {
122 result.addResult(updateGroup(data, dbgroup, group));
124 result.addResult(groupValidationResult);
129 if (!result.isValid()) {
130 throw new PfModelException(Status.BAD_REQUEST, result.getResult().trim());
137 * @param data session data
138 * @param group the group to be added
139 * @return the validation result
140 * @throws PfModelException if an error occurred
142 private ValidationResult addGroup(SessionData data, PdpGroup group) throws PfModelException {
143 var result = new BeanValidationResult(group.getName(), group);
145 validateGroupOnly(group, result);
146 if (!result.isValid()) {
151 if (group.getPdpGroupState() == null) {
152 group.setPdpGroupState(PdpState.ACTIVE);
155 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
156 result.addResult(addSubGroup(data, subgrp));
159 if (result.isValid()) {
167 * Performs additional validations of a group, but does not examine the subgroups.
169 * @param group the group to be validated
170 * @param result the validation result
172 private void validateGroupOnly(PdpGroup group, BeanValidationResult result) {
173 if (group.getPdpGroupState() == null) {
177 PdpState pdpGroupState = group.getPdpGroupState();
178 if (pdpGroupState != PdpState.ACTIVE && pdpGroupState != PdpState.PASSIVE) {
179 result.addResult("pdpGroupState", group.getPdpGroupState(),
180 ValidationStatus.INVALID, "must be null, ACTIVE, or PASSIVE");
185 * Updates an existing group.
187 * @param data session data
188 * @param dbgroup the group, as it appears within the DB
189 * @param group the group to be added
190 * @return the validation result
191 * @throws PfModelException if an error occurred
193 private ValidationResult updateGroup(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
194 var result = new BeanValidationResult(group.getName(), group);
196 if (!Objects.equals(dbgroup.getProperties(), group.getProperties())) {
197 result.addResult("properties", "", ValidationStatus.INVALID, "cannot change properties");
200 boolean updated = updateField(dbgroup.getDescription(), group.getDescription(), dbgroup::setDescription);
202 updateField(dbgroup.getPdpGroupState(), group.getPdpGroupState(), dbgroup::setPdpGroupState) || updated;
203 updated = notifyPdpsDelSubGroups(data, dbgroup, group) || updated;
204 updated = addOrUpdateSubGroups(data, dbgroup, group, result) || updated;
206 if (result.isValid() && updated) {
214 * Updates a field, if the new value is different from the old value.
216 * @param oldValue old value
217 * @param newValue new value
218 * @param setter function to set the field to the new value
219 * @return {@code true} if the field was updated, {@code false} if it already matched the new value
221 private <T> boolean updateField(T oldValue, T newValue, Consumer<T> setter) {
222 if (oldValue == newValue) {
226 if (oldValue != null && oldValue.equals(newValue)) {
230 setter.accept(newValue);
235 * Adds or updates subgroups within the group.
237 * @param data session data
238 * @param dbgroup the group, as it appears within the DB
239 * @param group the group to be added
240 * @param result the validation result
241 * @return {@code true} if the DB group was modified, {@code false} otherwise
242 * @throws PfModelException if an error occurred
244 private boolean addOrUpdateSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group,
245 BeanValidationResult result) throws PfModelException {
247 // create a map of existing subgroups
248 Map<String, PdpSubGroup> type2sub = new HashMap<>();
249 dbgroup.getPdpSubgroups().forEach(subgrp -> type2sub.put(subgrp.getPdpType(), subgrp));
253 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
254 PdpSubGroup dbsub = type2sub.get(subgrp.getPdpType());
255 var subResult = new BeanValidationResult(subgrp.getPdpType(), subgrp);
259 subResult.addResult(addSubGroup(data, subgrp));
260 dbgroup.getPdpSubgroups().add(subgrp);
263 updated = updateSubGroup(dbsub, subgrp, subResult) || updated;
266 result.addResult(subResult);
273 * Notifies any PDPs whose subgroups are being removed.
275 * @param data session data
276 * @param dbgroup the group, as it appears within the DB
277 * @param group the group being updated
278 * @return {@code true} if a subgroup was removed, {@code false} otherwise
279 * @throws PfModelException if an error occurred
281 private boolean notifyPdpsDelSubGroups(SessionData data, PdpGroup dbgroup, PdpGroup group) throws PfModelException {
284 // subgroups, as they appear within the updated group
285 Set<String> subgroups = new HashSet<>();
286 group.getPdpSubgroups().forEach(subgrp -> subgroups.add(subgrp.getPdpType()));
288 // loop through subgroups as they appear within the DB
289 for (PdpSubGroup subgrp : dbgroup.getPdpSubgroups()) {
291 if (!subgroups.contains(subgrp.getPdpType())) {
292 // this subgroup no longer appears - notify its PDPs
294 notifyPdpsDelSubGroup(data, subgrp);
295 trackPdpsDelSubGroup(data, dbgroup.getName(), subgrp);
303 * Notifies the PDPs that their subgroup is being removed.
305 * @param data session data
306 * @param subgrp subgroup that is being removed
308 private void notifyPdpsDelSubGroup(SessionData data, PdpSubGroup subgrp) {
309 for (Pdp pdp : subgrp.getPdpInstances()) {
310 String name = pdp.getInstanceId();
313 var change = new PdpStateChange();
314 change.setSource(PapConstants.PAP_NAME);
315 change.setName(name);
316 change.setState(PdpState.PASSIVE);
318 // remove it from subgroup and undeploy all policies
319 var update = new PdpUpdate();
320 update.setSource(PapConstants.PAP_NAME);
321 update.setName(name);
323 data.addRequests(update, change);
328 * Tracks PDP responses when their subgroup is removed.
330 * @param data session data
331 * @param pdpGroup PdpGroup name
332 * @param subgrp subgroup that is being removed
333 * @throws PfModelException if an error occurred
335 private void trackPdpsDelSubGroup(SessionData data, String pdpGroup, PdpSubGroup subgrp) throws PfModelException {
336 Set<String> pdps = subgrp.getPdpInstances().stream().map(Pdp::getInstanceId).collect(Collectors.toSet());
338 for (ToscaConceptIdentifier policyId : subgrp.getPolicies()) {
339 data.trackUndeploy(policyId, pdps, pdpGroup, subgrp.getPdpType());
344 * Adds a new subgroup.
346 * @param data session data
347 * @param subgrp the subgroup to be added, updated to fully qualified versions upon
349 * @return the validation result
350 * @throws PfModelException if an error occurred
352 private ValidationResult addSubGroup(SessionData data, PdpSubGroup subgrp) throws PfModelException {
353 subgrp.setCurrentInstanceCount(0);
354 subgrp.setPdpInstances(Collections.emptyList());
356 var result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
358 result.addResult(validateSupportedTypes(data, subgrp));
363 * Updates an existing subgroup.
365 * @param dbsub the subgroup, from the DB
366 * @param subgrp the subgroup to be updated, updated to fully qualified versions upon
368 * @param container container for additional validation results
369 * @return {@code true} if the subgroup content was changed, {@code false} if there were no changes
371 private boolean updateSubGroup(PdpSubGroup dbsub, PdpSubGroup subgrp, BeanValidationResult container) {
373 // perform additional validations first
374 if (!validateSubGroup(dbsub, subgrp, container)) {
378 if (null != subgrp.getSupportedPolicyTypes() && !new HashSet<>(dbsub.getSupportedPolicyTypes())
379 .equals(new HashSet<>(subgrp.getSupportedPolicyTypes()))) {
380 logger.warn("Supported policy types cannot be updated while updating PdpGroup. "
381 + "Hence, ignoring the new set of supported policy types.");
384 // while updating PdpGroup, list of policies (already deployed ones) and supported policies (the ones provided
385 // during PdpGroup creation) has to be retained
386 subgrp.setSupportedPolicyTypes(dbsub.getSupportedPolicyTypes());
387 subgrp.setPolicies(dbsub.getPolicies());
388 return updateField(dbsub.getDesiredInstanceCount(), subgrp.getDesiredInstanceCount(),
389 dbsub::setDesiredInstanceCount);
393 * Performs additional validations of a subgroup.
395 * @param dbsub the subgroup, from the DB
396 * @param subgrp the subgroup to be validated, updated to fully qualified versions
398 * @param container container for additional validation results
399 * @return {@code true} if the subgroup is valid, {@code false} otherwise
401 private boolean validateSubGroup(PdpSubGroup dbsub, PdpSubGroup subgrp, BeanValidationResult container) {
403 var result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
405 if (!Objects.equals(dbsub.getProperties(), subgrp.getProperties())) {
406 result.addResult("properties", "", ValidationStatus.INVALID, "cannot change properties");
409 container.addResult(result);
411 return result.isValid();
415 * Performs validations of the supported policy types within a subgroup.
417 * @param data session data
418 * @param subgrp the subgroup to be validated
419 * @return the validation result
420 * @throws PfModelException if an error occurred
422 private ValidationResult validateSupportedTypes(SessionData data, PdpSubGroup subgrp) throws PfModelException {
423 var result = new BeanValidationResult(subgrp.getPdpType(), subgrp);
424 for (ToscaConceptIdentifier type : subgrp.getSupportedPolicyTypes()) {
425 if (!type.getName().endsWith(".*") && data.getPolicyType(type) == null) {
426 result.addResult("policy type", type, ValidationStatus.INVALID, "unknown policy type");
434 protected Updater makeUpdater(SessionData data, ToscaPolicy policy,
435 ToscaConceptIdentifierOptVersion desiredPolicy) {
436 throw new UnsupportedOperationException("makeUpdater should not be invoked");