Added support of network-domain in EP's network-containment for vpp-rednerer 20/40620/5
authorMartin Sunal <msunal@cisco.com>
Tue, 21 Jun 2016 13:07:35 +0000 (15:07 +0200)
committerMartin Sunal <msunal@cisco.com>
Tue, 21 Jun 2016 16:34:00 +0000 (16:34 +0000)
VPP renderer can process endpoints which have network-containment with:
1. forwarding-context = L2-flood-domain
2. network-domain where parent = L2-flood-domain

Change-Id: I391f8cd11b8616b08b355efd1252002bfa4a4134
Signed-off-by: Martin Sunal <msunal@cisco.com>
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/iface/VppEndpointLocationProvider.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/policy/ForwardingManager.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/policy/PolicyContext.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/policy/VppRendererPolicyManager.java
renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/CloseOnFailTransactionChain.java [new file with mode: 0644]

index 6ca8e49695ac4a562e7e269297be4c03f14fb6fc..02b719b7848e3b6abc78ef7e5b886806e928dbef 100644 (file)
@@ -8,11 +8,15 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import javax.annotation.Nonnull;
 
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
 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;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.CloseOnFailTransactionChain;
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
 import org.opendaylight.groupbasedpolicy.util.IidFactory;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocation;
@@ -30,7 +34,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_render
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 
@@ -40,14 +43,15 @@ public class VppEndpointLocationProvider implements AutoCloseable {
     public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER =
             new ProviderName("VPP endpoint location provider");
     public static final long PROVIDER_PRIORITY = 10L;
-    private final DataBroker dataProvider;
+    private final BindingTransactionChain txChain;
 
     public VppEndpointLocationProvider(DataBroker dataProvider) {
-        this.dataProvider = Preconditions.checkNotNull(dataProvider);
         LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
             .setPriority(PROVIDER_PRIORITY)
             .build();
-        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        txChain = checkNotNull(dataProvider)
+            .createTransactionChain(new CloseOnFailTransactionChain(VppEndpointLocationProvider.class.getSimpleName()));
+        WriteTransaction wTx = txChain.newWriteOnlyTransaction();
         wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER),
                 locationProvider, true);
 
@@ -68,7 +72,7 @@ public class VppEndpointLocationProvider implements AutoCloseable {
     public void createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
         ProviderAddressEndpointLocation providerAddressEndpointLocation =
                 createProviderAddressEndpointLocation(vppEndpoint);
-        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        WriteTransaction wTx = txChain.newWriteOnlyTransaction();
         wTx.put(LogicalDatastoreType.CONFIGURATION,
                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
                         providerAddressEndpointLocation.getKey()),
@@ -105,7 +109,7 @@ public class VppEndpointLocationProvider implements AutoCloseable {
 
     public void deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
         ProviderAddressEndpointLocationKey provAddrEpLocKey = createProviderAddressEndpointLocationKey(vppEndpoint);
-        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        WriteTransaction wTx = txChain.newWriteOnlyTransaction();
         wTx.delete(LogicalDatastoreType.CONFIGURATION,
                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, provAddrEpLocKey));
         LOG.debug("Deleting location for {}", provAddrEpLocKey);
@@ -137,7 +141,7 @@ public class VppEndpointLocationProvider implements AutoCloseable {
                 new AbsoluteLocationBuilder().setLocationType(location).build();
         ProviderAddressEndpointLocation providerAddressEndpointLocation = new ProviderAddressEndpointLocationBuilder()
             .setKey(provAddrEpLocKey).setAbsoluteLocation(absoluteLocation).build();
-        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        WriteTransaction wTx = txChain.newWriteOnlyTransaction();
         wTx.put(LogicalDatastoreType.CONFIGURATION,
                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
                         providerAddressEndpointLocation.getKey()),
@@ -161,7 +165,7 @@ public class VppEndpointLocationProvider implements AutoCloseable {
 
     @Override
     public void close() throws Exception {
-        WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
+        WriteTransaction wTx = txChain.newWriteOnlyTransaction();
         wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER));
         wTx.submit();
     }
index d42e47d130da3b8b8634112dca6091a5ee42a5f2..dd04d7735813a9d755589b1e407cff22bab6e8b9 100644 (file)
@@ -31,9 +31,16 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpo
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.NetworkDomainContainment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.L2FloodDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.fields.Parent;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContextKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomainKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VlanNetwork;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.BridgeDomain;
@@ -50,6 +57,7 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Table;
 
 public final class ForwardingManager {
 
@@ -155,7 +163,7 @@ public final class ForwardingManager {
         }
 
         if (Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
-            Optional<String> optL2FloodDomain = resolveL2FloodDomain(rEp.getNetworkContainment());
+            java.util.Optional<String> optL2FloodDomain = resolveL2FloodDomain(rEp, policyCtx);
             if (!optL2FloodDomain.isPresent()) {
                 // TODO add it to the status for renderer manager
                 LOG.info("Rednerer endpoint does not have l2FloodDomain as network containment {}", rEp);
@@ -211,23 +219,50 @@ public final class ForwardingManager {
         return result;
     }
 
-    public static Optional<String> resolveL2FloodDomain(@Nullable NetworkContainment netCont) {
+    public static java.util.Optional<String> resolveL2FloodDomain(@Nonnull AddressEndpointWithLocation ep,
+            @Nonnull PolicyContext policyCtx) {
+        NetworkContainment netCont = ep.getNetworkContainment();
         if (netCont == null) {
-            return Optional.absent();
+            return java.util.Optional.empty();
         }
         Containment containment = netCont.getContainment();
         if (containment instanceof ForwardingContextContainment) {
             ForwardingContextContainment fwCtxCont = (ForwardingContextContainment) containment;
-            if (fwCtxCont.getContextType().isAssignableFrom(L2FloodDomain.class)) {
-                return fwCtxCont.getContextId() == null ? null : Optional.of(fwCtxCont.getContextId().getValue());
+            if (L2FloodDomain.class.equals(fwCtxCont.getContextType())) {
+                return fwCtxCont.getContextId() == null ? java.util.Optional.empty() : java.util.Optional
+                    .of(fwCtxCont.getContextId().getValue());
             }
         }
         if (containment instanceof NetworkDomainContainment) {
-            // TODO address missing impl
-            LOG.info("Network domain containment in endpoint is not supported yet. {}", netCont);
-            return Optional.absent();
+            NetworkDomainContainment netDomainCont = (NetworkDomainContainment) containment;
+            RendererNetworkDomain rendererNetworkDomain =
+                    policyCtx.getNetworkDomainTable().get(ep.getTenant(), new RendererNetworkDomainKey(
+                            netDomainCont.getNetworkDomainId(), netDomainCont.getNetworkDomainType()));
+            java.util.Optional<String> optL2Fd = getForwardingCtxForParent(ep.getTenant(),
+                    rendererNetworkDomain.getParent(), policyCtx.getForwardingCtxTable())
+                        .filter(fwdCtx -> L2FloodDomain.class.equals(fwdCtx.getContextType()))
+                        .map(RendererForwardingContext::getContextId)
+                        .map(ContextId::getValue);
+            if (!optL2Fd.isPresent()) {
+                LOG.info("network-domain-containment in endpoint does not have L2-flood-domain as parent. "
+                        + "This case is not supported in VPP renderer. {}", ep);
+            }
+            return optL2Fd;
+        }
+        return java.util.Optional.empty();
+    }
+
+    public static @Nonnull java.util.Optional<RendererForwardingContext> getForwardingCtxForParent(
+            @Nullable TenantId tenant, @Nullable Parent parent,
+            Table<TenantId, RendererForwardingContextKey, RendererForwardingContext> forwardingCtxTable) {
+        if (tenant == null || parent == null) {
+            return java.util.Optional.empty();
+        }
+        if (parent.getContextId() != null && parent.getContextType() != null) {
+            return java.util.Optional.ofNullable(forwardingCtxTable.get(tenant,
+                    new RendererForwardingContextKey(parent.getContextId(), parent.getContextType())));
         }
-        return Optional.absent();
+        return java.util.Optional.empty();
     }
 
 }
index 4a69b08ecedd2a70b6c76ee3c1fb50db0b52164b..815fecacfec51e7d09da0c53aff489458cce3bc6 100644 (file)
@@ -21,12 +21,17 @@ import javax.annotation.Nonnull;
 
 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.peer.endpoints.PeerEndpointKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicy;
 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;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContextKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomainKey;
 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.renderer.rev151103.renderers.renderer.renderer.policy.configuration.rule.groups.RuleGroupKey;
 
@@ -43,6 +48,8 @@ public class PolicyContext {
     private final ImmutableTable<RendererEndpointKey, PeerEndpointKey, ImmutableSortedSet<RendererResolvedPolicy>> policyTable;
     private final ImmutableMap<RuleGroupKey, ResolvedRuleGroup> ruleGroupByKey;
     private final ImmutableMap<AddressEndpointKey, AddressEndpointWithLocation> addrEpByKey;
+    private final ImmutableTable<TenantId, RendererForwardingContextKey, RendererForwardingContext> forwardingCtxTable;
+    private final ImmutableTable<TenantId, RendererNetworkDomainKey, RendererNetworkDomain> networkDomainTable;
 
     public PolicyContext(@Nonnull RendererPolicy policy) {
         this.policy = Preconditions.checkNotNull(policy);
@@ -53,10 +60,14 @@ public class PolicyContext {
             List<RendererEndpoint> rendererEps = resolveRendererEndpoints(config);
             this.policyTable = resolvePolicy(rendererEps, ruleGroupByKey);
             addrEpByKey = resolveAddrEpWithLoc(config);
+            this.forwardingCtxTable = resolveForwardingCtxTable(config);
+            this.networkDomainTable = resolveNetworkDomainTable(config);
         } else {
             this.ruleGroupByKey = ImmutableMap.of();
             this.policyTable = ImmutableTable.of();
             this.addrEpByKey = ImmutableMap.of();
+            this.forwardingCtxTable = ImmutableTable.of();
+            this.networkDomainTable = ImmutableTable.of();
         }
     }
 
@@ -97,6 +108,36 @@ public class PolicyContext {
             .collectingAndThen(Collectors.toMap(RuleGroup::getKey, ResolvedRuleGroup::new), ImmutableMap::copyOf));
     }
 
+    private static ImmutableTable<TenantId, RendererForwardingContextKey, RendererForwardingContext> resolveForwardingCtxTable(
+            Configuration config) {
+        Builder<TenantId, RendererForwardingContextKey, RendererForwardingContext> resultBuilder = new Builder<>();
+        Optional.ofNullable(config.getRendererForwarding()).map(x -> x.getRendererForwardingByTenant()).ifPresent(x -> {
+            x.forEach(fwdByTenant -> {
+                Optional.ofNullable(fwdByTenant.getRendererForwardingContext()).ifPresent(fwdCtxs -> {
+                    fwdCtxs.forEach(fwdCtx -> {
+                        resultBuilder.put(fwdByTenant.getTenantId(), fwdCtx.getKey(), fwdCtx);
+                    });
+                });
+            });
+        });
+        return resultBuilder.build();
+    }
+
+    private static ImmutableTable<TenantId, RendererNetworkDomainKey, RendererNetworkDomain> resolveNetworkDomainTable(
+            Configuration config) {
+        Builder<TenantId, RendererNetworkDomainKey, RendererNetworkDomain> resultBuilder = new Builder<>();
+        Optional.ofNullable(config.getRendererForwarding()).map(x -> x.getRendererForwardingByTenant()).ifPresent(x -> {
+            x.forEach(fwdByTenant -> {
+                Optional.ofNullable(fwdByTenant.getRendererNetworkDomain()).ifPresent(netDomains -> {
+                    netDomains.forEach(netDomain -> {
+                        resultBuilder.put(fwdByTenant.getTenantId(), netDomain.getKey(), netDomain);
+                    });
+                });
+            });
+        });
+        return resultBuilder.build();
+    }
+
     private static ImmutableMap<AddressEndpointKey, AddressEndpointWithLocation> resolveAddrEpWithLoc(
             Configuration config) {
         return Maps.uniqueIndex(config.getEndpoints().getAddressEndpointWithLocation(),
@@ -137,10 +178,19 @@ public class PolicyContext {
         return addrEpByKey;
     }
 
+    public @Nonnull ImmutableTable<TenantId, RendererForwardingContextKey, RendererForwardingContext> getForwardingCtxTable() {
+        return forwardingCtxTable;
+    }
+
+    public @Nonnull ImmutableTable<TenantId, RendererNetworkDomainKey, RendererNetworkDomain> getNetworkDomainTable() {
+        return networkDomainTable;
+    }
+
     @Override
     public String toString() {
-        return "PolicyContext [policy=" + policy + ", policyTable=" + policyTable + ", ruleGroupByKey=" + ruleGroupByKey
-                + ", addrEpByKey=" + addrEpByKey + "]";
+        return "PolicyContext [policyTable=" + policyTable + ", ruleGroupByKey=" + ruleGroupByKey + ", addrEpByKey="
+                + addrEpByKey + ", forwardingCtxTable=" + forwardingCtxTable + ", networkDomainTable="
+                + networkDomainTable + "]";
     }
 
 }
index 5f9c0cd80f562cc6176ff8c6c22fbc59d6cbec80..85d4205e3a73f6de7e29da086ea42dc82bada8d9 100644 (file)
@@ -36,7 +36,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
@@ -225,8 +224,7 @@ public class VppRendererPolicyManager {
             .map(addrEpKey -> policyCtx.getAddrEpByKey().get(addrEpKey))
             .collect(Collectors.toSet())
             .forEach(addrEpWithLoc -> {
-                Optional<String> optL2Fd =
-                        ForwardingManager.resolveL2FloodDomain(addrEpWithLoc.getNetworkContainment());
+                java.util.Optional<String> optL2Fd = ForwardingManager.resolveL2FloodDomain(addrEpWithLoc, policyCtx);
                 if (optL2Fd.isPresent()) {
                     ExternalLocationCase rEpLoc = ForwardingManager.resolveAndValidateLocation(addrEpWithLoc);
                     InstanceIdentifier<?> externalNodeMountPoint = rEpLoc.getExternalNodeMountPoint();
diff --git a/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/CloseOnFailTransactionChain.java b/renderers/vpp/src/main/java/org/opendaylight/groupbasedpolicy/renderer/vpp/util/CloseOnFailTransactionChain.java
new file mode 100644 (file)
index 0000000..b797937
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.vpp.util;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public class CloseOnFailTransactionChain implements TransactionChainListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CloseOnFailTransactionChain.class);
+    private final String owner;
+
+    public CloseOnFailTransactionChain(@Nonnull String owner) {
+        this.owner = Preconditions.checkNotNull(owner);
+    }
+
+    @Override
+    public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+        LOG.info("Transaction chain owned by {} was successful. {}", owner, chain);
+    }
+
+    @Override
+    public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,
+            Throwable cause) {
+        LOG.warn("Transaction chain owned by {} failed. Transaction which caused the chain to fail {}", owner,
+                transaction, cause);
+        chain.close();
+    }
+
+}