Adding guard PIP 60/83460/10
authorPamela Dragosh <pdragosh@research.att.com>
Wed, 27 Mar 2019 10:40:18 +0000 (06:40 -0400)
committerJoshua Reich <jreich@research.att.com>
Mon, 1 Apr 2019 22:35:43 +0000 (15:35 -0700)
Adding the PIP support into the translator to setup the count
attribute as an attribute generated by PIP and not sent by
calling application.

Added JUnit for OperationsHistoryPipEngine.

Issue-ID: POLICY-1471
Change-Id: I71773ee8ea0e6c02873506d973a604899383d4b5
Signed-off-by: Pamela Dragosh <pdragosh@research.att.com>
23 files changed:
applications/common/pom.xml
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryDbao.java [new file with mode: 0644]
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngine.java [new file with mode: 0644]
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/XacmlPolicyUtils.java
applications/common/src/test/java/org/onap/policy/pdp/xacml/application/common/OnapOperationsHistoryPipEngineTest.java [new file with mode: 0644]
applications/common/src/test/resources/META-INF/persistence.xml [new file with mode: 0644]
applications/common/src/test/resources/test.properties
applications/guard/pom.xml
applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardPolicyRequest.java
applications/guard/src/main/java/org/onap/policy/xacml/pdp/application/guard/LegacyGuardTranslator.java
applications/guard/src/main/resources/META-INF/persistence.xml [new file with mode: 0644]
applications/guard/src/test/java/org/onap/policy/xacml/pdp/application/guard/GuardPdpApplicationTest.java
applications/guard/src/test/resources/META-INF/createtest.sql [new file with mode: 0644]
applications/guard/src/test/resources/META-INF/persistence.xml [new file with mode: 0644]
applications/guard/src/test/resources/vDNS.policy.guard.frequency.output.tosca.yaml
applications/guard/src/test/resources/vDNS.policy.guard.minmax.output.tosca.yaml
applications/guard/src/test/resources/xacml.properties
main/src/test/resources/decisions/decision.guard.shoulddeny.input.json
main/src/test/resources/decisions/decision.guard.shouldpermit.input.json
main/src/test/resources/decisions/decision.guard.vfCount.1.input.json [new file with mode: 0644]
main/src/test/resources/decisions/decision.guard.vfCount.3.input.json [new file with mode: 0644]
main/src/test/resources/decisions/decision.guard.vfCount.6.input.json [moved from main/src/test/resources/decisions/decision.guard.shoulddeny.input2.json with 89% similarity]

index 1e75e0d..8f0b9fc 100644 (file)
             <artifactId>junit</artifactId>
             <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>
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 (file)
index 0000000..3075a6b
--- /dev/null
@@ -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 (file)
index 0000000..3d316b9
--- /dev/null
@@ -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;
+    }
+
+}
index 0dcafa0..2d3cebd 100644 (file)
@@ -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
      */
index 46742af..30363b4 100644 (file)
@@ -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 (file)
index 0000000..97b034f
--- /dev/null
@@ -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 (file)
index 0000000..09c76c5
--- /dev/null
@@ -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
index efe90d8..f326dc2 100644 (file)
@@ -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
index 6696e55..ab5873e 100644 (file)
             <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>
index 0b5b567..7346dde 100644 (file)
@@ -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());
index 81340b4..48861d8 100644 (file)
@@ -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 (file)
index 0000000..8d481a5
--- /dev/null
@@ -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
index 981afee..0e5d859 100644 (file)
@@ -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 (file)
index 0000000..c7389f3
--- /dev/null
@@ -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 (file)
index 0000000..de399c4
--- /dev/null
@@ -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
index a0552d4..fd414e1 100644 (file)
@@ -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
index 97282a6..88d2186 100644 (file)
@@ -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
index 9bd7bfb..d429a32 100644 (file)
@@ -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
 #
index 5906382..336d927 100644 (file)
@@ -9,7 +9,8 @@
           "actor": "SO",
           "recipe": "VF Module Create",
           "clname": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
-          "operationCount" : "1"
+          "target": "vLoadBalancer-00",
+          "vfCount" : "6"
       }
   }
 }
\ No newline at end of file
index a80a4cf..75fa00f 100644 (file)
@@ -9,7 +9,7 @@
           "actor": "SO",
           "recipe": "VF Module Create",
           "clname": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
-          "operationCount": "0",
+          "target": "vLoadBalancer-00",
           "vfCount": "1"
       }
   }
diff --git a/main/src/test/resources/decisions/decision.guard.vfCount.1.input.json b/main/src/test/resources/decisions/decision.guard.vfCount.1.input.json
new file mode 100644 (file)
index 0000000..ef1b5f6
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "ONAPName": "Policy",
+  "ONAPComponent": "drools-pdp",
+  "ONAPInstance": "usecase-template",
+  "requestId": "unique-request-id-1",
+  "action": "guard",
+  "resource": {
+      "guard": {
+          "actor": "SO",
+          "recipe": "VF Module Create",
+          "clname": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+          "target": "vLoadBalancer-00",
+          "vfCount": "1"
+      }
+  }
+}
diff --git a/main/src/test/resources/decisions/decision.guard.vfCount.3.input.json b/main/src/test/resources/decisions/decision.guard.vfCount.3.input.json
new file mode 100644 (file)
index 0000000..9faa55d
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "ONAPName": "Policy",
+  "ONAPComponent": "drools-pdp",
+  "ONAPInstance": "usecase-template",
+  "requestId": "unique-request-id-3",
+  "action": "guard",
+  "resource": {
+      "guard": {
+          "actor": "SO",
+          "recipe": "VF Module Create",
+          "clname": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+          "target": "vLoadBalancer-00",
+          "vfCount": "3"
+      }
+  }
+}
@@ -9,7 +9,8 @@
           "actor": "SO",
           "recipe": "VF Module Create",
           "clname": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3",
+          "target": "vLoadBalancer-00",
           "vfCount" : "6"
       }
   }
-}
\ No newline at end of file
+}