2 * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. 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.netvirt.qosservice;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
13 import com.google.gson.Gson;
14 import com.google.gson.GsonBuilder;
15 import com.google.gson.JsonArray;
16 import com.google.gson.JsonObject;
18 import java.util.Map.Entry;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Future;
24 import java.util.function.Supplier;
25 import javax.annotation.PostConstruct;
26 import javax.annotation.PreDestroy;
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29 import org.apache.felix.service.command.CommandSession;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
33 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
34 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
35 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
36 import org.opendaylight.mdsal.binding.api.DataBroker;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.OpendaylightDirectStatisticsService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.qosalert.config.rev170301.QosalertConfig;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.qosalert.config.rev170301.QosalertConfigBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapKey;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
58 public final class QosAlertManager implements Runnable {
59 private static final Logger LOG = LoggerFactory.getLogger(QosAlertManager.class);
61 private volatile boolean alertEnabled;
62 private volatile int pollInterval;
63 private volatile Thread thread;
64 private volatile boolean statsPollThreadStart;
66 private final ManagedNewTransactionRunner txRunner;
67 private final QosalertConfig defaultConfig;
68 private final OpendaylightDirectStatisticsService odlDirectStatisticsService;
69 private final QosNeutronUtils qosNeutronUtils;
70 private final QosEosHandler qosEosHandler;
71 private final IInterfaceManager interfaceManager;
72 private final Set unprocessedInterfaceIds = ConcurrentHashMap.newKeySet();
73 private final ConcurrentMap<Uint64, ConcurrentMap<String, QosAlertPortData>> qosAlertDpnPortNumberMap =
74 new ConcurrentHashMap<>();
75 private final AlertThresholdSupplier alertThresholdSupplier = new AlertThresholdSupplier();
78 public QosAlertManager(final DataBroker dataBroker,
79 final OpendaylightDirectStatisticsService odlDirectStatisticsService, final QosalertConfig defaultConfig,
80 final QosNeutronUtils qosNeutronUtils, final QosEosHandler qosEosHandler,
81 final IInterfaceManager interfaceManager) {
82 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
83 this.odlDirectStatisticsService = odlDirectStatisticsService;
84 this.interfaceManager = interfaceManager;
85 this.defaultConfig = defaultConfig;
86 this.qosNeutronUtils = qosNeutronUtils;
87 this.qosEosHandler = qosEosHandler;
88 LOG.trace("QosAlert default config poll alertEnabled:{} threshold:{} pollInterval:{}",
89 defaultConfig.isQosAlertEnabled(), defaultConfig.getQosDropPacketThreshold(),
90 defaultConfig.getQosAlertPollInterval());
96 qosEosHandler.addLocalOwnershipChangedListener(this::setQosAlertOwner);
97 qosAlertDpnPortNumberMap.clear();
98 statsPollThreadStart = true;
99 startStatsPollThread();
100 LOG.trace("{} init done", getClass().getSimpleName());
104 public void close() {
105 statsPollThreadStart = false;
106 if (thread != null) {
109 LOG.trace("{} close done", getClass().getSimpleName());
112 private void setQosAlertOwner(boolean isOwner) {
113 LOG.trace("qos alert set owner : {}", isOwner);
114 statsPollThreadStart = isOwner;
115 if (thread != null) {
118 startStatsPollThread();
124 LOG.debug("Qos alert poll thread started");
125 while (statsPollThreadStart && alertEnabled) {
126 LOG.trace("Thread loop polling :{} threshold:{} pollInterval:{}",
127 alertEnabled, alertThresholdSupplier.get(), pollInterval);
130 pollDirectStatisticsForAllNodes();
131 Thread.sleep(pollInterval * 60L * 1000L); // pollInterval in minutes
132 } catch (final InterruptedException e) {
133 LOG.debug("Qos polling thread interrupted");
137 LOG.debug("Qos alert poll thread stopped");
140 private void startStatsPollThread() {
141 if (statsPollThreadStart && alertEnabled && thread == null) {
143 thread = new Thread(this);
144 thread.setDaemon(true);
149 private void getDefaultConfig() {
150 alertEnabled = defaultConfig.isQosAlertEnabled();
151 pollInterval = defaultConfig.getQosAlertPollInterval().toJava();
153 alertThresholdSupplier.set(defaultConfig.getQosDropPacketThreshold().toJava());
156 public void setQosalertConfig(QosalertConfig config) {
158 LOG.debug("New QoS alert config threshold:{} polling alertEnabled:{} interval:{}",
159 config.getQosDropPacketThreshold(), config.isQosAlertEnabled(),
160 config.getQosAlertPollInterval());
162 alertEnabled = config.isQosAlertEnabled().booleanValue();
163 pollInterval = config.getQosAlertPollInterval().toJava();
165 alertThresholdSupplier.set(config.getQosDropPacketThreshold().shortValue());
167 if (thread != null) {
170 startStatsPollThread();
175 public void restoreDefaultConfig() {
176 LOG.debug("Restoring default configuration");
178 if (thread != null) {
181 startStatsPollThread();
185 public void setThreshold(short threshold) {
186 LOG.debug("setting threshold {} in config data store", threshold);
187 writeConfigDataStore(alertEnabled, threshold, pollInterval);
190 public void setPollInterval(int pollInterval) {
191 LOG.debug("setting interval {} in config data store", pollInterval);
192 writeConfigDataStore(alertEnabled, alertThresholdSupplier.get().shortValue(), pollInterval);
195 public void setEnable(boolean enable) {
196 LOG.debug("setting QoS poll to {} in config data store", enable);
197 writeConfigDataStore(enable, alertThresholdSupplier.get().shortValue(), pollInterval);
200 public void addInterfaceIdInQoSAlertCache(String ifaceId) {
201 LOG.trace("Adding interface id {} in cache", ifaceId);
202 InterfaceInfo interfaceInfo =
203 interfaceManager.getInterfaceInfoFromOperationalDataStore(ifaceId);
204 if (interfaceInfo == null) {
205 LOG.debug("Interface not found {}. Added in cache now to process later ", ifaceId);
206 unprocessedInterfaceIds.add(ifaceId);
208 addToQosAlertCache(interfaceInfo);
212 public void displayConfig(CommandSession session) {
214 session.getConsole().println("Qos Alert Configuration Details");
215 session.getConsole().println("Threshold: " + alertThresholdSupplier.get().shortValue());
216 session.getConsole().println("AlertEnabled: " + alertEnabled);
217 session.getConsole().println("Poll Interval: " + pollInterval);
221 Gson gson = new GsonBuilder().setPrettyPrinting().create();
222 if (qosAlertDpnPortNumberMap.isEmpty()) {
223 session.getConsole().println("\nQosAlert Cache not found\n");
226 session.getConsole().println("\nDPN Map");
227 JsonObject jsonObject;
229 JsonArray jsonArrayOuter = new JsonArray();
230 for (Entry<Uint64, ConcurrentMap<String, QosAlertPortData>> dpnEntry
231 : qosAlertDpnPortNumberMap.entrySet()) {
232 dpnId = dpnEntry.getKey();
233 jsonObject = new JsonObject();
234 jsonObject.addProperty("DpnId", dpnId.toString());
235 ConcurrentMap<String, QosAlertPortData> portInnerMap = qosAlertDpnPortNumberMap.get(dpnId);
236 jsonArray = new JsonArray();
237 for (ConcurrentMap.Entry<String, QosAlertPortData> portEntry : portInnerMap.entrySet()) {
238 portData = "Port_number: " + portEntry.getKey() + ", " + portEntry.getValue();
239 jsonArray.add(portData);
241 jsonObject.add("QosAlertPortData Cache", jsonArray);
242 jsonArrayOuter.add(jsonObject);
244 session.getConsole().println(gson.toJson(jsonArrayOuter));
245 session.getConsole().println();
249 public void processInterfaceUpEvent(String ifaceId) {
250 LOG.trace("processInterfaceUpEvent {}", ifaceId);
251 if (unprocessedInterfaceIds.remove(ifaceId)) {
252 addInterfaceIdInQoSAlertCache(ifaceId);
256 private void addToQosAlertCache(InterfaceInfo interfaceInfo) {
257 Uint64 dpnId = interfaceInfo.getDpId();
258 if (dpnId.equals(Uint64.valueOf(0L))) {
259 LOG.warn("Interface {} could not be added to Qos Alert Cache because Dpn Id is not found",
260 interfaceInfo.getInterfaceName());
264 Port port = qosNeutronUtils.getNeutronPort(interfaceInfo.getInterfaceName());
266 LOG.warn("Port {} not added to Qos Alert Cache because it is not found", interfaceInfo.getInterfaceName());
270 String portNumber = String.valueOf(interfaceInfo.getPortNo());
272 LOG.trace("Adding DPN ID {} with port {} port number {}", dpnId, port.getUuid(), portNumber);
274 qosAlertDpnPortNumberMap.computeIfAbsent(dpnId, key -> new ConcurrentHashMap<>())
275 .put(portNumber, new QosAlertPortData(port, qosNeutronUtils, alertThresholdSupplier));
278 public void removeInterfaceIdFromQosAlertCache(String ifaceId) {
280 LOG.trace("If present, remove interface {} from cache", ifaceId);
281 unprocessedInterfaceIds.remove(ifaceId);
282 InterfaceInfo interfaceInfo =
283 interfaceManager.getInterfaceInfoFromOperationalDataStore(ifaceId);
284 if (interfaceInfo == null) {
287 Uint64 dpnId = interfaceInfo.getDpId();
288 String portNumber = String.valueOf(interfaceInfo.getPortNo());
289 removeFromQosAlertCache(dpnId, portNumber);
292 public void removeLowerLayerIfFromQosAlertCache(String lowerLayerIf) {
293 LOG.trace("If present, remove lowerLayerIf {} from cache", lowerLayerIf);
294 Uint64 dpnId = qosNeutronUtils.getDpnIdFromLowerLayerIf(lowerLayerIf);
295 String portNumber = qosNeutronUtils.getPortNumberFromLowerLayerIf(lowerLayerIf);
296 if (dpnId == null || portNumber == null) {
297 LOG.warn("Interface {} not in openflow:dpnid:portnum format, could not remove from cache", lowerLayerIf);
300 removeFromQosAlertCache(dpnId, portNumber);
303 private void removeFromQosAlertCache(Uint64 dpnId, String portNumber) {
304 if (qosAlertDpnPortNumberMap.containsKey(dpnId)
305 && qosAlertDpnPortNumberMap.get(dpnId).containsKey(portNumber)) {
306 qosAlertDpnPortNumberMap.get(dpnId).remove(portNumber);
307 LOG.trace("Removed interace {}:{} from cache", dpnId, portNumber);
308 if (qosAlertDpnPortNumberMap.get(dpnId).isEmpty()) {
309 LOG.trace("DPN {} empty. Removing dpn from cache", dpnId);
310 qosAlertDpnPortNumberMap.remove(dpnId);
315 private void writeConfigDataStore(boolean qosAlertEnabled, short dropPacketThreshold, int alertPollInterval) {
317 InstanceIdentifier<QosalertConfig> path = InstanceIdentifier.builder(QosalertConfig.class).build();
319 QosalertConfig qosAlertConfig = new QosalertConfigBuilder()
320 .setQosDropPacketThreshold(dropPacketThreshold)
321 .setQosAlertEnabled(qosAlertEnabled)
322 .setQosAlertPollInterval(alertPollInterval)
325 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
326 tx -> tx.mergeParentStructurePut(path,
327 qosAlertConfig)), LOG, "Error writing to the config data store");
330 private void pollDirectStatisticsForAllNodes() {
331 LOG.trace("Polling direct statistics from nodes");
333 for (Entry<Uint64, ConcurrentMap<String, QosAlertPortData>> entry : qosAlertDpnPortNumberMap.entrySet()) {
334 Uint64 dpn = entry.getKey();
335 LOG.trace("Polling DPN ID {}", dpn);
336 GetNodeConnectorStatisticsInputBuilder input = new GetNodeConnectorStatisticsInputBuilder()
337 .setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class)
338 .child(Node.class, new NodeKey(new NodeId(IfmConstants.OF_URI_PREFIX + dpn))).build()))
339 .setStoreStats(false);
340 Future<RpcResult<GetNodeConnectorStatisticsOutput>> rpcResultFuture =
341 odlDirectStatisticsService.getNodeConnectorStatistics(input.build());
343 RpcResult<GetNodeConnectorStatisticsOutput> rpcResult = null;
345 rpcResult = rpcResultFuture.get();
346 } catch (InterruptedException | ExecutionException e) {
347 if (LOG.isDebugEnabled()) {
348 LOG.debug("Could not get Direct-Statistics for node {} Exception occurred ", dpn, e);
350 LOG.info("Could not get Direct-Statistics for node {}", dpn);
353 if (rpcResult != null && rpcResult.isSuccessful() && rpcResult.getResult() != null) {
355 GetNodeConnectorStatisticsOutput nodeConnectorStatisticsOutput = rpcResult.getResult();
357 Map<NodeConnectorStatisticsAndPortNumberMapKey, NodeConnectorStatisticsAndPortNumberMap>
358 nodeConnectorStatisticsAndPortNumberMap =
359 nodeConnectorStatisticsOutput.nonnullNodeConnectorStatisticsAndPortNumberMap();
361 ConcurrentMap<String, QosAlertPortData> portDataMap = entry.getValue();
362 for (NodeConnectorStatisticsAndPortNumberMap stats
363 : nodeConnectorStatisticsAndPortNumberMap.values()) {
364 QosAlertPortData portData = portDataMap.get(stats.getNodeConnectorId().getValue());
365 if (portData != null) {
366 portData.updatePortStatistics(stats);
370 LOG.info("Direct-Statistics not available for node {}", dpn);
376 private void initPortStatsData() {
377 qosAlertDpnPortNumberMap.values().forEach(portDataMap -> portDataMap.values()
378 .forEach(QosAlertPortData::initPortData));
381 private static class AlertThresholdSupplier implements Supplier<Uint64> {
382 private volatile Uint64 alertThreshold = Uint64.valueOf(0);
384 void set(short threshold) {
385 alertThreshold = Uint64.valueOf(threshold);
389 public Uint64 get() {
390 return alertThreshold;