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