From 0b278005ad98bcd862bd348d08f664005e9eda60 Mon Sep 17 00:00:00 2001 From: Pamela Dragosh Date: Mon, 28 Oct 2019 08:51:10 -0400 Subject: Optimization improvements and test cases * StdBaseTranslator added helpful support methods and can now add obligations to either rule, policy or policy sets. * StdMatchablePolicyRequest improved to support optional policy-type as part of the request to refine the output results. * Added more tests to ensure that the decision is returning the appropriate results. * Added more Javadoc for code. * Added some sonar fix for either log or throw exception. Issue-ID: POLICY-2066 Change-Id: I90d6d90c2cdbb627e96cfce1d2632b2439a1e477 Signed-off-by: Pamela Dragosh --- .../application/common/std/StdBaseTranslator.java | 155 ++++++++++- .../common/std/StdMatchablePolicyRequest.java | 22 +- .../common/std/StdMatchableTranslator.java | 153 ++++++++--- .../OptimizationPdpApplicationTest.java | 282 ++++++++++++++++++++- .../resources/decision.optimization.input.json | 12 + .../vCPE.policies.optimization.input.tosca.yaml | 22 +- .../decision.optimization.affinity.input.json | 12 - 7 files changed, 586 insertions(+), 72 deletions(-) create mode 100644 applications/optimization/src/test/resources/decision.optimization.input.json delete mode 100644 main/src/test/resources/decisions/decision.optimization.affinity.input.json diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java index 48da9969..d2d383b9 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java @@ -28,16 +28,23 @@ import com.att.research.xacml.api.Obligation; import com.att.research.xacml.api.Request; import com.att.research.xacml.api.Response; import com.att.research.xacml.api.Result; +import com.att.research.xacml.api.XACML3; import com.google.gson.Gson; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType; import org.onap.policy.models.decisions.concepts.DecisionRequest; @@ -46,6 +53,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,6 +107,13 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { return decisionResponse; } + /** + * scanObligations - scans the list of obligations and make appropriate method calls to process + * obligations. + * + * @param obligations Collection of obligation objects + * @param decisionResponse DecisionResponse object used to store any results from obligations. + */ protected void scanObligations(Collection obligations, DecisionResponse decisionResponse) { for (Obligation obligation : obligations) { LOGGER.info("Obligation: {}", obligation); @@ -109,6 +124,12 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { } } + /** + * processObligationAttribute - processes an individual obligation attribute assignment object. + * + * @param assignment AttributeAssignment object + * @param decisionResponse DecisionResponse object used to store any results from attribute assignment. + */ @SuppressWarnings("unchecked") protected void processObligationAttribute(AttributeAssignment assignment, DecisionResponse decisionResponse) { // @@ -169,11 +190,21 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { return policy; } - protected RuleType addObligation(RuleType rule, String jsonPolicy) { + /** + * addObligation - general code to add a json policy as an obligation. Probably could just + * return the obligation only instead of adding it directly to a rule/policy/policyset. + * But this is fine for now. + * + * @param RuleType, PolicyType, PolicySetType object + * @param ruleOrPolicy Incoming RuleType, PolicyType, PolicySetType object + * @param jsonPolicy JSON String representation of policy. + * @return Return the Incoming RuleType, PolicyType, PolicySetType object for convenience. + */ + protected T addObligation(T ruleOrPolicy, String jsonPolicy) { // - // Convert the YAML Policy to JSON Object + // Creating obligation for returning policy // - LOGGER.info("JSON Optimization Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); + LOGGER.info("Obligation Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); // // Create an AttributeValue for it // @@ -196,12 +227,124 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue()); obligation.getAttributeAssignmentExpression().add(expressionType); // - // Now we can add it into the rule + // Now we can add it into the rule/policy/policyset // ObligationExpressionsType obligations = new ObligationExpressionsType(); obligations.getObligationExpression().add(obligation); - rule.setObligationExpressions(obligations); - return rule; + if (ruleOrPolicy instanceof RuleType) { + ((RuleType) ruleOrPolicy).setObligationExpressions(obligations); + } else if (ruleOrPolicy instanceof PolicyType) { + ((PolicyType) ruleOrPolicy).setObligationExpressions(obligations); + } else if (ruleOrPolicy instanceof PolicySetType) { + ((PolicySetType) ruleOrPolicy).setObligationExpressions(obligations); + } + // + // Return as a convenience + // + return ruleOrPolicy; + } + + /** + * generateAnyOfForPolicyType - Creates a specific AnyOfType that includes the check + * to match on a specific TOSCA Policy Type. + * + * @param type String represenatation of TOSCA Policy Type (eg. "onap.policies.Foo") + * @return AnyOfType object + */ + protected AnyOfType generateAnyOfForPolicyType(String type) { + // + // Create the match for the policy type + // + MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_STRING_EQUAL, + type, + XACML3.ID_DATATYPE_STRING, + ToscaDictionary.ID_RESOURCE_POLICY_TYPE, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + // + // Add it to an AnyOfType object + // + AnyOfType anyOf = new AnyOfType(); + anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match)); + // + // Return new AnyOfType + // + return anyOf; + } + + /** + * generateConditionForPolicyType - create a ConditionType XACML object + * that is able to determine if a request specifies a specific policy type + * that the policy is created from, only then is the rule applied. If the + * request doesn't even care about the policy type (eg it is missing) then + * return the rule should not apply. + * + * @param type PolicyType (eg. onap.policies.Foo + * @return ConditionType object + */ + protected ConditionType generateConditionForPolicyType(String type) { + // + // Create an ApplyType that checks if the request contains the + // policy-type attribute + // + AttributeDesignatorType designator = new AttributeDesignatorType(); + designator.setAttributeId(ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue()); + designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); + designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue()); + + ApplyType applyBagSize = new ApplyType(); + applyBagSize.setDescription("Get the size of policy-type attributes"); + applyBagSize.setFunctionId(XACML3.ID_FUNCTION_STRING_BAG_SIZE.stringValue()); + + AttributeValueType valueZero = new AttributeValueType(); + valueZero.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue()); + valueZero.getContent().add("0"); // Yes really - represent as a string + + ObjectFactory factory = new ObjectFactory(); + applyBagSize.getExpression().add(factory.createAttributeDesignator(designator)); + + ApplyType applyGreaterThan = new ApplyType(); + applyGreaterThan.setDescription("Does the policy-type attribute exist?"); + applyGreaterThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_EQUAL.stringValue()); + + applyGreaterThan.getExpression().add(factory.createApply(applyBagSize)); + applyGreaterThan.getExpression().add(factory.createAttributeValue(valueZero)); + + // + // Create an apply type that checks the actual value + // + AttributeValueType value = new AttributeValueType(); + value.setDataType(XACML3.ID_DATATYPE_STRING.stringValue()); + value.getContent().add(type); + + // + // Create string-is-in apply - which determines if the policy-type + // is in the request bag of resources for policy-type + // + ApplyType applyIsIn = new ApplyType(); + applyIsIn.setDescription("Is this policy-type in the list?"); + applyIsIn.setFunctionId(XACML3.ID_FUNCTION_STRING_IS_IN.stringValue()); + applyIsIn.getExpression().add(factory.createAttributeValue(value)); + applyIsIn.getExpression().add(factory.createAttributeDesignator(designator)); + + // + // Create our outer apply + // + ApplyType applyOr = new ApplyType(); + applyOr.setDescription("IF exists and is equal"); + applyOr.setFunctionId(XACML3.ID_FUNCTION_OR.stringValue()); + + applyOr.getExpression().add(factory.createApply(applyGreaterThan)); + applyOr.getExpression().add(factory.createApply(applyIsIn)); + + // + // Finally create the condition + // + ConditionType condition = new ConditionType(); + + condition.setExpression(factory.createApply(applyOr)); + + return condition; } } diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java index 0f3a0338..b478e8c1 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java @@ -59,6 +59,8 @@ public class StdMatchablePolicyRequest { private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchablePolicyRequest.class); + public static final String POLICY_TYPE_KEY = "policy-type"; + @XACMLSubject(includeInResults = true) private String onapName; @@ -120,7 +122,7 @@ public class StdMatchablePolicyRequest { try { xacmlRequest = RequestParser.parseRequest(request); } catch (IllegalAccessException | DataTypeException e) { - throw new XacmlApplicationException("Could not parse request " + e.getLocalizedMessage()); + throw new XacmlApplicationException("Could not parse request ", e); } // // Create an object we can add to @@ -133,6 +135,15 @@ public class StdMatchablePolicyRequest { // Map resources = decisionRequest.getResource(); for (Entry entrySet : resources.entrySet()) { + // + // Check for special policy-type + // + String attributeId; + if (POLICY_TYPE_KEY.equals(entrySet.getKey())) { + attributeId = ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue(); + } else { + attributeId = ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey(); + } // // Making an assumption that these fields are matchable. // Its possible we may have to load the policy type model @@ -140,12 +151,12 @@ public class StdMatchablePolicyRequest { // try { if (entrySet.getValue() instanceof Collection) { - addResources(resourceAttributes, (Collection) entrySet.getValue(), entrySet.getKey()); + addResources(resourceAttributes, (Collection) entrySet.getValue(), attributeId); } else { - addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), entrySet.getKey()); + addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), attributeId); } } catch (DataTypeException e) { - throw new XacmlApplicationException("Failed to add resource " + e.getLocalizedMessage()); + throw new XacmlApplicationException("Failed to add resource ", e); } } mutableRequest.add(resourceAttributes); @@ -162,7 +173,7 @@ public class StdMatchablePolicyRequest { for (Object value : values) { StdMutableAttribute mutableAttribute = new StdMutableAttribute(); mutableAttribute.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); - mutableAttribute.setAttributeId(new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + id)); + mutableAttribute.setAttributeId(new IdentifierImpl(id)); mutableAttribute.setIncludeInResults(true); DataType dataTypeExtended = factory.getDataType(XACML3.ID_DATATYPE_STRING); @@ -175,5 +186,4 @@ public class StdMatchablePolicyRequest { } return attributes; } - } diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java index 5908f53b..7b47ad14 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java @@ -109,12 +109,12 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // Get the TOSCA Policy Type for this policy // - Collection policyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier()); + Collection toscaPolicyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier()); // - // If we don't have any policy types, then we cannot know + // If we don't have any TOSCA policy types, then we cannot know // which properties are matchable. // - if (policyTypes.isEmpty()) { + if (toscaPolicyTypes.isEmpty()) { throw new ToscaPolicyConversionException( "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName()); } @@ -134,7 +134,7 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // There should be a metadata section // - this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata()); + fillMetadataSection(newPolicyType, toscaPolicy.getMetadata()); // // Set the combining rule // @@ -142,17 +142,7 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // Generate the TargetType // - newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties(), policyTypes)); - // - // Now create the Permit Rule - // No target since the policy has a target - // With obligations. - // - RuleType rule = new RuleType(); - rule.setDescription("Default is to PERMIT if the policy matches."); - rule.setRuleId(policyName + ":rule"); - rule.setEffect(EffectType.PERMIT); - rule.setTarget(new TargetType()); + newPolicyType.setTarget(new TargetType()); // // Now represent the policy as Json // @@ -163,13 +153,41 @@ public class StdMatchableTranslator extends StdBaseTranslator { } catch (CoderException e) { throw new ToscaPolicyConversionException("Failed to encode policy to json", e); } - addObligation(rule, jsonPolicy); + // + // Add it as an obligation + // + addObligation(newPolicyType, jsonPolicy); + // + // Now create the Permit Rule for all the + // matchable properties. + // + RuleType rule = new RuleType(); + rule.setDescription("Default is to PERMIT if the policy matches."); + rule.setRuleId(policyName + ":rule"); + rule.setEffect(EffectType.PERMIT); + rule.setTarget(generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes)); + rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType())); + // + // Add the rule to the policy + // + newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule); + // + // If a Decision is for a specific policy-type, make sure + // there is a rule for it. + // + rule = new RuleType(); + rule.setDescription("Match on policy-type " + toscaPolicy.getType()); + rule.setRuleId(policyName + ":rule:policy-type"); + rule.setEffect(EffectType.PERMIT); + TargetType target = new TargetType(); + target.getAnyOf().add(this.generateAnyOfForPolicyType(toscaPolicy.getType())); + rule.setTarget(target); // // Add the rule to the policy // newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule); // - // Return our new policy + // Log output of the policy // try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { XACMLPolicyWriter.writePolicyFile(os, newPolicyType); @@ -177,18 +195,20 @@ public class StdMatchableTranslator extends StdBaseTranslator { } catch (IOException e) { LOGGER.error("Failed to create byte array stream", e); } + // + // Done + // return newPolicyType; } /** - * For generating target type, we are scan for matchable properties + * For generating target type, we scan for matchable properties * and use those to build the policy. * * @param properties Properties section of policy * @param policyTypes Collection of policy Type to find matchable metadata * @return TargetType object */ - @SuppressWarnings("unchecked") protected TargetType generateTargetType(Map properties, Collection policyTypes) { TargetType targetType = new TargetType(); // @@ -200,25 +220,21 @@ public class StdMatchableTranslator extends StdBaseTranslator { // if (isMatchable(entrySet.getKey(), policyTypes)) { LOGGER.info("Found matchable property {}", entrySet.getValue()); - if (entrySet.getValue() instanceof Collection) { - AnyOfType anyOf = generateMatches((Collection) entrySet.getValue(), - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey())); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - } - } else { - AnyOfType anyOf = generateMatches(Arrays.asList(entrySet.getValue()), - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey())); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - } - } + generateMatchable(targetType, entrySet.getKey(), entrySet.getValue()); } } return targetType; } + /** + * isMatchable - Iterates through available TOSCA Policy Types to determine if a property + * should be treated as matchable. + * + * @param propertyName Name of property + * @param policyTypes Collection of TOSCA Policy Types to scan + * @return true if matchable + */ protected boolean isMatchable(String propertyName, Collection policyTypes) { for (ToscaPolicyType policyType : policyTypes) { for (Entry propertiesEntry : policyType.getProperties().entrySet()) { @@ -236,6 +252,42 @@ public class StdMatchableTranslator extends StdBaseTranslator { return false; } + /** + * generateMatchable - Given the object, generates list of MatchType objects and add them + * to the TargetType object. + * + * @param targetType TargetType object to add matches to + * @param key Property key + * @param value Object is the value - which can be a Collection or single Object + * @return TargetType incoming TargetType returned as a convenience + */ + @SuppressWarnings("unchecked") + protected TargetType generateMatchable(TargetType targetType, String key, Object value) { + if (value instanceof Collection) { + AnyOfType anyOf = generateMatches((Collection) value, + new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); + if (! anyOf.getAllOf().isEmpty()) { + targetType.getAnyOf().add(anyOf); + } + } else { + AnyOfType anyOf = generateMatches(Arrays.asList(value), + new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); + if (! anyOf.getAllOf().isEmpty()) { + targetType.getAnyOf().add(anyOf); + } + } + return targetType; + } + + /** + * generateMatches - Goes through the collection of objects, creates a MatchType object + * for each object and associates it with the given attribute Id. Returns the AnyOfType + * object that contains all the generated MatchType objects. + * + * @param matchables Collection of object to generate MatchType from + * @param attributeId Given attribute Id for each MatchType + * @return AnyOfType object + */ protected AnyOfType generateMatches(Collection matchables, Identifier attributeId) { // // This is our outer AnyOf - which is an OR @@ -346,11 +398,17 @@ public class StdMatchableTranslator extends StdBaseTranslator { // childPolicyType = parentPolicyType; } - - return listTypes; } + /** + * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or + * then tries to find it either locally on disk or pull it from the Policy + * Lifecycle API the given TOSCA Policy Type. + * + * @param policyTypeId ToscaPolicyTypeIdentifier to find + * @return ToscaPolicyType object. Can be null if failure. + */ private ToscaPolicyType findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) { // // Is it loaded in memory? @@ -368,6 +426,14 @@ public class StdMatchableTranslator extends StdBaseTranslator { return policyType; } + /** + * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local + * storage. If it does not exist, will then attempt to pull from Policy Lifecycle + * API. + * + * @param policyTypeId ToscaPolicyTypeIdentifier input + * @return ToscaPolicyType object. Null if failure. + */ private ToscaPolicyType loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) { // // Construct what the file name should be @@ -392,6 +458,10 @@ public class StdMatchableTranslator extends StdBaseTranslator { // return this.pullPolicyType(policyTypeId, policyTypePath); } + // + // Success - we have read locally the policy type. Now bring it into our + // return object. + // LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath()); try { ToscaServiceTemplate serviceTemplate = standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8), @@ -432,6 +502,14 @@ public class StdMatchableTranslator extends StdBaseTranslator { return null; } + /** + * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API. + * If successful, will store it locally given the policyTypePath. + * + * @param policyTypeId ToscaPolicyTypeIdentifier + * @param policyTypePath Path object to store locally + * @return ToscaPolicyType object. Null if failure. + */ private synchronized ToscaPolicyType pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId, Path policyTypePath) { // // This is what we return @@ -460,6 +538,13 @@ public class StdMatchableTranslator extends StdBaseTranslator { return policyType; } + /** + * constructLocalFilePath - common method to ensure the name of the local file for the + * policy type is the same. + * + * @param policyTypeId ToscaPolicyTypeIdentifier + * @return Path object + */ private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) { return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-" + policyTypeId.getVersion() + ".yaml"); diff --git a/applications/optimization/src/test/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplicationTest.java b/applications/optimization/src/test/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplicationTest.java index b84ec078..be553cf1 100644 --- a/applications/optimization/src/test/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplicationTest.java +++ b/applications/optimization/src/test/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplicationTest.java @@ -32,10 +32,16 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Collection; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.ServiceLoader; +import jersey.repackaged.com.google.common.collect.Lists; import org.apache.commons.lang3.tuple.Pair; +import org.assertj.core.api.Condition; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.FixMethodOrder; @@ -65,7 +71,7 @@ public class OptimizationPdpApplicationTest { private static File propertiesFile; private static XacmlApplicationServiceProvider service; private static StandardCoder gson = new StandardCoder(); - private static DecisionRequest requestAffinity; + private static DecisionRequest baseRequest; private static RestServerParameters clientParams; private static String[] listPolicyTypeFiles = { "onap.policies.Optimization", @@ -95,10 +101,10 @@ public class OptimizationPdpApplicationTest { // // Load Single Decision Request // - requestAffinity = gson.decode( + baseRequest = gson.decode( TextFileUtils .getTextFileAsString( - "../../main/src/test/resources/decisions/decision.optimization.affinity.input.json"), + "src/test/resources/decision.optimization.input.json"), DecisionRequest.class); // // Setup our temporary folder @@ -154,7 +160,7 @@ public class OptimizationPdpApplicationTest { } @Test - public void test1Basics() { + public void test01Basics() { // // Make sure there's an application name // @@ -175,11 +181,11 @@ public class OptimizationPdpApplicationTest { } @Test - public void test2NoPolicies() { + public void test02NoPolicies() { // - // Ask for a decision + // Ask for a decision when there are no policies loaded // - Pair decision = service.makeDecision(requestAffinity, null); + Pair decision = service.makeDecision(baseRequest, null); LOGGER.info("Decision {}", decision.getKey()); assertThat(decision.getKey()).isNotNull(); @@ -187,23 +193,275 @@ public class OptimizationPdpApplicationTest { } @Test - public void test3AddOptimizationPolicies() throws CoderException, FileNotFoundException, IOException, + public void test03OptimizationDefault() throws CoderException, FileNotFoundException, IOException, XacmlApplicationException { // - // Now load the optimization policies + // Now load all the optimization policies // TestUtils.loadPolicies("src/test/resources/vCPE.policies.optimization.input.tosca.yaml", service); // - // Ask for a decision + // Ask for a decision for default // - Pair decision = service.makeDecision(requestAffinity, null); + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(1); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test04OptimizationDefaultGeography() throws CoderException { + // + // Add US to the geography list + // + ((List)baseRequest.getResource().get("geography")).add("US"); + // + // Ask for a decision for default US Policy + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(2); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test05OptimizationDefaultGeographyAndService() throws CoderException { + // + // Add vCPE to the service list + // + ((List)baseRequest.getResource().get("services")).add("vCPE"); + // + // Ask for a decision for default US policy for vCPE service + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(5); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test06OptimizationDefaultGeographyAndServiceAndResource() throws CoderException { + // + // Add vCPE to the service list + // + ((List)baseRequest.getResource().get("resources")).add("vG"); + // + // Ask for a decision for default US service vCPE resource vG policy + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(9); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test07OptimizationGeographyAndServiceAndResourceAndScope() throws CoderException { + // + // Add gold as a scope + // + ((List)baseRequest.getResource().get("scope")).add("gold"); + // + // Ask for a decision for specific US vCPE vG gold + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(10); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test08OptimizationGeographyAndServiceAndResourceAndScopeIsGoldOrPlatinum() throws CoderException { + // + // Add platinum to the scope list: this is now gold OR platinum + // + ((List)baseRequest.getResource().get("scope")).add("platinum"); + // + // Ask for a decision for specific US vCPE vG (gold or platinum) + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(11); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @SuppressWarnings("unchecked") + @Test + public void test09OptimizationGeographyAndServiceAndResourceAndScopeNotGold() throws CoderException { + // + // Add gold as a scope + // + ((List)baseRequest.getResource().get("scope")).remove("gold"); + // + // Ask for a decision for specific US vCPE vG gold + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(11); + // + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + // + // Validate it + // + validateDecision(decision.getKey(), baseRequest); + } + + @Test + public void test10OptimizationPolicyTypeDefault() throws CoderException { + // + // Remove all the other resources from the request + // + cleanOutResources(); + // + // Add in policy type + // + List policyTypes = Lists.newArrayList("onap.policies.optimization.AffinityPolicy"); + baseRequest.getResource().put("policy-type", policyTypes); + // + // Ask for a decision for default + // + Pair decision = service.makeDecision(baseRequest, null); LOGGER.info("Decision {}", decision.getKey()); assertThat(decision.getKey()).isNotNull(); assertThat(decision.getKey().getPolicies().size()).isEqualTo(4); // - // Dump it out as Json + // Double check that the contents are what we expect + // + LOGGER.info(gson.encode(decision.getKey())); + } + + @Test + public void test20OptimizationPolicyTypeDefault() throws CoderException { + // + // Remove all the other resources from the request + // + cleanOutResources(); + // + // Add in policy type + // + List policyTypes = Lists.newArrayList("onap.policies.optimization.HpaPolicy"); + baseRequest.getResource().put("policy-type", policyTypes); + // + // Ask for a decision for default + // + Pair decision = service.makeDecision(baseRequest, null); + LOGGER.info("Decision {}", decision.getKey()); + + assertThat(decision.getKey()).isNotNull(); + assertThat(decision.getKey().getPolicies().size()).isEqualTo(1); + // + // Double check that the contents are what we expect // LOGGER.info(gson.encode(decision.getKey())); } + + @SuppressWarnings("unchecked") + private void validateDecision(DecisionResponse decision, DecisionRequest request) { + for (Entry entrySet : decision.getPolicies().entrySet()) { + LOGGER.info("Decision Returned Policy {}", entrySet.getKey()); + assertThat(entrySet.getValue()).isInstanceOf(Map.class); + Map policyContents = (Map) entrySet.getValue(); + assertThat(policyContents.containsKey("properties")).isTrue(); + assertThat(policyContents.get("properties")).isInstanceOf(Map.class); + Map policyProperties = (Map) policyContents.get("properties"); + + validateMatchable((Collection) request.getResource().get("scope"), + (Collection) policyProperties.get("scope")); + + validateMatchable((Collection) request.getResource().get("services"), + (Collection) policyProperties.get("services")); + + validateMatchable((Collection) request.getResource().get("resources"), + (Collection) policyProperties.get("resources")); + + validateMatchable((Collection) request.getResource().get("geography"), + (Collection) policyProperties.get("geography")); + } + } + + private void validateMatchable(Collection requestList, Collection policyProperties) { + LOGGER.info("Validating matchable: {} with {}", policyProperties, requestList); + // + // Null or empty implies '*' - that is any value is acceptable + // for this policy. + // + if (policyProperties == null || policyProperties.isEmpty()) { + return; + } + Condition condition = new Condition<>( + requestList::contains, + "Request list is contained"); + assertThat(policyProperties).haveAtLeast(1, condition); + + } + + @SuppressWarnings("unchecked") + private void cleanOutResources() { + ((List)baseRequest.getResource().get("scope")).clear(); + ((List)baseRequest.getResource().get("services")).clear(); + ((List)baseRequest.getResource().get("resources")).clear(); + ((List)baseRequest.getResource().get("geography")).clear(); + } } diff --git a/applications/optimization/src/test/resources/decision.optimization.input.json b/applications/optimization/src/test/resources/decision.optimization.input.json new file mode 100644 index 00000000..3872ca90 --- /dev/null +++ b/applications/optimization/src/test/resources/decision.optimization.input.json @@ -0,0 +1,12 @@ +{ + "ONAPName": "OOF", + "ONAPComponent": "OOF-component", + "ONAPInstance": "OOF-component-instance", + "action": "optimize", + "resource": { + "scope": [], + "services": [], + "resources": [], + "geography": [] + } +} \ No newline at end of file diff --git a/applications/optimization/src/test/resources/vCPE.policies.optimization.input.tosca.yaml b/applications/optimization/src/test/resources/vCPE.policies.optimization.input.tosca.yaml index 80888149..919a2f8b 100644 --- a/applications/optimization/src/test/resources/vCPE.policies.optimization.input.tosca.yaml +++ b/applications/optimization/src/test/resources/vCPE.policies.optimization.input.tosca.yaml @@ -9,6 +9,24 @@ topology_template: metadata: policy-id: OSDF_CASABLANCA.Affinity_Default policy-version: 1 + properties: + scope: [] + services: [] + resources: [] + geography: [] + identity: affinity_vCPE + applicableResources: any + affinityProperties: + qualifier: same + category: complex + - + OSDF_CASABLANCA.Affinity_Default_US: + type: onap.policies.optimization.AffinityPolicy + version: 1.0.0 + type_version: 1.0.0 + metadata: + policy-id: OSDF_CASABLANCA.Affinity_Default_US + policy-version: 1 properties: scope: [] services: [] @@ -20,12 +38,12 @@ topology_template: qualifier: same category: complex - - OSDF_CASABLANCA.Affinity_vCPE_0: + OSDF_CASABLANCA.Affinity_Default_vCPE_0: type: onap.policies.optimization.AffinityPolicy version: 1.0.0 type_version: 1.0.0 metadata: - policy-id: OSDF_CASABLANCA.Affinity_vCPE_0 + policy-id: OSDF_CASABLANCA.Affinity_Default_vCPE_0 policy-version: 1 properties: scope: [] diff --git a/main/src/test/resources/decisions/decision.optimization.affinity.input.json b/main/src/test/resources/decisions/decision.optimization.affinity.input.json deleted file mode 100644 index 1bf18fde..00000000 --- a/main/src/test/resources/decisions/decision.optimization.affinity.input.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ONAPName": "OOF", - "ONAPComponent": "OOF-component", - "ONAPInstance": "OOF-component-instance", - "action": "optimize", - "resource": { - "scope": [], - "services": ["vCPE"], - "resources": [], - "geography": ["US"] - } -} \ No newline at end of file -- cgit 1.2.3-korg