-/*\r
- * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-\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.Nonnull;\r
-import javax.annotation.Nullable;\r
-\r
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;\r
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;\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.util.DataStoreHelper;\r
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;\r
-import org.opendaylight.groupbasedpolicy.util.SyncedChain;\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.GbpByNeutronMappings;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;\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
-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.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.portsecurity.rev150712.PortSecurityExtension;\r
-import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;\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.yangtools.yang.binding.InstanceIdentifier;\r
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.google.common.annotations.VisibleForTesting;\r
-import com.google.common.base.Optional;\r
-import com.google.common.base.Preconditions;\r
-import com.google.common.base.Strings;\r
-\r
-public class PortHandler implements TransactionChainListener {\r
-\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
- 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 SyncedChain syncedChain;\r
- private DataBroker dataBroker;\r
-\r
- PortHandler(DataBroker dataBroker, NodeId routingNodeId) {\r
- this.dataBroker = dataBroker;\r
- this.routingNode = routingNodeId;\r
- this.syncedChain = new SyncedChain(Preconditions.checkNotNull(dataBroker.createTransactionChain(this)));\r
- }\r
-\r
- void processCreated(Port port) {\r
- Optional<BaseEndpointByPort> optBaseEpByPort =\r
- syncedChain.readFromDs(LogicalDatastoreType.OPERATIONAL, createBaseEpByPortIid(port.getUuid()));\r
- if (!optBaseEpByPort.isPresent()) {\r
- return;\r
- }\r
- processCreatedData(port, optBaseEpByPort.get());\r
- }\r
-\r
- void processCreated(BaseEndpointByPort bebp) {\r
- Optional<Port> optPort =\r
- syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, createPortIid(bebp.getPortId()));\r
- if (!optPort.isPresent()) {\r
- return;\r
- }\r
- processCreatedData(optPort.get(), bebp);\r
- }\r
-\r
- @VisibleForTesting\r
- void processCreatedData(Port port, BaseEndpointByPort bebp) {\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
- }\r
-\r
- private boolean isValidVhostUser(Port port) {\r
- PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);\r
- if (portBindingExt != null) {\r
- String vifType = portBindingExt.getVifType();\r
- String deviceOwner = port.getDeviceOwner();\r
- if (vifType != null && deviceOwner != null) {\r
- if (vifType.contains(VHOST_USER)) {\r
- for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {\r
- if (deviceOwner.contains(supportedDeviceOwner)) {\r
- return true;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- return false;\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
- Optional<BaseEndpointByPort> optBebp =\r
- syncedChain.readFromDs(LogicalDatastoreType.OPERATIONAL, createBaseEpByPortIid(original.getUuid()));\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\r
- // is unbound and\r
- // device owner is ROUTER_OWNER, skip update. Openstack (or ml2) sometimes sends router\r
- // 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(\r
- "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
- Optional<VppEndpoint> readVppEp = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
- if (readVppEp.isPresent()) {\r
- writeVppEndpoint(vppEpIid, null);\r
- LOG.debug("Deleted vpp-endpoint {}", vppEpKey);\r
- }\r
- }\r
-\r
- private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {\r
- WriteTransaction wTx = syncedChain.newWriteOnlyTransaction();\r
- if (vppEp != null) {\r
- wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);\r
- } else {\r
- wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);\r
- }\r
- syncedChain.submitNow(wTx);\r
- }\r
-\r
- @VisibleForTesting\r
- VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {\r
- PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);\r
- ExcludeFromPolicy excludeFromPolicy = new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build();\r
- VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")\r
- .setContextId(bebp.getContextId())\r
- .setContextType(bebp.getContextType())\r
- .setAddress(bebp.getAddress())\r
- .setAddressType(bebp.getAddressType())\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
- Optional<PortSecurityExtension> portSecurity =\r
- Optional.fromNullable(port.getAugmentation(PortSecurityExtension.class));\r
- if (portSecurity.isPresent() && !portSecurity.get().isPortSecurityEnabled()) {\r
- vppEpBuilder.addAugmentation(ExcludeFromPolicy.class, excludeFromPolicy);\r
- }\r
-\r
- } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {\r
- IpAddress dhcpServerIpAddress = port.getFixedIps().stream().findFirst().isPresent() ?\r
- port.getFixedIps().stream().findFirst().get().getIpAddress() : null;\r
- TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))\r
- .setName(createPortName(port.getUuid()))\r
- .setDhcpServerAddress(dhcpServerIpAddress)\r
- .build();\r
- vppEpBuilder.setInterfaceTypeChoice(tapCase);\r
-\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
- vppEpBuilder.addAugmentation(ExcludeFromPolicy.class, excludeFromPolicy);\r
-\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
- Optional<Ports> optPorts = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
- InstanceIdentifier.builder(Neutron.class).child(Ports.class).build());\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, excludeFromPolicy);\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 =\r
- new LoopbackCaseBuilder().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
- Optional<Subnet> subnetOptional = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION,\r
- InstanceIdentifier.builder(Neutron.class)\r
- .child(Subnets.class)\r
- .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))\r
- .build());\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) && 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) && port.getMacAddress() != null;\r
- }\r
-\r
- private Optional<Router> getRouterOptional(Port port) {\r
- if (Strings.isNullOrEmpty(port.getDeviceId())) {\r
- return Optional.absent();\r
- }\r
- RouterKey routerKey = null;\r
- try {\r
- routerKey = new RouterKey(new Uuid(port.getDeviceId()));\r
- } catch (IllegalArgumentException e) {\r
- // port.getDeviceId() may not match Uuid.PATTERN_CONSTANTS\r
- return Optional.absent();\r
- }\r
- InstanceIdentifier<Router> routerIid =\r
- InstanceIdentifier.builder(Neutron.class).child(Routers.class).child(Router.class, routerKey).build();\r
- Optional<Router> optRouter = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid);\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
- return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();\r
- }\r
-\r
- private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {\r
- return createBaseEpByPortIid(new UniqueId(uuid.getValue()));\r
- }\r
-\r
- private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {\r
- return InstanceIdentifier.builder(Mappings.class)\r
- .child(GbpByNeutronMappings.class)\r
- .child(BaseEndpointsByPorts.class)\r
- .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))\r
- .build();\r
- }\r
-\r
- InstanceIdentifier<Port> createWildcartedPortIid() {\r
- return portsIid().child(Port.class).build();\r
- }\r
-\r
- private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {\r
- return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();\r
- }\r
-\r
- private InstanceIdentifierBuilder<Ports> portsIid() {\r
- return InstanceIdentifier.builder(Neutron.class).child(Ports.class);\r
- }\r
-\r
- @Override\r
- public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,\r
- Throwable cause) {\r
- LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),\r
- transaction, cause);\r
- syncedChain.closeChain();\r
- this.syncedChain = new SyncedChain(Preconditions.checkNotNull(dataBroker.createTransactionChain(this)));\r
- }\r
-\r
- @Override\r
- public void onTransactionChainSuccessful(TransactionChain<?, ?> 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
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.processors;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.groupbasedpolicy.util.SyncedChain;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.Mappings;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.GbpByNeutronMappings;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.BaseEndpointsByPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.base.endpoints.by.ports.BaseEndpointByPortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.ExcludeFromPolicy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.ExcludeFromPolicyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.binding.attributes.VifDetails;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.portsecurity.rev150712.PortSecurityExtension;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+public class PortHandler implements TransactionChainListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortHandler.class);
+
+ private static final String COMPUTE_OWNER = "compute";
+ private static final String DHCP_OWNER = "dhcp";
+ static final String ROUTER_OWNER = "network:router_interface";
+ private static final String[] SUPPORTED_DEVICE_OWNERS = {COMPUTE_OWNER, DHCP_OWNER, ROUTER_OWNER};
+ private static final String VHOST_USER = "vhostuser";
+ private static final String UNBOUND = "unbound";
+ private static final String VPP_INTERFACE_NAME_PREFIX = "neutron_port_";
+ private static final String TAP_PORT_NAME_PREFIX = "tap";
+ private static final String RT_PORT_NAME_PREFIX = "qr-";
+ private static final String VHOST_SOCKET_KEY = "vhostuser_socket";
+ static final String DEFAULT_NODE = "default";
+
+ private final NodeId routingNode;
+ private SyncedChain syncedChain;
+ private DataBroker dataBroker;
+
+ PortHandler(DataBroker dataBroker, NodeId routingNodeId) {
+ this.dataBroker = dataBroker;
+ this.routingNode = routingNodeId;
+ this.syncedChain = new SyncedChain(Preconditions.checkNotNull(dataBroker.createTransactionChain(this)));
+ }
+
+ void processCreated(Port port) {
+ Optional<BaseEndpointByPort> optBaseEpByPort =
+ syncedChain.readFromDs(LogicalDatastoreType.OPERATIONAL, createBaseEpByPortIid(port.getUuid()));
+ if (!optBaseEpByPort.isPresent()) {
+ return;
+ }
+ processCreatedData(port, optBaseEpByPort.get());
+ }
+
+ void processCreated(BaseEndpointByPort bebp) {
+ Optional<Port> optPort =
+ syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, createPortIid(bebp.getPortId()));
+ if (!optPort.isPresent()) {
+ return;
+ }
+ processCreatedData(optPort.get(), bebp);
+ }
+
+ @VisibleForTesting
+ void processCreatedData(Port port, BaseEndpointByPort bebp) {
+ if (isValidVhostUser(port)
+ // this is a hack for vpp router port
+ // Openstack does not send binding details yet
+ || isValidVppRouterPort(port)) {
+ VppEndpoint vppEp = buildVppEndpoint(port, bebp);
+ if (vppEp == null) {
+ LOG.warn("Cannot create vpp-endpoint from neutron port {}", port);
+ return;
+ }
+ writeVppEndpoint(createVppEndpointIid(vppEp.getKey()), vppEp);
+ LOG.debug("Created vpp-endpoint {}", vppEp);
+ }
+ }
+
+ private boolean isValidVhostUser(Port port) {
+ PortBindingExtension portBindingExt = port.getAugmentation(PortBindingExtension.class);
+ if (portBindingExt != null) {
+ String vifType = portBindingExt.getVifType();
+ String deviceOwner = port.getDeviceOwner();
+ if (vifType != null && deviceOwner != null) {
+ if (vifType.contains(VHOST_USER)) {
+ for (String supportedDeviceOwner : SUPPORTED_DEVICE_OWNERS) {
+ if (deviceOwner.contains(supportedDeviceOwner)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void processUpdated(Port original, Port delta) {
+ if (!isUpdateNeeded(original, delta)) {
+ LOG.trace("Port update skipped, port didn`t change. before {}, after: {}", original, delta);
+ return;
+ }
+
+ LOG.trace("Updating port before: {}, after: {}", original, delta);
+ if (isValidVhostUser(original)) {
+ Optional<BaseEndpointByPort> optBebp =
+ syncedChain.readFromDs(LogicalDatastoreType.OPERATIONAL, createBaseEpByPortIid(original.getUuid()));
+ if (!optBebp.isPresent()) {
+ return;
+ }
+ LOG.trace("Updating port - deleting old port {}", optBebp.get().getPortId());
+ processDeleted(optBebp.get());
+ }
+ LOG.trace("Updating port - creating new port {}", delta.getUuid());
+ processCreated(delta);
+ }
+
+ private boolean isUpdateNeeded(final Port oldPort, final Port newPort) {
+ // TODO fix this to better support update of ports for VPP
+ final PortBindingExtension oldPortAugmentation = oldPort.getAugmentation(PortBindingExtension.class);
+ final PortBindingExtension newPortAugmentation = newPort.getAugmentation(PortBindingExtension.class);
+
+ if (newPortAugmentation == null) {
+ LOG.trace("Port {} is no longer a vhost type port, updating port...");
+ return true;
+ }
+
+ final String oldDeviceOwner = oldPort.getDeviceOwner();
+ final String oldVifType = oldPortAugmentation.getVifType();
+ final String newDeviceOwner = newPort.getDeviceOwner();
+ final String newVifType = newPortAugmentation.getVifType();
+
+ // TODO potential bug here
+ // Temporary change for Openstack Mitaka: If old neutron-binding:vif-type is vhost, new one
+ // is unbound and
+ // device owner is ROUTER_OWNER, skip update. Openstack (or ml2) sometimes sends router
+ // update messages in
+ // incorrect order which causes unwanted port removal
+ if (oldVifType.equals(VHOST_USER) && newVifType.equals(UNBOUND) && oldDeviceOwner != null
+ && ROUTER_OWNER.equals(oldDeviceOwner) && ROUTER_OWNER.equals(newDeviceOwner)) {
+ LOG.warn(
+ "Port vif-type was updated from vhost to unbound. This update is currently disabled and will be skipped");
+ return false;
+ }
+
+ if (newVifType != null && !newVifType.equals(oldVifType)) {
+ LOG.trace("Vif type changed, old: {} new {}", oldVifType, newVifType);
+ return true;
+ }
+
+ final List<VifDetails> vifDetails = oldPortAugmentation.getVifDetails();
+
+ if (!oldPortAugmentation.getHostId().equals(newPortAugmentation.getHostId())
+ || nullToEmpty(vifDetails).size() != nullToEmpty(newPortAugmentation.getVifDetails()).size()) {
+ return true;
+ }
+
+ for (VifDetails vifDetail : nullToEmpty(vifDetails)) {
+ // check if vhostuser_socket, vhostuser_mode and port_filter are changed
+ if (!newPortAugmentation.getVifDetails().contains(vifDetail))
+ return true;
+ }
+ return false;
+ }
+
+ void processDeleted(BaseEndpointByPort bebp) {
+ LOG.trace("Deleting vpp-endpoint by BaseEndpointByPort {}", bebp);
+ VppEndpointKey vppEpKey = new VppEndpointKey(bebp.getAddress(), bebp.getAddressType(), bebp.getContextId(),
+ bebp.getContextType());
+ InstanceIdentifier<VppEndpoint> vppEpIid = createVppEndpointIid(vppEpKey);
+ Optional<VppEndpoint> readVppEp = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, vppEpIid);
+ if (readVppEp.isPresent()) {
+ writeVppEndpoint(vppEpIid, null);
+ LOG.debug("Deleted vpp-endpoint {}", vppEpKey);
+ }
+ }
+
+ private synchronized void writeVppEndpoint(InstanceIdentifier<VppEndpoint> vppEpIid, VppEndpoint vppEp) {
+ WriteTransaction wTx = syncedChain.newWriteOnlyTransaction();
+ if (vppEp != null) {
+ wTx.put(LogicalDatastoreType.CONFIGURATION, vppEpIid, vppEp, true);
+ } else {
+ wTx.delete(LogicalDatastoreType.CONFIGURATION, vppEpIid);
+ }
+ syncedChain.submitNow(wTx);
+ }
+
+ @VisibleForTesting
+ VppEndpoint buildVppEndpoint(Port port, BaseEndpointByPort bebp) {
+ PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
+ ExcludeFromPolicy excludeFromPolicy = new ExcludeFromPolicyBuilder().setExcludeFromPolicy(true).build();
+ VppEndpointBuilder vppEpBuilder = new VppEndpointBuilder().setDescription("neutron port")
+ .setContextId(bebp.getContextId())
+ .setContextType(bebp.getContextType())
+ .setAddress(bebp.getAddress())
+ .setAddressType(bebp.getAddressType())
+ .setVppInterfaceName(VPP_INTERFACE_NAME_PREFIX + bebp.getPortId().getValue())
+ .setVppNodeId(new NodeId(portBinding.getHostId()));
+
+ if (port.getDeviceOwner().contains(COMPUTE_OWNER)) {
+ vppEpBuilder.setInterfaceTypeChoice(
+ new VhostUserCaseBuilder().setSocket(getSocketFromPortBinding(portBinding)).build());
+ Optional<PortSecurityExtension> portSecurity =
+ Optional.fromNullable(port.getAugmentation(PortSecurityExtension.class));
+ if (portSecurity.isPresent() && !portSecurity.get().isPortSecurityEnabled()) {
+ vppEpBuilder.addAugmentation(ExcludeFromPolicy.class, excludeFromPolicy);
+ }
+
+ } else if (port.getDeviceOwner().contains(DHCP_OWNER) && port.getMacAddress() != null) {
+ IpAddress dhcpServerIpAddress = port.getFixedIps().stream().findFirst().isPresent() ?
+ port.getFixedIps().stream().findFirst().get().getIpAddress() : null;
+ TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))
+ .setName(createPortName(port.getUuid()))
+ .setDhcpServerAddress(dhcpServerIpAddress)
+ .build();
+ vppEpBuilder.setInterfaceTypeChoice(tapCase);
+
+ } else if (isValidQRouterPort(port)) {
+ TapCase tapCase = new TapCaseBuilder().setPhysicalAddress(new PhysAddress(port.getMacAddress().getValue()))
+ .setName(createQRouterPortName(port.getUuid()))
+ .build();
+ vppEpBuilder.setInterfaceTypeChoice(tapCase);
+ vppEpBuilder.addAugmentation(ExcludeFromPolicy.class, excludeFromPolicy);
+
+ } else if (isValidVppRouterPort(port)) {
+ if (!DEFAULT_NODE.equals(routingNode.getValue())) {
+ LOG.warn(
+ "Host-id changed by ODL for port {}. This is a supplementary workaround for choosing a routing node.",
+ port);
+ vppEpBuilder.setVppNodeId(routingNode);
+ } else if (port.getDeviceId() != null) {
+ LOG.debug("Resolving host-id for unbound router port {}", port.getUuid());
+ Optional<Ports> optPorts = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION,
+ InstanceIdentifier.builder(Neutron.class).child(Ports.class).build());
+ if (optPorts.isPresent() && optPorts.get().getPort() != null) {
+ java.util.Optional<Port> optPortOnTheSameNode = optPorts.get()
+ .getPort()
+ .stream()
+ .filter(p -> !p.getUuid().equals(port.getUuid()))
+ .filter(p -> p.getAugmentation(PortBindingExtension.class) != null)
+ .filter(p -> p.getDeviceOwner().contains(DHCP_OWNER))
+ .findFirst();
+ if (optPortOnTheSameNode.isPresent()) {
+ PortBindingExtension binding =
+ optPortOnTheSameNode.get().getAugmentation(PortBindingExtension.class);
+ if (binding != null && binding.getHostId() != null) {
+ vppEpBuilder.setVppNodeId(new NodeId(binding.getHostId()));
+ } else {
+ LOG.warn("Cannot resolve location of router-port {}", port.getUuid());
+ return null;
+ }
+ }
+ }
+ }
+ vppEpBuilder.addAugmentation(ExcludeFromPolicy.class, excludeFromPolicy);
+ vppEpBuilder.setInterfaceTypeChoice(getLoopbackCase(port));
+ }
+ return vppEpBuilder.build();
+ }
+
+ private String getSocketFromPortBinding(@Nonnull PortBindingExtension portBindingExtension) {
+ List<VifDetails> vifDetails = nullToEmpty(portBindingExtension.getVifDetails());
+
+ for (VifDetails detail : vifDetails) {
+ if (VHOST_SOCKET_KEY.equalsIgnoreCase(detail.getDetailsKey())) {
+ return detail.getValue();
+ }
+ }
+ return null;
+ }
+
+ private LoopbackCase getLoopbackCase(Port port) {
+ LoopbackCaseBuilder loopbackCase =
+ new LoopbackCaseBuilder().setPhysAddress(new PhysAddress(port.getMacAddress().getValue()));
+ Optional<FixedIps> fixedIpsOptional = resolveFirstFixedIps(port);
+ if (fixedIpsOptional.isPresent() && fixedIpsOptional.get().getIpAddress() != null) {
+ loopbackCase.setIpAddress(fixedIpsOptional.get().getIpAddress());
+ Optional<Subnet> subnetOptional = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION,
+ InstanceIdentifier.builder(Neutron.class)
+ .child(Subnets.class)
+ .child(Subnet.class, new SubnetKey(fixedIpsOptional.get().getSubnetId()))
+ .build());
+ if (subnetOptional.isPresent()) {
+ Ipv4Prefix ipv4Prefix = subnetOptional.get().getCidr().getIpv4Prefix();
+ loopbackCase.setIpPrefix(new IpPrefix(ipv4Prefix));
+ } else {
+ LOG.warn("IpPrefix for loopback port: {} was not set.", port);
+ }
+ if (loopbackCase.getIpAddress() != null && loopbackCase.getIpPrefix() != null) {
+ loopbackCase.setBvi(true);
+ LOG.trace("Creating loopback BVI interface: {} for VPP router port: {}.", loopbackCase, port);
+ }
+
+ } else {
+ LOG.warn("IpAddress for loopback port: {} was not set.", port);
+ }
+ return loopbackCase.build();
+ }
+
+ /**
+ * If Qrouter (L3 Agent) is in use, any of Openstack neutron routers is not going be mapped
+ * to ODL neutron.
+ */
+ private boolean isValidQRouterPort(Port port) {
+ Optional<Router> optRouter = getRouterOptional(port);
+ return !optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER) && port.getMacAddress() != null;
+ }
+
+ private boolean isValidVppRouterPort(Port port) {
+ Optional<Router> optRouter = getRouterOptional(port);
+ return optRouter.isPresent() && port.getDeviceOwner().contains(ROUTER_OWNER) && port.getMacAddress() != null;
+ }
+
+ private Optional<Router> getRouterOptional(Port port) {
+ if (Strings.isNullOrEmpty(port.getDeviceId())) {
+ return Optional.absent();
+ }
+ RouterKey routerKey = null;
+ try {
+ routerKey = new RouterKey(new Uuid(port.getDeviceId()));
+ } catch (IllegalArgumentException e) {
+ // port.getDeviceId() may not match Uuid.PATTERN_CONSTANTS
+ return Optional.absent();
+ }
+ InstanceIdentifier<Router> routerIid =
+ InstanceIdentifier.builder(Neutron.class).child(Routers.class).child(Router.class, routerKey).build();
+ Optional<Router> optRouter = syncedChain.readFromDs(LogicalDatastoreType.CONFIGURATION, routerIid);
+ return optRouter;
+ }
+
+ public static Optional<FixedIps> resolveFirstFixedIps(Port port) {
+ List<FixedIps> fixedIps = port.getFixedIps();
+ if (fixedIps != null && !fixedIps.isEmpty()) {
+ return Optional.of(fixedIps.get(0));
+ }
+ return Optional.absent();
+ }
+
+ private String createPortName(Uuid portUuid) {
+ String tapPortName;
+ String uuid = portUuid.getValue();
+ if (uuid != null && uuid.length() >= 12) {
+ tapPortName = TAP_PORT_NAME_PREFIX + uuid.substring(0, 11);
+ } else {
+ tapPortName = TAP_PORT_NAME_PREFIX + uuid;
+ }
+ return tapPortName;
+ }
+
+ private String createQRouterPortName(Uuid portUuid) {
+ String tapPortName;
+ String uuid = portUuid.getValue();
+ if (uuid != null && uuid.length() >= 12) {
+ tapPortName = RT_PORT_NAME_PREFIX + uuid.substring(0, 11);
+ } else {
+ tapPortName = RT_PORT_NAME_PREFIX + uuid;
+ }
+ return tapPortName;
+ }
+
+ private InstanceIdentifier<VppEndpoint> createVppEndpointIid(VppEndpointKey vppEpKey) {
+ return InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEpKey).build();
+ }
+
+ private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(Uuid uuid) {
+ return createBaseEpByPortIid(new UniqueId(uuid.getValue()));
+ }
+
+ private InstanceIdentifier<BaseEndpointByPort> createBaseEpByPortIid(UniqueId uuid) {
+ return InstanceIdentifier.builder(Mappings.class)
+ .child(GbpByNeutronMappings.class)
+ .child(BaseEndpointsByPorts.class)
+ .child(BaseEndpointByPort.class, new BaseEndpointByPortKey(uuid))
+ .build();
+ }
+
+ InstanceIdentifier<Port> createWildcartedPortIid() {
+ return portsIid().child(Port.class).build();
+ }
+
+ private InstanceIdentifier<Port> createPortIid(UniqueId uuid) {
+ return portsIid().child(Port.class, new PortKey(new Uuid(uuid.getValue()))).build();
+ }
+
+ private InstanceIdentifierBuilder<Ports> portsIid() {
+ return InstanceIdentifier.builder(Neutron.class).child(Ports.class);
+ }
+
+ @Override
+ public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,
+ Throwable cause) {
+ LOG.error("Transaction chain failed. {} \nTransaction which caused the chain to fail {}", cause.getMessage(),
+ transaction, cause);
+ syncedChain.closeChain();
+ this.syncedChain = new SyncedChain(Preconditions.checkNotNull(dataBroker.createTransactionChain(this)));
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+ LOG.trace("Transaction chain was successful. {}", chain);
+ }
+
+ private <T> List<T> nullToEmpty(@Nullable List<T> list) {
+ return list == null ? Collections.emptyList() : list;
+ }
+}