adding mac timeout as a configurable parameter to EVC
[unimgr.git] / netvirt / src / main / java / org / opendaylight / unimgr / mef / netvirt / EvcListener.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 import java.util.stream.Collectors;
15
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
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.services.rev150526.mef.services.MefService;
22 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.Evc;
23 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.Uni;
24 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.uni.EvcUniCeVlans;
25 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlan;
26 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElan;
27 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElanBuilder;
28 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.ElanPorts;
29 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.ElanPortsBuilder;
30
31 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcType;
32 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcUniRoleType;
33 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.RetailSvcIdType;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
35 import org.opendaylight.yangtools.concepts.ListenerRegistration;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.common.base.Optional;
41
42 import jline.internal.Log;
43
44 public class EvcListener extends UnimgrDataTreeChangeListener<Evc> implements IUniAwareService {
45
46     private static final Logger log = LoggerFactory.getLogger(EvcListener.class);
47     private ListenerRegistration<EvcListener> evcListenerRegistration;
48     private final IUniPortManager uniPortManager;
49     private final UniQosManager uniQosManager;
50     @SuppressWarnings("unused")
51     private final UniAwareListener uniAwareListener;
52
53     public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager,
54             final UniQosManager uniQosManager) {
55         super(dataBroker);
56         this.uniPortManager = uniPortManager;
57         this.uniQosManager = uniQosManager;
58         this.uniAwareListener = new UniAwareListener(dataBroker, this);
59         registerListener();
60     }
61
62     public void registerListener() {
63         try {
64             final DataTreeIdentifier<Evc> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
65                     MefServicesUtils.getEvcsInstanceIdentifier());
66             evcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
67             log.info("EvcDataTreeChangeListener created and registered");
68         } catch (final Exception e) {
69             log.error("Evc DataChange listener registration failed !", e);
70             throw new IllegalStateException("Evc registration Listener failed.", e);
71         }
72     }
73
74     @Override
75     public void close() throws Exception {
76         evcListenerRegistration.close();
77     }
78
79     @Override
80     public void add(DataTreeModification<Evc> newDataObject) {
81         if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
82             log.info("evc {} created", newDataObject.getRootNode().getIdentifier());
83             addEvc(newDataObject);
84         }
85     }
86
87     @Override
88     public void remove(DataTreeModification<Evc> removedDataObject) {
89         if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
90             log.info("evc {} deleted", removedDataObject.getRootNode().getIdentifier());
91             removeEvc(removedDataObject);
92         }
93     }
94
95     @Override
96     public void update(DataTreeModification<Evc> modifiedDataObject) {
97         if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
98             log.info("evc {} updated", modifiedDataObject.getRootNode().getIdentifier());
99             updateEvc(modifiedDataObject);
100         }
101     }
102
103     @Override
104     public void connectUni(String uniId) {
105         List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
106         allEvcs = (allEvcs != null) ? allEvcs : Collections.emptyList();
107
108         for (RetailSvcIdType evcSerId : allEvcs) {
109             InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
110             Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
111             if (evc == null) {
112                 Log.error("Inconsistent data for svcId {}", evcSerId);
113                 continue;
114             }
115
116             String instanceName = evc.getEvcId().getValue();
117             boolean isEtree = evc.getEvcType() == EvcType.RootedMultipoint;
118
119             List<Uni> toConnect = new ArrayList<>();
120             List<Uni> unis = (evc.getUnis() != null) ? evc.getUnis().getUni() : null;
121             unis = (unis != null) ? unis : Collections.emptyList();
122             for (Uni uni : unis) {
123                 if (uni.getUniId().getValue().equals(uniId)) {
124                     Log.info("Connecting Uni {} to svc id {}", uniId, evcSerId);
125                     toConnect.add(uni);
126                     break;
127                 }
128             }
129
130             EvcElan evcElan = getOperEvcElan(evcId);
131             if (evcElan == null) {
132                 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, evc.getSegmentationId());
133                 evcElan = getOperEvcElan(evcId);
134                 if (evcElan == null) {
135                     log.error("Evc {} has not been created as required. Nothing to reconnect", evcId);
136                     return;
137                 }
138             }
139
140             for (Uni uni : toConnect) {
141                 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
142             }
143             updateQos(toConnect);
144         }
145     }
146
147     @Override
148     public void disconnectUni(String uniId) {
149         List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
150         allEvcs = (allEvcs != null) ? allEvcs : Collections.emptyList();
151
152         for (RetailSvcIdType evcSerId : allEvcs) {
153             InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
154             Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
155             if (evc == null) {
156                 Log.error("Inconsistent data for svcId {}", evcSerId);
157                 continue;
158             }
159
160             String instanceName = evc.getEvcId().getValue();
161             List<Uni> toDisconnect = new ArrayList<>();
162             List<Uni> unis = (evc.getUnis() != null) ? evc.getUnis().getUni() : null;
163             unis = (unis != null) ? unis : Collections.emptyList();
164             for (Uni uni : unis) {
165                 if (uni.getUniId().getValue().equals(uniId)) {
166                     Log.info("Disconnecting Uni {} from svc id {}", uniId, evcSerId);
167                     toDisconnect.add(uni);
168                     break;
169                 }
170             }
171
172             EvcElan evcElan = getOperEvcElan(evcId);
173             if (evcElan == null) {
174                 log.error("Evc {} has not been created as required. Nothing to disconnect", evcId);
175                 return;
176             }
177
178             updateQos(toDisconnect);
179             for (Uni uni : toDisconnect) {
180                 removeUniElanInterfaces(evcId, instanceName, uni);
181             }
182         }
183
184     }
185
186     private void addEvc(DataTreeModification<Evc> newDataObject) {
187         try {
188             Evc data = newDataObject.getRootNode().getDataAfter();
189             String instanceName = data.getEvcId().getValue();
190             boolean isEtree = data.getEvcType() == EvcType.RootedMultipoint;
191             InstanceIdentifier<Evc> evcId = newDataObject.getRootPath().getRootIdentifier();
192
193             synchronized (instanceName.intern()) {
194                 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, data.getSegmentationId(), data.getMacTimeout());
195
196                 // Create interfaces
197                 if (data.getUnis() == null) {
198                     log.info("No UNI's in service {}, exiting", instanceName);
199                     return;
200                 }
201                 for (Uni uni : data.getUnis().getUni()) {
202                     createUniElanInterfaces(evcId, instanceName, uni, isEtree);
203                 }
204                 updateQos(data.getUnis().getUni());
205             }
206         } catch (final Exception e) {
207             log.error("Add evc failed !", e);
208         }
209     }
210
211     private void removeEvc(DataTreeModification<Evc> removedDataObject) {
212         try {
213             Evc data = removedDataObject.getRootNode().getDataBefore();
214             InstanceIdentifier<Evc> evcId = removedDataObject.getRootPath().getRootIdentifier();
215             List<Uni> uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null ? data.getUnis().getUni()
216                     : Collections.emptyList();
217
218             synchronized (data.getEvcId().getValue().intern()) {
219                 updateQos(uniToRemove);
220                 EvcElan evcElan = getOperEvcElan(evcId);
221                 if (evcElan == null) {
222                     log.error("Evc {} has not been created as required. Nothing to remove", data.getEvcId().getValue());
223                     return;
224                 }
225
226                 String instanceName = evcElan.getElanId();
227
228                 for (Uni uni : uniToRemove) {
229                     removeUniElanInterfaces(evcId, instanceName, uni);
230                 }
231
232                 log.info("Removing elan instance: " + instanceName);
233                 NetvirtUtils.deleteElanInstance(dataBroker, instanceName);
234                 removeOperEvcElan(evcId);
235             }
236         } catch (final Exception e) {
237             log.error("Remove evc failed !", e);
238         }
239     }
240
241     private void updateEvc(DataTreeModification<Evc> modifiedDataObject) {
242         InstanceIdentifier<Evc> evcId = modifiedDataObject.getRootPath().getRootIdentifier();
243
244         try {
245             Evc original = modifiedDataObject.getRootNode().getDataBefore();
246             Evc update = modifiedDataObject.getRootNode().getDataAfter();
247
248             List<Uni> originalUni = original.getUnis() != null && original.getUnis().getUni() != null
249                     ? original.getUnis().getUni() : Collections.emptyList();
250             List<Uni> updateUni = update.getUnis() != null && update.getUnis().getUni() != null
251                     ? update.getUnis().getUni() : Collections.emptyList();
252
253             synchronized (original.getEvcId().getValue().intern()) {
254
255                 String instanceName = original.getEvcId().getValue();
256                 boolean isEtree = update.getEvcType() == EvcType.RootedMultipoint;
257                 log.info("Updating {} instance: {}", isEtree ? "etree" : "elan", instanceName);
258
259                 // Changed Uni will be deleted / recreated
260                 List<Uni> uniToRemove = new ArrayList<>(originalUni);
261                 uniToRemove.removeAll(updateUni);
262                 for (Uni uni : uniToRemove) {
263                     removeUniElanInterfaces(evcId, instanceName, uni);
264                 }
265                 updateQos(uniToRemove);
266
267                 List<Uni> uniToCreate = new ArrayList<>(updateUni);
268                 uniToCreate.removeAll(originalUni);
269                 for (Uni uni : uniToCreate) {
270                     createUniElanInterfaces(evcId, instanceName, uni, isEtree);
271                 }
272                 updateQos(uniToCreate);
273             }
274         } catch (final Exception e) {
275             log.error("Update evc failed !", e);
276         }
277     }
278
279     private void createUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni, boolean isEtree) {
280         EvcUniRoleType role = uni.getRole();
281         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
282
283         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
284                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
285                         : Collections.emptyList();
286
287         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
288             Long vlan = safeCastVlan(ceVlan.getVid());
289             uniPortManager.addCeVlan(uni.getUniId().getValue(), vlan);
290         }
291
292         if (evcUniCeVlan.isEmpty()) {
293             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
294             if (interfaceName == null) {
295                 String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ", uni.getUniId(),
296                         0);
297                 Log.error(errorMessage);
298                 throw new UnsupportedOperationException(errorMessage);
299             }
300             if (isOperEvcElanPort(evcId, interfaceName)) {
301                 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
302                         interfaceName);
303                 return;
304             }
305             log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
306             NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
307                     isEtree);
308             uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
309                     uni.getIngressBwProfile());
310             setOperEvcElanPort(evcId, instanceName, interfaceName);
311         } else {
312             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
313                 Long vlan = safeCastVlan(ceVlan.getVid());
314                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
315                 if (interfaceName == null) {
316                     String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ",
317                             uni.getUniId(), 0);
318                     Log.error(errorMessage);
319                     throw new UnsupportedOperationException(errorMessage);
320                 }
321                 if (isOperEvcElanPort(evcId, interfaceName)) {
322                     log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
323                             interfaceName);
324                     return;
325                 }
326                 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
327                 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
328                         isEtree);
329                 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
330                         uni.getIngressBwProfile());
331                 setOperEvcElanPort(evcId, instanceName, interfaceName);
332             }
333         }
334     }
335
336     private void removeUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni) {
337         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
338
339         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
340                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
341                         : Collections.emptyList();
342
343         if (evcUniCeVlan.isEmpty()) {
344             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
345             if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
346                 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName, 0,
347                         interfaceName);
348                 interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), null);
349             }
350             removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
351         } else {
352             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
353                 Long vlan = safeCastVlan(ceVlan.getVid());
354                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
355                 if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
356                     log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName,
357                             vlan, interfaceName);
358                     interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), vlan);
359                 }
360                 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
361             }
362         }
363
364         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
365             Long vlan = safeCastVlan(ceVlan.getVid());
366             uniPortManager.removeCeVlan(uni.getUniId().getValue(), vlan);
367         }
368     }
369
370     private void removeElanInterface(InstanceIdentifier<Evc> identifier, String uniId, String interfaceName) {
371         log.info("Removing elan interface: " + interfaceName);
372         uniQosManager.unMapUniPortBandwidthLimits(uniId, interfaceName);
373         NetvirtUtils.deleteElanInterface(dataBroker, interfaceName);
374
375         EvcElan evcElan = getOperEvcElan(identifier);
376         if (evcElan == null) {
377             log.error("Removing non-operational Elan interface {}", interfaceName);
378         }
379
380         deleteOperEvcElanPort(identifier, interfaceName);
381     }
382
383     // Expected from API is Long
384     private Long safeCastVlan(Object vid) {
385         if (!(vid instanceof Long)) {
386             String errorMessage = String.format("vlan id %s cannot be cast to Long", vid);
387             log.error(errorMessage);
388             throw new UnsupportedOperationException(errorMessage);
389         }
390         return (Long) vid;
391     }
392
393     private static EtreeInterfaceType roleToInterfaceType(EvcUniRoleType role) {
394         if (role == EvcUniRoleType.Root) {
395             return EtreeInterfaceType.Root;
396         } else {
397             return EtreeInterfaceType.Leaf;
398         }
399     }
400
401     private EvcElan getOperEvcElan(InstanceIdentifier<Evc> identifier) {
402         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
403         Optional<EvcElan> evcElan = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
404         if (evcElan.isPresent()) {
405             return evcElan.get();
406         } else {
407             return null;
408         }
409     }
410
411     private void removeOperEvcElan(InstanceIdentifier<Evc> identifier) {
412         final InstanceIdentifier<MefService> serviceId = identifier.firstIdentifierOf(MefService.class);
413         MdsalUtils.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, serviceId);
414     }
415
416     private boolean isOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
417         EvcElan evcElan = getOperEvcElan(identifier);
418         if (evcElan == null || evcElan.getElanPorts() == null) {
419             return false;
420         }
421         List<ElanPorts> exPorts = evcElan.getElanPorts();
422         return exPorts.stream().anyMatch(p -> p.getPortId().equals(elanPort));
423     }
424
425     private void setOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanName, String elanPort) {
426         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
427         EvcElan evcElan = getOperEvcElan(identifier);
428         EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder();
429         List<ElanPorts> exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts()
430                 : new ArrayList<>();
431
432         ElanPortsBuilder portB = new ElanPortsBuilder();
433         portB.setPortId(elanPort);
434         exPorts.add(portB.build());
435         evcElanBuilder.setElanId(elanName);
436         evcElanBuilder.setElanPorts(exPorts);
437         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
438     }
439
440     private void deleteOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
441         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
442         EvcElan evcElan = getOperEvcElan(identifier);
443         EvcElanBuilder evcElanBuilder = null;
444         List<ElanPorts> exPorts = Collections.emptyList();
445         if (evcElan != null) {
446             evcElanBuilder = new EvcElanBuilder(evcElan);
447             exPorts = evcElan.getElanPorts() != null ? evcElan.getElanPorts() : Collections.emptyList();
448         } else {
449             Log.error("Deleting non-operational Elan port {}", elanPort);
450             return;
451         }
452         List<ElanPorts> newList = exPorts.stream().filter(p -> !p.getPortId().equals(elanPort))
453                 .collect(Collectors.toList());
454         evcElanBuilder.setElanPorts(newList);
455         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
456     }
457
458     private void updateQos(List<Uni> uniToUpdate) {
459         uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId()));
460     }
461
462 }