blob: f5dea71f63d20b10f54914e73978c6b252e79980 [file] [log] [blame]
Carmelo Cascone3977ea42019-02-28 13:43:42 -08001/*
2 * Copyright 2019-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
17package org.onosproject.p4runtime.ctl.controller;
18
19import com.google.common.collect.Maps;
pierventre381d4542021-01-15 17:27:48 +010020import org.apache.commons.lang3.tuple.ImmutablePair;
Carmelo Casconec2be50a2019-04-10 00:15:39 -070021import org.apache.commons.lang3.tuple.Pair;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080022import org.onlab.util.KryoNamespace;
23import org.onosproject.net.DeviceId;
24import org.onosproject.store.serializers.KryoNamespaces;
25import org.onosproject.store.service.EventuallyConsistentMap;
26import org.onosproject.store.service.EventuallyConsistentMapEvent;
27import org.onosproject.store.service.EventuallyConsistentMapListener;
28import org.onosproject.store.service.StorageService;
29import org.onosproject.store.service.WallClockTimestamp;
30import org.osgi.service.component.annotations.Activate;
31import org.osgi.service.component.annotations.Component;
32import org.osgi.service.component.annotations.Deactivate;
33import org.osgi.service.component.annotations.Reference;
34import org.osgi.service.component.annotations.ReferenceCardinality;
35import org.slf4j.Logger;
36
37import java.math.BigInteger;
38import java.util.concurrent.ConcurrentMap;
39
40import static com.google.common.base.Preconditions.checkNotNull;
41import static org.slf4j.LoggerFactory.getLogger;
42
43/**
44 * Distributed implementation of MasterElectionIdStore.
45 */
46@Component(immediate = true, service = MasterElectionIdStore.class)
47public class DistributedMasterElectionIdStore implements MasterElectionIdStore {
48
49 @Reference(cardinality = ReferenceCardinality.MANDATORY)
50 protected StorageService storageService;
51
52 private static final KryoNamespace SERIALIZER = KryoNamespace.newBuilder()
53 .register(KryoNamespaces.API)
Carmelo Casconec2be50a2019-04-10 00:15:39 -070054 .register(Pair.class)
pierventre381d4542021-01-15 17:27:48 +010055 .register(ImmutablePair.class)
Carmelo Casconec2be50a2019-04-10 00:15:39 -070056 .register(Long.class)
Carmelo Cascone3977ea42019-02-28 13:43:42 -080057 .register(BigInteger.class)
58 .build();
59
60 private final Logger log = getLogger(getClass());
Carmelo Casconec2be50a2019-04-10 00:15:39 -070061 private final EventuallyConsistentMapListener<Pair<DeviceId, Long>, BigInteger> mapListener =
Carmelo Cascone3977ea42019-02-28 13:43:42 -080062 new InternalMapListener();
63
Carmelo Casconec2be50a2019-04-10 00:15:39 -070064 private EventuallyConsistentMap<Pair<DeviceId, Long>, BigInteger> masterElectionIds;
65 private ConcurrentMap<Pair<DeviceId, Long>, MasterElectionIdListener> listeners =
Carmelo Cascone3977ea42019-02-28 13:43:42 -080066 Maps.newConcurrentMap();
67
68 @Activate
69 public void activate() {
Carmelo Casconec2be50a2019-04-10 00:15:39 -070070 listeners = Maps.newConcurrentMap();
71 masterElectionIds = storageService.<Pair<DeviceId, Long>,
72 BigInteger>eventuallyConsistentMapBuilder()
Carmelo Cascone3977ea42019-02-28 13:43:42 -080073 .withName("p4runtime-master-election-ids")
74 .withSerializer(SERIALIZER)
75 .withTimestampProvider((k, v) -> new WallClockTimestamp())
76 .build();
Carmelo Casconec2be50a2019-04-10 00:15:39 -070077 masterElectionIds.addListener(mapListener);
Carmelo Cascone3977ea42019-02-28 13:43:42 -080078 log.info("Started");
79 }
80
81 @Deactivate
82 public void deactivate() {
Carmelo Casconec2be50a2019-04-10 00:15:39 -070083 masterElectionIds.removeListener(mapListener);
84 masterElectionIds.destroy();
85 masterElectionIds = null;
86 listeners.clear();
87 listeners = null;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080088 log.info("Stopped");
89 }
90
91
92 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -070093 public void set(DeviceId deviceId, long p4DeviceId, BigInteger electionId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -080094 checkNotNull(deviceId);
95 checkNotNull(electionId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -070096 masterElectionIds.put(Pair.of(deviceId, p4DeviceId), electionId);
Carmelo Cascone3977ea42019-02-28 13:43:42 -080097 }
98
99 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700100 public BigInteger get(DeviceId deviceId, long p4DeviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800101 checkNotNull(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700102 return masterElectionIds.get(Pair.of(deviceId, p4DeviceId));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800103 }
104
105 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700106 public void remove(DeviceId deviceId, long p4DeviceId) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800107 checkNotNull(deviceId);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700108 masterElectionIds.remove(Pair.of(deviceId, p4DeviceId));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800109 }
110
111 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700112 public void removeAll(DeviceId deviceId) {
113 masterElectionIds.keySet().forEach(k -> {
114 if (k.getLeft().equals(deviceId)) {
115 masterElectionIds.remove(k);
116 }
117 });
118 }
119
120 @Override
121 public void setListener(DeviceId deviceId, long p4DeviceId,
122 MasterElectionIdListener newListener) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800123 checkNotNull(deviceId);
124 checkNotNull(newListener);
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700125 listeners.compute(Pair.of(deviceId, p4DeviceId), (x, existingListener) -> {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800126 if (existingListener == null || existingListener == newListener) {
127 return newListener;
128 } else {
129 log.error("Cannot add listener as one already exist for {}", deviceId);
130 return existingListener;
131 }
132 });
133 }
134
135 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700136 public void unsetListener(DeviceId deviceId, long p4DeviceId) {
137 listeners.remove(Pair.of(deviceId, p4DeviceId));
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800138 }
139
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700140 private class InternalMapListener implements EventuallyConsistentMapListener<Pair<DeviceId, Long>, BigInteger> {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800141 @Override
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700142 public void event(EventuallyConsistentMapEvent<Pair<DeviceId, Long>, BigInteger> event) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800143 final MasterElectionIdListener listener = listeners.get(event.key());
144 if (listener == null) {
145 return;
146 }
147 switch (event.type()) {
148 case PUT:
149 listener.updated(event.value());
150 break;
151 case REMOVE:
152 listener.updated(null);
153 break;
154 default:
155 log.error("Unrecognized map event type {}", event.type());
156 }
157 }
158 }
159}