blob: ef3b80341d6cbdd999deaec1543ba90aaad24c9d [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.intent.impl;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070017
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080018import com.codahale.metrics.Timer;
19import com.codahale.metrics.Timer.Context;
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080020import com.google.common.base.Verify;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080021import com.google.common.cache.CacheBuilder;
22import com.google.common.cache.CacheLoader;
23import com.google.common.cache.LoadingCache;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070024import com.google.common.collect.ImmutableSet;
alshabiba9819bf2014-11-30 18:15:52 -080025import com.google.common.collect.Lists;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070031import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080032import org.onlab.metrics.MetricsService;
Jonathan Hartc0363672015-01-20 16:21:08 -080033import org.onlab.util.KryoNamespace;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.core.MetricsHelper;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080035import org.onosproject.net.intent.BatchWrite;
Jonathan Hartc0363672015-01-20 16:21:08 -080036import org.onosproject.net.intent.BatchWrite.Operation;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.intent.Intent;
38import org.onosproject.net.intent.IntentEvent;
39import org.onosproject.net.intent.IntentId;
40import org.onosproject.net.intent.IntentState;
41import org.onosproject.net.intent.IntentStore;
42import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.store.AbstractStore;
44import org.onosproject.store.serializers.KryoNamespaces;
45import org.onosproject.store.serializers.KryoSerializer;
46import org.onosproject.store.serializers.StoreSerializer;
47import org.onosproject.store.service.BatchWriteRequest;
48import org.onosproject.store.service.BatchWriteRequest.Builder;
49import org.onosproject.store.service.BatchWriteResult;
50import org.onosproject.store.service.DatabaseAdminService;
51import org.onosproject.store.service.DatabaseService;
52import org.onosproject.store.service.impl.CMap;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070053import org.slf4j.Logger;
54
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080055import java.util.ArrayList;
Sho SHIMIZU2bb988b2015-01-20 13:45:35 -080056import java.util.Collections;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080057import java.util.EnumSet;
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080058import java.util.HashSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070059import java.util.List;
60import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080061import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070062import java.util.concurrent.ConcurrentHashMap;
63
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080064import static com.google.common.base.Preconditions.checkArgument;
Jonathan Hartc0363672015-01-20 16:21:08 -080065import static org.onlab.metrics.MetricsUtil.startTimer;
66import static org.onlab.metrics.MetricsUtil.stopTimer;
67import static org.onosproject.net.intent.IntentState.FAILED;
68import static org.onosproject.net.intent.IntentState.INSTALLED;
69import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
70import static org.onosproject.net.intent.IntentState.WITHDRAWN;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070071import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070072
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080073@Component(immediate = true, enabled = false)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070074@Service
75public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080076 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080077 implements IntentStore, MetricsHelper {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070078
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080079 /** Valid parking state, which can transition to INSTALLED. */
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080080 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(INSTALL_REQ, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080081
82 /** Valid parking state, which can transition to WITHDRAWN. */
83 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
84
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080085 private static final Set<IntentState> PARKING = EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAWN, FAILED);
Yuta HIGUCHIf5682452014-12-01 10:17:15 -080086
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070087 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070088
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070089 // Assumption: IntentId will not have synonyms
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080090 private static final String INTENTS_TABLE = "intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080091 private CMap<IntentId, Intent> intents;
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -080092
93 private static final String STATES_TABLE = "intent-states";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080094 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070095
Yuta HIGUCHI65934892014-12-04 17:47:44 -080096 // TODO transient state issue remains for this impl.: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070097 // Map to store instance local intermediate state transition
98 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
99
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800100 private static final String INSTALLABLE_TABLE = "installable-intents";
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800101 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700102
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800103 private LoadingCache<IntentId, String> keyCache;
104
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800105 private StoreSerializer serializer;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected DatabaseAdminService dbAdminService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected DatabaseService dbService;
112
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected MetricsService metricsService;
115
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800116 // TODO make this configurable
117 private boolean onlyLogTransitionError = true;
118
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800119 private Timer getInstallableIntentsTimer;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800120 private Timer getIntentCountTimer;
121 private Timer getIntentsTimer;
122 private Timer getIntentTimer;
123 private Timer getIntentStateTimer;
124
125
126 private Timer createResponseTimer(String methodName) {
127 return createTimer("IntentStore", methodName, "responseTime");
128 }
129
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700130 @Activate
131 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800132 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800133 getIntentCountTimer = createResponseTimer("getIntentCount");
134 getIntentsTimer = createResponseTimer("getIntents");
135 getIntentTimer = createResponseTimer("getIntent");
136 getIntentStateTimer = createResponseTimer("getIntentState");
137
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800138 // We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700139 // As a short term workaround, relax Kryo config to
140 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800141 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700142
143 @Override
144 protected void setupKryoPool() {
145 serializerPool = KryoNamespace.newBuilder()
146 .setRegistrationRequired(false)
147 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800148 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
149 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700150 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700151 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700152
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800153 keyCache = CacheBuilder.newBuilder()
154 .softValues()
155 .build(new CacheLoader<IntentId, String>() {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700156
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800157 @Override
158 public String load(IntentId key) {
159 return key.toString();
160 }
161 });
162
163 intents = new IntentIdMap<>(dbAdminService, dbService, INTENTS_TABLE, serializer);
164
165 states = new IntentIdMap<>(dbAdminService, dbService, STATES_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700166
167 transientStates.clear();
168
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800169 installable = new IntentIdMap<>(dbAdminService, dbService, INSTALLABLE_TABLE, serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700170
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700171 log.info("Started");
172 }
173
174 @Deactivate
175 public void deactivate() {
176 log.info("Stopped");
177 }
178
179 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800180 public MetricsService metricsService() {
181 return metricsService;
182 }
183
184 @Override
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700185 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800186 Context timer = startTimer(getIntentCountTimer);
187 try {
188 return intents.size();
189 } finally {
190 stopTimer(timer);
191 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700192 }
193
194 @Override
195 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800196 Context timer = startTimer(getIntentsTimer);
197 try {
198 return ImmutableSet.copyOf(intents.values());
199 } finally {
200 stopTimer(timer);
201 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700202 }
203
204 @Override
205 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800206 Context timer = startTimer(getIntentTimer);
207 try {
208 return intents.get(intentId);
209 } finally {
210 stopTimer(timer);
211 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700212 }
213
214 @Override
215 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800216 Context timer = startTimer(getIntentStateTimer);
217 try {
218 final IntentState localState = transientStates.get(id);
219 if (localState != null) {
220 return localState;
221 }
222 return states.get(id);
223 } finally {
224 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700225 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700226 }
227
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800228 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
229 if (onlyLogTransitionError) {
230 if (!expression) {
231 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
232 }
233 } else {
234 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
235 }
236 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800237
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800240 Context timer = startTimer(getInstallableIntentsTimer);
241 try {
242 return installable.get(intentId);
243 } finally {
244 stopTimer(timer);
245 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700246 }
247
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800248 protected String strIntentId(IntentId key) {
249 return keyCache.getUnchecked(key);
250 }
251
252 /**
253 * Distributed Map from IntentId to some value.
254 *
255 * @param <V> Map value type
256 */
257 final class IntentIdMap<V> extends CMap<IntentId, V> {
258
259 /**
260 * Creates a IntentIdMap instance.
261 *
262 * @param dbAdminService DatabaseAdminService to use for this instance
263 * @param dbService DatabaseService to use for this instance
264 * @param tableName table which this Map corresponds to
265 * @param serializer Value serializer
266 */
267 public IntentIdMap(DatabaseAdminService dbAdminService,
268 DatabaseService dbService,
269 String tableName,
270 StoreSerializer serializer) {
271 super(dbAdminService, dbService, tableName, serializer);
272 }
273
274 @Override
275 protected String sK(IntentId key) {
276 return strIntentId(key);
277 }
278 }
279
280 @Override
281 public List<Operation> batchWrite(BatchWrite batch) {
Sho SHIMIZU2bb988b2015-01-20 13:45:35 -0800282 if (batch.isEmpty()) {
283 return Collections.emptyList();
284 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800285
286 List<Operation> failed = new ArrayList<>();
287 final Builder builder = BatchWriteRequest.newBuilder();
alshabiba9819bf2014-11-30 18:15:52 -0800288 List<IntentEvent> events = Lists.newArrayList();
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800289
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800290 final Set<IntentId> transitionedToParking = new HashSet<>();
291
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800292 for (Operation op : batch.operations()) {
293 switch (op.type()) {
294 case CREATE_INTENT:
295 checkArgument(op.args().size() == 1,
296 "CREATE_INTENT takes 1 argument. %s", op);
297 Intent intent = op.arg(0);
298 builder.putIfAbsent(INTENTS_TABLE, strIntentId(intent.id()), serializer.encode(intent));
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800299 builder.putIfAbsent(STATES_TABLE, strIntentId(intent.id()), serializer.encode(INSTALL_REQ));
300 events.add(IntentEvent.getEvent(INSTALL_REQ, intent));
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800301 break;
302
303 case REMOVE_INTENT:
304 checkArgument(op.args().size() == 1,
305 "REMOVE_INTENT takes 1 argument. %s", op);
306 IntentId intentId = (IntentId) op.arg(0);
307 builder.remove(INTENTS_TABLE, strIntentId(intentId));
308 builder.remove(STATES_TABLE, strIntentId(intentId));
309 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
310 break;
311
312 case SET_STATE:
313 checkArgument(op.args().size() == 2,
314 "SET_STATE takes 2 arguments. %s", op);
315 intent = op.arg(0);
316 IntentState newState = op.arg(1);
317 builder.put(STATES_TABLE, strIntentId(intent.id()), serializer.encode(newState));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800318 if (PARKING.contains(newState)) {
319 transitionedToParking.add(intent.id());
Yuta HIGUCHI5cd352d2014-12-01 20:16:02 -0800320 events.add(IntentEvent.getEvent(newState, intent));
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800321 } else {
322 transitionedToParking.remove(intent.id());
323 }
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800324 break;
325
326 case SET_INSTALLABLE:
327 checkArgument(op.args().size() == 2,
328 "SET_INSTALLABLE takes 2 arguments. %s", op);
329 intentId = op.arg(0);
330 List<Intent> installableIntents = op.arg(1);
331 builder.put(INSTALLABLE_TABLE, strIntentId(intentId), serializer.encode(installableIntents));
332 break;
333
334 case REMOVE_INSTALLED:
335 checkArgument(op.args().size() == 1,
336 "REMOVE_INSTALLED takes 1 argument. %s", op);
337 intentId = op.arg(0);
338 builder.remove(INSTALLABLE_TABLE, strIntentId(intentId));
339 break;
340
341 default:
342 log.warn("Unknown Operation encountered: {}", op);
343 failed.add(op);
344 break;
345 }
346 }
347
348 BatchWriteResult batchWriteResult = dbService.batchWrite(builder.build());
349 if (batchWriteResult.isSuccessful()) {
350 // no-failure (except for invalid input)
Yuta HIGUCHIf5682452014-12-01 10:17:15 -0800351 transitionedToParking.forEach((intentId) -> transientStates.remove(intentId));
alshabiba9819bf2014-11-30 18:15:52 -0800352 notifyDelegate(events);
Yuta HIGUCHIa94c6e82014-11-28 18:49:54 -0800353 return failed;
354 } else {
355 // everything failed
356 return batch.operations();
357 }
358 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700359}