diff options
Diffstat (limited to 'applications')
18 files changed, 918 insertions, 96 deletions
diff --git a/applications/common/pom.xml b/applications/common/pom.xml index 1e75e0dc..8f0b9fc3 100644 --- a/applications/common/pom.xml +++ b/applications/common/pom.xml @@ -38,6 +38,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.onap.policy.common</groupId> <artifactId>utils-test</artifactId> <version>${policy.common.version}</version> 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/OnapOperationsHistoryDbao.java new file mode 100644 index 00000000..3075a6bd --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryDbao.java @@ -0,0 +1,82 @@ +/*- + * ============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.pdp.xacml.application.common; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import lombok.Data; + +@Entity +@Table(name = "operationshistory") +@Data +public class OnapOperationsHistoryDbao implements Serializable { + + private static final long serialVersionUID = -551420180714993577L; + + @Id + @GeneratedValue + @Column(name = "id") + private Long id; + + @Column(name = "closedLoopName", length = 255) + private String clName; + + @Column(name = "requestId", length = 50) + private String requestId; + + @Column(name = "subrequestId", length = 50) + private String subrequestId; + + @Column(name = "actor", length = 50) + private String actor; + + @Column(name = "operation", length = 50) + private String operation; + + @Column(name = "target", length = 50) + private String target; + + @Column(name = "starttime") + private Date starttime; + + @Column(name = "outcome", length = 50) + private String outcome; + + @Column(name = "message", length = 255) + private String message; + + @Column(name = "endtime") + private Date endtime; + + public OnapOperationsHistoryDbao() { + super(); + } + +} 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 new file mode 100644 index 00000000..3d316b9a --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngine.java @@ -0,0 +1,353 @@ +/*- + * ============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 0dcafa00..2d3cebd1 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 @@ -93,6 +93,8 @@ public final class ToscaDictionary { public static final Identifier ID_RESOURCE_GUARD_OPERATIONCOUNT = new IdentifierImpl(URN_ONAP, "guard:operation:operation-count"); + public static final String GUARD_ISSUER = URN_ONAP.stringValue() + "xacml:guard:historydb"; + /* * This id is specifically for advice returned from guard */ diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/XacmlPolicyUtils.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/XacmlPolicyUtils.java index 46742af9..30363b43 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/XacmlPolicyUtils.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/XacmlPolicyUtils.java @@ -34,6 +34,7 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.StringJoiner; @@ -162,7 +163,7 @@ public class XacmlPolicyUtils { // int id = 1; while (true) { - String refId = "ref" + id; + String refId = "root" + id; if (rootPolicies.contains(refId)) { id++; } else { @@ -326,6 +327,16 @@ public class XacmlPolicyUtils { try (InputStream is = Files.newInputStream(propertyPath)) { Properties properties = new Properties(); properties.load(is); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Loaded xacml properties {} {}", System.lineSeparator(), properties); + // + // It would be nice to sort this first + // + properties.list(System.out); + for (Entry<Object, Object> entrySet : properties.entrySet()) { + LOGGER.debug("{} -> {}", entrySet.getKey(), entrySet.getValue()); + } + } return properties; } } @@ -336,6 +347,10 @@ public class XacmlPolicyUtils { * @throws IOException If unable to store the file. */ public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Storing xacml properties {} {} {}", properties, System.lineSeparator(), propertyPath); + properties.list(System.out); + } try (OutputStream os = Files.newOutputStream(propertyPath)) { String strComments = "#"; properties.store(os, strComments); 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/OnapOperationsHistoryPipEngineTest.java new file mode 100644 index 00000000..97b034f8 --- /dev/null +++ b/applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngineTest.java @@ -0,0 +1,135 @@ +/*- + * ============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; + +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 javax.persistence.Query; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +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; + + 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 OnapOperationsHistoryPipEngine(); + // + // 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"); + // + // Connect to in-mem db + // + em = Persistence.createEntityManagerFactory(properties.getProperty("historydb.persistenceunit"), properties) + .createEntityManager(); + // + // + // + 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(); + 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"); + + // Use reflection to run getCountFromDB + Method method = OnapOperationsHistoryPipEngine.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(), + 1, "HOUR"); + + // No entries yet + assertEquals(0, count); + + + em.getTransaction().begin(); + em.persist(newEntry); + em.getTransaction().commit(); + + 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(), + 1, "HOUR"); + // Should count 1 entry now + assertEquals(1, count); + } + +} diff --git a/applications/common/src/test/resources/META-INF/persistence.xml b/applications/common/src/test/resources/META-INF/persistence.xml new file mode 100644 index 00000000..09c76c56 --- /dev/null +++ b/applications/common/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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. + ============LICENSE_END========================================================= + --> + +<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> + + <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> + + <properties> + <property name="eclipselink.ddl-generation" value="create-tables" /> + <property name="eclipselink.logging.level" value="FINE" /> + <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" /> + <property name="javax.persistence.jdbc.password" value="P01icY" /> + <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> + <property name="javax.persistence.schema-generation.create-source" value="metadata"/> + </properties> + </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 efe90d82..f326dc2a 100644 --- a/applications/common/src/test/resources/test.properties +++ b/applications/common/src/test/resources/test.properties @@ -30,3 +30,8 @@ refstart1.file=src/test/resources/ref1.xml refstart2.file=src/test/resources/ref2.xml refstart3.file=src/test/resources/ref3.xml refstart4.file=src/test/resources/ref4.xml + +# +# Database persistence for PIP +# +historydb.persistenceunit=PipEngineTest diff --git a/applications/guard/pom.xml b/applications/guard/pom.xml index 6696e55e..ab5873e6 100644 --- a/applications/guard/pom.xml +++ b/applications/guard/pom.xml @@ -38,6 +38,15 @@ <artifactId>common</artifactId> <version>${project.version}</version> </dependency> - </dependencies> + <dependency> + <groupId>org.eclipse.persistence</groupId> + <artifactId>eclipselink</artifactId> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>test</scope> + </dependency> + </dependencies> </project> 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 0b5b5675..7346dded 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 @@ -138,8 +138,8 @@ public class LegacyGuardPolicyRequest { if (guard.containsKey("clname")) { request.clnameId = guard.get("clname").toString(); } - if (guard.containsKey("targets")) { - request.targetId = guard.get("targets").toString(); + if (guard.containsKey("target")) { + request.targetId = guard.get("target").toString(); } if (guard.containsKey("vfCount")) { request.vfCount = Integer.decode(guard.get("vfCount").toString()); 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 81340b4d..48861d86 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 @@ -398,23 +398,16 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { // Now combine into an And // ApplyType applyAnd = new ApplyType(); - applyAnd.setDescription("return true if all the apply's are true."); + applyAnd.setDescription("return true if time range and count checks are true."); applyAnd.setFunctionId(XACML3.ID_FUNCTION_AND.stringValue()); applyAnd.getExpression().add(new ObjectFactory().createApply(timeRange)); applyAnd.getExpression().add(new ObjectFactory().createApply(countCheck)); - // - // And create an outer negation of the And - // - ApplyType applyNot = new ApplyType(); - applyNot.setDescription("Negate the and"); - applyNot.setFunctionId(XACML3.ID_FUNCTION_NOT.stringValue()); - applyNot.getExpression().add(new ObjectFactory().createApply(applyAnd)); // // Create our condition // final ConditionType condition = new ConditionType(); - condition.setExpression(new ObjectFactory().createApply(applyNot)); + condition.setExpression(new ObjectFactory().createApply(applyAnd)); // // Now we can create our rule @@ -617,8 +610,8 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { // // Right now I am faking the count value by re-using the request-id field // - //String issuer = "org:onap:xacml:guard:historydb:tw:" + timeWindow + ":" + timeUnits; - //designator.setIssuer(issuer); + String issuer = ToscaDictionary.GUARD_ISSUER + ":tw:" + timeWindow + ":" + timeUnits; + designator.setIssuer(issuer); AttributeValueType valueLimit = new AttributeValueType(); valueLimit.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue()); @@ -636,13 +629,13 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue()); applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator)); - ApplyType applyGreaterThanEqual = new ApplyType(); - applyGreaterThanEqual.setDescription("return true if current count is greater than or equal."); - applyGreaterThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_GREATER_THAN_OR_EQUAL.stringValue()); - applyGreaterThanEqual.getExpression().add(factory.createApply(applyOneAndOnly)); - applyGreaterThanEqual.getExpression().add(factory.createAttributeValue(valueLimit)); + ApplyType applyLessThan = new ApplyType(); + applyLessThan.setDescription("return true if current count is less than."); + applyLessThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN.stringValue()); + applyLessThan.getExpression().add(factory.createApply(applyOneAndOnly)); + applyLessThan.getExpression().add(factory.createAttributeValue(valueLimit)); - return applyGreaterThanEqual; + return applyLessThan; } private static ApplyType generateMinCheck(Integer min) { @@ -706,13 +699,13 @@ public class LegacyGuardTranslator implements ToscaPolicyTranslator { applyOneAndOnly.setFunctionId(XACML3.ID_FUNCTION_INTEGER_ONE_AND_ONLY.stringValue()); applyOneAndOnly.getExpression().add(factory.createAttributeDesignator(designator)); - ApplyType applyGreaterThanEqual = new ApplyType(); - applyGreaterThanEqual.setDescription("return true if current count is less than or equal."); - applyGreaterThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN_OR_EQUAL.stringValue()); - applyGreaterThanEqual.getExpression().add(factory.createApply(applyOneAndOnly)); - applyGreaterThanEqual.getExpression().add(factory.createAttributeValue(valueLimit)); + ApplyType applyLessThanEqual = new ApplyType(); + applyLessThanEqual.setDescription("return true if current count is less than or equal."); + applyLessThanEqual.setFunctionId(XACML3.ID_FUNCTION_INTEGER_LESS_THAN_OR_EQUAL.stringValue()); + applyLessThanEqual.getExpression().add(factory.createApply(applyOneAndOnly)); + applyLessThanEqual.getExpression().add(factory.createAttributeValue(valueLimit)); - return applyGreaterThanEqual; + return applyLessThanEqual; } private static AdviceExpressionsType generateRequestIdAdvice() { diff --git a/applications/guard/src/main/resources/META-INF/persistence.xml b/applications/guard/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000..8d481a59 --- /dev/null +++ b/applications/guard/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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. + ============LICENSE_END========================================================= + --> + +<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> + + <persistence-unit name="OperationsHistoryPU" transaction-type="RESOURCE_LOCAL"> + <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> + <properties> + <property name="eclipselink.ddl-generation" value="create-tables" /> + <property name="eclipselink.logging.level" value="INFO" /> + </properties> + </persistence-unit> + +</persistence> +
\ No newline at end of file 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 981afee7..0e5d8593 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 @@ -29,6 +29,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.sql.Date; +import java.time.Instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -36,6 +38,11 @@ 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; @@ -47,6 +54,7 @@ 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.OnapOperationsHistoryDbao; import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider; import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; import org.slf4j.Logger; @@ -60,10 +68,13 @@ public class GuardPdpApplicationTest { private static Properties properties = new Properties(); private static File propertiesFile; private static XacmlApplicationServiceProvider service; - private static DecisionRequest requestGuardPermit; - private static DecisionRequest requestGuardDeny; - private static DecisionRequest requestGuardDeny2; + private static DecisionRequest requestVfCount1; + private static DecisionRequest requestVfCount3; + private static DecisionRequest requestVfCount6; private static StandardCoder gson = new StandardCoder(); + private static EntityManager em; + private static final String DENY = "Deny"; + private static final String PERMIT = "Permit"; @ClassRule public static final TemporaryFolder policyFolder = new TemporaryFolder(); @@ -115,30 +126,79 @@ public class GuardPdpApplicationTest { // we just built for it. // service.initialize(propertiesFile.toPath().getParent()); - } - - @Test - public void test1Basics() throws CoderException, IOException { - LOGGER.info("**************** Running test1 ****************"); // - // Load Single Decision Request + // Load Decision Requests // - requestGuardPermit = gson.decode( + requestVfCount1 = gson.decode( + TextFileUtils.getTextFileAsString( + "../../main/src/test/resources/decisions/decision.guard.vfCount.1.input.json"), + DecisionRequest.class); + requestVfCount3 = gson.decode( + TextFileUtils.getTextFileAsString( + "../../main/src/test/resources/decisions/decision.guard.vfCount.3.input.json"), + DecisionRequest.class); + requestVfCount6 = gson.decode( TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.shouldpermit.input.json"), + "../../main/src/test/resources/decisions/decision.guard.vfCount.6.input.json"), DecisionRequest.class); // - // Load Single Decision Request + // Create EntityManager for manipulating DB + // + em = Persistence.createEntityManagerFactory( + GuardPdpApplicationTest.properties.getProperty("historydb.persistenceunit"), properties) + .createEntityManager(); + } + + /** + * Clears the database before each test. + * + */ + @Before + public void startClean() throws Exception { + em.getTransaction().begin(); + em.createQuery("DELETE FROM OnapOperationsHistoryDbao").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 // - requestGuardDeny = gson.decode(TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.shoulddeny.input.json"), - DecisionRequest.class); + DecisionResponse response = service.makeDecision(request); // - // Load Single Decision Request + // Check decision // - requestGuardDeny2 = gson.decode(TextFileUtils.getTextFileAsString( - "../../main/src/test/resources/decisions/decision.guard.shoulddeny.input2.json"), - DecisionRequest.class); + checkDecision(expected, response); + } + + @Test + public void test1Basics() throws CoderException, IOException { + LOGGER.info("**************** Running test1 ****************"); // // Make sure there's an application name // @@ -164,16 +224,9 @@ public class GuardPdpApplicationTest { } @Test - public void test2NoPolicies() { + public void test2NoPolicies() throws CoderException { LOGGER.info("**************** Running test2 ****************"); - // - // Ask for a decision - // - DecisionResponse response = service.makeDecision(requestGuardPermit); - LOGGER.info("Decision {}", response); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo("Permit"); + requestAndCheckDecision(requestVfCount1,PERMIT); } @Test @@ -196,30 +249,25 @@ public class GuardPdpApplicationTest { service.loadPolicies(toscaObject); } // - // Ask for a decision - should get permit + // Zero recent actions: should get permit // - DecisionResponse response = service.makeDecision(requestGuardPermit); - LOGGER.info("Looking for Permit Decision {}", response); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isNotNull(); - assertThat(response.getStatus()).isEqualTo("Permit"); + requestAndCheckDecision(requestVfCount1,PERMIT); // - // Dump it out as Json + // Add entry into operations history DB // - LOGGER.info(gson.encode(response)); + insertOperationEvent(requestVfCount1); // - // Ask for a decision - should get deny + // Only one recent actions: should get permit // - response = service.makeDecision(requestGuardDeny); - LOGGER.info("Looking for Deny Decision {}", response); - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isNotNull(); - assertThat(response.getStatus()).isEqualTo("Deny"); + requestAndCheckDecision(requestVfCount1,PERMIT); // - // Dump it out as Json + // Add entry into operations history DB // - LOGGER.info(gson.encode(response)); + insertOperationEvent(requestVfCount1); + // + // Two recent actions, more than specified limit of 2: should get deny + // + requestAndCheckDecision(requestVfCount1,DENY); } @Test @@ -240,32 +288,32 @@ public class GuardPdpApplicationTest { // Load the policies // service.loadPolicies(toscaObject); - // - // Ask for a decision - should get permit - // } - DecisionResponse response = service.makeDecision(requestGuardPermit); - LOGGER.info("Looking for Permit Decision {}", response); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isNotNull(); - assertThat(response.getStatus()).isEqualTo("Permit"); // - // Dump it out as Json + // vfcount=1 below min of 2: should get a Deny // - LOGGER.info(gson.encode(response)); + requestAndCheckDecision(requestVfCount1, DENY); // - // Ask for a decision - should get deny + // vfcount=3 between min of 2 and max of 5: should get a Permit // - response = service.makeDecision(requestGuardDeny); - LOGGER.info("Looking for Deny Decision {}", response); - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isNotNull(); - assertThat(response.getStatus()).isEqualTo("Deny"); + requestAndCheckDecision(requestVfCount3, PERMIT); // - // Dump it out as Json + // vfcount=6 above max of 5: should get a Deny // - LOGGER.info(gson.encode(response)); + requestAndCheckDecision(requestVfCount6,DENY); + // + // Add two entry into operations history DB + // + insertOperationEvent(requestVfCount1); + insertOperationEvent(requestVfCount1); + // + // vfcount=3 between min of 2 and max of 5, but 2 recent actions is above frequency limit: should get a Deny + // + requestAndCheckDecision(requestVfCount3, DENY); + // + // vfcount=6 above max of 5: should get a Deny + // + requestAndCheckDecision(requestVfCount6, DENY); } @Test @@ -324,4 +372,33 @@ public class GuardPdpApplicationTest { assertThat(response.getStatus()).isEqualTo("Deny"); } } + + @SuppressWarnings("unchecked") + private void insertOperationEvent(DecisionRequest request) { + // + // Get the properties + // + Map<String, Object> properties = (Map<String, Object>) request.getResource().get("guard"); + assertThat(properties).isNotNull(); + // + // Add an entry + // + OnapOperationsHistoryDbao newEntry = new OnapOperationsHistoryDbao(); + newEntry.setActor(properties.get("actor").toString()); + newEntry.setOperation(properties.get("recipe").toString()); + newEntry.setClName(properties.get("clname").toString()); + newEntry.setOutcome("SUCCESS"); + 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(); + } + + @AfterClass + public static void cleanup() throws Exception { + em.close(); + } } diff --git a/applications/guard/src/test/resources/META-INF/createtest.sql b/applications/guard/src/test/resources/META-INF/createtest.sql new file mode 100644 index 00000000..c7389f33 --- /dev/null +++ b/applications/guard/src/test/resources/META-INF/createtest.sql @@ -0,0 +1,16 @@ +# +# Create the operations history table +# +CREATE TABLE `operationshistory` + ( + `id` bigint not null, + `closedLoopName` varchar(255) not null, + `requestId` varchar(50) not null, + `subrequestId` varchar(50) not null, + `actor` varchar(50) not null, + `operation` varchar(50) not null, + `target` varchar(50) not null, + `starttime` timestamp not null, + `outcome` varchar(50) not null, + `message` varchar(255) not null, + `endtime` timestamp not null); diff --git a/applications/guard/src/test/resources/META-INF/persistence.xml b/applications/guard/src/test/resources/META-INF/persistence.xml new file mode 100644 index 00000000..de399c48 --- /dev/null +++ b/applications/guard/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============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. + ============LICENSE_END========================================================= + --> + +<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> + + <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> + <properties> + <property name="eclipselink.ddl-generation" value="create-tables" /> + <property name="eclipselink.logging.level" value="FINE" /> + <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" /> + <property name="javax.persistence.jdbc.password" value="P01icY" /> + <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> + <property name="javax.persistence.schema-generation.create-source" value="script"/> + <property name="javax.persistence.schema-generation.create-script-source" value="META-INF/createtest.sql"/> + </properties> + </persistence-unit> + +</persistence> +
\ No newline at end of file diff --git a/applications/guard/src/test/resources/vDNS.policy.guard.frequency.output.tosca.yaml b/applications/guard/src/test/resources/vDNS.policy.guard.frequency.output.tosca.yaml index a0552d42..fd414e13 100644 --- a/applications/guard/src/test/resources/vDNS.policy.guard.frequency.output.tosca.yaml +++ b/applications/guard/src/test/resources/vDNS.policy.guard.frequency.output.tosca.yaml @@ -7,14 +7,14 @@ topology_template: version: 1.0.0 metadata: policy-id: guard.frequency.scaleout - policy-version: 1 + policy-version: 1 properties: actor: SO recipe: VF Module Create targets: .* clname: ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3 - limit: 1 + limit: 2 timeWindow: 10 timeUnits: minute - guardActiveStart: 00:00:01-05:00 + guardActiveStart: 00:00:00-05:00 guardActiveEnd: 23:59:59-05:00 diff --git a/applications/guard/src/test/resources/vDNS.policy.guard.minmax.output.tosca.yaml b/applications/guard/src/test/resources/vDNS.policy.guard.minmax.output.tosca.yaml index 97282a6f..88d2186e 100644 --- a/applications/guard/src/test/resources/vDNS.policy.guard.minmax.output.tosca.yaml +++ b/applications/guard/src/test/resources/vDNS.policy.guard.minmax.output.tosca.yaml @@ -13,7 +13,7 @@ topology_template: recipe: VF Module Create targets: .* clname: ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3 - min: 1 + min: 2 max: 5 - guardActiveStart: 00:00:01-05:00 + guardActiveStart: 00:00:00-05:00 guardActiveEnd: 23:59:59-05:00 diff --git a/applications/guard/src/test/resources/xacml.properties b/applications/guard/src/test/resources/xacml.properties index 9bd7bfb8..d429a32e 100644 --- a/applications/guard/src/test/resources/xacml.properties +++ b/applications/guard/src/test/resources/xacml.properties @@ -24,6 +24,20 @@ 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:permit-unless-deny +xacml.pip.engines=historydb + +# +# PIP Engine Definition +# +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 + +# +# Database persistence for PIP +# +historydb.persistenceunit=OperationsHistoryPUTest # Policies to load # |