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