diff options
author | Joshua Reich <jreich@research.att.com> | 2019-04-08 09:41:36 -0700 |
---|---|---|
committer | Joshua Reich <jreich@research.att.com> | 2019-04-11 16:50:54 -0700 |
commit | fca3dd7b4bdc33b579750004c9d3bc163d20a2a7 (patch) | |
tree | d82b7935f724cd2370789bb77c3bab036ec85147 | |
parent | 4013653daa38a7fe1b9ffcae02e27d0bc411ac8f (diff) |
Add Control Loop Coordination policy.
Refactor code to support multiple pip engines.
Add pip engine for outcome.
Modify LegacyGuardTranslator to use coordination translator's
convertPolicy function when processing coordination guard.
This version of convertPolicy intentionally uses string
replacement on template-like xacml coordination guard to
enable future support for API-based creation of new coordination
guard types.
Bug fixes and code cleanup.
* Unused imports, sonar problems.
* Consolidation of getAttribute method
* Only use TypedQuery for Pips
Issue-ID: POLICY-1471
Change-Id: I4e9365b7f23bee96cf438dad44feda97c65f6ecc
Signed-off-by: Joshua Reich <jreich@research.att.com>
Signed-off-by: Pamela Dragosh <pdragosh@research.att.com>
31 files changed, 1543 insertions, 467 deletions
diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngine.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngine.java deleted file mode 100644 index 3d316b9a..00000000 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngine.java +++ /dev/null @@ -1,353 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.pdp.xacml.application.common; - -import com.att.research.xacml.api.Attribute; -import com.att.research.xacml.api.AttributeValue; -import com.att.research.xacml.api.Identifier; -import com.att.research.xacml.api.XACML3; -import com.att.research.xacml.api.pip.PIPException; -import com.att.research.xacml.api.pip.PIPFinder; -import com.att.research.xacml.api.pip.PIPRequest; -import com.att.research.xacml.api.pip.PIPResponse; -import com.att.research.xacml.std.StdMutableAttribute; -import com.att.research.xacml.std.datatypes.DataTypes; -import com.att.research.xacml.std.pip.StdMutablePIPResponse; -import com.att.research.xacml.std.pip.StdPIPRequest; -import com.att.research.xacml.std.pip.StdPIPResponse; -import com.att.research.xacml.std.pip.engines.StdConfigurableEngine; -import com.google.common.base.Strings; - -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Properties; - -import javax.persistence.EntityManager; -import javax.persistence.Persistence; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OnapOperationsHistoryPipEngine extends StdConfigurableEngine { - private static Logger logger = LoggerFactory.getLogger(OnapOperationsHistoryPipEngine.class); - - private static final PIPRequest PIP_REQUEST_ACTOR = new StdPIPRequest( - XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, - ToscaDictionary.ID_RESOURCE_GUARD_ACTOR, - XACML3.ID_DATATYPE_STRING); - - private static final PIPRequest PIP_REQUEST_RECIPE = new StdPIPRequest( - XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, - ToscaDictionary.ID_RESOURCE_GUARD_RECIPE, - XACML3.ID_DATATYPE_STRING); - - private static final PIPRequest PIP_REQUEST_TARGET = new StdPIPRequest( - XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, - ToscaDictionary.ID_RESOURCE_GUARD_TARGETID, - XACML3.ID_DATATYPE_STRING); - - private Properties properties; - - public OnapOperationsHistoryPipEngine() { - super(); - } - - @Override - public Collection<PIPRequest> attributesRequired() { - return Arrays.asList(PIP_REQUEST_ACTOR, PIP_REQUEST_RECIPE, PIP_REQUEST_TARGET); - } - - @Override - public Collection<PIPRequest> attributesProvided() { - return Collections.emptyList(); - } - - @Override - public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { - logger.debug("getAttributes requesting attribute {} of type {} for issuer {}", - pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer()); - // - // Determine if the issuer is correct - // - if (Strings.isNullOrEmpty(pipRequest.getIssuer())) { - logger.debug("issuer is null - returning empty response"); - // - // We only respond to ourself as the issuer - // - return StdPIPResponse.PIP_RESPONSE_EMPTY; - } - if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER)) { - logger.debug("Issuer does not start with guard"); - // - // We only respond to ourself as the issuer - // - return StdPIPResponse.PIP_RESPONSE_EMPTY; - } - // - // Parse out the issuer which denotes the time window - // - // Eg: urn:org:onapxacml:guard:historydb:tw:10:minute - // - String[] s1 = pipRequest.getIssuer().split("tw:"); - String[] s2 = s1[1].split(":"); - int timeWindowVal = Integer.parseInt(s2[0]); - String timeWindowScale = s2[1]; - // - // Grab other attribute values - // - String actor = getActor(pipFinder); - String operation = getRecipe(pipFinder); - String target = getTarget(pipFinder); - String timeWindow = timeWindowVal + " " + timeWindowScale; - logger.info("Going to query DB about: actor {} operation {} target {} time window {}", - actor, operation, target, timeWindow); - // - // Sanity check - // - if (actor == null || operation == null || target == null) { - // - // See if we have all the values - // - logger.error("missing attributes return empty"); - return StdPIPResponse.PIP_RESPONSE_EMPTY; - } - // - // Ok do the database query - // - int operationCount = doDatabaseQuery(actor, operation, target, timeWindowVal, timeWindowScale); - // - // Right now return empty - // - StdMutablePIPResponse stdPipResponse = new StdMutablePIPResponse(); - this.addIntegerAttribute(stdPipResponse, - XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, - ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT, - operationCount, - pipRequest); - return new StdPIPResponse(stdPipResponse); - } - - @Override - public void configure(String id, Properties properties) throws PIPException { - super.configure(id, properties); - logger.debug("Configuring historyDb PIP {}", properties); - this.properties = properties; - } - - private String getActor(PIPFinder pipFinder) { - // - // Get the actor value - // - PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_ACTOR, pipFinder); - if (pipResponse == null) { - logger.error("Need actor attribute which is not found"); - return null; - } - // - // Find the actor - // - return findFirstAttributeValue(pipResponse); - } - - private String getRecipe(PIPFinder pipFinder) { - // - // Get the actor value - // - PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_RECIPE, pipFinder); - if (pipResponse == null) { - logger.error("Need recipe attribute which is not found"); - return null; - } - // - // Find the actor - // - return findFirstAttributeValue(pipResponse); - } - - private String getTarget(PIPFinder pipFinder) { - // - // Get the actor value - // - PIPResponse pipResponse = this.getAttribute(PIP_REQUEST_TARGET, pipFinder); - if (pipResponse == null) { - logger.error("Need target attribute which is not found"); - return null; - } - // - // Find the actor - // - return findFirstAttributeValue(pipResponse); - } - - private PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) { - PIPResponse pipResponse = null; - try { - pipResponse = pipFinder.getMatchingAttributes(pipRequest, this); - if (pipResponse.getStatus() != null && !pipResponse.getStatus().isOk()) { - if (logger.isInfoEnabled()) { - logger.info("get attribute error retrieving {}: {}", pipRequest.getAttributeId().stringValue(), - pipResponse.getStatus()); - } - pipResponse = null; - } - if (pipResponse != null && pipResponse.getAttributes().isEmpty()) { - if (logger.isInfoEnabled()) { - logger.info("No value for {}", pipRequest.getAttributeId().stringValue()); - } - pipResponse = null; - } - } catch (PIPException ex) { - logger.error("PIPException getting subject-id attribute: " + ex.getMessage(), ex); - } - return pipResponse; - } - - private String findFirstAttributeValue(PIPResponse pipResponse) { - for (Attribute attribute: pipResponse.getAttributes()) { - Iterator<AttributeValue<String>> iterAttributeValues = attribute.findValues(DataTypes.DT_STRING); - if (iterAttributeValues != null) { - while (iterAttributeValues.hasNext()) { - String value = iterAttributeValues.next().getValue(); - if (value != null) { - return value; - } - } - } - } - return null; - } - - private void addIntegerAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, - Identifier attributeId, int value, PIPRequest pipRequest) { - AttributeValue<BigInteger> attributeValue = null; - try { - attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value); - } catch (Exception e) { - logger.error("Failed to convert {} to integer {}", value, e); - } - if (attributeValue != null) { - stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, - pipRequest.getIssuer(), false)); - } - } - - private int doDatabaseQuery(String actor, String operation, String target, int timeWindowVal, - String timeWindowScale) { - logger.info("Querying operations history for {} {} {} {} {}", - actor, operation, target, timeWindowVal, timeWindowScale); - // - // Create our entity manager - // - EntityManager em; - try { - // - // In case there are any overloaded properties for the JPA - // - Properties emProperties = new Properties(properties); - // - // Create the entity manager factory - // - em = Persistence.createEntityManagerFactory( - properties.getProperty("historydb.persistenceunit", "OperationsHistoryPU"), - emProperties).createEntityManager(); - } catch (Exception e) { - logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); - return -1; - } - // - // Compute the time window - // - if (! "minute".equalsIgnoreCase(timeWindowScale) - && ! "hour".equalsIgnoreCase(timeWindowScale) - && ! "day".equalsIgnoreCase(timeWindowScale) - && ! "week".equalsIgnoreCase(timeWindowScale) - && ! "month".equalsIgnoreCase(timeWindowScale) - && ! "year".equalsIgnoreCase(timeWindowScale)) { - // - // Unsupported - // - logger.error("Unsupported time window scale value {}", timeWindowScale); - // - // Throw an exception instead? - // - return -1; - } - // - // Do the query - // - Object result = null; - try { - // - // - // - String strQuery = "select count(*) as numops from operationshistory" - + " where outcome<>'Failure_Guard'" - + " and actor=?" - + " and operation=?" - + " and target=?" - + " and endtime between TIMESTAMPADD(" - + timeWindowScale.toUpperCase() - + ", ?, CURRENT_TIMESTAMP)" - + " and CURRENT_TIMESTAMP"; - // - // We are expecting a single result - // - result = em.createNativeQuery(strQuery) - .setParameter(1, actor) - .setParameter(2, operation) - .setParameter(3, target) - .setParameter(4, timeWindowVal * -1) - .getSingleResult(); - } catch (Exception e) { - logger.error("Named query failed ", e); - } - // - // Check our query results - // - if (result != null) { - // - // Success let's see what JPA returned to us - // - logger.info("operations query returned {}", result); - // - // Should get back a long - // - if (result instanceof Long) { - return ((Long) result).intValue(); - } - // - // We shouldn't really get this result, but just - // in case we'll do the dirty work of parsing the - // string representation of the object. - // - return Integer.parseInt(result.toString()); - } - // - // We get here if we didn't get a result. Should - // we propagate back an exception? - // - return -1; - } - -} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java index 2d3cebd1..21820b99 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java @@ -31,17 +31,18 @@ public final class ToscaDictionary { /* * These are the ID's for various TOSCA Policy Types we are supporting in the Applications. */ - public static final Identifier URN_ONAP = - new IdentifierImpl("urn:org:onap"); + public static final String URN_ONAP = "urn:org:onap"; + public static final Identifier ID_URN_ONAP = + new IdentifierImpl(URN_ONAP); public static final Identifier ID_RESOURCE_POLICY_ID = XACML3.ID_RESOURCE_RESOURCE_ID; public static final Identifier ID_RESOURCE_POLICY_TYPE = - new IdentifierImpl(URN_ONAP, "policy-type"); + new IdentifierImpl(ID_URN_ONAP, "policy-type"); public static final Identifier ID_RESOURCE_POLICY_TYPE_VERSION = - new IdentifierImpl(URN_ONAP, "policy-type-version"); + new IdentifierImpl(ID_URN_ONAP, "policy-type-version"); /* * These ID's are for identifying Subjects @@ -51,70 +52,75 @@ public final class ToscaDictionary { XACML3.ID_SUBJECT_SUBJECT_ID; public static final Identifier ID_SUBJECT_ONAP_COMPONENT = - new IdentifierImpl(URN_ONAP, "onap-component"); + new IdentifierImpl(ID_URN_ONAP, "onap-component"); public static final Identifier ID_SUBJECT_ONAP_INSTANCE = - new IdentifierImpl(URN_ONAP, "onap-instance"); + new IdentifierImpl(ID_URN_ONAP, "onap-instance"); /* * These 2 ID's are for Optimization policies */ public static final Identifier ID_RESOURCE_POLICY_SCOPE_PROPERTY = - new IdentifierImpl(URN_ONAP, "policy-scope-property"); + new IdentifierImpl(ID_URN_ONAP, "policy-scope-property"); public static final Identifier ID_RESOURCE_POLICY_TYPE_PROPERTY = - new IdentifierImpl(URN_ONAP, "policy-type-property"); + new IdentifierImpl(ID_URN_ONAP, "policy-type-property"); /* * These ID's are for Legacy Guard Policies */ public static final Identifier ID_RESOURCE_GUARD_ACTOR = - new IdentifierImpl(URN_ONAP, "guard:actor:actor-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:actor:actor-id"); public static final Identifier ID_RESOURCE_GUARD_RECIPE = - new IdentifierImpl(URN_ONAP, "guard:operation:operation-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:operation:operation-id"); public static final Identifier ID_RESOURCE_GUARD_CLNAME = - new IdentifierImpl(URN_ONAP, "guard:clname:clname-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:clname:clname-id"); public static final Identifier ID_RESOURCE_GUARD_TARGETID = - new IdentifierImpl(URN_ONAP, "guard:target:target-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:target:target-id"); public static final Identifier ID_SUBJECT_GUARD_REQUESTID = - new IdentifierImpl(URN_ONAP, "guard:request:request-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:request:request-id"); public static final Identifier ID_RESOURCE_GUARD_VFCOUNT = - new IdentifierImpl(URN_ONAP, "guard:target:vf-count"); + new IdentifierImpl(ID_URN_ONAP, "guard:target:vf-count"); public static final Identifier ID_RESOURCE_GUARD_MIN = - new IdentifierImpl(URN_ONAP, "guard:target:min"); + new IdentifierImpl(ID_URN_ONAP, "guard:target:min"); public static final Identifier ID_RESOURCE_GUARD_MAX = - new IdentifierImpl(URN_ONAP, "guard:target:max"); + new IdentifierImpl(ID_URN_ONAP, "guard:target:max"); /* * This id specifically for guard is provided by the * operational history database PIP. */ + public static final String GUARD_OPERATIONCOUNT = "guard:operation:operation-count"; public static final Identifier ID_RESOURCE_GUARD_OPERATIONCOUNT = - new IdentifierImpl(URN_ONAP, "guard:operation:operation-count"); + new IdentifierImpl(ID_URN_ONAP, GUARD_OPERATIONCOUNT); - public static final String GUARD_ISSUER = URN_ONAP.stringValue() + "xacml:guard:historydb"; + public static final String GUARD_OPERATIONOUTCOME = "guard:operation:operation-outcome"; + public static final Identifier ID_RESOURCE_GUARD_OPERATIONOUTCOME = + new IdentifierImpl(ID_URN_ONAP, GUARD_OPERATIONOUTCOME); + + public static final String GUARD_ISSUER_PREFIX = URN_ONAP + ":xacml:guard:"; /* * This id is specifically for advice returned from guard */ public static final Identifier ID_ADVICE_GUARD = - new IdentifierImpl(URN_ONAP, "guard:advice"); + new IdentifierImpl(ID_URN_ONAP, "guard:advice"); public static final Identifier ID_ADVICE_GUARD_REQUESTID = - new IdentifierImpl(URN_ONAP, "guard:advice:request-id"); + new IdentifierImpl(ID_URN_ONAP, "guard:advice:request-id"); /* * Obligation specific ID's */ public static final Identifier ID_OBLIGATION_REST_BODY = - new IdentifierImpl(URN_ONAP, "rest:body"); + new IdentifierImpl(ID_URN_ONAP, "rest:body"); public static final Identifier ID_OBLIGATION_POLICY_MONITORING = - new IdentifierImpl(URN_ONAP, ":obligation:monitoring"); + new IdentifierImpl(ID_URN_ONAP, ":obligation:monitoring"); public static final Identifier ID_OBLIGATION_POLICY_MONITORING_CONTENTS = - new IdentifierImpl(URN_ONAP, ":obligation:monitoring:contents"); + new IdentifierImpl(ID_URN_ONAP, ":obligation:monitoring:contents"); public static final Identifier ID_OBLIGATION_POLICY_MONITORING_CATEGORY = XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE; @@ -123,7 +129,7 @@ public final class ToscaDictionary { XACML3.ID_DATATYPE_STRING; public static final Identifier ID_OBLIGATION_MONITORING_ISSUER = - new IdentifierImpl(URN_ONAP, "issuer:monitoring"); + new IdentifierImpl(ID_URN_ONAP, "issuer:monitoring"); diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/CountRecentOperationsPip.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/CountRecentOperationsPip.java new file mode 100644 index 00000000..c18ad5fa --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/CountRecentOperationsPip.java @@ -0,0 +1,221 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.operationshistory; + +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.pip.StdMutablePIPResponse; +import com.att.research.xacml.std.pip.StdPIPResponse; +import com.google.common.base.Strings; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collection; +import java.util.Properties; + +import javax.persistence.Persistence; + +import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; +import org.onap.policy.pdp.xacml.application.common.std.StdOnapPip; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class CountRecentOperationsPip extends StdOnapPip { + public static final String ISSUER_NAME = "count-recent-operations"; + private static Logger logger = LoggerFactory.getLogger(CountRecentOperationsPip.class); + + public CountRecentOperationsPip() { + super(); + } + + @Override + public Collection<PIPRequest> attributesRequired() { + return Arrays.asList(PIP_REQUEST_ACTOR, PIP_REQUEST_RECIPE, PIP_REQUEST_TARGET); + } + + @Override + public void configure(String id, Properties properties) throws PIPException { + super.configure(id, properties); + // + // Create our entity manager + // + em = null; + try { + // + // In case there are any overloaded properties for the JPA + // + Properties emProperties = new Properties(properties); + // + // Create the entity manager factory + // + em = Persistence.createEntityManagerFactory( + properties.getProperty(ISSUER_NAME + ".persistenceunit"), + emProperties).createEntityManager(); + } catch (Exception e) { + logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); + } + } + + /** + * getAttributes. + * + * @param pipRequest the request + * @param pipFinder the pip finder + * @return PIPResponse + */ + @Override + public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { + logger.debug("getAttributes requesting attribute {} of type {} for issuer {}", + pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer()); + // + // Determine if the issuer is correct + // + if (Strings.isNullOrEmpty(pipRequest.getIssuer())) { + logger.debug("issuer is null - returning empty response"); + // + // We only respond to ourself as the issuer + // + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } + if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER_PREFIX)) { + logger.debug("Issuer does not start with guard"); + // + // We only respond to ourself as the issuer + // + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } + // + // Parse out the issuer which denotes the time window + // Eg: any-prefix:tw:10:minute + // + String[] s1 = pipRequest.getIssuer().split("tw:"); + String[] s2 = s1[1].split(":"); + int timeWindowVal = Integer.parseInt(s2[0]); + String timeWindowScale = s2[1]; + // + // Grab other attribute values + // + String actor = getAttribute(pipFinder, PIP_REQUEST_ACTOR); + String operation = getAttribute(pipFinder, PIP_REQUEST_RECIPE); + String target = getAttribute(pipFinder, PIP_REQUEST_TARGET); + String timeWindow = timeWindowVal + " " + timeWindowScale; + logger.info("Going to query DB about: actor {} operation {} target {} time window {}", + actor, operation, target, timeWindow); + // + // Sanity check + // + if (actor == null || operation == null || target == null) { + // + // See if we have all the values + // + logger.error("missing attributes return empty"); + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } + // + // Ok do the database query + // + long operationCount = doDatabaseQuery(actor, operation, target, timeWindowVal, timeWindowScale); + // + // Create and return PipResponse + // + StdMutablePIPResponse pipResponse = new StdMutablePIPResponse(); + this.addLongAttribute(pipResponse, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, + ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT, + operationCount, + pipRequest); + return new StdPIPResponse(pipResponse); + } + + private long doDatabaseQuery(String actor, String operation, String target, int timeWindowVal, + String timeWindowScale) { + logger.info("Querying operations history for {} {} {} {} {}", + actor, operation, target, timeWindowVal, timeWindowScale); + // + // Only can query if we have an EntityManager + // + if (em == null) { + logger.error("No EntityManager available"); + return -1; + } + // + // Do the query + // + try { + // + // We are expecting a single result + // + return em.createQuery("select count(e) from Dbao e" + + " where e.outcome<>'Failure_Guard'" + + " and e.actor= ?1" + + " and e.operation= ?2" + + " and e.target= ?3" + + " and e.endtime between" + + " ?4 and CURRENT_TIMESTAMP", + Long.class) + .setParameter(1, actor) + .setParameter(2, operation) + .setParameter(3, target) + .setParameter(4, Timestamp.from(Instant.now() + .minus(timeWindowVal, + stringToChronoUnit(timeWindowScale)))) + .getSingleResult(); + } catch (Exception e) { + logger.error("Typed query failed ", e); + return -1; + } + } + + private ChronoUnit stringToChronoUnit(String scale) { + // + // Compute the time window + // + switch (scale.toLowerCase()) { + case "second": + return ChronoUnit.SECONDS; + case "minute": + return ChronoUnit.MINUTES; + case "hour": + return ChronoUnit.HOURS; + case "day": + return ChronoUnit.DAYS; + case "week": + return ChronoUnit.WEEKS; + case "month": + return ChronoUnit.MONTHS; + case "year": + return ChronoUnit.YEARS; + default: + // + // Unsupported + // + logger.error("Unsupported time window scale value {}", scale); + } + return null; + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryDbao.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/Dbao.java index 3075a6bd..2b70c9be 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryDbao.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/Dbao.java @@ -20,7 +20,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.policy.pdp.xacml.application.common; +package org.onap.policy.pdp.xacml.application.common.operationshistory; import java.io.Serializable; import java.util.Date; @@ -36,7 +36,7 @@ import lombok.Data; @Entity @Table(name = "operationshistory") @Data -public class OnapOperationsHistoryDbao implements Serializable { +public class Dbao implements Serializable { private static final long serialVersionUID = -551420180714993577L; @@ -46,7 +46,7 @@ public class OnapOperationsHistoryDbao implements Serializable { private Long id; @Column(name = "closedLoopName", length = 255) - private String clName; + private String closedLoopName; @Column(name = "requestId", length = 50) private String requestId; @@ -75,8 +75,4 @@ public class OnapOperationsHistoryDbao implements Serializable { @Column(name = "endtime") private Date endtime; - public OnapOperationsHistoryDbao() { - super(); - } - } diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePip.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePip.java new file mode 100644 index 00000000..717e537d --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePip.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + + +package org.onap.policy.pdp.xacml.application.common.operationshistory; + +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.pip.StdMutablePIPResponse; +import com.att.research.xacml.std.pip.StdPIPResponse; +import com.google.common.base.Strings; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Properties; + +import javax.persistence.NoResultException; +import javax.persistence.Persistence; + +import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; +import org.onap.policy.pdp.xacml.application.common.std.StdOnapPip; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class GetOperationOutcomePip extends StdOnapPip { + public static final String ISSUER_NAME = "get-operation-outcome"; + private static Logger logger = LoggerFactory.getLogger(GetOperationOutcomePip.class); + + public GetOperationOutcomePip() { + super(); + } + + @Override + public Collection<PIPRequest> attributesRequired() { + return Arrays.asList(PIP_REQUEST_TARGET); + } + + @Override + public void configure(String id, Properties properties) throws PIPException { + super.configure(id, properties); + // + // Create our entity manager + // + em = null; + try { + // + // In case there are any overloaded properties for the JPA + // + Properties emProperties = new Properties(properties); + // + // Create the entity manager factory + // + em = Persistence.createEntityManagerFactory( + properties.getProperty(ISSUER_NAME + ".persistenceunit"), + emProperties).createEntityManager(); + } catch (Exception e) { + logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); + } + } + + /** + * getAttributes. + * + * @param pipRequest the request + * @param pipFinder the pip finder + * @return PIPResponse + */ + @Override + public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { + logger.debug("getAttributes requesting attribute {} of type {} for issuer {}", + pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer()); + // + // Determine if the issuer is correct + // + if (Strings.isNullOrEmpty(pipRequest.getIssuer())) { + logger.debug("issuer is null - returning empty response"); + // + // We only respond to ourself as the issuer + // + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } + if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER_PREFIX)) { + logger.debug("Issuer does not start with guard"); + // + // We only respond to ourself as the issuer + // + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } + // + // Parse out the issuer which denotes the time window + // Eg: any-prefix:clname:some-controlloop-name + // + String[] s1 = pipRequest.getIssuer().split("clname:"); + String clname = s1[1]; + String target = null; + target = getAttribute(pipFinder, PIP_REQUEST_TARGET); + + logger.debug("Going to query DB about: clname={}, target={}", clname, target); + String outcome = doDatabaseQuery(clname, target); + logger.debug("Query result is: {}", outcome); + + StdMutablePIPResponse pipResponse = new StdMutablePIPResponse(); + this.addStringAttribute(pipResponse, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, + ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONOUTCOME, + outcome, + pipRequest); + return new StdPIPResponse(pipResponse); + } + + private String doDatabaseQuery(String clname, String target) { + logger.info("Querying operations history for {} {}", clname, target); + // + // Only can query if we have an EntityManager + // + if (em == null) { + logger.error("No EntityManager available"); + return null; + } + // + // Do the query + // + try { + // + // We are expecting a single result + // + return em.createQuery("select e.outcome from Dbao e" + + " where e.closedLoopName= ?1" + + " and e.target= ?2" + + " order by e.endtime desc", + String.class) + .setParameter(1, clname) + .setParameter(2, target) + .setMaxResults(1) + .getSingleResult(); + } catch (Exception e) { + logger.error("Typed query failed ", e); + return null; + } + } +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdOnapPip.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdOnapPip.java new file mode 100644 index 00000000..70d49418 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdOnapPip.java @@ -0,0 +1,183 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.std; + +import com.att.research.xacml.api.Attribute; +import com.att.research.xacml.api.AttributeValue; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.StdMutableAttribute; +import com.att.research.xacml.std.datatypes.DataTypes; +import com.att.research.xacml.std.pip.StdMutablePIPResponse; +import com.att.research.xacml.std.pip.StdPIPRequest; +import com.att.research.xacml.std.pip.engines.StdConfigurableEngine; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Properties; + +import javax.persistence.EntityManager; + +import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public abstract class StdOnapPip extends StdConfigurableEngine { + protected static Logger logger = LoggerFactory.getLogger(StdOnapPip.class); + + protected static final PIPRequest PIP_REQUEST_ACTOR = new StdPIPRequest( + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, + ToscaDictionary.ID_RESOURCE_GUARD_ACTOR, + XACML3.ID_DATATYPE_STRING); + + protected static final PIPRequest PIP_REQUEST_RECIPE = new StdPIPRequest( + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, + ToscaDictionary.ID_RESOURCE_GUARD_RECIPE, + XACML3.ID_DATATYPE_STRING); + + protected static final PIPRequest PIP_REQUEST_TARGET = new StdPIPRequest( + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, + ToscaDictionary.ID_RESOURCE_GUARD_TARGETID, + XACML3.ID_DATATYPE_STRING); + + protected Properties properties; + protected EntityManager em; + + public StdOnapPip() { + super(); + } + + @Override + public Collection<PIPRequest> attributesProvided() { + return Collections.emptyList(); + } + + @Override + public void configure(String id, Properties properties) throws PIPException { + super.configure(id, properties); + logger.debug("Configuring historyDb PIP {}", properties); + this.properties = properties; + } + + protected String getAttribute(PIPFinder pipFinder, PIPRequest pipRequest) { + // + // Get the actor value + // + PIPResponse pipResponse = this.getAttribute(pipRequest, pipFinder); + if (pipResponse == null) { + logger.error("Need actor attribute which is not found"); + return null; + } + // + // Find the actor + // + return findFirstAttributeValue(pipResponse); + } + + protected PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) { + PIPResponse pipResponse = null; + try { + pipResponse = pipFinder.getMatchingAttributes(pipRequest, this); + if (pipResponse.getStatus() != null && !pipResponse.getStatus().isOk()) { + if (logger.isInfoEnabled()) { + logger.info("get attribute error retrieving {}: {}", pipRequest.getAttributeId().stringValue(), + pipResponse.getStatus()); + } + pipResponse = null; + } + if (pipResponse != null && pipResponse.getAttributes().isEmpty()) { + if (logger.isInfoEnabled()) { + logger.info("No value for {}", pipRequest.getAttributeId().stringValue()); + } + pipResponse = null; + } + } catch (PIPException ex) { + logger.error("PIPException getting subject-id attribute: " + ex.getMessage(), ex); + } + return pipResponse; + } + + protected String findFirstAttributeValue(PIPResponse pipResponse) { + for (Attribute attribute: pipResponse.getAttributes()) { + Iterator<AttributeValue<String>> iterAttributeValues = attribute.findValues(DataTypes.DT_STRING); + if (iterAttributeValues != null) { + while (iterAttributeValues.hasNext()) { + String value = iterAttributeValues.next().getValue(); + if (value != null) { + return value; + } + } + } + } + return null; + } + + protected void addIntegerAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, + Identifier attributeId, int value, PIPRequest pipRequest) { + AttributeValue<BigInteger> attributeValue = null; + try { + attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value); + } catch (Exception e) { + logger.error("Failed to convert {} to integer {}", value, e); + } + if (attributeValue != null) { + stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, + pipRequest.getIssuer(), false)); + } + } + + protected void addLongAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, + Identifier attributeId, long value, PIPRequest pipRequest) { + AttributeValue<BigInteger> attributeValue = null; + try { + attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value); + } catch (Exception e) { + logger.error("Failed to convert {} to long {}", value, e); + } + if (attributeValue != null) { + stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, + pipRequest.getIssuer(), false)); + } + } + + protected void addStringAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, Identifier attributeId, + String value, PIPRequest pipRequest) { + AttributeValue<String> attributeValue = null; + try { + attributeValue = DataTypes.DT_STRING.createAttributeValue(value); + } catch (Exception ex) { + logger.error("Failed to convert {} to an AttributeValue<String>", value, ex); + } + if (attributeValue != null) { + stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, + pipRequest.getIssuer(), false)); + } + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdXacmlApplicationServiceProvider.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdXacmlApplicationServiceProvider.java index 7f85d2f0..5f84154f 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdXacmlApplicationServiceProvider.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdXacmlApplicationServiceProvider.java @@ -129,7 +129,8 @@ public abstract class StdXacmlApplicationServiceProvider implements XacmlApplica // // Convert the policies first // - PolicyType xacmlPolicy = this.getTranslator().convertPolicy(toscaPolicy); + PolicyType xacmlPolicy = this.getTranslator(toscaPolicy.getType()) + .convertPolicy(toscaPolicy); if (xacmlPolicy == null) { throw new ToscaPolicyConversionException("Failed to convert policy"); } @@ -187,8 +188,11 @@ public abstract class StdXacmlApplicationServiceProvider implements XacmlApplica return this.getTranslator().convertResponse(xacmlResponse); } + protected abstract ToscaPolicyTranslator getTranslator(String type); - protected abstract ToscaPolicyTranslator getTranslator(); + protected ToscaPolicyTranslator getTranslator() { + return this.getTranslator(""); + } protected synchronized PDPEngine getEngine() { return this.pdpEngine; @@ -251,7 +255,6 @@ public abstract class StdXacmlApplicationServiceProvider implements XacmlApplica PDPEngine engine = factory.newEngine(properties); if (engine != null) { this.pdpEngine = engine; -// this.pdpProperties = new Properties(properties); } } catch (FactoryException e) { LOGGER.error("Failed to create XACML PDP Engine {}", e); diff --git a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngineTest.java b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/operationshistory/CountRecentOperationsPipTest.java index 97b034f8..10b9d899 100644 --- a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngineTest.java +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/operationshistory/CountRecentOperationsPipTest.java @@ -16,7 +16,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.policy.pdp.xacml.application.common; +package org.onap.policy.pdp.xacml.application.common.operationshistory; import static org.junit.Assert.assertEquals; @@ -24,6 +24,7 @@ import java.io.FileInputStream; import java.lang.reflect.Method; import java.sql.Date; import java.time.Instant; + import java.util.Properties; import java.util.UUID; @@ -37,9 +38,9 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class OnapOperationsHistoryPipEngineTest { - private static final Logger LOGGER = LoggerFactory.getLogger(OnapOperationsHistoryPipEngineTest.class); - private static OnapOperationsHistoryPipEngine pipEngine; +public class CountRecentOperationsPipTest { + private static final Logger LOGGER = LoggerFactory.getLogger(CountRecentOperationsPipTest.class); + private static CountRecentOperationsPip pipEngine; private static EntityManager em; @@ -50,12 +51,12 @@ public class OnapOperationsHistoryPipEngineTest { * @throws Exception connectivity issues */ @BeforeClass - public static void setUp() throws Exception { + public static void setup() throws Exception { LOGGER.info("Setting up PIP Testing"); // // Create instance // - pipEngine = new OnapOperationsHistoryPipEngine(); + pipEngine = new CountRecentOperationsPip(); // // Load our test properties to use // @@ -68,10 +69,13 @@ public class OnapOperationsHistoryPipEngineTest { // pipEngine.configure("issuer", properties); LOGGER.info("PIP configured now creating our entity manager"); + LOGGER.info("properties {}", properties); // // Connect to in-mem db // - em = Persistence.createEntityManagerFactory(properties.getProperty("historydb.persistenceunit"), properties) + String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit"; + LOGGER.info("persistenceunit {}", persistenceUnit); + em = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties) .createEntityManager(); // // @@ -79,57 +83,78 @@ public class OnapOperationsHistoryPipEngineTest { LOGGER.info("Configured own entity manager", em.toString()); } - /** - * Close the entity manager. - */ - @AfterClass - public static void tearDown() { - if (em != null) { - em.close(); - } - } - - @Test - public void testGetCountFromDb() throws Exception { - - // Add an entry - OnapOperationsHistoryDbao newEntry = new OnapOperationsHistoryDbao(); + private Dbao createEntry(String cl, String target, String outcome) { + // + // Create entry + // + Dbao newEntry = new Dbao(); + newEntry.setClosedLoopName(cl); + newEntry.setTarget(target); + newEntry.setOutcome(outcome); newEntry.setActor("Controller"); newEntry.setOperation("operationA"); - newEntry.setClName("cl-foobar-1"); - newEntry.setOutcome("SUCCESS"); newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); newEntry.setEndtime(Date.from(Instant.now())); newEntry.setRequestId(UUID.randomUUID().toString()); - newEntry.setTarget("vnf-1"); + return newEntry; + } + @Test + public void testGetCountFromDb() throws Exception { + // // Use reflection to run getCountFromDB - Method method = OnapOperationsHistoryPipEngine.class.getDeclaredMethod("doDatabaseQuery", + // + Method method = CountRecentOperationsPip.class.getDeclaredMethod("doDatabaseQuery", String.class, String.class, String.class, int.class, String.class); method.setAccessible(true); - int count = (int) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), + // + // create entry + // + Dbao newEntry = createEntry("cl-foobar-1", "vnf-1", "SUCCESS"); + // + // Test pipEngine + // + long count = (long) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), 1, "HOUR"); - + // // No entries yet + // assertEquals(0, count); - - + // + // Add entry + // em.getTransaction().begin(); em.persist(newEntry); em.getTransaction().commit(); - + // + // Directly check ground truth + // Query queryCount = em.createNativeQuery("select count(*) as numops from operationshistory") .setParameter(1, 1); LOGGER.info("{} entries", queryCount.getSingleResult()); - - count = (int) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), + // + // Test pipEngine + // + count = (long) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), 1, "HOUR"); + // // Should count 1 entry now + // assertEquals(1, count); } + /** + * Close the entity manager. + */ + @AfterClass + public static void cleanup() { + if (em != null) { + em.close(); + } + } + } diff --git a/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePipTest.java b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePipTest.java new file mode 100644 index 00000000..037f49a4 --- /dev/null +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/operationshistory/GetOperationOutcomePipTest.java @@ -0,0 +1,156 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common.operationshistory; + +import static org.junit.Assert.assertEquals; + +import java.io.FileInputStream; +import java.lang.reflect.Method; +import java.sql.Date; +import java.time.Instant; +import java.util.Properties; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.Persistence; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GetOperationOutcomePipTest { + private static final Logger LOGGER = LoggerFactory.getLogger(GetOperationOutcomePipTest.class); + private static GetOperationOutcomePip pipEngine; + + private static EntityManager em; + + /** + * Create an instance of our engine and also the persistence + * factory. + * + * @throws Exception connectivity issues + */ + @BeforeClass + public static void setup() throws Exception { + LOGGER.info("Setting up PIP Testing"); + // + // Create instance + // + pipEngine = new GetOperationOutcomePip(); + // + // Load our test properties to use + // + Properties properties = new Properties(); + try (FileInputStream is = new FileInputStream("src/test/resources/test.properties")) { + properties.load(is); + } + // + // Configure it using properties + // + pipEngine.configure("issuer", properties); + LOGGER.info("PIP configured now creating our entity manager"); + LOGGER.info("properties {}", properties); + // + // Connect to in-mem db + // + String persistenceUnit = GetOperationOutcomePip.ISSUER_NAME + ".persistenceunit"; + LOGGER.info("persistenceunit {}", persistenceUnit); + em = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties) + .createEntityManager(); + // + // + // + LOGGER.info("Configured own entity manager", em.toString()); + } + + private void insertEntry(String cl, String target, String outcome) { + // + // Create entry + // + Dbao newEntry = new Dbao(); + newEntry.setClosedLoopName(cl); + newEntry.setTarget(target); + newEntry.setOutcome(outcome); + newEntry.setActor("Controller"); + newEntry.setOperation("operationA"); + newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); + newEntry.setEndtime(Date.from(Instant.now())); + newEntry.setRequestId(UUID.randomUUID().toString()); + // + // Add entry + // + em.getTransaction().begin(); + em.persist(newEntry); + em.getTransaction().commit(); + } + + + @Test + public void testGetOutcomeFromDb() throws Exception { + // + // Use reflection to run getCountFromDB + // + Method method = GetOperationOutcomePip.class.getDeclaredMethod("doDatabaseQuery", + String.class, + String.class); + method.setAccessible(true); + // + // Insert entry + // + insertEntry("testcl1", "testtarget1", "1"); + // + // Test pipEngine + // + String outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget1"); + // + // outcome should be "1" + // + assertEquals("1", outcome); + // + // Insert more entries + // + insertEntry("testcl1", "testtarget1", "2"); + insertEntry("testcl2", "testtarget2", "3"); + insertEntry("testcl1", "testtarget2", "4"); + // + // Test pipEngine + // + outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget1"); + assertEquals("2", outcome); + + outcome = (String) method.invoke(pipEngine, "testcl2", "testtarget2"); + assertEquals("3", outcome); + + outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget2"); + assertEquals("4", outcome); + } + + /** + * Close the entity manager. + */ + @AfterClass + public static void cleanup() { + if (em != null) { + em.close(); + } + } + +} diff --git a/applications/common/src/test/resources/META-INF/persistence.xml b/applications/common/src/test/resources/META-INF/persistence.xml index 09c76c56..65ecc624 100644 --- a/applications/common/src/test/resources/META-INF/persistence.xml +++ b/applications/common/src/test/resources/META-INF/persistence.xml @@ -8,9 +8,9 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,12 +23,13 @@ <persistence-unit name="PipEngineTest" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> - - <class>org.onap.policy.pdp.xacml.application.common.OnapOperationsHistoryDbao</class> - + + <class>org.onap.policy.pdp.xacml.application.common.operationshistory.Dbao</class> + <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.logging.level" value="FINE" /> + <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:testdb;DATABASE_TO_UPPER=FALSE" /> <property name="javax.persistence.jdbc.user" value="policy" /> @@ -39,4 +40,3 @@ </persistence-unit> </persistence> -
\ No newline at end of file diff --git a/applications/common/src/test/resources/test.properties b/applications/common/src/test/resources/test.properties index f326dc2a..fb3d3ce5 100644 --- a/applications/common/src/test/resources/test.properties +++ b/applications/common/src/test/resources/test.properties @@ -20,7 +20,7 @@ xacml.att.functionDefinitionFactory=com.att.research.xacmlatt.pdp.std.StdFunctio xacml.att.policyFinderFactory=org.onap.policy.pdp.xacml.application.common.OnapPolicyFinderFactory # -# +# # xacml.rootPolicies=rootstart rootstart.file=src/test/resources/root.xml @@ -34,4 +34,5 @@ refstart4.file=src/test/resources/ref4.xml # # Database persistence for PIP # -historydb.persistenceunit=PipEngineTest +count-recent-operations.persistenceunit=PipEngineTest +get-operation-outcome.persistenceunit=PipEngineTest diff --git a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationDirective.java b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationDirective.java new file mode 100644 index 00000000..91f8ed2d --- /dev/null +++ b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationDirective.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * guard + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.xacml.pdp.application.guard; + +import java.io.Serializable; +import java.util.List; + +import lombok.Data; + +@Data +public class CoordinationDirective implements Serializable { + private static final long serialVersionUID = 6897293694639777548L; + private List<String> controlLoop; + private String coordinationFunction; + + /** + * gets the ith control loop. + * + * @param index the control loop's index + * @return the CoordinationDirective's string representation + */ + public String getControlLoop(int index) { + return controlLoop.get(index); + } +} diff --git a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationGuardTranslator.java b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationGuardTranslator.java new file mode 100644 index 00000000..c296526b --- /dev/null +++ b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/CoordinationGuardTranslator.java @@ -0,0 +1,166 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.xacml.pdp.application.guard; + +import com.att.research.xacml.api.Request; +import com.att.research.xacml.api.Response; +import com.att.research.xacml.util.XACMLPolicyScanner; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; + +import org.apache.commons.io.IOUtils; +import org.onap.policy.models.decisions.concepts.DecisionRequest; +import org.onap.policy.models.decisions.concepts.DecisionResponse; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +public class CoordinationGuardTranslator implements ToscaPolicyTranslator { + + private static final Logger LOGGER = LoggerFactory.getLogger(CoordinationGuardTranslator.class); + + public CoordinationGuardTranslator() { + super(); + } + + @Override + public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException { + LOGGER.debug("Using CoordinationGuardTranslator.convertPolicy"); + // + // Policy name should be at the root + // + String policyName = toscaPolicy.getMetadata().get("policy-id"); + String type = toscaPolicy.getType(); + String coordinationFunctionPath = "src/main/resources/coordination/function"; + Map<String, Object> policyProps = toscaPolicy.getProperties(); + LOGGER.debug("path = {}", coordinationFunctionPath); + LOGGER.debug("props = {}", policyProps); + List<String> controlLoop = (List<String>) policyProps.get("controlLoop"); + CoordinationDirective cd = new CoordinationDirective(); + cd.setCoordinationFunction(type); + cd.setControlLoop(controlLoop); + LOGGER.debug("CoordinationDirective = {}", cd); + + String xacmlStr = generateXacmlFromCoordinationDirective(cd, coordinationFunctionPath); + + LOGGER.debug("xacmlStr\n{}", xacmlStr); + PolicyType scannedPolicy = null; + try (InputStream is = new ByteArrayInputStream(xacmlStr.getBytes(StandardCharsets.UTF_8))) { + scannedPolicy = (PolicyType) XACMLPolicyScanner.readPolicy(is); + } catch (IOException e) { + LOGGER.error("Failed to read policy", e); + } + return scannedPolicy; + } + + @Override + public Request convertRequest(DecisionRequest request) { + LOGGER.info("this convertRequest shouldn't be used"); + return null; + } + + @Override + public DecisionResponse convertResponse(Response xacmlResponse) { + LOGGER.info("this convertRequest shouldn't be used"); + return null; + } + + /** + * Load YAML coordination directive. + * + * @param directiveFilename yaml directive file to load + * @return the CoordinationDirective + */ + public static CoordinationDirective loadCoordinationDirectiveFromFile(String directiveFilename) { + try (InputStream is = new FileInputStream(new File(directiveFilename))) { + String contents = IOUtils.toString(is, StandardCharsets.UTF_8); + // + // Read the yaml into our Java Object + // + Yaml yaml = new Yaml(new Constructor(CoordinationDirective.class)); + Object obj = yaml.load(contents); + + LOGGER.debug(contents); + + return (CoordinationDirective) obj; + } catch (IOException e) { + LOGGER.error("Error while loading YAML coordination directive", e); + } + return null; + } + + /** + * Generate Xacml rule implementing specified CoordinationDirective. + * + * @param cd the CoordinationDirective + * @param protoDir the directory containing Xacml implementation prototypes + * @return the generated Xacml policy + */ + public static String generateXacmlFromCoordinationDirective(CoordinationDirective cd, + String protoDir) { + /* + * Determine file names + */ + String xacmlProtoFilename = protoDir + File.separator + cd.getCoordinationFunction() + ".xml"; + LOGGER.debug("xacmlProtoFilename={}", xacmlProtoFilename); + /* + * Values to be used for placeholders + */ + final String uniqueId = UUID.randomUUID().toString(); + final String cLOne = cd.getControlLoop(0); + final String cLTwo = cd.getControlLoop(1); + /* + * Replace prototype placeholders with appropriate values + */ + String xacmlPolicy = null; + try (Stream<String> stream = Files.lines(Paths.get(xacmlProtoFilename))) { + xacmlPolicy = stream.map(s -> s.replaceAll("UNIQUE_ID", uniqueId)) + .map(s -> s.replaceAll("CONTROL_LOOP_ONE", cLOne)) + .map(s -> s.replaceAll("CONTROL_LOOP_TWO", cLTwo)) + .collect(Collectors.joining(System.lineSeparator())); + } catch (IOException e) { + LOGGER.error("Error while generating XACML policy for coordination directive", e); + } + return xacmlPolicy; + } + +} diff --git a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplication.java b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplication.java index 0b3b1542..55568c36 100644 --- a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplication.java +++ b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplication.java @@ -43,15 +43,22 @@ public class GuardPdpApplication extends StdXacmlApplicationServiceProvider { private static final Logger LOGGER = LoggerFactory.getLogger(GuardPdpApplication.class); private static final String STRING_VERSION100 = "1.0.0"; private List<ToscaPolicyTypeIdentifier> supportedPolicyTypes = new ArrayList<>(); - private LegacyGuardTranslator translator = new LegacyGuardTranslator(); + private LegacyGuardTranslator legacyTranslator = new LegacyGuardTranslator(); + private CoordinationGuardTranslator coordinationTranslator = new CoordinationGuardTranslator(); + /** Constructor. * */ public GuardPdpApplication() { - this.supportedPolicyTypes.add(new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.FrequencyLimiter", + this.supportedPolicyTypes.add(new ToscaPolicyTypeIdentifier( + "onap.policies.controlloop.guard.FrequencyLimiter", + STRING_VERSION100)); + this.supportedPolicyTypes.add(new ToscaPolicyTypeIdentifier( + "onap.policies.controlloop.guard.MinMax", STRING_VERSION100)); - this.supportedPolicyTypes.add(new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.MinMax", + this.supportedPolicyTypes.add(new ToscaPolicyTypeIdentifier( + "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", STRING_VERSION100)); } @@ -85,7 +92,15 @@ public class GuardPdpApplication extends StdXacmlApplicationServiceProvider { } @Override - protected ToscaPolicyTranslator getTranslator() { - return translator; + protected ToscaPolicyTranslator getTranslator(String type) { + LOGGER.debug("Policy type {}", type); + if ( type.contains("coordination") ) { + LOGGER.debug("returning coordinationTranslator"); + return coordinationTranslator; + } else { + LOGGER.debug("returning legacyTranslator"); + return legacyTranslator; + } } + } diff --git a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardPolicyRequest.java b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardPolicyRequest.java index fa04e6bd..7b6c37a6 100644 --- a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardPolicyRequest.java +++ b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardPolicyRequest.java @@ -34,6 +34,7 @@ import lombok.Setter; import lombok.ToString; import org.onap.policy.models.decisions.concepts.DecisionRequest; +import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; @Getter @Setter @@ -42,6 +43,7 @@ import org.onap.policy.models.decisions.concepts.DecisionRequest; public class LegacyGuardPolicyRequest { private static final String STR_GUARD = "guard"; + private static final String URN_ONAP = ToscaDictionary.URN_ONAP; @XACMLSubject(includeInResults = true) private String onapName; @@ -79,6 +81,7 @@ public class LegacyGuardPolicyRequest { @XACMLResource(includeInResults = true, attributeId = "urn:org:onap:guard:target:max") private Integer max; + public LegacyGuardPolicyRequest() { super(); } diff --git a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardTranslator.java b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardTranslator.java index 77dbb353..28e62b73 100644 --- a/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardTranslator.java +++ b/applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardTranslator.java @@ -57,6 +57,7 @@ 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.operationshistory.CountRecentOperationsPip; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -167,7 +168,6 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { return decisionResponse; } - /** * From the TOSCA metadata section, pull in values that are needed into the XACML policy. * @@ -527,7 +527,9 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { // // Right now I am faking the count value by re-using the request-id field // - String issuer = ToscaDictionary.GUARD_ISSUER + ":tw:" + timeWindow + ":" + timeUnits; + String issuer = ToscaDictionary.GUARD_ISSUER_PREFIX + + CountRecentOperationsPip.ISSUER_NAME + + ":tw:" + timeWindow + ":" + timeUnits; designator.setIssuer(issuer); AttributeValueType valueLimit = new AttributeValueType(); diff --git a/applications/guard/src/main/resources/coordination/function/onap.policies.controlloop.guard.coordination.FirstBlocksSecond.xml b/applications/guard/src/main/resources/coordination/function/onap.policies.controlloop.guard.coordination.FirstBlocksSecond.xml new file mode 100644 index 00000000..bea05f26 --- /dev/null +++ b/applications/guard/src/main/resources/coordination/function/onap.policies.controlloop.guard.coordination.FirstBlocksSecond.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + drools-applications + ================================================================================ + Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ============LICENSE_END========================================================= + --> + +<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" + PolicyId="UNIQUE_ID" Version="1" + RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:permit-unless-deny"> + <Description>Policy for first_blocks_second coordination (if first + running and second requests to run, deny second).</Description> + <Target> + <AnyOf> + <AllOf> + <Match + MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal"> + <AttributeValue + DataType="http://www.w3.org/2001/XMLSchema#string">CONTROL_LOOP_TWO</AttributeValue> + <AttributeDesignator + Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" + AttributeId="urn:org:onap:guard:clname:clname-id" + DataType="http://www.w3.org/2001/XMLSchema#string" + MustBePresent="false" /> + </Match> + </AllOf> + </AnyOf> + </Target> + + <Rule RuleId="UNIQUE_ID:rule:1" Effect="Deny"> + <Description>First Is Running</Description> + <Condition> + <Apply + FunctionId="urn:oasis:names:tc:xacml:3.0:function:string-equal-ignore-case"> + <Apply + FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only"> + <AttributeDesignator + Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" + AttributeId="urn:org:onap:guard:operation:operation-outcome" + DataType="http://www.w3.org/2001/XMLSchema#string" + Issuer="urn:org:onap:xacml:guard:get-operation-outcome:clname:CONTROL_LOOP_ONE" + MustBePresent="false" /> + </Apply> + <AttributeValue + DataType="http://www.w3.org/2001/XMLSchema#string">Success</AttributeValue> + </Apply> + </Condition> + </Rule> +</Policy> diff --git a/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/CoordinationTest.java b/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/CoordinationTest.java new file mode 100644 index 00000000..e94ad712 --- /dev/null +++ b/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/CoordinationTest.java @@ -0,0 +1,298 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.xacml.pdp.application.guard; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.sql.Date; +import java.time.Instant; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.Persistence; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runners.MethodSorters; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.resources.TextFileUtils; +import org.onap.policy.models.decisions.concepts.DecisionRequest; +import org.onap.policy.models.decisions.concepts.DecisionResponse; +import org.onap.policy.pdp.xacml.application.common.TestUtils; +import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException; +import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider; +import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; +import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip; +import org.onap.policy.pdp.xacml.application.common.operationshistory.Dbao; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class CoordinationTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(CoordinationTest.class); + private static Properties properties = new Properties(); + private static File propertiesFile; + private static XacmlApplicationServiceProvider service; + private static DecisionRequest requestCl1Node1; + private static DecisionRequest requestCl1Node2; + private static DecisionRequest requestCl2Node1; + private static DecisionRequest requestCl2Node2; + private static StandardCoder gson = new StandardCoder(); + private static EntityManager em; + private static final String DENY = "Deny"; + private static final String PERMIT = "Permit"; + private static final String OPEN = "Success"; + private static final String CLOSE = "Closed"; + + + @ClassRule + public static final TemporaryFolder policyFolder = new TemporaryFolder(); + + /** + * Copies the xacml.properties and policies files into + * temporary folder and loads the service provider saving + * instance of provider off for other tests to use. + */ + @BeforeClass + public static void setup() throws Exception { + LOGGER.info("Setting up class"); + // + // Setup our temporary folder + // + XacmlPolicyUtils.FileCreator myCreator = (String filename) -> policyFolder.newFile(filename); + propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties", + properties, myCreator); + // + // Load service + // + ServiceLoader<XacmlApplicationServiceProvider> applicationLoader = + ServiceLoader.load(XacmlApplicationServiceProvider.class); + // + // Find the guard service application and save for use in all the tests + // + StringBuilder strDump = new StringBuilder("Loaded applications:" + System.lineSeparator()); + Iterator<XacmlApplicationServiceProvider> iterator = applicationLoader.iterator(); + while (iterator.hasNext()) { + XacmlApplicationServiceProvider application = iterator.next(); + // + // Is it our service? + // + if (application instanceof GuardPdpApplication) { + // + // Should be the first and only one + // + assertThat(service).isNull(); + service = application; + } + strDump.append(application.applicationName()); + strDump.append(" supports "); + strDump.append(application.supportedPolicyTypes()); + strDump.append(System.lineSeparator()); + } + LOGGER.info("{}", strDump); + // + // Tell it to initialize based on the properties file + // we just built for it. + // + service.initialize(propertiesFile.toPath().getParent()); + // + // Load Decision Requests + // + requestCl1Node1 = gson.decode( + TextFileUtils.getTextFileAsString( + "src/test/resources/requests/coordination.cl.1.node.1.json"), + DecisionRequest.class); + requestCl2Node1 = gson.decode( + TextFileUtils.getTextFileAsString( + "src/test/resources/requests/coordination.cl.2.node.1.json"), + DecisionRequest.class); + requestCl1Node2 = gson.decode( + TextFileUtils.getTextFileAsString( + "src/test/resources/requests/coordination.cl.1.node.2.json"), + DecisionRequest.class); + requestCl2Node2 = gson.decode( + TextFileUtils.getTextFileAsString( + "src/test/resources/requests/coordination.cl.2.node.2.json"), + DecisionRequest.class); + // + // Create EntityManager for manipulating DB + // + String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit"; + em = Persistence.createEntityManagerFactory( + CoordinationTest.properties.getProperty(persistenceUnit), properties) + .createEntityManager(); + } + + /** + * Clears the database before each test. + * + */ + @Before + public void startClean() throws Exception { + em.getTransaction().begin(); + em.createQuery("DELETE FROM Dbao").executeUpdate(); + em.getTransaction().commit(); + } + + /** + * Check that decision matches expectation. + * + * @param expected from the response + * @param response received + * + **/ + public void checkDecision(String expected, DecisionResponse response) throws CoderException { + LOGGER.info("Looking for {} Decision", expected); + assertThat(response).isNotNull(); + assertThat(response.getStatus()).isNotNull(); + assertThat(response.getStatus()).isEqualTo(expected); + // + // Dump it out as Json + // + LOGGER.info(gson.encode(response)); + } + + /** + * Request a decision and check that it matches expectation. + * + * @param request to send to Xacml PDP + * @param expected from the response + * + **/ + public void requestAndCheckDecision(DecisionRequest request, String expected) throws CoderException { + // + // Ask for a decision + // + DecisionResponse response = service.makeDecision(request); + // + // Check decision + // + checkDecision(expected, response); + } + + @Test + public void test1() throws CoderException, IOException, XacmlApplicationException { + LOGGER.info("**************** Running test1 ****************"); + // + // Now load the test coordination policy - make sure + // the pdp can support it and have it load + // into the PDP. + // + TestUtils.loadPolicies("src/test/resources/test.policy.guard.coordination.firstBlocksSecond.tosca.yaml", + service); + // + // cl1 doesn't have open action: cl2 should get permit + // + requestAndCheckDecision(requestCl2Node1, PERMIT); + // + // Open cl2 on node1 + // + insertOperationEvent(requestCl2Node1, OPEN); + // + // Under current coordination policy cl1 always can go + // + requestAndCheckDecision(requestCl1Node1, PERMIT); + // + // Open cl1 on node1 + // + insertOperationEvent(requestCl1Node1, OPEN); + // + // Close cl2 on node1 + // + insertOperationEvent(requestCl2Node1, CLOSE); + // + // Try cl2 again, cl1 has open action on node1: should get deny + // + requestAndCheckDecision(requestCl2Node1, DENY); + // + // Close cl1 on node1 + // + insertOperationEvent(requestCl1Node1, CLOSE); + // + // Under current coordination policy cl1 always can go + // + requestAndCheckDecision(requestCl1Node1, PERMIT); + // + // Open cl1 on node1 + // + insertOperationEvent(requestCl1Node1, OPEN); + // + // Try cl2 on node2, cl1 only open on node1: should get permit + // + requestAndCheckDecision(requestCl2Node2, PERMIT); + // + // Open cl2 on node2 + // + insertOperationEvent(requestCl2Node2, OPEN); + // + // Try cl2 on node1, cl1 open on node1: should get DENY + // + requestAndCheckDecision(requestCl2Node1, DENY); + } + + @SuppressWarnings("unchecked") + private void insertOperationEvent(DecisionRequest request, String outcome) { + // + // Get the properties + // + Map<String, Object> properties = (Map<String, Object>) request.getResource().get("guard"); + // + // Add an entry + // + Dbao newEntry = new Dbao(); + newEntry.setActor(properties.get("actor").toString()); + newEntry.setOperation(properties.get("recipe").toString()); + newEntry.setClosedLoopName(properties.get("clname").toString()); + newEntry.setOutcome(outcome); + newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); + newEntry.setEndtime(Date.from(Instant.now())); + newEntry.setRequestId(UUID.randomUUID().toString()); + newEntry.setTarget(properties.get("target").toString()); + em.getTransaction().begin(); + em.persist(newEntry); + em.getTransaction().commit(); + } + + /** + * Close the entity manager. + */ + @AfterClass + public static void cleanup() throws Exception { + if (em != null) { + em.close(); + } + } +} diff --git a/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplicationTest.java b/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplicationTest.java index 17917af0..c5cf0327 100644 --- a/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplicationTest.java +++ b/applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplicationTest.java @@ -53,11 +53,12 @@ import org.onap.policy.common.utils.resources.TextFileUtils; import org.onap.policy.models.decisions.concepts.DecisionRequest; import org.onap.policy.models.decisions.concepts.DecisionResponse; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier; -import org.onap.policy.pdp.xacml.application.common.OnapOperationsHistoryDbao; import org.onap.policy.pdp.xacml.application.common.TestUtils; import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException; import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider; import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; +import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip; +import org.onap.policy.pdp.xacml.application.common.operationshistory.Dbao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,7 +86,7 @@ public class GuardPdpApplicationTest { * instance of provider off for other tests to use. */ @BeforeClass - public static void setUp() throws Exception { + public static void setup() throws Exception { LOGGER.info("Setting up class"); // // Setup our temporary folder @@ -131,21 +132,22 @@ public class GuardPdpApplicationTest { // requestVfCount1 = gson.decode( TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.vfCount.1.input.json"), + "src/test/resources/requests/guard.vfCount.1.json"), DecisionRequest.class); requestVfCount3 = gson.decode( TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.vfCount.3.input.json"), + "src/test/resources/requests/guard.vfCount.3.json"), DecisionRequest.class); requestVfCount6 = gson.decode( TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.vfCount.6.input.json"), + "src/test/resources/requests/guard.vfCount.6.json"), DecisionRequest.class); // // Create EntityManager for manipulating DB // + String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit"; em = Persistence.createEntityManagerFactory( - GuardPdpApplicationTest.properties.getProperty("historydb.persistenceunit"), properties) + GuardPdpApplicationTest.properties.getProperty(persistenceUnit), properties) .createEntityManager(); } @@ -156,7 +158,7 @@ public class GuardPdpApplicationTest { @Before public void startClean() throws Exception { em.getTransaction().begin(); - em.createQuery("DELETE FROM OnapOperationsHistoryDbao").executeUpdate(); + em.createQuery("DELETE FROM Dbao").executeUpdate(); em.getTransaction().commit(); } @@ -213,7 +215,7 @@ public class GuardPdpApplicationTest { // can support the correct policy types. // assertThat(service.supportedPolicyTypes()).isNotEmpty(); - assertThat(service.supportedPolicyTypes().size()).isEqualTo(2); + assertThat(service.supportedPolicyTypes().size()).isEqualTo(3); assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier( "onap.policies.controlloop.guard.FrequencyLimiter", "1.0.0"))).isTrue(); assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier( @@ -222,6 +224,10 @@ public class GuardPdpApplicationTest { "onap.policies.controlloop.guard.MinMax", "1.0.0"))).isTrue(); assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier( "onap.policies.controlloop.guard.MinMax", "1.0.1"))).isFalse(); + assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier( + "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.0"))).isTrue(); + assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier( + "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.1"))).isFalse(); assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier("onap.foo", "1.0.1"))).isFalse(); } @@ -357,10 +363,10 @@ public class GuardPdpApplicationTest { // // Add an entry // - OnapOperationsHistoryDbao newEntry = new OnapOperationsHistoryDbao(); + Dbao newEntry = new Dbao(); newEntry.setActor(properties.get("actor").toString()); newEntry.setOperation(properties.get("recipe").toString()); - newEntry.setClName(properties.get("clname").toString()); + newEntry.setClosedLoopName(properties.get("clname").toString()); newEntry.setOutcome("SUCCESS"); newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); newEntry.setEndtime(Date.from(Instant.now())); @@ -372,8 +378,14 @@ public class GuardPdpApplicationTest { em.getTransaction().commit(); } + /** + * Close the entity manager. + */ @AfterClass public static void cleanup() throws Exception { - em.close(); + if (em != null) { + em.close(); + } } + } diff --git a/applications/guard/src/test/resources/META-INF/persistence.xml b/applications/guard/src/test/resources/META-INF/persistence.xml index de399c48..8d1e08ad 100644 --- a/applications/guard/src/test/resources/META-INF/persistence.xml +++ b/applications/guard/src/test/resources/META-INF/persistence.xml @@ -8,9 +8,9 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,10 +23,11 @@ <persistence-unit name="OperationsHistoryPUTest" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> - <class>org.onap.policy.pdp.xacml.application.common.OnapOperationsHistoryDbao</class> + <class>org.onap.policy.pdp.xacml.application.common.operationshistory.Dbao</class> <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.logging.level" value="FINE" /> + <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:testdb;DATABASE_TO_UPPER=FALSE" /> <property name="javax.persistence.jdbc.user" value="policy" /> @@ -38,4 +39,3 @@ </persistence-unit> </persistence> -
\ No newline at end of file diff --git a/applications/guard/src/test/resources/requests/coordination.cl.1.node.1.json b/applications/guard/src/test/resources/requests/coordination.cl.1.node.1.json new file mode 100644 index 00000000..dd461005 --- /dev/null +++ b/applications/guard/src/test/resources/requests/coordination.cl.1.node.1.json @@ -0,0 +1,15 @@ +{ + "ONAPName": "Policy", + "ONAPComponent": "drools-pdp", + "ONAPInstance": "usecase-template", + "requestId": "unique-request-id-47", + "action": "guard", + "resource": { + "guard": { + "actor": "SDNR", + "recipe": "Tilt", + "clname": "cl1", + "target": "node1" + } + } +} diff --git a/applications/guard/src/test/resources/requests/coordination.cl.1.node.2.json b/applications/guard/src/test/resources/requests/coordination.cl.1.node.2.json new file mode 100644 index 00000000..dbd3a53a --- /dev/null +++ b/applications/guard/src/test/resources/requests/coordination.cl.1.node.2.json @@ -0,0 +1,15 @@ +{ + "ONAPName": "Policy", + "ONAPComponent": "drools-pdp", + "ONAPInstance": "usecase-template", + "requestId": "unique-request-id-4712", + "action": "guard", + "resource": { + "guard": { + "actor": "SDNR", + "recipe": "Tilt", + "clname": "cl1", + "target": "node2" + } + } +} diff --git a/applications/guard/src/test/resources/requests/coordination.cl.2.node.1.json b/applications/guard/src/test/resources/requests/coordination.cl.2.node.1.json new file mode 100644 index 00000000..edccc45b --- /dev/null +++ b/applications/guard/src/test/resources/requests/coordination.cl.2.node.1.json @@ -0,0 +1,15 @@ +{ + "ONAPName": "Policy", + "ONAPComponent": "drools-pdp", + "ONAPInstance": "usecase-template", + "requestId": "unique-request-id-6877", + "action": "guard", + "resource": { + "guard": { + "actor": "SDNR", + "recipe": "Tilt", + "clname": "cl2", + "target": "node1" + } + } +} diff --git a/applications/guard/src/test/resources/requests/coordination.cl.2.node.2.json b/applications/guard/src/test/resources/requests/coordination.cl.2.node.2.json new file mode 100644 index 00000000..c1401f22 --- /dev/null +++ b/applications/guard/src/test/resources/requests/coordination.cl.2.node.2.json @@ -0,0 +1,16 @@ + +{ + "ONAPName": "Policy", + "ONAPComponent": "drools-pdp", + "ONAPInstance": "usecase-template", + "requestId": "unique-request-id-6837", + "action": "guard", + "resource": { + "guard": { + "actor": "SDNR", + "recipe": "Tilt", + "clname": "cl2", + "target": "node2" + } + } +} diff --git a/main/src/test/resources/decisions/decision.guard.vfCount.1.input.json b/applications/guard/src/test/resources/requests/guard.vfCount.1.json index ef1b5f65..ef1b5f65 100644 --- a/main/src/test/resources/decisions/decision.guard.vfCount.1.input.json +++ b/applications/guard/src/test/resources/requests/guard.vfCount.1.json diff --git a/main/src/test/resources/decisions/decision.guard.vfCount.3.input.json b/applications/guard/src/test/resources/requests/guard.vfCount.3.json index 9faa55d6..9faa55d6 100644 --- a/main/src/test/resources/decisions/decision.guard.vfCount.3.input.json +++ b/applications/guard/src/test/resources/requests/guard.vfCount.3.json diff --git a/main/src/test/resources/decisions/decision.guard.vfCount.6.input.json b/applications/guard/src/test/resources/requests/guard.vfCount.6.json index e00d67ae..e00d67ae 100644 --- a/main/src/test/resources/decisions/decision.guard.vfCount.6.input.json +++ b/applications/guard/src/test/resources/requests/guard.vfCount.6.json diff --git a/applications/guard/src/test/resources/test.policy.guard.coordination.firstBlocksSecond.tosca.yaml b/applications/guard/src/test/resources/test.policy.guard.coordination.firstBlocksSecond.tosca.yaml new file mode 100644 index 00000000..649820b0 --- /dev/null +++ b/applications/guard/src/test/resources/test.policy.guard.coordination.firstBlocksSecond.tosca.yaml @@ -0,0 +1,14 @@ +tosca_definitions_version: tosca_simple_yaml_1_0_0 +topology_template: + policies: + - + guard.coordination.firstBlocksSecond.test: + type: onap.policies.controlloop.guard.coordination.FirstBlocksSecond + version: 1.0.0 + metadata: + policy-id : guard.coordination.firstBlocksSecond.test + policy-version: 1 + properties: + controlLoop: + - cl1 + - cl2 diff --git a/applications/guard/src/test/resources/xacml.properties b/applications/guard/src/test/resources/xacml.properties index 25dee375..534c538c 100644 --- a/applications/guard/src/test/resources/xacml.properties +++ b/applications/guard/src/test/resources/xacml.properties @@ -24,23 +24,22 @@ xacml.att.policyFinderFactory=org.onap.policy.pdp.xacml.application.common.OnapP # xacml.att.policyFinderFactory.combineRootPolicies=urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-overrides -xacml.pip.engines=historydb - # -# PIP Engine Definition +# PIP Engine Definitions # -historydb.classname=org.onap.policy.pdp.xacml.application.common.OnapOperationsHistoryPipEngine -historydb.issuer=urn:org:onap:xacml:guard:historydb -historydb.name=operationHistoryDB -historydb.description=Returns operation counts based on time window +count-recent-operations.classname=org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip +count-recent-operations.issuer=urn:org:onap:xacml:guard:count-recent-operations +count-recent-operations.name=CountRecentOperations +count-recent-operations.description=Returns operation counts based on time window +count-recent-operations.persistenceunit=OperationsHistoryPUTest -# -# Database persistence for PIP -# -historydb.persistenceunit=OperationsHistoryPUTest +get-operation-outcome.classname=org.onap.policy.pdp.xacml.application.common.operationshistory.GetOperationOutcomePip +get-operation-outcome.issuer=urn:org:onap:xacml:guard:get-operation-outcome +get-operation-outcome.name=GetOperationOutcome +get-operation-outcome.description=Returns operation outcome +get-operation-outcome.persistenceunit=OperationsHistoryPUTest -# Policies to load # -#xacml.rootPolicies=guard -#guard.file=src/main/resources/RootGuardPolicy.xml - +# Make pips available to finder +# +xacml.pip.engines=count-recent-operations,get-operation-outcome
\ No newline at end of file diff --git a/applications/monitoring/src/main/java/org/onap/policy/xacml/pdp/application/monitoring/MonitoringPdpApplication.java b/applications/monitoring/src/main/java/org/onap/policy/xacml/pdp/application/monitoring/MonitoringPdpApplication.java index 0661b869..bcdfd987 100644 --- a/applications/monitoring/src/main/java/org/onap/policy/xacml/pdp/application/monitoring/MonitoringPdpApplication.java +++ b/applications/monitoring/src/main/java/org/onap/policy/xacml/pdp/application/monitoring/MonitoringPdpApplication.java @@ -88,7 +88,8 @@ public class MonitoringPdpApplication extends StdXacmlApplicationServiceProvider } @Override - protected ToscaPolicyTranslator getTranslator() { + protected ToscaPolicyTranslator getTranslator(String type) { return translator; } + } diff --git a/applications/optimization/src/main/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplication.java b/applications/optimization/src/main/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplication.java index 4bb1da65..337b40d7 100644 --- a/applications/optimization/src/main/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplication.java +++ b/applications/optimization/src/main/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplication.java @@ -100,7 +100,8 @@ public class OptimizationPdpApplication extends StdXacmlApplicationServiceProvid } @Override - protected ToscaPolicyTranslator getTranslator() { + protected ToscaPolicyTranslator getTranslator(String type) { return translator; } + } |