Fixing socket for vpp endpoint
[groupbasedpolicy.git] / neutron-vpp-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / vpp / mapper / processors / PortHandler.java
index 2887a44cf313ef6049d666cd4d80d8f21b5f339b..01c2152cc0309627e18c489ee87464937916f265 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
  *\r
  * This program and the accompanying materials are made available under the\r
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
@@ -8,16 +8,23 @@
 \r
 package org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.processors;\r
 \r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import javax.annotation.Nullable;\r
+\r
 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;\r
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
-import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;\r
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;\r
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;\r
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;\r
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;\r
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;\r
 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;\r
@@ -26,14 +33,28 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gb
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCaseBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCaseBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCaseBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.binding.attributes.VifDetails;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;\r
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
 import org.slf4j.Logger;\r
@@ -42,13 +63,24 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.VisibleForTesting;\r
 import com.google.common.base.Optional;\r
 \r
-public class PortHandler  implements TransactionChainListener {\r
+import javax.annotation.Nonnull;\r
+public class PortHandler implements TransactionChainListener {\r
 \r
-    private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);\r
+    private static final Logger LOG = LoggerFactory.getLogger(PortHandler.class);\r
+\r
+    private static final String COMPUTE_OWNER = "compute";\r
+    private static final String DHCP_OWNER = "dhcp";\r
+    private static final String ROUTER_OWNER = "network:router_interface";\r
+    private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};\r
+    private static final String VHOST_USER = "vhostuser";\r
+    private static final String UNBOUND = "unbound";\r
+    private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";\r
+    private static final String TAP_PORT_NAME_PREFIX = "tap";\r
+    private static final String RT_PORT_NAME_PREFIX = "qr-";\r
+    private static final String VHOST_SOCKET_KEY = "vhostuser_socket";\r
 \r
     private BindingTransactionChain transactionChain;\r
-    BaseEndpointByPortListener portByBaseEpListener;\r
-    DataBroker dataBroker;\r
+    private DataBroker dataBroker;\r
 \r
     PortHandler(DataBroker dataBroker) {\r
         this.dataBroker = dataBroker;\r
@@ -56,9 +88,10 @@ public class PortHandler  implements TransactionChainListener {
     }\r
 \r
     void processCreated(Port port) {\r
-        ReadTransaction rTx = dataBroker.newReadOnlyTransaction();\r
+        ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
         Optional<BaseEndpointByPort> optBaseEpByPort = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
                 createBaseEpByPortIid(port.getUuid()), rTx);\r
+        rTx.close();\r
         if (!optBaseEpByPort.isPresent()) {\r
             return;\r
         }\r
@@ -66,9 +99,10 @@ public class PortHandler  implements TransactionChainListener {
     }\r
 \r
     void processCreated(BaseEndpointByPort bebp) {\r
-        ReadTransaction rTx = dataBroker.newReadOnlyTransaction();\r
-        Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, createPortIid(\r
-                bebp.getPortId()), rTx);\r
+        ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+        Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                createPortIid(bebp.getPortId()), rTx);\r
+        rTx.close();\r
         if (!optPort.isPresent()) {\r
             return;\r
         }\r
@@ -77,47 +111,248 @@ public class PortHandler  implements TransactionChainListener {
 \r
     @VisibleForTesting\r
     void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
-        // port not used yet\r
-        VppEndpoint vppEp = buildVppEp(bebp);\r
-        writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp, true);\r
-        LOG.debug("Created vpp-endpoint {}", vppEp);\r
+        if (isValidVhostUser(port)) {\r
+            VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
+            writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
+            LOG.debug("Created vpp-endpoint {}", vppEp);\r
+        }\r
+    }\r
+\r
+    private boolean isValidVhostUser(Port port) {\r
+        PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);\r
+        if (portBindingExt != null) {\r
+            String vifType = portBindingExt.getVifType();\r
+            String deviceOwner = port.getDeviceOwner();\r
+            if (vifType != null && deviceOwner != null) {\r
+                if (vifType.contains(VHOST_USER)) {\r
+                    for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
+                        if (deviceOwner.contains(supportedDeviceOwner)) {\r
+                            return true;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return false;\r
     }\r
 \r
     void processUpdated(Port original, Port delta) {\r
+        if (!isUpdateNeeded(original, delta)){\r
+            LOG.trace("Port update skipped, port didn`t change. before {}, after: {}" , original, delta);\r
+            return;\r
+        }\r
+\r
+        LOG.trace("Updating port before: {}, after: {}" , original, delta);\r
+        if (isValidVhostUser(original)) {\r
+            ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+            Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
+                    createBaseEpByPortIid(original.getUuid()), rTx);\r
+            rTx.close();\r
+            if (!optBebp.isPresent()) {\r
+                return;\r
+            }\r
+            LOG.trace("Updating port - deleting old port {}" , optBebp.get().getPortId());\r
+            processDeleted(optBebp.get());\r
+        }\r
+        LOG.trace("Updating port - creating new port {}" , delta.getUuid());\r
         processCreated(delta);\r
     }\r
 \r
+    private boolean isUpdateNeeded(final Port oldPort, final Port newPort) {\r
+        //TODO fix this to better support update of ports for VPP\r
+        final PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);\r
+        final PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);\r
+\r
+        if (newPortAugmentation == null) {\r
+            LOG.trace("Port {} is no longer a vhost type port, updating port...");\r
+            return true;\r
+        }\r
+\r
+        final String oldDeviceOwner = oldPort.getDeviceOwner();\r
+        final String oldVifType = oldPortAugmentation.getVifType();\r
+        final String newDeviceOwner = newPort.getDeviceOwner();\r
+        final String newVifType = newPortAugmentation.getVifType();\r
+\r
+        // TODO potential bug here\r
+        // Temporary change for Openstack Mitaka: If old neutron-binding:vif-type is vhost, new one is unbound and\r
+        // device owner is ROUTER_OWNER, skip update. Openstack (or ml2) sometimes sends router update messages in\r
+        // incorrect order which causes unwanted port removal\r
+        if (oldVifType.equals(VHOST_USER) && newVifType.equals(UNBOUND) && oldDeviceOwner != null &&\r
+                ROUTER_OWNER.equals(oldDeviceOwner) && ROUTER_OWNER.equals(newDeviceOwner)) {\r
+            LOG.warn("Port vif-type was updated from vhost to unbound. This update is currently disabled and will be skipped");\r
+            return false;\r
+        }\r
+\r
+        if (newVifType != null && !newVifType.equals(oldVifType)) {\r
+            LOG.trace("Vif type changed, old: {} new {}", oldVifType, newVifType);\r
+            return true;\r
+        }\r
+\r
+        final List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();\r
+\r
+        if (!oldPortAugmentation.getHostId().equals(newPortAugmentation.getHostId()) ||\r
+            nullToEmpty(vifDetails).size() != nullToEmpty(newPortAugmentation.getVifDetails()).size()) {\r
+            return true;\r
+        }\r
+\r
+        for (VifDetails vifDetail : nullToEmpty(vifDetails)) {\r
+            //check if vhostuser_socket, vhostuser_mode and port_filter are changed\r
+            if (!newPortAugmentation.getVifDetails().contains(vifDetail))\r
+                return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
     void processDeleted(BaseEndpointByPort bebp) {\r
-        VppEndpoint vppEp = buildVppEp(bebp);\r
-        writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp, false);\r
-        LOG.debug("Deleted vpp-endpoint {}", vppEp);\r
+        LOG.trace("Deleting vpp-endpoint by BaseEndpointByPort {}" , bebp);\r
+        VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),\r
+                bebp.getContextType());\r
+        InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);\r
+        ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+        Optional<VppEndpoint> readVppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid, rTx);\r
+        rTx.close();\r
+        if (readVppEp.isPresent()) {\r
+            writeVppEndpoint(vppEpIid, null);\r
+            LOG.debug("Deleted vpp-endpoint {}", vppEpKey);\r
+        }\r
     }\r
 \r
-    private void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp, boolean created) {\r
+    private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {\r
         WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();\r
-        InstanceIdentifier<VppEndpoint> iid = createVppEndpointIid(vppEp.getKey());\r
-        if (created == true) {\r
-            wTx.put(LogicalDatastoreType.CONFIGURATION, iid, vppEp, true);\r
+        if (vppEp != null) {\r
+            wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);\r
         } else {\r
-            wTx.delete(LogicalDatastoreType.CONFIGURATION, iid);\r
-        }\r
-        try {\r
-            wTx.submit().checkedGet();\r
-        } catch (TransactionCommitFailedException e) {\r
-            LOG.error("Transaction chain commit failed. {}", e);\r
-            transactionChain.close();\r
-            transactionChain = dataBroker.createTransactionChain(this);\r
+            wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
         }\r
+        wTx.submit();\r
     }\r
 \r
     @VisibleForTesting\r
-    VppEndpoint buildVppEp(BaseEndpointByPort bebp) {\r
-        return new VppEndpointBuilder().setContextId(bebp.getContextId())\r
+    VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
+        PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
+        VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
+            .setContextId(bebp.getContextId())\r
             .setContextType(bebp.getContextType())\r
             .setAddress(bebp.getAddress())\r
-            .setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(bebp.getPortId().getValue()).build())\r
             .setAddressType(bebp.getAddressType())\r
+            .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
+            .setVppNodeId(new NodeId(portBinding.getHostId()));\r
+\r
+        if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
+            vppEpBuilder.setInterfaceTypeChoice(\r
+                new VhostUserCaseBuilder().setSocket(getSocketFromPortBinding(portBinding)).build());\r
+        } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
+            TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
+                .setName(createPortName(port.getUuid()))\r
+                .build();\r
+            vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+        } else if (isValidQRouterPort(port)) {\r
+            TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
+                    .setName(createQRouterPortName(port.getUuid()))\r
+                    .build();\r
+            vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+        } else if (isValidVppRouterPort(port)) {\r
+            vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));\r
+        }\r
+        return vppEpBuilder.build();\r
+    }\r
+\r
+    private String getSocketFromPortBinding(@Nonnull PortBindingExtension portBindingExtension) {\r
+        List<VifDetails> vifDetails = nullToEmpty(portBindingExtension.getVifDetails());\r
+\r
+        for (VifDetails detail : vifDetails) {\r
+            if (VHOST_SOCKET_KEY.equalsIgnoreCase(detail.getDetailsKey())) {\r
+                return detail.getValue();\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private LoopbackCase getLoopbackCase(Port port) {\r
+        LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
+            .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
+        Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);\r
+        if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){\r
+            loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());\r
+            ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+            Optional<Subnet> subnetOptional =\r
+                DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+                    InstanceIdentifier.builder(Neutron.class)\r
+                        .child(Subnets.class)\r
+                        .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
+                        .build(), rTx);\r
+            if (subnetOptional.isPresent()) {\r
+                Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();\r
+                loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));\r
+            } else {\r
+                LOG.warn("IpPrefix for loopback port: {} was not set.", port);\r
+            }\r
+            if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {\r
+                loopbackCase.setBvi(true);\r
+                LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);\r
+            }\r
+\r
+        } else {\r
+            LOG.warn("IpAddress for loopback port: {} was not set.", port);\r
+        }\r
+        return loopbackCase.build();\r
+    }\r
+\r
+    /**\r
+     * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
+     * to ODL neutron.\r
+     */\r
+    private boolean isValidQRouterPort(Port port) {\r
+        Optional<Router> optRouter = getRouterOptional(port);\r
+        return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+                && port.getMacAddress() != null;\r
+    }\r
+\r
+    private boolean isValidVppRouterPort(Port port) {\r
+        Optional<Router> optRouter = getRouterOptional(port);\r
+        return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+            && port.getMacAddress() != null;\r
+    }\r
+\r
+    private Optional<Router> getRouterOptional(Port port) {\r
+        ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+        InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
+            .child(Routers.class)\r
+            .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))\r
             .build();\r
+        Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
+        rTx.close();\r
+        return optRouter;\r
+    }\r
+\r
+    public static Optional<FixedIps> resolveFirstFixedIps(Port port) {\r
+        List<FixedIps> fixedIps = port.getFixedIps();\r
+        if (fixedIps != null && !fixedIps.isEmpty()) {\r
+            return Optional.of(fixedIps.get(0));\r
+        }\r
+        return Optional.absent();\r
+    }\r
+\r
+    private String createPortName(Uuid portUuid) {\r
+        String tapPortName;\r
+        String uuid = portUuid.getValue();\r
+        if (uuid != null && uuid.length() >= 12) {\r
+            tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
+        } else {\r
+            tapPortName = TAP_PORT_NAME_PREFIX + uuid;\r
+        }\r
+        return tapPortName;\r
+    }\r
+\r
+    private String createQRouterPortName(Uuid portUuid) {\r
+        String tapPortName;\r
+        String uuid = portUuid.getValue();\r
+        if (uuid != null && uuid.length() >= 12) {\r
+            tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
+        } else {\r
+            tapPortName = RT_PORT_NAME_PREFIX + uuid;\r
+        }\r
+        return tapPortName;\r
     }\r
 \r
     private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {\r
@@ -151,13 +386,18 @@ public class PortHandler  implements TransactionChainListener {
     @Override\r
     public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,\r
             Throwable cause) {\r
-        LOG.error("Transaction chain failed. {}", cause.getMessage());\r
+        LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),\r
+                transaction, cause);\r
         transactionChain.close();\r
         transactionChain = dataBroker.createTransactionChain(this);\r
     }\r
 \r
     @Override\r
     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
-        LOG.trace("Transaction chain was successfull. {}", chain);\r
+        LOG.trace("Transaction chain was successful. {}", chain);\r
+    }\r
+\r
+    private <T> List<T> nullToEmpty(@Nullable List<T> list) {\r
+        return list == null ? Collections.emptyList() : list;\r
     }\r
 }\r