Support for IPv6 East-West Routing
[netvirt.git] / vpnservice / neutronvpn / neutronvpn-impl / src / main / java / org / opendaylight / netvirt / neutronvpn / TransportZoneNotificationUtil.java
1 /*
2  * Copyright (c) 2015 - 2016 HPE and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.neutronvpn;
9
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.Set;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.genius.mdsalutil.MDSALUtil;
17 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
18 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZonesBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Subnets;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.SubnetsKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.Vteps;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.subnets.VtepsBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVxlan;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.Networks;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.NetworkKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class TransportZoneNotificationUtil {
60
61     private static final Logger LOG = LoggerFactory.getLogger(TransportZoneNotificationUtil.class);
62     private static final String OF_URI_SEPARATOR = ":";
63     private static final String TUNNEL_PORT = "tunnel_port";
64     private static final String LOCAL_IP = "local_ip";
65     private static final String ALL_SUBNETS = "0.0.0.0/0";
66     private final DataBroker dataBroker;
67     private final NeutronvpnManager nvManager;
68     private final MdsalUtils mdsalUtils;
69     private final SouthboundUtils southBoundUtils;
70     private final NeutronvpnConfig neutronvpnConfig;
71
72     public TransportZoneNotificationUtil(DataBroker dbx, NeutronvpnManager nvManager) {
73         this.dataBroker = dbx;
74         this.nvManager = nvManager;
75         this.mdsalUtils = new MdsalUtils(dbx);
76         southBoundUtils = new SouthboundUtils(mdsalUtils);
77         this.neutronvpnConfig = nvManager.getNeutronvpnConfig();
78     }
79
80
81     /**
82      * Update/add TransportZone for bridheEntryRef change.<br>
83      * for any update on bridge entry we are looking for all the routers which are affected and try to recreate
84      * its TZ
85      * @param entry - the BridgeEntryRef that was updated
86      */
87     public void updateTrasportZone(BridgeRefEntry entry) {
88         BigInteger dpid = entry.getDpid();
89         Set<RouterDpnList> allRouterDpnList = NeutronvpnUtils.getAllRouterDpnList(dataBroker, dpid);
90         for (RouterDpnList routerDpnList : allRouterDpnList) {
91             updateTrasportZone(routerDpnList);
92         }
93     }
94
95     /**
96      * Update/add TransportZone for interface State inter.<br>
97      * If Transport zone for given Network doesn't exist, then it will be added.<br>
98      * If the TEP of the port's node exists in the TZ, it will not be added.
99      * @param inter - the interface to update
100      */
101     public void updateTrasportZone(Interface inter) {
102         List<Port> ports = getPortsFromInterface(inter);
103         //supports VPN aware VMs (multiple ports for one interface)
104         for (Port port : ports) {
105             try {
106
107                 if (!checkIfVxlanNetwork(port)) {
108                     continue;
109                 }
110
111                 String subnetIp = ALL_SUBNETS;
112
113                 BigInteger dpnId = getDpnIdFromInterfaceState(inter);
114
115                 InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class)
116                         .child(TransportZone.class, new TransportZoneKey(port.getNetworkId().getValue()));
117                 TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst);
118
119                 if (zone == null) {
120                     zone = createZone(subnetIp, port.getNetworkId().getValue());
121                 }
122
123                 if (addVtep(zone, subnetIp, dpnId) > 0) {
124                     addTransportZone(zone, inter.getName());
125                 }
126
127             } catch (Exception e) {
128                 LOG.warn("failed to add tunnels on interface added to subnet {}. ", inter, e);
129             }
130         }
131     }
132
133     public void updateTrasportZone(RouterDpnList routerDpnList) {
134         try{
135             InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class).
136                     child(TransportZone.class, new TransportZoneKey(routerDpnList.getRouterId()));
137             TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst);
138
139             String subnetIp = ALL_SUBNETS;
140
141             if (zone == null) {
142                 zone = createZone(subnetIp, routerDpnList.getRouterId());
143             }
144             int addedTeps = 0;
145             for (DpnVpninterfacesList dpnVpninterfacesList : routerDpnList.getDpnVpninterfacesList()) {
146                 BigInteger dpnId = dpnVpninterfacesList.getDpnId();
147                 addedTeps += addVtep(zone, subnetIp, dpnId);
148             }
149             if (addedTeps > 0) {
150                 addTransportZone(zone, "router " + routerDpnList.getRouterId());
151             }
152         } catch (Exception e) {
153             LOG.warn("failed to add tunnels on router added of routerDpnList {}. ", routerDpnList, e);
154         }
155     }
156
157     public boolean isAutoTunnelConfigEnabled() {
158         Boolean useTZ = true;
159         if (neutronvpnConfig != null && neutronvpnConfig.isUseTransportZone() != null) {
160             useTZ = neutronvpnConfig.isUseTransportZone();
161         }
162         LOG.info("isAutoTunnelConfigEnabled: useTz: {}, neutronvpnConfig: {}", useTZ, neutronvpnConfig);
163         return useTZ;
164     }
165
166     private boolean checkIfVxlanNetwork(Port port) {
167         InstanceIdentifier<Network> networkPath = InstanceIdentifier.create(Neutron.class)
168                 .child(Networks.class).child(Network.class, new NetworkKey(port.getNetworkId()));
169         Network network = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, networkPath);
170
171         if (network == null || !NeutronvpnUtils.isNetworkOfType(network, NetworkTypeVxlan.class)) {
172             LOG.debug("port in non-VXLAN network " + port.getName());
173             return false;
174         }
175
176         return true;
177     }
178
179
180     private BigInteger getDpnIdFromInterfaceState(Interface inter) {
181         String lowerLayerIf = inter.getLowerLayerIf().get(0);
182         NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
183         BigInteger dpId = new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId));
184         return dpId;
185     }
186
187     /*
188      * takes all Neutron Ports that are related to the given interface state
189      * @param interfaceState - interface state to update
190      * @return - list of ports bound to interface
191      */
192     private List<Port> getPortsFromInterface(Interface interfaceState) {
193         String physPortId = getPortFromInterfaceName(interfaceState.getName());
194         List<Port> portsList = new ArrayList<>();
195
196         InstanceIdentifier<Interfaces> interPath = InstanceIdentifier.create(Interfaces.class);
197         Interfaces interfaces = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, interPath);
198         if (interfaces == null) {
199             LOG.error("No interfaces in configuration");
200             return portsList;
201         }
202         List<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
203             .Interface> inters = interfaces.getInterface();
204
205         // take all interfaces with parent-interface with physPortId name
206         for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
207                 .Interface inter : inters) {
208             ParentRefs parent = inter.getAugmentation(ParentRefs.class);
209             if (parent == null || !physPortId.equals(parent.getParentInterface())) {
210                 continue;
211             }
212             String parentInt = inter.getName();
213             Uuid portUid = new Uuid(parentInt);
214             InstanceIdentifier<Port> pathPort = InstanceIdentifier.create(Neutron.class).child(Ports.class)
215                     .child(Port.class, new PortKey(portUid));
216             Port port = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, pathPort);
217
218             if (port == null) {
219                 LOG.debug("got Interface State of non NeutronPort instance " + physPortId);
220                 continue;
221             }
222
223             portsList.add(port);
224         }
225
226         return portsList;
227     }
228
229
230     private String getPortFromInterfaceName(String name) {
231         String[] splitedStr = name.split(OF_URI_SEPARATOR);
232         name = splitedStr.length > 1 ? splitedStr[1] : name;
233         return name;
234     }
235
236
237     // TODO: code is used in another places. Should be extracted into utility
238     private String getDpnFromNodeConnectorId(NodeConnectorId portId) {
239         String[] split = portId.getValue().split(OF_URI_SEPARATOR);
240         return split[1];
241     }
242
243
244
245     private TransportZone createZone(String subnetIp, String zoneName) {
246         TransportZoneBuilder tzb = new TransportZoneBuilder();
247         tzb.setKey(new TransportZoneKey(zoneName));
248         tzb.setTunnelType(TunnelTypeVxlan.class);
249         tzb.setZoneName(zoneName);
250         List<Subnets> subnets = new ArrayList<>();
251         subnets.add(newSubnets(subnetIp));
252         tzb.setSubnets(subnets);
253         return tzb.build();
254     }
255
256
257     private void addTransportZone(TransportZone zone, String interName) {
258         InstanceIdentifier<TransportZones> path = InstanceIdentifier.builder(TransportZones.class).build();
259         TransportZones zones = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path);
260         if (zones == null) {
261             List<TransportZone> zoneList = new ArrayList<>();
262             zoneList.add(zone);
263             zones = new TransportZonesBuilder().setTransportZone(zoneList).build();
264         } else {
265             zones.getTransportZone().add(zone);
266         }
267
268         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, zones);
269         LOG.info("updating transport zone {} due to {} handling", zone.getZoneName(), interName);
270     }
271
272     private int addVtep(TransportZone zone, String subnetIp, BigInteger dpnId) throws Exception {
273
274         Subnets subnets = findSubnets(zone.getSubnets(), subnetIp);
275
276         for(Vteps existingVtep : subnets.getVteps()){
277             if (existingVtep.getDpnId().equals(dpnId)) {
278                 return 0;
279             }
280         }
281
282         IpAddress nodeIp =  getNodeIP(dpnId);
283
284         VtepsBuilder vtepsBuilder = new VtepsBuilder();
285         vtepsBuilder.setDpnId(dpnId);
286         vtepsBuilder.setIpAddress(nodeIp);
287         vtepsBuilder.setPortname(TUNNEL_PORT);
288
289         subnets.getVteps().add(vtepsBuilder.build());
290
291         return 1;
292     }
293
294     // search for relevant subnets for the given subnetIP, add one if it is necessary
295     private Subnets findSubnets(List<Subnets> subnets, String subnetIp) {
296         for (Subnets subnet : subnets) {
297             IpPrefix subnetPrefix = new IpPrefix(subnetIp.toCharArray());
298             if (subnet.getPrefix().equals(subnetPrefix)) {
299                 return subnet;
300             }
301         }
302
303         Subnets retSubnet = newSubnets(subnetIp);
304         subnets.add(retSubnet);
305
306         return retSubnet;
307     }
308
309     private Subnets newSubnets(String subnetIp) {
310         SubnetsBuilder subnetsBuilder = new SubnetsBuilder();
311         subnetsBuilder.setDeviceVteps(new ArrayList<>());
312         subnetsBuilder.setGatewayIp(new IpAddress("0.0.0.0".toCharArray()));
313         subnetsBuilder.setKey(new SubnetsKey(new IpPrefix(subnetIp.toCharArray())));
314         subnetsBuilder.setVlanId(0);
315         subnetsBuilder.setVteps(new ArrayList<Vteps>());
316         return subnetsBuilder.build();
317     }
318
319     private IpAddress getNodeIP(BigInteger dpId) throws Exception {
320         Node node = getPortsNode(dpId);
321         String localIp = southBoundUtils.getOpenvswitchOtherConfig(node, LOCAL_IP);
322         if (localIp == null) {
323             throw new Exception("missing local_ip key in ovsdb:openvswitch-other-configs in operational"
324                     + " network-topology for node: " + node.getNodeId().getValue());
325         }
326
327         return new IpAddress(localIp.toCharArray());
328     }
329
330     @SuppressWarnings("unchecked")
331     private Node getPortsNode(BigInteger dpnId) throws Exception{
332         InstanceIdentifier<BridgeRefEntry> bridgeRefInfoPath = InstanceIdentifier.
333                 create(BridgeRefInfo.class).child(BridgeRefEntry.class, new BridgeRefEntryKey(dpnId));
334         BridgeRefEntry bridgeRefEntry = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, bridgeRefInfoPath);
335         if (bridgeRefEntry == null) {
336             throw new Exception("no bridge ref entry found for dpnId: " + dpnId);
337         }
338
339         InstanceIdentifier<Node> nodeId = ((InstanceIdentifier<OvsdbBridgeAugmentation>) bridgeRefEntry
340                 .getBridgeReference().getValue()).firstIdentifierOf(Node.class);
341         Node node = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, nodeId);
342
343         if (node == null) {
344             throw new Exception("missing node for dpnId: " + dpnId);
345         }
346         return node;
347
348     }
349 }