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.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.ExcludeFromPolicy;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.ExcludeFromPolicyBuilder;\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
\r
import com.google.common.annotations.VisibleForTesting;\r
import com.google.common.base.Optional;\r
+import com.google.common.base.Strings;\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 DHCP_OWNER = "dhcp";\r
- private static final String ROUTER_OWNER = "network:router_interface";\r
+ 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 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
+ static final String DEFAULT_NODE = "default";\r
\r
+ private final NodeId routingNode;\r
private BindingTransactionChain transactionChain;\r
private DataBroker dataBroker;\r
- private SocketInfo socketInfo;\r
\r
- PortHandler(DataBroker dataBroker, SocketInfo socketInfo) {\r
+ PortHandler(DataBroker dataBroker, NodeId routingNodeId) {\r
this.dataBroker = dataBroker;\r
- this.socketInfo = socketInfo;\r
+ this.routingNode = routingNodeId;\r
transactionChain = this.dataBroker.createTransactionChain(this);\r
}\r
\r
\r
@VisibleForTesting\r
void processCreatedData(Port port, BaseEndpointByPort bebp) {\r
- if (isValidVhostUser(port)) {\r
+ if (isValidVhostUser(port)\r
+ // this is a hack for vpp router port\r
+ // Openstack does not send binding details yet\r
+ || isValidVppRouterPort(port)) {\r
VppEndpoint vppEp = buildVppEndpoint(port, bebp);\r
+ if (vppEp == null) {\r
+ LOG.warn("Cannot create vpp-endpoint from neutron port {}", port);\r
+ return;\r
+ }\r
writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);\r
LOG.debug("Created vpp-endpoint {}", vppEp);\r
}\r
processCreated(delta);\r
}\r
\r
- private boolean isUpdateNeeded(Port oldPort, Port newPort) {\r
+ private boolean isUpdateNeeded(final Port oldPort, final Port newPort) {\r
//TODO fix this to better support update of ports for VPP\r
- PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);\r
- PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);\r
-\r
- List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();\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
.setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())\r
.setVppNodeId(new NodeId(portBinding.getHostId()));\r
if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {\r
- String socket = socketInfo.getSocketPath() + socketInfo.getSocketPrefix() + bebp.getPortId().getValue();\r
- vppEpBuilder.setInterfaceTypeChoice(new VhostUserCaseBuilder().setSocket(socket).build());\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
.setName(createQRouterPortName(port.getUuid()))\r
.build();\r
vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
+ vppEpBuilder.addAugmentation(ExcludeFromPolicy.class,\r
+ new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build());\r
} else if (isValidVppRouterPort(port)) {\r
+ if (!DEFAULT_NODE.equals(routingNode.getValue())) {\r
+ LOG.warn(\r
+ "Host-id changed by ODL for port {}. This is a supplementary workaround for choosing a routing node.",\r
+ port);\r
+ vppEpBuilder.setVppNodeId(routingNode);\r
+ } else if (port.getDeviceId() != null) {\r
+ LOG.debug("Resolving host-id for unbound router port {}", port.getUuid());\r
+ ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();\r
+ Optional<Ports> optPorts = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
+ InstanceIdentifier.builder(Neutron.class).child(Ports.class).build(), readTx);\r
+ readTx.close();\r
+ if (optPorts.isPresent() && optPorts.get().getPort() != null) {\r
+ java.util.Optional<Port> optPortOnTheSameNode = optPorts.get()\r
+ .getPort()\r
+ .stream()\r
+ .filter(p -> !p.getUuid().equals(port.getUuid()))\r
+ .filter(p -> p.getAugmentation(PortBindingExtension.class) != null)\r
+ .filter(p -> p.getDeviceOwner().contains(DHCP_OWNER))\r
+ .findFirst();\r
+ if (optPortOnTheSameNode.isPresent()) {\r
+ PortBindingExtension binding =\r
+ optPortOnTheSameNode.get().getAugmentation(PortBindingExtension.class);\r
+ if (binding != null && binding.getHostId() != null) {\r
+ vppEpBuilder.setVppNodeId(new NodeId(binding.getHostId()));\r
+ } else {\r
+ LOG.warn("Cannot resolve location of router-port {}", port.getUuid());\r
+ return null;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ vppEpBuilder.addAugmentation(ExcludeFromPolicy.class,\r
+ new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build());\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 LoopbackCase getLoopbackCase(Port port) {\r
LoopbackCaseBuilder loopbackCase = new LoopbackCaseBuilder()\r
.setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));\r
}\r
\r
private Optional<Router> getRouterOptional(Port port) {\r
+ if (Strings.isNullOrEmpty(port.getDeviceId())) {\r
+ return Optional.absent();\r
+ }\r
ReadOnlyTransaction rTx = transactionChain.newReadOnlyTransaction();\r
InstanceIdentifier<Router> routerIid = InstanceIdentifier.builder(Neutron.class)\r
.child(Routers.class)\r