2 * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved.
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
9 package org.opendaylight.unimgr.mef.netvirt;
11 import com.google.common.base.Optional;
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;
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;
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;
67 // key in first map is uniId, key in second map is logical portId
68 private ConcurrentHashMap<String, ConcurrentHashMap<String, BandwidthLimits>> uniPortBandwidthLimits;
70 // map of current values per uni
71 private ConcurrentHashMap<String, BandwidthLimits> uniBandwidthLimits;
73 private ConcurrentHashMap<String, BigInteger> uniToDpn;
75 public UniQosManager(final DataBroker dataBroker, OdlInterfaceRpcService odlInterfaceRpcService) {
78 this.dataBroker = dataBroker;
79 this.odlInterfaceRpcService = odlInterfaceRpcService;
80 this.uniPortBandwidthLimits = new ConcurrentHashMap<>();
81 this.uniBandwidthLimits = new ConcurrentHashMap<>();
82 this.uniToDpn = new ConcurrentHashMap<>();
86 public void registerListener() {
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);
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);
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);
115 mapUniPortBandwidthLimits(uniId, portId, maxKbps, maxBurstKb, replaceNull(bwProfile));
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));
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());
134 for (String portName : uniMap.keySet()) {
135 if (uniMap.get(portName).getProfileName().equals(bwProfileSafe)) {
138 if (bwProfile != null) {
139 mapUniPortBandwidthLimits(uniId.getValue(), portName, new Identifier45(bwProfileSafe));
141 unMapUniPortBandwidthLimits(uniId.getValue(), portName);
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());
152 for (String uniId : unisWithProfile) {
153 ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
154 uniMap.forEach((k, v) -> {
155 mapUniPortBandwidthLimits(uniId, k, bwProfile);
159 for (String uniId : unisWithProfile) {
160 setUniBandwidthLimits(uniId);
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());
170 for (String uniId : unisWithProfile) {
171 ConcurrentHashMap<String, BandwidthLimits> uniMap = uniPortBandwidthLimits.get(uniId);
172 uniMap.forEach((k, v) -> {
173 unMapUniPortBandwidthLimits(uniId, k, bwProfile.getValue());
177 for (String uniId : unisWithProfile) {
178 setUniBandwidthLimits(uniId);
182 public synchronized void unMapUniPortBandwidthLimits(String uniId, String portId) {
183 unMapUniPortBandwidthLimits(uniId, portId, noProfile);
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);
193 uniMap.remove(portId);
194 if (uniMap.isEmpty()) {
195 uniMap.put(portId, new BandwidthLimits(noLimit, noLimit, profileTosave));
199 public void setUniBandwidthLimits(Identifier45 uniIden) {
200 String uniId = uniIden.getValue();
201 setUniBandwidthLimits(uniId);
204 private synchronized void setUniBandwidthLimits(String uniId) {
205 if (!uniPortBandwidthLimits.containsKey(uniId)) {
206 Log.debug("Uni {} doesn't have rate limits configured", uniId);
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);
214 String logicalPort = uniPorts.next();
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);
222 setPortBandwidthLimits(uniId, logicalPort, newLimits.getMaxKbps(), newLimits.getMaxBurstKb());
223 uniBandwidthLimits.put(uniId, newLimits);
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;
234 if (uniLimits == null || uniLimits.keySet() == null) {
235 return new BandwidthLimits(sumOfRate, sumOfBurst, profileName);
238 for (BandwidthLimits v : uniLimits.values()) {
239 if (v.maxKbps == null) {
243 if (v.maxBurstKb == null) {
246 sumOfRate = sumOfRate + v.maxKbps;
247 sumOfBurst = sumOfBurst + v.maxBurstKb;
248 profileName = v.profileName;
252 sumOfBurst = noLimit;
253 } else if (hasNullBurst) {
254 sumOfBurst = noLimit;
256 return new BandwidthLimits(sumOfRate, sumOfBurst, profileName);
259 private void setPortBandwidthLimits(String uniId, String logicalPortId, Long maxKbps, Long maxBurstKb) {
260 Log.info("Setting bandwidth limits {} {} on Port {}", maxKbps, maxBurstKb, logicalPortId);
262 BigInteger dpId = BigInteger.ZERO;
263 if (uniToDpn.containsKey(uniId)) {
264 dpId = uniToDpn.get(uniId);
266 dpId = NetvirtUtils.getDpnForInterface(odlInterfaceRpcService, logicalPortId);
267 uniToDpn.put(uniId, dpId);
269 if (dpId.equals(BigInteger.ZERO)) {
270 Log.error("DPN ID for interface {} not found", logicalPortId);
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);
282 String physicalPort = getPhysicalPortForUni(dataBroker, uniId);
283 if (physicalPort == null) {
284 Log.error("Physical port for interface {} not found", logicalPortId);
288 TerminationPoint tp = getTerminationPoint(bridgeNode.get(), physicalPort);
290 Log.error("Termination point for port {} not found", physicalPort);
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);
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())),
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))
321 private static String getPhysicalPortForUni(DataBroker dataBroker, String uniId) {
322 String nodeId = null;
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);
333 private static BridgeRefEntry getBridgeRefEntryFromOperDS(InstanceIdentifier<BridgeRefEntry> dpnBridgeEntryIid,
334 DataBroker dataBroker) {
335 Optional<BridgeRefEntry> bridgeRefEntryOptional = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
337 if (!bridgeRefEntryOptional.isPresent()) {
340 return bridgeRefEntryOptional.get();
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;
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();
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();
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();
369 private class BandwidthLimits {
370 private final Long maxKbps;
371 private final Long maxBurstKb;
372 private final String profileName;
374 public BandwidthLimits(Long maxKbps, Long maxBurstKb, String profileName) {
375 this.maxKbps = replaceNull(maxKbps);
376 this.maxBurstKb = replaceNull(maxBurstKb);
377 this.profileName = profileName;
380 public Long getMaxKbps() {
384 public Long getMaxBurstKb() {
388 public String getProfileName() {
392 private Long replaceNull(Long value) {
393 return (value == null) ? Long.valueOf(0) : value;
397 public boolean equals(Object obj) {
402 if (getClass() != obj.getClass())
404 BandwidthLimits other = (BandwidthLimits) obj;
405 if (!getOuterType().equals(other.getOuterType()))
407 if (maxBurstKb == null) {
408 if (other.maxBurstKb != null)
410 } else if (!maxBurstKb.equals(other.maxBurstKb))
412 if (maxKbps == null) {
413 if (other.maxKbps != null)
415 } else if (!maxKbps.equals(other.maxKbps))
421 public String toString() {
422 return "BandwidthLimitsBandwidthLimitsalues [maxKbps=" + maxKbps + ", maxBurstKb=" + maxBurstKb + "]";
425 private UniQosManager getOuterType() {
426 return UniQosManager.this;
430 private static String replaceNull(Identifier45 value) {
431 return (value == null) ? noProfile : value.getValue();
435 public void close() throws Exception {
436 bwListenerRegistration.close();
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());
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());
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());