Connectivity service synchronous write.
[unimgr.git] / netvirt / src / main / java / org / opendaylight / unimgr / mef / netvirt / UniQosManager.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 com.google.common.base.Optional;
12
13 import java.math.BigInteger;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Future;
19 import java.util.stream.Collectors;
20
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
27 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.MefGlobal;
29 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.Profiles;
30 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.bwp.flows.group.BwpFlow;
31 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.bwp.flows.group.BwpFlowKey;
32 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.profiles.IngressBwpFlows;
33 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link;
34 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentationBuilder;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
52 import org.opendaylight.yangtools.concepts.ListenerRegistration;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.opendaylight.yangtools.yang.common.RpcResult;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 public class UniQosManager extends UnimgrDataTreeChangeListener<BwpFlow> {
59     private static final Logger Log = LoggerFactory.getLogger(UniQosManager.class);
60     private OdlInterfaceRpcService odlInterfaceRpcService;
61     private DataBroker dataBroker;
62     private final Long noLimit = 0l;
63     private final static String noProfile = "";
64     private ListenerRegistration<UniQosManager> bwListenerRegistration;
65
66
67     // key in first map is uniId, key in second map is logical portId
68     private ConcurrentHashMap<String, ConcurrentHashMap<String, BandwidthLimits>> uniPortBandwidthLimits;
69
70     // map of current values per uni
71     private ConcurrentHashMap<String, BandwidthLimits> uniBandwidthLimits;
72
73     private ConcurrentHashMap<String, BigInteger> uniToDpn;
74
75     public UniQosManager(final DataBroker dataBroker, OdlInterfaceRpcService odlInterfaceRpcService) {
76         super(dataBroker);
77
78         this.dataBroker = dataBroker;
79         this.odlInterfaceRpcService = odlInterfaceRpcService;
80         this.uniPortBandwidthLimits = new ConcurrentHashMap<>();
81         this.uniBandwidthLimits = new ConcurrentHashMap<>();
82         this.uniToDpn = new ConcurrentHashMap<>();
83         registerListener();
84     }
85
86     public void registerListener() {
87         try {
88             final DataTreeIdentifier<BwpFlow> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
89                     getBwFlowsInstanceIdentifier());
90             bwListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
91             Log.info("UniQosManager created and registered");
92         } catch (final Exception e) {
93             Log.error("UniQosManager DataChange listener registration failed !", e);
94             throw new IllegalStateException("UniQosManager registration Listener failed.", e);
95         }
96     }
97
98     public synchronized void mapUniPortBandwidthLimits(String uniId, String portId, Identifier45 bwProfile) {
99         Long maxKbps = noLimit;
100         Long maxBurstKb = noLimit;
101         if (bwProfile != null) {
102             Optional<BwpFlow> bwFlowOp = MdsalUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
103                     getBwFlowInstanceIdentifier(bwProfile));
104             if (!bwFlowOp.isPresent()) {
105                 Log.trace("Can't read bw profile {} for Uni {}", bwProfile, uniId);
106             } else {
107                 // Kb per second
108                 maxKbps = bwFlowOp.get().getCir().getValue();
109                 // burst in bytes, ovs requires in Kb
110                 maxBurstKb = bwFlowOp.get().getCbs().getValue() * 8 / 1024;
111                 Log.info("Record rate limits for Uni {} Profile {}", uniId, bwProfile);
112             }
113         }
114
115         mapUniPortBandwidthLimits(uniId, portId, maxKbps, maxBurstKb, replaceNull(bwProfile));
116     }
117
118     private synchronized void mapUniPortBandwidthLimits(String uniId, String portId, Long maxKbps, Long maxBurstKb,
119             String profileName) {
120         Log.info("Record rate limits for Uni {} port {} maxKbps {} maxBurstKb {}", uniId, portId, maxKbps, maxBurstKb);
121         uniPortBandwidthLimits.putIfAbsent(uniId, new ConcurrentHashMap<>());
122         ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
123         uniMap.put(portId, new BandwidthLimits(maxKbps, maxBurstKb, profileName));
124     }
125
126     public void updateUni(Identifier45 uniId, Identifier45 bwProfile) {
127         String bwProfileSafe = replaceNull(bwProfile);
128         Log.info("Update rate limits for Uni {}", uniId.getValue());
129         ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId.getValue());
130         if (uniMap == null) {
131             Log.error("Trying to update limits for non-exsting uni {}", uniId.getValue());
132             return;
133         }
134         for (String portName : uniMap.keySet()) {
135             if (uniMap.get(portName).getProfileName().equals(bwProfileSafe)) {
136                 continue;
137             }
138             if (bwProfile != null) {
139                 mapUniPortBandwidthLimits(uniId.getValue(), portName, new Identifier45(bwProfileSafe));
140             } else {
141                 unMapUniPortBandwidthLimits(uniId.getValue(), portName);
142             }
143         }
144     }
145
146     private void updateProfile(Identifier45 bwProfile) {
147         Log.info("Update rate limits for profile {}", bwProfile);
148         List<String> unisWithProfile = uniBandwidthLimits.entrySet().stream()
149                 .filter(m -> m.getValue().profileName.equals(bwProfile.getValue())).map(m -> m.getKey())
150                 .collect(Collectors.toList());
151
152         for (String uniId : unisWithProfile) {
153             ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
154             uniMap.forEach((k, v) -> {
155                 mapUniPortBandwidthLimits(uniId, k, bwProfile);
156             });
157         }
158
159         for (String uniId : unisWithProfile) {
160             setUniBandwidthLimits(uniId);
161         }
162     }
163
164     public void deleteProfile(Identifier45 bwProfile) {
165         Log.info("Delete rate limits for profile {}", bwProfile);
166         List<String> unisWithProfile = uniBandwidthLimits.entrySet().stream()
167                 .filter(m -> m.getValue().profileName.equals(bwProfile.getValue())).map(m -> m.getKey())
168                 .collect(Collectors.toList());
169
170         for (String uniId : unisWithProfile) {
171             ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
172             uniMap.forEach((k, v) -> {
173                 unMapUniPortBandwidthLimits(uniId, k, bwProfile.getValue());
174             });
175         }
176
177         for (String uniId : unisWithProfile) {
178             setUniBandwidthLimits(uniId);
179         }
180     }
181
182     public synchronized void unMapUniPortBandwidthLimits(String uniId, String portId) {
183         unMapUniPortBandwidthLimits(uniId, portId, noProfile);
184     }
185
186     public synchronized void unMapUniPortBandwidthLimits(String uniId, String portId, String profileTosave) {
187         Log.info("Delete rate limits for Uni {} port {}", uniId, portId);
188         ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
189         if (uniMap == null) {
190             Log.error("Trying to delete limits for non-exsting uni {}", uniId);
191             return;
192         }
193         uniMap.remove(portId);
194         if (uniMap.isEmpty()) {
195             uniMap.put(portId, new BandwidthLimits(noLimit, noLimit, profileTosave));
196         }
197     }
198
199     public void setUniBandwidthLimits(Identifier45 uniIden) {
200         String uniId = uniIden.getValue();
201         setUniBandwidthLimits(uniId);
202     }
203
204     private synchronized void setUniBandwidthLimits(String uniId) {
205         if (!uniPortBandwidthLimits.containsKey(uniId)) {
206             Log.debug("Uni {} doesn't have rate limits configured", uniId);
207             return;
208         }
209         Iterator<String> uniPorts = uniPortBandwidthLimits.get(uniId).keySet().iterator();
210         if (uniPorts == null || !uniPorts.hasNext()) {
211             Log.debug("Uni {} doesn't have rate limits configured", uniId);
212             return;
213         }
214         String logicalPort = uniPorts.next();
215
216         BandwidthLimits newLimits = recalculateLimitsForUni(uniId, uniPortBandwidthLimits.get(uniId));
217         if (newLimits.equals(uniBandwidthLimits.get(uniId))) {
218             Log.debug("Uni {} rate limits has not changed", uniId);
219             return;
220         }
221
222         setPortBandwidthLimits(uniId, logicalPort, newLimits.getMaxKbps(), newLimits.getMaxBurstKb());
223         uniBandwidthLimits.put(uniId, newLimits);
224     }
225
226     private BandwidthLimits recalculateLimitsForUni(String uniId,
227             ConcurrentHashMap<String, BandwidthLimits> uniLimits) {
228         Long sumOfRate = noLimit;
229         Long sumOfBurst = noLimit;
230         String profileName = noProfile;
231         Boolean hasNullRate = false;
232         Boolean hasNullBurst = false;
233
234         if (uniLimits == null || uniLimits.keySet() == null) {
235             return new BandwidthLimits(sumOfRate, sumOfBurst, profileName);
236         }
237
238         for (BandwidthLimits v : uniLimits.values()) {
239             if (v.maxKbps == null) {
240                 hasNullRate = true;
241                 break;
242             }
243             if (v.maxBurstKb == null) {
244                 hasNullBurst = true;
245             }
246             sumOfRate = sumOfRate + v.maxKbps;
247             sumOfBurst = sumOfBurst + v.maxBurstKb;
248             profileName = v.profileName;
249         }
250         if (hasNullRate) {
251             sumOfRate = noLimit;
252             sumOfBurst = noLimit;
253         } else if (hasNullBurst) {
254             sumOfBurst = noLimit;
255         }
256         return new BandwidthLimits(sumOfRate, sumOfBurst, profileName);
257     }
258
259     private void setPortBandwidthLimits(String uniId, String logicalPortId, Long maxKbps, Long maxBurstKb) {
260         Log.info("Setting bandwidth limits {} {} on Port {}", maxKbps, maxBurstKb, logicalPortId);
261
262         BigInteger dpId = BigInteger.ZERO;
263         if (uniToDpn.containsKey(uniId)) {
264             dpId = uniToDpn.get(uniId);
265         } else {
266             dpId = NetvirtUtils.getDpnForInterface(odlInterfaceRpcService, logicalPortId);
267             uniToDpn.put(uniId, dpId);
268         }
269         if (dpId.equals(BigInteger.ZERO)) {
270             Log.error("DPN ID for interface {} not found", logicalPortId);
271             return;
272         }
273
274         OvsdbBridgeRef bridgeRefEntry = getBridgeRefEntryFromOperDS(dpId, dataBroker);
275         Optional<Node> bridgeNode = MDSALUtil.read(LogicalDatastoreType.OPERATIONAL,
276                 bridgeRefEntry.getValue().firstIdentifierOf(Node.class), dataBroker);
277         if (bridgeNode == null) {
278             Log.error("Bridge ref for interface {} not found", logicalPortId);
279             return;
280         }
281
282         String physicalPort = getPhysicalPortForUni(dataBroker, uniId);
283         if (physicalPort == null) {
284             Log.error("Physical port for interface {} not found", logicalPortId);
285             return;
286         }
287
288         TerminationPoint tp = getTerminationPoint(bridgeNode.get(), physicalPort);
289         if (tp == null) {
290             Log.error("Termination point for port {} not found", physicalPort);
291             return;
292         }
293
294         OvsdbTerminationPointAugmentation ovsdbTp = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
295         OvsdbTerminationPointAugmentationBuilder tpAugmentationBuilder = new OvsdbTerminationPointAugmentationBuilder();
296         tpAugmentationBuilder.setName(ovsdbTp.getName());
297         tpAugmentationBuilder.setIngressPolicingRate(maxKbps);
298         tpAugmentationBuilder.setIngressPolicingBurst(maxBurstKb);
299
300         TerminationPointBuilder tpBuilder = new TerminationPointBuilder();
301         tpBuilder.setKey(tp.getKey());
302         tpBuilder.addAugmentation(OvsdbTerminationPointAugmentation.class, tpAugmentationBuilder.build());
303         MdsalUtils.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION,
304                 InstanceIdentifier.create(NetworkTopology.class)
305                         .child(Topology.class, new TopologyKey(SouthboundUtils.OVSDB_TOPOLOGY_ID))
306                         .child(Node.class, bridgeNode.get().getKey())
307                         .child(TerminationPoint.class, new TerminationPointKey(tp.getKey())),
308                 tpBuilder.build());
309     }
310
311     private static TerminationPoint getTerminationPoint(Node bridgeNode, String portName) {
312         for (TerminationPoint tp : bridgeNode.getTerminationPoint()) {
313             String tpIdStr = tp.getTpId().getValue();
314             if (tpIdStr != null && tpIdStr.equals(portName))
315                 return tp;
316         }
317         return null;
318     }
319
320
321     private static String getPhysicalPortForUni(DataBroker dataBroker, String uniId) {
322         String nodeId = null;
323         try {
324             Link link = MefInterfaceUtils.getLink(dataBroker, uniId, LogicalDatastoreType.OPERATIONAL);
325             String parentInterfaceName = MefInterfaceUtils.getTrunkParentName(link);
326             return parentInterfaceName.split(":")[1];
327         } catch (Exception e) {
328             Log.error("Exception when getting physical port for Uni {}", uniId, e);
329         }
330         return nodeId;
331     }
332
333     private static BridgeRefEntry getBridgeRefEntryFromOperDS(InstanceIdentifier<BridgeRefEntry> dpnBridgeEntryIid,
334             DataBroker dataBroker) {
335         Optional<BridgeRefEntry> bridgeRefEntryOptional = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
336                 dpnBridgeEntryIid);
337         if (!bridgeRefEntryOptional.isPresent()) {
338             return null;
339         }
340         return bridgeRefEntryOptional.get();
341     }
342
343     private static OvsdbBridgeRef getBridgeRefEntryFromOperDS(BigInteger dpId, DataBroker dataBroker) {
344         BridgeRefEntryKey bridgeRefEntryKey = new BridgeRefEntryKey(dpId);
345         InstanceIdentifier<BridgeRefEntry> bridgeRefEntryIid = getBridgeRefEntryIdentifier(bridgeRefEntryKey);
346         BridgeRefEntry bridgeRefEntry = getBridgeRefEntryFromOperDS(bridgeRefEntryIid, dataBroker);
347         return (bridgeRefEntry != null) ? bridgeRefEntry.getBridgeReference() : null;
348     }
349
350     private static InstanceIdentifier<BridgeRefEntry> getBridgeRefEntryIdentifier(BridgeRefEntryKey bridgeRefEntryKey) {
351         InstanceIdentifier.InstanceIdentifierBuilder<BridgeRefEntry> bridgeRefEntryInstanceIdentifierBuilder = InstanceIdentifier
352                 .builder(BridgeRefInfo.class).child(BridgeRefEntry.class, bridgeRefEntryKey);
353         return bridgeRefEntryInstanceIdentifierBuilder.build();
354     }
355
356     private static InstanceIdentifier<BwpFlow> getBwFlowInstanceIdentifier(Identifier45 bwProfile) {
357         InstanceIdentifier.InstanceIdentifierBuilder<BwpFlow> bwProfileInstanceIdentifierBuilder = InstanceIdentifier
358                 .builder(MefGlobal.class).child(Profiles.class).child(IngressBwpFlows.class)
359                 .child(BwpFlow.class, new BwpFlowKey(bwProfile));
360         return bwProfileInstanceIdentifierBuilder.build();
361     }
362
363     private static InstanceIdentifier<BwpFlow> getBwFlowsInstanceIdentifier() {
364         InstanceIdentifier.InstanceIdentifierBuilder<BwpFlow> bwProfileInstanceIdentifierBuilder = InstanceIdentifier
365                 .builder(MefGlobal.class).child(Profiles.class).child(IngressBwpFlows.class).child(BwpFlow.class);
366         return bwProfileInstanceIdentifierBuilder.build();
367     }
368
369     private class BandwidthLimits {
370         private final Long maxKbps;
371         private final Long maxBurstKb;
372         private final String profileName;
373
374         public BandwidthLimits(Long maxKbps, Long maxBurstKb, String profileName) {
375             this.maxKbps = replaceNull(maxKbps);
376             this.maxBurstKb = replaceNull(maxBurstKb);
377             this.profileName = profileName;
378         }
379
380         public Long getMaxKbps() {
381             return maxKbps;
382         }
383
384         public Long getMaxBurstKb() {
385             return maxBurstKb;
386         }
387
388         public String getProfileName() {
389             return profileName;
390         }
391
392         private Long replaceNull(Long value) {
393             return (value == null) ? Long.valueOf(0) : value;
394         }
395
396         @Override
397         public boolean equals(Object obj) {
398             if (this == obj)
399                 return true;
400             if (obj == null)
401                 return false;
402             if (getClass() != obj.getClass())
403                 return false;
404             BandwidthLimits other = (BandwidthLimits) obj;
405             if (!getOuterType().equals(other.getOuterType()))
406                 return false;
407             if (maxBurstKb == null) {
408                 if (other.maxBurstKb != null)
409                     return false;
410             } else if (!maxBurstKb.equals(other.maxBurstKb))
411                 return false;
412             if (maxKbps == null) {
413                 if (other.maxKbps != null)
414                     return false;
415             } else if (!maxKbps.equals(other.maxKbps))
416                 return false;
417             return true;
418         }
419
420         @Override
421         public String toString() {
422             return "BandwidthLimitsBandwidthLimitsalues [maxKbps=" + maxKbps + ", maxBurstKb=" + maxBurstKb + "]";
423         }
424
425         private UniQosManager getOuterType() {
426             return UniQosManager.this;
427         }
428     }
429
430     private static String replaceNull(Identifier45 value) {
431         return (value == null) ? noProfile : value.getValue();
432     }
433
434     @Override
435     public void close() throws Exception {
436         bwListenerRegistration.close();
437     }
438
439     @Override
440     public void add(DataTreeModification<BwpFlow> newDataObject) {
441         if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
442             Log.info("bw profile {} created", newDataObject.getRootNode().getIdentifier());
443             updateProfile(newDataObject.getRootNode().getDataAfter().getBwProfile());
444         }
445     }
446
447     @Override
448     public void remove(DataTreeModification<BwpFlow> removedDataObject) {
449         if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
450             Log.info("bw profile {} deleted", removedDataObject.getRootNode().getIdentifier());
451             deleteProfile(removedDataObject.getRootNode().getDataBefore().getBwProfile());
452         }
453     }
454
455     @Override
456     public void update(DataTreeModification<BwpFlow> modifiedDataObject) {
457         if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
458             Log.info("bw profile {} modified", modifiedDataObject.getRootNode().getIdentifier());
459             updateProfile(modifiedDataObject.getRootNode().getDataAfter().getBwProfile());
460         }
461     }
462 }