Bug 7831 : BgpRouter receives unnecessary events
[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.InterfacesState;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.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.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig;
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.Network;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 public class TransportZoneNotificationUtil {
54
55     private static final Logger LOG = LoggerFactory.getLogger(TransportZoneNotificationUtil.class);
56     private static final String OF_URI_SEPARATOR = ":";
57     private static final String TUNNEL_PORT = "tunnel_port";
58     private static final String LOCAL_IP = "local_ip";
59     private static final String ALL_SUBNETS = "0.0.0.0/0";
60     private final DataBroker dataBroker;
61     private final NeutronvpnManager nvManager;
62     private final MdsalUtils mdsalUtils;
63     private final SouthboundUtils southBoundUtils;
64     private final NeutronvpnConfig neutronvpnConfig;
65
66     public TransportZoneNotificationUtil(DataBroker dbx, NeutronvpnManager nvManager) {
67         this.dataBroker = dbx;
68         this.nvManager = nvManager;
69         this.mdsalUtils = new MdsalUtils(dbx);
70         southBoundUtils = new SouthboundUtils(mdsalUtils);
71         this.neutronvpnConfig = nvManager.getNeutronvpnConfig();
72     }
73
74
75     /**
76      * Update/add TransportZone for bridheEntryRef change.<br>
77      * for any update on bridge entry we are looking for all the routers which are affected and try to recreate
78      * its TZ
79      * @param entry - the BridgeEntryRef that was updated
80      */
81     public void updateTransportZone(BridgeRefEntry entry) {
82         BigInteger dpid = entry.getDpid();
83         Set<RouterDpnList> allRouterDpnList = NeutronvpnUtils.getAllRouterDpnList(dataBroker, dpid);
84         for (RouterDpnList routerDpnList : allRouterDpnList) {
85             updateTransportZone(routerDpnList);
86         }
87     }
88
89     /**
90      * Update/add TransportZone for interface config.
91      * If Transport zone for given Network doesn't exist, then it will be added.
92      * If the TEP of the port's node exists in the TZ, it will not be added.
93      * @param iface - the interface to update
94      */
95     // TODO Clean up the exception handling
96     @SuppressWarnings("checkstyle:IllegalCatch")
97     public void updateTransportZone(Interface iface) {
98         try {
99             Uuid portUid = new Uuid(iface.getName());
100             Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portUid);
101             if (port == null) {
102                 LOG.debug("No neutron port found for iface {} portUuid {}, skipping update of transport zone",
103                         portUid, iface.getName());
104                 return;
105             }
106             if (!checkIfVxlanNetwork(port)) {
107                 return;
108             }
109
110             String subnetIp = ALL_SUBNETS;
111
112             ParentRefs parentRefs = iface.getAugmentation(ParentRefs.class);
113             if (parentRefs == null || parentRefs.getParentInterface() == null) {
114                 // If parentRefs are missing, try to find a matching parent and update - this will trigger another DCN
115                 LOG.debug("parentRef is missing for interface {}, skipping update of transport zone");
116                 return;
117             }
118
119             // FIXME Use IInterfaceManager getInterfaceInfoFromConfigDataStore when refactoring into ELAN
120             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
121                     .Interface ifState = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
122                     buildStateInterfaceId(parentRefs.getParentInterface()));
123
124             if (ifState == null) {
125                 LOG.debug("ifState is missing for parentRef {} of interface {}, skipping update of transport zone",
126                         parentRefs.getParentInterface(), iface.getName());
127                 return;
128             }
129
130             BigInteger dpnId = getDpnIdFromInterfaceState(ifState);
131
132
133             InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class)
134                     .child(TransportZone.class, new TransportZoneKey(port.getNetworkId().getValue()));
135             TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst);
136
137             if (zone == null) {
138                 zone = createZone(subnetIp, port.getNetworkId().getValue());
139             }
140
141             if (addVtep(zone, subnetIp, dpnId) > 0) {
142                 addTransportZone(zone, iface.getName());
143             }
144
145         } catch (Exception e) {
146             LOG.warn("failed to add tunnels on interface added to subnet {}. ", iface, e);
147         }
148     }
149
150     // TODO Clean up the exception handling
151     @SuppressWarnings("checkstyle:IllegalCatch")
152     public void updateTransportZone(RouterDpnList routerDpnList) {
153         try {
154             InstanceIdentifier<TransportZone> inst = InstanceIdentifier.create(TransportZones.class)
155                 .child(TransportZone.class, new TransportZoneKey(routerDpnList.getRouterId()));
156             TransportZone zone = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, inst);
157
158             String subnetIp = ALL_SUBNETS;
159
160             int addedTeps = 0;
161             for (DpnVpninterfacesList dpnVpninterfacesList : routerDpnList.getDpnVpninterfacesList()) {
162                 for (RouterInterfaces routerInterfaces: dpnVpninterfacesList.getRouterInterfaces()) {
163                     Uuid portUid = new Uuid(routerInterfaces.getInterface());
164                     Port neutronPort = NeutronvpnUtils.getNeutronPort(dataBroker, portUid);
165                     if (neutronPort != null) {
166                         if (!checkIfVxlanNetwork(neutronPort)) {
167                             continue;
168                         }
169                         if (zone == null) {
170                             zone = createZone(subnetIp, routerDpnList.getRouterId());
171                         }
172                         BigInteger dpnId = dpnVpninterfacesList.getDpnId();
173                         addedTeps += addVtep(zone, subnetIp, dpnId);
174                         break;
175                     }
176                 }
177             }
178             if (addedTeps > 0) {
179                 addTransportZone(zone, "router " + routerDpnList.getRouterId());
180             }
181         } catch (Exception e) {
182             LOG.warn("failed to add tunnels on router added of routerDpnList {}. ", routerDpnList, e);
183         }
184     }
185
186     public boolean isAutoTunnelConfigEnabled() {
187         Boolean useTZ = true;
188         if (neutronvpnConfig != null && neutronvpnConfig.isUseTransportZone() != null) {
189             useTZ = neutronvpnConfig.isUseTransportZone();
190         }
191         LOG.info("isAutoTunnelConfigEnabled: useTz: {}, neutronvpnConfig: {}", useTZ, neutronvpnConfig);
192         return useTZ;
193     }
194
195     private boolean checkIfVxlanNetwork(Port port) {
196         Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, port.getNetworkId());
197         if (network == null || !NeutronvpnUtils.isNetworkOfType(network, NetworkTypeVxlan.class)) {
198             LOG.debug("port in non-VXLAN network " + port.getName());
199             return false;
200         }
201
202         return true;
203     }
204
205
206     private BigInteger getDpnIdFromInterfaceState(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
207                                                           .interfaces.rev140508.interfaces.state.Interface ifState) {
208         String lowerLayerIf = ifState.getLowerLayerIf().get(0);
209         NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
210         BigInteger dpId = new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId));
211         return dpId;
212     }
213
214     // TODO: code is used in another places. Should be extracted into utility
215     private String getDpnFromNodeConnectorId(NodeConnectorId portId) {
216         String[] split = portId.getValue().split(OF_URI_SEPARATOR);
217         return split[1];
218     }
219
220
221
222     private TransportZone createZone(String subnetIp, String zoneName) {
223         TransportZoneBuilder tzb = new TransportZoneBuilder();
224         tzb.setKey(new TransportZoneKey(zoneName));
225         tzb.setTunnelType(TunnelTypeVxlan.class);
226         tzb.setZoneName(zoneName);
227         List<Subnets> subnets = new ArrayList<>();
228         subnets.add(newSubnets(subnetIp));
229         tzb.setSubnets(subnets);
230         return tzb.build();
231     }
232
233
234     private void addTransportZone(TransportZone zone, String interName) {
235         InstanceIdentifier<TransportZones> path = InstanceIdentifier.builder(TransportZones.class).build();
236         TransportZones zones = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path);
237         if (zones == null) {
238             List<TransportZone> zoneList = new ArrayList<>();
239             zoneList.add(zone);
240             zones = new TransportZonesBuilder().setTransportZone(zoneList).build();
241         } else {
242             zones.getTransportZone().add(zone);
243         }
244
245         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, zones);
246         LOG.info("updating transport zone {} due to {} handling", zone.getZoneName(), interName);
247     }
248
249     private int addVtep(TransportZone zone, String subnetIp, BigInteger dpnId) throws Exception {
250
251         Subnets subnets = findSubnets(zone.getSubnets(), subnetIp);
252
253         for (Vteps existingVtep : subnets.getVteps()) {
254             if (existingVtep.getDpnId().equals(dpnId)) {
255                 return 0;
256             }
257         }
258
259         IpAddress nodeIp =  getNodeIP(dpnId);
260
261         VtepsBuilder vtepsBuilder = new VtepsBuilder();
262         vtepsBuilder.setDpnId(dpnId);
263         vtepsBuilder.setIpAddress(nodeIp);
264         vtepsBuilder.setPortname(TUNNEL_PORT);
265         vtepsBuilder.setOptionOfTunnel(neutronvpnConfig.isUseOfTunnels());
266
267         subnets.getVteps().add(vtepsBuilder.build());
268
269         return 1;
270     }
271
272     // search for relevant subnets for the given subnetIP, add one if it is necessary
273     private Subnets findSubnets(List<Subnets> subnets, String subnetIp) {
274         for (Subnets subnet : subnets) {
275             IpPrefix subnetPrefix = new IpPrefix(subnetIp.toCharArray());
276             if (subnet.getPrefix().equals(subnetPrefix)) {
277                 return subnet;
278             }
279         }
280
281         Subnets retSubnet = newSubnets(subnetIp);
282         subnets.add(retSubnet);
283
284         return retSubnet;
285     }
286
287     private Subnets newSubnets(String subnetIp) {
288         SubnetsBuilder subnetsBuilder = new SubnetsBuilder();
289         subnetsBuilder.setDeviceVteps(new ArrayList<>());
290         subnetsBuilder.setGatewayIp(new IpAddress("0.0.0.0".toCharArray()));
291         subnetsBuilder.setKey(new SubnetsKey(new IpPrefix(subnetIp.toCharArray())));
292         subnetsBuilder.setVlanId(0);
293         subnetsBuilder.setVteps(new ArrayList<>());
294         return subnetsBuilder.build();
295     }
296
297     private IpAddress getNodeIP(BigInteger dpId) throws Exception {
298         Node node = getPortsNode(dpId);
299         String localIp = southBoundUtils.getOpenvswitchOtherConfig(node, LOCAL_IP);
300         if (localIp == null) {
301             throw new Exception("missing local_ip key in ovsdb:openvswitch-other-configs in operational"
302                     + " network-topology for node: " + node.getNodeId().getValue());
303         }
304
305         return new IpAddress(localIp.toCharArray());
306     }
307
308     @SuppressWarnings("unchecked")
309     private Node getPortsNode(BigInteger dpnId) throws Exception {
310         InstanceIdentifier<BridgeRefEntry> bridgeRefInfoPath =
311             InstanceIdentifier.create(BridgeRefInfo.class).child(BridgeRefEntry.class, new BridgeRefEntryKey(dpnId));
312         BridgeRefEntry bridgeRefEntry = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, bridgeRefInfoPath);
313         if (bridgeRefEntry == null) {
314             throw new Exception("no bridge ref entry found for dpnId: " + dpnId);
315         }
316
317         InstanceIdentifier<Node> nodeId = ((InstanceIdentifier<OvsdbBridgeAugmentation>) bridgeRefEntry
318                 .getBridgeReference().getValue()).firstIdentifierOf(Node.class);
319         Node node = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, nodeId);
320
321         if (node == null) {
322             throw new Exception("missing node for dpnId: " + dpnId);
323         }
324         return node;
325
326     }
327
328     // FIXME Remove this, duplicated from IfmUtil
329     public static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
330             .rev140508.interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
331         InstanceIdentifier.InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
332                 .interfaces.rev140508.interfaces.state.Interface> idBuilder =
333                 InstanceIdentifier.builder(InterfacesState.class)
334                         .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
335                                         .rev140508.interfaces.state.Interface.class,
336                                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
337                                         .rev140508.interfaces.state.InterfaceKey(interfaceName));
338         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
339                 .rev140508.interfaces.state.Interface> id = idBuilder.build();
340         return id;
341     }
342 }