Merge "Bug 6421 - Missing tap port for Qrouter"
[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.yang.types.rev130715.PhysAddress;\r
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;\r
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.GbpByNeutronMappings;\r
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;\r
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;\r
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;\r
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;\r
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCase;\r
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.TapCaseBuilder;\r
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;\r
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;\r
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;\r
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;\r
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;\r
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;\r
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;\r
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;\r
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
53 import org.slf4j.Logger;\r
54 import org.slf4j.LoggerFactory;\r
55 \r
56 import com.google.common.annotations.VisibleForTesting;\r
57 import com.google.common.base.Optional;\r
58 \r
59 public class PortHandler implements TransactionChainListener {\r
60 \r
61     private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);\r
62 \r
63     private static final String COMPUTE_OWNER = "compute";\r
64     private static final String DHCP_OWNER = "dhcp";\r
65     private static final String ROUTER_OWNER = "network:router_interface";\r
66     private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};\r
67     private static final String VHOST_USER = "vhostuser";\r
68     private static final String NETCONF_TOPOLOGY_ID = "topology-netconf";\r
69     private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";\r
70     private static final String TAP_PORT_NAME_PREFIX = "tap";\r
71     private static final String RT_PORT_NAME_PREFIX = "qr-";\r
72 \r
73     private BindingTransactionChain transactionChain;\r
74     private DataBroker dataBroker;\r
75     private SocketInfo socketInfo;\r
76 \r
77     PortHandler(DataBroker dataBroker, SocketInfo socketInfo) {\r
78         this.dataBroker = dataBroker;\r
79         this.socketInfo = socketInfo;\r
80         transactionChain = this.dataBroker.createTransactionChain(this);\r
81     }\r
82 \r
83     void processCreated(Port port) {\r
84         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
85         Optional<BaseEndpointByPort> optBaseEpByPort = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
86                 createBaseEpByPortIid(port.getUuid()), rTx);\r
87         rTx.close();\r
88         if (!optBaseEpByPort.isPresent()) {\r
89             return;\r
90         }\r
91         processCreatedData(port, optBaseEpByPort.get());\r
92     }\r
93 \r
94     void processCreated(BaseEndpointByPort bebp) {\r
95         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
96         Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
97                 createPortIid(bebp.getPortId()), rTx);\r
98         rTx.close();\r
99         if (!optPort.isPresent()) {\r
100             return;\r
101         }\r
102         processCreatedData(optPort.get(), bebp);\r
103     }\r
104 \r
105     @VisibleForTesting\r
106     void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
107         if (isValidVhostUser(port)) {\r
108             VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
109             writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
110             LOG.debug("Created vpp-endpoint {}", vppEp);\r
111         }\r
112     }\r
113 \r
114     private boolean isValidVhostUser(Port port) {\r
115         PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);\r
116         if (portBindingExt != null) {\r
117             String vifType = portBindingExt.getVifType();\r
118             String deviceOwner = port.getDeviceOwner();\r
119             if (vifType != null && deviceOwner != null) {\r
120                 if (vifType.contains(VHOST_USER)) {\r
121                     for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
122                         if (deviceOwner.contains(supportedDeviceOwner)) {\r
123                             return true;\r
124                         }\r
125                     }\r
126                 }\r
127             }\r
128         }\r
129         return false;\r
130     }\r
131 \r
132     void processUpdated(Port original, Port delta) {\r
133         if (isValidVhostUser(original)) {\r
134             ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
135             Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
136                     createBaseEpByPortIid(original.getUuid()), rTx);\r
137             rTx.close();\r
138             if (!optBebp.isPresent()) {\r
139                 return;\r
140             }\r
141             processDeleted(optBebp.get());\r
142         }\r
143         processCreated(delta);\r
144     }\r
145 \r
146     void processDeleted(BaseEndpointByPort bebp) {\r
147         VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),\r
148                 bebp.getContextType());\r
149         InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);\r
150         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
151         Optional<VppEndpoint> readVppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid, rTx);\r
152         rTx.close();\r
153         if (readVppEp.isPresent()) {\r
154             writeVppEndpoint(vppEpIid, null);\r
155             LOG.debug("Deleted vpp-endpoint {}", vppEpKey);\r
156         }\r
157     }\r
158 \r
159     private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {\r
160         WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();\r
161         if (vppEp != null) {\r
162             wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);\r
163         } else {\r
164             wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
165         }\r
166         wTx.submit();\r
167     }\r
168 \r
169     @VisibleForTesting\r
170     VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
171         PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
172         VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
173             .setContextId(bebp.getContextId())\r
174             .setContextType(bebp.getContextType())\r
175             .setAddress(bebp.getAddress())\r
176             .setAddressType(bebp.getAddressType())\r
177             .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
178             .setVppNodePath(createNodeIid(new NodeId(portBinding.getHostId())));\r
179         if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
180             String socket = socketInfo.getSocketPath() + socketInfo.getSocketPrefix() + bebp.getPortId().getValue();\r
181             vppEpBuilder.setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(socket).build());\r
182         } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
183             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
184                 .setName(createPortName(port.getUuid()))\r
185                 .build();\r
186             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
187         } else if (isValidQRouterPort(port)) {\r
188             TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
189                     .setName(createQRouterPortName(port.getUuid()))\r
190                     .build();\r
191             vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
192         }\r
193         return vppEpBuilder.build();\r
194     }\r
195 \r
196     /**\r
197      * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
198      * to ODL neutron.\r
199      */\r
200     private boolean isValidQRouterPort(Port port) {\r
201         ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
202         InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
203             .child(Routers.class)\r
204             .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))\r
205             .build();\r
206         Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
207         rTx.close();\r
208         return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
209                 && port.getMacAddress() != null;\r
210     }\r
211 \r
212     private String createPortName(Uuid portUuid) {\r
213         String tapPortName;\r
214         String uuid = portUuid.getValue();\r
215         if (uuid != null && uuid.length() >= 12) {\r
216             tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
217         } else {\r
218             tapPortName = TAP_PORT_NAME_PREFIX + uuid;\r
219         }\r
220         return tapPortName;\r
221     }\r
222 \r
223     private String createQRouterPortName(Uuid portUuid) {\r
224         String tapPortName;\r
225         String uuid = portUuid.getValue();\r
226         if (uuid != null && uuid.length() >= 12) {\r
227             tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
228         } else {\r
229             tapPortName = RT_PORT_NAME_PREFIX + uuid;\r
230         }\r
231         return tapPortName;\r
232     }\r
233 \r
234     private InstanceIdentifier<Node> createNodeIid(NodeId nodeId) {\r
235         return InstanceIdentifier.builder(NetworkTopology.class)\r
236             .child(Topology.class, new TopologyKey(new TopologyId(NETCONF_TOPOLOGY_ID)))\r
237             .child(Node.class, new NodeKey(nodeId))\r
238             .build();\r
239     }\r
240 \r
241     private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {\r
242         return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();\r
243     }\r
244 \r
245     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {\r
246         return createBaseEpByPortIid(new UniqueId(uuid.getValue()));\r
247     }\r
248 \r
249     private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {\r
250         return InstanceIdentifier.builder(Mappings.class)\r
251             .child(GbpByNeutronMappings.class)\r
252             .child(BaseEndpointsByPorts.class)\r
253             .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))\r
254             .build();\r
255     }\r
256 \r
257     InstanceIdentifier<Port> createWildcartedPortIid() {\r
258         return portsIid().child(Port.class).build();\r
259     }\r
260 \r
261     private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {\r
262         return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();\r
263     }\r
264 \r
265     private InstanceIdentifierBuilder<Ports> portsIid() {\r
266         return InstanceIdentifier.builder(Neutron.class).child(Ports.class);\r
267     }\r
268 \r
269     @Override\r
270     public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,\r
271             Throwable cause) {\r
272         LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),\r
273                 transaction, cause);\r
274         transactionChain.close();\r
275         transactionChain = dataBroker.createTransactionChain(this);\r
276     }\r
277 \r
278     @Override\r
279     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
280         LOG.trace("Transaction chain was successfull. {}", chain);\r
281     }\r
282 }\r