introducing loopback port for VPP
[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 org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;\r
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
13 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
14 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;\r
15 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;\r
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
17 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;\r
18 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;\r
19 import org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.SocketInfo;\r
20 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;\r
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.GbpByNeutronMappings;\r
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;\r
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;\r
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;\r
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;\r
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCase;\r
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCaseBuilder;\r
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCase;\r
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCaseBuilder;\r
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;\r
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;\r
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;\r
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;\r
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;\r
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;\r
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;\r
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;\r
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;\r
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;\r
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;\r
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;\r
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
61 import org.slf4j.Logger;\r
62 import org.slf4j.LoggerFactory;\r
63 \r
64 import com.google.common.annotations.VisibleForTesting;\r
65 import com.google.common.base.Optional;\r
66 \r
67 import java.util.List;\r
68 \r
69 public class PortHandler implements TransactionChainListener {\r
70 \r
71     private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);\r
72 \r
73     private static final String COMPUTE_OWNER = "compute";\r
74     private static final String DHCP_OWNER = "dhcp";\r
75     private 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 NETCONF_TOPOLOGY_ID = "topology-netconf";\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 \r
83     private BindingTransactionChain transactionChain;\r
84     private DataBroker dataBroker;\r
85     private SocketInfo socketInfo;\r
86 \r
87     PortHandler(DataBroker dataBroker, SocketInfo socketInfo) {\r
88         this.dataBroker = dataBroker;\r
89         this.socketInfo = socketInfo;\r
90         transactionChain = this.dataBroker.createTransactionChain(this);\r
91     }\r
92 \r
93     void processCreated(Port port) {\r
94         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
95         Optional<BaseEndpointByPort> optBaseEpByPort = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
96                 createBaseEpByPortIid(port.getUuid()), rTx);\r
97         rTx.close();\r
98         if (!optBaseEpByPort.isPresent()) {\r
99             return;\r
100         }\r
101         processCreatedData(port, optBaseEpByPort.get());\r
102     }\r
103 \r
104     void processCreated(BaseEndpointByPort bebp) {\r
105         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
106         Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
107                 createPortIid(bebp.getPortId()), rTx);\r
108         rTx.close();\r
109         if (!optPort.isPresent()) {\r
110             return;\r
111         }\r
112         processCreatedData(optPort.get(), bebp);\r
113     }\r
114 \r
115     @VisibleForTesting\r
116     void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
117         if (isValidVhostUser(port)) {\r
118             VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
119             writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
120             LOG.debug("Created vpp-endpoint {}", vppEp);\r
121         }\r
122     }\r
123 \r
124     private boolean isValidVhostUser(Port port) {\r
125         PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);\r
126         if (portBindingExt != null) {\r
127             String vifType = portBindingExt.getVifType();\r
128             String deviceOwner = port.getDeviceOwner();\r
129             if (vifType != null && deviceOwner != null) {\r
130                 if (vifType.contains(VHOST_USER)) {\r
131                     for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
132                         if (deviceOwner.contains(supportedDeviceOwner)) {\r
133                             return true;\r
134                         }\r
135                     }\r
136                 }\r
137             }\r
138         }\r
139         return false;\r
140     }\r
141 \r
142     void processUpdated(Port original, Port delta) {\r
143         if (isValidVhostUser(original)) {\r
144             ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
145             Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
146                     createBaseEpByPortIid(original.getUuid()), rTx);\r
147             rTx.close();\r
148             if (!optBebp.isPresent()) {\r
149                 return;\r
150             }\r
151             processDeleted(optBebp.get());\r
152         }\r
153         processCreated(delta);\r
154     }\r
155 \r
156     void processDeleted(BaseEndpointByPort bebp) {\r
157         VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),\r
158                 bebp.getContextType());\r
159         InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);\r
160         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
161         Optional<VppEndpoint> readVppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid, rTx);\r
162         rTx.close();\r
163         if (readVppEp.isPresent()) {\r
164             writeVppEndpoint(vppEpIid, null);\r
165             LOG.debug("Deleted vpp-endpoint {}", vppEpKey);\r
166         }\r
167     }\r
168 \r
169     private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {\r
170         WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();\r
171         if (vppEp != null) {\r
172             wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);\r
173         } else {\r
174             wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
175         }\r
176         wTx.submit();\r
177     }\r
178 \r
179     @VisibleForTesting\r
180     VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
181         PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
182         VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
183             .setContextId(bebp.getContextId())\r
184             .setContextType(bebp.getContextType())\r
185             .setAddress(bebp.getAddress())\r
186             .setAddressType(bebp.getAddressType())\r
187             .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
188             .setVppNodePath(createNodeIid(new NodeId(portBinding.getHostId())));\r
189         if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
190             String socket = socketInfo.getSocketPath() + socketInfo.getSocketPrefix() + bebp.getPortId().getValue();\r
191             vppEpBuilder.setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(socket).build());\r
192         } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
193             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
194                 .setName(createPortName(port.getUuid()))\r
195                 .build();\r
196             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
197         } else if (isValidQRouterPort(port)) {\r
198             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
199                     .setName(createQRouterPortName(port.getUuid()))\r
200                     .build();\r
201             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
202         } else if (isValidVppRouterPort(port)) {\r
203             vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));\r
204         }\r
205         return vppEpBuilder.build();\r
206     }\r
207 \r
208     private LoopbackCase getLoopbackCase(Port port) {\r
209         LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
210             .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
211         Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);\r
212         if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){\r
213             loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());\r
214             ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
215             Optional<Subnet> subnetOptional =\r
216                 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
217                     InstanceIdentifier.builder(Neutron.class)\r
218                         .child(Subnets.class)\r
219                         .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
220                         .build(), rTx);\r
221             if (subnetOptional.isPresent()) {\r
222                 Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();\r
223                 loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));\r
224             } else {\r
225                 LOG.warn("IpPrefix for loopback port: {} was not set.", port);\r
226             }\r
227             if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {\r
228                 loopbackCase.setBvi(true);\r
229                 LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);\r
230             }\r
231 \r
232         } else {\r
233             LOG.warn("IpAddress for loopback port: {} was not set.", port);\r
234         }\r
235         return loopbackCase.build();\r
236     }\r
237 \r
238     /**\r
239      * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
240      * to ODL neutron.\r
241      */\r
242     private boolean isValidQRouterPort(Port port) {\r
243         Optional<Router> optRouter = getRouterOptional(port);\r
244         return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
245                 && port.getMacAddress() != null;\r
246     }\r
247 \r
248     private boolean isValidVppRouterPort(Port port) {\r
249         Optional<Router> optRouter = getRouterOptional(port);\r
250         return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
251             && port.getMacAddress() != null;\r
252     }\r
253 \r
254     private Optional<Router> getRouterOptional(Port port) {\r
255         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
256         InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
257             .child(Routers.class)\r
258             .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))\r
259             .build();\r
260         Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
261         rTx.close();\r
262         return optRouter;\r
263     }\r
264 \r
265     public static Optional<FixedIps> resolveFirstFixedIps(Port port) {\r
266         List<FixedIps> fixedIps = port.getFixedIps();\r
267         if (fixedIps != null && !fixedIps.isEmpty()) {\r
268             return Optional.of(fixedIps.get(0));\r
269         }\r
270         return Optional.absent();\r
271     }\r
272 \r
273     private String createPortName(Uuid portUuid) {\r
274         String tapPortName;\r
275         String uuid = portUuid.getValue();\r
276         if (uuid != null && uuid.length() >= 12) {\r
277             tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
278         } else {\r
279             tapPortName = TAP_PORT_NAME_PREFIX + uuid;\r
280         }\r
281         return tapPortName;\r
282     }\r
283 \r
284     private String createQRouterPortName(Uuid portUuid) {\r
285         String tapPortName;\r
286         String uuid = portUuid.getValue();\r
287         if (uuid != null && uuid.length() >= 12) {\r
288             tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
289         } else {\r
290             tapPortName = RT_PORT_NAME_PREFIX + uuid;\r
291         }\r
292         return tapPortName;\r
293     }\r
294 \r
295     private InstanceIdentifier<Node> createNodeIid(NodeId nodeId) {\r
296         return InstanceIdentifier.builder(NetworkTopology.class)\r
297             .child(Topology.class, new TopologyKey(new TopologyId(NETCONF_TOPOLOGY_ID)))\r
298             .child(Node.class, new NodeKey(nodeId))\r
299             .build();\r
300     }\r
301 \r
302     private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {\r
303         return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();\r
304     }\r
305 \r
306     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {\r
307         return createBaseEpByPortIid(new UniqueId(uuid.getValue()));\r
308     }\r
309 \r
310     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {\r
311         return InstanceIdentifier.builder(Mappings.class)\r
312             .child(GbpByNeutronMappings.class)\r
313             .child(BaseEndpointsByPorts.class)\r
314             .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))\r
315             .build();\r
316     }\r
317 \r
318     InstanceIdentifier<Port> createWildcartedPortIid() {\r
319         return portsIid().child(Port.class).build();\r
320     }\r
321 \r
322     private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {\r
323         return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();\r
324     }\r
325 \r
326     private InstanceIdentifierBuilder<Ports> portsIid() {\r
327         return InstanceIdentifier.builder(Neutron.class).child(Ports.class);\r
328     }\r
329 \r
330     @Override\r
331     public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,\r
332             Throwable cause) {\r
333         LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),\r
334                 transaction, cause);\r
335         transactionChain.close();\r
336         transactionChain = dataBroker.createTransactionChain(this);\r
337     }\r
338 \r
339     @Override\r
340     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
341         LOG.trace("Transaction chain was successful. {}", chain);\r
342     }\r
343 }\r