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