/* * Copyright (c) 2015 - 2016 HPE 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.netvirt.neutronvpn; import com.google.common.base.Optional; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.genius.mdsalutil.MDSALDataStoreUtils; import org.opendaylight.genius.mdsalutil.MDSALUtil; import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils; import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; 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.interfaces.rev140508.Interfaces; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; 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.genius.interfacemanager.meta.rev160406.BridgeRefInfo; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZonesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVxlan; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.Networks; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.NetworkKey; 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.rev150712.Neutron; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransportZoneNotificationUtil { private static final Logger LOG = LoggerFactory.getLogger(TransportZoneNotificationUtil.class); private static final String OF_URI_SEPARATOR = ":"; private static final String TUNNEL_PORT = "tunnel_port"; private static final String LOCAL_IP = "local_ip"; private static final String ALL_SUBNETS = "0.0.0.0/0"; private DataBroker dataBroker; private NeutronvpnManager nvManager; private MdsalUtils mdsalUtils; private SouthboundUtils southBoundUtils; public TransportZoneNotificationUtil(DataBroker dbx, NeutronvpnManager nvManager) { this.dataBroker = dbx; this.nvManager = nvManager; this.mdsalUtils = new MdsalUtils(dbx); southBoundUtils = new SouthboundUtils(mdsalUtils); } /** * Update/add TransportZone for bridheEntryRef change.
* for any update on bridge entry we are looking for all the routers which are affected and try to recreate * its TZ * @param entry - the BridgeEntryRef that was updated */ public void updateTrasportZone(BridgeRefEntry entry) { BigInteger dpid = entry.getDpid(); Set allRouterDpnList = NeutronvpnUtils.getAllRouterDpnList(dataBroker, dpid); for (RouterDpnList routerDpnList : allRouterDpnList) { updateTrasportZone(routerDpnList); } } /** * Update/add TransportZone for interface State inter.
* If Transport zone for given Network doesn't exist, then it will be added.
* If the TEP of the port's node exists in the TZ, it will not be added. * @param inter - the interface to update */ public void updateTrasportZone(Interface inter) { List ports = getPortsFromInterface(inter); //supports VPN aware VMs (multiple ports for one interface) for (Port port : ports) { try { if (!checkIfVxlanNetwork(port)) { continue; } String subnetIp = getSubnetIPFromPort(port); BigInteger dpnId = getDpnIdFromInterfaceState(inter); InstanceIdentifier inst = InstanceIdentifier.create(TransportZones.class) .child(TransportZone.class, new TransportZoneKey(port.getNetworkId().getValue())); TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst); if (zone == null) { zone = createZone(subnetIp, port.getNetworkId().getValue()); } if (addVtep(zone, subnetIp, dpnId) > 0) { addTransportZone(zone, inter.getName()); } } catch (Exception e) { LOG.warn("failed to add tunnels on interface added to subnet {} due to {}", inter, e.getMessage()); } } } public void updateTrasportZone(RouterDpnList routerDpnList) { try{ InstanceIdentifier inst = InstanceIdentifier.create(TransportZones.class). child(TransportZone.class, new TransportZoneKey(routerDpnList.getRouterId())); TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst); String subnetIp = ALL_SUBNETS; if (zone == null) { zone = createZone(subnetIp, routerDpnList.getRouterId()); } int addedTeps = 0; for (DpnVpninterfacesList dpnVpninterfacesList : routerDpnList.getDpnVpninterfacesList()) { BigInteger dpnId = dpnVpninterfacesList.getDpnId(); addedTeps += addVtep(zone, subnetIp, dpnId); } if (addedTeps > 0) { addTransportZone(zone, "router " + routerDpnList.getRouterId()); } } catch (Exception e) { LOG.warn("failed to add tunnels on router added of routerDpnList {} due to {}", routerDpnList, e.getMessage()); } } public boolean isAutoTunnelConfigEnabled() { Optional nvsConfig = MDSALDataStoreUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier .create(NeutronvpnConfig.class)); Boolean useTZ = true; if (nvsConfig.isPresent()) { useTZ = nvsConfig.get().isUseTransportZone() == null ? true : nvsConfig.get().isUseTransportZone(); } if (useTZ) { LOG.info("using automatic tunnel configuration"); } else { LOG.info("don't use automatic tunnel configuration"); } return useTZ; } private boolean checkIfVxlanNetwork(Port port) { InstanceIdentifier networkPath = InstanceIdentifier.create(Neutron.class) .child(Networks.class).child(Network.class, new NetworkKey(port.getNetworkId())); Network network = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, networkPath); if (network == null || !NeutronvpnUtils.isNetworkOfType(network, NetworkTypeVxlan.class)) { LOG.debug("port in non-VXLAN network " + port.getName()); return false; } return true; } private BigInteger getDpnIdFromInterfaceState(Interface inter) { String lowerLayerIf = inter.getLowerLayerIf().get(0); NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf); BigInteger dpId = new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId)); return dpId; } private String getSubnetIPFromPort(Port port) throws Exception { FixedIps ip = port.getFixedIps().get(0); if (ip == null) { LOG.error("No fixed ip for port " + port.getName()); throw new Exception("No fixed ip for port" + port.getName()); } Uuid subnetId = ip.getSubnetId(); Subnetmap subnetmap = nvManager.updateSubnetmapNodeWithPorts(subnetId, port.getUuid(), null); String subnetIp = subnetmap.getSubnetIp(); return subnetIp; } /* * takes all Neutron Ports that are related to the given interface state * @param interfaceState - interface state to update * @return - list of ports bound to interface */ private List getPortsFromInterface(Interface interfaceState) { String physPortId = getPortFromInterfaceName(interfaceState.getName()); List portsList = new ArrayList<>(); InstanceIdentifier interPath = InstanceIdentifier.create(Interfaces.class); Interfaces interfaces = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, interPath); if (interfaces == null) { LOG.error("No interfaces in configuration"); return portsList; } List inters = interfaces.getInterface(); // take all interfaces with parent-interface with physPortId name for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces .Interface inter : inters) { ParentRefs parent = inter.getAugmentation(ParentRefs.class); if (parent == null || !physPortId.equals(parent.getParentInterface())) { continue; } String parentInt = inter.getName(); Uuid portUid = new Uuid(parentInt); InstanceIdentifier pathPort = InstanceIdentifier.create(Neutron.class).child(Ports.class) .child(Port.class, new PortKey(portUid)); Port port = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, pathPort); if (port == null) { LOG.debug("got Interface State of non NeutronPort instance " + physPortId); continue; } portsList.add(port); } return portsList; } private String getPortFromInterfaceName(String name) { String[] splitedStr = name.split(OF_URI_SEPARATOR); name = splitedStr.length > 1 ? splitedStr[1] : name; return name; } // TODO: code is used in another places. Should be extracted into utility private String getDpnFromNodeConnectorId(NodeConnectorId portId) { String[] split = portId.getValue().split(OF_URI_SEPARATOR); return split[1]; } private TransportZone createZone(String subnetIp, String zoneName) { TransportZoneBuilder tzb = new TransportZoneBuilder(); tzb.setKey(new TransportZoneKey(zoneName)); tzb.setTunnelType(TunnelTypeVxlan.class); tzb.setZoneName(zoneName); List subnets = new ArrayList<>(); subnets.add(newSubnets(subnetIp)); tzb.setSubnets(subnets); return tzb.build(); } private void addTransportZone(TransportZone zone, String interName) { InstanceIdentifier path = InstanceIdentifier.builder(TransportZones.class).build(); TransportZones zones = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path); if (zones == null) { List zoneList = new ArrayList<>(); zoneList.add(zone); zones = new TransportZonesBuilder().setTransportZone(zoneList).build(); } else { zones.getTransportZone().add(zone); } MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, zones); LOG.info("updating transport zone {} due to {} handling", zone.getZoneName(), interName); } private int addVtep(TransportZone zone, String subnetIp, BigInteger dpnId) throws Exception { Subnets subnets = findSubnets(zone.getSubnets(), subnetIp); for(Vteps existingVtep : subnets.getVteps()){ if (existingVtep.getDpnId().equals(dpnId)) { return 0; } } IpAddress nodeIp = getNodeIP(dpnId); VtepsBuilder vtepsBuilder = new VtepsBuilder(); vtepsBuilder.setDpnId(dpnId); vtepsBuilder.setIpAddress(nodeIp); vtepsBuilder.setPortname(TUNNEL_PORT); subnets.getVteps().add(vtepsBuilder.build()); return 1; } // search for relevant subnets for the given subnetIP, add one if it is necessary private Subnets findSubnets(List subnets, String subnetIp) { for (Subnets subnet : subnets) { IpPrefix subnetPrefix = new IpPrefix(subnetIp.toCharArray()); if (subnet.getPrefix().equals(subnetPrefix)) { return subnet; } } Subnets retSubnet = newSubnets(subnetIp); subnets.add(retSubnet); return retSubnet; } private Subnets newSubnets(String subnetIp) { SubnetsBuilder subnetsBuilder = new SubnetsBuilder(); subnetsBuilder.setDeviceVteps(new ArrayList<>()); subnetsBuilder.setGatewayIp(new IpAddress("0.0.0.0".toCharArray())); subnetsBuilder.setKey(new SubnetsKey(new IpPrefix(subnetIp.toCharArray()))); subnetsBuilder.setVlanId(0); subnetsBuilder.setVteps(new ArrayList()); return subnetsBuilder.build(); } private IpAddress getNodeIP(BigInteger dpId) throws Exception { Node node = getPortsNode(dpId); String localIp = southBoundUtils.getOpenvswitchOtherConfig(node, LOCAL_IP); if (localIp == null) { throw new Exception("missing local_ip key in ovsdb:openvswitch-other-configs in operational" + " network-topology for node: " + node.getNodeId().getValue()); } return new IpAddress(localIp.toCharArray()); } @SuppressWarnings("unchecked") private Node getPortsNode(BigInteger dpnId) throws Exception{ InstanceIdentifier bridgeRefInfoPath = InstanceIdentifier. create(BridgeRefInfo.class).child(BridgeRefEntry.class, new BridgeRefEntryKey(dpnId)); BridgeRefEntry bridgeRefEntry = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, bridgeRefInfoPath); if (bridgeRefEntry == null) { throw new Exception("no bridge ref entry found for dpnId: " + dpnId); } InstanceIdentifier nodeId = ((InstanceIdentifier) bridgeRefEntry .getBridgeReference().getValue()).firstIdentifierOf(Node.class); Node node = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, nodeId); if (node == null) { throw new Exception("missing node for dpnId: " + dpnId); } return node; } }