\r
package org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.processors;\r
\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import javax.annotation.Nullable;\r
+\r
import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;\r
import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;\r
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;\r
-import org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.SocketInfo;\r
import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;\r
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;\r
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCaseBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCaseBuilder;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCaseBuilder;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCaseBuilder;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.binding.attributes.VifDetails;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;\r
import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;\r
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;\r
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;\r
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
import org.slf4j.Logger;\r
import com.google.common.annotations.VisibleForTesting;\r
import com.google.common.base.Optional;\r
\r
+import javax.annotation.Nonnull;\r
public class PortHandler implements TransactionChainListener {\r
\r
- private static final Logger LOG = LoggerFactory.getLogger(MappingProvider.class);\r
+ private static final Logger LOG = LoggerFactory.getLogger(PortHandler.class);\r
\r
- private static final String[] COMPUTE_OWNER = {"compute"};\r
+ private static final String COMPUTE_OWNER = "compute";\r
+ private static final String DHCP_OWNER = "dhcp";\r
+ private static final String ROUTER_OWNER = "network:router_interface";\r
+ private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};\r
private static final String VHOST_USER = "vhostuser";\r
- private static final String NETCONF_TOPOLOGY_ID = "topology-netconf";\r
+ private static final String UNBOUND = "unbound";\r
+ private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";\r
+ private static final String TAP_PORT_NAME_PREFIX = "tap";\r
+ private static final String RT_PORT_NAME_PREFIX = "qr-";\r
+ private static final String VHOST_SOCKET_KEY = "vhostuser_socket";\r
\r
private BindingTransactionChain transactionChain;\r
- PortAware portByBaseEpListener;\r
- DataBroker dataBroker;\r
- SocketInfo socketInfo;\r
+ private DataBroker dataBroker;\r
\r
- PortHandler(DataBroker dataBroker, SocketInfo socketInfo) {\r
+ PortHandler(DataBroker dataBroker) {\r
this.dataBroker = dataBroker;\r
- this.socketInfo = socketInfo;\r
transactionChain = this.dataBroker.createTransactionChain(this);\r
}\r
\r
@VisibleForTesting\r
void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
if (isValidVhostUser(port)) {\r
- VppEndpoint vppEp = buildVhostUserEndpoint(port, bebp);\r
+ VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
LOG.debug("Created vpp-endpoint {}", vppEp);\r
}\r
String deviceOwner = port.getDeviceOwner();\r
if (vifType != null && deviceOwner != null) {\r
if (vifType.contains(VHOST_USER)) {\r
- for (String computeOwner : COMPUTE_OWNER) {\r
- if (deviceOwner.contains(computeOwner)) {\r
+ for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
+ if (deviceOwner.contains(supportedDeviceOwner)) {\r
return true;\r
}\r
}\r
}\r
\r
void processUpdated(Port original, Port delta) {\r
+ if (!isUpdateNeeded(original, delta)){\r
+ LOG.trace("Port update skipped, port didn`t change. before {}, after: {}" , original, delta);\r
+ return;\r
+ }\r
+\r
+ LOG.trace("Updating port before: {}, after: {}" , original, delta);\r
if (isValidVhostUser(original)) {\r
ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
Optional<BaseEndpointByPort> optBebp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,\r
if (!optBebp.isPresent()) {\r
return;\r
}\r
+ LOG.trace("Updating port - deleting old port {}" , optBebp.get().getPortId());\r
processDeleted(optBebp.get());\r
}\r
+ LOG.trace("Updating port - creating new port {}" , delta.getUuid());\r
processCreated(delta);\r
}\r
\r
+ private boolean isUpdateNeeded(final Port oldPort, final Port newPort) {\r
+ //TODO fix this to better support update of ports for VPP\r
+ final PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);\r
+ final PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);\r
+\r
+ if (newPortAugmentation == null) {\r
+ LOG.trace("Port {} is no longer a vhost type port, updating port...");\r
+ return true;\r
+ }\r
+\r
+ final String oldDeviceOwner = oldPort.getDeviceOwner();\r
+ final String oldVifType = oldPortAugmentation.getVifType();\r
+ final String newDeviceOwner = newPort.getDeviceOwner();\r
+ final String newVifType = newPortAugmentation.getVifType();\r
+\r
+ // TODO potential bug here\r
+ // Temporary change for Openstack Mitaka: If old neutron-binding:vif-type is vhost, new one is unbound and\r
+ // device owner is ROUTER_OWNER, skip update. Openstack (or ml2) sometimes sends router update messages in\r
+ // incorrect order which causes unwanted port removal\r
+ if (oldVifType.equals(VHOST_USER) && newVifType.equals(UNBOUND) && oldDeviceOwner != null &&\r
+ ROUTER_OWNER.equals(oldDeviceOwner) && ROUTER_OWNER.equals(newDeviceOwner)) {\r
+ LOG.warn("Port vif-type was updated from vhost to unbound. This update is currently disabled and will be skipped");\r
+ return false;\r
+ }\r
+\r
+ if (newVifType != null && !newVifType.equals(oldVifType)) {\r
+ LOG.trace("Vif type changed, old: {} new {}", oldVifType, newVifType);\r
+ return true;\r
+ }\r
+\r
+ final List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();\r
+\r
+ if (!oldPortAugmentation.getHostId().equals(newPortAugmentation.getHostId()) ||\r
+ nullToEmpty(vifDetails).size() != nullToEmpty(newPortAugmentation.getVifDetails()).size()) {\r
+ return true;\r
+ }\r
+\r
+ for (VifDetails vifDetail : nullToEmpty(vifDetails)) {\r
+ //check if vhostuser_socket, vhostuser_mode and port_filter are changed\r
+ if (!newPortAugmentation.getVifDetails().contains(vifDetail))\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
void processDeleted(BaseEndpointByPort bebp) {\r
+ LOG.trace("Deleting vpp-endpoint by BaseEndpointByPort {}" , bebp);\r
VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),\r
bebp.getContextType());\r
InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);\r
}\r
\r
@VisibleForTesting\r
- VppEndpoint buildVhostUserEndpoint(Port port, BaseEndpointByPort bebp) {\r
+ VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
- String socket = socketInfo.getSocketPath() + socketInfo.getSocketPrefix() + bebp.getPortId().getValue();\r
- return new VppEndpointBuilder().setDescription("neutron port")\r
+ VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
.setContextId(bebp.getContextId())\r
.setContextType(bebp.getContextType())\r
.setAddress(bebp.getAddress())\r
- .setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(socket).build())\r
.setAddressType(bebp.getAddressType())\r
- .setVppInterfaceName(bebp.getPortId().getValue())\r
- .setVppNodePath(createNodeIid(new NodeId(portBinding.getHostId())))\r
- .build();\r
+ .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
+ .setVppNodeId(new NodeId(portBinding.getHostId()));\r
+\r
+ if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
+ vppEpBuilder.setInterfaceTypeChoice(\r
+ new VhostUserCaseBuilder().setSocket(getSocketFromPortBinding(portBinding)).build());\r
+ } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
+ TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
+ .setName(createPortName(port.getUuid()))\r
+ .build();\r
+ vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+ } else if (isValidQRouterPort(port)) {\r
+ TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
+ .setName(createQRouterPortName(port.getUuid()))\r
+ .build();\r
+ vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+ } else if (isValidVppRouterPort(port)) {\r
+ vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));\r
+ }\r
+ return vppEpBuilder.build();\r
+ }\r
+\r
+ private String getSocketFromPortBinding(@Nonnull PortBindingExtension portBindingExtension) {\r
+ List<VifDetails> vifDetails = nullToEmpty(portBindingExtension.getVifDetails());\r
+\r
+ for (VifDetails detail : vifDetails) {\r
+ if (VHOST_SOCKET_KEY.equalsIgnoreCase(detail.getDetailsKey())) {\r
+ return detail.getValue();\r
+ }\r
+ }\r
+ return null;\r
}\r
\r
- private InstanceIdentifier<Node> createNodeIid(NodeId nodeId) {\r
- return InstanceIdentifier.builder(NetworkTopology.class)\r
- .child(Topology.class, new TopologyKey(new TopologyId(NETCONF_TOPOLOGY_ID)))\r
- .child(Node.class, new NodeKey(nodeId))\r
+ private LoopbackCase getLoopbackCase(Port port) {\r
+ LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
+ .setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
+ Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);\r
+ if(fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null){\r
+ loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());\r
+ ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+ Optional<Subnet> subnetOptional =\r
+ DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+ InstanceIdentifier.builder(Neutron.class)\r
+ .child(Subnets.class)\r
+ .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
+ .build(), rTx);\r
+ if (subnetOptional.isPresent()) {\r
+ Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();\r
+ loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));\r
+ } else {\r
+ LOG.warn("IpPrefix for loopback port: {} was not set.", port);\r
+ }\r
+ if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {\r
+ loopbackCase.setBvi(true);\r
+ LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);\r
+ }\r
+\r
+ } else {\r
+ LOG.warn("IpAddress for loopback port: {} was not set.", port);\r
+ }\r
+ return loopbackCase.build();\r
+ }\r
+\r
+ /**\r
+ * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped\r
+ * to ODL neutron.\r
+ */\r
+ private boolean isValidQRouterPort(Port port) {\r
+ Optional<Router> optRouter = getRouterOptional(port);\r
+ return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+ && port.getMacAddress() != null;\r
+ }\r
+\r
+ private boolean isValidVppRouterPort(Port port) {\r
+ Optional<Router> optRouter = getRouterOptional(port);\r
+ return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER)\r
+ && port.getMacAddress() != null;\r
+ }\r
+\r
+ private Optional<Router> getRouterOptional(Port port) {\r
+ ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
+ InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
+ .child(Routers.class)\r
+ .child(Router.class, new RouterKey(new Uuid(port.getDeviceId())))\r
.build();\r
+ Optional<Router> optRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid, rTx);\r
+ rTx.close();\r
+ return optRouter;\r
+ }\r
+\r
+ public static Optional<FixedIps> resolveFirstFixedIps(Port port) {\r
+ List<FixedIps> fixedIps = port.getFixedIps();\r
+ if (fixedIps != null && !fixedIps.isEmpty()) {\r
+ return Optional.of(fixedIps.get(0));\r
+ }\r
+ return Optional.absent();\r
+ }\r
+\r
+ private String createPortName(Uuid portUuid) {\r
+ String tapPortName;\r
+ String uuid = portUuid.getValue();\r
+ if (uuid != null && uuid.length() >= 12) {\r
+ tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
+ } else {\r
+ tapPortName = TAP_PORT_NAME_PREFIX + uuid;\r
+ }\r
+ return tapPortName;\r
+ }\r
+\r
+ private String createQRouterPortName(Uuid portUuid) {\r
+ String tapPortName;\r
+ String uuid = portUuid.getValue();\r
+ if (uuid != null && uuid.length() >= 12) {\r
+ tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);\r
+ } else {\r
+ tapPortName = RT_PORT_NAME_PREFIX + uuid;\r
+ }\r
+ return tapPortName;\r
}\r
\r
private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {\r
\r
@Override\r
public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {\r
- LOG.trace("Transaction chain was successfull. {}", chain);\r
+ LOG.trace("Transaction chain was successful. {}", chain);\r
+ }\r
+\r
+ private <T> List<T> nullToEmpty(@Nullable List<T> list) {\r
+ return list == null ? Collections.emptyList() : list;\r
}\r
}\r