Rate limits on uni port
[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
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);
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                 EvcElan evcElan = getOperEvcElan(evcId);
133                 if (evcElan == null) {
134                     log.error("Evc {} has not been created as required. Nothing to remove", data.getEvcId().getValue());
135                 }
136
137                 String instanceName = evcElan.getElanId();
138
139                 for (Uni uni : uniToRemove) {
140                     removeUniElanInterfaces(evcId, instanceName, uni);
141                 }
142                 updateQos(uniToRemove);
143
144                 log.info("Removing elan instance: " + instanceName);
145                 NetvirtUtils.deleteElanInstance(dataBroker, instanceName);
146                 removeOperEvcElan(evcId);
147             }
148         } catch (final Exception e) {
149             log.error("Remove evc failed !", e);
150         }
151     }
152
153     private void updateEvc(DataTreeModification<Evc> modifiedDataObject) {
154         InstanceIdentifier<Evc> evcId = modifiedDataObject.getRootPath().getRootIdentifier();
155
156         try {
157             Evc original = modifiedDataObject.getRootNode().getDataBefore();
158             Evc update = modifiedDataObject.getRootNode().getDataAfter();
159
160             List<Uni> originalUni = original.getUnis() != null && original.getUnis().getUni() != null
161                     ? original.getUnis().getUni() : Collections.emptyList();
162             List<Uni> updateUni = update.getUnis() != null && update.getUnis().getUni() != null
163                     ? update.getUnis().getUni() : Collections.emptyList();
164
165             synchronized (original.getEvcId().getValue().intern()) {
166
167                 String instanceName = original.getEvcId().getValue();
168                 boolean isEtree = update.getEvcType() == EvcType.RootedMultipoint;
169                 log.info("Updating {} instance: {}", isEtree ? "etree" : "elan", instanceName);
170
171                 // Changed Uni will be deleted / recreated
172                 List<Uni> uniToRemove = new ArrayList<>(originalUni);
173                 uniToRemove.removeAll(updateUni);
174                 for (Uni uni : uniToRemove) {
175                     removeUniElanInterfaces(evcId, instanceName, uni);
176                 }
177                 updateQos(uniToRemove);
178
179                 List<Uni> uniToCreate = new ArrayList<>(updateUni);
180                 uniToCreate.removeAll(originalUni);
181                 for (Uni uni : uniToCreate) {
182                     createUniElanInterfaces(evcId, instanceName, uni, isEtree);
183                 }
184                 updateQos(uniToCreate);
185             }
186         } catch (final Exception e) {
187             log.error("Update evc failed !", e);
188         }
189     }
190
191     private void createUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni, boolean isEtree) {
192         EvcUniRoleType role = uni.getRole();
193         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
194
195         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
196                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
197                         : Collections.emptyList();
198
199         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
200             Long vlan = safeCastVlan(ceVlan.getVid());
201             uniPortManager.addCeVlan(uni.getUniId().getValue(), vlan);
202         }
203
204         if (evcUniCeVlan.isEmpty()) {
205             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
206             if (isOperEvcElanPort(evcId, interfaceName)) {
207                 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
208                         interfaceName);
209                 return;
210             }
211             log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
212             NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
213                     isEtree);
214             uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
215                     uni.getIngressBwProfile());
216             setOperEvcElanPort(evcId, instanceName, interfaceName);
217         } else {
218             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
219                 Long vlan = safeCastVlan(ceVlan.getVid());
220                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
221                 if (isOperEvcElanPort(evcId, interfaceName)) {
222                     log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
223                             interfaceName);
224                     return;
225                 }
226                 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
227                 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
228                         isEtree);
229                 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
230                         uni.getIngressBwProfile());
231                 setOperEvcElanPort(evcId, instanceName, interfaceName);
232             }
233         }
234     }
235
236     private void removeUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni) {
237         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
238
239         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
240                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
241                         : Collections.emptyList();
242
243         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
244             Long vlan = safeCastVlan(ceVlan.getVid());
245             uniPortManager.removeCeVlan(uni.getUniId().getValue(), vlan);
246         }
247
248         if (evcUniCeVlan.isEmpty()) {
249             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
250             if (!isOperEvcElanPort(evcId, interfaceName)) {
251                 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName, 0,
252                         interfaceName);
253                 return;
254             }
255             removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
256         } else {
257             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
258                 Long vlan = safeCastVlan(ceVlan.getVid());
259                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
260                 if (!isOperEvcElanPort(evcId, interfaceName)) {
261                     log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName,
262                             vlan, interfaceName);
263                     return;
264                 }
265                 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
266             }
267         }
268     }
269
270     private void removeElanInterface(InstanceIdentifier<Evc> identifier, String uniId, String interfaceName) {
271         log.info("Removing elan interface: " + interfaceName);
272         uniQosManager.unMapUniPortBandwidthLimits(uniId, interfaceName);
273         NetvirtUtils.deleteElanInterface(dataBroker, interfaceName);
274
275         EvcElan evcElan = getOperEvcElan(identifier);
276         if (evcElan == null) {
277             log.error("Removing non-operational Elan interface {}", interfaceName);
278         }
279
280         deleteOperEvcElanPort(identifier, interfaceName);
281     }
282
283     // Expected from API is Long
284     private Long safeCastVlan(Object vid) {
285         if (!(vid instanceof Long)) {
286             String errorMessage = String.format("vlan id %s cannot be cast to Long", vid);
287             log.error(errorMessage);
288             throw new UnsupportedOperationException(errorMessage);
289         }
290         return (Long) vid;
291     }
292
293     private static EtreeInterfaceType roleToInterfaceType(EvcUniRoleType role) {
294         if (role == EvcUniRoleType.Root) {
295             return EtreeInterfaceType.Root;
296         } else {
297             return EtreeInterfaceType.Leaf;
298         }
299     }
300
301     private EvcElan getOperEvcElan(InstanceIdentifier<Evc> identifier) {
302         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
303         Optional<EvcElan> evcElan = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
304         if (evcElan.isPresent()) {
305             return evcElan.get();
306         } else {
307             return null;
308         }
309     }
310
311     private void removeOperEvcElan(InstanceIdentifier<Evc> identifier) {
312         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
313         MdsalUtils.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
314     }
315
316     private boolean isOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
317         EvcElan evcElan = getOperEvcElan(identifier);
318         if (evcElan == null || evcElan.getElanPorts() == null) {
319             return false;
320         }
321         List<ElanPorts> exPorts = evcElan.getElanPorts();
322         return exPorts.stream().anyMatch(p -> p.getPortId().equals(elanPort));
323     }
324
325     private void setOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanName, String elanPort) {
326         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
327         EvcElan evcElan = getOperEvcElan(identifier);
328         EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder();
329         List<ElanPorts> exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts()
330                 : new ArrayList<>();
331
332         ElanPortsBuilder portB = new ElanPortsBuilder();
333         portB.setPortId(elanPort);
334         exPorts.add(portB.build());
335         evcElanBuilder.setElanId(elanName);
336         evcElanBuilder.setElanPorts(exPorts);
337         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
338     }
339
340     private void deleteOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
341         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
342         EvcElan evcElan = getOperEvcElan(identifier);
343         EvcElanBuilder evcElanBuilder = null;
344         List<ElanPorts> exPorts = Collections.emptyList();
345         if (evcElan != null) {
346             evcElanBuilder = new EvcElanBuilder(evcElan);
347             exPorts = evcElan.getElanPorts() != null ? evcElan.getElanPorts() : Collections.emptyList();
348         } else {
349             Log.error("Deleting non-operational Elan port {}", elanPort);
350             return;
351         }
352         List<ElanPorts> newList = exPorts.stream().filter(p -> !p.getPortId().equals(elanPort))
353                 .collect(Collectors.toList());
354         evcElanBuilder.setElanPorts(newList);
355         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
356     }
357
358     private void updateQos(List<Uni> uniToUpdate) {
359         uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId()));
360     }
361
362 }