2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
\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
9 package org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.processors;
\r
11 import java.util.Collections;
\r
12 import java.util.List;
\r
14 import javax.annotation.Nullable;
\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.neutron.vpp.mapper.SocketInfo;
\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.IpPrefix;
\r
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
\r
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
\r
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
\r
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
\r
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;
\r
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.GbpByNeutronMappings;
\r
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;
\r
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;
\r
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;
\r
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
\r
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;
\r
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCaseBuilder;
\r
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;
\r
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCaseBuilder;
\r
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCaseBuilder;
\r
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
\r
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
\r
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
\r
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
\r
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.binding.attributes.VifDetails;
\r
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
\r
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
\r
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;
\r
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
\r
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
\r
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
\r
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
\r
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
\r
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
\r
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
\r
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;
\r
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
\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
64 import com.google.common.annotations.VisibleForTesting;
\r
65 import com.google.common.base.Optional;
\r
67 public class PortHandler implements TransactionChainListener {
\r
69 private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);
\r
71 private static final String COMPUTE_OWNER = "compute";
\r
72 private static final String DHCP_OWNER = "dhcp";
\r
73 private static final String ROUTER_OWNER = "network:router_interface";
\r
74 private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};
\r
75 private static final String VHOST_USER = "vhostuser";
\r
76 private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";
\r
77 private static final String TAP_PORT_NAME_PREFIX = "tap";
\r
78 private static final String RT_PORT_NAME_PREFIX = "qr-";
\r
80 private BindingTransactionChain transactionChain;
\r
81 private DataBroker dataBroker;
\r
82 private SocketInfo socketInfo;
\r
84 PortHandler(DataBroker dataBroker, SocketInfo socketInfo) {
\r
85 this.dataBroker = dataBroker;
\r
86 this.socketInfo = socketInfo;
\r
87 transactionChain = this.dataBroker.createTransactionChain(this);
\r
90 void processCreated(Port port) {
\r
91 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
92 Optional<BaseEndpointByPort> optBaseEpByPort = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
\r
93 createBaseEpByPortIid(port.getUuid()), rTx);
\r
95 if (!optBaseEpByPort.isPresent()) {
\r
98 processCreatedData(port, optBaseEpByPort.get());
\r
101 void processCreated(BaseEndpointByPort bebp) {
\r
102 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
103 Optional<Port> optPort = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
\r
104 createPortIid(bebp.getPortId()), rTx);
\r
106 if (!optPort.isPresent()) {
\r
109 processCreatedData(optPort.get(), bebp);
\r
113 void processCreatedData(Port port, BaseEndpointByPort bebp) {
\r
114 if (isValidVhostUser(port)) {
\r
115 VppEndpoint vppEp = buildVppEndpoint(port, bebp);
\r
116 writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);
\r
117 LOG.debug("Created vpp-endpoint {}", vppEp);
\r
121 private boolean isValidVhostUser(Port port) {
\r
122 PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);
\r
123 if (portBindingExt != null) {
\r
124 String vifType = portBindingExt.getVifType();
\r
125 String deviceOwner = port.getDeviceOwner();
\r
126 if (vifType != null && deviceOwner != null) {
\r
127 if (vifType.contains(VHOST_USER)) {
\r
128 for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {
\r
129 if (deviceOwner.contains(supportedDeviceOwner)) {
\r
139 void processUpdated(Port original, Port delta) {
\r
140 if (!isUpdateNeeded(original, delta)){
\r
141 LOG.trace("Port update skipped, port didn`t change. before {}, after: {}" , original, delta);
\r
145 LOG.trace("Updating port before: {}, after: {}" , original, delta);
\r
146 if (isValidVhostUser(original)) {
\r
147 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
148 Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
\r
149 createBaseEpByPortIid(original.getUuid()), rTx);
\r
151 if (!optBebp.isPresent()) {
\r
154 LOG.trace("Updating port - deleting old port {}" , optBebp.get().getPortId());
\r
155 processDeleted(optBebp.get());
\r
157 LOG.trace("Updating port - creating new port {}" , delta.getUuid());
\r
158 processCreated(delta);
\r
161 private boolean isUpdateNeeded(Port oldPort, Port newPort) {
\r
162 //TODO fix this to better support update of ports for VPP
\r
163 PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);
\r
164 PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);
\r
166 List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();
\r
168 if (newPortAugmentation == null) {
\r
169 LOG.trace("Port {} is no longer a vhost type port, updating port...");
\r
173 if (!oldPortAugmentation.getHostId().equals(newPortAugmentation.getHostId()) ||
\r
174 nullToEmpty(vifDetails).size() != nullToEmpty(newPortAugmentation.getVifDetails()).size()) {
\r
178 for (VifDetails vifDetail : nullToEmpty(vifDetails)) {
\r
179 //check if vhostuser_socket, vhostuser_mode and port_filter are changed
\r
180 if (!newPortAugmentation.getVifDetails().contains(vifDetail))
\r
186 void processDeleted(BaseEndpointByPort bebp) {
\r
187 LOG.trace("Deleting vpp-endpoint by BaseEndpointByPort {}" , bebp);
\r
188 VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),
\r
189 bebp.getContextType());
\r
190 InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);
\r
191 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
192 Optional<VppEndpoint> readVppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid, rTx);
\r
194 if (readVppEp.isPresent()) {
\r
195 writeVppEndpoint(vppEpIid, null);
\r
196 LOG.debug("Deleted vpp-endpoint {}", vppEpKey);
\r
200 private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {
\r
201 WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
\r
202 if (vppEp != null) {
\r
203 wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);
\r
205 wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);
\r
211 VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {
\r
212 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
\r
213 VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")
\r
214 .setContextId(bebp.getContextId())
\r
215 .setContextType(bebp.getContextType())
\r
216 .setAddress(bebp.getAddress())
\r
217 .setAddressType(bebp.getAddressType())
\r
218 .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())
\r
219 .setVppNodeId(new NodeId(portBinding.getHostId()));
\r
220 if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {
\r
221 String socket = socketInfo.getSocketPath() + socketInfo.getSocketPrefix() + bebp.getPortId().getValue();
\r
222 vppEpBuilder.setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(socket).build());
\r
223 } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {
\r
224 TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))
\r
225 .setName(createPortName(port.getUuid()))
\r
227 vppEpBuilder.setInterfaceTypeChoice(tapCase);
\r
228 } else if (isValidQRouterPort(port)) {
\r
229 TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))
\r
230 .setName(createQRouterPortName(port.getUuid()))
\r
232 vppEpBuilder.setInterfaceTypeChoice(tapCase);
\r
233 } else if (isValidVppRouterPort(port)) {
\r
234 vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));
\r
236 return vppEpBuilder.build();
\r
239 private LoopbackCase getLoopbackCase(Port port) {
\r
240 LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()
\r
241 .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));
\r
242 Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);
\r
243 if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){
\r
244 loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());
\r
245 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
246 Optional<Subnet> subnetOptional =
\r
247 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
\r
248 InstanceIdentifier.builder(Neutron.class)
\r
249 .child(Subnets.class)
\r
250 .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))
\r
252 if (subnetOptional.isPresent()) {
\r
253 Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();
\r
254 loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));
\r
256 LOG.warn("IpPrefix for loopback port: {} was not set.", port);
\r
258 if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {
\r
259 loopbackCase.setBvi(true);
\r
260 LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);
\r
264 LOG.warn("IpAddress for loopback port: {} was not set.", port);
\r
266 return loopbackCase.build();
\r
270 * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped
\r
273 private boolean isValidQRouterPort(Port port) {
\r
274 Optional<Router> optRouter = getRouterOptional(port);
\r
275 return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)
\r
276 && port.getMacAddress() != null;
\r
279 private boolean isValidVppRouterPort(Port port) {
\r
280 Optional<Router> optRouter = getRouterOptional(port);
\r
281 return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)
\r
282 && port.getMacAddress() != null;
\r
285 private Optional<Router> getRouterOptional(Port port) {
\r
286 ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();
\r
287 InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)
\r
288 .child(Routers.class)
\r
289 .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))
\r
291 Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);
\r
296 public static Optional<FixedIps> resolveFirstFixedIps(Port port) {
\r
297 List<FixedIps> fixedIps = port.getFixedIps();
\r
298 if (fixedIps != null && !fixedIps.isEmpty()) {
\r
299 return Optional.of(fixedIps.get(0));
\r
301 return Optional.absent();
\r
304 private String createPortName(Uuid portUuid) {
\r
305 String tapPortName;
\r
306 String uuid = portUuid.getValue();
\r
307 if (uuid != null && uuid.length() >= 12) {
\r
308 tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);
\r
310 tapPortName = TAP_PORT_NAME_PREFIX + uuid;
\r
312 return tapPortName;
\r
315 private String createQRouterPortName(Uuid portUuid) {
\r
316 String tapPortName;
\r
317 String uuid = portUuid.getValue();
\r
318 if (uuid != null && uuid.length() >= 12) {
\r
319 tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);
\r
321 tapPortName = RT_PORT_NAME_PREFIX + uuid;
\r
323 return tapPortName;
\r
326 private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {
\r
327 return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();
\r
330 private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {
\r
331 return createBaseEpByPortIid(new UniqueId(uuid.getValue()));
\r
334 private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {
\r
335 return InstanceIdentifier.builder(Mappings.class)
\r
336 .child(GbpByNeutronMappings.class)
\r
337 .child(BaseEndpointsByPorts.class)
\r
338 .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))
\r
342 InstanceIdentifier<Port> createWildcartedPortIid() {
\r
343 return portsIid().child(Port.class).build();
\r
346 private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {
\r
347 return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();
\r
350 private InstanceIdentifierBuilder<Ports> portsIid() {
\r
351 return InstanceIdentifier.builder(Neutron.class).child(Ports.class);
\r
355 public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,
\r
357 LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),
\r
358 transaction, cause);
\r
359 transactionChain.close();
\r
360 transactionChain = dataBroker.createTransactionChain(this);
\r
364 public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
\r
365 LOG.trace("Transaction chain was successful. {}", chain);
\r
368 private <T> List<T> nullToEmpty(@Nullable List<T> list) {
\r
369 return list == null ? Collections.emptyList() : list;
\r