2 * ============LICENSE_START=======================================================
\r
4 * ================================================================================
\r
5 * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
\r
6 * ================================================================================
\r
7 * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * you may not use this file except in compliance with the License.
\r
9 * You may obtain a copy of the License at
\r
11 * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * Unless required by applicable law or agreed to in writing, software
\r
14 * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * See the License for the specific language governing permissions and
\r
17 * limitations under the License.
\r
19 * SPDX-License-Identifier: Apache-2.0
\r
20 * ============LICENSE_END=========================================================
\r
23 package org.onap.policy.api.main.rest.provider;
\r
25 import java.util.ArrayList;
\r
26 import java.util.HashMap;
\r
27 import java.util.List;
\r
28 import java.util.Map;
\r
29 import java.util.Map.Entry;
\r
30 import javax.ws.rs.core.Response;
\r
32 import org.apache.commons.lang3.tuple.Pair;
\r
33 import org.onap.policy.models.base.PfConceptKey;
\r
34 import org.onap.policy.models.base.PfModelException;
\r
35 import org.onap.policy.models.pdp.concepts.PdpGroup;
\r
36 import org.onap.policy.models.pdp.concepts.PdpGroupFilter;
\r
37 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
\r
38 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyFilter;
\r
39 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
\r
40 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
\r
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
\r
44 * Class to provide all kinds of policy operations.
\r
46 * @author Chenfei Gao (cgao@research.att.com)
\r
48 public class PolicyProvider extends CommonModelProvider {
\r
51 * Default constructor.
\r
53 public PolicyProvider() throws PfModelException {
\r
58 * Retrieves a list of policies matching specified ID and version of both policy type and policy.
\r
60 * @param policyTypeId the ID of policy type
\r
61 * @param policyTypeVersion the version of policy type
\r
62 * @param policyId the ID of policy
\r
63 * @param policyVersion the version of policy
\r
65 * @return the ToscaServiceTemplate object
\r
67 * @throws PfModelException the PfModel parsing exception
\r
69 public ToscaServiceTemplate fetchPolicies(String policyTypeId, String policyTypeVersion,
\r
70 String policyId, String policyVersion) throws PfModelException {
\r
72 ToscaServiceTemplate serviceTemplate = getFilteredPolicies(
\r
73 policyTypeId, policyTypeVersion, policyId, policyVersion);
\r
75 if (!hasPolicy(serviceTemplate)) {
\r
76 throw new PfModelException(Response.Status.NOT_FOUND,
\r
77 constructResourceNotFoundMessage(policyTypeId, policyTypeVersion, policyId, policyVersion));
\r
80 return serviceTemplate;
\r
84 * Retrieves a list of policies with the latest versions that match specified policy type id and version.
\r
86 * @param policyTypeId the ID of policy type
\r
87 * @param policyTypeVersion the version of policy type
\r
88 * @param policyId the ID of the policy
\r
90 * @return the ToscaServiceTemplate object
\r
92 * @throws PfModelException the PfModel parsing exception
\r
94 public ToscaServiceTemplate fetchLatestPolicies(String policyTypeId, String policyTypeVersion,
\r
95 String policyId) throws PfModelException {
\r
97 ToscaServiceTemplate serviceTemplate = getFilteredPolicies(
\r
98 policyTypeId, policyTypeVersion, policyId, ToscaPolicyFilter.LATEST_VERSION);
\r
100 if (!hasPolicy(serviceTemplate)) {
\r
101 throw new PfModelException(Response.Status.NOT_FOUND,
\r
102 constructResourceNotFoundMessage(policyTypeId, policyTypeVersion, policyId, null));
\r
105 return serviceTemplate;
\r
109 * Retrieves a list of deployed policies in each pdp group.
\r
111 * @param policyTypeId the ID of policy type
\r
112 * @param policyTypeVersion the version of policy type
\r
113 * @param policyId the ID of the policy
\r
115 * @return a list of deployed policies in each pdp group
\r
117 * @throws PfModelException the PfModel parsing exception
\r
119 public Map<Pair<String, String>, List<ToscaPolicy>> fetchDeployedPolicies(
\r
120 String policyTypeId, String policyTypeVersion, String policyId) throws PfModelException {
\r
122 return collectDeployedPolicies(policyId, new PfConceptKey(policyTypeId, policyTypeVersion),
\r
123 modelsProvider::getPolicyList, List::addAll, new ArrayList<>(5));
\r
127 * Creates one or more new policies for the same policy type ID and version.
\r
129 * @param policyTypeId the ID of policy type
\r
130 * @param policyTypeVersion the version of policy type
\r
131 * @param body the entity body of polic(ies)
\r
133 * @return the ToscaServiceTemplate object
\r
135 * @throws PfModelException the PfModel parsing exception
\r
137 public ToscaServiceTemplate createPolicy(String policyTypeId, String policyTypeVersion,
\r
138 ToscaServiceTemplate body) throws PfModelException {
\r
140 validatePolicyTypeExist(policyTypeId, policyTypeVersion);
\r
141 validatePolicyTypeMatch(policyTypeId, policyTypeVersion, body);
\r
142 validatePolicyVersion(body);
\r
143 return modelsProvider.createPolicies(body);
\r
147 * Creates one or more new policies.
\r
149 * @param body the entity body of policy
\r
151 * @return the ToscaServiceTemplate object
\r
153 * @throws PfModelException the PfModel parsing exception
\r
155 public ToscaServiceTemplate createPolicies(ToscaServiceTemplate body) throws PfModelException {
\r
157 validatePolicyVersion(body);
\r
158 return modelsProvider.createPolicies(body);
\r
162 * Deletes the policy matching specified ID and version of both policy type and policy.
\r
164 * @param policyTypeId the ID of policy type
\r
165 * @param policyTypeVersion the version of policy type
\r
166 * @param policyId the ID of policy
\r
167 * @param policyVersion the version of policy
\r
169 * @return the ToscaServiceTemplate object
\r
171 * @throws PfModelException the PfModel parsing exception
\r
173 public ToscaServiceTemplate deletePolicy(String policyTypeId, String policyTypeVersion,
\r
174 String policyId, String policyVersion) throws PfModelException {
\r
176 validateDeleteEligibility(policyTypeId, policyTypeVersion, policyId, policyVersion);
\r
178 ToscaServiceTemplate serviceTemplate = modelsProvider.deletePolicy(policyId, policyVersion);
\r
180 if (!hasPolicy(serviceTemplate)) {
\r
181 throw new PfModelException(Response.Status.NOT_FOUND,
\r
182 constructResourceNotFoundMessage(policyTypeId, policyTypeVersion, policyId, policyVersion));
\r
185 return serviceTemplate;
\r
189 * Validates whether policy type exists.
\r
191 * @param policyTypeId the ID of policy type
\r
192 * @param policyTypeVersion the version of policy type
\r
194 * @throws PfModelException the PfModel parsing exception
\r
196 private void validatePolicyTypeExist(String policyTypeId, String policyTypeVersion) throws PfModelException {
\r
198 ToscaServiceTemplate serviceTemplate = modelsProvider.getPolicyTypes(policyTypeId, policyTypeVersion);
\r
199 if (!hasPolicyType(serviceTemplate)) {
\r
200 throw new PfModelException(Response.Status.NOT_FOUND,
\r
201 "policy type with ID " + policyTypeId + ":" + policyTypeVersion + " does not exist");
\r
206 * Validates the match between policy type specified in path and the one specified in type of policy.
\r
208 * @param policyTypeId the ID of policy type
\r
209 * @param policyTypeVersion the version of policy type
\r
210 * @param serviceTemplate the ToscaServiceTemplate to validate
\r
212 * @throws PfModelException the PfModel parsing exception
\r
214 private void validatePolicyTypeMatch(String policyTypeId, String policyTypeVersion,
\r
215 ToscaServiceTemplate serviceTemplate) throws PfModelException {
\r
217 List<Map<String, ToscaPolicy>> policies = serviceTemplate.getToscaTopologyTemplate().getPolicies();
\r
218 for (Map<String, ToscaPolicy> policy : policies) {
\r
219 if (policy.size() > 1) {
\r
220 throw new PfModelException(Response.Status.BAD_REQUEST,
\r
221 "one policy block contains more than one policies");
\r
223 ToscaPolicy policyContent = policy.values().iterator().next();
\r
224 if (!policyTypeId.equalsIgnoreCase(policyContent.getType())) {
\r
225 throw new PfModelException(Response.Status.BAD_REQUEST, "policy type id does not match");
\r
227 if (policyContent.getTypeVersion() != null
\r
228 && !policyTypeVersion.equalsIgnoreCase(policyContent.getTypeVersion())) {
\r
229 throw new PfModelException(Response.Status.BAD_REQUEST, "policy type version does not match");
\r
235 * Validates whether specified policy can be deleted based on the rule that deployed policy cannot be deleted.
\r
237 * @param policyTypeId the ID of policy type
\r
238 * @param policyTypeVersion the version of policy type
\r
239 * @param policyId the ID of policy
\r
240 * @param policyVersion the version of policy
\r
242 * @throws PfModelException the PfModel parsing exception
\r
244 private void validateDeleteEligibility(String policyTypeId, String policyTypeVersion,
\r
245 String policyId, String policyVersion) throws PfModelException {
\r
247 List<ToscaPolicyTypeIdentifier> policyTypes = new ArrayList<>(1);
\r
248 policyTypes.add(new ToscaPolicyTypeIdentifier(policyTypeId, policyTypeVersion));
\r
249 List<ToscaPolicyIdentifier> policies = new ArrayList<>(1);
\r
250 policies.add(new ToscaPolicyIdentifier(policyId, policyVersion));
\r
251 PdpGroupFilter pdpGroupFilter = PdpGroupFilter.builder()
\r
252 .policyTypeList(policyTypes).policyList(policies).build();
\r
254 List<PdpGroup> pdpGroups = modelsProvider.getFilteredPdpGroups(pdpGroupFilter);
\r
256 if (!pdpGroups.isEmpty()) {
\r
257 throw new PfModelException(Response.Status.CONFLICT,
\r
258 constructDeletePolicyViolationMessage(policyId, policyVersion, pdpGroups));
\r
263 * Validates the provided policy version in the payload.
\r
265 * @param body the provided TOSCA service template which contains the policies
\r
267 * @throws PfModelException the PfModel parsing exception
\r
269 private void validatePolicyVersion(ToscaServiceTemplate body) throws PfModelException {
\r
271 validatePolicyVersionExist(body);
\r
272 validateNoDuplicateVersionInDb(body);
\r
273 validateNoDuplicateVersionInPl(body);
\r
277 * Validates the existence of at least one policies in the provided payload.
\r
279 * @param body the TOSCA service template payload to check against
\r
281 * @throws PfModelException the PfModel parsing exception
\r
283 private void validatePolicyVersionExist(ToscaServiceTemplate body) throws PfModelException {
\r
285 List<String> invalidPolicyNames = new ArrayList<>(body.getToscaTopologyTemplate().getPolicies().size());
\r
286 for (Map<String, ToscaPolicy> policy : body.getToscaTopologyTemplate().getPolicies()) {
\r
287 ToscaPolicy policyContent = policy.values().iterator().next();
\r
288 if (policyContent.getVersion() == null) {
\r
289 invalidPolicyNames.add(policy.keySet().iterator().next());
\r
293 if (!invalidPolicyNames.isEmpty()) {
\r
294 String errMsg = "mandatory 'version' field is missing in policies: "
\r
295 + String.join(", ", invalidPolicyNames);
\r
296 throw new PfModelException(Response.Status.NOT_ACCEPTABLE, errMsg);
\r
301 * Validates there is no duplicate policy version stored in the database.
\r
303 * @param body the TOSCA service template payload to check against
\r
305 * @throws PfModelException the PfModel parsing exception
\r
307 private void validateNoDuplicateVersionInDb(ToscaServiceTemplate body) throws PfModelException {
\r
309 Map<String, String> invalidPolicies = new HashMap<>();
\r
310 for (Map<String, ToscaPolicy> policy: body.getToscaTopologyTemplate().getPolicies()) {
\r
311 ToscaPolicy policyContent = policy.values().iterator().next();
\r
312 String policyName = policy.keySet().iterator().next();
\r
313 String policyVersion = policyContent.getVersion();
\r
314 String policyTypeName = policyContent.getType();
\r
315 String policyTypeVersion = policyContent.getTypeVersion();
\r
316 ToscaServiceTemplate serviceTemplate =
\r
317 getFilteredPolicies(policyTypeName, policyTypeVersion, policyName, policyVersion);
\r
318 if (hasPolicy(serviceTemplate)) {
\r
319 String latestVersion = getFilteredPolicies(policyTypeName, policyTypeVersion,
\r
320 policyName, ToscaPolicyFilter.LATEST_VERSION).getToscaTopologyTemplate().getPoliciesAsMap()
\r
321 .values().iterator().next().getVersion();
\r
322 invalidPolicies.put(String.join(":", policyName, policyVersion), latestVersion);
\r
326 if (!invalidPolicies.isEmpty()) {
\r
327 List<String> duplicateVersions = new ArrayList<>(5);
\r
328 for (Entry<String, String> invalidPolicy : invalidPolicies.entrySet()) {
\r
329 String eachDuplicateVersion = "policy " + invalidPolicy.getKey()
\r
330 + " already exists; its latest version is " + invalidPolicy.getValue();
\r
331 duplicateVersions.add(eachDuplicateVersion);
\r
333 throw new PfModelException(Response.Status.NOT_ACCEPTABLE, String.join("\n", duplicateVersions));
\r
338 * Validates there is no duplicate policy version in the provided payload if multiple policies are present.
\r
340 * @param body the TOSCA service template payload to check against
\r
342 * @throws PfModelException the PfModel parsing exception
\r
344 private void validateNoDuplicateVersionInPl(ToscaServiceTemplate body) throws PfModelException {
\r
346 List<Map<String, ToscaPolicy>> policies = body.getToscaTopologyTemplate().getPolicies();
\r
347 List<String> duplicateVersions = new ArrayList<>();
\r
348 for (int i = 0; i < policies.size() - 1; i++) {
\r
349 for (int j = i + 1; j < policies.size(); j++) {
\r
350 if (hasSameNameVersion(policies.get(i), policies.get(j))) {
\r
351 String nameVersion = policies.get(i).keySet().iterator().next() + ":"
\r
352 + policies.get(i).values().iterator().next().getVersion();
\r
353 duplicateVersions.add(nameVersion);
\r
358 if (!duplicateVersions.isEmpty()) {
\r
359 String errMsg = "the same version of policies '" + String.join(", ", duplicateVersions)
\r
360 + "' appear multiple times in the payload";
\r
361 throw new PfModelException(Response.Status.NOT_ACCEPTABLE, errMsg);
\r
366 * Checks if two policies have the same name and version.
\r
368 * @param policy1 the first policy
\r
369 * @param policy2 the second policy
\r
371 * @return the boolean flag to indicate the result
\r
373 private boolean hasSameNameVersion(Map<String, ToscaPolicy> policy1, Map<String, ToscaPolicy> policy2) {
\r
375 return (policy1.keySet().iterator().next().equals(policy2.keySet().iterator().next())
\r
376 && policy1.values().iterator().next().getVersion().equals(
\r
377 policy2.values().iterator().next().getVersion()));
\r
381 * Retrieves the specified version of the policy.
\r
383 * @param policyTypeName the name of the policy type
\r
384 * @param policyTypeVersion the version of the policy type
\r
385 * @param policyName the name of the policy
\r
386 * @param policyVersion the version of the policy
\r
388 * @return the TOSCA service template containing the specified version of the policy
\r
390 * @throws PfModelException the PfModel parsing exception
\r
392 private ToscaServiceTemplate getFilteredPolicies(String policyTypeName, String policyTypeVersion,
\r
393 String policyName, String policyVersion) throws PfModelException {
\r
395 ToscaPolicyFilter policyFilter = ToscaPolicyFilter.builder()
\r
396 .name(policyName).version(policyVersion)
\r
397 .type(policyTypeName).typeVersion(policyTypeVersion).build();
\r
398 return modelsProvider.getFilteredPolicies(policyFilter);
\r
402 * Constructs returned message for not found resource.
\r
404 * @param policyTypeId the ID of policy type
\r
405 * @param policyTypeVersion the version of policy type
\r
406 * @param policyId the ID of policy
\r
407 * @param policyVersion the version of policy
\r
409 * @return constructed message
\r
411 private String constructResourceNotFoundMessage(String policyTypeId, String policyTypeVersion,
\r
412 String policyId, String policyVersion) {
\r
414 return "policy with ID " + policyId + ":" + policyVersion
\r
415 + " and type " + policyTypeId + ":" + policyTypeVersion + " does not exist";
\r