671b0e275e9a5ad4aef39a499ce15976d5404f5f
[hc2vpp.git] /
1 /*
2  * Copyright (c) 2019 PANTHEON.tech.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.hc2vpp.v3po.notification;
18
19 import com.google.common.collect.Lists;
20 import io.fd.hc2vpp.common.translate.util.ByteDataTranslator;
21 import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer;
22 import io.fd.hc2vpp.v3po.interfacesstate.cache.InterfaceCacheStatisticsDumpManager;
23 import io.fd.honeycomb.notification.ManagedNotificationProducer;
24 import io.fd.honeycomb.notification.NotificationCollector;
25 import io.fd.vpp.jvpp.VppBaseCallException;
26 import io.fd.vpp.jvpp.VppCallbackException;
27 import io.fd.vpp.jvpp.core.callback.VnetPerInterfaceCombinedCountersCallback;
28 import io.fd.vpp.jvpp.core.dto.VnetPerInterfaceCombinedCounters;
29 import io.fd.vpp.jvpp.core.dto.WantPerInterfaceCombinedStats;
30 import io.fd.vpp.jvpp.core.dto.WantPerInterfaceCombinedStatsReply;
31 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
32 import java.math.BigInteger;
33 import java.time.LocalDateTime;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Optional;
37 import java.util.concurrent.CompletionStage;
38 import java.util.concurrent.TimeoutException;
39 import javax.annotation.Nonnull;
40 import javax.annotation.Nullable;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Counter64;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181128.InterfaceStatisticsChange;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181128.InterfaceStatisticsChangeBuilder;
44 import org.opendaylight.yangtools.yang.binding.Notification;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public class StatisticsChangeNotificationProducer implements ManagedNotificationProducer, JvppReplyConsumer,
49         ByteDataTranslator {
50
51     private static final Logger LOG = LoggerFactory.getLogger(StatisticsChangeNotificationProducer.class);
52
53     private final FutureJVppCore jvpp;
54     private InterfaceCacheStatisticsDumpManager ifcStatDumpManager;
55
56     @Nullable
57     private AutoCloseable notificationListenerReg;
58
59     public StatisticsChangeNotificationProducer(final FutureJVppCore jvpp,
60                                                 final InterfaceCacheStatisticsDumpManager ifcStatDumpManager) {
61
62         this.jvpp = jvpp;
63         this.ifcStatDumpManager = ifcStatDumpManager;
64     }
65
66     @Override
67     public void start(@Nonnull final NotificationCollector collector) {
68         LOG.trace("Starting statistics notifications");
69
70         notificationListenerReg = jvpp.getEventRegistry().registerVnetPerInterfaceCombinedCountersCallback(
71                 new VnetPerInterfaceCombinedCountersCallback() {
72                     @Override
73                     public void onVnetPerInterfaceCombinedCounters(
74                             final VnetPerInterfaceCombinedCounters vnetPerInterfaceCombinedCounters) {
75                         LOG.trace("Statistics notification received: {}", vnetPerInterfaceCombinedCounters);
76                         try {
77                             ifcStatDumpManager.setStatisticsData(vnetPerInterfaceCombinedCounters, LocalDateTime.now(),
78                                     vnetPerInterfaceCombinedCounters.data[0].swIfIndex);
79                             collector.onNotification(transformNotification(vnetPerInterfaceCombinedCounters));
80
81                         } catch (Exception e) {
82                             // There is no need to propagate exception to jvpp rx thread in case of unexpected failures.
83                             // We can't do much about it, so lets log the exception.
84                             LOG.warn("Failed to process statistics notification {}", vnetPerInterfaceCombinedCounters,
85                                     e);
86                         }
87                     }
88
89                     @Override
90                     public void onError(final VppCallbackException e) {
91                         LOG.warn("Statistics notification error received.", e);
92                     }
93                 }
94         );
95     }
96
97     private Notification transformNotification(final VnetPerInterfaceCombinedCounters reading) {
98
99         InterfaceStatisticsChangeBuilder builder = new InterfaceStatisticsChangeBuilder();
100         if (reading.data.length > 0) {
101             builder.setInBroadcastPkts(new Counter64(BigInteger.valueOf(reading.data[0].rxBroadcastPackets)))
102                     .setOutBroadcastPkts(new Counter64(BigInteger.valueOf(reading.data[0].txBroadcastPackets)))
103                     .setInMulticastPkts(new Counter64(BigInteger.valueOf(reading.data[0].rxMulticastPackets)))
104                     .setOutMulticastPkts(new Counter64(BigInteger.valueOf(reading.data[0].txMulticastPackets)))
105                     .setInOctets(new Counter64(BigInteger.valueOf(reading.data[0].rxBytes)))
106                     .setOutOctets(new Counter64(BigInteger.valueOf(reading.data[0].txBytes)));
107         }
108         return builder.build();
109     }
110
111     @Override
112     public void stop() {
113         LOG.trace("Stopping statistics notifications");
114         disableIfcNotifications(ifcStatDumpManager.getEnabledInterfaces());
115         ifcStatDumpManager.disableAll();
116         LOG.debug("Statistics notifications stopped successfully");
117         try {
118             if (notificationListenerReg != null) {
119                 notificationListenerReg.close();
120             }
121         } catch (Exception e) {
122             LOG.warn("Unable to properly close notification registration: {}", notificationListenerReg, e);
123         }
124     }
125
126     @Nonnull
127     @Override
128     public Collection<Class<? extends Notification>> getNotificationTypes() {
129         final ArrayList<Class<? extends Notification>> classes = Lists.newArrayList();
130         classes.add(InterfaceStatisticsChange.class);
131         return classes;
132     }
133
134     @Override
135     public void close() throws Exception {
136         LOG.trace("Closing statistics notifications producer");
137         stop();
138     }
139
140     private void disableIfcNotifications(final int[] swIfIndexes) {
141         if (swIfIndexes.length == 0) {
142             return;
143         }
144         WantPerInterfaceCombinedStats request = new WantPerInterfaceCombinedStats();
145         request.num = swIfIndexes.length;
146         request.enableDisable = BYTE_FALSE;
147         request.pid = 1;
148         request.swIfs = Optional.of(swIfIndexes).orElse(new int[]{});
149         final CompletionStage<WantPerInterfaceCombinedStatsReply> result =
150                 this.jvpp.wantPerInterfaceCombinedStats(request);
151         try {
152             getReply(result.toCompletableFuture());
153         } catch (VppBaseCallException | TimeoutException e) {
154             LOG.warn("Unable to disable statistics notifications", e);
155             throw new IllegalStateException("Unable to disable statistics notifications", e);
156         }
157     }
158 }