fix for BUG_7341
[unimgr.git] / netvirt / src / main / java / org / opendaylight / unimgr / mef / netvirt / IpvcListener.java
1 /*
2  * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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
9 package org.opendaylight.unimgr.mef.netvirt;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
17 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
18 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
21 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.subnets.Subnet;
22 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.subnets.SubnetBuilder;
23 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.ip.unis.IpUni;
24 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.IpvcVpn;
25 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.ipvc.choice.Ipvc;
26 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.ipvc.choice.ipvc.VpnElans;
27 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.ipvc.choice.ipvc.unis.Uni;
28 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
36 import org.opendaylight.yangtools.concepts.ListenerRegistration;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public class IpvcListener extends UnimgrDataTreeChangeListener<Ipvc> {
42     private static final Logger Log = LoggerFactory.getLogger(IpvcListener.class);
43     private final IUniPortManager uniPortManager;
44     private final ISubnetManager subnetManager;
45     private final UniQosManager uniQosManager;
46     private ListenerRegistration<IpvcListener> ipvcListenerRegistration;
47
48     public IpvcListener(final DataBroker dataBroker, final IUniPortManager uniPortManager,
49             final ISubnetManager subnetManager, final UniQosManager uniQosManager) {
50         super(dataBroker);
51         this.uniPortManager = uniPortManager;
52         this.subnetManager = subnetManager;
53         this.uniQosManager = uniQosManager;
54         registerListener();
55     }
56
57     public void registerListener() {
58         try {
59             final DataTreeIdentifier<Ipvc> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
60                     MefServicesUtils.getIpvcsInstanceIdentifier());
61             ipvcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
62             Log.info("IpvcDataTreeChangeListener created and registered");
63         } catch (final Exception e) {
64             Log.error("Ipvc DataChange listener registration failed !", e);
65             throw new IllegalStateException("Ipvc registration Listener failed.", e);
66         }
67     }
68
69     @Override
70     public void close() throws Exception {
71         ipvcListenerRegistration.close();
72     }
73
74     @Override
75     public void add(DataTreeModification<Ipvc> newDataObject) {
76         if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
77             Log.info("ipvc {} created", newDataObject.getRootNode().getIdentifier());
78             addIpvc(newDataObject);
79         }
80     }
81
82     @Override
83     public void remove(DataTreeModification<Ipvc> removedDataObject) {
84         if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
85             Log.info("ipvc {} deleted", removedDataObject.getRootNode().getIdentifier());
86             removeIpvc(removedDataObject);
87         }
88     }
89
90     @Override
91     public void update(DataTreeModification<Ipvc> modifiedDataObject) {
92         if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
93             Log.info("ipvc {} updated", modifiedDataObject.getRootNode().getIdentifier());
94             updateIpvc(modifiedDataObject);
95         }
96     }
97
98     private void addIpvc(DataTreeModification<Ipvc> newDataObject) {
99         try {
100             Ipvc ipvc = newDataObject.getRootNode().getDataAfter();
101             String instanceName = ipvc.getIpvcId().getValue();
102             final String vpnName = NetvirtVpnUtils.getUUidFromString(instanceName);
103             InstanceIdentifier<Ipvc> ipvcId = newDataObject.getRootPath().getRootIdentifier();
104             List<Uni> unis = new ArrayList<>();
105             String rd = null;
106             synchronized (vpnName.intern()) {
107                 WriteTransaction tx = MdsalUtils.createTransaction(dataBroker);
108                 Log.info("Adding vpn instance: " + instanceName);
109                 NetvirtVpnUtils.createVpnInstance(vpnName, tx);
110                 MefServicesUtils.addOperIpvcVpnElan(ipvcId, vpnName, tx);
111                 MdsalUtils.commitTransaction(tx);
112
113                 InstanceIdentifier<VpnInstance> vpnId = NetvirtVpnUtils.getVpnInstanceToVpnIdIdentifier(vpnName);
114                 DataWaitListener<VpnInstance> vpnInstanceWaiter = new DataWaitListener<VpnInstance>(dataBroker, vpnId,
115                         5, LogicalDatastoreType.CONFIGURATION, vpn -> vpn.getVrfId());
116                 if (!vpnInstanceWaiter.waitForData()) {
117                     String errorMessage = String.format("Fail to wait for vrfId for vpn %s", vpnName);
118                     Log.error(errorMessage);
119                     throw new UnsupportedOperationException(errorMessage);
120                 }
121                 rd = (String) vpnInstanceWaiter.getData();
122             }
123
124             if (ipvc.getUnis() != null && ipvc.getUnis() != null) {
125                 unis = ipvc.getUnis().getUni();
126             }
127             Log.info("Number of UNI's: " + unis.size());
128
129             // Create elan/vpn interfaces
130             for (Uni uni : unis) {
131                 createInterfaces(vpnName, uni, ipvcId, rd);
132             }
133
134             createUnis(ipvcId, unis);
135         } catch (final Exception e) {
136             Log.error("Add ipvc failed !", e);
137         }
138     }
139
140     private void removeIpvc(DataTreeModification<Ipvc> removedDataObject) {
141         try {
142             Ipvc ipvc = removedDataObject.getRootNode().getDataBefore();
143             InstanceIdentifier<Ipvc> ipvcId = removedDataObject.getRootPath().getRootIdentifier();
144             IpvcVpn operIpvcVpn = MefServicesUtils.getOperIpvcVpn(dataBroker, ipvcId);
145             if (operIpvcVpn == null) {
146                 Log.error("Ipvc {} hasn't been created as required", ipvc.getIpvcId());
147                 return;
148             }
149             String vpnName = operIpvcVpn.getVpnId();
150
151             synchronized (vpnName.intern()) {
152                 // remove elan/vpn interfaces
153                 // must be in different transactios
154                 WriteTransaction tx = MdsalUtils.createTransaction(dataBroker);
155                 removeUnis(ipvcId, operIpvcVpn, ipvc.getUnis().getUni(), tx);
156                 MdsalUtils.commitTransaction(tx);
157                 // Let to work for listeners
158                 // TODO : change to listener
159                 NetvirtUtils.safeSleep();
160
161                 WriteTransaction txvpn = MdsalUtils.createTransaction(dataBroker);
162                 NetvirtVpnUtils.removeVpnInstance(operIpvcVpn.getVpnId(), txvpn);
163                 MefServicesUtils.removeOperIpvcVpn(ipvcId, txvpn);
164                 MdsalUtils.commitTransaction(txvpn);
165             }
166         } catch (final Exception e) {
167             Log.error("Remove ipvc failed !", e);
168         }
169     }
170
171     private void removeUnis(InstanceIdentifier<Ipvc> ipvcId, IpvcVpn operIpvcVpn, List<Uni> uniToRemove,
172             WriteTransaction tx) {
173         if (uniToRemove == null) {
174             Log.trace("No UNI's to remove");
175         }
176         for (Uni uni : uniToRemove) {
177             Identifier45 uniId = uni.getUniId();
178             Identifier45 ipUniId = uni.getIpUniId();
179             IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uniId, ipUniId, LogicalDatastoreType.CONFIGURATION);
180             if (ipUni == null) {
181                 String errorMessage = String.format("Couldn't find ipuni %s for uni %s", ipUniId, uniId);
182                 Log.error(errorMessage);
183                 throw new UnsupportedOperationException(errorMessage);
184             }
185
186             removeDirectSubnet(uni, ipUni);
187             subnetManager.unAssignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId);
188             removeInterfaces(ipvcId, operIpvcVpn, uni, ipUni, tx);
189         }
190         updateQos(uniToRemove);
191     }
192
193     private void createUnis(InstanceIdentifier<Ipvc> ipvcId, List<Uni> uniToCreate) {
194         for (Uni uni : uniToCreate) {
195             IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uni.getUniId(), uni.getIpUniId(),
196                     LogicalDatastoreType.CONFIGURATION);
197             createDirectSubnet(uni, ipUni);
198             subnetManager.assignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId);
199         }
200         updateQos(uniToCreate);
201     }
202
203     private void updateIpvc(DataTreeModification<Ipvc> modifiedDataObject) {
204         try {
205             Ipvc origIpvc = modifiedDataObject.getRootNode().getDataBefore();
206             Ipvc updateIpvc = modifiedDataObject.getRootNode().getDataAfter();
207             InstanceIdentifier<Ipvc> ipvcId = modifiedDataObject.getRootPath().getRootIdentifier();
208             IpvcVpn operIpvcVpn = MefServicesUtils.getOperIpvcVpn(dataBroker, ipvcId);
209             if (operIpvcVpn == null) {
210                 Log.error("Ipvc {} hasn't been created as required", origIpvc.getIpvcId());
211                 return;
212             }
213             String vpnName = operIpvcVpn.getVpnId();
214             InstanceIdentifier<VpnInstance> vpnId = NetvirtVpnUtils.getVpnInstanceToVpnIdIdentifier(vpnName);
215             @SuppressWarnings("resource")
216             DataWaitListener<VpnInstance> vpnInstanceWaiter = new DataWaitListener<VpnInstance>(dataBroker, vpnId, 5,
217                     LogicalDatastoreType.CONFIGURATION, vpn -> vpn.getVrfId());
218             if (!vpnInstanceWaiter.waitForData()) {
219                 String errorMessage = String.format("Fail to wait for vrfId for vpn %s", vpnName);
220                 Log.error(errorMessage);
221                 throw new UnsupportedOperationException(errorMessage);
222             }
223             String rd = (String) vpnInstanceWaiter.getData();
224
225             List<Uni> originalUni = origIpvc.getUnis() != null && origIpvc.getUnis().getUni() != null
226                     ? origIpvc.getUnis().getUni() : Collections.emptyList();
227             List<Uni> updateUni = updateIpvc.getUnis() != null && updateIpvc.getUnis().getUni() != null
228                     ? updateIpvc.getUnis().getUni() : Collections.emptyList();
229
230             synchronized (vpnName.intern()) {
231                 WriteTransaction txRemove = MdsalUtils.createTransaction(dataBroker);
232                 List<Uni> uniToRemove = new ArrayList<>(originalUni);
233                 uniToRemove.removeAll(updateUni);
234                 removeUnis(ipvcId, operIpvcVpn, uniToRemove, txRemove);
235                 MdsalUtils.commitTransaction(txRemove);
236
237                 List<Uni> uniToCreate = new ArrayList<>(updateUni);
238                 uniToCreate.removeAll(originalUni);
239                 for (Uni uni : uniToCreate) {
240                     createInterfaces(vpnName, uni, ipvcId, rd);
241                 }
242                 createUnis(ipvcId, uniToCreate);
243             }
244
245         } catch (final Exception e) {
246             Log.error("Update ipvc failed !", e);
247         }
248     }
249
250     private void createInterfaces(String vpnName, Uni uniInService, InstanceIdentifier<Ipvc> ipvcId, String rd) {
251
252         WriteTransaction tx = MdsalUtils.createTransaction(dataBroker);
253         String uniId = uniInService.getUniId().getValue();
254         String ipUniId = uniInService.getIpUniId().getValue();
255         org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni uni = MefInterfaceUtils
256                 .getUni(dataBroker, uniId, LogicalDatastoreType.OPERATIONAL);
257         if (uni == null) {
258             String errorMessage = String.format("Couldn't find uni %s for ipvc", uniId);
259             Log.error(errorMessage);
260             throw new UnsupportedOperationException(errorMessage);
261         }
262         IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uniInService.getUniId(), uniInService.getIpUniId(),
263                 LogicalDatastoreType.CONFIGURATION);
264         if (ipUni == null) {
265             String errorMessage = String.format("Couldn't find ipuni %s for uni %s", ipUniId, uniId);
266             Log.error(errorMessage);
267             throw new UnsupportedOperationException(errorMessage);
268         }
269
270         String interfaceName = null;
271         synchronized (vpnName.intern()) {
272             Long vlan = ipUni.getVlan() != null ? Long.valueOf(ipUni.getVlan().getValue()) : null;
273             String elanName = NetvirtVpnUtils.getElanNameForVpnPort(uniId, ipUniId);
274
275             String srcIpAddressStr = NetvirtVpnUtils
276                     .getIpAddressFromPrefix(NetvirtVpnUtils.ipPrefixToString(ipUni.getIpAddress()));
277             IpAddress ipAddress = new IpAddress(srcIpAddressStr.toCharArray());
278
279             interfaceName = createElanInterface(vpnName, ipvcId, uniId, elanName, vlan, ipAddress, tx,
280                     ipUni.getSegmentationId());
281             uniQosManager.mapUniPortBandwidthLimits(uniId, interfaceName, uniInService.getIngressBwProfile());
282             createVpnInterface(vpnName, uni, ipUni, interfaceName, elanName, tx);
283             MefServicesUtils.addOperIpvcVpnElan(ipvcId, vpnName, uniInService.getUniId(), uniInService.getIpUniId(),
284                     elanName, interfaceName, null, tx);
285             MdsalUtils.commitTransaction(tx);
286         }
287
288         InstanceIdentifier<VpnInstanceOpDataEntry> vpnId = NetvirtVpnUtils.getVpnInstanceOpDataIdentifier(rd);
289         @SuppressWarnings("resource")
290         DataWaitListener<VpnInstanceOpDataEntry> vpnInstanceWaiter = new DataWaitListener<VpnInstanceOpDataEntry>(
291                 dataBroker, vpnId, 5, LogicalDatastoreType.OPERATIONAL, vpn -> vpn.getVpnToDpnList());
292         if (!vpnInstanceWaiter.waitForData()) {
293             String errorMessage = String.format("Fail to wait for vpn to dpn list %s", vpnName);
294             Log.error(errorMessage);
295             throw new UnsupportedOperationException(errorMessage);
296         }
297
298         NetvirtVpnUtils.createVpnPortFixedIp(dataBroker, vpnName, interfaceName, ipUni.getIpAddress(),
299                 uni.getMacAddress());
300     }
301
302     private String createElanInterface(String vpnName, InstanceIdentifier<Ipvc> ipvcId, String uniId, String elanName,
303             Long vlan, IpAddress ipAddress, WriteTransaction tx, Long segmentationId) {
304         Log.info("Adding elan instance: " + elanName);
305         NetvirtUtils.updateElanInstance(elanName, tx, segmentationId);
306         NetvirtVpnUtils.registerDirectSubnetForVpn(dataBroker, new Uuid(elanName), ipAddress);
307
308         Log.info("Added trunk interface for uni {} vlan: {}", uniId, vlan);
309         if (vlan != null) {
310             uniPortManager.addCeVlan(uniId, vlan);
311         }
312         String interfaceName = uniPortManager.getUniVlanInterface(uniId, vlan);
313         if (interfaceName == null) {
314             String errorMessage = String.format("Couldn't create  uni %s vlan interface %s", uniId, vlan);
315             Log.error(errorMessage);
316             throw new UnsupportedOperationException(errorMessage);
317         }
318
319         Log.info("Adding elan interface: " + interfaceName);
320         NetvirtUtils.createElanInterface(elanName, interfaceName, EtreeInterfaceType.Root, false, tx);
321         return interfaceName;
322     }
323
324     private void createVpnInterface(String vpnName,
325             org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni uni,
326             IpUni ipUni, String interfaceName, String elanName, WriteTransaction tx) {
327
328         Log.info("Adding vpn interface: " + interfaceName);
329
330         NetvirtVpnUtils.createUpdateVpnInterface(vpnName, interfaceName, ipUni.getIpAddress(),
331                 uni.getMacAddress().getValue(), true, null, elanName, tx);
332
333         Log.info("Finished working on vpn instance {} interface () ", vpnName, interfaceName);
334     }
335
336     private void createDirectSubnet(Uni uni, IpUni ipUni) {
337         IpPrefix uniIpPrefix = ipUni.getIpAddress();
338         String subnetIp = NetvirtVpnUtils.getSubnetFromPrefix(uniIpPrefix);
339         IpPrefix subnetPrefix = new IpPrefix(new Ipv4Prefix(subnetIp));
340         InstanceIdentifier<Subnet> path = MefInterfaceUtils.getSubnetInstanceIdentifier(uni.getUniId(),
341                 ipUni.getIpUniId(), subnetPrefix);
342         SubnetBuilder subnet = new SubnetBuilder();
343         subnet.setUniId(uni.getUniId());
344         subnet.setIpUniId(ipUni.getIpUniId());
345         subnet.setSubnet(subnetPrefix);
346         MdsalUtils.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, path, subnet.build());
347     }
348
349     private void removeInterfaces(InstanceIdentifier<Ipvc> ipvcId, IpvcVpn ipvcVpn, Uni uniInService, IpUni ipUni,
350             WriteTransaction tx) {
351         String uniId = uniInService.getUniId().getValue();
352         org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.Uni uni = MefInterfaceUtils
353                 .getUni(dataBroker, uniId, LogicalDatastoreType.OPERATIONAL);
354         if (uni == null) {
355             String errorMessage = String.format("Couldn't find uni %s for ipvc", uniId);
356             Log.error(errorMessage);
357             throw new UnsupportedOperationException(errorMessage);
358         }
359
360         String vpnName = ipvcVpn.getVpnId();
361         VpnElans vpnElans = MefServicesUtils.findVpnElanForNetwork(new Identifier45(uniId), ipUni.getIpUniId(),
362                 ipvcVpn);
363         if (vpnElans == null) {
364             Log.error("Trying to remome non-operational vpn/elan for Uni {} Ip-UNi {}", uniId, ipUni.getIpUniId());
365         }
366
367         NetvirtVpnUtils.removeVpnInterfaceAdjacencies(dataBroker, vpnName, vpnElans.getElanPort());
368         // TODO : change to listener
369         NetvirtUtils.safeSleep();
370         uniQosManager.unMapUniPortBandwidthLimits(uniId, vpnElans.getElanPort());
371         removeElan(vpnElans, uniId, ipUni, tx);
372         // record Uni bw limits
373         removeVpnInterface(vpnName, vpnElans, uniId, ipUni, tx);
374         MefServicesUtils.removeOperIpvcElan(dataBroker, ipvcId, ipvcVpn.getVpnId(), uniInService.getUniId(),
375                 uniInService.getIpUniId(), vpnElans.getElanId(), vpnElans.getElanPort());
376     }
377
378     private void removeElan(VpnElans vpnElans, String uniId, IpUni ipUni, WriteTransaction tx) {
379         Long vlan = ipUni.getVlan() != null ? Long.valueOf(ipUni.getVlan().getValue()) : 0;
380         Log.info("Removing trunk interface for uni {} vlan: {}", uniId, vlan);
381         uniPortManager.removeCeVlan(uniId, vlan);
382
383         String elanName = vpnElans.getElanId();
384         String interfaceName = vpnElans.getElanPort();
385
386         Log.info("Removing elan instance {} and interface {}: ", elanName, interfaceName);
387         NetvirtVpnUtils.unregisterDirectSubnetForVpn(dataBroker, new Uuid(elanName));
388         NetvirtUtils.deleteElanInterface(interfaceName, tx);
389         NetvirtUtils.deleteElanInstance(elanName, tx);
390     }
391
392     private void removeVpnInterface(String vpnName, VpnElans vpnElans, String uniId, IpUni ipUni, WriteTransaction tx) {
393         String interfaceName = vpnElans.getElanPort();
394         Log.info("Removing vpn interface: " + interfaceName);
395         NetvirtVpnUtils.removeVpnInterface(interfaceName, tx);
396         NetvirtVpnUtils.removeVpnPortFixedIp(vpnName, ipUni.getIpAddress(), tx);
397         Log.info("Finished working on vpn instance: " + vpnName);
398     }
399
400     private void removeDirectSubnet(Uni uni, IpUni ipUni) {
401         IpPrefix uniIpPrefix = ipUni.getIpAddress();
402         String subnetIp = NetvirtVpnUtils.getSubnetFromPrefix(uniIpPrefix);
403         IpPrefix subnetPrefix = new IpPrefix(new Ipv4Prefix(subnetIp));
404         InstanceIdentifier<Subnet> path = MefInterfaceUtils.getSubnetInstanceIdentifier(uni.getUniId(),
405                 ipUni.getIpUniId(), subnetPrefix);
406         MdsalUtils.delete(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
407     }
408
409     private void updateQos(List<Uni> uniToUpdate) {
410         uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId()));
411     }
412 }