summaryrefslogtreecommitdiffstats
path: root/controlloop/m2/guard
diff options
context:
space:
mode:
Diffstat (limited to 'controlloop/m2/guard')
-rw-r--r--controlloop/m2/guard/pom.xml84
-rw-r--r--controlloop/m2/guard/src/main/java/org/onap/policy/guard/GuardContext.java400
-rw-r--r--controlloop/m2/guard/src/test/java/org/onap/policy/guard/GuardContextTest.java139
3 files changed, 623 insertions, 0 deletions
diff --git a/controlloop/m2/guard/pom.xml b/controlloop/m2/guard/pom.xml
new file mode 100644
index 000000000..501151a85
--- /dev/null
+++ b/controlloop/m2/guard/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!--
+ ============LICENSE_START=======================================================
+ ONAP Policy Engine - Drools PDP
+ ================================================================================
+ Copyright (C) 2020 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=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId>
+ <artifactId>m2</artifactId>
+ <version>1.6.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>guard</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.13.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId>
+ <artifactId>util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onap.policy.drools-pdp</groupId>
+ <artifactId>policy-core</artifactId>
+ <version>${version.policy.drools-pdp}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onap.policy.drools-pdp</groupId>
+ <artifactId>policy-management</artifactId>
+ <version>${version.policy.drools-pdp}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>guard</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>database</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/controlloop/m2/guard/src/main/java/org/onap/policy/guard/GuardContext.java b/controlloop/m2/guard/src/main/java/org/onap/policy/guard/GuardContext.java
new file mode 100644
index 000000000..d0d1b831f
--- /dev/null
+++ b/controlloop/m2/guard/src/main/java/org/onap/policy/guard/GuardContext.java
@@ -0,0 +1,400 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2020 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.guard;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+import org.drools.core.WorkingMemory;
+
+import org.onap.policy.database.operationshistory.Dbao;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.core.PolicyContainer;
+import org.onap.policy.drools.core.PolicySession;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyControllerConstants;
+import org.onap.policy.drools.system.PolicyEngineConstants;
+import org.onap.policy.guard.Util;
+import org.onap.policy.util.DroolsSessionCommonSerializable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Each instance of this class is initialized from a 'Properties' object,
+ * which is most likely a '*-controller.properties' file. The expectation is
+ * that it will be initialized within a '.drl' file, and be referenced by
+ * the associated 'Context' object.
+ */
+public class GuardContext implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private static final String ECLIPSE_LINK_KEY_DRIVER = "javax.persistence.jdbc.driver";
+
+ private static Logger logger = LoggerFactory.getLogger(GuardContext.class);
+
+ // object that should be serialized
+ private Object namedSerializable;
+
+ /*==================================*/
+ /* fields extracted from properties */
+ /*==================================*/
+ // contains the four database properties, 'javax.persistence.jdbc.*',
+ private Properties dbProperties = null;
+
+ // initialized from 'guard.disabled', but may also be set to 'true' if
+ // there is an initialization error
+ private boolean disabled = false;
+
+ // errors that forced 'disabled' to be set to 'true'
+ private String errorMessage = null;
+
+ /*======================================================*/
+ /* fields that shouldn't be included in serialized data */
+ /*======================================================*/
+
+ // derived from DB properties
+ private transient EntityManagerFactory emf = null;
+
+ /**
+ * Constructor - initialize the 'GuardContext' instance using the
+ * controller's properties file. The properties file is located using a
+ * 'PolicySession' instance, but the way this mapping is done isn't
+ * perfect -- it may use the wrong properties file if there is another
+ * 'PolicyContainer' instance using the same 'artifactId' and 'groupId'.
+ *
+ * @param session the 'PolicySession' instance used to locate the associated
+ * 'Properties' instance
+ */
+ public GuardContext(PolicySession session) {
+ this(session, null);
+ }
+
+ /**
+ * Constructor - initialize the 'GuardContext' instance using the
+ * controller's properties file. The properties file is located using a
+ * 'PolicySession' instance, but the way this mapping is done isn't
+ * perfect -- it may use the wrong properties file if there is another
+ * 'PolicyContainer' instance using the same 'artifactId' and 'groupId'.
+ *
+ * @param session the 'PolicySession' instance used to locate the associated
+ * 'Properties' instance
+ * @param serializableName a String name unique within the Drools session
+ * that can be used to locate the corresponding 'GuardContext' object
+ * on the remote host
+ */
+ public GuardContext(PolicySession session, String serializableName) {
+ namedSerializable =
+ (serializableName == null ? this :
+ new DroolsSessionCommonSerializable(serializableName, this));
+
+ // At present, there is no simple way to get the properties based
+ // upon a 'PolicyContainer'. Instead, we search through all of the
+ // 'PolicyController' instances looking for one with a matching
+ // 'artifactId' and 'groupId'. Note that this may not work correctly
+ // if there is more than one controller using the same or different
+ // version of the same artifact.
+
+ PolicyContainer container = session.getPolicyContainer();
+ String artifactId = container.getArtifactId();
+ String groupId = container.getGroupId();
+
+ Properties properties =
+ PolicyControllerConstants.getFactory().get(groupId, artifactId).getProperties();
+ init(properties);
+ }
+
+ /**
+ * Constructor - initialize the 'GuardContext' instance using the
+ * specified properties.
+ *
+ * @param properties configuration data used to initialize the 'GuardContext' instance
+ */
+ public GuardContext(Properties properties) {
+ init(properties);
+ }
+
+ /**
+ * Common initialization routine code used by both constructors.
+ *
+ * @param properties configuration data used to initialize the 'GuardContext' instance
+ */
+ private void init(Properties properties) {
+ // used to store error messages
+ StringBuilder sb = new StringBuilder();
+
+ // fetch these parameters, if they exist
+ String disabledString =
+ PolicyEngineConstants.getManager().getEnvironmentProperty(Util.PROP_GUARD_DISABLED);
+
+ if (disabledString != null) {
+ // decode optional 'guard.disabled' parameter
+ disabled = Boolean.valueOf(disabledString);
+ if (disabled) {
+ // skip everything else
+ return;
+ }
+ }
+
+ // extract 'guard.java.persistence.jdbc.*' parameters,
+ // which are all mandatory
+ dbProperties = new Properties();
+ setProperty(dbProperties, Util.ONAP_KEY_URL, Util.ECLIPSE_LINK_KEY_URL, sb);
+ setProperty(dbProperties, Util.ONAP_KEY_USER, Util.ECLIPSE_LINK_KEY_USER, sb);
+ setProperty(dbProperties, Util.ONAP_KEY_PASS, Util.ECLIPSE_LINK_KEY_PASS, sb);
+ String driver = properties.getProperty("guard." + ECLIPSE_LINK_KEY_DRIVER);
+ if (driver != null) {
+ dbProperties.setProperty(ECLIPSE_LINK_KEY_DRIVER, driver);
+ }
+
+ // if there are any errors, update 'errorMessage' & disable guard queries
+ if (sb.length() != 0) {
+ // remove the terminating ", ", and extract resulting error message
+ sb.setLength(sb.length() - 2);
+ errorMessage = sb.toString();
+ disabled = true;
+ logger.error("Initialization failure: " + errorMessage);
+ }
+ }
+
+ /**
+ * Fetch a property from the PolicyEngine environment, and store it in
+ * a corresponding property in 'properties'.
+ *
+ * @param properties the location to store the properties
+ * @param srcName source environment property name
+ * @param destName destination property name
+ * @param log a 'StringBuilder' used to construct an error message, if needed
+ */
+ private void setProperty(Properties properties, String srcName, String destName, StringBuilder log) {
+ String value =
+ PolicyEngineConstants.getManager().getEnvironmentProperty(srcName);
+ if (value == null) {
+ log.append("'").append(srcName).append("' is not defined, ");
+ } else {
+ properties.setProperty(destName, value);
+ }
+ }
+
+ /**
+ * Do an asynchronous (non-blocking) HTTP REST query to see if this
+ * operation is permitted by 'guard'. The response is returned by
+ * inserting a 'PolicyGuardResponse' instance into Drools memory.
+ *
+ * @param workingMemory the Drools response is inserted here
+ * @param actor the processor being acted upon (e.g. "APPC")
+ * @param recipe otherwise known as "operation" (e.g. "Restart")
+ * @param target a further qualifier on 'actor'? (e.g. "VM")
+ * @param requestId the UUID string identifying the overall request
+ */
+ public void asyncQuery(
+ WorkingMemory workingMemory,
+ String actor, String recipe, String target,
+ String requestId) {
+
+ asyncQuery(workingMemory, actor, recipe, target, requestId, null);
+ }
+
+ /**
+ * Do an asynchronous (non-blocking) HTTP REST query to see if this
+ * operation is permitted by 'guard'. The response is returned by
+ * inserting a 'PolicyGuardResponse' instance into Drools memory.
+ *
+ * @param workingMemory the Drools response is inserted here
+ * @param actor the processor being acted upon (e.g. "APPC")
+ * @param recipe otherwise known as "operation" (e.g. "Restart")
+ * @param target a further qualifier on 'actor'? (e.g. "VM")
+ * @param requestId the UUID string identifying the overall request
+ * @param controlLoopName the 'controlLoopName' value or 'null'
+ * (if 'null', it is ommitted from the query to 'guard')
+ */
+ public void asyncQuery(
+ final WorkingMemory workingMemory,
+ final String actor, final String recipe, final String target,
+ final String requestId, final String controlLoopName) {
+
+ if (disabled) {
+ logger.error("query skipped: {}", errorMessage);
+ workingMemory.insert(
+ new PolicyGuardResponse("Deny", UUID.fromString(requestId), recipe));
+ return;
+ }
+
+ CallGuardTask cgt = new CallGuardTask(workingMemory, controlLoopName,
+ actor, recipe, target, requestId, () -> null);
+
+ PolicyEngineConstants.getManager().getExecutorService().execute(cgt);
+ }
+
+ /**
+ * Create an 'EntityManagerFactory', if needed, and then create a new
+ * 'EntityManager' instance.
+ *
+ * @return a new 'EntityManager' instance
+ */
+ private EntityManager createEntityManager() {
+ if (emf == null) {
+ // 'EntityManagerFactory' does not exist yet -- create one
+
+ // copy database properties to a 'HashMap'
+ HashMap<Object,Object> propertiesMap = new HashMap<>(dbProperties);
+
+ // use 'ClassLoader' from Drools session
+ propertiesMap.put("eclipselink.classloader",
+ GuardContext.class.getClassLoader());
+
+ // create DB tables, if needed
+ propertiesMap.put("eclipselink.ddl-generation", "create-tables");
+
+ // create entity manager factory
+ emf = Persistence.createEntityManagerFactory("OperationsHistoryPU", propertiesMap);
+ }
+
+ // create and return the 'EntityManager'
+ return emf.createEntityManager();
+ }
+
+ /**
+ * This is a synchronous (blocking) method, which creates a database entity
+ * for an in-progress request.
+ *
+ * @param starttime this is used as the 'starttime' timestamp in the record
+ * @param endtime this is used as the 'endtime' timestamp in the record
+ * @param closedLoopControlName uniquely identifies the Drools rules
+ * @param actor the processor being acted upon (e.g. "APPC")
+ * @param recipe otherwise known as "operation" (e.g. "Restart")
+ * @param target a further qualifier on 'actor'? (e.g. "VM")
+ * @param requestId the UUID string identifying the overall request
+ * @param subRequestId further qualifier on 'requestId'
+ * @param message indicates success status, or reason for failure
+ * @param outcome 'PolicyResult' enumeration string
+ * @return 'true' if the operation was successful, and 'false' if not
+ */
+ public boolean createDbEntry(
+ Instant starttime, Instant endtime, String closedLoopControlName,
+ String actor, String recipe, String target,
+ String requestId, String subRequestId, String message, String outcome) {
+
+ if (disabled) {
+ if (errorMessage != null) {
+ logger.error("Database update skipped: " + errorMessage);
+ }
+ return false;
+ }
+
+ EntityManager em = null;
+ boolean rval = false;
+
+ try {
+ em = createEntityManager();
+
+ // create the new DB table entry
+ Dbao newEntry = new Dbao();
+
+ // populate the new DB table entry
+ newEntry.setClosedLoopName(closedLoopControlName);
+ newEntry.setRequestId(requestId);
+ newEntry.setActor(actor);
+ newEntry.setOperation(recipe);
+ newEntry.setTarget(target);
+ newEntry.setStarttime(new Timestamp(starttime.toEpochMilli()));
+ newEntry.setSubrequestId(subRequestId);
+
+ newEntry.setEndtime(new Timestamp(endtime.toEpochMilli()));
+ newEntry.setMessage(message);
+ newEntry.setOutcome(outcome);
+
+ // store the new entry in the DB
+ em.getTransaction().begin();
+ em.persist(newEntry);
+ em.getTransaction().commit();
+
+ rval = true;
+ } finally {
+ // free EntityManager
+ if (em != null) {
+ em.close();
+ }
+ }
+ return rval;
+ }
+
+ /**
+ * This is an asynchronous (non-blocking) method, which creates a database
+ * entity for an in-progress request.
+ *
+ * @param starttime this is used as the 'starttime' timestamp in the record
+ * @param endtime this is used as the 'endtime' timestamp in the record
+ * @param closedLoopControlName uniquely identifies the Drools rules
+ * @param actor the processor being acted upon (e.g. "APPC")
+ * @param recipe otherwise known as "operation" (e.g. "Restart")
+ * @param target a further qualifier on 'actor'? (e.g. "VM")
+ * @param requestId the UUID string identifying the overall request
+ * @param subRequestId further qualifier on 'requestId'
+ * @param message indicates success status, or reason for failure
+ * @param outcome 'PolicyResult' enumeration string
+ */
+ public void asyncCreateDbEntry(
+ final Instant starttime, final Instant endtime,
+ final String closedLoopControlName,
+ final String actor, final String recipe, final String target,
+ final String requestId, final String subRequestId,
+ final String message, final String outcome) {
+ if (disabled) {
+ if (errorMessage != null) {
+ logger.error("Database update skipped: " + errorMessage);
+ }
+ return;
+ }
+
+ PolicyEngineConstants.getManager().getExecutorService().execute(() -> {
+ try {
+ // using a separate thread, call the synchronous 'createDbEntry'
+ // method
+ createDbEntry(starttime, endtime, closedLoopControlName,
+ actor, recipe, target, requestId, subRequestId,
+ message, outcome);
+ } catch (Exception e) {
+ logger.error("GuardContext.asyncCreateDbEntry", e);
+ }
+ });
+ }
+
+ /**
+ * This method is used as part of serialization -- 'namedSerializable'
+ * is serialized instead of 'this'.
+ *
+ * @return the object to be serialized
+ */
+ private Object writeReplace() throws ObjectStreamException {
+ return namedSerializable;
+ }
+}
diff --git a/controlloop/m2/guard/src/test/java/org/onap/policy/guard/GuardContextTest.java b/controlloop/m2/guard/src/test/java/org/onap/policy/guard/GuardContextTest.java
new file mode 100644
index 000000000..1a61d9019
--- /dev/null
+++ b/controlloop/m2/guard/src/test/java/org/onap/policy/guard/GuardContextTest.java
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2020 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.guard;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.drools.core.WorkingMemory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.onap.policy.drools.system.PolicyEngineConstants;
+
+public class GuardContextTest {
+
+ private static Properties prop;
+ private static GuardContext guardContext;
+ private static WorkingMemory workingMemory;
+ private static LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<>();
+
+ /**
+ * Class-level initialization.
+ */
+ @BeforeClass
+ public static void setup() throws IOException {
+ PolicyEngineConstants.getManager().configure(new Properties());
+ PolicyEngineConstants.getManager().start();
+
+ prop = new Properties();
+ prop.setProperty("guard.pdp.rest.url", "http://www.google.com/");
+ prop.setProperty("guard.pdp.rest.client.user", "testuser");
+ prop.setProperty("guard.pdp.rest.client.password", "testpassword");
+ prop.setProperty("guard.pdp.rest.timeout", "1000");
+ prop.setProperty("guard.pdp.rest.environment", "dev");
+
+ workingMemory = mock(WorkingMemory.class);
+ when(workingMemory.insert(isNotNull())).thenAnswer(
+ invocation -> {
+ queue.add(invocation.getArgument(0));
+ return null;
+ });
+ }
+
+ @AfterClass
+ public static void stop() {
+ PolicyEngineConstants.getManager().stop();
+ }
+
+ @Test
+ public void guardDbResponseTest() throws InterruptedException {
+ Properties props = new Properties(prop);
+ props.setProperty("guard.disabled", "false");
+ props.setProperty("guard.javax.persistence.jdbc.user", "user");
+ props.setProperty("guard.javax.persistence.jdbc.password", "secret");
+ props.setProperty("guard.javax.persistence.jdbc.driver", "org.h2.Driver");
+ props.setProperty("guard.javax.persistence.jdbc.url", "jdbc:h2:file:./H2DB");
+
+ guardContext = new GuardContext(props);
+ assertNotNull(guardContext);
+
+ guardContext.asyncCreateDbEntry(Instant.now().minusSeconds(1), Instant.now(),
+ "testCLName", "testActor", "testRecipe", "testTarget",
+ UUID.randomUUID().toString(), "1", "testMessage", "testOutcome");
+
+ queue.clear();
+ guardContext.asyncQuery(workingMemory, "testActor", "testRecipe",
+ "testTarget", UUID.randomUUID().toString(), "testCLName");
+ Object response = queue.poll(10, TimeUnit.SECONDS);
+ assertNotNull(response);
+ }
+
+ @Test
+ public void badValuesTest() throws InterruptedException {
+ Properties props = new Properties(prop);
+ props.setProperty("guard.disabled", "true");
+ props.setProperty("guard.pdp.rest.client.user", "");
+ props.setProperty("guard.pdp.rest.client.password", "");
+ props.setProperty("guard.pdp.rest.url", "bad,testuser,testpassword");
+
+ guardContext = new GuardContext(props);
+
+ guardContext.asyncCreateDbEntry(Instant.now().minusSeconds(1), Instant.now(),
+ "testCLName", "testActor", "testRecipe", "testTarget",
+ UUID.randomUUID().toString(), "1", "testMessage", "testOutcome");
+
+ queue.clear();
+ guardContext.asyncQuery(workingMemory, "testActor", "testRecipe",
+ "testTarget", UUID.randomUUID().toString());
+ Object response = queue.poll(10, TimeUnit.SECONDS);
+ assertNotNull(response);
+ }
+
+ @Test
+ public void policyGuardResponseTest() {
+ UUID requestId = UUID.randomUUID();
+ PolicyGuardResponse emptyResponse1 = new PolicyGuardResponse(null, null, null);
+
+ assertNotNull(emptyResponse1);
+
+ PolicyGuardResponse response = new PolicyGuardResponse("Some Result", requestId, "Some Details");
+
+ response.setRequestId(requestId);
+ assertEquals(requestId, response.getRequestId());
+
+ response.setResult("Some Result");
+ assertEquals("Some Result", response.getResult());
+
+ assertEquals("PolicyGuardResponse [requestId=", response.toString().substring(0, 31));
+ }
+}