Integrate cds actor service provider

* Add request enrichment with aai service instance id and vnf id
* Implement cds grpc request
* Implement VfwControlLoopCdsTest

Issue-ID: POLICY-2088
Signed-off-by: Bruno Sakoto <bruno.sakoto@bell.ca>
Change-Id: Ib44d447d6a3a70ff800a5760032b676fdfa32d9c
diff --git a/controlloop/common/controller-usecases/pom.xml b/controlloop/common/controller-usecases/pom.xml
index cac01b8..63c9243 100644
--- a/controlloop/common/controller-usecases/pom.xml
+++ b/controlloop/common/controller-usecases/pom.xml
@@ -5,6 +5,7 @@
   ================================================================================
   Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
   Modifications Copyright (C) 2019 Nordix Foundation.
+  Modifications Copyright (C) 2019 Bell Canada.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -107,6 +108,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>cds</artifactId>
+            <version>${policy.models.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
             <artifactId>eventmanager</artifactId>
             <version>${project.version}</version>
@@ -161,6 +168,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+            <artifactId>actor.cds</artifactId>
+            <version>${policy.models.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.onap.policy.models.policy-models-interactions</groupId>
             <artifactId>model-yaml</artifactId>
             <version>${policy.models.version}</version>
@@ -173,6 +185,11 @@
             <scope>provided</scope>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/controlloop/common/controller-usecases/src/main/resources/usecases.drl b/controlloop/common/controller-usecases/src/main/resources/usecases.drl
index 02161b8..4adf740 100644
--- a/controlloop/common/controller-usecases/src/main/resources/usecases.drl
+++ b/controlloop/common/controller-usecases/src/main/resources/usecases.drl
@@ -3,6 +3,7 @@
  * ONAP
  * ================================================================================
  * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +21,7 @@
 
 package org.onap.policy.controlloop;
 
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
 import org.onap.policy.controlloop.params.ControlLoopParams;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.VirtualControlLoopNotification;
@@ -35,6 +37,9 @@
 import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
 import org.onap.policy.controlloop.utils.ControlLoopUtils;
 import org.onap.policy.controlloop.actor.so.SoActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider.CdsActorServiceManager;
+import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
 import org.onap.policy.aai.AaiNqResponseWrapper;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.appc.Request;
@@ -45,6 +50,9 @@
 import org.onap.policy.appclcm.LcmRequest;
 import org.onap.policy.appclcm.LcmResponse;
 import org.onap.policy.appclcm.LcmCommonHeader;
+import org.onap.policy.cds.CdsResponse;
+import org.onap.policy.cds.client.CdsProcessorGrpcClient;
+import org.onap.policy.cds.properties.CdsServerProperties;
 import org.onap.policy.sdnr.PciRequestWrapper;
 import org.onap.policy.sdnr.PciResponseWrapper;
 import org.onap.policy.sdnr.PciRequest;
@@ -78,6 +86,7 @@
 import java.util.LinkedList;
 import java.util.Iterator;
 
+
 import org.onap.policy.drools.system.PolicyEngineConstants;
 
 /*
@@ -621,6 +630,33 @@
                       t.start();
                   }
                   break;                 
+
+              case "CDS":
+
+                  if(request instanceof ExecutionServiceInput) {
+
+                      // Instantiate cds actor, service manager and grpc properties
+
+                      CdsActorServiceProvider cdsActor = new CdsActorServiceProvider();
+                      CdsActorServiceProvider.CdsActorServiceManager cdsActorServiceManager = cdsActor.new CdsActorServiceManager();
+
+                      CdsServerProperties props = new CdsServerProperties();
+                      props.setHost(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcHost"));
+                      props.setPort(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPort")));
+                      props.setUsername(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcUsername"));
+                      props.setPassword(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPassword"));
+                      props.setTimeout(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcTimeout")));
+
+                      // Send cds grpc request
+                      try (CdsProcessorGrpcClient client = new CdsProcessorGrpcClient(cdsActorServiceManager, props)) {
+                          CdsResponse response =
+                                cdsActorServiceManager.sendRequestToCds(client, props, (ExecutionServiceInput) request);
+                          logger.info("CDS response: {}", response);
+                          insert(response);
+                      }
+                  }
+                  break;
+
           }
         } else {
           //
@@ -1302,6 +1338,87 @@
 
 end
 
+
+/**
+ * This rule responds to CDS Response Events
+ */
+rule "${policyName}.CDS.RESPONSE"
+    when
+        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
+        $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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
+            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
+        $lock : TargetLock (requestId == $event.getRequestId())
+        $response : CdsResponse( requestId == $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);
+
+            // The operation has completed, construct a notification showing our results
+            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+            notification.setFrom("policy");
+            notification.setPolicyName(drools.getRule().getName());
+            notification.setPolicyScope($params.getPolicyScope());
+            notification.setPolicyVersion($params.getPolicyVersion());
+            notification.setMessage($operation.getOperationHistory());
+            notification.setHistory($operation.getHistory());
+            notification.setNotification(
+                ($response != null && CdsActorConstants.SUCCESS.equals($response.getStatus()))
+                    ? ControlLoopNotificationType.OPERATION_SUCCESS : ControlLoopNotificationType.OPERATION_FAILURE);
+
+            // Send the notification
+            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+
+            // Ensure the operation is complete
+            if ($operation.isOperationComplete()) {
+
+                // 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
+            logger.info(
+                "policyResult is null"
+                    + "\nIt's not finished yet (i.e. expecting more Response objects)"
+                    + "\nOr 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 manages a single timer.
diff --git a/controlloop/common/eventmanager/pom.xml b/controlloop/common/eventmanager/pom.xml
index a7cb753..a020e5e 100644
--- a/controlloop/common/eventmanager/pom.xml
+++ b/controlloop/common/eventmanager/pom.xml
@@ -4,6 +4,7 @@
   ================================================================================
   Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
   Modifications Copyright (C) 2019 Nordix Foundation.
+  Modifications Copyright (C) 2019 Bell Canada.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -85,6 +86,12 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+      <artifactId>actor.cds</artifactId>
+      <version>${policy.models.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>org.onap.policy.drools-applications.controlloop.common</groupId>
       <artifactId>database</artifactId>
       <version>${project.version}</version>
@@ -132,6 +139,16 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+      <artifactId>cds</artifactId>
+      <version>${policy.models.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.ccsdk.cds.components</groupId>
+      <artifactId>proto-definition</artifactId>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
index aaccef6..f6cfe55 100644
--- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
+++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
@@ -1056,7 +1056,7 @@
      * This function calls Aai Custom Query and responds with the AaiCqResponse.
      *
      * @param event input event
-     * @return AaiCqResponse Response from Aai for custom query
+     * @return AaiCqResponse Response from Aai for custom query. Can not be null.
      * @throws AaiException if error occurs
      */
     public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
@@ -1088,6 +1088,11 @@
 
         response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
                 vserverId);
+
+        if (response == null) {
+            throw new AaiException("Aai response is undefined");
+        }
+
         return response;
 
     }
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java
index 293fa43..eb19019 100644
--- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java
+++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java
@@ -5,6 +5,7 @@
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
  * Modifications Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved.
  * Modifications Copyright (C) 2019 Tech Mahindra
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,18 +27,26 @@
 import java.sql.Timestamp;
 import java.time.Instant;
 import java.util.AbstractMap;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Optional;
 import java.util.Properties;
 import javax.persistence.EntityManager;
 import javax.persistence.Persistence;
+
 import org.eclipse.persistence.config.PersistenceUnitProperties;
 import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.ServiceInstance;
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
+import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.aai.util.AaiException;
 import org.onap.policy.appc.Response;
 import org.onap.policy.appc.ResponseCode;
 import org.onap.policy.appclcm.LcmResponseWrapper;
+import org.onap.policy.cds.CdsResponse;
 import org.onap.policy.controlloop.ControlLoopEvent;
 import org.onap.policy.controlloop.ControlLoopException;
 import org.onap.policy.controlloop.ControlLoopOperation;
@@ -45,12 +54,15 @@
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.actor.appc.AppcActorServiceProvider;
 import org.onap.policy.controlloop.actor.appclcm.AppcLcmActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
 import org.onap.policy.controlloop.actor.sdnc.SdncActorServiceProvider;
 import org.onap.policy.controlloop.actor.sdnr.SdnrActorServiceProvider;
 import org.onap.policy.controlloop.actor.so.SoActorServiceProvider;
 import org.onap.policy.controlloop.actor.vfc.VfcActorServiceProvider;
 import org.onap.policy.controlloop.policy.Policy;
 import org.onap.policy.controlloop.policy.PolicyResult;
+import org.onap.policy.controlloop.policy.TargetType;
 import org.onap.policy.database.operationshistory.Dbao;
 import org.onap.policy.drools.system.PolicyEngineConstants;
 import org.onap.policy.guard.Util;
@@ -72,6 +84,8 @@
     private static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
     private static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
 
+    private static final String AAI_SERVICE_INSTANCE_ID_KEY = "service-instance.service-instance-id";
+
     //
     // These properties are not changeable, but accessible
     // for Drools Rule statements.
@@ -89,6 +103,7 @@
     private ControlLoopEventManager eventManager = null;
     private String targetEntity;
     private String guardApprovalStatus = "NONE";// "NONE", "PERMIT", "DENY"
+    private AaiCqResponse aaiCqResponse;
     private transient Object operationRequest;
 
     /**
@@ -106,10 +121,15 @@
         this.policy = policy;
         this.guardApprovalStatus = "NONE";
         this.eventManager = em;
-        this.targetEntity = getTarget(policy);
 
         try {
 
+            if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
+                this.aaiCqResponse = this.eventManager.getCqResponse((VirtualControlLoopEvent) onset);
+            }
+
+            this.targetEntity = getTarget(policy);
+
             //
             // Let's make a sanity check
             //
@@ -125,6 +145,8 @@
                     break;
                 case "SDNC":
                     break;
+                case "CDS":
+                    break;
                 default:
                     throw new ControlLoopException("ControlLoopEventManager: policy has an unknown actor.");
             }
@@ -142,8 +164,8 @@
              * vnf-id is retrieved by a named query to A&AI.
              */
             if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
-                GenericVnf genvnf = this.eventManager.getCqResponse((VirtualControlLoopEvent) onset)
-                        .getGenericVnfByModelInvariantId(policy.getTarget().getResourceID());
+                GenericVnf genvnf =
+                        this.aaiCqResponse.getGenericVnfByModelInvariantId(policy.getTarget().getResourceID());
                 if (genvnf == null) {
                     logger.info("Target entity could not be found");
                     throw new AaiException("Target vnf-id could not be found");
@@ -261,8 +283,7 @@
             try {
                 String vnfId;
                 if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
-                    vnfId = this.eventManager.getCqResponse((VirtualControlLoopEvent) onset).getDefaultGenericVnf()
-                            .getVnfId();
+                    vnfId = this.aaiCqResponse.getDefaultGenericVnf().getVnfId();
                 } else {
                     vnfId = this.eventManager.getVnfResponse().getVnfId();
                 }
@@ -314,6 +335,8 @@
                     return startSdnrOperation(onset, operation);
                 case "SDNC":
                     return startSdncOperation(onset, operation);
+                case "CDS":
+                    return startCdsOperation(onset, operation);
                 default:
                     throw new ControlLoopException("invalid actor " + policy.getActor() + " on policy");
             }
@@ -350,7 +373,7 @@
         if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
             this.operationRequest =
                     soActorSp.constructRequestCq((VirtualControlLoopEvent) onset, operation.clOperation,
-                            this.policy, eventManager.getCqResponse((VirtualControlLoopEvent) onset));
+                            this.policy, this.aaiCqResponse);
         } else {
             this.operationRequest = soActorSp.constructRequest((VirtualControlLoopEvent) onset,
                     operation.clOperation, this.policy, eventManager.getNqVserverFromAai());
@@ -370,8 +393,7 @@
     private Object startVfcOperation(ControlLoopEvent onset, Operation operation) throws AaiException {
         if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
             this.operationRequest = VfcActorServiceProvider.constructRequestCq((VirtualControlLoopEvent) onset,
-                    operation.clOperation, this.policy,
-                    eventManager.getCqResponse((VirtualControlLoopEvent) onset));
+                    operation.clOperation, this.policy, this.aaiCqResponse);
         } else {
             this.operationRequest = VfcActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset,
                     operation.clOperation, this.policy, this.eventManager.getVnfResponse(),
@@ -416,6 +438,60 @@
         return operationRequest;
     }
 
+    private Object startCdsOperation(ControlLoopEvent onset, Operation operation) throws AaiException {
+
+        CdsActorServiceProvider provider = new CdsActorServiceProvider();
+        Optional<ExecutionServiceInput> optionalRequest = provider.constructRequest(
+                (VirtualControlLoopEvent) onset, operation.clOperation, this.policy, this.buildAaiParams());
+
+        this.currentOperation = operation;
+        if (optionalRequest.isPresent()) {
+            this.operationRequest = optionalRequest.get();
+        } else {
+            this.operationRequest = null;
+            this.policyResult = PolicyResult.FAILURE;
+        }
+
+        return this.operationRequest;
+    }
+
+    /**
+     * Build AAI parameters for CDS operation.
+     * @return a map containing vnf id key and value for the vnf to apply the action to.
+     * @throws AaiException if the vnf can not be found.
+     */
+    private Map<String, String> buildAaiParams() throws AaiException {
+
+        Map<String, String> result = new HashMap<>();
+
+        if (TargetType.VNF.equals(policy.getTarget().getType())
+                    || TargetType.VFMODULE.equals(policy.getTarget().getType())) {
+
+            if (Boolean.valueOf(PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_CUSTOM_QUERY))) {
+
+                ServiceInstance serviceInstance = this.aaiCqResponse.getServiceInstance();
+                if (serviceInstance == null) {
+                    logger.info("Target entity service instance could not be found");
+                    throw new AaiException("Target service instance could not be found");
+                }
+
+                GenericVnf genericVnf =
+                        this.aaiCqResponse.getGenericVnfByModelInvariantId(policy.getTarget().getResourceID());
+                if (genericVnf == null) {
+                    logger.info("Target entity generic vnf could not be found");
+                    throw new AaiException("Target generic vnf could not be found");
+                }
+
+                result.put(AAI_SERVICE_INSTANCE_ID_KEY, serviceInstance.getServiceInstanceId());
+                result.put(GENERIC_VNF_VNF_ID, genericVnf.getVnfId());
+            }
+
+        }
+
+        return result;
+
+    }
+
     /**
      * Handle a response.
      *
@@ -456,6 +532,11 @@
             // Cast SDNC response and handle it
             //
             return onResponse((SdncResponse) response);
+        } else if (response instanceof CdsResponse) {
+            //
+            // Cast CDS response and handle it
+            //
+            return onResponse((CdsResponse) response);
         } else {
             return null;
         }
@@ -694,6 +775,33 @@
         }
     }
 
+    /**
+     * This method handles operation responses from CDS.
+     *
+     * @param response the CDS response
+     * @return The result of the response handling
+     */
+    private PolicyResult onResponse(CdsResponse response) {
+        if (response != null && CdsActorConstants.SUCCESS.equals(response.getStatus())) {
+            //
+            // Consider it as success
+            //
+            this.completeOperation(this.attempts, SUCCESS_MSG, PolicyResult.SUCCESS);
+            return getTimeoutResult(PolicyResult.SUCCESS);
+        } else {
+            //
+            // Consider it as failure
+            //
+            this.completeOperation(this.attempts, FAILED_MSG, PolicyResult.FAILURE);
+            if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) {
+                return null;
+            }
+            // increment operation attempts for retries
+            this.attempts += 1;
+            return PolicyResult.FAILURE;
+        }
+    }
+
     private PolicyResult getTimeoutResult(PolicyResult result) {
         return (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult) ? null : result);
     }
diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java
index 9b16335..914eb76 100644
--- a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java
+++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManagerTest.java
@@ -3,6 +3,7 @@
  * unit test
  * ================================================================================
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,6 +44,7 @@
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
 import org.onap.policy.aai.util.AaiException;
 import org.onap.policy.appc.CommonHeader;
 import org.onap.policy.appc.Response;
@@ -79,6 +81,7 @@
 public class ControlLoopOperationManagerTest {
     private static final String VSERVER_NAME = "vserver.vserver-name";
     private static final String TEST_YAML = "src/test/resources/test.yaml";
+    private static final String TEST_CDS_YAML = "src/test/resources/test-cds.yaml";
     private static final String ONSET_ONE = "onsetOne";
     private static final String VNF_NAME = "generic-vnf.vnf-name";
     private static final String VNF_ID = "generic-vnf.vnf-id";
@@ -746,6 +749,46 @@
     }
 
     @Test
+    public void testStartCdsOperation() throws ControlLoopException, IOException {
+
+        // Prepare
+        String yamlString;
+        try (InputStream is = new FileInputStream(new File(TEST_CDS_YAML))) {
+            yamlString = IOUtils.toString(is, StandardCharsets.UTF_8);
+        }
+
+        UUID requestId = UUID.randomUUID();
+        VirtualControlLoopEvent event = new VirtualControlLoopEvent();
+        event.setClosedLoopControlName(TWO_ONSET_TEST);
+        event.setRequestId(requestId);
+        event.setTarget(VNF_ID);
+        event.setClosedLoopAlarmStart(Instant.now());
+        event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
+        event.setAai(new HashMap<>());
+        event.getAai().put(VNF_NAME, ONSET_ONE);
+        event.getAai().put(VSERVER_NAME, "OzVServer");
+
+        ControlLoopEventManager eventManager =
+                new ControlLoopEventManager(event.getClosedLoopControlName(), event.getRequestId());
+        VirtualControlLoopNotification notification = eventManager.activate(yamlString, event);
+        assertNotNull(notification);
+        assertEquals(ControlLoopNotificationType.ACTIVE, notification.getNotification());
+
+        Policy policy = eventManager.getProcessor().getCurrentPolicy();
+        ControlLoopOperationManager operationManager = new ControlLoopOperationManager(event, policy, eventManager);
+
+        // Run
+        Object result = operationManager.startOperation(event);
+
+        // Verify
+        assertNotNull(result);
+        assertTrue(result instanceof ExecutionServiceInput);
+        ExecutionServiceInput request = (ExecutionServiceInput) result;
+        logger.debug("request: " + request);
+
+    }
+
+    @Test
     public void testCommitAbatement() throws Exception {
 
         String yamlString = null;
diff --git a/controlloop/common/eventmanager/src/test/resources/test-cds.yaml b/controlloop/common/eventmanager/src/test/resources/test-cds.yaml
new file mode 100644
index 0000000..332c724
--- /dev/null
+++ b/controlloop/common/eventmanager/src/test/resources/test-cds.yaml
@@ -0,0 +1,46 @@
+# Copyright (C) 2019 Bell Canada.
+#
+# 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: ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: CDS
+    recipe: ModifyConfig
+    target:
+      resourceID: bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38
+      type: VNF
+    payload:
+      artifact_name: vfw-cds
+      artifact_version: 1.0.0
+      mode: async
+      data: '{"mapInfo":{"key":"val"},"arrayInfo":["one","two"],"paramInfo":"val"}'
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
diff --git a/controlloop/common/feature-controlloop-management/pom.xml b/controlloop/common/feature-controlloop-management/pom.xml
index bf43f20..24f7a17 100644
--- a/controlloop/common/feature-controlloop-management/pom.xml
+++ b/controlloop/common/feature-controlloop-management/pom.xml
@@ -245,6 +245,17 @@
             </exclusions>
         </dependency>
         <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+            <artifactId>actor.cds</artifactId>
+            <version>${policy.models.version}</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>guava</artifactId>
+                    <groupId>com.google.guava</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
             <groupId>org.onap.policy.models.policy-models-interactions</groupId>
             <artifactId>model-yaml</artifactId>
             <version>${policy.models.version}</version>
@@ -266,6 +277,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>cds</artifactId>
+            <version>${policy.models.version}</version>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
diff --git a/controlloop/common/feature-controlloop-management/src/main/feature/config/controlloop.properties.environment b/controlloop/common/feature-controlloop-management/src/main/feature/config/controlloop.properties.environment
index 84bef12..4a09168 100644
--- a/controlloop/common/feature-controlloop-management/src/main/feature/config/controlloop.properties.environment
+++ b/controlloop/common/feature-controlloop-management/src/main/feature/config/controlloop.properties.environment
@@ -3,6 +3,7 @@
 # ONAP
 # ================================================================================
 # Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+# Modifications Copyright (C) 2019 Bell Canada.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -51,3 +52,9 @@
 sdnc.password=${env:SDNC_PASSWORD}
 
 aai.customQuery=true
+
+cds.grpcHost=${env:CDS_GRPC_HOST}
+cds.grpcPort=${env:CDS_GRPC_PORT}
+cds.grpcUsername=${env:CDS_GRPC_USERNAME}
+cds.grpcPassword=${env:CDS_GRPC_PASSWORD}
+cds.grpcTimeout=10
diff --git a/controlloop/common/feature-controlloop-utils/src/main/java/org/onap/policy/drools/apps/controlloop/feature/utils/ControlLoopUtilsFeature.java b/controlloop/common/feature-controlloop-utils/src/main/java/org/onap/policy/drools/apps/controlloop/feature/utils/ControlLoopUtilsFeature.java
index 2cf6430..c2e7802 100644
--- a/controlloop/common/feature-controlloop-utils/src/main/java/org/onap/policy/drools/apps/controlloop/feature/utils/ControlLoopUtilsFeature.java
+++ b/controlloop/common/feature-controlloop-utils/src/main/java/org/onap/policy/drools/apps/controlloop/feature/utils/ControlLoopUtilsFeature.java
@@ -47,9 +47,6 @@
         } catch (final InterruptedException e) {
             logger.error("{}: initialization aborted", ControlLoopUtilsFeature.class.getName(), e);
             Thread.currentThread().interrupt();
-        } catch (final IOException e) {
-            logger.error("{}: a simulator cannot be built because of {}", ControlLoopUtilsFeature.class.getName(),
-                    e.getMessage(), e);
         }
         return false;
     }
diff --git a/controlloop/pom.xml b/controlloop/pom.xml
index 377c9f6..dff61c1 100644
--- a/controlloop/pom.xml
+++ b/controlloop/pom.xml
@@ -31,6 +31,21 @@
     <groupId>org.onap.policy.drools-applications.controlloop</groupId>
     <artifactId>controlloop</artifactId>
 
+  <properties>
+    <ccsdk.version>0.4.4</ccsdk.version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.onap.ccsdk.cds.components</groupId>
+        <artifactId>proto-definition</artifactId>
+        <version>${ccsdk.version}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+
   <modules>
     <module>common</module>
     <module>templates</module>
diff --git a/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl b/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl
index 57ebd70..4cbdd49 100644
--- a/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl
+++ b/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl
@@ -3,6 +3,7 @@
  * ONAP
  * ================================================================================
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +21,8 @@
 
 package org.onap.policy.controlloop;
 
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
+
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.VirtualControlLoopNotification;
 import org.onap.policy.controlloop.ControlLoopEventStatus;
@@ -32,6 +35,9 @@
 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NewEventStatus;
 import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
 import org.onap.policy.controlloop.actor.so.SoActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider;
+import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider.CdsActorServiceManager;
+import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
 import org.onap.policy.aai.AaiNqResponseWrapper;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.appc.Request;
@@ -42,6 +48,9 @@
 import org.onap.policy.appclcm.LcmRequest;
 import org.onap.policy.appclcm.LcmResponse;
 import org.onap.policy.appclcm.LcmCommonHeader;
+import org.onap.policy.cds.CdsResponse;
+import org.onap.policy.cds.client.CdsProcessorGrpcClient;
+import org.onap.policy.cds.properties.CdsServerProperties;
 import org.onap.policy.sdnr.PciRequestWrapper;
 import org.onap.policy.sdnr.PciResponseWrapper;
 import org.onap.policy.sdnr.PciRequest;
@@ -636,6 +645,34 @@
                       PolicyEngineConstants.getManager().deliver("SDNR-CL", request);
                   }
                   break;
+
+             case "CDS":
+
+                  if(request instanceof ExecutionServiceInput) {
+
+                      // Instantiate cds actor, service manager and grpc properties
+
+                      CdsActorServiceProvider cdsActor = new CdsActorServiceProvider();
+                      CdsActorServiceProvider.CdsActorServiceManager cdsActorServiceManager = cdsActor.new CdsActorServiceManager();
+
+                      CdsServerProperties props = new CdsServerProperties();
+                      props.setHost(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcHost"));
+                      props.setPort(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPort")));
+                      props.setUsername(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcUsername"));
+                      props.setPassword(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPassword"));
+                      props.setTimeout(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcTimeout")));
+
+                      // Send cds grpc request
+                      try (CdsProcessorGrpcClient client = new CdsProcessorGrpcClient(cdsActorServiceManager, props)) {
+                          CdsResponse response =
+                                cdsActorServiceManager.sendRequestToCds(client, props, (ExecutionServiceInput) request);
+                          logger.info("CDS response: {}", response);
+                          insert(response);
+                      }
+
+                  }
+                  break;
+
           }
         } else {
           //
@@ -1429,6 +1466,86 @@
 
 end
 
+
+/**
+ * This rule responds to CDS Response Events
+ */
+rule "${policyName}.CDS.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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
+            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
+        $lock : TargetLock (requestId == $event.getRequestId())
+        $response : CdsResponse( requestId == $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);
+
+            // The 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());
+            notification.setNotification(
+                ($response != null && CdsActorConstants.SUCCESS.equals($response.getStatus()))
+                    ? ControlLoopNotificationType.OPERATION_SUCCESS : ControlLoopNotificationType.OPERATION_FAILURE);
+
+            // Send the notification
+            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+
+            // Ensure the operation is complete
+            if ($operation.isOperationComplete()) {
+
+                // 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
+            logger.info(
+                "policyResult is null"
+                    + "\nIt's not finished yet (i.e. expecting more Response objects)"
+                    + "\nOr 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 manages a single timer.
diff --git a/controlloop/templates/template.demo/pom.xml b/controlloop/templates/template.demo/pom.xml
index 548bb76..57edba6 100644
--- a/controlloop/templates/template.demo/pom.xml
+++ b/controlloop/templates/template.demo/pom.xml
@@ -4,6 +4,7 @@
   ================================================================================
   Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
   Modifications Copyright (C) 2019 Nordix Foundation.
+  Modifications Copyright (C) 2019 Bell Canada.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -30,6 +31,11 @@
 
   <artifactId>template.demo</artifactId>
 
+  <properties>
+    <protobuf.version>3.6.1</protobuf.version>
+    <grpc.version>1.17.1</grpc.version>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.eclipse.persistence</groupId>
@@ -185,6 +191,28 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+      <artifactId>actor.cds</artifactId>
+      <version>${policy.models.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.ccsdk.cds.components</groupId>
+      <artifactId>proto-definition</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+      <artifactId>cds</artifactId>
+      <version>${policy.models.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+      <version>${protobuf.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
@@ -207,6 +235,12 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>io.grpc</groupId>
+      <artifactId>grpc-testing</artifactId>
+      <version>${grpc.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.onap.policy.drools-pdp</groupId>
       <artifactId>policy-management</artifactId>
       <version>${version.policy.drools-pdp}</version>
diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopBase.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopBase.java
index ab6ec5a..5817dc9 100644
--- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopBase.java
+++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopBase.java
@@ -3,6 +3,7 @@
  * demo
  * ================================================================================
  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -84,6 +85,7 @@
         SupportUtil.setSoProps();
         SupportUtil.setVfcProps();
         SupportUtil.setPuProp();
+        SupportUtil.setCdsProps();
 
         LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO");
 
diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java
index bc5a982..0793b88 100644
--- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java
+++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java
@@ -252,6 +252,7 @@
         event.setClosedLoopAlarmStart(Instant.now());
         event.setAai(new HashMap<>());
         event.getAai().put("generic-vnf.vnf-id", target);
+        event.getAai().put("vserver.vserver-name", "OzVServer");
         event.setClosedLoopEventStatus(status);
         kieSession.insert(event);
     }
diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/SupportUtil.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/SupportUtil.java
index ab57fdf..878f94b 100644
--- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/SupportUtil.java
+++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/SupportUtil.java
@@ -3,6 +3,7 @@
  * demo
  * ================================================================================
  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,6 +45,7 @@
 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.common.utils.network.NetworkUtil;
 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
 import org.onap.policy.controlloop.policy.guard.ControlLoopGuard;
 import org.onap.policy.drools.system.PolicyEngineConstants;
@@ -58,6 +60,16 @@
     private static final String OPSHISTPUPROP = "OperationsHistoryPU";
     private static final Logger logger = LoggerFactory.getLogger(SupportUtil.class);
 
+    static final int GRPC_SERVER_PORT;
+
+    static {
+        try {
+            GRPC_SERVER_PORT = NetworkUtil.allocPort();
+        } catch (IOException e) {
+            throw new RuntimeException("Socket cannot be created for grpc server port", e);
+        }
+    }
+
     public static class Pair<A, B> {
         public final A first;
         public final B second;
@@ -370,6 +382,17 @@
     }
 
     /**
+     * Set cds properties.
+     */
+    public static void setCdsProps() {
+        PolicyEngineConstants.getManager().setEnvironmentProperty("cds.grpcHost", "localhost");
+        PolicyEngineConstants.getManager().setEnvironmentProperty("cds.grpcPort", Integer.toString(GRPC_SERVER_PORT));
+        PolicyEngineConstants.getManager().setEnvironmentProperty("cds.grpcUsername", "grpc-username");
+        PolicyEngineConstants.getManager().setEnvironmentProperty("cds.grpcPassword", "grpc-password");
+        PolicyEngineConstants.getManager().setEnvironmentProperty("cds.grpcTimeout", "5");
+    }
+
+    /**
      * Rule specification.
      */
     public static class RuleSpec {
diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java
index 1cd431f..25bef2e 100644
--- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java
+++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java
@@ -58,6 +58,7 @@
             "service=ServiceDemo;resource=Res1Demo;type=operational",
             "CL_vCPE",
             "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
+        SupportUtil.setCustomQuery("false");
     }
 
     @Test
@@ -122,7 +123,6 @@
          */
         sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, "getFail", false);
 
-
         kieSession.fireUntilHalt();
 
         // allow object clean-up
diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopCdsTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopCdsTest.java
new file mode 100644
index 0000000..435faf2
--- /dev/null
+++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopCdsTest.java
@@ -0,0 +1,266 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Bell Canada.
+ * ================================================================================
+ * 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;
+
+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 io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader;
+import org.onap.ccsdk.cds.controllerblueprints.common.api.EventType;
+import org.onap.ccsdk.cds.controllerblueprints.common.api.Status;
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.BluePrintProcessingServiceGrpc;
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
+import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.event.comm.TopicListener;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.controlloop.ControlLoopEventStatus;
+import org.onap.policy.controlloop.ControlLoopNotificationType;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.controlloop.util.Serialization;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class for vfirewall use case using CDS actor.
+ */
+public class VfwControlLoopCdsTest extends ControlLoopBase implements TopicListener {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(VfwControlLoopCdsTest.class);
+
+    private final AtomicReference<StreamObserver<ExecutionServiceOutput>> responseObserverRef = new AtomicReference<>();
+    private Server server;
+
+    /**
+     * Setup the simulator.
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        ControlLoopBase.setUpBeforeClass("../archetype-cl-amsterdam/src/main/resources/archetype-resources/src/"
+                                                 + "main/resources/__closedLoopControlName__.drl",
+                "src/test/resources/yaml/policy_ControlLoop_vFW_CDS.yaml",
+                "service=ServiceDemo;resource=Res1Demo;type=operational", "CL_vFW",
+                "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0");
+        SupportUtil.setCustomQuery("true");
+    }
+
+    @Before
+    public void setUp() throws IOException {
+        this.startGrpcServer();
+    }
+
+    @After
+    public void tearDown() {
+        this.stopGrpcServer();
+    }
+
+    private void startGrpcServer() throws IOException {
+
+        BluePrintProcessingServiceGrpc.BluePrintProcessingServiceImplBase cdsBlueprintServerImpl =
+
+            new BluePrintProcessingServiceGrpc.BluePrintProcessingServiceImplBase() {
+
+                @Override
+                public StreamObserver<ExecutionServiceInput> process(
+                        final StreamObserver<ExecutionServiceOutput> responseObserver) {
+
+                    responseObserverRef.set(responseObserver);
+
+                    return new StreamObserver<ExecutionServiceInput>() {
+                        @Override
+                        public void onNext(final ExecutionServiceInput input) {
+                            LOGGER.info("gRPC server onNext() for input: {} ...", input);
+                            ExecutionServiceOutput output =
+                                    ExecutionServiceOutput.newBuilder()
+                                            .setCommonHeader(
+                                                    CommonHeader.newBuilder().setRequestId(
+                                                            input.getCommonHeader().getRequestId()).build())
+                                            .setStatus(
+                                                    Status.newBuilder().setEventType(
+                                                            EventType.EVENT_COMPONENT_EXECUTED).build())
+                                            .build();
+                            responseObserver.onNext(output);
+                        }
+
+                        @Override
+                        public void onError(final Throwable throwable) {
+                            LOGGER.error("gRPC server onError() for throwable: {} ...", throwable);
+                        }
+
+                        @Override
+                        public void onCompleted() {
+                            LOGGER.info("gRPC server onCompleted() ...");
+                            responseObserver.onCompleted();
+                        }
+                    };
+                }
+            };
+
+        server = ServerBuilder.forPort(SupportUtil.GRPC_SERVER_PORT).addService(cdsBlueprintServerImpl).build().start();
+        LOGGER.info("gRPC server is listening for CDS requests on port {}", SupportUtil.GRPC_SERVER_PORT);
+
+    }
+
+    private void stopGrpcServer() {
+        if (server != null) {
+            this.server.shutdown();
+            LOGGER.info("gRPC server handling CDS requests has been successfully shut down.");
+        }
+    }
+
+    @Test
+    public void successTest() {
+
+        /*
+         * 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 a unique requestId
+         */
+        requestId = UUID.randomUUID();
+
+        /*
+         * Simulate an onset event the policy engine will receive from DCAE to kick off processing
+         * through the rules
+         */
+        sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET);
+
+        kieSession.fireUntilHalt();
+        kieSession.fireAllRules();
+
+        /*
+         * The only fact in memory should be Params
+         */
+        assertEquals(1, kieSession.getFactCount());
+
+        /*
+         * Print what's left in memory
+         */
+        dumpFacts(kieSession);
+    }
+
+
+    /*
+     * @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 ControlLoopNotification
+         * of type active
+         */
+        assertEquals("POLICY-CL-MGT", topic);
+        VirtualControlLoopNotification notification =
+                Serialization.gsonJunit.fromJson(event, VirtualControlLoopNotification.class);
+        assertNotNull(notification);
+        String policyName = notification.getPolicyName();
+        if (policyName.endsWith("EVENT")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            assertEquals(ControlLoopNotificationType.ACTIVE, notification.getNotification());
+        } else if (policyName.endsWith("GUARD_NOT_YET_QUERIED")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            assertEquals(ControlLoopNotificationType.OPERATION, notification.getNotification());
+            assertNotNull(notification.getMessage());
+            assertTrue(notification.getMessage().startsWith("Sending guard query"));
+        } else if (policyName.endsWith("GUARD.RESPONSE")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            assertEquals(ControlLoopNotificationType.OPERATION, notification.getNotification());
+            assertNotNull(notification.getMessage());
+            assertTrue(notification.getMessage().toLowerCase().endsWith("permit"));
+        } 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=CDS"));
+        } else if (policyName.endsWith("OPERATION.TIMEOUT")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            kieSession.halt();
+            logger.debug("The operation timed out");
+            fail("Operation Timed Out");
+        } else if (policyName.endsWith("CDS.RESPONSE")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            assertEquals(ControlLoopNotificationType.OPERATION_SUCCESS, notification.getNotification());
+            assertNotNull(notification.getMessage());
+            assertTrue(notification.getMessage().startsWith("actor=CDS"));
+            sendEvent(pair.first, requestId, ControlLoopEventStatus.ABATED);
+        } else if (policyName.endsWith("EVENT.MANAGER")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            if ("error".equals(notification.getAai().get("generic-vnf.vnf-name"))) {
+                assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notification.getNotification());
+                assertEquals("Target vnf-id could not be found", notification.getMessage());
+            } else if ("getFail".equals(notification.getAai().get("generic-vnf.vnf-name"))) {
+                assertEquals(ControlLoopNotificationType.FINAL_FAILURE, notification.getNotification());
+            } else {
+                assertEquals(ControlLoopNotificationType.FINAL_SUCCESS, notification.getNotification());
+            }
+            kieSession.halt();
+        } else if (policyName.endsWith("EVENT.MANAGER.TIMEOUT")) {
+            logger.debug("Rule Fired: " + notification.getPolicyName());
+            kieSession.halt();
+            logger.debug("The control loop timed out");
+            fail("Control Loop Timed Out");
+        }
+    }
+
+    /**
+     * 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 policy the controlLoopName comes from the policy
+     * @param requestId the requestId for this event
+     * @param status could be onset or abated
+     */
+    private void sendEvent(ControlLoopPolicy policy, UUID requestId, ControlLoopEventStatus status) {
+        VirtualControlLoopEvent event = new VirtualControlLoopEvent();
+        event.setClosedLoopControlName(policy.getControlLoop().getControlLoopName());
+        event.setRequestId(requestId);
+        event.setTarget("generic-vnf.vnf-name");
+        event.setClosedLoopAlarmStart(Instant.now());
+        event.setAai(new HashMap<>());
+        event.getAai().put("generic-vnf.vnf-name", "testGenericVnfID");
+        event.getAai().put("vserver.vserver-name", "OzVServer");
+        event.setClosedLoopEventStatus(status);
+        kieSession.insert(event);
+    }
+
+}
diff --git a/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_vFW_CDS.yaml b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_vFW_CDS.yaml
new file mode 100644
index 0000000..511a969
--- /dev/null
+++ b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_vFW_CDS.yaml
@@ -0,0 +1,46 @@
+# Copyright (C) 2019 Bell Canada.
+#
+# 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: ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: CDS
+    recipe: ModifyConfig
+    target:
+      resourceID: bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38
+      type: VNF
+    payload:
+      artifact_name: vfw-cds
+      artifact_version: 1.0.0
+      mode: async
+      data: '{"mapInfo":{"key":"val"},"arrayInfo":["one","two"],"paramInfo":"val"}'
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard