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