Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017-present Open Networking Foundation |
| 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 | package org.opencord.dhcpl2relay.impl; |
| 17 | |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 18 | import com.google.common.base.Strings; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableMap; |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 20 | import org.onlab.util.KryoNamespace; |
| 21 | import org.onlab.util.SafeRecurringTask; |
| 22 | import org.onlab.util.Tools; |
| 23 | import org.onosproject.cfg.ComponentConfigService; |
| 24 | import org.onosproject.cluster.ClusterService; |
| 25 | import org.onosproject.cluster.LeadershipService; |
| 26 | import org.onosproject.cluster.NodeId; |
| 27 | import org.onosproject.store.AbstractStore; |
| 28 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
| 29 | import org.onosproject.store.cluster.messaging.ClusterMessage; |
| 30 | import org.onosproject.store.cluster.messaging.MessageSubject; |
| 31 | import org.onosproject.store.serializers.KryoNamespaces; |
| 32 | import org.onosproject.store.service.EventuallyConsistentMap; |
| 33 | import org.onosproject.store.service.Serializer; |
| 34 | import org.onosproject.store.service.StorageService; |
| 35 | import org.onosproject.store.service.WallClockTimestamp; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 36 | import org.opencord.dhcpl2relay.DhcpL2RelayEvent; |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 37 | import org.opencord.dhcpl2relay.DhcpL2RelayStoreDelegate; |
| 38 | import org.osgi.service.component.ComponentContext; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 39 | import org.osgi.service.component.annotations.Activate; |
| 40 | import org.osgi.service.component.annotations.Component; |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 41 | import org.osgi.service.component.annotations.Deactivate; |
| 42 | import org.osgi.service.component.annotations.Modified; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 43 | import org.osgi.service.component.annotations.Reference; |
| 44 | import org.osgi.service.component.annotations.ReferenceCardinality; |
| 45 | import org.slf4j.Logger; |
| 46 | |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 47 | import java.nio.charset.StandardCharsets; |
| 48 | import java.util.AbstractMap; |
| 49 | import java.util.Dictionary; |
| 50 | import java.util.Objects; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 51 | import java.util.concurrent.ConcurrentHashMap; |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 52 | import java.util.concurrent.ConcurrentMap; |
| 53 | import java.util.concurrent.Executors; |
| 54 | import java.util.concurrent.ScheduledExecutorService; |
| 55 | import java.util.concurrent.ScheduledFuture; |
| 56 | import java.util.concurrent.TimeUnit; |
| 57 | import java.util.concurrent.atomic.AtomicBoolean; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 58 | import java.util.concurrent.atomic.AtomicLong; |
| 59 | |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 60 | import static com.google.common.base.Preconditions.checkNotNull; |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 61 | import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.PUBLISH_COUNTERS_RATE; |
| 62 | import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.PUBLISH_COUNTERS_RATE_DEFAULT; |
| 63 | import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.SYNC_COUNTERS_RATE; |
| 64 | import static org.opencord.dhcpl2relay.impl.OsgiPropertyConstants.SYNC_COUNTERS_RATE_DEFAULT; |
| 65 | import static org.slf4j.LoggerFactory.getLogger; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 66 | |
| 67 | /** |
| 68 | * DHCP Relay Agent Counters Manager Component. |
| 69 | */ |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 70 | @Component(immediate = true, |
| 71 | property = { |
| 72 | PUBLISH_COUNTERS_RATE + ":Integer=" + PUBLISH_COUNTERS_RATE_DEFAULT, |
| 73 | SYNC_COUNTERS_RATE + ":Integer=" + SYNC_COUNTERS_RATE_DEFAULT, |
| 74 | } |
| 75 | ) |
| 76 | public class SimpleDhcpL2RelayCountersStore extends AbstractStore<DhcpL2RelayEvent, DhcpL2RelayStoreDelegate> |
| 77 | implements DhcpL2RelayCountersStore { |
| 78 | |
| 79 | private static final String DHCP_STATISTICS_LEADERSHIP = "dhcpl2relay-statistics"; |
| 80 | private static final MessageSubject RESET_SUBJECT = new MessageSubject("dhcpl2relay-statistics-reset"); |
| 81 | |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 82 | private final Logger log = getLogger(getClass()); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 83 | private ConcurrentMap<DhcpL2RelayCountersIdentifier, Long> countersMap; |
| 84 | |
| 85 | private EventuallyConsistentMap<NodeId, DhcpL2RelayStatistics> statistics; |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 86 | |
| 87 | @Reference(cardinality = ReferenceCardinality.MANDATORY) |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 88 | protected StorageService storageService; |
| 89 | |
| 90 | @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| 91 | protected ClusterService clusterService; |
| 92 | |
| 93 | @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| 94 | protected LeadershipService leadershipService; |
| 95 | |
| 96 | @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| 97 | protected ComponentConfigService componentConfigService; |
| 98 | |
| 99 | @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| 100 | protected ClusterCommunicationService clusterCommunicationService; |
| 101 | |
| 102 | protected int publishCountersRate = PUBLISH_COUNTERS_RATE_DEFAULT; |
| 103 | protected int syncCountersRate = SYNC_COUNTERS_RATE_DEFAULT; |
| 104 | |
| 105 | KryoNamespace serializer = KryoNamespace.newBuilder() |
| 106 | .register(KryoNamespaces.API) |
| 107 | .register(DhcpL2RelayStatistics.class) |
| 108 | .register(DhcpL2RelayCountersIdentifier.class) |
| 109 | .register(DhcpL2RelayCounterNames.class) |
| 110 | .register(ClusterMessage.class) |
| 111 | .register(MessageSubject.class) |
| 112 | .build(); |
| 113 | |
| 114 | private ScheduledExecutorService executor; |
| 115 | |
| 116 | private ScheduledFuture<?> publisherTask; |
| 117 | private ScheduledFuture<?> syncTask; |
| 118 | |
| 119 | private AtomicBoolean dirty = new AtomicBoolean(true); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 120 | |
| 121 | @Activate |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 122 | public void activate(ComponentContext context) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 123 | log.info("Activate Dhcp L2 Counters Manager"); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 124 | countersMap = new ConcurrentHashMap<>(); |
| 125 | componentConfigService.registerProperties(getClass()); |
| 126 | |
| 127 | modified(context); |
| 128 | |
| 129 | statistics = storageService.<NodeId, DhcpL2RelayStatistics>eventuallyConsistentMapBuilder() |
| 130 | .withName("dhcpl2relay-statistics") |
| 131 | .withSerializer(serializer) |
| 132 | .withTimestampProvider((k, v) -> new WallClockTimestamp()) |
| 133 | .build(); |
| 134 | |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 135 | // Initialize counter values for the global counters |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 136 | initCounters(DhcpL2RelayEvent.GLOBAL_COUNTER, statistics.get(clusterService.getLocalNode().id())); |
| 137 | syncStats(); |
| 138 | |
| 139 | leadershipService.runForLeadership(DHCP_STATISTICS_LEADERSHIP); |
| 140 | |
| 141 | executor = Executors.newScheduledThreadPool(1); |
| 142 | |
| 143 | clusterCommunicationService.addSubscriber(RESET_SUBJECT, Serializer.using(serializer)::decode, |
| 144 | this::resetLocal, executor); |
| 145 | |
| 146 | startSyncTask(); |
| 147 | startPublishTask(); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 148 | } |
| 149 | |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 150 | @Deactivate |
| 151 | public void deactivate() { |
| 152 | clusterCommunicationService.removeSubscriber(RESET_SUBJECT); |
| 153 | leadershipService.withdraw(DHCP_STATISTICS_LEADERSHIP); |
| 154 | |
| 155 | stopPublishTask(); |
| 156 | stopSyncTask(); |
| 157 | executor.shutdownNow(); |
| 158 | componentConfigService.unregisterProperties(getClass(), false); |
| 159 | } |
| 160 | |
| 161 | @Modified |
| 162 | public void modified(ComponentContext context) { |
| 163 | Dictionary<String, Object> properties = context.getProperties(); |
| 164 | |
| 165 | String s = Tools.get(properties, PUBLISH_COUNTERS_RATE); |
| 166 | int oldPublishCountersRate = publishCountersRate; |
| 167 | publishCountersRate = Strings.isNullOrEmpty(s) ? PUBLISH_COUNTERS_RATE_DEFAULT |
| 168 | : Integer.parseInt(s.trim()); |
| 169 | if (oldPublishCountersRate != publishCountersRate) { |
| 170 | stopPublishTask(); |
| 171 | startPublishTask(); |
| 172 | } |
| 173 | |
| 174 | s = Tools.get(properties, SYNC_COUNTERS_RATE); |
| 175 | int oldSyncCountersRate = syncCountersRate; |
| 176 | syncCountersRate = Strings.isNullOrEmpty(s) ? SYNC_COUNTERS_RATE_DEFAULT |
| 177 | : Integer.parseInt(s.trim()); |
| 178 | if (oldSyncCountersRate != syncCountersRate) { |
| 179 | stopSyncTask(); |
| 180 | startSyncTask(); |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | private ScheduledFuture<?> startTask(Runnable r, int rate) { |
| 185 | return executor.scheduleAtFixedRate(SafeRecurringTask.wrap(r), |
| 186 | 0, rate, TimeUnit.SECONDS); |
| 187 | } |
| 188 | |
| 189 | private void stopTask(ScheduledFuture<?> task) { |
| 190 | task.cancel(true); |
| 191 | } |
| 192 | |
| 193 | private void startSyncTask() { |
| 194 | syncTask = startTask(this::syncStats, syncCountersRate); |
| 195 | } |
| 196 | |
| 197 | private void stopSyncTask() { |
| 198 | stopTask(syncTask); |
| 199 | } |
| 200 | |
| 201 | private void startPublishTask() { |
| 202 | publisherTask = startTask(this::publishStats, publishCountersRate); |
| 203 | } |
| 204 | |
| 205 | private void stopPublishTask() { |
| 206 | stopTask(publisherTask); |
| 207 | } |
| 208 | |
| 209 | ImmutableMap<DhcpL2RelayCountersIdentifier, Long> getCountersMap() { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 210 | return ImmutableMap.copyOf(countersMap); |
| 211 | } |
| 212 | |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 213 | public DhcpL2RelayStatistics getCounters() { |
| 214 | return aggregate(); |
| 215 | } |
| 216 | |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 217 | /** |
| 218 | * Initialize the supported counters map for the given counter class. |
| 219 | * @param counterClass class of counters (global, per subscriber) |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 220 | * @param existingStats existing values to intialise the counters to |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 221 | */ |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 222 | public void initCounters(String counterClass, DhcpL2RelayStatistics existingStats) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 223 | checkNotNull(counterClass, "counter class can't be null"); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 224 | for (DhcpL2RelayCounterNames counterType : DhcpL2RelayCounterNames.SUPPORTED_COUNTERS) { |
| 225 | DhcpL2RelayCountersIdentifier id = new DhcpL2RelayCountersIdentifier(counterClass, counterType); |
| 226 | countersMap.put(id, existingStats == null ? 0L : existingStats.get(id)); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 227 | } |
| 228 | } |
| 229 | |
| 230 | /** |
| 231 | * Inserts the counter entry if it is not already in the set otherwise increment the existing counter entry. |
| 232 | * @param counterClass class of counters (global, per subscriber) |
| 233 | * @param counterType name of counter |
| 234 | */ |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 235 | public void incrementCounter(String counterClass, DhcpL2RelayCounterNames counterType) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 236 | checkNotNull(counterClass, "counter class can't be null"); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 237 | if (DhcpL2RelayCounterNames.SUPPORTED_COUNTERS.contains(counterType)) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 238 | DhcpL2RelayCountersIdentifier counterIdentifier = |
| 239 | new DhcpL2RelayCountersIdentifier(counterClass, counterType); |
| 240 | countersMap.compute(counterIdentifier, (key, counterValue) -> |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 241 | (counterValue != null) ? counterValue + 1 : 1L); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 242 | } else { |
| 243 | log.error("Failed to increment counter class {} of type {}", counterClass, counterType); |
| 244 | } |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 245 | dirty.set(true); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | /** |
| 249 | * Reset the counters map for the given counter class. |
| 250 | * @param counterClass class of counters (global, per subscriber) |
| 251 | */ |
| 252 | public void resetCounters(String counterClass) { |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 253 | byte[] payload = counterClass.getBytes(StandardCharsets.UTF_8); |
| 254 | ClusterMessage reset = new ClusterMessage(clusterService.getLocalNode().id(), RESET_SUBJECT, payload); |
| 255 | clusterCommunicationService.broadcastIncludeSelf(reset, RESET_SUBJECT, Serializer.using(serializer)::encode); |
| 256 | } |
| 257 | |
| 258 | private void resetLocal(ClusterMessage m) { |
| 259 | String counterClass = new String(m.payload(), StandardCharsets.UTF_8); |
| 260 | |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 261 | checkNotNull(counterClass, "counter class can't be null"); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 262 | for (DhcpL2RelayCounterNames counterType : DhcpL2RelayCounterNames.SUPPORTED_COUNTERS) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 263 | DhcpL2RelayCountersIdentifier counterIdentifier = |
| 264 | new DhcpL2RelayCountersIdentifier(counterClass, counterType); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 265 | countersMap.computeIfPresent(counterIdentifier, (key, counterValue) -> 0L); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 266 | } |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 267 | dirty.set(true); |
| 268 | syncStats(); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Inserts the counter entry if it is not already in the set otherwise update the existing counter entry. |
| 273 | * @param counterClass class of counters (global, per subscriber). |
| 274 | * @param counterType name of counter |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 275 | * @param value counter value |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 276 | */ |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 277 | public void setCounter(String counterClass, DhcpL2RelayCounterNames counterType, Long value) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 278 | checkNotNull(counterClass, "counter class can't be null"); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 279 | if (DhcpL2RelayCounterNames.SUPPORTED_COUNTERS.contains(counterType)) { |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 280 | DhcpL2RelayCountersIdentifier counterIdentifier = |
| 281 | new DhcpL2RelayCountersIdentifier(counterClass, counterType); |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 282 | countersMap.put(counterIdentifier, value); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 283 | } else { |
| 284 | log.error("Failed to increment counter class {} of type {}", counterClass, counterType); |
| 285 | } |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 286 | dirty.set(true); |
| 287 | syncStats(); |
| 288 | } |
| 289 | |
| 290 | private DhcpL2RelayStatistics aggregate() { |
| 291 | return statistics.values().stream() |
| 292 | .reduce(new DhcpL2RelayStatistics(), DhcpL2RelayStatistics::add); |
| 293 | } |
| 294 | |
| 295 | /** |
| 296 | * Creates a snapshot of the current in-memory statistics. |
| 297 | * |
| 298 | * @return snapshot of statistics |
| 299 | */ |
| 300 | private DhcpL2RelayStatistics snapshot() { |
| 301 | return DhcpL2RelayStatistics.withCounters(countersMap); |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * Syncs in-memory stats to the eventually consistent map. |
| 306 | */ |
| 307 | private void syncStats() { |
| 308 | if (dirty.get()) { |
| 309 | statistics.put(clusterService.getLocalNode().id(), snapshot()); |
| 310 | dirty.set(false); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | private void publishStats() { |
| 315 | // Only publish events if we are the cluster leader for DHCP L2 relay stats |
| 316 | if (!Objects.equals(leadershipService.getLeader(DHCP_STATISTICS_LEADERSHIP), |
| 317 | clusterService.getLocalNode().id())) { |
| 318 | return; |
| 319 | } |
| 320 | |
| 321 | aggregate().counters().forEach((counterKey, counterValue) -> { |
| 322 | // Subscriber-specific counters have the subscriber ID set |
| 323 | String subscriberId = null; |
| 324 | if (!counterKey.counterClassKey.equals(DhcpL2RelayEvent.GLOBAL_COUNTER)) { |
| 325 | subscriberId = counterKey.counterClassKey; |
| 326 | } |
| 327 | |
Jonathan Hart | b4fbc92 | 2020-04-14 12:17:44 -0700 | [diff] [blame] | 328 | notifyDelegate(new DhcpL2RelayEvent(DhcpL2RelayEvent.Type.STATS_UPDATE, null, null, |
Jonathan Hart | 77ca315 | 2020-02-21 14:31:21 -0800 | [diff] [blame] | 329 | new AbstractMap.SimpleEntry<>(counterKey.counterTypeKey.toString(), |
| 330 | new AtomicLong(counterValue)), subscriberId)); |
| 331 | }); |
Marcos Aurelio Carrero | eaf02b8 | 2019-11-25 13:34:25 -0300 | [diff] [blame] | 332 | } |
| 333 | } |