blob: 5974b622157460d1c47ee289fdee3b13f7743bc7 [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 */
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070016package org.onlab.onos.store.intent.impl;
17
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;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070021import com.google.common.collect.ImmutableSet;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070022
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070028import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080029import org.onlab.metrics.MetricsService;
30import org.onlab.onos.core.MetricsHelper;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070031import org.onlab.onos.net.intent.Intent;
32import org.onlab.onos.net.intent.IntentEvent;
33import org.onlab.onos.net.intent.IntentId;
34import org.onlab.onos.net.intent.IntentState;
35import org.onlab.onos.net.intent.IntentStore;
36import org.onlab.onos.net.intent.IntentStoreDelegate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080037import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070038import org.onlab.onos.store.serializers.KryoNamespaces;
39import org.onlab.onos.store.serializers.KryoSerializer;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080040import org.onlab.onos.store.serializers.StoreSerializer;
41import org.onlab.onos.store.service.DatabaseAdminService;
42import org.onlab.onos.store.service.DatabaseService;
43import org.onlab.onos.store.service.impl.CMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070044import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070045import org.slf4j.Logger;
46
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080047import java.util.EnumSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070048import java.util.List;
49import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080050import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070051import java.util.concurrent.ConcurrentHashMap;
52
53import static org.onlab.onos.net.intent.IntentState.*;
54import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080055import static org.onlab.metrics.MetricsUtil.*;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070056
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080057@Component(immediate = true, enabled = true)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070058@Service
59public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080060 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080061 implements IntentStore, MetricsHelper {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070062
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080063 /** Valid parking state, which can transition to INSTALLED. */
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -080064 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(SUBMITTED, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080065
66 /** Valid parking state, which can transition to WITHDRAWN. */
67 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
68
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070069 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070070
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070071 // Assumption: IntentId will not have synonyms
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080072 private CMap<IntentId, Intent> intents;
73 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070074
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080075 // TODO left behind transient state issue: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070076 // Map to store instance local intermediate state transition
77 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
78
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080079 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070080
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080081 private StoreSerializer serializer;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DatabaseAdminService dbAdminService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected DatabaseService dbService;
88
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected MetricsService metricsService;
91
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080092 // TODO make this configurable
93 private boolean onlyLogTransitionError = true;
94
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080095 private Timer createIntentTimer;
96 private Timer removeIntentTimer;
97 private Timer setInstallableIntentsTimer;
98 private Timer getInstallableIntentsTimer;
99 private Timer removeInstalledIntentsTimer;
100 private Timer setStateTimer;
101 private Timer getIntentCountTimer;
102 private Timer getIntentsTimer;
103 private Timer getIntentTimer;
104 private Timer getIntentStateTimer;
105
106
107 private Timer createResponseTimer(String methodName) {
108 return createTimer("IntentStore", methodName, "responseTime");
109 }
110
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700111 @Activate
112 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800113 createIntentTimer = createResponseTimer("createIntent");
114 removeIntentTimer = createResponseTimer("removeIntent");
115 setInstallableIntentsTimer = createResponseTimer("setInstallableIntents");
116 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
117 removeInstalledIntentsTimer = createResponseTimer("removeInstalledIntents");
118 setStateTimer = createResponseTimer("setState");
119 getIntentCountTimer = createResponseTimer("getIntentCount");
120 getIntentsTimer = createResponseTimer("getIntents");
121 getIntentTimer = createResponseTimer("getIntent");
122 getIntentStateTimer = createResponseTimer("getIntentState");
123
Yuta HIGUCHI71fa4932014-10-28 22:27:49 -0700124 // FIXME: We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700125 // As a short term workaround, relax Kryo config to
126 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800127 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700128
129 @Override
130 protected void setupKryoPool() {
131 serializerPool = KryoNamespace.newBuilder()
132 .setRegistrationRequired(false)
133 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800134 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
135 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700136 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700137 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700138
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800139 intents = new CMap<>(dbAdminService, dbService, "intents", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700140
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800141 states = new CMap<>(dbAdminService, dbService, "intent-states", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700142
143 transientStates.clear();
144
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800145 installable = new CMap<>(dbAdminService, dbService, "installable-intents", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700146
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700147 log.info("Started");
148 }
149
150 @Deactivate
151 public void deactivate() {
152 log.info("Stopped");
153 }
154
155 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800156 public MetricsService metricsService() {
157 return metricsService;
158 }
159
160 @Override
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700161 public IntentEvent createIntent(Intent intent) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800162 Context timer = startTimer(createIntentTimer);
163 try {
164 boolean absent = intents.putIfAbsent(intent.id(), intent);
165 if (!absent) {
166 // duplicate, ignore
167 return null;
168 } else {
169 return this.setState(intent, IntentState.SUBMITTED);
170 }
171 } finally {
172 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700173 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700174 }
175
176 @Override
177 public IntentEvent removeIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800178 Context timer = startTimer(removeIntentTimer);
179 try {
180 Intent intent = intents.remove(intentId);
181 installable.remove(intentId);
182 if (intent == null) {
183 // was already removed
184 return null;
185 }
186 IntentEvent event = this.setState(intent, WITHDRAWN);
187 states.remove(intentId);
188 transientStates.remove(intentId);
189 // TODO: Should we callremoveInstalledIntents if this Intent was
190 return event;
191 } finally {
192 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700193 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700194 }
195
196 @Override
197 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800198 Context timer = startTimer(getIntentCountTimer);
199 try {
200 return intents.size();
201 } finally {
202 stopTimer(timer);
203 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700204 }
205
206 @Override
207 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800208 Context timer = startTimer(getIntentsTimer);
209 try {
210 return ImmutableSet.copyOf(intents.values());
211 } finally {
212 stopTimer(timer);
213 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700214 }
215
216 @Override
217 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800218 Context timer = startTimer(getIntentTimer);
219 try {
220 return intents.get(intentId);
221 } finally {
222 stopTimer(timer);
223 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700224 }
225
226 @Override
227 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800228 Context timer = startTimer(getIntentStateTimer);
229 try {
230 final IntentState localState = transientStates.get(id);
231 if (localState != null) {
232 return localState;
233 }
234 return states.get(id);
235 } finally {
236 stopTimer(timer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700237 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700238 }
239
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800240 // FIXME temporary workaround until we fix our state machine
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800241 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
242 if (onlyLogTransitionError) {
243 if (!expression) {
244 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
245 }
246 } else {
247 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
248 }
249 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800250
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700251 @Override
252 public IntentEvent setState(Intent intent, IntentState state) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800253 Context timer = startTimer(setStateTimer);
254 try {
255 final IntentId id = intent.id();
256 IntentEvent.Type evtType = null;
257 final IntentState prevParking;
258 boolean transitionedToParking = true;
259 boolean updated;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700260
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800261 // parking state transition
262 switch (state) {
263 case SUBMITTED:
264 prevParking = states.get(id);
265 if (prevParking == null) {
266 updated = states.putIfAbsent(id, SUBMITTED);
267 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
268 } else {
269 verify(prevParking == WITHDRAWN,
270 "Illegal state transition attempted from %s to SUBMITTED",
271 prevParking);
272 updated = states.replace(id, prevParking, SUBMITTED);
273 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
274 }
275 evtType = IntentEvent.Type.SUBMITTED;
276 break;
277
278 case INSTALLED:
279 prevParking = states.get(id);
280 verify(PRE_INSTALLED.contains(prevParking),
281 "Illegal state transition attempted from %s to INSTALLED",
282 prevParking);
283 updated = states.replace(id, prevParking, INSTALLED);
284 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALLED);
285 evtType = IntentEvent.Type.INSTALLED;
286 break;
287
288 case FAILED:
289 prevParking = states.get(id);
290 updated = states.replace(id, prevParking, FAILED);
291 verify(updated, "Conditional replace %s => %s failed", prevParking, FAILED);
292 evtType = IntentEvent.Type.FAILED;
293 break;
294
295 case WITHDRAWN:
296 prevParking = states.get(id);
297 verify(PRE_WITHDRAWN.contains(prevParking),
298 "Illegal state transition attempted from %s to WITHDRAWN",
299 prevParking);
300 updated = states.replace(id, prevParking, WITHDRAWN);
301 verify(updated, "Conditional replace %s => %s failed", prevParking, WITHDRAWN);
302 evtType = IntentEvent.Type.WITHDRAWN;
303 break;
304
305 default:
306 transitionedToParking = false;
307 prevParking = null;
308 break;
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800309 }
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800310 if (transitionedToParking) {
311 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
312 // remove instance local state
313 transientStates.remove(id);
314 } else {
315 // Update instance local state, which includes non-parking state transition
316 final IntentState prevTransient = transientStates.put(id, state);
317 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
318 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800319
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800320 if (evtType == null) {
321 return null;
322 }
323 return new IntentEvent(evtType, intent);
324 } finally {
325 stopTimer(timer);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700326 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700327 }
328
329 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700330 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800331 Context timer = startTimer(setInstallableIntentsTimer);
332 try {
333 installable.put(intentId, result);
334 } finally {
335 stopTimer(timer);
336 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700337 }
338
339 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700340 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800341 Context timer = startTimer(getInstallableIntentsTimer);
342 try {
343 return installable.get(intentId);
344 } finally {
345 stopTimer(timer);
346 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700347 }
348
349 @Override
350 public void removeInstalledIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800351 Context timer = startTimer(removeInstalledIntentsTimer);
352 try {
353 installable.remove(intentId);
354 } finally {
355 stopTimer(timer);
356 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700357 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700358}