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