1b49ffca563d736f3ae9a8356f82b29f8320cc7e
[groupbasedpolicy.git] / neutron-vpp-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / vpp / mapper / processors / PortHandler.java
1 /*\r
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
3  *\r
4  * This program and the accompanying materials are made available under the\r
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
6  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
7  */\r
8 \r
9 package org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.processors;\r
10 \r
11 import java.util.Collections;\r
12 import java.util.List;\r
13 \r
14 import javax.annotation.Nullable;\r
15 \r
16 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;\r
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
18 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;\r
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;\r
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;\r
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;\r
24 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;\r
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.GbpByNeutronMappings;\r
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;\r
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;\r
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;\r
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;\r
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.ExcludeFromPolicy;\r
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.ExcludeFromPolicyBuilder;\r
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;\r
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCaseBuilder;\r
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;\r
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCaseBuilder;\r
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCaseBuilder;\r
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;\r
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.binding.attributes.VifDetails;\r
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;\r
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;\r
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;\r
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;\r
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
62 import org.slf4j.Logger;\r
63 import org.slf4j.LoggerFactory;\r
64 \r
65 import com.google.common.annotations.VisibleForTesting;\r
66 import com.google.common.base.Optional;\r
67 \r
68 import javax.annotation.Nonnull;\r
69 public class PortHandler implements TransactionChainListener {\r
70 \r
71     private static final Logger LOG = LoggerFactory.getLogger(PortHandler.class);\r
72 \r
73     private static final String COMPUTE_OWNER = "compute";\r
74     private static final String DHCP_OWNER = "dhcp";\r
75     static final String ROUTER_OWNER = "network:router_interface";\r
76     private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};\r
77     private static final String VHOST_USER = "vhostuser";\r
78     private static final String UNBOUND = "unbound";\r
79     private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";\r
80     private static final String TAP_PORT_NAME_PREFIX = "tap";\r
81     private static final String RT_PORT_NAME_PREFIX = "qr-";\r
82     private static final String VHOST_SOCKET_KEY = "vhostuser_socket";\r
83     static final String DEFAULT_NODE = "default";\r
84 \r
85     private final NodeId routingNode;\r
86     private BindingTransactionChain transactionChain;\r
87     private DataBroker dataBroker;\r
88 \r
89     PortHandler(DataBroker dataBroker, NodeId routingNodeId) {\r
90         this.dataBroker = dataBroker;\r
91         this.routingNode = routingNodeId;\r
92         transactionChain = this.dataBroker.createTransactionChain(this);\r
93     }\r
94 \r
95     void processCreated(Port port) {\r
96         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
97         Optional<BaseEndpointByPort> optBaseEpByPort = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
98                 createBaseEpByPortIid(port.getUuid()), rTx);\r
99         rTx.close();\r
100         if (!optBaseEpByPort.isPresent()) {\r
101             return;\r
102         }\r
103         processCreatedData(port, optBaseEpByPort.get());\r
104     }\r
105 \r
106     void processCreated(BaseEndpointByPort bebp) {\r
107         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
108         Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
109                 createPortIid(bebp.getPortId()), rTx);\r
110         rTx.close();\r
111         if (!optPort.isPresent()) {\r
112             return;\r
113         }\r
114         processCreatedData(optPort.get(), bebp);\r
115     }\r
116 \r
117     @VisibleForTesting\r
118     void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
119         if (isValidVhostUser(port)) {\r
120             VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
121             writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
122             LOG.debug("Created vpp-endpoint {}", vppEp);\r
123         }\r
124     }\r
125 \r
126     private boolean isValidVhostUser(Port port) {\r
127         PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);\r
128         if (portBindingExt != null) {\r
129             String vifType = portBindingExt.getVifType();\r
130             String deviceOwner = port.getDeviceOwner();\r
131             if (vifType != null && deviceOwner != null) {\r
132                 if (vifType.contains(VHOST_USER)) {\r
133                     for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
134                         if (deviceOwner.contains(supportedDeviceOwner)) {\r
135                             return true;\r
136                         }\r
137                     }\r
138                 }\r
139             }\r
140         }\r
141         return false;\r
142     }\r
143 \r
144     void processUpdated(Port original, Port delta) {\r
145         if (!isUpdateNeeded(original, delta)){\r
146             LOG.trace("Port update skipped, port didn`t change. before {}, after: {}" , original, delta);\r
147             return;\r
148         }\r
149 \r
150         LOG.trace("Updating port before: {}, after: {}" , original, delta);\r
151         if (isValidVhostUser(original)) {\r
152             ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
153             Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
154                     createBaseEpByPortIid(original.getUuid()), rTx);\r
155             rTx.close();\r
156             if (!optBebp.isPresent()) {\r
157                 return;\r
158             }\r
159             LOG.trace("Updating port - deleting old port {}" , optBebp.get().getPortId());\r
160             processDeleted(optBebp.get());\r
161         }\r
162         LOG.trace("Updating port - creating new port {}" , delta.getUuid());\r
163         processCreated(delta);\r
164     }\r
165 \r
166     private boolean isUpdateNeeded(final Port oldPort, final Port newPort) {\r
167         //TODO fix this to better support update of ports for VPP\r
168         final PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);\r
169         final PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);\r
170 \r
171         if (newPortAugmentation == null) {\r
172             LOG.trace("Port {} is no longer a vhost type port, updating port...");\r
173             return true;\r
174         }\r
175 \r
176         final String oldDeviceOwner = oldPort.getDeviceOwner();\r
177         final String oldVifType = oldPortAugmentation.getVifType();\r
178         final String newDeviceOwner = newPort.getDeviceOwner();\r
179         final String newVifType = newPortAugmentation.getVifType();\r
180 \r
181         // TODO potential bug here\r
182         // Temporary change for Openstack Mitaka: If old neutron-binding:vif-type is vhost, new one is unbound and\r
183         // device owner is ROUTER_OWNER, skip update. Openstack (or ml2) sometimes sends router update messages in\r
184         // incorrect order which causes unwanted port removal\r
185         if (oldVifType.equals(VHOST_USER) && newVifType.equals(UNBOUND) && oldDeviceOwner != null &&\r
186                 ROUTER_OWNER.equals(oldDeviceOwner) && ROUTER_OWNER.equals(newDeviceOwner)) {\r
187             LOG.warn("Port vif-type was updated from vhost to unbound. This update is currently disabled and will be skipped");\r
188             return false;\r
189         }\r
190 \r
191         if (newVifType != null && !newVifType.equals(oldVifType)) {\r
192             LOG.trace("Vif type changed, old: {} new {}", oldVifType, newVifType);\r
193             return true;\r
194         }\r
195 \r
196         final List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();\r
197 \r
198         if (!oldPortAugmentation.getHostId().equals(newPortAugmentation.getHostId()) ||\r
199             nullToEmpty(vifDetails).size() != nullToEmpty(newPortAugmentation.getVifDetails()).size()) {\r
200             return true;\r
201         }\r
202 \r
203         for (VifDetails vifDetail : nullToEmpty(vifDetails)) {\r
204             //check if vhostuser_socket, vhostuser_mode and port_filter are changed\r
205             if (!newPortAugmentation.getVifDetails().contains(vifDetail))\r
206                 return true;\r
207         }\r
208         return false;\r
209     }\r
210 \r
211     void processDeleted(BaseEndpointByPort bebp) {\r
212         LOG.trace("Deleting vpp-endpoint by BaseEndpointByPort {}" , bebp);\r
213         VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),\r
214                 bebp.getContextType());\r
215         InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);\r
216         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
217         Optional<VppEndpoint> readVppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid, rTx);\r
218         rTx.close();\r
219         if (readVppEp.isPresent()) {\r
220             writeVppEndpoint(vppEpIid, null);\r
221             LOG.debug("Deleted vpp-endpoint {}", vppEpKey);\r
222         }\r
223     }\r
224 \r
225     private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {\r
226         WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();\r
227         if (vppEp != null) {\r
228             wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);\r
229         } else {\r
230             wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
231         }\r
232         wTx.submit();\r
233     }\r
234 \r
235     @VisibleForTesting\r
236     VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
237         PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
238         VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
239             .setContextId(bebp.getContextId())\r
240             .setContextType(bebp.getContextType())\r
241             .setAddress(bebp.getAddress())\r
242             .setAddressType(bebp.getAddressType())\r
243             .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
244             .setVppNodeId(new NodeId(portBinding.getHostId()));\r
245         if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
246             vppEpBuilder.setInterfaceTypeChoice(\r
247                 new VhostUserCaseBuilder().setSocket(getSocketFromPortBinding(portBinding)).build());\r
248         } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
249             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
250                 .setName(createPortName(port.getUuid()))\r
251                 .build();\r
252             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
253         } else if (isValidQRouterPort(port)) {\r
254             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
255                     .setName(createQRouterPortName(port.getUuid()))\r
256                     .build();\r
257             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
258             vppEpBuilder.addAugmentation(ExcludeFromPolicy.class,\r
259                     new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build());\r
260         } else if (isValidVppRouterPort(port)) {\r
261             if (!DEFAULT_NODE.equals(routingNode.getValue())) {\r
262                 LOG.warn(\r
263                         "Host-id changed by ODL for port {}. This is a supplementary workaround for choosing a routing node.",\r
264                         port);\r
265                 vppEpBuilder.setVppNodeId(routingNode);\r
266             }\r
267             vppEpBuilder.addAugmentation(ExcludeFromPolicy.class,\r
268                     new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build());\r
269             vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));\r
270         }\r
271         return vppEpBuilder.build();\r
272     }\r
273 \r
274     private String getSocketFromPortBinding(@Nonnull PortBindingExtension portBindingExtension) {\r
275         List<VifDetails> vifDetails = nullToEmpty(portBindingExtension.getVifDetails());\r
276 \r
277         for (VifDetails detail : vifDetails) {\r
278             if (VHOST_SOCKET_KEY.equalsIgnoreCase(detail.getDetailsKey())) {\r
279                 return detail.getValue();\r
280             }\r
281         }\r
282         return null;\r
283     }\r
284 \r
285     private LoopbackCase getLoopbackCase(Port port) {\r
286         LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
287             .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
288         Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);\r
289         if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){\r
290             loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());\r
291             ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
292             Optional<Subnet> subnetOptional =\r
293                 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
294                     InstanceIdentifier.builder(Neutron.class)\r
295                         .child(Subnets.class)\r
296                         .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
297                         .build(), rTx);\r
298             if (subnetOptional.isPresent()) {\r
299                 Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();\r
300                 loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));\r
301             } else {\r
302                 LOG.warn("IpPrefix for loopback port: {} was not set.", port);\r
303             }\r
304             if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {\r
305                 loopbackCase.setBvi(true);\r
306                 LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);\r
307             }\r
308 \r
309         } else {\r
310             LOG.warn("IpAddress for loopback port: {} was not set.", port);\r
311         }\r
312         return loopbackCase.build();\r
313     }\r
314 \r
315     /**\r
316      * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
317      * to ODL neutron.\r
318      */\r
319     private boolean isValidQRouterPort(Port port) {\r
320         Optional<Router> optRouter = getRouterOptional(port);\r
321         return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
322                 && port.getMacAddress() != null;\r
323     }\r
324 \r
325     private boolean isValidVppRouterPort(Port port) {\r
326         Optional<Router> optRouter = getRouterOptional(port);\r
327         return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
328             && port.getMacAddress() != null;\r
329     }\r
330 \r
331     private Optional<Router> getRouterOptional(Port port) {\r
332         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
333         InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
334             .child(Routers.class)\r
335             .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))\r
336             .build();\r
337         Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
338         rTx.close();\r
339         return optRouter;\r
340     }\r
341 \r
342     public static Optional<FixedIps> resolveFirstFixedIps(Port port) {\r
343         List<FixedIps> fixedIps = port.getFixedIps();\r
344         if (fixedIps != null && !fixedIps.isEmpty()) {\r
345             return Optional.of(fixedIps.get(0));\r
346         }\r
347         return Optional.absent();\r
348     }\r
349 \r
350     private String createPortName(Uuid portUuid) {\r
351         String tapPortName;\r
352         String uuid = portUuid.getValue();\r
353         if (uuid != null && uuid.length() >= 12) {\r
354             tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
355         } else {\r
356             tapPortName = TAP_PORT_NAME_PREFIX + uuid;\r
357         }\r
358         return tapPortName;\r
359     }\r
360 \r
361     private String createQRouterPortName(Uuid portUuid) {\r
362         String tapPortName;\r
363         String uuid = portUuid.getValue();\r
364         if (uuid != null && uuid.length() >= 12) {\r
365             tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
366         } else {\r
367             tapPortName = RT_PORT_NAME_PREFIX + uuid;\r
368         }\r
369         return tapPortName;\r
370     }\r
371 \r
372     private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {\r
373         return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();\r
374     }\r
375 \r
376     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {\r
377         return createBaseEpByPortIid(new UniqueId(uuid.getValue()));\r
378     }\r
379 \r
380     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {\r
381         return InstanceIdentifier.builder(Mappings.class)\r
382             .child(GbpByNeutronMappings.class)\r
383             .child(BaseEndpointsByPorts.class)\r
384             .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))\r
385             .build();\r
386     }\r
387 \r
388     InstanceIdentifier<Port> createWildcartedPortIid() {\r
389         return portsIid().child(Port.class).build();\r
390     }\r
391 \r
392     private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {\r
393         return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();\r
394     }\r
395 \r
396     private InstanceIdentifierBuilder<Ports> portsIid() {\r
397         return InstanceIdentifier.builder(Neutron.class).child(Ports.class);\r
398     }\r
399 \r
400     @Override\r
401     public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,\r
402             Throwable cause) {\r
403         LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),\r
404                 transaction, cause);\r
405         transactionChain.close();\r
406         transactionChain = dataBroker.createTransactionChain(this);\r
407     }\r
408 \r
409     @Override\r
410     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
411         LOG.trace("Transaction chain was successful. {}", chain);\r
412     }\r
413 \r
414     private <T> List<T> nullToEmpty(@Nullable List<T> list) {\r
415         return list == null ? Collections.emptyList() : list;\r
416     }\r
417 }\r