aboutsummaryrefslogtreecommitdiffstats
path: root/controlloop
diff options
context:
space:
mode:
Diffstat (limited to 'controlloop')
-rw-r--r--controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetHistory.java (renamed from controlloop/common/guard/src/main/java/org/onap/policy/guard/PIPEngineGetHistory.java)6
-rw-r--r--controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetStatus.java311
-rw-r--r--controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetHistoryTest.java23
-rw-r--r--controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetStatusTest.java411
-rw-r--r--controlloop/templates/pom.xml1
-rw-r--r--controlloop/templates/template.demo.clc/README.md6
-rw-r--r--controlloop/templates/template.demo.clc/pom.xml216
-rw-r--r--controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/CallGuardTaskEmbedded.java165
-rw-r--r--controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelperEmbedded.java518
-rw-r--r--controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl1413
-rw-r--r--controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopCoordinationTest.java497
-rw-r--r--controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java297
-rw-r--r--controlloop/templates/template.demo.clc/src/test/resources/META-INF/persistence.xml48
-rw-r--r--controlloop/templates/template.demo.clc/src/test/resources/xacml/synthetic_control_loop_one_blocks_synthetic_control_loop_two.xml51
-rw-r--r--controlloop/templates/template.demo.clc/src/test/resources/xacml/xacml_guard_clc.properties65
-rw-r--r--controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticOne.yaml43
-rw-r--r--controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticTwo.yaml43
-rw-r--r--controlloop/templates/template.demo/src/test/resources/xacml/xacml_guard.properties2
18 files changed, 4105 insertions, 11 deletions
diff --git a/controlloop/common/guard/src/main/java/org/onap/policy/guard/PIPEngineGetHistory.java b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetHistory.java
index a9a984ade..2a1ef2fc6 100644
--- a/controlloop/common/guard/src/main/java/org/onap/policy/guard/PIPEngineGetHistory.java
+++ b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetHistory.java
@@ -55,8 +55,8 @@ import org.onap.policy.drools.system.PolicyEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class PIPEngineGetHistory extends StdConfigurableEngine {
- private static final Logger logger = LoggerFactory.getLogger(PIPEngineGetHistory.class);
+public class PipEngineGetHistory extends StdConfigurableEngine {
+ private static final Logger logger = LoggerFactory.getLogger(PipEngineGetHistory.class);
//
// Base issuer string. The issuer in the policy will also contain time window information
@@ -91,7 +91,7 @@ public class PIPEngineGetHistory extends StdConfigurableEngine {
new StdPIPRequest(new IdentifierImpl(XACML_ATTRIBUTE_CATEGORY_RESOURCE),
new IdentifierImpl(XACML_TARGET_TARGET_ID), new IdentifierImpl(XML_SCHEMA_STRING));
- public PIPEngineGetHistory() {
+ public PipEngineGetHistory() {
super();
}
diff --git a/controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetStatus.java b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetStatus.java
new file mode 100644
index 000000000..40b26c847
--- /dev/null
+++ b/controlloop/common/guard/src/main/java/org/onap/policy/guard/PipEngineGetStatus.java
@@ -0,0 +1,311 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2018 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 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.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.IdentifierImpl;
+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 java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+
+import org.onap.policy.drools.system.PolicyEngine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PipEngineGetStatus extends StdConfigurableEngine {
+ private static final Logger logger = LoggerFactory.getLogger(PipEngineGetStatus.class);
+
+ //
+ // Base issuer string. The issuer in the policy will contain the operations
+ // E.g., "org:onap:policy:guard:status:clname:testcl"
+ //
+ public static final String DEFAULT_ISSUER = "org:onap:policy:guard:status";
+ public static final String DEFAULT_DESCRIPTION = "PIP for retrieving Operation Status from DB";
+
+ private static final String XML_SCHEMA_STRING = "http://www.w3.org/2001/XMLSchema#string";
+
+ private static final String XACML_SUBJECT_CATEGORY_ACCESS_SUBJECT =
+ "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject";
+ private static final String XACML_ACTOR_ACTOR_ID = "urn:oasis:names:tc:xacml:1.0:actor:actor-id";
+ private static final String XACML_ATTRIBUTE_CATEGORY_ACTION =
+ "urn:oasis:names:tc:xacml:3.0:attribute-category:action";
+ private static final String XACML_OPERATION_OPERATION_ID = "urn:oasis:names:tc:xacml:1.0:operation:operation-id";
+ private static final String XACML_ATTRIBUTE_CATEGORY_RESOURCE =
+ "urn:oasis:names:tc:xacml:3.0:attribute-category:resource";
+ private static final String XACML_TARGET_TARGET_ID = "urn:oasis:names:tc:xacml:1.0:target:target-id";
+ private static final String XACML_TEST_SQL_RESOURCE_OPERATIONS_STATUS =
+ "com:att:research:xacml:test:sql:resource:operations:status";
+
+ private static final PIPRequest PIP_REQUEST_ACTOR =
+ new StdPIPRequest(new IdentifierImpl(XACML_SUBJECT_CATEGORY_ACCESS_SUBJECT),
+ new IdentifierImpl(XACML_ACTOR_ACTOR_ID), new IdentifierImpl(XML_SCHEMA_STRING));
+
+ private static final PIPRequest PIP_REQUEST_RECIPE =
+ new StdPIPRequest(new IdentifierImpl(XACML_ATTRIBUTE_CATEGORY_ACTION),
+ new IdentifierImpl(XACML_OPERATION_OPERATION_ID), new IdentifierImpl(XML_SCHEMA_STRING));
+
+ private static final PIPRequest PIP_REQUEST_TARGET =
+ new StdPIPRequest(new IdentifierImpl(XACML_ATTRIBUTE_CATEGORY_RESOURCE),
+ new IdentifierImpl(XACML_TARGET_TARGET_ID), new IdentifierImpl(XML_SCHEMA_STRING));
+
+ public PipEngineGetStatus() {
+ super();
+ }
+
+ @Override
+ public Collection<PIPRequest> attributesRequired() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<PIPRequest> attributesProvided() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException {
+ logger.debug("Entering Status PIP");
+
+ /*
+ * First check to see if the issuer is set and then match it
+ */
+ String issuer;
+ if ((issuer = pipRequest.getIssuer()) == null) {
+
+ logger.debug("No issuer in the request...");
+ logger.debug("Status PIP - No issuer in the request!");
+ return StdPIPResponse.PIP_RESPONSE_EMPTY;
+
+ } else if (!issuer.contains(this.getIssuer())) {
+ // Notice, we are checking here for the base issuer prefix.
+ logger.debug("Requested issuer '{}' does not match {}", issuer, getIssuer());
+ logger.debug("Status PIP - Issuer {} does not match with: ", issuer, this.getIssuer());
+ return StdPIPResponse.PIP_RESPONSE_EMPTY;
+ }
+
+ String[] s1 = issuer.split("clname:");
+ String clname = s1[1];
+ String target = null;
+ try {
+ target = getTarget(pipFinder).iterator().next();
+ } catch (Exception e) {
+ logger.debug("could not retrieve target from PIP finder", e);
+ return StdPIPResponse.PIP_RESPONSE_EMPTY;
+ }
+
+ logger.debug("Going to query DB about: clname={}, target={}", clname, target);
+ String statusFromDb = getStatusFromDb(clname, target);
+
+ StdMutablePIPResponse stdPipResponse = new StdMutablePIPResponse();
+
+ this.addStringAttribute(stdPipResponse, new IdentifierImpl(XACML_ATTRIBUTE_CATEGORY_RESOURCE),
+ new IdentifierImpl(XACML_TEST_SQL_RESOURCE_OPERATIONS_STATUS), statusFromDb, pipRequest);
+
+ return new StdPIPResponse(stdPipResponse);
+ }
+
+ @Override
+ public void configure(String id, Properties properties) throws PIPException {
+ super.configure(id, properties);
+
+ if (this.getDescription() == null) {
+ this.setDescription(DEFAULT_DESCRIPTION);
+ }
+ if (this.getIssuer() == null) {
+ this.setIssuer(DEFAULT_ISSUER);
+ }
+ }
+
+ private PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) {
+ PIPResponse pipResponse = null;
+
+ try {
+ pipResponse = pipFinder.getMatchingAttributes(pipRequest, this);
+ } catch (PIPException ex) {
+ logger.error("getAttribute threw:", ex);
+ return null;
+ }
+ if (pipResponse == null) {
+ return null;
+ }
+ if (pipResponse.getStatus() != null && !pipResponse.getStatus().isOk()) {
+ logger.warn("PIP response error {}: {}", pipRequest.getAttributeId().stringValue(),
+ pipResponse.getStatus());
+ return null;
+ }
+ if (pipResponse.getAttributes() != null && pipResponse.getAttributes().isEmpty()) {
+ logger.warn("No attributes in POP response {}: {}", pipRequest.getAttributeId().stringValue(),
+ pipResponse.getStatus());
+ return null;
+ }
+ return pipResponse;
+ }
+
+ private void addStringAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, Identifier attributeId,
+ String value, PIPRequest pipRequest) {
+ AttributeValue<String> attributeValue = null;
+ try {
+ attributeValue = DataTypes.DT_STRING.createAttributeValue(value);
+ } catch (Exception ex) {
+ logger.error("Failed to convert {} to an AttributeValue<String>", value, ex);
+ }
+ if (attributeValue != null) {
+ stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue,
+ pipRequest.getIssuer()/* this.getIssuer() */, false));
+ }
+ }
+
+ private Set<String> getTarget(PIPFinder pipFinder) {
+ /*
+ * Get the UID from either the subject id or the uid property
+ */
+ PIPResponse pipResponseUid = this.getAttribute(PIP_REQUEST_TARGET, pipFinder);
+ if (pipResponseUid == null) {
+ return new HashSet<>();
+ }
+
+ /*
+ * Iterate over all of the returned results and do the LDAP requests
+ */
+ Collection<Attribute> listUids = pipResponseUid.getAttributes();
+ Set<String> setUids = new HashSet<>();
+ for (Attribute attributeUid : listUids) {
+ Iterator<AttributeValue<String>> iterAttributeValues = attributeUid.findValues(DataTypes.DT_STRING);
+ if (iterAttributeValues != null) {
+ while (iterAttributeValues.hasNext()) {
+ String uid = iterAttributeValues.next().getValue();
+ if (uid != null) {
+ setUids.add(uid);
+ }
+ }
+ }
+ }
+
+ return setUids;
+ }
+
+ private static String getStatusFromDb(String clname, String target) {
+ //
+ // DB Properties
+ //
+ Properties props = new Properties();
+ try {
+ props.put(Util.ECLIPSE_LINK_KEY_URL, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL));
+ props.put(Util.ECLIPSE_LINK_KEY_USER, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER));
+ props.put(Util.ECLIPSE_LINK_KEY_PASS, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS));
+ } catch (NullPointerException e) {
+ logger.error("getStatusFromDB: {} when setting properties", e.getMessage());
+ }
+ //
+ // Set opsHistPu to the correct value and clear properties if necessary.
+ //
+ String opsHistPu = System.getProperty("OperationsHistoryPU");
+ if (opsHistPu == null || !opsHistPu.equals("TestOperationsHistoryPU")) {
+ opsHistPu = "OperationsHistoryPU";
+ } else {
+ props.clear();
+ }
+ //
+ // Set up the EntityManager
+ //
+ EntityManagerFactory emf = null;
+ EntityManager em = null;
+ try {
+ emf = Persistence.createEntityManagerFactory(opsHistPu, props);
+ } catch (Exception ex) {
+ logger.error("PIP thread got Exception. Can't connect to Operations History DB -- {}", opsHistPu);
+ logger.error("getStatusFromDb threw: ", ex);
+ return null;
+ }
+ try {
+ em = emf.createEntityManager();
+ } catch (Exception ex) {
+ logger.error("PIP thread got Exception. Problem creating EntityManager");
+ logger.error("getStatusFromDb threw: ", ex);
+ emf.close();
+ return null;
+ }
+ //
+ // Create the query
+ //
+ String sql = "select outcome from operationshistory10 where"
+ + " clname= ?"
+ + " and target= ?"
+ + " order by endtime desc limit 1";
+ Query nq = em.createNativeQuery(sql);
+ nq.setParameter(1, clname);
+ nq.setParameter(2, target);
+ logger.debug("SQL query: {}, {}, {}", sql, clname, target);
+ //
+ // Run the query
+ //
+ String ret = null;
+ try{
+ ret = ((String)nq.getSingleResult());
+ } catch(NoResultException ex) {
+ logger.debug("NoResultException for getSingleResult()");
+ ret = "NO_MATCHING_ENTRY";
+ } catch(Exception ex){
+ logger.error("getStatusFromDB threw: ", ex);
+ }
+ if (ret != null) {
+ logger.debug("SQL query result: {}", ret);
+ }
+ //
+ // Clean up and return the result
+ //
+ try {
+ em.close();
+ } catch(Exception ex){
+ logger.error("getStatusFromDB threw: ", ex);
+ }
+ try {
+ emf.close();
+ } catch(Exception ex){
+ logger.error("getStatusFromDB threw: ", ex);
+ }
+ return ret;
+ }
+}
diff --git a/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetHistoryTest.java b/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetHistoryTest.java
index 5780cecd6..288c6ca33 100644
--- a/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetHistoryTest.java
+++ b/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetHistoryTest.java
@@ -67,7 +67,7 @@ import org.junit.Test;
import org.onap.policy.drools.system.PolicyEngine;
public class PipEngineGetHistoryTest {
- static PIPEngineGetHistory pegh;
+ static PipEngineGetHistory pegh;
private static final String ISSUER = "issuerIntw:mid:end";
private static EntityManagerFactory emf;
@@ -80,9 +80,9 @@ public class PipEngineGetHistoryTest {
public static void testPipEngineGetHistory() {
pegh = null;
try {
- pegh = new PIPEngineGetHistory();
+ pegh = new PipEngineGetHistory();
} catch (Exception e) {
- fail("PIPEngineGetHistory constructor failed");
+ fail("PipEngineGetHistory constructor failed");
}
// Set PU
@@ -108,8 +108,17 @@ public class PipEngineGetHistoryTest {
em.getTransaction().commit();
}
+ /**
+ * Clean up test class.
+ */
@AfterClass
public static void tearDown() {
+ String sql = "DROP TABLE `operationshistory10`";
+ Query nq = em.createNativeQuery(sql);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+ em.close();
emf.close();
}
@@ -174,7 +183,7 @@ public class PipEngineGetHistoryTest {
Method method = null;
int count = -1;
try {
- method = PIPEngineGetHistory.class.getDeclaredMethod("getCountFromDb", String.class, String.class,
+ method = PipEngineGetHistory.class.getDeclaredMethod("getCountFromDb", String.class, String.class,
String.class, String.class);
method.setAccessible(true);
count = (int) method.invoke(null, "actor", "op", "target", "1 MINUTE");
@@ -204,7 +213,7 @@ public class PipEngineGetHistoryTest {
@Test
public void testConfigure() throws PIPException {
- PIPEngineGetHistory pegh = new PIPEngineGetHistory();
+ PipEngineGetHistory pegh = new PipEngineGetHistory();
pegh.configure("Dorothy", new Properties());
pegh.setDescription(null);
@@ -214,7 +223,7 @@ public class PipEngineGetHistoryTest {
@Test
public void getAttributesTest() throws URISyntaxException, PIPException, FactoryException {
- PIPEngineGetHistory pegh = new PIPEngineGetHistory();
+ PipEngineGetHistory pegh = new PipEngineGetHistory();
pegh.setIssuer("Dorothy");
Identifier identifierCategory = new IdentifierImpl(new URI("http://somewhere.over.the.rainbow/category"));;
@@ -233,7 +242,7 @@ public class PipEngineGetHistoryTest {
@Test
public void timeWindowTest() throws URISyntaxException, PIPException, FactoryException {
- PIPEngineGetHistory pegh = new PIPEngineGetHistory();
+ PipEngineGetHistory pegh = new PipEngineGetHistory();
pegh.setIssuer("Dorothy");
Identifier identifierCategory = new IdentifierImpl(new URI("http://somewhere.over.the.rainbow/category"));;
diff --git a/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetStatusTest.java b/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetStatusTest.java
new file mode 100644
index 000000000..cf83c883c
--- /dev/null
+++ b/controlloop/common/guard/src/test/java/org/onap/policy/guard/PipEngineGetStatusTest.java
@@ -0,0 +1,411 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2018 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.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+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.Status;
+import com.att.research.xacml.api.pip.PIPEngine;
+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.IdentifierImpl;
+import com.att.research.xacml.std.StdAttribute;
+import com.att.research.xacml.std.StdAttributeValue;
+import com.att.research.xacml.std.StdStatus;
+import com.att.research.xacml.std.StdStatusCode;
+import com.att.research.xacml.std.pip.StdPIPRequest;
+import com.att.research.xacml.std.pip.StdPIPResponse;
+import com.att.research.xacml.std.pip.finders.EngineFinder;
+import com.att.research.xacml.util.FactoryException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.drools.system.PolicyEngine;
+
+public class PipEngineGetStatusTest {
+ static PipEngineGetStatus pegs;
+ private static final String ISSUER = "issuer:clname:testclname";
+
+ private static EntityManagerFactory emf;
+ private static EntityManager em;
+
+ /**
+ * Set up test class.
+ */
+ @BeforeClass
+ public static void testPipEngineGetStatus() {
+ pegs = null;
+ try {
+ pegs = new PipEngineGetStatus();
+ } catch (Exception e) {
+ fail("PipEngineGetStatus constructor failed");
+ }
+
+ // Set PU
+ System.setProperty(Util.PU_KEY, Util.JUNITPU);
+
+ // Enter dummy props to avoid nullPointerException
+ PolicyEngine.manager.setEnvironmentProperty(Util.ONAP_KEY_URL, "a");
+ PolicyEngine.manager.setEnvironmentProperty(Util.ONAP_KEY_USER, "b");
+ PolicyEngine.manager.setEnvironmentProperty(Util.ONAP_KEY_PASS, "c");
+
+ // Connect to in-mem db
+ emf = Persistence.createEntityManagerFactory(Util.JUNITPU);
+ em = emf.createEntityManager();
+
+ // Create necessary table
+ String sql = "CREATE TABLE `operationshistory10` (" + "`CLNAME` varchar(255)," + "`requestID` varchar(100),"
+ + "`actor` varchar(50) ," + "`operation` varchar(50)," + "`target` varchar(50),"
+ + "`starttime` timestamp," + "`outcome` varchar(50)," + "`message` varchar(255),"
+ + "`subrequestId` varchar(100)," + "`endtime` timestamp" + ")";
+ Query nq = em.createNativeQuery(sql);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+ }
+
+ /**
+ * Clean up test class.
+ */
+ @AfterClass
+ public static void tearDown() {
+ String sql = "DROP TABLE `operationshistory10`";
+ Query nq = em.createNativeQuery(sql);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+ em.close();
+ emf.close();
+ }
+
+ /**
+ * Setup method.
+ */
+ @Before
+ public void setUp() {
+ // clear the table
+ String sql = "DELETE FROM `operationshistory10`";
+ Query nq = em.createNativeQuery(sql);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+ }
+
+ @Test
+ public void testAttributesRequired() {
+ assertTrue(pegs.attributesRequired().isEmpty());
+ }
+
+ @Test
+ public void testAttributesProvided() {
+ assertTrue(pegs.attributesProvided().isEmpty());
+ }
+
+ @Test
+ public void testGetAttributes() {
+ StdPIPRequest mockPipRequest = mock(StdPIPRequest.class);
+ EngineFinder mockPipFinder = mock(EngineFinder.class);
+
+ // Test issuer null
+ when(mockPipRequest.getIssuer()).thenReturn(null);
+ try {
+ assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pegs.getAttributes(mockPipRequest, mockPipFinder));
+ } catch (Exception e) {
+ fail("getAttributes failed");
+ }
+
+ // Test issuer not equal to our issuer
+ pegs.setIssuer(ISSUER);
+ when(mockPipRequest.getIssuer()).thenReturn("something else");
+ try {
+ assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pegs.getAttributes(mockPipRequest, mockPipFinder));
+ } catch (Exception e) {
+ fail("getAttributes failed");
+ }
+
+ // Test issuer equal to our issuer
+ when(mockPipRequest.getIssuer()).thenReturn(ISSUER);
+ try {
+ assertNotNull(pegs.getAttributes(mockPipRequest, mockPipFinder));
+ } catch (Exception e) {
+ // Normal to catch exception
+ }
+ }
+
+ @Test
+ public void testGetStatusFromDb() {
+
+ // Use reflection to run getStatsFromDB
+ Method method = null;
+ String status = null;
+ String addEntry;
+ Query nq;
+
+ // Add an entry
+ addEntry = "insert into operationshistory10 (outcome, CLNAME, actor, operation, target, endtime)"
+ + "values('1','testcl', 'actor', 'op', 'testtarget', CURRENT_TIMESTAMP())";
+ nq = em.createNativeQuery(addEntry);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+
+ try {
+ method = PipEngineGetStatus.class.getDeclaredMethod("getStatusFromDb", String.class, String.class);
+ method.setAccessible(true);
+ status = (String) method.invoke(null, "testcl", "testtarget");
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException e) {
+ fail(e.getLocalizedMessage());
+ }
+
+ // Status should be "success"
+ assertEquals("1", status);
+
+ // Add entries
+ addEntry = "insert into operationshistory10 (outcome, CLNAME, actor, operation, target, endtime)"
+ + "values('2','testcl', 'actor', 'op', 'testtarget', CURRENT_TIMESTAMP())";
+ nq = em.createNativeQuery(addEntry);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+
+ addEntry = "insert into operationshistory10 (outcome, CLNAME, actor, operation, target, endtime)"
+ + "values('3','testcl', 'actor', 'op', 'testtarget2', CURRENT_TIMESTAMP())";
+ nq = em.createNativeQuery(addEntry);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+
+ addEntry = "insert into operationshistory10 (outcome, CLNAME, actor, operation, target, endtime)"
+ + "values('4','testcl2', 'actor', 'op', 'testtarget2', CURRENT_TIMESTAMP())";
+ nq = em.createNativeQuery(addEntry);
+ em.getTransaction().begin();
+ nq.executeUpdate();
+ em.getTransaction().commit();
+
+ try {
+ method = PipEngineGetStatus.class.getDeclaredMethod("getStatusFromDb", String.class, String.class);
+ method.setAccessible(true);
+ status = (String) method.invoke(null, "testcl", "testtarget");
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException e) {
+ fail(e.getLocalizedMessage());
+ }
+ assertEquals("2", status);
+
+ try {
+ method = PipEngineGetStatus.class.getDeclaredMethod("getStatusFromDb", String.class, String.class);
+ method.setAccessible(true);
+ status = (String) method.invoke(null, "testcl", "testtarget2");
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException e) {
+ fail(e.getLocalizedMessage());
+ }
+ assertEquals("3", status);
+
+ try {
+ method = PipEngineGetStatus.class.getDeclaredMethod("getStatusFromDb", String.class, String.class);
+ method.setAccessible(true);
+ status = (String) method.invoke(null, "testcl2", "testtarget2");
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException e) {
+ fail(e.getLocalizedMessage());
+ }
+ assertEquals("4", status);
+ }
+
+ @Test
+ public void testConfigure() throws PIPException {
+ PipEngineGetStatus pegs = new PipEngineGetStatus();
+ pegs.configure("Dorothy", new Properties());
+
+ pegs.setDescription(null);
+ pegs.setIssuer(null);
+ pegs.configure("Dorothy", new Properties());
+ }
+
+ private class DummyPipFinder implements PIPFinder {
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ try {
+ List<Attribute> attributeList = new ArrayList<>();
+ Identifier categoryIdIn = new IdentifierImpl(new URI("http://somewhere.over.the.rainbow/category"));
+ Identifier dataTypeIdIn = new IdentifierImpl(new URI("http://www.w3.org/2001/XMLSchema#string"));
+
+ Identifier attributeIdIn0 = new IdentifierImpl(new URI(UUID.randomUUID().toString()));
+ AttributeValue<String> valueIn0 = new StdAttributeValue<String>(dataTypeIdIn, "ActorDorothy");
+ Attribute attribute0 = new StdAttribute(categoryIdIn, attributeIdIn0, valueIn0);
+ attributeList.add(attribute0);
+
+ Identifier attributeIdIn1 = new IdentifierImpl(new URI(UUID.randomUUID().toString()));
+ AttributeValue<String> valueIn1 = new StdAttributeValue<String>(dataTypeIdIn, "OperationHomeFromOZ");
+ Attribute attribute1 = new StdAttribute(categoryIdIn, attributeIdIn1, valueIn1);
+ attributeList.add(attribute1);
+
+ Identifier attributeIdIn2 = new IdentifierImpl(new URI(UUID.randomUUID().toString()));
+ AttributeValue<String> valueIn2 = new StdAttributeValue<String>(dataTypeIdIn, "TargetWickedWitch");
+ Attribute attribute2 = new StdAttribute(categoryIdIn, attributeIdIn2, valueIn2);
+ attributeList.add(attribute2);
+
+ return new StdPIPResponse(attributeList);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public Collection<PIPEngine> getPIPEngines() {
+ return null;
+ }
+ }
+
+ private class DummyPipFinderPipException implements PIPFinder {
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ throw new PIPException();
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public Collection<PIPEngine> getPIPEngines() {
+ return null;
+ }
+ }
+
+ private class DummyPipFinderResponseStatusNok implements PIPFinder {
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ Status status = new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, "Processing Error");
+ return new StdPIPResponse(status);
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public Collection<PIPEngine> getPIPEngines() {
+ return null;
+ }
+ }
+
+ private class DummyPipFinderResponseEmptyAttrs implements PIPFinder {
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude) throws PIPException {
+ List<Attribute> attributeList = new ArrayList<>();
+ return new StdPIPResponse(attributeList);
+ }
+
+ @Override
+ public PIPResponse getMatchingAttributes(PIPRequest pipRequest, PIPEngine exclude, PIPFinder pipFinderParent)
+ throws PIPException {
+ return null;
+ }
+
+ @Override
+ public Collection<PIPEngine> getPIPEngines() {
+ return null;
+ }
+ }
+}
diff --git a/controlloop/templates/pom.xml b/controlloop/templates/pom.xml
index 10a4a24d2..779837faf 100644
--- a/controlloop/templates/pom.xml
+++ b/controlloop/templates/pom.xml
@@ -34,6 +34,7 @@
<modules>
<module>template.demo</module>
+ <module>template.demo.clc</module>
<module>archetype-cl-amsterdam</module>
<module>archetype-cl-casablanca</module>
</modules>
diff --git a/controlloop/templates/template.demo.clc/README.md b/controlloop/templates/template.demo.clc/README.md
new file mode 100644
index 000000000..b20161931
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/README.md
@@ -0,0 +1,6 @@
+Copyright 2018 AT&T Intellectual Property. All rights reserved.
+This file is licensed under the CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL LICENSE
+Full license text at https://creativecommons.org/licenses/by/4.0/legalcode
+
+This is the ongoing implementation of template demonstrating use of control loop coordination facility.
+
diff --git a/controlloop/templates/template.demo.clc/pom.xml b/controlloop/templates/template.demo.clc/pom.xml
new file mode 100644
index 000000000..9295d78f7
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/pom.xml
@@ -0,0 +1,216 @@
+<!--
+ ============LICENSE_START=======================================================
+ drools-applications Control Loop Drools Templates
+ ================================================================================
+ Copyright (C) 2018 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.templates</groupId>
+ <artifactId>templates</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>template.demo.clc</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.jpa</artifactId>
+ <version>2.7.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.drools</groupId>
+ <artifactId>drools-core</artifactId>
+ <version>6.5.0.Final</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.drools</groupId>
+ <artifactId>drools-compiler</artifactId>
+ <version>6.5.0.Final</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>appc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>sdnr</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>appclcm</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>so</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>trafficgenerator</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>vfc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>events</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>guard</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>aai</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.model-impl</groupId>
+ <artifactId>sdc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>policy-yaml</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>eventmanager</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.att.research.xacml</groupId>
+ <artifactId>xacml</artifactId>
+ <version>1.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.att.research.xacml</groupId>
+ <artifactId>xacml-pdp</artifactId>
+ <version>1.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.persistence</groupId>
+ <artifactId>persistence-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actorServiceProvider</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actor.appc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actor.sdnr</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actor.appclcm</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actor.so</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common.actors</groupId>
+ <artifactId>actor.vfc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <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>policy-endpoints</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
+ <artifactId>simulators</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.policy.drools-pdp</groupId>
+ <artifactId>policy-management</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
+
diff --git a/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/CallGuardTaskEmbedded.java b/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/CallGuardTaskEmbedded.java
new file mode 100644
index 000000000..aaa2a0a83
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/CallGuardTaskEmbedded.java
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2018 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.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Supplier;
+import org.drools.core.WorkingMemory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.att.research.xacml.api.DataTypeException;
+import com.att.research.xacml.std.annotations.RequestParser;
+
+public class CallGuardTaskEmbedded implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(CallGuardTaskEmbedded.class);
+
+ /**
+ * Actor/recipe pairs whose guard requests need a VF Module count. Each element is of
+ * the form "<actor>:<recipe>".
+ */
+ private static final Set<String> NEEDS_VF_COUNT = new HashSet<>();
+
+ /**
+ * Actor/recipe pairs whose guard requests need the VF Module count to be incremented
+ * (i.e., because a module is being added). Each element is of the form
+ * "<actor>:<recipe>".
+ */
+ private static final Set<String> INCR_VF_COUNT = new HashSet<>();
+
+ static {
+ INCR_VF_COUNT.add("SO:VF Module Create");
+ NEEDS_VF_COUNT.addAll(INCR_VF_COUNT);
+ }
+
+ private WorkingMemory workingMemory;
+ private String clname;
+ private String actor;
+ private String recipe;
+ private String target;
+ private String requestId;
+ private Integer vfCount;
+
+ /**
+ * Populated once the response has been determined, which may happen during the
+ * constructor or later, during {@link #run()}.
+ */
+ private PolicyGuardResponse guardResponse;
+
+ /**
+ * Guard url is grabbed from PolicyEngine.manager properties
+ */
+ public CallGuardTaskEmbedded(WorkingMemory wm, String cl, String act, String rec, String tar, String reqId, Supplier<Integer> vfcnt) {
+ workingMemory = wm;
+ clname = cl;
+ actor = act;
+ recipe = rec;
+ requestId = reqId;
+ target = tar;
+
+ vfCount = null;
+
+ String key = act + ":" + rec;
+
+ if (NEEDS_VF_COUNT.contains(key)) {
+ // this actor/recipe needs the count - get it
+ if ((vfCount = vfcnt.get()) == null) {
+ /*
+ * The count is missing - create an artificial Deny, which will be
+ * inserted into working memory when "run()" is called.
+ */
+ guardResponse = new PolicyGuardResponse(Util.DENY, UUID.fromString(requestId), recipe);
+ logger.error("CallEmbeddedGuardTask.run missing VF Module count; requestId={}", requestId);
+ return;
+ }
+
+ if (INCR_VF_COUNT.contains(key)) {
+ // this actor/recipe needs the count to be incremented
+ ++vfCount;
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ if (guardResponse != null) {
+ // already have a response - just insert it
+ workingMemory.insert(guardResponse);
+ return;
+ }
+
+ final long startTime = System.nanoTime();
+ com.att.research.xacml.api.Request request = null;
+
+ PolicyGuardXacmlRequestAttributes xacmlReq =
+ new PolicyGuardXacmlRequestAttributes(clname, actor, recipe, target, requestId, vfCount);
+
+ try {
+ request = RequestParser.parseRequest(xacmlReq);
+ } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
+ logger.error("CallEmbeddedGuardTask.run threw: {}", e);
+ }
+
+
+ logger.debug("\n********** XACML REQUEST START ********");
+ logger.debug("{}", request);
+ logger.debug("********** XACML REQUEST END ********\n");
+
+ String guardDecision = null;
+
+ //
+ // Make guard request
+ //
+ guardDecision = new PolicyGuardXacmlHelperEmbedded().callPdp(xacmlReq);
+
+ logger.debug("\n********** XACML RESPONSE START ********");
+ logger.debug("{}", guardDecision);
+ logger.debug("********** XACML RESPONSE END ********\n");
+
+ //
+ // Check if the restful call was unsuccessful or property doesn't exist
+ //
+ if (guardDecision == null) {
+ logger.error("********** XACML FAILED TO CONNECT ********");
+ guardDecision = Util.INDETERMINATE;
+ }
+
+ guardResponse = new PolicyGuardResponse(guardDecision, UUID.fromString(this.requestId), this.recipe);
+
+
+ //
+ // Create an artificial Guard response in case we didn't get a clear Permit or Deny
+ //
+ if (guardResponse.getResult().equals("Indeterminate")) {
+ guardResponse.setOperation(recipe);
+ guardResponse.setRequestID(UUID.fromString(requestId));
+ }
+
+ long estimatedTime = System.nanoTime() - startTime;
+ logger.debug("\n\n============ Guard inserted with decision {} !!! =========== time took: {} mili sec \n\n",
+ guardResponse.getResult(), (double) estimatedTime / 1000 / 1000);
+ workingMemory.insert(guardResponse);
+
+ }
+
+}
diff --git a/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelperEmbedded.java b/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelperEmbedded.java
new file mode 100644
index 000000000..947b187e4
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/main/java/org/onap/policy/guard/PolicyGuardXacmlHelperEmbedded.java
@@ -0,0 +1,518 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * guard
+ * ================================================================================
+ * Copyright (C) 2018 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 com.att.research.xacml.api.Attribute;
+import com.att.research.xacml.api.AttributeCategory;
+import com.att.research.xacml.api.AttributeValue;
+import com.att.research.xacml.api.Result;
+import com.att.research.xacml.api.pdp.PDPEngine;
+import com.att.research.xacml.api.pdp.PDPException;
+import com.att.research.xacml.api.pdp.PDPEngineFactory;
+import com.att.research.xacmlatt.pdp.ATTPDPEngineFactory;
+import com.att.research.xacml.std.annotations.RequestParser;
+
+import com.google.gson.Gson;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.entity.ContentType;
+import org.json.JSONObject;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
+import org.onap.policy.guard.PolicyGuardResponse;
+import org.onap.policy.guard.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class PolicyGuardXacmlHelperEmbedded {
+ private static final Logger logger = LoggerFactory.getLogger(PolicyGuardXacmlHelperEmbedded.class);
+ private static final Logger netLogger =
+ LoggerFactory.getLogger(org.onap.policy.common.endpoints.event.comm.Topic.NETWORK_LOGGER);
+
+ // Constant for the systme line separator
+ private static final String SYSTEM_LS = System.lineSeparator();
+ private static String propfile;
+
+ public PolicyGuardXacmlHelperEmbedded() {
+ init(PolicyEngine.manager.getEnvironment());
+ }
+
+ // initialized from 'pdpx.url' property --
+ // Each entry in 'restUrls' contains a destination URL, and an optional
+ // 'Authorization' header entry. 'restUrlIndex' indicates the next
+ // entry to try -- after each failure, the index is advanced to the
+ // next entry (wrapping to the beginning, if needed).
+ private static class UrlEntry implements Serializable {
+ private static final long serialVersionUID = -8859237552195400518L;
+
+ URL restUrl;
+ String authorization = null;
+ String clientAuth = null;
+ String environment = null;
+ }
+
+ private UrlEntry[] restUrls = null;
+ private int restUrlIndex = 0;
+
+ // REST timeout, initialized from 'pdpx.timeout' property
+ private int timeout = 20000;
+
+ /**
+ * Call PDP.
+ *
+ * @param xacmlReq the XACML request
+ * @return the response
+ */
+ public String callPdp(PolicyGuardXacmlRequestAttributes xacmlReq) {
+ //
+ // Send it to the PDP
+ //
+ String response = null;
+
+ if ( propfile != null ) {
+ logger.debug("callEmbeddedPdp");
+ return callEmbeddedPdp(xacmlReq);
+ }
+ //
+ // Build the json request
+ //
+ JSONObject attributes = new JSONObject();
+ attributes.put("actor", xacmlReq.getActorID());
+ attributes.put("recipe", xacmlReq.getOperationID());
+ attributes.put("target", xacmlReq.getTargetID());
+ if (xacmlReq.getClnameID() != null) {
+ attributes.put("clname", xacmlReq.getClnameID());
+ }
+ if (xacmlReq.getVfCount() != null) {
+ attributes.put("vfCount", xacmlReq.getVfCount());
+ }
+ JSONObject jsonReq = new JSONObject();
+ jsonReq.put("decisionAttributes", attributes);
+ jsonReq.put("onapName", "PDPD");
+
+
+ try {
+ //
+ // Call RESTful PDP
+ //
+ UrlEntry urlEntry = restUrls[restUrlIndex];
+ String jsonRequestString = jsonReq.toString();
+ netLogger.info("[OUT|{}|{}|]{}{}", "GUARD", urlEntry.restUrl, SYSTEM_LS, jsonRequestString);
+ response = callRestfulPdp(new ByteArrayInputStream(jsonReq.toString().getBytes()), urlEntry.restUrl,
+ urlEntry.authorization, urlEntry.clientAuth, urlEntry.environment);
+ netLogger.info("[IN|{}|{}|]{}{}", "GUARD", urlEntry.restUrl, SYSTEM_LS, response);
+ } catch (Exception e) {
+ logger.error("Error in sending RESTful request: ", e);
+ }
+
+ return response;
+ }
+
+ /**
+ * This makes an HTTP POST call to a running PDP RESTful servlet to get a decision.
+ *
+ * @param is the InputStream
+ * @param authorization the Authorization
+ * @param clientauth the ClientAuth
+ * @param environment the Environment
+ * @return response from guard which contains "Permit" or "Deny"
+ */
+ private String callRestfulPdp(InputStream is, URL restURL, String authorization, String clientauth,
+ String environment) {
+ HttpURLConnection connection = null;
+
+ try {
+ //
+ // Open up the connection
+ //
+ connection = (HttpURLConnection) restURL.openConnection();
+ connection.setRequestProperty("Content-Type", "application/json");
+ //
+ // Setup our method and headers
+ //
+ connection.setRequestProperty("Accept", "application/json");
+ if (authorization != null) {
+ connection.setRequestProperty("Authorization", authorization);
+ }
+ if (clientauth != null) {
+ connection.setRequestProperty("ClientAuth", clientauth);
+ }
+ if (environment != null) {
+ connection.setRequestProperty("Environment", environment);
+ }
+ connection.setConnectTimeout(timeout);
+ connection.setReadTimeout(timeout);
+ connection.setRequestMethod("POST");
+ connection.setUseCaches(false);
+ //
+ // Adding this in. It seems the HttpUrlConnection class does NOT
+ // properly forward our headers for POST re-direction. It does so
+ // for a GET re-direction.
+ //
+ // So we need to handle this ourselves.
+ //
+ connection.setInstanceFollowRedirects(false);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ //
+ // Send the request
+ //
+ try (OutputStream os = connection.getOutputStream()) {
+ IOUtils.copy(is, os);
+ }
+
+ //
+ // Do the connect
+ //
+ connection.connect();
+
+ if (connection.getResponseCode() != 200) {
+ logger.error(connection.getResponseCode() + " " + connection.getResponseMessage());
+ return Util.INDETERMINATE;
+ }
+ } catch (Exception e) {
+ logger.error("Exception in 'PolicyGuardEmbeddedHelper.callRESTfulPDP'", e);
+ return Util.INDETERMINATE;
+ }
+
+ //
+ // Read the response
+ //
+ try {
+ ContentType contentType = ContentType.parse(connection.getContentType());
+
+ if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) {
+ InputStream inputStream = connection.getInputStream();
+ int contentLength = connection.getContentLength();
+
+ return readResponseFromStream(inputStream, contentLength);
+ } else {
+ logger.error("unknown content-type: {}", contentType);
+ return Util.INDETERMINATE;
+ }
+
+ } catch (Exception e) {
+ String message = "Parsing Content-Type: " + connection.getContentType();
+ logger.error(message, e);
+ return Util.INDETERMINATE;
+ }
+ }
+
+ /**
+ * Call embedded PDP.
+ *
+ * @param xacmlReq the XACML request
+ * @return the response
+ */
+ public static String callEmbeddedPdp(PolicyGuardXacmlRequestAttributes xacmlReq) {
+ com.att.research.xacml.api.Response response = null;
+ Properties props = new Properties();
+ //
+ // Get properties
+ //
+ try ( InputStream is = new FileInputStream(propfile);
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr) ) {
+ props.load(br);
+ } catch (Exception e) {
+ logger.error("Unable to load properties file {} {}", propfile, e.getMessage());
+ }
+ //
+ // Create embedded PDPEngine
+ //
+ PDPEngine xacmlPdpEngine;
+ try {
+ xacmlPdpEngine = ATTPDPEngineFactory.newInstance().newEngine(props);
+ } catch (Exception e) {
+ logger.error("Failed to create new PDPEngine {}", e.getMessage());
+ return null;
+ }
+ logger.debug("embedded Engine created");
+ //
+ // Embedded call to PDP
+ //
+ long lTimeStart = System.currentTimeMillis();
+ if (xacmlReq.getVfCount() == null ) {
+ xacmlReq.setVfCount(1);
+ }
+ try {
+ response = xacmlPdpEngine.decide(RequestParser.parseRequest(xacmlReq));
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ long lTimeEnd = System.currentTimeMillis();
+ logger.debug("Elapsed Time: {} ms", (lTimeEnd - lTimeStart));
+ //
+ // Convert response to string
+ //
+ logger.debug("converting response to string");
+ PolicyGuardResponse pgr = parseXacmlPdpResponse(response);
+ logger.debug("parsed XacmlPdpResponse {}", pgr);
+ String decision = pgr.getResult();
+ logger.debug("decision={}",decision);
+ return decision;
+ }
+
+ /**
+ * Parse XACML PDP response.
+ *
+ * @param xacmlResponse the XACML response
+ * @return the PolicyGuardResponse
+ */
+ public static PolicyGuardResponse parseXacmlPdpResponse(com.att.research.xacml.api.Response xacmlResponse) {
+ if (xacmlResponse == null) {
+ //
+ // In case the actual XACML response was null, create an empty
+ // response object with decision "Indeterminate"
+ //
+ return new PolicyGuardResponse("Indeterminate", null, "");
+ }
+
+ Iterator<Result> itRes = xacmlResponse.getResults().iterator();
+
+ Result res = itRes.next();
+ String decisionFromXacmlResponse = res.getDecision().toString();
+ Iterator<AttributeCategory> itAttrCat = res.getAttributes().iterator();
+ UUID reqIdFromXacmlResponse = null;
+ String operationFromXacmlResponse = "";
+
+ while (itAttrCat.hasNext()) {
+ Iterator<Attribute> itAttr = itAttrCat.next().getAttributes().iterator();
+ while (itAttr.hasNext()) {
+ Attribute currentAttr = itAttr.next();
+ String attributeId = currentAttr.getAttributeId().stringValue();
+ if ("urn:oasis:names:tc:xacml:1.0:request:request-id".equals(attributeId)) {
+ Iterator<AttributeValue<?>> itValues = currentAttr.getValues().iterator();
+ reqIdFromXacmlResponse = UUID.fromString(itValues.next().getValue().toString());
+ }
+ if ("urn:oasis:names:tc:xacml:1.0:operation:operation-id".equals(attributeId)) {
+ Iterator<AttributeValue<?>> itValues = currentAttr.getValues().iterator();
+ operationFromXacmlResponse = itValues.next().getValue().toString();
+ }
+ }
+ }
+
+ return new PolicyGuardResponse(decisionFromXacmlResponse, reqIdFromXacmlResponse, operationFromXacmlResponse);
+
+ }
+
+ private void init(Properties properties) {
+ propfile = properties.getProperty("prop.guard.propfile");
+
+ // used to store error messages
+ StringBuilder sb = new StringBuilder();
+
+ // fetch these parameters, if they exist
+ String timeoutString = properties.getProperty("pdpx.timeout");
+ String disabledString = properties.getProperty("guard.disabled");
+
+ if (disabledString != null && Boolean.parseBoolean(disabledString)) {
+ return;
+ }
+
+ ArrayList<UrlEntry> entries = initEntries(properties, sb);
+
+ if (entries.isEmpty()) {
+ sb.append("'pdpx.*' -- no URLs specified, ");
+ } else {
+ restUrls = entries.toArray(new UrlEntry[0]);
+ }
+
+ if (timeoutString != null) {
+ try {
+ // decode optional 'pdpx.timeout' parameter
+ timeout = Integer.valueOf(timeoutString);
+ } catch (NumberFormatException e) {
+ sb.append("'pdpx.timeout': " + e + ", ");
+ logger.trace(e.getLocalizedMessage());
+ }
+ }
+
+
+ // 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);
+ String errorMessage = sb.toString();
+ logger.error("Initialization failure: {}", errorMessage);
+ }
+ }
+
+ private ArrayList<UrlEntry> initEntries(Properties properties, StringBuilder sb) {
+ // now, see which numeric entries (1-9) exist
+ ArrayList<UrlEntry> entries = new ArrayList<>();
+
+ for (int index = 0; index < 10; index += 1) {
+ String urlPrefix = "guard.";
+ if (index != 0) {
+ urlPrefix = urlPrefix + index + ".";
+ }
+
+ // see if the associated URL exists
+ String restUrllist = properties.getProperty(urlPrefix + "url");
+ if (nullOrEmpty(restUrllist)) {
+ // no entry for this index
+ continue;
+ }
+
+ // support a list of entries separated by semicolons. Each entry
+ // can be:
+ // URL
+ // URL,user
+ // URL,user,password
+ for (String restUrl : restUrllist.split("\\s*;\\s*")) {
+ UrlEntry entry = initRestUrl(properties, sb, restUrl);
+ // include this URLEntry in the list
+ if (entry != null) {
+ entries.add(entry);
+ }
+ }
+ }
+
+ return entries;
+ }
+
+ private UrlEntry initRestUrl(Properties properties, StringBuilder sb, String restUrl) {
+ String urlPrefix = "guard.";
+ String pdpxPrefix = "pdpx.";
+
+ String[] segments = restUrl.split("\\s*,\\s*");
+ String user = null;
+ String password = null;
+
+ if (segments.length >= 2) {
+ // user id is provided
+ restUrl = segments[0];
+ user = segments[1];
+ if (segments.length >= 3) {
+ // password is also provided
+ password = segments[2];
+ }
+ }
+
+ // URL does exist -- create the entry
+ UrlEntry urlEntry = new UrlEntry();
+ try {
+ urlEntry.restUrl = new URL(restUrl);
+ } catch (java.net.MalformedURLException e) {
+ // if we don't have a URL,
+ // don't bother with the rest on this one
+ sb.append("'").append(urlPrefix).append("url' '").append(restUrl).append("': ").append(e).append(",");
+ return null;
+ }
+
+ if (nullOrEmpty(user)) {
+ // user id was not provided on '*.url' line --
+ // extract it from a separate property
+ user = properties.getProperty(pdpxPrefix + "username", properties.getProperty("pdpx.username"));
+ }
+ if (nullOrEmpty(password)) {
+ // password was not provided on '*.url' line --
+ // extract it from a separate property
+ password = properties.getProperty(pdpxPrefix + "password", properties.getProperty("pdpx.password"));
+ }
+
+ // see if 'user' and 'password' entries both exist
+ if (!nullOrEmpty(user) && !nullOrEmpty(password)) {
+ urlEntry.authorization = "Basic " + Base64.getEncoder().encodeToString((user + ":" + password).getBytes());
+ }
+
+ // see if 'client.user' and 'client.password' entries both exist
+ String clientUser =
+ properties.getProperty(pdpxPrefix + "client.username", properties.getProperty("pdpx.client.username"));
+ String clientPassword =
+ properties.getProperty(pdpxPrefix + "client.password", properties.getProperty("pdpx.client.password"));
+ if (!nullOrEmpty(clientUser) && !nullOrEmpty(clientPassword)) {
+ urlEntry.clientAuth =
+ "Basic " + Base64.getEncoder().encodeToString((clientUser + ":" + clientPassword).getBytes());
+ }
+
+ // see if there is an 'environment' entry
+ String environment =
+ properties.getProperty(pdpxPrefix + "environment", properties.getProperty("pdpx.environment"));
+ if (!nullOrEmpty(environment)) {
+ urlEntry.environment = environment;
+ }
+
+ return urlEntry;
+ }
+
+ /**
+ * Check if a string is null or an empty string.
+ *
+ * @param value the string to be tested
+ * @return 'true' if the string is 'null' or has a length of 0, 'false' otherwise
+ */
+ private static boolean nullOrEmpty(String value) {
+ return (value == null || value.isEmpty());
+ }
+
+ private static String readResponseFromStream(InputStream inputStream, int contentLength) throws IOException {
+ // if content length is -1, response is chunked, and
+ // TCP connection will be dropped at the end
+ byte[] buf = new byte[contentLength < 0 ? 1024 : contentLength];
+
+ int offset = 0;
+ do {
+ int size = inputStream.read(buf, offset, buf.length - offset);
+ if (size < 0) {
+ // In a chunked response a dropped connection is expected, but not if the response
+ // is not chunked
+ if (contentLength > 0) {
+ logger.error("partial input stream");
+ }
+ break;
+ }
+ offset += size;
+ }
+ while (offset != contentLength);
+
+ String response = new String(buf, 0, offset);
+
+ //
+ // Connection may have failed or not been 200 OK, return Indeterminate
+ //
+ if (response.isEmpty()) {
+ return Util.INDETERMINATE;
+ }
+
+ return new JSONObject(response).getString("decision");
+
+ }
+}
diff --git a/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl b/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl
new file mode 100644
index 000000000..3981f0def
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl
@@ -0,0 +1,1413 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 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.controlloop;
+
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.ControlLoopEventStatus;
+import org.onap.policy.controlloop.ControlLoopException;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.ControlLoopLogger;
+import org.onap.policy.controlloop.policy.PolicyResult;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.controlloop.policy.Policy;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NEW_EVENT_STATUS;
+import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
+import org.onap.policy.controlloop.actor.so.SOActorServiceProvider;
+import org.onap.policy.aai.AaiNqResponseWrapper;
+import org.onap.policy.appc.Request;
+import org.onap.policy.appc.Response;
+import org.onap.policy.appc.CommonHeader;
+import org.onap.policy.appclcm.LcmRequestWrapper;
+import org.onap.policy.appclcm.LcmResponseWrapper;
+import org.onap.policy.appclcm.LcmRequest;
+import org.onap.policy.appclcm.LcmResponse;
+import org.onap.policy.appclcm.LcmCommonHeader;
+import org.onap.policy.vfc.VFCRequest;
+import org.onap.policy.vfc.VFCResponse;
+import org.onap.policy.vfc.VFCManager;
+import org.onap.policy.so.SOManager;
+import org.onap.policy.so.SORequest;
+import org.onap.policy.so.SORequestStatus;
+import org.onap.policy.so.SORequestDetails;
+import org.onap.policy.so.SOModelInfo;
+import org.onap.policy.so.SOCloudConfiguration;
+import org.onap.policy.so.SORequestInfo;
+import org.onap.policy.so.SORequestParameters;
+import org.onap.policy.so.SORelatedInstanceListElement;
+import org.onap.policy.so.SORelatedInstance;
+import org.onap.policy.so.SOResponse;
+import org.onap.policy.so.SOResponseWrapper;
+import org.onap.policy.guard.PolicyGuard;
+import org.onap.policy.guard.PolicyGuard.LockResult;
+import org.onap.policy.guard.TargetLock;
+import org.onap.policy.guard.GuardResult;
+import org.onap.policy.guard.PolicyGuardRequest;
+import org.onap.policy.guard.PolicyGuardResponse;
+import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
+import org.onap.policy.guard.CallGuardTaskEmbedded;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import java.time.Instant;
+import java.util.LinkedList;
+import java.util.Iterator;
+
+import org.onap.policy.drools.system.PolicyEngine;
+
+/*
+ * This structure mimics the Params structure.
+ * Its only purpose is to allow management of
+ * rules by the PAP component..
+ * It has no use at runtime since the rules go by
+ * Params for matching purposes.
+ */
+declare PapParams
+ closedLoopControlName : String
+ controlLoopYaml : String
+end
+
+/*
+ * Control Loop Identity
+ */
+declare Params
+ closedLoopControlName : String
+ controlLoopYaml : String
+end
+
+/*
+ * Used to clean up Params that no longer have associated rules.
+ */
+declare ParamsCleaner
+ closedLoopControlName : String
+ controlLoopYaml : String
+end
+
+
+/*
+ * Operation Timer
+ */
+declare OperationTimer
+ closedLoopControlName : String
+ requestID : String
+ delay : String
+end
+
+/*
+ * Control Loop Timer
+ */
+declare ControlLoopTimer
+ closedLoopControlName : String
+ requestID : String
+ delay : String
+end
+
+/*
+*
+* Called once and only once to insert the parameters into working memory for this Closed Loop policy.
+* This has a higher salience so we can ensure that the Params is created before we have a chance to
+* discard any events.
+*
+*/
+rule "${policyName}.SETUP"
+ salience 1
+ when
+ not( Params( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" ) )
+ then
+
+ Params params = new Params();
+ params.setClosedLoopControlName("${closedLoopControlName}");
+ params.setControlLoopYaml("${controlLoopYaml}");
+ insert(params);
+
+ // Note: globals have bad behavior when persistence is used,
+ // hence explicitly getting the logger vs using a global
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : YAML=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), params.getControlLoopYaml());
+end
+
+/*
+*
+* This rule responds to DCAE Events where there is no manager yet. Either it is
+* the first ONSET, or a subsequent badly formed Event (i.e. Syntax error, or is-closed-loop-disabled)
+*
+*/
+rule "${policyName}.EVENT"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() ) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+
+ try {
+
+ //
+ // Check the event, because we need it to not be null when
+ // we create the ControlLoopEventManager. The ControlLoopEventManager
+ // will do extra syntax checking as well check if the closed loop is disabled.
+ //
+ if ($event.getRequestId() == null) {
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.REJECTED);
+ notification.setFrom("policy");
+ notification.setMessage("Missing requestId");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ //
+ // Retract it from memory
+ //
+ retract($event);
+ } else if ($event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET) {
+ throw new ControlLoopException($event.getClosedLoopEventStatus() + " received with no prior onset");
+ } else {
+ //
+ // Create an EventManager
+ //
+ ControlLoopEventManager manager = new ControlLoopEventManager($params.getClosedLoopControlName(), $event.getRequestId());
+ //
+ // Disable target locking
+ //
+ manager.setUseTargetLock(false);
+ //
+ // Determine if EventManager can actively process the event (i.e. syntax, is_closed_loop_disabled checks etc.)
+ //
+ VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
+ notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName()
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ //
+ // Are we actively pursuing this event?
+ //
+ if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) {
+ //
+ // Insert Event Manager into memory, this will now kick off processing.
+ //
+ insert(manager);
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Setup the Overall Control Loop timer
+ //
+ ControlLoopTimer clTimer = new ControlLoopTimer();
+ clTimer.setClosedLoopControlName($event.getClosedLoopControlName());
+ clTimer.setRequestID($event.getRequestId().toString());
+ clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
+ //
+ // Insert it
+ //
+ insert(clTimer);
+ } else {
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Retract it from memory
+ //
+ retract($event);
+ }
+
+ //
+ // Now that the manager is inserted into Drools working memory, we'll wait for
+ // another rule to fire in order to continue processing. This way we can also
+ // then screen for additional ONSET and ABATED events for this RequestId.
+ //
+ }
+ } catch (Exception e) {
+ logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
+
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.REJECTED);
+ notification.setMessage("Exception occurred: " + e.getMessage());
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ //
+ //
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Retract the event
+ //
+ retract($event);
+ }
+end
+
+/*
+*
+* This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
+* is now created. We can start processing the yaml specification via the Event Manager.
+*
+*/
+rule "${policyName}.EVENT.MANAGER"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $clTimer : ControlLoopTimer ( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}: event={} manager={} clTimer={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $clTimer);
+
+ try {
+ //
+ // Check which event this is.
+ //
+ ControlLoopEventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
+ //
+ // Check what kind of event this is
+ //
+ if (eventStatus == NEW_EVENT_STATUS.SUBSEQUENT_ONSET) {
+ //
+ // We don't care about subsequent onsets
+ //
+ logger.info("{}: {}: subsequent onset",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ retract($event);
+ return;
+ }
+ if (eventStatus == NEW_EVENT_STATUS.SYNTAX_ERROR) {
+ //
+ // Ignore any bad syntax events
+ //
+ logger.warn("{}: {}: syntax error",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ retract($event);
+ return;
+ }
+ //
+ // We only want the initial ONSET event in memory,
+ // all the other events need to be retracted to support
+ // cleanup and avoid the other rules being fired for this event.
+ //
+ if (eventStatus != NEW_EVENT_STATUS.FIRST_ONSET) {
+ logger.warn("{}: {}: not first onset",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ retract($event);
+ }
+
+ logger.debug("{}: {}: target={}", $params.getClosedLoopControlName(),
+ drools.getRule().getName(), $event.getTarget());
+ //
+ // Now start seeing if we need to process this event
+ //
+
+ //
+ // Check if this is a Final Event
+ //
+ VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
+
+ if (notification != null) {
+ //
+ // Set common notification fields
+ //
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ //
+ // Its final, but are we waiting for abatement?
+ //
+ if ($manager.getNumAbatements() > 0) {
+ //
+ // We have received an abatement and are done
+ //
+ logger.info("{}: {}: abatement received for {}. Closing the control loop",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event.getRequestId());
+ //
+ // Set notification message
+ //
+ notification.setMessage("Abatement received. Closing the control loop.");
+ //
+ /// DB Write---end event processing for this RequestId()
+ //
+ $manager.commitAbatement("Event Abated","Closed");
+ //
+ // Unlock the target
+ //
+ TargetLock lock = $manager.unlockCurrentOperation();
+ if (lock != null) {
+ logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(),
+ drools.getRule().getName(), lock);
+ retract(lock);
+ }
+ //
+ // Retract everything from memory
+ //
+ logger.info("{}: {}: retracting onset, manager, and timer",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+
+ retract($manager.getOnsetEvent());
+ retract($manager);
+ retract($clTimer);
+ //
+ // TODO - what if we get subsequent Events for this RequestId?
+ // By default, it will all start over again. May be confusing for Ruby.
+ // Or, we could track this and then subsequently ignore the events
+ //
+ } else {
+ //
+ // Check whether we need to wait for abatement
+ //
+ if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) {
+ //
+ // We will wait
+ //
+ logger.info("{}: {}: waiting for abatement ..",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ //
+ // Set notification message
+ //
+ notification.setMessage("Waiting for abatement");
+ } else {
+ //
+ // We are done
+ //
+ logger.info("{}: {}: no abatement expected for {}. Closing the control loop",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event.getRequestId());
+ //
+ // Set notification message
+ //
+ notification.setMessage("No abatement expected. Closing the control loop.");
+ //
+ // Unlock the target
+ //
+ TargetLock lock = $manager.unlockCurrentOperation();
+ if (lock != null) {
+ logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(),
+ drools.getRule().getName(), lock);
+ retract(lock);
+ }
+ //
+ // Retract everything from memory
+ //
+ logger.info("{}: {}: retracting onset, manager, and timer",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+
+ retract($manager.getOnsetEvent());
+ retract($manager);
+ retract($clTimer);
+ }
+ }
+ //
+ // Send the notification
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ } else {
+ //
+ // NOT final, so let's ask for the next operation
+ //
+ ControlLoopOperationManager operation = $manager.processControlLoop();
+ if (operation != null) {
+ //
+ // Let's ask for a lock right away
+ //
+ LockResult<GuardResult, TargetLock> result = $manager.lockCurrentOperation();
+ logger.info("{}: {}: guard lock acquired={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ result.getB());
+ if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) {
+ //
+ // insert the operation into memory
+ //
+ insert(operation);
+
+ //
+ // insert operation timeout object
+ //
+ OperationTimer opTimer = new OperationTimer();
+ opTimer.setClosedLoopControlName($event.getClosedLoopControlName());
+ opTimer.setRequestID($event.getRequestId().toString());
+ opTimer.setDelay(operation.getOperationTimeout().toString() + "s");
+ insert(opTimer);
+
+ //
+ // Insert lock into memory
+ //
+ insert(result.getB());
+ }
+ else {
+ logger.debug("The target resource {} is already processing",
+ $event.getAai().get($event.getTarget()));
+ notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.REJECTED);
+ notification.setMessage("The target " + $event.getAai().get($event.getTarget()) + " is already locked");
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ retract($event);
+ retract($manager);
+ retract($clTimer);
+
+ if(result.getB() != null) {
+ retract(result.getB());
+ }
+ }
+ logger.info("{}: {}: starting operation={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ operation);
+ } else {
+ //
+ // Probably waiting for abatement
+ //
+ logger.info("{}: {}: no operation, probably waiting for abatement",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("{}: {}: unexpected",
+ $params.getClosedLoopControlName(),
+ drools.getRule().getName(), e);
+
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
+ notification.setMessage(e.getMessage());
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ retract($event);
+ retract($manager);
+ retract($clTimer);
+ }
+
+end
+
+/*
+*
+* Guard Permitted, let's send request to the actor.
+*
+*/
+rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}: event={} manager={} operation={} lock={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock);
+
+ Object request = null;
+ boolean caughtException = false;
+
+ try {
+ request = $operation.startOperation($event);
+
+ if (request != null) {
+ logger.debug("{}: {}: starting operation ..",
+ $params.getClosedLoopControlName(), drools.getRule().getName());
+ //
+ // Tell interested parties we are performing this Operation
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.OPERATION);
+ notification.setMessage($operation.getOperationMessage());
+ notification.setHistory($operation.getHistory());
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ switch ($operation.policy.getActor()){
+
+ case "APPC":
+
+ if (request instanceof Request) {
+ PolicyEngine.manager.deliver("APPC-CL", request);
+ }
+ else if (request instanceof LcmRequestWrapper) {
+ PolicyEngine.manager.deliver("APPC-LCM-READ", request);
+ }
+ break;
+ case "SO":
+ // at this point the AAI named query request should have already been made, the response recieved and used
+ // in the construction of the SO Request which is stored in operationRequest
+
+ if(request instanceof SORequest) {
+ // Call SO. The response will be inserted into memory once it's received
+ SOActorServiceProvider.sendRequest($event.getRequestId().toString(), drools.getWorkingMemory(), request);
+ }
+ break;
+ case "VFC":
+ if (request instanceof VFCRequest) {
+ // Start VFC thread
+ Thread t = new Thread(new VFCManager(drools.getWorkingMemory(), (VFCRequest)request));
+ t.start();
+ }
+ break;
+ }
+ } else {
+ //
+ // What happens if its null?
+ //
+ logger.warn("{}: {}: unexpected null operation request",
+ $params.getClosedLoopControlName(),
+ drools.getRule().getName());
+ if ("SO".equals($operation.policy.getActor())) {
+ retract($opTimer);
+ retract($operation);
+ modify($manager) {finishOperation($operation)};
+ }
+ else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
+ retract($opTimer);
+ retract($operation);
+ modify($manager) {finishOperation($operation)};
+ }
+ }
+
+ } catch (Exception e) {
+ String msg = e.getMessage();
+ logger.warn("{}: {}: operation={}: AAI failure: {}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $operation, msg, e);
+ $operation.setOperationHasException(msg);
+
+ if(request != null) {
+ //
+ // Create a notification for it ("DB Write - end operation")
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ }
+
+ retract($opTimer);
+ retract($operation);
+ caughtException = true;
+ }
+
+ // Having the modify statement in the catch clause doesn't work for whatever reason
+ if (caughtException) {
+ modify($manager) {finishOperation($operation)};
+ }
+end
+
+
+/*
+*
+* We were able to acquire a lock so now let's ask Xacml Guard whether
+* we are allowed to proceed with the request to the actor.
+*
+*/
+rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), getGuardApprovalStatus() == "NONE" )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}: event={} manager={} operation={} lock={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock);
+
+ //
+ // Sending notification that we are about to query Guard ("DB write - start operation")
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.OPERATION);
+ notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe());
+ notification.setHistory($operation.getHistory());
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ //
+ // Now send Guard Request to XACML Guard. In order to bypass the call to Guard,
+ // just change guardEnabled to false.
+ //
+ //
+
+ // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
+ boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
+
+ if(guardEnabled){
+
+ Thread t = new Thread(new CallGuardTaskEmbedded(
+ drools.getWorkingMemory(),
+ $event.getClosedLoopControlName(),
+ $operation.policy.getActor().toString(),
+ $operation.policy.getRecipe(),
+ $operation.getTargetEntity(),
+ $event.getRequestId().toString(),
+ () -> {
+ AaiNqResponseWrapper resp = $manager.getNqVserverFromAai();
+ return(resp == null ? null : resp.countVfModules());
+ }));
+ t.start();
+ }
+ else{
+ insert(new PolicyGuardResponse("Permit", $event.getRequestId(), $operation.policy.getRecipe()));
+ }
+
+end
+
+//
+// This rule will be triggered when a thread talking to the XACML Guard inserts a
+// guardResponse object into the working memory
+//
+rule "${policyName}.GUARD.RESPONSE"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ $guardResponse : PolicyGuardResponse(requestID == $event.getRequestId(), $operation.policy.recipe == operation)
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}: event={} manager={} operation={} lock={} opTimer={} guardResponse={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $opTimer, $guardResponse);
+
+
+ //we will permit the operation if there was no Guard for it
+ if("Indeterminate".equalsIgnoreCase($guardResponse.getResult())){
+ $guardResponse.setResult("Permit");
+ }
+
+ //
+ // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny")
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setNotification(ControlLoopNotificationType.OPERATION);
+ notification.setMessage("Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe() + " is " + $guardResponse.getResult());
+ notification.setHistory($operation.getHistory());
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+
+ if("Permit".equalsIgnoreCase($guardResponse.getResult())){
+
+ modify($operation){setGuardApprovalStatus($guardResponse.getResult())};
+ }
+ else {
+ //This is the Deny case
+ $operation.startOperation($event);
+ $operation.setOperationHasGuardDeny();
+ retract($opTimer);
+ retract($operation);
+ modify($manager) {finishOperation($operation)};
+ }
+
+ retract($guardResponse);
+
+end
+
+/*
+*
+* This rule responds to APPC Response Events
+*
+* I would have like to be consistent and write the Response like this:
+* $response : Response( CommonHeader.RequestId == $onset.getRequestId() )
+*
+* However, no compile error was given. But a runtime error was given. I think
+* because drools is confused between the classname CommonHeader vs the property CommonHeader.
+*
+*/
+rule "${policyName}.APPC.RESPONSE"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $response : Response( getCommonHeader().RequestId == $event.getRequestId() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $opTimer, $response);
+ //
+ // Get the result of the operation
+ //
+ PolicyResult policyResult = $operation.onResponse($response);
+ if (policyResult != null) {
+ logger.debug("{}: {}: operation finished - result={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ policyResult);
+ //
+ // This Operation has completed, construct a notification showing our results. (DB write - end operation)
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+ if (policyResult.equals(PolicyResult.SUCCESS)) {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ } else {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ }
+ //
+ // Ensure the operation is complete
+ //
+ if ($operation.isOperationComplete() == true) {
+ //
+ // It is complete, remove it from memory
+ //
+ retract($operation);
+ //
+ // We must also retract the timer object
+ // NOTE: We could write a Rule to do this
+ //
+ retract($opTimer);
+ //
+ // Complete the operation
+ //
+ modify($manager) {finishOperation($operation)};
+ } else {
+ //
+ // Just doing this will kick off the LOCKED rule again
+ //
+ modify($operation) {};
+ }
+ } else {
+ //
+ // Its not finished yet (i.e. expecting more Response objects)
+ //
+ // Or possibly it is a leftover response that we timed the request out previously
+ //
+ }
+ //
+ // We are going to retract these objects from memory
+ //
+ retract($response);
+end
+
+/*
+*
+* The problem with Responses is that they don't have a controlLoopControlName
+* field in them, so the only way to attach them is via RequestId. If we have multiple
+* control loop .drl's loaded in the same container, we need to be sure the cleanup
+* rules don't remove Responses for other control loops.
+*
+*/
+rule "${policyName}.APPC.RESPONSE.CLEANUP"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $response : Response($id : getCommonHeader().RequestId )
+ not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: orphan appc response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
+
+ //
+ // Retract it
+ //
+ retract($response);
+end
+
+/*
+*
+* This rule responds to APPC Response Events using the new LCM interface provided by appc
+*
+*/
+rule "${policyName}.APPC.LCM.RESPONSE"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $response : LcmResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $operation, $opTimer, $response);
+
+ //
+ // Get the result of the operation
+ //
+ PolicyResult policyResult = $operation.onResponse($response);
+ if (policyResult != null) {
+ logger.debug("{}: {}: operation finished - result={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ policyResult);
+
+ //
+ // This Operation has completed, construct a notification showing our results. (DB write - end operation)
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+ if (policyResult.equals(PolicyResult.SUCCESS)) {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
+ } else {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+ }
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Ensure the operation is complete
+ //
+ if ($operation.isOperationComplete() == true) {
+ //
+ // It is complete, remove it from memory
+ //
+ retract($operation);
+ //
+ // We must also retract the timer object
+ // NOTE: We could write a Rule to do this
+ //
+ retract($opTimer);
+ //
+ // Complete the operation
+ //
+ modify($manager) {finishOperation($operation)};
+ } else {
+ //
+ // Just doing this will kick off the LOCKED rule again
+ //
+ modify($operation) {};
+ }
+ } else {
+ //
+ // Its not finished yet (i.e. expecting more Response objects)
+ //
+ // Or possibly it is a leftover response that we timed the request out previously
+ //
+ }
+ //
+ // We are going to retract these objects from memory
+ //
+ retract($response);
+end
+
+/*
+*
+* Clean Up any lingering LCM reponses
+*
+*/
+rule "${policyName}.APPC.LCM.RESPONSE.CLEANUP"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $response : LcmResponseWrapper($id : getBody().getCommonHeader().getRequestId )
+ not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: orphan appc response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
+ //
+ // Retract it
+ //
+ retract($response);
+end
+
+/*
+*
+* This rule responds to SO Response Events
+*
+*/
+rule "${policyName}.SO.RESPONSE"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $response : SOResponseWrapper(requestID.toString() == $event.getRequestId().toString() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $operation, $opTimer, $response);
+
+ // Get the result of the operation
+ //
+ PolicyResult policyResult = $operation.onResponse($response);
+ if (policyResult != null) {
+ logger.debug("{}: {}: operation finished - result={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ policyResult);
+
+ //
+ // This Operation has completed, construct a notification showing our results
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+ if (policyResult.equals(PolicyResult.SUCCESS)) {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
+ } else {
+ notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+
+ }
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Ensure the operation is complete
+ //
+ if ($operation.isOperationComplete() == true) {
+ //
+ // It is complete, remove it from memory
+ //
+ retract($operation);
+ //
+ // We must also retract the timer object
+ // NOTE: We could write a Rule to do this
+ //
+ retract($opTimer);
+ //
+ // Complete the operation
+ //
+ modify($manager) {finishOperation($operation)};
+ } else {
+ //
+ // Just doing this will kick off the LOCKED rule again
+ //
+ modify($operation) {};
+ }
+ } else {
+ //
+ // Its not finished yet (i.e. expecting more Response objects)
+ //
+ // Or possibly it is a leftover response that we timed the request out previously
+ //
+ }
+ //
+ // We are going to retract these objects from memory
+ //
+ retract($response);
+
+end
+
+/*
+*
+* This rule responds to VFC Response Events
+*
+*/
+rule "${policyName}.VFC.RESPONSE"
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ $response : VFCResponse( requestId.toString() == $event.getRequestId().toString() )
+ then
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $operation, $opTimer, $response);
+
+ // Get the result of the operation
+ //
+ PolicyResult policyResult = $operation.onResponse($response);
+ if (policyResult != null) {
+ //
+ // This Operation has completed, construct a notification showing our results
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+ //
+ // Ensure the operation is complete
+ //
+ if ($operation.isOperationComplete() == true) {
+ //
+ // It is complete, remove it from memory
+ //
+ retract($operation);
+ //
+ // We must also retract the timer object
+ // NOTE: We could write a Rule to do this
+ //
+ retract($opTimer);
+ //
+ // Complete the operation
+ //
+ modify($manager) {finishOperation($operation)};
+ } else {
+ //
+ // Just doing this will kick off the LOCKED rule again
+ //
+ modify($operation) {};
+ }
+ } else {
+ //
+ // Its not finished yet (i.e. expecting more Response objects)
+ //
+ // Or possibly it is a leftover response that we timed the request out previously
+ //
+ }
+ //
+ // We are going to retract these objects from memory
+ //
+ retract($response);
+
+end
+
+/*
+*
+* This is the timer that manages the timeout for an individual operation.
+*
+*/
+rule "${policyName}.EVENT.MANAGER.OPERATION.TIMEOUT"
+ timer (expr: $to )
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
+ $opTimer : OperationTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), $to : getDelay() )
+ $lock : TargetLock (requestID == $event.getRequestId())
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+ logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event, $manager, $operation, $lock, $operation, $opTimer);
+
+ //
+ // Tell it its timed out
+ //
+ $operation.setOperationHasTimedOut();
+ //
+ // Create a notification for it ("DB Write - end operation")
+ //
+ VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+ notification.setMessage($operation.getOperationHistory());
+ notification.setHistory($operation.getHistory());
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ //
+ // Get rid of the timer
+ //
+ retract($opTimer);
+ //
+ // Ensure the operation is complete
+ //
+ if ($operation.isOperationComplete() == true) {
+ //
+ // It is complete, remove it from memory
+ //
+ retract($operation);
+ //
+ // Complete the operation
+ //
+ modify($manager) {finishOperation($operation)};
+ } else {
+ //
+ // Just doing this will kick off the LOCKED rule again
+ //
+ modify($operation) {};
+ }
+end
+
+/*
+*
+* This is the timer that manages the overall control loop timeout.
+*
+*/
+rule "${policyName}.EVENT.MANAGER.TIMEOUT"
+ timer (expr: $to )
+ when
+ $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
+ $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
+ $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
+ $clTimer : ControlLoopTimer ( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), $to : getDelay() )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
+
+ logger.debug("{}: {}: event={}",
+ $params.getClosedLoopControlName(), drools.getRule().getName(),
+ $event);
+ //
+ // Tell the Event Manager it has timed out
+ //
+ VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
+ if (notification != null) {
+ notification.setFrom("policy");
+ notification.setPolicyName(drools.getRule().getName());
+ notification.setPolicyScope("${policyScope}");
+ notification.setPolicyVersion("${policyVersion}");
+ //
+ // Let interested parties know
+ //
+ PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
+ }
+ //
+ // Retract the event
+ //
+ retract($event);
+end
+
+/*
+*
+* This rule cleans up the manager and other objects after an event has
+* been retracted.
+*
+*/
+rule "${policyName}.EVENT.MANAGER.CLEANUP"
+ when
+ $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestID() )
+ $clTimer : ControlLoopTimer ( closedLoopControlName == $clName, requestID == $requestId.toString() )
+ $operations : LinkedList()
+ from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName, onset.getRequestId() == $requestId ) )
+ $opTimers : LinkedList()
+ from collect( OperationTimer( closedLoopControlName == $clName, requestID == $requestId.toString() ) )
+ $locks : LinkedList()
+ from collect( TargetLock (requestID == $requestId) )
+ not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $clName, drools.getRule().getName());
+
+ logger.debug("{}: {}: manager={} clTimer={} operations={}",
+ $clName, drools.getRule().getName(),
+ $manager, $clTimer, $operations.size());
+
+ //
+ // Retract EVERYTHING
+ //
+ retract($manager);
+ retract($clTimer);
+
+ for(Object manager: $operations) {
+ retract((ControlLoopOperationManager) manager);
+ }
+ for(Object opTimer: $opTimers) {
+ retract((OperationTimer) opTimer);
+ }
+ for(Object lock: $locks) {
+ TargetLock tgt = (TargetLock) lock;
+ //
+ // Ensure we release the lock
+ //
+ PolicyGuard.unlockTarget(tgt);
+ retract(tgt);
+ }
+end
+
+/*
+*
+* This rule will clean up any rogue onsets where there is no
+* ControlLoopParams object corresponding to the onset event.
+*
+*/
+rule "${policyName}.EVENT.CLEANUP"
+ when
+ $event : VirtualControlLoopEvent( $clName: closedLoopControlName )
+ not ( Params( getClosedLoopControlName() == $clName) )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {}", $clName, drools.getRule().getName());
+ logger.debug("{}: {}: orphan onset event={}",
+ $clName, drools.getRule().getName(), $event);
+
+ retract($event);
+end
+
+/*
+*
+* When rules are deleted, the associated Params (and its subordinate objects)
+* remain in working memory, because there are no longer any rules to clean
+* them up. However, ANY time new rules are loaded, this rule will trigger
+* a clean-up of ALL Params, regardless of their name & yaml, thus removing
+* any that no longer have associated rules.
+* This has a higher salience so that we immediately check Params when the
+* rules change, before processing any events.
+*
+*/
+rule "${policyName}.PARAMS.CHECKUP"
+ salience 2
+ when
+ Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml);
+
+ ParamsCleaner cleaner = new ParamsCleaner();
+ cleaner.setClosedLoopControlName($clName);
+ cleaner.setControlLoopYaml($yaml);
+
+ insert(cleaner);
+end
+
+/*
+*
+* This rule removes "cleaner" objects for rules that are still active, thus
+* preventing the associated Params objects from being removed. Any cleaners
+* that are left after this rule has fired will cause their corresponding Params
+* to be removed.
+* This has a higher salience so that we discard the cleaner before it has
+* a chance to force the removal of the associated Params.
+*
+*/
+rule "${policyName}.CLEANER.ACTIVE"
+ salience 2
+ when
+ $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());
+
+ retract($cleaner);
+end
+
+/*
+*
+* This rule removes Params objects that no longer have associated rules; if a
+* Params still had associated rules, then the cleaner would have been removed
+* by those rules and thus this rule would not fire.
+* This has a higher salience so that we remove old Params before it causes any
+* events to be processed.
+*
+*/
+rule "${policyName}.PARAMS.CLEANUP"
+ salience 1
+ when
+ $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
+ ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), $params.getControlLoopYaml());
+
+ retract($params);
+
+ // Note: the cleaner may be needed for cleaning additional params, thus
+ // we do not retract it here - we'll leave that to another rule
+end
+
+/*
+*
+* This rule removes "cleaner" objects when they're no longer needed.
+*
+*/
+rule "${policyName}.CLEANER.CLEANUP"
+ when
+ $cleaner: ParamsCleaner( )
+ then
+
+ Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+ logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());
+
+ retract($cleaner);
+end
diff --git a/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopCoordinationTest.java b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopCoordinationTest.java
new file mode 100644
index 000000000..2d6279f32
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopCoordinationTest.java
@@ -0,0 +1,497 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * demo
+ * ================================================================================
+ * Copyright (C) 2018 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.template.demo.clc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.att.research.xacml.util.XACMLProperties;
+
+import com.google.gson.Gson;
+
+import java.io.IOException;
+import java.lang.StringBuilder;
+import java.net.URLEncoder;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.FactHandle;
+
+import org.onap.policy.appclcm.LcmRequest;
+import org.onap.policy.appclcm.LcmRequestWrapper;
+import org.onap.policy.appclcm.LcmResponse;
+import org.onap.policy.appclcm.LcmResponseWrapper;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicListener;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.onap.policy.controlloop.ControlLoopEventStatus;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.ControlLoopTargetType;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyEngine;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ControlLoopCoordinationTest implements TopicListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(ControlLoopCoordinationTest.class);
+
+ private static List<? extends TopicSink> noopTopics;
+
+ private static KieSession kieSession1;
+ private static KieSession kieSession2;
+ private static StringBuilder controlLoopOneName = new StringBuilder();
+ private static StringBuilder controlLoopTwoName = new StringBuilder();
+ private static String expectedDecision;
+
+ static {
+ /* Set environment properties */
+ Util.setAaiProps();
+ Util.setGuardPropsEmbedded();
+ Util.setPuProp();
+ }
+
+ /**
+ * Setup simulator.
+ */
+ @BeforeClass
+ public static void setUpSimulator() {
+ PolicyEngine.manager.configure(new Properties());
+ assertTrue(PolicyEngine.manager.start());
+ Properties noopSinkProperties = new Properties();
+ noopSinkProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-LCM-READ,POLICY-CL-MGT");
+ noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events", "org.onap.policy.appclcm.LcmRequestWrapper");
+ noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events.custom.gson",
+ "org.onap.policy.appclcm.util.Serialization,gson");
+ noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events",
+ "org.onap.policy.controlloop.VirtualControlLoopNotification");
+ noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson",
+ "org.onap.policy.controlloop.util.Serialization,gsonPretty");
+ noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties);
+
+ EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT",
+ "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null, null,
+ 1111);
+ EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-LCM-READ",
+ "org.onap.policy.appclcm.LcmRequestWrapper", new JsonProtocolFilter(), null, null, 1111);
+ try {
+ Util.buildAaiSim();
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ /*
+ * Start the kie sessions
+ */
+ try {
+ kieSession1 = startSession(
+ controlLoopOneName,
+ "src/main/resources/__closedLoopControlName__.drl",
+ "src/test/resources/yaml/policy_ControlLoop_SyntheticOne.yaml",
+ "service=ServiceDemo;resource=Res1Demo;type=operational",
+ "SyntheticControlLoopOnePolicy",
+ "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
+ kieSession2 = startSession(
+ controlLoopTwoName,
+ "src/main/resources/__closedLoopControlName__.drl",
+ "src/test/resources/yaml/policy_ControlLoop_SyntheticTwo.yaml",
+ "service=ServiceDemo;resource=Res1Demo;type=operational",
+ "SyntheticControlLoopTwoPolicy",
+ "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
+ } catch (IOException e) {
+ logger.debug("Could not create kieSession, exception {}", e.getMessage());
+ fail("Could not create kieSession");
+ }
+ }
+
+ /**
+ * Tear down simulator.
+ */
+ @AfterClass
+ public static void tearDownSimulator() {
+ /*
+ * Gracefully shut down the kie session
+ */
+ kieSession1.dispose();
+ kieSession2.dispose();
+
+ PolicyEngine.manager.stop();
+ HttpServletServer.factory.destroy();
+ PolicyController.factory.shutdown();
+ TopicEndpoint.manager.shutdown();
+ }
+
+ /**
+ * Set expected decision.
+ *
+ * @param ed the expected decision ("PERMIT" or "DENY")
+ */
+ public void expectedDecisionIs(String ed) {
+ expectedDecision = ed;
+ logger.info("Expected decision is {}", ed);
+ }
+
+ /**
+ * This method is used to simulate event messages from DCAE
+ * that start the control loop (onset message) or end the
+ * control loop (abatement message).
+ *
+ * @param controlLoopName the control loop name
+ * @param requestID the requestId for this event
+ * @param status could be onset or abated
+ * @param target the target name
+ * @param kieSession the kieSession to which this event is being sent
+ */
+ protected void sendEvent(String controlLoopName,
+ UUID requestId,
+ ControlLoopEventStatus status,
+ String target,
+ KieSession kieSession) {
+ logger.debug("sendEvent controlLoopName={}", controlLoopName);
+ VirtualControlLoopEvent event = new VirtualControlLoopEvent();
+ event.setClosedLoopControlName(controlLoopName);
+ event.setRequestId(requestId);
+ event.setTarget("generic-vnf.vnf-name");
+ event.setTargetType(ControlLoopTargetType.VNF);
+ event.setClosedLoopAlarmStart(Instant.now());
+ event.setAai(new HashMap<>());
+ event.getAai().put("generic-vnf.vnf-name", target);
+ event.setClosedLoopEventStatus(status);
+
+ Gson gson = new Gson();
+ String json = gson.toJson(event);
+ logger.debug("sendEvent {}", json);
+
+ kieSession.insert(event);
+ }
+
+
+ /**
+ * Simulate an event by inserting into kieSession and firing rules as needed.
+ *
+ * @param cles the ControlLoopEventStatus
+ * @param rid the request ID
+ * @param controlLoopName the control loop name
+ * @param kieSession the kieSession to which this event is being sent
+ * @param expectedDecision the expected decision
+ */
+ protected void simulateEvent(ControlLoopEventStatus cles,
+ UUID rid,
+ String controlLoopName,
+ String target,
+ KieSession kieSession,
+ String expectedDecision) {
+ int waitMillis = 5000;
+ //
+ // if onset, set expected decision
+ //
+ if (cles == ControlLoopEventStatus.ONSET) {
+ expectedDecisionIs(expectedDecision);
+ }
+ //
+ // simulate sending event
+ //
+ sendEvent(controlLoopName, rid, cles, target, kieSession);
+ kieSession.fireUntilHalt();
+ //
+ // get dump of database entries and log
+ //
+ List entries = Util.dumpDb();
+ assertNotNull(entries);
+ logger.debug("dumpDB, {} entries", entries.size());
+ for (Object entry : entries) {
+ logger.debug("{}", entry);
+ }
+ //
+ // we are done
+ //
+ logger.info("simulateEvent: done");
+ }
+
+ /**
+ * Simulate an onset event.
+ *
+ * @param rid the request ID
+ * @param controlLoopName the control loop name
+ * @param kieSession the kieSession to which this event is being sent
+ * @param expectedDecision the expected decision
+ */
+ public void simulateOnset(UUID rid,
+ String controlLoopName,
+ String target,
+ KieSession kieSession,
+ String expectedDecision) {
+ simulateEvent(ControlLoopEventStatus.ONSET, rid, controlLoopName, target, kieSession, expectedDecision);
+ }
+
+ /**
+ * Simulate an abated event.
+ *
+ * @param rid the request ID
+ * @param controlLoopName the control loop name
+ * @param kieSession the kieSession to which this event is being sent
+ */
+ public void simulateAbatement(UUID rid,
+ String controlLoopName,
+ String target,
+ KieSession kieSession) {
+ simulateEvent(ControlLoopEventStatus.ABATED, rid, controlLoopName, target, kieSession, null);
+ }
+
+ /**
+ * This method will start a kie session and instantiate the Policy Engine.
+ *
+ * @param droolsTemplate the DRL rules file
+ * @param yamlFile the yaml file containing the policies
+ * @param policyScope scope for policy
+ * @param policyName name of the policy
+ * @param policyVersion version of the policy
+ * @return the kieSession to be used to insert facts
+ * @throws IOException throws IO exception
+ */
+ private static KieSession startSession(StringBuilder controlLoopName,
+ String droolsTemplate,
+ String yamlFile,
+ String policyScope,
+ String policyName,
+ String policyVersion) throws IOException {
+
+ /*
+ * Load policies from yaml
+ */
+ Util.Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile);
+ assertNotNull(pair);
+ assertNotNull(pair.first);
+ assertNotNull(pair.first.getControlLoop());
+ assertNotNull(pair.first.getControlLoop().getControlLoopName());
+ assertTrue(!pair.first.getControlLoop().getControlLoopName().isEmpty());
+
+ controlLoopName.append(pair.first.getControlLoop().getControlLoopName());
+ String yamlContents = pair.second;
+
+ /*
+ * Construct a kie session
+ */
+ final KieSession kieSession = Util.buildContainer(droolsTemplate,
+ controlLoopName.toString(),
+ policyScope,
+ policyName,
+ policyVersion,
+ URLEncoder.encode(yamlContents, "UTF-8"));
+
+ /*
+ * Retrieve the Policy Engine
+ */
+
+ logger.debug("============");
+ logger.debug(URLEncoder.encode(yamlContents, "UTF-8"));
+ logger.debug("============");
+
+ return kieSession;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.onap.policy.drools.PolicyEngineListener#newEventNotification(java.lang.String)
+ */
+ @Override
+ public void onTopicEvent(CommInfrastructure commType, String topic, String event) {
+ /*
+ * Pull the object that was sent out to DMAAP and make sure it is a ControlLoopNoticiation
+ * of type active
+ */
+ Object obj = null;
+ if ("POLICY-CL-MGT".equals(topic)) {
+ obj = org.onap.policy.controlloop.util.Serialization.gsonJunit.fromJson(event,
+ org.onap.policy.controlloop.VirtualControlLoopNotification.class);
+ } else if ("APPC-LCM-READ".equals(topic)) {
+ obj = org.onap.policy.appclcm.util.Serialization.gsonJunit.fromJson(event,
+ org.onap.policy.appclcm.LcmRequestWrapper.class);
+ }
+ assertNotNull(obj);
+ if (obj instanceof VirtualControlLoopNotification) {
+ VirtualControlLoopNotification notification = (VirtualControlLoopNotification) obj;
+ String policyName = notification.getPolicyName();
+ if (policyName.endsWith("EVENT")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ assertTrue(ControlLoopNotificationType.ACTIVE.equals(notification.getNotification()));
+ } else if (policyName.endsWith("GUARD_NOT_YET_QUERIED")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.getNotification()));
+ assertNotNull(notification.getMessage());
+ assertTrue(notification.getMessage().startsWith("Sending guard query"));
+ } else if (policyName.endsWith("GUARD.RESPONSE")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.getNotification()));
+ assertNotNull(notification.getMessage());
+ // THESE ARE THE MOST CRITICAL ASSERTS
+ // TEST IF GUARD.RESPONSE IS CORRECT
+ logger.debug("Testing whether decision was {} as expected", expectedDecision);
+ assertTrue(notification.getMessage().toUpperCase().endsWith(expectedDecision));
+ } else if (policyName.endsWith("GUARD_PERMITTED")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ assertEquals(ControlLoopNotificationType.OPERATION,notification.getNotification());
+ assertNotNull(notification.getMessage());
+ assertTrue(notification.getMessage().startsWith("actor=APPC"));
+ } else if (policyName.endsWith("OPERATION.TIMEOUT")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ kieSession1.halt();
+ kieSession2.halt();
+ logger.debug("The operation timed out");
+ fail("Operation Timed Out");
+ } else if (policyName.endsWith("APPC.LCM.RESPONSE")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ assertTrue(ControlLoopNotificationType.OPERATION_SUCCESS.equals(notification.getNotification()));
+ assertNotNull(notification.getMessage());
+ assertTrue(notification.getMessage().startsWith("actor=APPC"));
+ } else if (policyName.endsWith("EVENT.MANAGER")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ if (notification.getMessage().endsWith("Closing the control loop.")
+ || notification.getMessage().equals("Waiting for abatement")) {
+ if (policyName.startsWith(controlLoopOneName.toString())) {
+ logger.debug("Halting kieSession1");
+ kieSession1.halt();
+ } else if (policyName.startsWith(controlLoopTwoName.toString())) {
+ logger.debug("Halting kieSession2");
+ kieSession2.halt();
+ } else {
+ fail("Unknown ControlLoop");
+ }
+ }
+ } else if (policyName.endsWith("EVENT.MANAGER.TIMEOUT")) {
+ logger.debug("Rule Fired: " + notification.getPolicyName());
+ kieSession1.halt();
+ kieSession2.halt();
+ logger.debug("The control loop timed out");
+ fail("Control Loop Timed Out");
+ }
+ } else if (obj instanceof LcmRequestWrapper) {
+ /*
+ * The request should be of type LCMRequestWrapper and the subrequestid should be 1
+ */
+ LcmRequestWrapper dmaapRequest = (LcmRequestWrapper) obj;
+ LcmRequest appcRequest = dmaapRequest.getBody();
+ assertEquals(appcRequest.getCommonHeader().getSubRequestId(),"1");
+
+ logger.debug("\n============ APPC received the request!!! ===========\n");
+
+ /*
+ * Simulate a success response from APPC and insert the response into the working memory
+ */
+ LcmResponseWrapper dmaapResponse = new LcmResponseWrapper();
+ LcmResponse appcResponse = new LcmResponse(appcRequest);
+ appcResponse.getStatus().setCode(400);
+ appcResponse.getStatus().setMessage("AppC success");
+ dmaapResponse.setBody(appcResponse);
+ kieSession1.insert(dmaapResponse);
+ kieSession2.insert(dmaapResponse);
+ }
+ }
+
+ /**
+ * This method will dump all the facts in the working memory.
+ *
+ * @param kieSession the session containing the facts
+ */
+ public void dumpFacts(KieSession kieSession) {
+ logger.debug("Fact Count: {}", kieSession.getFactCount());
+ for (FactHandle handle : kieSession.getFactHandles()) {
+ logger.debug("FACT: {}", handle);
+ }
+ }
+
+ /**
+ * Test that SyntheticControlLoopOne blocks SyntheticControlLoopTwo
+ * is enforced correctly.
+ */
+ @Test
+ public void testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo() throws InterruptedException {
+ logger.info("Beginning testSyntheticControlLoopOneBlocksSyntheticControlLoopTwo");
+ /*
+ * Allows the PolicyEngine to callback to this object to
+ * notify that there is an event ready to be pulled
+ * from the queue
+ */
+ for (TopicSink sink : noopTopics) {
+ assertTrue(sink.start());
+ sink.register(this);
+ }
+
+ /*
+ * Create unique requestIds
+ */
+ final UUID requestId1 = UUID.randomUUID();
+ final UUID requestId2 = UUID.randomUUID();
+ final UUID requestId3 = UUID.randomUUID();
+ final UUID requestId4 = UUID.randomUUID();
+ final UUID requestId5 = UUID.randomUUID();
+ final String cl1 = controlLoopOneName.toString();
+ final String cl2 = controlLoopTwoName.toString();
+ final String t1 = "TARGET_1";
+ final String t2 = "TARGET_2";
+
+ logger.info("@@@@@@@@@@ cl2 ONSET t1 (Success) @@@@@@@@@@");
+ simulateOnset(requestId1, cl2, t1, kieSession2,"PERMIT");
+ logger.info("@@@@@@@@@@ cl1 ONSET t1 @@@@@@@@@@");
+ simulateOnset(requestId2, cl1, t1, kieSession1,"PERMIT");
+ logger.info("@@@@@@@@@@ cl2 ABATED t1 @@@@@@@@@@");
+ simulateAbatement(requestId1, cl2, t1, kieSession2);
+ logger.info("@@@@@@@@@@ cl2 ONSET t1 (Fail) @@@@@@@@@@");
+ simulateOnset(requestId3, cl2, t1, kieSession2,"DENY");
+ logger.info("@@@@@@@@@@ cl2 ONSET t2 (Success) @@@@@@@@@@");
+ simulateOnset(requestId4, cl2, t2, kieSession2,"PERMIT");
+ logger.info("@@@@@@@@@@ cl2 ABATED t2 @@@@@@@@@@");
+ simulateAbatement(requestId4, cl2, t2, kieSession2);
+ logger.info("@@@@@@@@@@ cl1 ABATED t1 @@@@@@@@@@");
+ simulateAbatement(requestId2, cl1, t1, kieSession1);
+ logger.info("@@@@@@@@@@ cl2 ONSET t1 (Success) @@@@@@@@@@");
+ simulateOnset(requestId5, cl2, t1, kieSession2,"PERMIT");
+ logger.info("@@@@@@@@@@ cl2 ABATED t1 @@@@@@@@@@");
+ simulateAbatement(requestId5, cl2, t1, kieSession2);
+
+ /*
+ * Print what's left in memory
+ */
+ dumpFacts(kieSession1);
+ dumpFacts(kieSession2);
+ }
+}
+
diff --git a/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java
new file mode 100644
index 000000000..5b5aa2d9c
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java
@@ -0,0 +1,297 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * demo
+ * ================================================================================
+ * Copyright (C) 2018 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.template.demo.clc;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+
+import org.apache.commons.io.IOUtils;
+import org.kie.api.KieServices;
+import org.kie.api.builder.KieBuilder;
+import org.kie.api.builder.KieFileSystem;
+import org.kie.api.builder.Message;
+import org.kie.api.builder.ReleaseId;
+import org.kie.api.builder.Results;
+import org.kie.api.builder.model.KieModuleModel;
+import org.kie.api.runtime.KieContainer;
+import org.kie.api.runtime.KieSession;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.controlloop.policy.guard.ControlLoopGuard;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.guard.PolicyGuardYamlToXacml;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+
+public final class Util {
+
+ private static final String OPSHISTPUPROP = "OperationsHistoryPU";
+ private static final Logger logger = LoggerFactory.getLogger(Util.class);
+
+ public static class Pair<A, B> {
+ public final A first;
+ public final B second;
+
+ public Pair(A first, B second) {
+ this.first = first;
+ this.second = second;
+ }
+ }
+
+ /**
+ * Load YAML.
+ *
+ * @param testFile test file to load
+ * @return the Pair of a policy and the yaml contents
+ */
+ public static Pair<ControlLoopPolicy, String> loadYaml(String testFile) {
+ try (InputStream is = new FileInputStream(new File(testFile))) {
+ String contents = IOUtils.toString(is, StandardCharsets.UTF_8);
+ //
+ // Read the yaml into our Java Object
+ //
+ Yaml yaml = new Yaml(new Constructor(ControlLoopPolicy.class));
+ Object obj = yaml.load(contents);
+
+ logger.debug(contents);
+
+ return new Pair<ControlLoopPolicy, String>((ControlLoopPolicy) obj, contents);
+ } catch (IOException e) {
+ fail(e.getLocalizedMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Load the YAML guard policy.
+ *
+ * @param testFile the test file to load
+ * @return return the guard object
+ */
+ public static ControlLoopGuard loadYamlGuard(String testFile) {
+ try (InputStream is = new FileInputStream(new File(testFile))) {
+ String contents = IOUtils.toString(is, StandardCharsets.UTF_8);
+ //
+ // Read the yaml into our Java Object
+ //
+ Yaml yaml = new Yaml(new Constructor(ControlLoopGuard.class));
+ Object obj = yaml.load(contents);
+ return (ControlLoopGuard) obj;
+ } catch (IOException e) {
+ fail(e.getLocalizedMessage());
+ }
+ return null;
+ }
+
+ public static HttpServletServer buildAaiSim() throws InterruptedException, IOException {
+ return org.onap.policy.simulators.Util.buildAaiSim();
+ }
+
+ private static String generatePolicy(String ruleContents,
+ String closedLoopControlName,
+ String policyScope,
+ String policyName,
+ String policyVersion,
+ String controlLoopYaml) {
+
+ Pattern pattern = Pattern.compile("\\$\\{closedLoopControlName\\}");
+ Matcher matcher = pattern.matcher(ruleContents);
+ ruleContents = matcher.replaceAll(closedLoopControlName);
+
+ pattern = Pattern.compile("\\$\\{policyScope\\}");
+ matcher = pattern.matcher(ruleContents);
+ ruleContents = matcher.replaceAll(policyScope);
+
+ pattern = Pattern.compile("\\$\\{policyName\\}");
+ matcher = pattern.matcher(ruleContents);
+ ruleContents = matcher.replaceAll(policyName);
+
+ pattern = Pattern.compile("\\$\\{policyVersion\\}");
+ matcher = pattern.matcher(ruleContents);
+ ruleContents = matcher.replaceAll(policyVersion);
+
+ pattern = Pattern.compile("\\$\\{controlLoopYaml\\}");
+ matcher = pattern.matcher(ruleContents);
+ ruleContents = matcher.replaceAll(controlLoopYaml);
+
+ return ruleContents;
+ }
+
+ /**
+ * Build the container.
+ *
+ * @param droolsTemplate template
+ * @param closedLoopControlName control loop id
+ * @param policyScope policy scope
+ * @param policyName policy name
+ * @param policyVersion policy version
+ * @param yamlSpecification incoming yaml specification
+ * @return the Kie session
+ * @throws IOException if the container cannot be built
+ */
+ public static KieSession buildContainer(String droolsTemplate, String closedLoopControlName,
+ String policyScope, String policyName, String policyVersion,
+ String yamlSpecification) throws IOException {
+ //
+ // Get our Drools Kie factory
+ //
+ KieServices ks = KieServices.Factory.get();
+
+ KieModuleModel kieModule = ks.newKieModuleModel();
+
+ logger.debug("KMODULE:" + System.lineSeparator() + kieModule.toXML());
+
+ //
+ // Generate our drools rule from our template
+ //
+ KieFileSystem kfs = ks.newKieFileSystem();
+
+ kfs.writeKModuleXML(kieModule.toXML());
+ {
+ Path rule = Paths.get(droolsTemplate);
+ String ruleTemplate = new String(Files.readAllBytes(rule));
+ String drlContents = generatePolicy(ruleTemplate,
+ closedLoopControlName,
+ policyScope,
+ policyName,
+ policyVersion,
+ yamlSpecification);
+
+ kfs.write("src/main/resources/" + policyName + ".drl",
+ ks.getResources().newByteArrayResource(drlContents.getBytes()));
+ }
+ //
+ // Compile the rule
+ //
+ KieBuilder builder = ks.newKieBuilder(kfs).buildAll();
+ Results results = builder.getResults();
+ if (results.hasMessages(Message.Level.ERROR)) {
+ for (Message msg : results.getMessages()) {
+ logger.error(msg.toString());
+ }
+ throw new RuntimeException("Drools Rule has Errors");
+ }
+ for (Message msg : results.getMessages()) {
+ logger.debug(msg.toString());
+ }
+ //
+ // Create our kie Session and container
+ //
+ ReleaseId releaseId = ks.getRepository().getDefaultReleaseId();
+ logger.debug(releaseId.toString());
+ KieContainer keyContainer = ks.newKieContainer(releaseId);
+
+ return keyContainer.newKieSession();
+ }
+
+ /**
+ * Set the A&AI properties.
+ */
+ public static void setAaiProps() {
+ PolicyEngine.manager.setEnvironmentProperty("aai.url", "http://localhost:6666");
+ PolicyEngine.manager.setEnvironmentProperty("aai.username", "AAI");
+ PolicyEngine.manager.setEnvironmentProperty("aai.password", "AAI");
+ }
+
+ /**
+ * Set the Guard properties to use embedded XACML PDPEngine.
+ */
+ public static void setGuardPropsEmbedded() {
+ /*
+ * Guard PDP-x connection Properties. No URL specified -> use embedded PDPEngine.
+ */
+ PolicyEngine.manager.setEnvironmentProperty("prop.guard.propfile",
+ "src/test/resources/xacml/xacml_guard_clc.properties");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_USER, "python");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_PASS, "test");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_CLIENT_USER, "python");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_CLIENT_PASS, "test");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_ENV, "TEST");
+ PolicyEngine.manager.setEnvironmentProperty(org.onap.policy.guard.Util.PROP_GUARD_DISABLED, "false");
+ }
+
+ /**
+ * Set the operation history properties.
+ */
+ public static void setPuProp() {
+ System.setProperty(OPSHISTPUPROP, "TestOperationsHistoryPU");
+ }
+
+ /**
+ * Dump the contents of the History database.
+ *
+ * @return a list of the database entries
+ */
+ public static List dumpDb() {
+ //
+ // Connect to in-mem db
+ //
+ EntityManagerFactory emf = Persistence.createEntityManagerFactory("TestOperationsHistoryPU");
+ EntityManager em = emf.createEntityManager();
+ //
+ // Create query
+ //
+ String sql = "select * from operationshistory10";
+ Query nq = em.createNativeQuery(sql);
+ List results = null;
+ //
+ // Execute query
+ //
+ try {
+ results = nq.getResultList();
+ } catch (Exception ex) {
+ logger.error("getStatusFromDB threw: ", ex);
+ //
+ // Clean up and return null
+ //
+ em.close();
+ emf.close();
+ return null;
+ }
+ //
+ // Clean up and return results
+ //
+ em.close();
+ emf.close();
+ return results;
+ }
+}
diff --git a/controlloop/templates/template.demo.clc/src/test/resources/META-INF/persistence.xml b/controlloop/templates/template.demo.clc/src/test/resources/META-INF/persistence.xml
new file mode 100644
index 000000000..808cef9b6
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ drools-applications
+ ================================================================================
+ Copyright (C) 2018 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 version="2.1"
+ xmlns="http://xmlns.jcp.org/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
+
+ <!-- In-mem DB for junit -->
+ <persistence-unit name="TestOperationsHistoryPU"
+ transaction-type="RESOURCE_LOCAL">
+ <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+ <class>org.onap.policy.controlloop.eventmanager.OperationsHistoryDbEntry</class>
+ <properties>
+ <property name="eclipselink.ddl-generation"
+ value="create-tables" />
+ <property name="javax.persistence.jdbc.driver"
+ value="org.h2.Driver" />
+ <property name="javax.persistence.jdbc.url"
+ value="jdbc:h2:mem:test" />
+ <property name="javax.persistence.jdbc.user"
+ value="sa" />
+ <property name="javax.persistence.jdbc.password"
+ value="" />
+ <property name="eclipselink.logging.level"
+ value="CONFIG" />
+ </properties>
+ </persistence-unit>
+
+
+</persistence>
diff --git a/controlloop/templates/template.demo.clc/src/test/resources/xacml/synthetic_control_loop_one_blocks_synthetic_control_loop_two.xml b/controlloop/templates/template.demo.clc/src/test/resources/xacml/synthetic_control_loop_one_blocks_synthetic_control_loop_two.xml
new file mode 100644
index 000000000..f503a2b11
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/resources/xacml/synthetic_control_loop_one_blocks_synthetic_control_loop_two.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ============LICENSE_START=======================================================
+ drools-applications
+ ================================================================================
+ Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+ -->
+<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicyId="urn:com:att:xacml:policy:id:son:guard:5" Version="1" RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:permit-unless-deny">
+<Description>Policy for first_blocks_second coordination (if first running and second requests to run, deny second).</Description>
+<Target>
+ <AnyOf>
+ <AllOf>
+ <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
+ <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">SyntheticControlLoopTwo</AttributeValue>
+ <!-- value should be autofilled by yaml from a xacml template -->
+ <AttributeDesignator Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" AttributeId="urn:oasis:names:tc:xacml:1.0:clname:clname-id" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false"/>
+ </Match>
+ </AllOf>
+ </AnyOf>
+</Target>
+
+<Rule RuleId="urn:com:att:xacml:rule:id:1" Effect="Deny">
+ <Description>First Is Running</Description>
+ <Condition>
+ <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
+ <VariableReference VariableId="clc_status"/>
+ <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Success</AttributeValue>
+ </Apply>
+ </Condition>
+</Rule>
+
+<!-- 'action_one' should be autofilled by yaml from a xacml template -->
+<VariableDefinition VariableId="clc_status">
+ <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
+ <AttributeDesignator Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" AttributeId="com:att:research:xacml:test:sql:resource:operations:status" DataType="http://www.w3.org/2001/XMLSchema#string" Issuer="org:onap:policy:guard:getstatus:clname:SyntheticControlLoopOne" MustBePresent="false"/>
+ </Apply>
+</VariableDefinition>
+</Policy>
diff --git a/controlloop/templates/template.demo.clc/src/test/resources/xacml/xacml_guard_clc.properties b/controlloop/templates/template.demo.clc/src/test/resources/xacml/xacml_guard_clc.properties
new file mode 100644
index 000000000..5f5e0c8b2
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/resources/xacml/xacml_guard_clc.properties
@@ -0,0 +1,65 @@
+###
+# ============LICENSE_START=======================================================
+# ONAP
+# ================================================================================
+# Copyright (C) 2018 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=========================================================
+###
+#
+#
+# This files defines PIPs that will be used by XACML Guard Policies. One PIP per time window (5 min, 10min,...,1 month).
+#
+#
+#
+
+#
+# Default XACML Properties File
+# Standard API Factories
+#
+xacml.dataTypeFactory=com.att.research.xacml.std.StdDataTypeFactory
+xacml.pdpEngineFactory=com.att.research.xacmlatt.pdp.ATTPDPEngineFactory
+xacml.pepEngineFactory=com.att.research.xacml.std.pep.StdEngineFactory
+xacml.pipFinderFactory=com.att.research.xacml.std.pip.StdPIPFinderFactory
+xacml.traceEngineFactory=com.att.research.xacml.std.trace.LoggingTraceEngineFactory
+#
+# AT&T PDP Implementation Factories
+#
+xacml.att.evaluationContextFactory=com.att.research.xacmlatt.pdp.std.StdEvaluationContextFactory
+xacml.att.combiningAlgorithmFactory=com.att.research.xacmlatt.pdp.std.StdCombiningAlgorithmFactory
+xacml.att.functionDefinitionFactory=com.att.research.xacmlatt.pdp.std.StdFunctionDefinitionFactory
+xacml.att.policyFinderFactory=com.att.research.xacmlatt.pdp.std.StdPolicyFinderFactory
+
+
+#
+# NOTE: If you are testing against a RESTful PDP, then the PDP must be configured with the
+# policies and PIP configuration as defined below. Otherwise, this is the configuration that
+# the embedded PDP uses.
+#
+
+# In case we have multiple applicable Guard policies, we will deny if any of them denies.
+#xacml.att.policyFinderFactory.combineRootPolicies=urn:com:att:xacml:3.0:policy-combining-algorithm:combined-deny-overrides
+xacml.att.policyFinderFactory.combineRootPolicies=urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:permit-unless-deny
+
+
+# Policies to load
+#
+xacml.rootPolicies=p1
+p1.file=src/test/resources/xacml/synthetic_control_loop_one_blocks_synthetic_control_loop_two.xml
+
+# PIP Engine Definition
+#
+xacml.pip.engines=getstatus
+getstatus.classname=org.onap.policy.guard.PipEngineGetStatus
+getstatus.issuer=org:onap:policy:guard:getstatus
diff --git a/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticOne.yaml b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticOne.yaml
new file mode 100644
index 000000000..2a74843a0
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticOne.yaml
@@ -0,0 +1,43 @@
+# Copyright 2018 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.
+controlLoop:
+ version: 2.0.0
+ controlLoopName: SyntheticControlLoopOne
+ services:
+ - serviceName: ServiceSimple
+ resources:
+ - resourceName: res1
+ resourceType: VFC
+ - resourceName: res2
+ resourceType: VFC
+ trigger_policy: unique-policy-id-1
+ timeout: 2500
+ abatement: true
+
+policies:
+ - id: unique-policy-id-1
+ name: SyntheticControlLoopOnePolicy
+ description:
+ actor: APPC
+ recipe: action_one
+ target:
+ type: VNF
+ retry: 3
+ timeout: 200
+ success: final_success
+ failure: final_failure
+ failure_timeout: final_failure_timeout
+ failure_retries: final_failure_retries
+ failure_guard: final_failure_guard
+ failure_exception: final_failure_exception
diff --git a/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticTwo.yaml b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticTwo.yaml
new file mode 100644
index 000000000..12e1bdf2b
--- /dev/null
+++ b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_SyntheticTwo.yaml
@@ -0,0 +1,43 @@
+# Copyright 2018 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.
+controlLoop:
+ version: 2.0.0
+ controlLoopName: SyntheticControlLoopTwo
+ services:
+ - serviceName: ServiceSimple
+ resources:
+ - resourceName: res1
+ resourceType: VFC
+ - resourceName: res2
+ resourceType: VFC
+ trigger_policy: unique-policy-id-1
+ timeout: 2500
+ abatement: true
+
+policies:
+ - id: unique-policy-id-1
+ name: SyntheticControlLoopTwoPolicy
+ description:
+ actor: APPC
+ recipe: action_two
+ target:
+ type: VNF
+ retry: 3
+ timeout: 200
+ success: final_success
+ failure: final_failure
+ failure_timeout: final_failure_timeout
+ failure_retries: final_failure_retries
+ failure_guard: final_failure_guard
+ failure_exception: final_failure_exception
diff --git a/controlloop/templates/template.demo/src/test/resources/xacml/xacml_guard.properties b/controlloop/templates/template.demo/src/test/resources/xacml/xacml_guard.properties
index d05648bcb..0486f9581 100644
--- a/controlloop/templates/template.demo/src/test/resources/xacml/xacml_guard.properties
+++ b/controlloop/templates/template.demo/src/test/resources/xacml/xacml_guard.properties
@@ -66,6 +66,6 @@ p5.file=src/test/resources/xacml/autogenerated_blacklist.xml
# PIP Engine Definition
#
xacml.pip.engines=historydb
-historydb.classname=org.onap.policy.guard.PIPEngineGetHistory
+historydb.classname=org.onap.policy.guard.PipEngineGetHistory
historydb.issuer=com:att:research:xacml:guard:historydb