ios-xe renderer checks SFC RSP configuration progress 14/40614/8
authorVladimir Lavor <vlavor@cisco.com>
Tue, 21 Jun 2016 12:22:50 +0000 (14:22 +0200)
committerVladimir Lavor <vlavor@cisco.com>
Mon, 27 Jun 2016 07:27:44 +0000 (07:27 +0000)
        - should not fail when accessing locked device
        - added timeout for sfc part (create service-paths, etc)
        - added checks in dependent entities (e.g. entry in policy-map
          will not be created when appropriate class map does not exist)
- delete case
        - bugfix

Change-Id: I5af812fa22bfc4e1fca2c7f1340b3814681c79bf
Signed-off-by: Vladimir Lavor <vlavor@cisco.com>
12 files changed:
renderers/ios-xe/pom.xml
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/IosXeRendererProviderImpl.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/manager/NodeManager.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/manager/PolicyManagerImpl.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/PolicyManagerUtil.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtil.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/NetconfTransactionCreator.java [new file with mode: 0644]
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/NodeWriter.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/PolicyWriter.java
renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/PolicyWriterUtil.java [new file with mode: 0644]
renderers/ios-xe/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/PolicyManagerUtilTest.java
renderers/ios-xe/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtilTest.java

index da4bf774ee18d3ca7ce78d6c5cf963f3ddcec480..43e555265b444a0c07cc045100f98c795ab9e4bb 100755 (executable)
@@ -93,6 +93,7 @@
         <dependency>
             <groupId>org.opendaylight.mdsal.model</groupId>
             <artifactId>ietf-inet-types-2013-07-15</artifactId>
+            <version>1.1.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.powermock</groupId>
index c1c1eca9cf16c897283c6037e0a30ddcfcfa6ab8..4dc9323ba430245e0036c3c7f3e5c77c7b81f885 100644 (file)
@@ -9,8 +9,10 @@
 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl;
 
 import com.google.common.base.Preconditions;
-import java.util.List;
-
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -24,6 +26,7 @@ import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.listener.
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.NodeManager;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerZipImpl;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.sf.ChainAction;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.sf.Classifier;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.sf.EtherTypeClassifier;
@@ -42,10 +45,9 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
+import java.util.List;
+import java.util.Optional;
+
 /**
  * Purpose: bootstrap provider implementation of Ios-xe renderer
  */
@@ -83,12 +85,12 @@ public class IosXeRendererProviderImpl implements IosXeRendererProvider, Binding
         LOG.info("starting ios-xe renderer");
         //TODO register listeners:
         // node-manager
-        NodeManager nodeManager = new NodeManager(dataBroker, providerContext);
+        final NodeManager nodeManager = new NodeManager(dataBroker, providerContext);
         // network-topology
         iosXeCapableNodeListener = new IosXeCapableNodeListenerImpl(dataBroker, nodeManager);
 
         // policy-manager and delegates
-        PolicyManager policyManager = new PolicyManagerImpl(dataBroker, nodeManager);
+        final PolicyManager policyManager = new PolicyManagerImpl(dataBroker, nodeManager);
         final PolicyManager policyManagerZip = new PolicyManagerZipImpl(policyManager);
 
         // renderer-configuration endpoints
@@ -101,37 +103,42 @@ public class IosXeRendererProviderImpl implements IosXeRendererProvider, Binding
     }
 
     private void writeRendererCapabilities() {
-        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-
-        ChainAction action = new ChainAction();
-        List<SupportedActionDefinition> actionDefinitions =
+        final Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(dataBroker);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+            return;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+        final ChainAction action = new ChainAction();
+        final List<SupportedActionDefinition> actionDefinitions =
                 ImmutableList.of(new SupportedActionDefinitionBuilder().setActionDefinitionId(action.getId())
-                    .setSupportedParameterValues(action.getSupportedParameterValues())
-                    .build());
-
-        Classifier etherClassifier = new EtherTypeClassifier(null);
-        Classifier ipProtoClassifier = new IpProtoClassifier(etherClassifier.getId());
-        List<SupportedClassifierDefinition> classifierDefinitions = ImmutableList
-            .of(new SupportedClassifierDefinitionBuilder().setClassifierDefinitionId(etherClassifier.getId())
-                .setParentClassifierDefinitionId(etherClassifier.getParent())
-                .setSupportedParameterValues(etherClassifier.getSupportedParameterValues())
-                .build(),
-                new SupportedClassifierDefinitionBuilder().setClassifierDefinitionId(ipProtoClassifier.getId())
-                .setParentClassifierDefinitionId(ipProtoClassifier.getParent())
-                .setSupportedParameterValues(ipProtoClassifier.getSupportedParameterValues())
-                .build());
-
-        Renderer renderer = new RendererBuilder().setName(NodeManager.iosXeRenderer)
-            .setCapabilities(new CapabilitiesBuilder().setSupportedActionDefinition(actionDefinitions)
-                .setSupportedClassifierDefinition(classifierDefinitions)
-                .build())
-            .build();
-
-        InstanceIdentifier<Renderer> iid = InstanceIdentifier.builder(Renderers.class)
-        .child(Renderer.class, new RendererKey(new RendererName(NodeManager.iosXeRenderer)))
-        .build();
+                        .setSupportedParameterValues(action.getSupportedParameterValues())
+                        .build());
+
+        final Classifier etherClassifier = new EtherTypeClassifier(null);
+        final Classifier ipProtoClassifier = new IpProtoClassifier(etherClassifier.getId());
+        final List<SupportedClassifierDefinition> classifierDefinitions = ImmutableList
+                .of(new SupportedClassifierDefinitionBuilder().setClassifierDefinitionId(etherClassifier.getId())
+                                .setParentClassifierDefinitionId(etherClassifier.getParent())
+                                .setSupportedParameterValues(etherClassifier.getSupportedParameterValues())
+                                .build(),
+                        new SupportedClassifierDefinitionBuilder().setClassifierDefinitionId(ipProtoClassifier.getId())
+                                .setParentClassifierDefinitionId(ipProtoClassifier.getParent())
+                                .setSupportedParameterValues(ipProtoClassifier.getSupportedParameterValues())
+                                .build());
+
+        final Renderer renderer = new RendererBuilder().setName(NodeManager.iosXeRenderer)
+                .setCapabilities(new CapabilitiesBuilder().setSupportedActionDefinition(actionDefinitions)
+                        .setSupportedClassifierDefinition(classifierDefinitions)
+                        .build())
+                .build();
+
+        final InstanceIdentifier<Renderer> iid = InstanceIdentifier.builder(Renderers.class)
+                .child(Renderer.class, new RendererKey(new RendererName(NodeManager.iosXeRenderer)))
+                .build();
         writeTransaction.merge(LogicalDatastoreType.OPERATIONAL, iid, renderer, true);
-        CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+        final CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
         Futures.addCallback(future, new FutureCallback<Void>() {
 
             @Override
index 9916b3446d129f7acaabfeca4e30042fcf2b57a8..26a0963f017a0504875c77daa62e540f0067dd74 100644 (file)
@@ -14,10 +14,11 @@ import com.google.common.util.concurrent.CheckedFuture;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.MountPoint;
 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NodeWriter;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.RendererName;
@@ -218,10 +219,16 @@ public class NodeManager {
         InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(NetworkTopology.class)
                 .child(Topology.class, new TopologyKey(new TopologyId(NodeManager.TOPOLOGY_ID)))
                 .child(Node.class, new NodeKey(nodeId)).build();
-        ReadWriteTransaction rwt = dataBroker.newReadWriteTransaction();
+        java.util.Optional<ReadOnlyTransaction> optionalTransaction =
+                NetconfTransactionCreator.netconfReadOnlyTransaction(dataBroker);
+        if (!optionalTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+            return null;
+        }
+        ReadOnlyTransaction transaction = optionalTransaction.get();
         try {
             CheckedFuture<Optional<Node>, ReadFailedException> submitFuture =
-                    rwt.read(LogicalDatastoreType.CONFIGURATION, nodeIid);
+                    transaction.read(LogicalDatastoreType.CONFIGURATION, nodeIid);
             Optional<Node> optional = submitFuture.checkedGet();
             if (optional != null && optional.isPresent()) {
                 Node node = optional.get();
index b1cd010cf9472cfb937e6759afd3f9debfa9c7ed..2b78c3077fb8aaca63397bb659c53ab69301b7b4 100644 (file)
@@ -8,26 +8,20 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager;
 
-import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Create;
-import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Delete;
-
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.api.manager.PolicyManager;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.Renderers;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
@@ -44,6 +38,16 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Create;
+import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Delete;
+
 public class PolicyManagerImpl implements PolicyManager {
 
     private static final Logger LOG = LoggerFactory.getLogger(PolicyManagerImpl.class);
@@ -66,7 +70,8 @@ public class PolicyManagerImpl implements PolicyManager {
         } else if (dataBefore != null && dataAfter == null) {
             result = syncPolicy(dataBefore, Delete);
         } else {
-            // TODO implement
+            syncPolicy(dataBefore, Delete);
+            syncPolicy(dataAfter, Create);
             result = Futures.immediateFuture(false);
         }
 
@@ -104,7 +109,6 @@ public class PolicyManagerImpl implements PolicyManager {
                 //TODO: dump all resolvedRule-rule-peerEP-EP combinantions to status
                 continue;
             }
-
             final List<AddressEndpointWithLocation> endpointsWithLocation = dataAfter.getEndpoints()
                     .getAddressEndpointWithLocation();
             final InstanceIdentifier mountpointIid = PolicyManagerUtil.getAbsoluteLocationMountpoint(rendererEndpoint, endpointsWithLocation);
@@ -144,27 +148,26 @@ public class PolicyManagerImpl implements PolicyManager {
                     //TODO: dump particular resolvedRule-rule-peerEP-EP combinantions to status
                     continue;
                 }
-                PolicyManagerUtil.syncPolicyEntities(sourceSgt, destinationSgt, policyWriter, dataAfter, peerEndpoint);
+                PolicyManagerUtil.syncPolicyEntities(sourceSgt, destinationSgt, policyWriter, dataAfter, peerEndpoint,
+                        dataBroker, action);
             }
         }
 
         //TODO: return real (cumulated) future
-        final List<CheckedFuture<Void, TransactionCommitFailedException>> allFutureResults = new ArrayList<>();
+        final List<CheckedFuture<Boolean, TransactionCommitFailedException>> allFutureResults = new ArrayList<>();
         if (action.equals(Create)) {
             policyWriterPerDeviceCache.values().forEach(pw -> allFutureResults.add(pw.commitToDatastore()));
         } else if (action.equals(Delete)) {
             policyWriterPerDeviceCache.values().forEach(pw -> allFutureResults.add(pw.removeFromDatastore()));
         } else {
             LOG.info("unsupported policy manage action: {}", action);
-
         }
+        final ListenableFuture<List<Boolean>> cumulativeResult = Futures.allAsList(allFutureResults);
 
-        final ListenableFuture<List<Void>> cumulativeResult = Futures.allAsList(allFutureResults);
-
-        return Futures.transform(cumulativeResult, new Function<List<Void>, Boolean>() {
+        return Futures.transform(cumulativeResult, new Function<List<Boolean>, Boolean>() {
             @Nullable
             @Override
-            public Boolean apply(@Nullable final List<Void> input) {
+            public Boolean apply(@Nullable final List<Boolean> input) {
                 LOG.trace("considering all submits as successful - otherwise there will be exception");
                 return Boolean.TRUE;
             }
@@ -172,12 +175,19 @@ public class PolicyManagerImpl implements PolicyManager {
     }
 
     private CheckedFuture<Void, TransactionCommitFailedException> reportVersion(long version) {
-        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
-        InstanceIdentifier<RendererPolicy> iid = InstanceIdentifier.create(Renderers.class)
+        final Optional<ReadWriteTransaction> optionalReadWriteTransaction =
+                NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker);
+        if (!optionalReadWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+            return Futures.immediateCheckedFuture(null);
+        }
+        final ReadWriteTransaction readWriteTransaction = optionalReadWriteTransaction.get();
+        final InstanceIdentifier<RendererPolicy> iid = InstanceIdentifier.create(Renderers.class)
                 .child(Renderer.class, new RendererKey(NodeManager.iosXeRenderer))
                 .child(RendererPolicy.class);
-        wtx.merge(LogicalDatastoreType.OPERATIONAL, iid, new RendererPolicyBuilder().setVersion(version).build());
-        return wtx.submit();
+        readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, iid,
+                new RendererPolicyBuilder().setVersion(version).build());
+        return readWriteTransaction.submit();
     }
 
     @Override
@@ -185,7 +195,7 @@ public class PolicyManagerImpl implements PolicyManager {
         //NOOP
     }
 
-    enum DsAction {Create, Delete}
+    public enum DsAction {Create, Delete}
 
     public enum ActionCase {ALLOW, CHAIN}
 }
index 55ed2993c0aa5635c6d956c11968bda75531e3c1..1d00aca03e61feb2b1dc606f7762fc6b7c2d6057 100644 (file)
@@ -8,16 +8,7 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util;
 
-import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.ActionCase.CHAIN;
-import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.In;
-import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.Out;
-import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.CONSUMER;
-import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.PROVIDER;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
@@ -49,7 +40,6 @@ import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.ClassKe
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.ActionList;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.ActionListBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.ActionListKey;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.AppnavPolicyBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.action.list.action.param.ForwardCaseBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.action.list.action.param.forward._case.ForwardBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map._class.action.list.action.param.forward._case.forward.ServicePath;
@@ -63,8 +53,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.rule.group.with.renderer.endpoint.participation.RuleGroupWithRendererEndpointParticipation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.Configuration;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
@@ -72,7 +60,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointWithPolicy;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.rule.groups.RuleGroup;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.groupbasedpolicy.sxp.mapper.model.rev160302.AddressEndpointWithLocationAug;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
@@ -80,27 +67,42 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.ActionCase.CHAIN;
+import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Create;
+import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Delete;
+
 public class PolicyManagerUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(PolicyManagerUtil.class);
+    private static final String DEFAULT = "class-default";
 
     public static void syncPolicyEntities(final Sgt sourceSgt, final Sgt destinationSgt, PolicyWriter policyWriter,
-                                          final Configuration dataAfter, final PeerEndpointWithPolicy peerEndpoint) {
-        // Class map
-        final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
-        final Match match = PolicyManagerUtil.createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
-        final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, match);
+                                          final Configuration dataAfter, final PeerEndpointWithPolicy peerEndpoint,
+                                          final DataBroker dataBroker, final PolicyManagerImpl.DsAction action) {
+        // Action
         final Map<PolicyManagerImpl.ActionCase, Action> actionMap = PolicyManagerUtil.getActionInDirection(dataAfter, peerEndpoint);
         if (actionMap == null || actionMap.isEmpty()) {
             LOG.debug("no usable action found for EP-sgt[{}] | peerEP-sgt[{}]",
                     sourceSgt, destinationSgt);
             return;
         }
-        policyWriter.cache(classMap);
 
-        // Policy map entry
-        if (actionMap.containsKey(PolicyManagerImpl.ActionCase.CHAIN)) {
-            ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        // TODO allow action not supported
+
+        // Resolve chain action - create
+        if (actionMap.containsKey(PolicyManagerImpl.ActionCase.CHAIN) && action.equals(Create)) {
+            ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter,
+                    dataBroker);
+        }
+        if (actionMap.containsKey(PolicyManagerImpl.ActionCase.CHAIN) && action.equals(Delete)) {
+            ServiceChainingUtil.removeChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter);
         }
     }
 
@@ -119,7 +121,7 @@ public class PolicyManagerUtil {
         return augmentation.getSgt();
     }
 
-    private static Match createSecurityGroupMatch(final int sourceTag, final int destinationTag) {
+    static Match createSecurityGroupMatch(final int sourceTag, final int destinationTag) {
         final SecurityGroupBuilder sgBuilder = new SecurityGroupBuilder();
         final Source source = new SourceBuilder().setTag(sourceTag).build();
         final Destination destination = new DestinationBuilder().setTag(destinationTag).build();
@@ -187,12 +189,10 @@ public class PolicyManagerUtil {
 
     public static PolicyMap createPolicyMap(final String policyMapName, final List<Class> policyMapEntries) {
         // Create default class entry
-        final AppnavPolicyBuilder appnavPolicyBuilder = new AppnavPolicyBuilder();
-        appnavPolicyBuilder.setPassThrough(true);
         final ClassBuilder defaultBuilder = new ClassBuilder();
-        defaultBuilder.setName(new ClassNameType("class-default"))
-                .setKey(new ClassKey(new ClassNameType("class-default")))
-                .setAppnavPolicy(appnavPolicyBuilder.build());
+        defaultBuilder.setName(new ClassNameType(DEFAULT))
+                .setKey(new ClassKey(new ClassNameType(DEFAULT)));
+        // TODO add pass-through value
         policyMapEntries.add(defaultBuilder.build());
         // Construct policy map
         final PolicyMapBuilder policyMapBuilder = new PolicyMapBuilder();
@@ -256,11 +256,10 @@ public class PolicyManagerUtil {
 
 
     private static Map<PolicyManagerImpl.ActionCase, Action> getActionInDirection(final Configuration data, final PeerEndpointWithPolicy peer) {
-        final List<ResolvedRule> rulesInDirection = new ArrayList<>();
+        final Set<ResolvedRule> rulesInDirection = new HashSet<>();
         // Find all rules in desired direction
         for (RuleGroupWithRendererEndpointParticipation ruleGroupKey :
                 peer.getRuleGroupWithRendererEndpointParticipation()) {
-            final EndpointPolicyParticipation participation = ruleGroupKey.getRendererEndpointParticipation();
             final RuleGroup ruleGroup = findRuleGroup(data, ruleGroupKey);
             if (ruleGroup == null || ruleGroup.getResolvedRule() == null) {
                 continue;
@@ -273,13 +272,7 @@ public class PolicyManagerUtil {
                 if (resolvedRule.getClassifier() == null || resolvedRule.getAction() == null) {
                     continue;
                 }
-                // TODO only first Classifier used
-                final Classifier classifier = resolvedRule.getClassifier().get(0);
-                final HasDirection.Direction direction = classifier.getDirection();
-                if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
-                        (participation.equals(CONSUMER) && direction.equals(In))) {
-                    rulesInDirection.add(resolvedRule);
-                }
+                rulesInDirection.add(resolvedRule);
             }
         }
         if (rulesInDirection.isEmpty()) {
@@ -320,5 +313,4 @@ public class PolicyManagerUtil {
         }
         return null;
     }
-
 }
index e029ae136f0aa25b3170b98925f533e8525a73d2..48de2c22e2d0fccd0be5a938a22c833707c7c792 100644 (file)
@@ -9,24 +9,26 @@
 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util;
 
 import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.CheckedFuture;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
@@ -40,8 +42,10 @@ import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev1407
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
@@ -67,9 +71,20 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.security.cert.PKIXRevocationChecker;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
 public class ServiceChainingUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
+    private static final String RSP_SUFFIX = "-gbp-rsp";
+    private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse";
 
     static ServiceFunctionPath getServicePath(final List<ParameterValue> params) {
         if (params == null || params.isEmpty()) {
@@ -104,13 +119,13 @@ public class ServiceChainingUtil {
         return serviceFunctionPath;
     }
 
-    static void resolveChainAction(final PeerEndpointWithPolicy peerEndpoint, final Sgt sourceSgt,
-                                   final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, Action> actionMap,
-                                   final String classMapName, PolicyWriter policyWriter) {
-        final List<Class> entries = new ArrayList<>();
+    static void resolveNewChainAction(final PeerEndpointWithPolicy peerEndpoint, final Sgt sourceSgt,
+                                      final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, Action> actionMap,
+                                      PolicyWriter policyWriter, final DataBroker dataBroker) {
+        final List<Class> policyMapEntries = new ArrayList<>();
         final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN);
         final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue());
-        if (servicePath == null) {
+        if (servicePath == null || servicePath.getName() == null) {
             //TODO: dump particular resolvedRule-rule-peerEP-EP combinantions to status
             return;
         }
@@ -119,42 +134,89 @@ public class ServiceChainingUtil {
             //TODO: dump particular resolvedRule-rule-peerEP-EP combinantions to status
             return;
         }
-        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId);
+        final RenderedServicePath directPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId, dataBroker);
+        // Rsp found, create class-map and policy-map entry
+        final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
+        final Match match = PolicyManagerUtil.createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
+        final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, match);
+        policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(classMapName, directPath, PolicyManagerImpl.ActionCase.CHAIN));
+        RenderedServicePath reversedPath = null;
+        if (servicePath.isSymmetric()) {
+            // symmetric path is in opposite direction. Roles of renderer and peer endpoint will invert
+            reversedPath = ServiceChainingUtil.createSymmetricRenderedPath(servicePath, directPath, tenantId, dataBroker);
+            // Reversed Rsp found, create class-map and policy-map entry in opposite direction
+            final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue());
+            final Match oppositeMatch = PolicyManagerUtil.createSecurityGroupMatch(destinationSgt.getValue(), sourceSgt.getValue());
+            final ClassMap oppositeClassMap = PolicyManagerUtil.createClassMap(oppositeClassMapName, oppositeMatch);
+            policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, reversedPath, PolicyManagerImpl.ActionCase.CHAIN));
+            policyWriter.cache(oppositeClassMap);
+        }
         // Create appropriate service path && remote forwarder
-        final boolean sfcPartSucessful = setSfcPart(renderedPath, policyWriter);
-        if (!sfcPartSucessful) {
+        final boolean sfcPartSuccessful = setSfcPart(servicePath, directPath, reversedPath, policyWriter);
+        if (!sfcPartSuccessful) {
             //TODO: dump particular resolvedRule-rule-peerEP-EP combinantions to status
             return;
         }
+        policyWriter.cache(classMap);
+        policyWriter.cache(policyMapEntries);
+    }
 
-        // atomic creation of symmetric policy-entries
-        final Class policyEntry = PolicyManagerUtil.createPolicyEntry(classMapName, renderedPath, PolicyManagerImpl.ActionCase.CHAIN);
-        if (!servicePath.isSymmetric()) {
-            entries.add(policyEntry);
-        } else {
-            // symmetric path is in opposite direction. Roles of renderer and peer endpoint will invert
-            final RenderedServicePath symmetricPath = ServiceChainingUtil
-                    .createSymmetricRenderedPath(servicePath, renderedPath, tenantId);
-            if (symmetricPath == null) {
-                //TODO: dump particular resolvedRule-rule-peerEP-EP combinantions to status
-                return;
-            } else {
-                final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(),
-                        sourceSgt.getValue());
-                final Class policyEntrySymmetric = PolicyManagerUtil.createPolicyEntry(oppositeClassMapName,
-                        symmetricPath, PolicyManagerImpl.ActionCase.CHAIN);
+    static void removeChainAction(final PeerEndpointWithPolicy peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt,
+                                  final Map<PolicyManagerImpl.ActionCase, Action> actionMap, PolicyWriter policyWriter) {
+        final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN);
+        final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue());
+        if (servicePath == null || servicePath.getName() == null) {
+            return;
+        }
+        final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
+        if (tenantId == null) {
+            return;
+        }
+        // Cache class-maps, appropriate policy-map entries and service-chains
+        final List<Class> policyMapEntries = new ArrayList<>();
+        final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
+        final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
+        final RspName rspName = generateRspName(servicePath, tenantId);
+        final ServiceChain serviceChain = findServiceChainToRsp(rspName);
+        policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN));
+        policyWriter.cache(classMap);
+        policyWriter.cache(serviceChain);
+        if (servicePath.isSymmetric()) {
+            final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue());
+            final ClassMap oppositeClassMap = PolicyManagerUtil.createClassMap(oppositeClassMapName, null);
+            final RspName reversedRspName = generateReversedRspName(servicePath, tenantId);
+            final ServiceChain reversedServiceChain = findServiceChainToRsp(reversedRspName);
+            policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, null, PolicyManagerImpl.ActionCase.CHAIN));
+            policyWriter.cache(oppositeClassMap);
+            policyWriter.cache(reversedServiceChain);
+        }
+        policyWriter.cache(policyMapEntries);
+        // TODO remove other sfc stuff - forwarders, etc.
+    }
 
-                entries.add(policyEntry);
-                entries.add(policyEntrySymmetric);
-            }
+    private static ServiceChain findServiceChainToRsp(final RspName rspName) {
+        // Do not actually remove rsp from DS, could be used by someone else
+        final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
+        if (renderedServicePath == null) {
+            LOG.debug("Rendered service path not found, if there is service-path created according to that rsp, " +
+                    "it cannot be removed. Rendered path name: {} ", rspName.getValue());
+            return null;
         }
-        policyWriter.cache(entries);
+        // Construct service chain with key
+        final Long pathId = renderedServicePath.getPathId();
+        final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
+        final ServiceChainBuilder serviceChainBuilder = new ServiceChainBuilder();
+        servicePathBuilder.setServicePathId(pathId)
+                .setKey(new ServicePathKey(pathId));
+        serviceChainBuilder.setServicePath(Collections.singletonList(servicePathBuilder.build()));
+        return serviceChainBuilder.build();
     }
 
-    static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId) {
+    static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId,
+                                                  final DataBroker dataBroker) {
         RenderedServicePath renderedServicePath;
         // Try to read existing RSP
-        final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp");
+        final RspName rspName = generateRspName(sfp, tenantId);
         renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
         if (renderedServicePath != null) {
             return renderedServicePath;
@@ -167,21 +229,23 @@ public class ServiceChainingUtil {
                 .build();
         renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
         LOG.info("Rendered service path {} created", rspName.getValue());
+        checkSfcRspStatus(rspName, dataBroker);
         return renderedServicePath;
     }
 
     static RenderedServicePath createSymmetricRenderedPath(final ServiceFunctionPath sfp, final RenderedServicePath rsp,
-                                                           final TenantId tenantId) {
+                                                           final TenantId tenantId, final DataBroker dataBroker) {
         RenderedServicePath reversedRenderedPath;
         // Try to read existing RSP
-        final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp-Reverse");
+        final RspName rspName = generateReversedRspName(sfp, tenantId);
         reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
         if (reversedRenderedPath != null) {
             return reversedRenderedPath;
         }
         LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", rspName.getValue());
-        reversedRenderedPath = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
+        reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(rsp);
         LOG.info("Rendered service path {} created", rspName.getValue());
+        checkSfcRspStatus(rspName, dataBroker);
         return reversedRenderedPath;
     }
 
@@ -191,16 +255,24 @@ public class ServiceChainingUtil {
      * @param mountpoint used to access specific device
      * @return true if Local Forwarder is present, false otherwise
      */
-    static boolean checkLocalForwarderPresence(DataBroker mountpoint) {
+    private static boolean checkLocalForwarderPresence(DataBroker mountpoint) {
         InstanceIdentifier<Local> localSffIid = InstanceIdentifier.builder(Native.class)
                 .child(ServiceChain.class)
                 .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
                 .child(Local.class).build();
-        ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction();
-        CheckedFuture<Optional<Local>, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION,
-                localSffIid);
         try {
+            java.util.Optional<ReadOnlyTransaction> optionalTransaction =
+                    NetconfTransactionCreator.netconfReadOnlyTransaction(mountpoint);
+            if (!optionalTransaction.isPresent()) {
+                LOG.warn("Failed to create transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            ReadOnlyTransaction transaction = optionalTransaction.get();
+            CheckedFuture<Optional<Local>, ReadFailedException> submitFuture =
+                    transaction.read(LogicalDatastoreType.CONFIGURATION,
+                    localSffIid);
             Optional<Local> optionalLocalSff = submitFuture.checkedGet();
+            transaction.close(); // Release lock
             return optionalLocalSff.isPresent();
         } catch (ReadFailedException e) {
             LOG.warn("Read transaction failed to {} ", e);
@@ -219,8 +291,14 @@ public class ServiceChainingUtil {
     public static boolean checkServicePathPresence(DataBroker mountpoint) {
         InstanceIdentifier<ServiceChain> serviceChainIid = InstanceIdentifier.builder(Native.class)
                 .child(ServiceChain.class).build();
-        ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction();
-        CheckedFuture<Optional<ServiceChain>, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION,
+        java.util.Optional<ReadOnlyTransaction> optionalTransaction =
+                NetconfTransactionCreator.netconfReadOnlyTransaction(mountpoint);
+        if (!optionalTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", mountpoint);
+            return false;
+        }
+        ReadOnlyTransaction transaction = optionalTransaction.get();
+        CheckedFuture<Optional<ServiceChain>, ReadFailedException> submitFuture = transaction.read(LogicalDatastoreType.CONFIGURATION,
                 serviceChainIid);
         try {
             Optional<ServiceChain> optionalServiceChain = submitFuture.checkedGet();
@@ -248,6 +326,14 @@ public class ServiceChainingUtil {
         return null;
     }
 
+    private static RspName generateRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
+        return new RspName(serviceFunctionPath.getName().getValue() + tenantId.getValue() + RSP_SUFFIX);
+    }
+
+    private static RspName generateReversedRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
+        return new RspName(serviceFunctionPath.getName().getValue() + tenantId.getValue() + RSP_REVERSED_SUFFIX);
+    }
+
     private static <T> Supplier<Boolean> createNegativePathWithLogSupplier(final T value, final Consumer<T> logCommand) {
         return () -> {
             // fireLog
@@ -256,42 +342,73 @@ public class ServiceChainingUtil {
         };
     }
 
-    static boolean setSfcPart(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) {
+    static boolean setSfcPart(final ServiceFunctionPath serviceFunctionPath, final RenderedServicePath renderedServicePath,
+                              final RenderedServicePath reversedRenderedServicePath, PolicyWriter policyWriter) {
+        if (!checkLocalForwarderPresence(policyWriter.getCurrentMountpoint())) {
+            appendLocalSff(policyWriter);
+        } else {
+            LOG.info("Local forwarder for node {} is already created", policyWriter.getCurrentNodeId());
+        }
+        boolean outcome = true;
+        // Direct path
         final java.util.Optional<RenderedServicePath> renderedServicePathSafe = java.util.Optional.ofNullable(renderedServicePath);
-        final java.util.Optional<RenderedServicePathHop> renderedServicePathHop = renderedServicePathSafe
-                .map(RenderedServicePath::getRenderedServicePathHop)
-                .map(rspHop -> Iterables.getFirst(rspHop, null));
-
-        final boolean outcome;
-        if (!renderedServicePathHop.isPresent()) {
-            LOG.warn("Rendered service path {} does not contain any hop",
-                    renderedServicePathSafe.map(RenderedServicePath::getName).map(RspName::getValue).orElse("n/a"));
+        if (renderedServicePathSafe.isPresent()) {
+            if (renderedServicePath.getRenderedServicePathHop() != null
+                    && !renderedServicePath.getRenderedServicePathHop().isEmpty()) {
+                if (!resolveRenderedServicePath(renderedServicePath, policyWriter)) {
+                    outcome = false;
+                }
+            }
+            else {
+                LOG.warn("Rendered service path {} does not contain any hop",
+                        renderedServicePathSafe.map(RenderedServicePath::getName).map(RspName::getValue).orElse("n/a"));
+                outcome = false;
+            }
+        }
+        else {
+            LOG.warn("Rendered service path is null");
             outcome = false;
-        } else {
-            final RenderedServicePathHop firstHop = renderedServicePathHop.get();
-            final SffName sffName = firstHop.getServiceFunctionForwarder();
-
-            // Forwarders
-            //
-            // If classifier node is also forwarder, first entry in service path has to point to first service function
-            // (Local case)
-            //
-            // If first hop Sff is on different node, first service path entry has to point to that specific service
-            // forwarder (Remote case)
-
-            // Local case (only when does not exist)
-
-            if (!checkLocalForwarderPresence(policyWriter.getCurrentMountpoint())) {
-                appendLocalSff(policyWriter);
+        }
+        if (serviceFunctionPath.isSymmetric()) {
+            // Reversed path
+            final java.util.Optional<RenderedServicePath> reversedRenderedServicePathSafe = java.util.Optional.ofNullable(reversedRenderedServicePath);
+            if (reversedRenderedServicePathSafe.isPresent()) {
+                if (reversedRenderedServicePath.getRenderedServicePathHop() != null
+                        && !reversedRenderedServicePath.getRenderedServicePathHop().isEmpty()) {
+                    if (!resolveRenderedServicePath(reversedRenderedServicePath, policyWriter)) {
+                        outcome = false;
+                    }
+                } else {
+                    LOG.warn("Rendered service path {} does not contain any hop",
+                            reversedRenderedServicePathSafe.map(RenderedServicePath::getName).map(RspName::getValue).orElse("n/a"));
+                    outcome = false;
+                }
             } else {
-                LOG.info("Local forwarder for node {} is already created", policyWriter.getCurrentNodeId());
+                LOG.warn("Reversed rendered service path is null");
+                outcome = false;
             }
+        }
+        return outcome;
+    }
 
-            // Remote case
-            final java.util.Optional<ServiceFunctionForwarder> serviceFunctionForwarder = java.util.Optional.ofNullable(
-                    SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName));
+    private static boolean resolveRenderedServicePath(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) {
+        final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
+        if (firstHop == null) {
+            return false;
+        }
+        final SffName sffName = firstHop.getServiceFunctionForwarder();
 
-            outcome = serviceFunctionForwarder.map(sff -> java.util.Optional.ofNullable(sff.getIpMgmtAddress())
+        // Forwarders
+        //
+        // If classifier node is also forwarder, first entry in service path has to point to first service function
+        // (Local case)
+        //
+        // If first hop Sff is on different node, first service path entry has to point to that specific service
+        // forwarder (Remote case)
+
+        final java.util.Optional<ServiceFunctionForwarder> serviceFunctionForwarder = java.util.Optional.ofNullable(
+                    SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName));
+        return serviceFunctionForwarder.map(sff -> java.util.Optional.ofNullable(sff.getIpMgmtAddress())
                     .map(IpAddress::getIpv4Address)
                     .map((ipv4Address) -> ipv4Address.getValue())
                     .map(addressValue -> {
@@ -310,10 +427,10 @@ public class ServiceChainingUtil {
 
                         // Service chain
                         final List<Services> services = new ArrayList<>();
-                        //TODO: servicesBuilder is never used
                         final ServicesBuilder servicesBuilder = new ServicesBuilder();
                         servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
                                 .setServiceTypeChoice(serviceTypeChoice);
+                        services.add(servicesBuilder.build());
                         final List<ServicePath> servicePaths = new ArrayList<>();
                         final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
                         servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
@@ -335,9 +452,6 @@ public class ServiceChainingUtil {
             ).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
                     (value) -> LOG.error("Sff with name {} does not exist", value))
             );
-        }
-
-        return outcome;
     }
 
     private static void appendLocalSff(final PolicyWriter policyWriter) {
@@ -359,4 +473,60 @@ public class ServiceChainingUtil {
         return sfBuilder.build();
     }
 
+    private static void checkSfcRspStatus(final RspName rspName, final DataBroker dataBroker) {
+        /** TODO A better way to do this is to register listener and wait for notification than using hardcoded timeout
+         *  with Thread.sleep(). Example in class BridgeDomainManagerImpl
+        */
+        ConfiguredRenderedPath renderedPath = null;
+        LOG.info("Waiting for SFC to configure path {} ...", rspName.getValue());
+
+        byte attempt = 0;
+        do {
+            attempt++;
+            // Wait
+            try {
+                Thread.sleep(5000L);
+            } catch (InterruptedException e) {
+                LOG.error("Thread interrupted while waiting ... {} ", e);
+            }
+            // Read actual status
+            final InstanceIdentifier<ConfiguredRenderedPath> statusIid = InstanceIdentifier.builder(RendererPathStates.class)
+                    .child(RendererPathState.class, new RendererPathStateKey(new RendererName("ios-xe-renderer")))
+                    .child(ConfiguredRenderedPaths.class)
+                    .child(ConfiguredRenderedPath.class, new ConfiguredRenderedPathKey(rspName)).build();
+            final java.util.Optional<ReadWriteTransaction> optionalTransaction =
+                    NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker);
+            if (!optionalTransaction.isPresent()) {
+                LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+                return;
+            }
+            ReadWriteTransaction transaction = optionalTransaction.get();
+            try {
+                final CheckedFuture<Optional<ConfiguredRenderedPath>, ReadFailedException> submitFuture =
+                        transaction.read(LogicalDatastoreType.OPERATIONAL, statusIid);
+                final Optional<ConfiguredRenderedPath> optionalPath = submitFuture.checkedGet();
+                if (optionalPath.isPresent()) {
+                    renderedPath = optionalPath.get();
+                }
+            } catch (ReadFailedException e) {
+                LOG.warn("Failed while read rendered path status ... {} ", e.getMessage());
+            }
+            if (renderedPath == null || renderedPath.getPathStatus() == null ||
+                    renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.InProgress)) {
+                LOG.info("Still waiting for SFC ... ");
+            } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Failure)) {
+                LOG.warn("SFC failed to configure rsp");
+            } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Success)) {
+                LOG.info("RSP {} configured by SFC", rspName.getValue());
+                try {
+                    Thread.sleep(5000); // Just for sure, maybe will be safe to remove this
+                } catch (InterruptedException e) {
+                    LOG.error("Thread interrupted while waiting ... {} ", e);
+                }
+                return;
+            }
+        }
+        while (attempt <= 6);
+        LOG.warn("Maximum number of attempts reached");
+    }
 }
diff --git a/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/NetconfTransactionCreator.java b/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/NetconfTransactionCreator.java
new file mode 100644 (file)
index 0000000..7b7b8b2
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.netconf.api.NetconfDocumentedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+/**
+ * Purpose: safely create transaction
+ */
+
+public class NetconfTransactionCreator {
+
+    private final static Logger LOG = LoggerFactory.getLogger(NetconfTransactionCreator.class);
+    private static final long TIMEOUT = 5000L;
+
+    public static Optional<ReadOnlyTransaction> netconfReadOnlyTransaction(DataBroker mountpoint) {
+        int attempt = 0;
+        do {
+            try {
+                return Optional.ofNullable(mountpoint.newReadOnlyTransaction());
+            } catch (RuntimeException e) {
+                final Optional<Throwable> optionalCause = Optional.ofNullable(e.getCause());
+                final Optional<Class> optionalCauseClass = optionalCause.map(Throwable::getClass);
+                if (optionalCauseClass.isPresent() && optionalCauseClass.get().equals(NetconfDocumentedException.class)) {
+                    attempt++;
+                    LOG.warn("NetconfDocumentedException thrown, retrying ({})...", attempt);
+                    try {
+                        Thread.sleep(TIMEOUT);
+                    } catch (InterruptedException i) {
+                        LOG.error("Thread interrupted while waiting ... {} ", i);
+                    }
+                } else {
+                    LOG.error("Runtime exception ... {}", e.getMessage(), e);
+                    return Optional.empty();
+                }
+            }
+        } while (attempt <= 5);
+        LOG.error("Maximum number of attempts reached");
+        return Optional.empty();
+    }
+
+    public static Optional<WriteTransaction> netconfWriteOnlyTransaction(DataBroker mountpoint) {
+        int attempt = 0;
+        do {
+            try {
+                return Optional.of(mountpoint.newWriteOnlyTransaction());
+            } catch (RuntimeException e) {
+                final Optional<Throwable> optionalCause = Optional.ofNullable(e.getCause());
+                final Optional<Class> optionalCauseClass = optionalCause.map(Throwable::getClass);
+                if (optionalCauseClass.isPresent() && optionalCauseClass.get().equals(NetconfDocumentedException.class)) {
+                    attempt++;
+                    LOG.warn("NetconfDocumentedException thrown, retrying ({})...", attempt);
+                    try {
+                        Thread.sleep(TIMEOUT);
+                    } catch (InterruptedException i) {
+                        LOG.error("Thread interrupted while waiting ... {} ", i);
+                    }
+                } else {
+                    LOG.error("Runtime exception ... {}", e.getMessage());
+                    return Optional.empty();
+                }
+            }
+        } while (attempt <= 5);
+        LOG.error("Maximum number of attempts reached");
+        return Optional.empty();
+    }
+
+    public static Optional<ReadWriteTransaction> netconfReadWriteTransaction(DataBroker mountpoint) {
+        int attempt = 0;
+        do {
+            try {
+                return Optional.of(mountpoint.newReadWriteTransaction());
+            } catch (RuntimeException e) {
+                final Optional<Throwable> optionalCause = Optional.ofNullable(e.getCause());
+                final Optional<Class> optionalCauseClass = optionalCause.map(Throwable::getClass);
+                if (optionalCauseClass.isPresent() && optionalCauseClass.get().equals(NetconfDocumentedException.class)) {
+                    attempt++;
+                    LOG.warn("NetconfDocumentedException thrown, retrying ({})...", attempt);
+                    try {
+                        Thread.sleep(TIMEOUT);
+                    } catch (InterruptedException i) {
+                        LOG.error("Thread interrupted while waiting ... {} ", i);
+                    }
+                } else {
+                    LOG.error("Runtime exception ... {}", e.getMessage());
+                    return Optional.empty();
+                }
+            }
+        } while (attempt <= 5);
+        LOG.error("Maximum number of attempts reached");
+        return Optional.empty();
+    }
+}
index c7443d99d7ee6f5c0847be5c63115f70137cf05c..bf4cd266c0fdd2ffda9bf3ee42993437a1734603 100644 (file)
@@ -28,11 +28,12 @@ import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 public class NodeWriter {
 
     private static final Logger LOG = LoggerFactory.getLogger(NodeWriter.class);
-    private List<RendererNode> rendererNodesCache;
+    private final List<RendererNode> rendererNodesCache;
 
     public NodeWriter() {
         rendererNodesCache = new ArrayList<>();
@@ -49,11 +50,17 @@ public class NodeWriter {
      */
     public void commitToDatastore(DataBroker dataBroker) {
         RendererNodes rendererNodes = buildRendererNodes();
-        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
-        InstanceIdentifier<RendererNodes> iid = buildRendererNodesIid();
+        final Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(dataBroker);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+            return;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+        final InstanceIdentifier<RendererNodes> iid = buildRendererNodesIid();
         try {
-            wtx.merge(LogicalDatastoreType.OPERATIONAL, iid, rendererNodes, true);
-            CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wtx.submit();
+            writeTransaction.merge(LogicalDatastoreType.OPERATIONAL, iid, rendererNodes, true);
+            CheckedFuture<Void, TransactionCommitFailedException> submitFuture = writeTransaction.submit();
             submitFuture.checkedGet();
             // Clear cache
             rendererNodesCache.clear();
@@ -70,12 +77,18 @@ public class NodeWriter {
      * @param dataBroker appropriate data provider
      */
     public void removeFromDatastore(DataBroker dataBroker) {
-        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        final Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(dataBroker);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
+            return;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
         for (RendererNode nodeToRemove : rendererNodesCache) {
             InstanceIdentifier<RendererNode> iid = buildRendererNodeIid(nodeToRemove);
             try {
-                wtx.delete(LogicalDatastoreType.OPERATIONAL, iid);
-                CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wtx.submit();
+                writeTransaction.delete(LogicalDatastoreType.OPERATIONAL, iid);
+                CheckedFuture<Void, TransactionCommitFailedException> submitFuture = writeTransaction.submit();
                 submitFuture.checkedGet();
                 // Clear cache
             } catch (TransactionCommitFailedException e) {
index 4f387bb94334c6718c12f0873378bec9b453f28f..67948290eca79871c2c127bb51cf3094cf307709 100644 (file)
@@ -10,33 +10,15 @@ package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer;
 
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil;
-import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.ServiceChainingUtil;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._interface.common.grouping.ServicePolicy;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._interface.common.grouping.service.policy.type.ServiceChain.Direction;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMapKey;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.Interface;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.PolicyMap;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.PolicyMapKey;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._interface.GigabitEthernet;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._interface.GigabitEthernetKey;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
-import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -93,141 +75,37 @@ public class PolicyWriter {
         serviceChains.add(serviceChain);
     }
 
-    public CheckedFuture<Void, TransactionCommitFailedException> commitToDatastore() {
-        WriteTransaction wtx = mountpoint.newWriteOnlyTransaction();
-        // GBP
-        // Class maps
-        for (ClassMap entry : classMapEntries) {
-            InstanceIdentifier<ClassMap> classMapIid = classMapInstanceIdentifier(entry);
-            wtx.merge(LogicalDatastoreType.CONFIGURATION, classMapIid, entry);
-            LOG.info("Created class-map {} on node {}", entry.getName(), nodeId.getValue());
-        }
-
-        // Policy map
-        PolicyMap policyMap = PolicyManagerUtil.createPolicyMap(policyMapName, policyMapEntries);
-        InstanceIdentifier<PolicyMap> policyMapIid = policyMapInstanceIdentifier();
-        wtx.merge(LogicalDatastoreType.CONFIGURATION, policyMapIid, policyMap);
-        LOG.info("Created policy-map {} on node {}", policyMap.getName(), nodeId.getValue());
-
-        // Interface
-        ServicePolicy servicePolicy = PolicyManagerUtil.createServicePolicy(policyMapName, Direction.Input);
-        InstanceIdentifier<ServicePolicy> servicePolicyIid = interfaceInstanceIdentifier(interfaceName);
-        wtx.merge(LogicalDatastoreType.CONFIGURATION, servicePolicyIid, servicePolicy);
-        LOG.info("Service-policy interface {}, bound to policy-map {} created on  node {}",
-                interfaceName, policyMap.getName(), nodeId.getValue());
-
-        //SFC
-        // Local forwarder (if some service chain exists, otherwise is useless)
-        if (!serviceChains.isEmpty()) {
-            InstanceIdentifier<Local> localIid = localSffInstanceIdentifier();
-            wtx.merge(LogicalDatastoreType.CONFIGURATION, localIid, localForwarder);
-            LOG.info("Local forwarder created on node {}", nodeId.getValue());
-        }
-
-        // Remote forwarders
-        for (ServiceFfName forwarder : remoteForwarders) {
-            InstanceIdentifier<ServiceFfName> forwarderIid = remoteSffInstanceIdentifier(forwarder);
-            wtx.merge(LogicalDatastoreType.CONFIGURATION, forwarderIid, forwarder);
-            LOG.info("Remote forwarder {} created on node {}", forwarder.getName(), nodeId.getValue());
-        }
-
-        // Service paths
-        for (ServiceChain serviceChain : serviceChains) {
-            for (ServicePath entry : serviceChain.getServicePath()) {
-                InstanceIdentifier<ServicePath> servicePathIid = servicePathInstanceIdentifier(entry.getKey());
-                wtx.merge(LogicalDatastoreType.CONFIGURATION, servicePathIid, entry);
-                LOG.info("Service path with Id {} created on node {}", entry.getServicePathId(), nodeId.getValue());
-            }
-        }
-
-        return wtx.submit();
-    }
-
-    public CheckedFuture<Void, TransactionCommitFailedException> removeFromDatastore() {
-        ReadWriteTransaction wtx = mountpoint.newReadWriteTransaction();
-        //GBP
-        // Interface
-        InstanceIdentifier<ServicePolicy> servicePolicyIid = interfaceInstanceIdentifier(interfaceName);
-        wtx.delete(LogicalDatastoreType.CONFIGURATION, servicePolicyIid);
-        LOG.info("Service-policy removed from interface {} on node {}", interfaceName, nodeId.getValue());
-
-        // Policy map
-        InstanceIdentifier<PolicyMap> policyMapIid = policyMapInstanceIdentifier();
-        wtx.delete(LogicalDatastoreType.CONFIGURATION, policyMapIid);
-        LOG.info("Policy-map removed from node node {}", nodeId.getValue());
-
-        // Class map
-        for (ClassMap entry : classMapEntries) {
-            InstanceIdentifier<ClassMap> classMapIid = classMapInstanceIdentifier(entry);
-            wtx.delete(LogicalDatastoreType.CONFIGURATION, classMapIid);
-            LOG.info("Class-map {} removed from node {}", entry.getName(), nodeId.getValue());
-        }
-
-        //SFC
-        // Service paths
-        for (ServiceChain serviceChain : serviceChains) {
-            for (ServicePath entry : serviceChain.getServicePath()) {
-                InstanceIdentifier<ServicePath> servicePathIid = servicePathInstanceIdentifier(entry.getKey());
-                wtx.delete(LogicalDatastoreType.CONFIGURATION, servicePathIid);
-                LOG.info("Service path with Id {} removed from node {}", entry.getServicePathId(), nodeId.getValue());
-            }
-        }
-
-        // Remote forwarders
-        for (ServiceFfName forwarder : remoteForwarders) {
-            InstanceIdentifier<ServiceFfName> forwarderIid = remoteSffInstanceIdentifier(forwarder);
-            wtx.delete(LogicalDatastoreType.CONFIGURATION, forwarderIid);
-            LOG.info("Remote forwarder {} removed from node {}", forwarder.getName(), nodeId.getValue());
-        }
-
-        // Local forwarder - remove only if there is no more service-paths on device. If paths removed above were last
-        // ones, remove local forwarder. If there are still some paths present, they were created by sfc and local
-        // forwarder cannot be removed (because it was created by sfc as well)
-        if (ServiceChainingUtil.checkServicePathPresence(mountpoint)) {
-            InstanceIdentifier<Local> localIid = localSffInstanceIdentifier();
-            wtx.delete(LogicalDatastoreType.CONFIGURATION, localIid);
-            LOG.info("Local forwarder removed from node {}", nodeId.getValue());
-        }
-
-        return wtx.submit();
-    }
-
-    private InstanceIdentifier<ClassMap> classMapInstanceIdentifier(ClassMap classMap) {
-        return InstanceIdentifier.builder(Native.class)
-                .child(ClassMap.class, new ClassMapKey(classMap.getName())).build();
-    }
-
-    private InstanceIdentifier<PolicyMap> policyMapInstanceIdentifier() {
-        return InstanceIdentifier.builder(Native.class)
-                .child(PolicyMap.class, new PolicyMapKey(policyMapName)).build();
-    }
-
-    private InstanceIdentifier<ServicePolicy> interfaceInstanceIdentifier(String ethernetName) {
-        return InstanceIdentifier.builder(Native.class)
-                .child(Interface.class)
-                .child(GigabitEthernet.class, new GigabitEthernetKey(ethernetName))
-                .child(ServicePolicy.class)
-                .build();
-    }
-
-    private InstanceIdentifier<Local> localSffInstanceIdentifier() {
-        return InstanceIdentifier.builder(Native.class)
-                .child(ServiceChain.class)
-                .child(ServiceFunctionForwarder.class)
-                .child(Local.class).build();
-    }
-
-    private InstanceIdentifier<ServiceFfName> remoteSffInstanceIdentifier(ServiceFfName sffName) {
-        return InstanceIdentifier.builder(Native.class)
-                .child(ServiceChain.class)
-                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
-                .child(ServiceFfName.class, new ServiceFfNameKey(sffName.getName())).build();
-    }
-
-    private InstanceIdentifier<ServicePath> servicePathInstanceIdentifier(ServicePathKey key) {
-        return InstanceIdentifier.builder(Native.class)
-                .child(ServiceChain.class)
-                .child(ServicePath.class, key).build();
+    public CheckedFuture<Boolean, TransactionCommitFailedException> commitToDatastore() {
+        LOG.info("Configuring policy on node {} ... ", nodeId.getValue());
+        // SFC
+        boolean localResult = PolicyWriterUtil.writeLocal(localForwarder, nodeId, mountpoint);
+        boolean remoteResult = PolicyWriterUtil.writeRemote(remoteForwarders, nodeId, mountpoint);
+        boolean servicePathsResult = PolicyWriterUtil.writeServicePaths(serviceChains, nodeId, mountpoint);
+        // GBP - maintain order!
+        boolean classMapResult = PolicyWriterUtil.writeClassMaps(classMapEntries, nodeId, mountpoint);
+        boolean policyMapResult = PolicyWriterUtil.writePolicyMap(policyMapName, policyMapEntries, nodeId, mountpoint);
+        boolean interfaceResult = PolicyWriterUtil.writeInterface(policyMapName, interfaceName, nodeId, mountpoint);
+        // Result
+        LOG.info("Policy configuration on node {} completed", nodeId.getValue());
+        return Futures.immediateCheckedFuture(classMapResult && policyMapResult && interfaceResult && localResult
+                && remoteResult && servicePathsResult);
+    }
+
+    public CheckedFuture<Boolean, TransactionCommitFailedException> removeFromDatastore() {
+        LOG.info("Removing policy from node {} ... ", nodeId.getValue());
+        // GBP - maintain order!
+        boolean policyMapEntriesResult = PolicyWriterUtil.removePolicyMapEntries(policyMapName, policyMapEntries,
+                nodeId, mountpoint);
+        boolean classMapResult = PolicyWriterUtil.removeClassMaps(classMapEntries, nodeId, mountpoint);
+        // TODO remove class map?
+        // SFC
+        boolean servicePathsResult = PolicyWriterUtil.removeServicePaths(serviceChains, nodeId, mountpoint);
+        boolean localResult = PolicyWriterUtil.removeLocal(nodeId, mountpoint);
+        // TODO remove remote forwarders
+        // Result
+        LOG.info("Policy removed from node {}", nodeId.getValue());
+        return Futures.immediateCheckedFuture(classMapResult && policyMapEntriesResult && servicePathsResult
+                && localResult);
     }
 
     public String getManagementIpAddress() {
diff --git a/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/PolicyWriterUtil.java b/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/writer/PolicyWriterUtil.java
new file mode 100644 (file)
index 0000000..45e3bb8
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.ServiceChainingUtil;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308.ClassNameType;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._interface.common.grouping.ServicePolicy;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._interface.common.grouping.service.policy.type.ServiceChain.Direction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMapKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.Interface;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.PolicyMap;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.PolicyMapKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._interface.GigabitEthernet;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._interface.GigabitEthernetKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.ClassKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Purpose: Util class for every policy writer
+ */
+class PolicyWriterUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PolicyWriterUtil.class);
+
+    static boolean writeClassMaps(final List<ClassMap> classMapEntries, final NodeId nodeId, final DataBroker mountpoint) {
+        if (classMapEntries == null || classMapEntries.isEmpty()) {
+            return true;
+        }
+        for (ClassMap entry : classMapEntries) {
+            final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                    NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+            if (!optionalWriteTransaction.isPresent()) {
+                LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+            final InstanceIdentifier<ClassMap> classMapIid = classMapInstanceIdentifier(entry);
+            writeMergeTransaction(writeTransaction, classMapIid, entry);
+            // Check
+            final java.util.Optional<ReadOnlyTransaction> optionalTransaction =
+                    NetconfTransactionCreator.netconfReadOnlyTransaction(mountpoint);
+            if (!optionalTransaction.isPresent()) {
+                LOG.warn("Failed to create read-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final ReadOnlyTransaction readTransaction = optionalTransaction.get();
+            if (checkWritten(readTransaction, classMapIid) == null) {
+                return false;
+            }
+            LOG.info("Created class-map {} on node {}", entry.getName(), nodeId.getValue());
+        }
+        return true;
+    }
+
+    static boolean removeClassMaps(final List<ClassMap> classMapEntries, final NodeId nodeId, final DataBroker mountpoint) {
+        boolean result = true;
+        if (classMapEntries == null || classMapEntries.isEmpty()) {
+            return true;
+        }
+        for (ClassMap entry : classMapEntries) {
+            final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                    NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+            if (!optionalWriteTransaction.isPresent()) {
+                LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+            final InstanceIdentifier<ClassMap> classMapIid = classMapInstanceIdentifier(entry);
+            deleteTransaction(writeTransaction, classMapIid);
+            // Check
+            final java.util.Optional<ReadOnlyTransaction> optionalReadTransaction =
+                    NetconfTransactionCreator.netconfReadOnlyTransaction(mountpoint);
+            if (!optionalReadTransaction.isPresent()) {
+                LOG.warn("Failed to create read-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final ReadOnlyTransaction readTransaction = optionalReadTransaction.get();
+            result = checkRemoved(readTransaction, classMapIid);
+            LOG.info("Class-map {} removed from node {}", entry.getName(), nodeId.getValue());
+        }
+        return result;
+    }
+
+    static boolean writePolicyMap(final String policyMapName, final List<Class> policyMapEntries, NodeId nodeId,
+                                  final DataBroker mountpoint) {
+        final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+            return false;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+        final PolicyMap policyMap = PolicyManagerUtil.createPolicyMap(policyMapName, policyMapEntries);
+        final InstanceIdentifier<PolicyMap> policyMapIid = policyMapInstanceIdentifier(policyMapName);
+        writeMergeTransaction(writeTransaction, policyMapIid, policyMap);
+        // Check
+        final java.util.Optional<ReadOnlyTransaction> optionalReadTransaction =
+                NetconfTransactionCreator.netconfReadOnlyTransaction(mountpoint);
+        if (!optionalReadTransaction.isPresent()) {
+            LOG.warn("Failed to create read-only transaction, mountpoint: {}", mountpoint);
+            return false;
+        }
+        final ReadOnlyTransaction readTransaction = optionalReadTransaction.get();
+        if (checkWritten(readTransaction, policyMapIid) == null) {
+            return false;
+        }
+        LOG.info("Created policy-map {} on node {}", policyMap.getName(), nodeId.getValue());
+        return true;
+    }
+
+    static boolean removePolicyMapEntries(final String policyMapName, final List<Class> policyMapEntries,
+                                          final NodeId nodeId, final DataBroker mountpoint) {
+        if (policyMapEntries == null || policyMapEntries.isEmpty()) {
+            return true;
+        }
+        for (Class entry : policyMapEntries) {
+            final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                    NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+            if (!optionalWriteTransaction.isPresent()) {
+                LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+            final InstanceIdentifier policyMapEntryIid = policyMapEntryInstanceIdentifier(policyMapName, entry.getName());
+            if (deleteTransaction(writeTransaction, policyMapEntryIid)) {
+                LOG.info("Policy map entry {} removed from node {}", entry.getName(), nodeId.getValue());
+            }
+        }
+        return true;
+    }
+
+    static boolean writeInterface(final String policyMapName, final String interfaceName, final NodeId nodeId,
+                                  final DataBroker mountpoint) {
+        final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+            return false;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+        final ServicePolicy servicePolicy = PolicyManagerUtil.createServicePolicy(policyMapName, Direction.Input);
+        final InstanceIdentifier<ServicePolicy> servicePolicyIid = interfaceInstanceIdentifier(interfaceName);
+        writeMergeTransaction(writeTransaction, servicePolicyIid, servicePolicy);
+        LOG.info("Service-policy interface {}, bound to policy-map {} created on  node {}",
+                interfaceName, policyMapName, nodeId.getValue());
+        return true;
+    }
+
+    static boolean writeLocal(final Local localForwarder, final NodeId nodeId, final DataBroker mountpoint) {
+        if (localForwarder == null) {
+            return true;
+        }
+        final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+        if (!optionalWriteTransaction.isPresent()) {
+            LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+            return false;
+        }
+        final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+        final InstanceIdentifier<Local> localIid = localSffInstanceIdentifier();
+        writeMergeTransaction(writeTransaction, localIid, localForwarder);
+        LOG.info("Local forwarder created on node {}", nodeId.getValue());
+        return true;
+    }
+
+    static boolean removeLocal(final NodeId nodeId, final DataBroker mountpoint) {
+        // Remove local forwarder only when there are no more service-paths
+        if (ServiceChainingUtil.checkServicePathPresence(mountpoint)) {
+            final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                    NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+            if (!optionalWriteTransaction.isPresent()) {
+                LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+            final InstanceIdentifier<Local> localIid = localSffInstanceIdentifier();
+            deleteTransaction(writeTransaction, localIid);
+            LOG.info("Local forwarder removed from node {}", nodeId.getValue());
+        }
+        return true;
+    }
+
+    static boolean writeRemote(final List<ServiceFfName> remoteForwarders, final NodeId nodeId,
+                               final DataBroker mountpoint) {
+        if (remoteForwarders == null || remoteForwarders.isEmpty()) {
+            return true;
+        }
+        for (ServiceFfName forwarder : remoteForwarders) {
+            final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                    NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+            if (!optionalWriteTransaction.isPresent()) {
+                LOG.warn("Failed to create transaction, mountpoint: {}", mountpoint);
+                return false;
+            }
+            final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+            final InstanceIdentifier<ServiceFfName> forwarderIid = remoteSffInstanceIdentifier(forwarder);
+            writeMergeTransaction(writeTransaction, forwarderIid, forwarder);
+            LOG.info("Remote forwarder {} created on node {}", forwarder.getName(), nodeId.getValue());
+        }
+        return true;
+    }
+
+    static boolean writeServicePaths(final List<ServiceChain> serviceChains, final NodeId nodeId,
+                                     final DataBroker mountpoint) {
+        for (org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain serviceChain : serviceChains) {
+            for (ServicePath entry : serviceChain.getServicePath()) {
+                final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                        NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+                if (!optionalWriteTransaction.isPresent()) {
+                    LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                    return false;
+                }
+                final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+                final InstanceIdentifier<ServicePath> servicePathIid = servicePathInstanceIdentifier(entry.getKey());
+                writeMergeTransaction(writeTransaction, servicePathIid, entry);
+                LOG.info("Service path with ID: {} created on node {}", entry.getServicePathId(), nodeId.getValue());
+            }
+        }
+        return true;
+    }
+
+    static boolean removeServicePaths(final List<ServiceChain> serviceChains, final NodeId nodeId,
+                                      final DataBroker mountpoint) {
+        if (serviceChains == null || serviceChains.isEmpty()) {
+            return true;
+        }
+        for (ServiceChain chain : serviceChains) {
+            List<ServicePath> servicePaths = chain.getServicePath();
+            if (servicePaths == null || servicePaths.isEmpty()) {
+                continue;
+            }
+            for (ServicePath servicePath : servicePaths) {
+                final java.util.Optional<WriteTransaction> optionalWriteTransaction =
+                        NetconfTransactionCreator.netconfWriteOnlyTransaction(mountpoint);
+                if (!optionalWriteTransaction.isPresent()) {
+                    LOG.warn("Failed to create write-only transaction, mountpoint: {}", mountpoint);
+                    return false;
+                }
+                final WriteTransaction writeTransaction = optionalWriteTransaction.get();
+                final InstanceIdentifier<ServicePath> servicePathIid = servicePathInstanceIdentifier(servicePath.getKey());
+                if (deleteTransaction(writeTransaction, servicePathIid)) {
+                    LOG.info("Service-path with ID: {} removed from node {}", servicePath.getServicePathId(),
+                            nodeId.getValue());
+                }
+            }
+        }
+        return true;
+    }
+
+    private static InstanceIdentifier<ClassMap> classMapInstanceIdentifier(final ClassMap classMap) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ClassMap.class, new ClassMapKey(classMap.getName())).build();
+    }
+
+    private static InstanceIdentifier<PolicyMap> policyMapInstanceIdentifier(final String policyMapName) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(PolicyMap.class, new PolicyMapKey(policyMapName)).build();
+    }
+
+    private static InstanceIdentifier<Class> policyMapEntryInstanceIdentifier(final String policyMapName,
+                                                                              final ClassNameType classNameType) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(PolicyMap.class, new PolicyMapKey(policyMapName))
+                .child(Class.class, new ClassKey(classNameType)).build();
+    }
+
+    private static InstanceIdentifier<ServicePolicy> interfaceInstanceIdentifier(final String ethernetName) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(Interface.class)
+                .child(GigabitEthernet.class, new GigabitEthernetKey(ethernetName))
+                .child(ServicePolicy.class)
+                .build();
+    }
+
+    private static InstanceIdentifier<Local> localSffInstanceIdentifier() {
+        return InstanceIdentifier.builder(Native.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain.class)
+                .child(ServiceFunctionForwarder.class)
+                .child(Local.class).build();
+    }
+
+    private static InstanceIdentifier<ServiceFfName> remoteSffInstanceIdentifier(final ServiceFfName sffName) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(ServiceFfName.class, new ServiceFfNameKey(sffName.getName())).build();
+    }
+
+    private static InstanceIdentifier<ServicePath> servicePathInstanceIdentifier(final ServicePathKey key) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain.class)
+                .child(ServicePath.class, key).build();
+    }
+
+    private static <U extends DataObject> void writeMergeTransaction(final WriteTransaction transaction,
+                                                                     final InstanceIdentifier<U> addIID,
+                                                                     final U data) {
+        try {
+            transaction.merge(LogicalDatastoreType.CONFIGURATION, addIID, data);
+            final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = transaction.submit();
+            submitFuture.checkedGet();
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("Write transaction failed to {}", e.getMessage());
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+        }
+    }
+
+    private static <U extends DataObject> boolean deleteTransaction(final WriteTransaction transaction,
+                                                                    final InstanceIdentifier<U> addIID) {
+        try {
+            transaction.delete(LogicalDatastoreType.CONFIGURATION, addIID);
+            final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = transaction.submit();
+            submitFuture.checkedGet();
+            return true;
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("Write transaction failed to {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private static <U extends DataObject> U checkWritten(final ReadOnlyTransaction transaction,
+                                                         final InstanceIdentifier<U> readIID) {
+        for (int attempt = 1; attempt <= 5; attempt++) {
+            try {
+                final CheckedFuture<Optional<U>, ReadFailedException> submitFuture =
+                        transaction.read(LogicalDatastoreType.CONFIGURATION, readIID);
+                final Optional<U> optional = submitFuture.checkedGet();
+                if (optional != null && optional.isPresent()) {
+                    transaction.close(); // Release lock
+                    return optional.get();
+                } else {
+                    // Could take some time until specific configuration appears on device, try to read a few times
+                    Thread.sleep(2000L);
+                }
+            } catch (InterruptedException i) {
+                LOG.error("Thread interrupted while waiting ... {} ", i);
+            } catch (ReadFailedException e) {
+                LOG.warn("Read transaction failed to {} ", e);
+            } catch (Exception e) {
+                LOG.error("Failed to .. {}", e.getMessage());
+            }
+        }
+        return null;
+    }
+
+    private static <U extends DataObject> boolean checkRemoved(final ReadOnlyTransaction transaction,
+                                                               final InstanceIdentifier<U> readIID) {
+        for (int attempt = 1; attempt <= 5; attempt++) {
+            try {
+                final CheckedFuture<Optional<U>, ReadFailedException> submitFuture =
+                        transaction.read(LogicalDatastoreType.CONFIGURATION, readIID);
+                final Optional<U> optional = submitFuture.checkedGet();
+                if (optional != null && optional.isPresent()) {
+                    // Could take some time until specific configuration is removed from the device
+                    Thread.sleep(2000L);
+                } else {
+                    transaction.close(); // Release lock
+                    return true;
+                }
+            } catch (InterruptedException i) {
+                LOG.error("Thread interrupted while waiting ... {} ", i);
+            } catch (ReadFailedException e) {
+                LOG.warn("Read transaction failed to {} ", e);
+            } catch (Exception e) {
+                LOG.error("Failed to .. {}", e.getMessage());
+            }
+        }
+        return false;
+    }
+
+}
index fba4c12a67e2b2d07911d59f3d1fb79e99344c64..2e5b5a78329c5e57a48fb0cbe45b9aa4cd3428e6 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev1407
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
@@ -102,7 +103,8 @@ public class PolicyManagerUtilTest {
         Mockito.when(roTx.read(Matchers.eq(LogicalDatastoreType.OPERATIONAL), rendererServicePathIICaptor.capture()))
                 .thenReturn(Futures.immediateCheckedFuture(Optional.of(renderedSP)));
 
-        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(serviceFunctionPath, tenantId);
+        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(serviceFunctionPath, tenantId,
+                dataBroker);
         Assert.assertEquals(renderedSP, renderedPath);
         final InstanceIdentifier<RenderedServicePath> ii = rendererServicePathIICaptor.getValue();
         Assert.assertEquals("sfp-name-01tenant-id-01-gbp-rsp", ii.firstKeyOf(RenderedServicePath.class).getName().getValue());
@@ -119,9 +121,15 @@ public class PolicyManagerUtilTest {
 
 
         final RenderedServicePath symmetricRenderedPath = ServiceChainingUtil.createSymmetricRenderedPath(
-                serviceFunctionPath, renderedServicePath, tenantId);
+                serviceFunctionPath, renderedServicePath, tenantId, dataBroker);
         Assert.assertEquals(renderedServicePath, symmetricRenderedPath);
         final InstanceIdentifier<RenderedServicePath> ii = rendererServicePathIICaptor.getValue();
         Assert.assertEquals("sfp-name-01tenant-id-02-gbp-rsp-Reverse", ii.firstKeyOf(RenderedServicePath.class).getName().getValue());
     }
+
+    @Test
+    public void testMatch() {
+        Match result = PolicyManagerUtil.createSecurityGroupMatch(10, 20);
+        assertNotNull(result);
+    }
 }
\ No newline at end of file
index 2b436b6e4bb521e2909b9bdacfbdee8aa8accf56..9e3096e5c1b21a1cb8b05f255b53a6b10d961627 100644 (file)
@@ -27,12 +27,15 @@ import org.mockito.Captor;
 import org.mockito.Matchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.internal.verification.Times;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
+import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
@@ -54,6 +57,7 @@ import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev1407
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
@@ -88,7 +92,8 @@ import org.powermock.modules.junit4.PowerMockRunner;
         ServiceChainingUtil.class,
         SfcProviderServicePathAPI.class,
         SfcProviderRenderedPathAPI.class,
-        SfcProviderServiceForwarderAPI.class
+        SfcProviderServiceForwarderAPI.class,
+        NetconfTransactionCreator.class
 })
 public class ServiceChainingUtilTest {
 
@@ -104,6 +109,8 @@ public class ServiceChainingUtilTest {
     private ArgumentCaptor<RenderedServicePath> rspCaptor;
     @Captor
     private ArgumentCaptor<List<Class>> listClassCaptor;
+    @Captor
+    private ArgumentCaptor<ClassMap> classMapCaptor;
     @Mock
     private PolicyWriter policyWriter;
     @Mock
@@ -163,8 +170,9 @@ public class ServiceChainingUtilTest {
         stub(method(SfcProviderRenderedPathAPI.class, "readRenderedServicePath")).toReturn(rsp);
         stub(method(ServiceChainingUtil.class, "setSfcPart")).toReturn(true);
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
 
+        Mockito.verify(policyWriter).cache(classMapCaptor.capture());
         Mockito.verify(policyWriter).cache(listClassCaptor.capture());
         Mockito.verifyNoMoreInteractions(policyWriter);
         Assert.assertEquals(1, listClassCaptor.getValue().size());
@@ -190,8 +198,9 @@ public class ServiceChainingUtilTest {
         stub(method(SfcProviderRenderedPathAPI.class, "readRenderedServicePath")).toReturn(rsp);
         stub(method(ServiceChainingUtil.class, "setSfcPart")).toReturn(true);
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
 
+        Mockito.verify(policyWriter, Mockito.times(2)).cache(classMapCaptor.capture());
         Mockito.verify(policyWriter).cache(listClassCaptor.capture());
         Mockito.verifyNoMoreInteractions(policyWriter);
         Assert.assertEquals(2, listClassCaptor.getValue().size());
@@ -218,8 +227,9 @@ public class ServiceChainingUtilTest {
         stub(method(ServiceChainingUtil.class, "setSfcPart")).toReturn(true);
         stub(method(ServiceChainingUtil.class, "createSymmetricRenderedPath")).toReturn(null);
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
-
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
+        Mockito.verify(policyWriter, Mockito.times(2)).cache(classMapCaptor.capture());
+        Mockito.verify(policyWriter).cache(listClassCaptor.capture());
         Mockito.verifyNoMoreInteractions(policyWriter);
     }
 
@@ -243,7 +253,7 @@ public class ServiceChainingUtilTest {
         stub(method(SfcProviderRenderedPathAPI.class, "readRenderedServicePath")).toReturn(rsp);
         stub(method(ServiceChainingUtil.class, "setSfcPart")).toReturn(false);
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
 
         Mockito.verifyNoMoreInteractions(policyWriter);
     }
@@ -267,7 +277,7 @@ public class ServiceChainingUtilTest {
         final RenderedServicePath rsp = createRsp("unit-rsp-02");
         stub(method(SfcProviderRenderedPathAPI.class, "readRenderedServicePath")).toReturn(rsp);
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
 
         Mockito.verifyNoMoreInteractions(policyWriter);
     }
@@ -280,7 +290,7 @@ public class ServiceChainingUtilTest {
         final Map<PolicyManagerImpl.ActionCase, Action> actionMap = createActionMap(false);
         final String classMapName = "unit-class-map-name-01";
 
-        ServiceChainingUtil.resolveChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, classMapName, policyWriter);
+        ServiceChainingUtil.resolveNewChainAction(peerEndpoint, sourceSgt, destinationSgt, actionMap, policyWriter, dataBroker);
 
         Mockito.verifyNoMoreInteractions(policyWriter);
     }
@@ -328,7 +338,7 @@ public class ServiceChainingUtilTest {
         final SfcProviderRenderedPathAPI api = PowerMockito.mock(SfcProviderRenderedPathAPI.class);
         PowerMockito.when(api.readRenderedServicePath(rspNameCaptor.capture())).thenReturn(rsp);
 
-        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(sfp, tenantId);
+        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(sfp, tenantId, dataBroker);
 
         Assert.assertEquals("123_plainunit-tennant-01-gbp-rsp", rspNameCaptor.getValue().getValue());
         Assert.assertEquals(rsp, renderedPath);
@@ -348,8 +358,11 @@ public class ServiceChainingUtilTest {
         PowerMockito.when(api.createRenderedServicePathAndState(
                 sfpCaptor.capture(), createRspCaptor.capture()
         )).thenReturn(rsp);
+        PowerMockito.mockStatic(NetconfTransactionCreator.class);
+        final NetconfTransactionCreator creator = PowerMockito.mock(NetconfTransactionCreator.class);
+        PowerMockito.when(creator.netconfReadWriteTransaction(dataBroker)).thenReturn(java.util.Optional.empty());
 
-        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(sfp, tenantId);
+        final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(sfp, tenantId, dataBroker);
 
         Assert.assertEquals("123_plainunit-tennant-01-gbp-rsp", rspNameCaptor.getValue().getValue());
 
@@ -397,7 +410,7 @@ public class ServiceChainingUtilTest {
         final SfcProviderRenderedPathAPI api = PowerMockito.mock(SfcProviderRenderedPathAPI.class);
         PowerMockito.when(api.readRenderedServicePath(rspNameCaptor.capture())).thenReturn(rsp);
 
-        final RenderedServicePath symmetricRenderedPath = ServiceChainingUtil.createSymmetricRenderedPath(sfp, rsp, tennantId);
+        final RenderedServicePath symmetricRenderedPath = ServiceChainingUtil.createSymmetricRenderedPath(sfp, rsp, tennantId, dataBroker);
 
         Assert.assertEquals("unit-sfp-02_plaintenant-02-gbp-rsp-Reverse", rspNameCaptor.getValue().getValue());
         Assert.assertEquals(rsp, symmetricRenderedPath);
@@ -412,54 +425,18 @@ public class ServiceChainingUtilTest {
         PowerMockito.mockStatic(SfcProviderRenderedPathAPI.class);
         final SfcProviderRenderedPathAPI api = PowerMockito.mock(SfcProviderRenderedPathAPI.class);
         PowerMockito.when(api.readRenderedServicePath(rspNameCaptor.capture())).thenReturn(null);
-        PowerMockito.when(api.createSymmetricRenderedServicePathAndState(rspCaptor.capture())).thenReturn(rsp);
+        PowerMockito.when(api.createReverseRenderedServicePathEntry(rspCaptor.capture())).thenReturn(rsp);
+        PowerMockito.mockStatic(NetconfTransactionCreator.class);
+        final NetconfTransactionCreator creator = PowerMockito.mock(NetconfTransactionCreator.class);
+        PowerMockito.when(creator.netconfReadWriteTransaction(dataBroker)).thenReturn(java.util.Optional.empty());
 
-        final RenderedServicePath symmetricRenderedPath = ServiceChainingUtil.createSymmetricRenderedPath(sfp, rsp, tennantId);
+        final RenderedServicePath symmetricRenderedPath = ServiceChainingUtil.createSymmetricRenderedPath(sfp, rsp, tennantId, dataBroker);
 
         Assert.assertEquals("unit-sfp-02_plaintenant-02-gbp-rsp-Reverse", rspNameCaptor.getValue().getValue());
         Assert.assertEquals(rsp, rspCaptor.getValue());
         Assert.assertEquals(rsp, symmetricRenderedPath);
     }
 
-    @Test
-    public void testCheckLocalForwarderPresence() throws Exception {
-        final Local local = new LocalBuilder().build();
-
-        Mockito.when(rwTx.read(Matchers.eq(LogicalDatastoreType.CONFIGURATION), Matchers.<InstanceIdentifier<Local>>any()))
-                .thenReturn(Futures.<Optional<Local>, ReadFailedException>immediateCheckedFuture(Optional.of(local)))
-                .thenReturn(Futures.<Optional<Local>, ReadFailedException>immediateCheckedFuture(Optional.absent()))
-                .thenReturn(Futures.<Optional<Local>, ReadFailedException>immediateFailedCheckedFuture(new ReadFailedException("n/a")));
-
-        Assert.assertTrue(ServiceChainingUtil.checkLocalForwarderPresence(dataBroker));
-        Assert.assertFalse(ServiceChainingUtil.checkLocalForwarderPresence(dataBroker));
-        Assert.assertFalse(ServiceChainingUtil.checkLocalForwarderPresence(dataBroker));
-        Assert.assertFalse(ServiceChainingUtil.checkLocalForwarderPresence(dataBroker));
-    }
-
-    @Test
-    public void testCheckServicePathPresence() throws Exception {
-        final ServiceChain serviceChainOk = new ServiceChainBuilder()
-                .setServicePath(Collections.singletonList(new ServicePathBuilder().build()))
-                .build();
-        final ServiceChain serviceChainBad1 = new ServiceChainBuilder()
-                .setServicePath(Collections.emptyList())
-                .build();
-        final ServiceChain serviceChainBad2 = new ServiceChainBuilder().build();
-
-        Mockito.when(rwTx.read(Matchers.eq(LogicalDatastoreType.CONFIGURATION), Matchers.<InstanceIdentifier<ServiceChain>>any()))
-                .thenReturn(Futures.<Optional<ServiceChain>, ReadFailedException>immediateCheckedFuture(Optional.of(serviceChainOk)))
-                .thenReturn(Futures.<Optional<ServiceChain>, ReadFailedException>immediateCheckedFuture(Optional.of(serviceChainBad1)))
-                .thenReturn(Futures.<Optional<ServiceChain>, ReadFailedException>immediateCheckedFuture(Optional.of(serviceChainBad2)))
-                .thenReturn(Futures.<Optional<ServiceChain>, ReadFailedException>immediateCheckedFuture(Optional.absent()))
-                .thenReturn(Futures.<Optional<ServiceChain>, ReadFailedException>immediateFailedCheckedFuture(new ReadFailedException("n/a")));
-
-        Assert.assertFalse(ServiceChainingUtil.checkServicePathPresence(dataBroker));
-        Assert.assertTrue(ServiceChainingUtil.checkServicePathPresence(dataBroker));
-        Assert.assertTrue(ServiceChainingUtil.checkServicePathPresence(dataBroker));
-        Assert.assertTrue(ServiceChainingUtil.checkServicePathPresence(dataBroker));
-        Assert.assertFalse(ServiceChainingUtil.checkServicePathPresence(dataBroker));
-    }
-
     @Test
     public void testFindServiceFunctionPath() throws Exception {
         final String sfcNameValue = "123";
@@ -482,6 +459,9 @@ public class ServiceChainingUtilTest {
                 .setName(new SffName("unit-sff-03"))
                 .setIpMgmtAddress(new IpAddress(new Ipv4Address("1.2.3.4")))
                 .build();
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
 
         stub(method(ServiceChainingUtil.class, "checkLocalForwarderPresence")).toReturn(true);
 
@@ -489,8 +469,7 @@ public class ServiceChainingUtilTest {
         final SfcProviderServiceForwarderAPI api = PowerMockito.mock(SfcProviderServiceForwarderAPI.class);
         PowerMockito.when(api.readServiceFunctionForwarder(sffNameCaptor.capture())).thenReturn(sff);
 
-
-        final boolean outcome = ServiceChainingUtil.setSfcPart(rsp, policyWriter);
+        final boolean outcome = ServiceChainingUtil.setSfcPart(sfp, rsp, null, policyWriter);
 
         Assert.assertEquals("rsp-hop-01-sf+sff", sffNameCaptor.getValue().getValue());
         Assert.assertTrue(outcome);
@@ -511,6 +490,9 @@ public class ServiceChainingUtilTest {
                 .setName(new SffName("unit-sff-03"))
                 .setIpMgmtAddress(new IpAddress(new Ipv4Address("1.2.3.4")))
                 .build();
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
 
         stub(method(ServiceChainingUtil.class, "checkLocalForwarderPresence")).toReturn(false);
 
@@ -518,8 +500,7 @@ public class ServiceChainingUtilTest {
         final SfcProviderServiceForwarderAPI api = PowerMockito.mock(SfcProviderServiceForwarderAPI.class);
         PowerMockito.when(api.readServiceFunctionForwarder(sffNameCaptor.capture())).thenReturn(sff);
 
-
-        final boolean outcome = ServiceChainingUtil.setSfcPart(rsp, policyWriter);
+        final boolean outcome = ServiceChainingUtil.setSfcPart(sfp, rsp, null, policyWriter);
 
         Assert.assertEquals("rsp-hop-01-sf+sff", sffNameCaptor.getValue().getValue());
         Assert.assertTrue(outcome);
@@ -534,23 +515,28 @@ public class ServiceChainingUtilTest {
 
     @Test
     public void testSetSfcPart_fail01() throws Exception {
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(null, policyWriter));
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
+
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, null, null, policyWriter));
 
         final RenderedServicePathBuilder rspBuilder = new RenderedServicePathBuilder().setName(new RspName("unit-rsp-05"));
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(rspBuilder.build(), policyWriter));
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, rspBuilder.build(), null, policyWriter));
 
         rspBuilder.setRenderedServicePathHop(Collections.emptyList());
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(rspBuilder.build(), policyWriter));
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, rspBuilder.build(), null, policyWriter));
 
         rspBuilder.setRenderedServicePathHop(Collections.singletonList(null));
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(rspBuilder.build(), policyWriter));
-
-        Mockito.verifyNoMoreInteractions(policyWriter);
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, rspBuilder.build(), null, policyWriter));
     }
 
     @Test
     public void testSetSfcPart_fail02() throws Exception {
         final RenderedServicePath rsp = createRsp("unit-rsp-03");
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
 
         Mockito.doReturn(Futures.immediateCheckedFuture(Optional.absent()))
                 .when(rwTx).read(Mockito.eq(LogicalDatastoreType.CONFIGURATION), Mockito.<InstanceIdentifier<Local>>any());
@@ -559,7 +545,7 @@ public class ServiceChainingUtilTest {
         final SfcProviderServiceForwarderAPI api = PowerMockito.mock(SfcProviderServiceForwarderAPI.class);
         PowerMockito.when(api.readServiceFunctionForwarder(sffNameCaptor.capture())).thenReturn(null);
 
-        final boolean outcome = ServiceChainingUtil.setSfcPart(rsp, policyWriter);
+        final boolean outcome = ServiceChainingUtil.setSfcPart(sfp, rsp, null, policyWriter);
 
         Assert.assertEquals("rsp-hop-01-sf+sff", sffNameCaptor.getValue().getValue());
         Assert.assertFalse(outcome);
@@ -576,6 +562,9 @@ public class ServiceChainingUtilTest {
         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder()
                 .setName(new SffName("unit-sff-03"))
                 .setIpMgmtAddress(null);
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
 
         stub(method(ServiceChainingUtil.class, "checkLocalForwarderPresence")).toReturn(true);
 
@@ -584,7 +573,7 @@ public class ServiceChainingUtilTest {
         PowerMockito.when(api.readServiceFunctionForwarder(sffNameCaptor.capture())).thenReturn(
                 sffBuilder.build());
 
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(rsp, policyWriter));
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, rsp, null, policyWriter));
 
         Assert.assertEquals("rsp-hop-01-sf+sff", sffNameCaptor.getValue().getValue());
 
@@ -599,6 +588,9 @@ public class ServiceChainingUtilTest {
         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder()
                 .setName(new SffName("unit-sff-03"))
                 .setIpMgmtAddress(new IpAddress((Ipv4Address) null));
+        final ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder();
+        sfpBuilder.setSymmetric(false);
+        final ServiceFunctionPath sfp = sfpBuilder.build();
 
         stub(method(ServiceChainingUtil.class, "checkLocalForwarderPresence")).toReturn(true);
 
@@ -607,7 +599,7 @@ public class ServiceChainingUtilTest {
         PowerMockito.when(api.readServiceFunctionForwarder(sffNameCaptor.capture())).thenReturn(
                 sffBuilder.build());
 
-        Assert.assertFalse(ServiceChainingUtil.setSfcPart(rsp, policyWriter));
+        Assert.assertFalse(ServiceChainingUtil.setSfcPart(sfp, rsp, null, policyWriter));
 
         Assert.assertEquals("rsp-hop-01-sf+sff", sffNameCaptor.getValue().getValue());
 
@@ -616,7 +608,6 @@ public class ServiceChainingUtilTest {
         Mockito.verifyNoMoreInteractions(policyWriter);
     }
 
-
     @Test
     public void testForwarderTypeChoice() throws Exception {
         final String sffValue = "unit-xx";