blob: aa060a9ddc5f32c2a846fc3bd7a9180687e8bb4c [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 HIGUCHIc8f30262014-11-20 19:14:42 -080018import com.google.common.base.Verify;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070019import com.google.common.collect.ImmutableSet;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070020
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070026import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070027import org.onlab.onos.net.intent.Intent;
28import org.onlab.onos.net.intent.IntentEvent;
29import org.onlab.onos.net.intent.IntentId;
30import org.onlab.onos.net.intent.IntentState;
31import org.onlab.onos.net.intent.IntentStore;
32import org.onlab.onos.net.intent.IntentStoreDelegate;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080033import org.onlab.onos.store.AbstractStore;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070034import org.onlab.onos.store.serializers.KryoNamespaces;
35import org.onlab.onos.store.serializers.KryoSerializer;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080036import org.onlab.onos.store.serializers.StoreSerializer;
37import org.onlab.onos.store.service.DatabaseAdminService;
38import org.onlab.onos.store.service.DatabaseService;
39import org.onlab.onos.store.service.impl.CMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070040import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070041import org.slf4j.Logger;
42
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080043import java.util.EnumSet;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070044import java.util.List;
45import java.util.Map;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080046import java.util.Set;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070047import java.util.concurrent.ConcurrentHashMap;
48
49import static org.onlab.onos.net.intent.IntentState.*;
50import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070051
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080052@Component(immediate = true, enabled = true)
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070053@Service
54public class DistributedIntentStore
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080055 extends AbstractStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070056 implements IntentStore {
57
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080058 /** Valid parking state, which can transition to INSTALLED. */
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -080059 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(SUBMITTED, INSTALLED, FAILED);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -080060
61 /** Valid parking state, which can transition to WITHDRAWN. */
62 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
63
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070064 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070065
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070066 // Assumption: IntentId will not have synonyms
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080067 private CMap<IntentId, Intent> intents;
68 private CMap<IntentId, IntentState> states;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070069
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080070 // TODO left behind transient state issue: ONOS-103
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070071 // Map to store instance local intermediate state transition
72 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
73
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080074 private CMap<IntentId, List<Intent>> installable;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070075
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080076 private StoreSerializer serializer;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected DatabaseAdminService dbAdminService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected DatabaseService dbService;
83
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080084 // TODO make this configurable
85 private boolean onlyLogTransitionError = true;
86
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070087 @Activate
88 public void activate() {
Yuta HIGUCHI71fa4932014-10-28 22:27:49 -070089 // FIXME: We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070090 // As a short term workaround, relax Kryo config to
91 // registrationRequired=false
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080092 serializer = new KryoSerializer() {
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070093
94 @Override
95 protected void setupKryoPool() {
96 serializerPool = KryoNamespace.newBuilder()
97 .setRegistrationRequired(false)
98 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080099 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
100 .build();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700101 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700102 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700103
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800104 intents = new CMap<>(dbAdminService, dbService, "intents", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700105
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800106 states = new CMap<>(dbAdminService, dbService, "intent-states", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700107
108 transientStates.clear();
109
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800110 installable = new CMap<>(dbAdminService, dbService, "installable-intents", serializer);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700111
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
117 log.info("Stopped");
118 }
119
120 @Override
121 public IntentEvent createIntent(Intent intent) {
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800122 boolean absent = intents.putIfAbsent(intent.id(), intent);
123 if (!absent) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700124 // duplicate, ignore
125 return null;
126 } else {
127 return this.setState(intent, IntentState.SUBMITTED);
128 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700129 }
130
131 @Override
132 public IntentEvent removeIntent(IntentId intentId) {
133 Intent intent = intents.remove(intentId);
134 installable.remove(intentId);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700135 if (intent == null) {
136 // was already removed
137 return null;
138 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700139 IntentEvent event = this.setState(intent, WITHDRAWN);
140 states.remove(intentId);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700141 transientStates.remove(intentId);
142 // TODO: Should we callremoveInstalledIntents if this Intent was
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700143 return event;
144 }
145
146 @Override
147 public long getIntentCount() {
148 return intents.size();
149 }
150
151 @Override
152 public Iterable<Intent> getIntents() {
153 return ImmutableSet.copyOf(intents.values());
154 }
155
156 @Override
157 public Intent getIntent(IntentId intentId) {
158 return intents.get(intentId);
159 }
160
161 @Override
162 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700163 final IntentState localState = transientStates.get(id);
164 if (localState != null) {
165 return localState;
166 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700167 return states.get(id);
168 }
169
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800170 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
171 if (onlyLogTransitionError) {
172 if (!expression) {
173 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
174 }
175 } else {
176 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
177 }
178 }
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800179
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700180 @Override
181 public IntentEvent setState(Intent intent, IntentState state) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700182 final IntentId id = intent.id();
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800183 IntentEvent.Type evtType = null;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800184 final IntentState prevParking;
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800185 boolean transitionedToParking = true;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800186 boolean updated;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700187
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800188 // parking state transition
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700189 switch (state) {
190 case SUBMITTED:
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800191 prevParking = states.get(id);
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800192 if (prevParking == null) {
193 updated = states.putIfAbsent(id, SUBMITTED);
194 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
195 } else {
196 verify(prevParking == WITHDRAWN,
197 "Illegal state transition attempted from %s to SUBMITTED",
198 prevParking);
199 updated = states.replace(id, prevParking, SUBMITTED);
200 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
201 }
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800202 evtType = IntentEvent.Type.SUBMITTED;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700203 break;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800204
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700205 case INSTALLED:
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800206 prevParking = states.get(id);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800207 verify(PRE_INSTALLED.contains(prevParking),
208 "Illegal state transition attempted from %s to INSTALLED",
209 prevParking);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800210 updated = states.replace(id, prevParking, INSTALLED);
211 verify(updated, "Conditional replace %s => %s failed", prevParking, INSTALLED);
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800212 evtType = IntentEvent.Type.INSTALLED;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700213 break;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800214
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700215 case FAILED:
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800216 prevParking = states.get(id);
217 updated = states.replace(id, prevParking, FAILED);
218 verify(updated, "Conditional replace %s => %s failed", prevParking, FAILED);
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800219 evtType = IntentEvent.Type.FAILED;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700220 break;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800221
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700222 case WITHDRAWN:
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800223 prevParking = states.get(id);
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800224 verify(PRE_WITHDRAWN.contains(prevParking),
225 "Illegal state transition attempted from %s to WITHDRAWN",
226 prevParking);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800227 updated = states.replace(id, prevParking, WITHDRAWN);
228 verify(updated, "Conditional replace %s => %s failed", prevParking, WITHDRAWN);
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800229 evtType = IntentEvent.Type.WITHDRAWN;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700230 break;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800231
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700232 default:
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800233 transitionedToParking = false;
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800234 prevParking = null;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700235 break;
236 }
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800237 if (transitionedToParking) {
Yuta HIGUCHI9a2e18a2014-11-18 16:41:58 -0800238 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800239 // remove instance local state
240 transientStates.remove(id);
241 } else {
242 // Update instance local state, which includes non-parking state transition
243 final IntentState prevTransient = transientStates.put(id, state);
244 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700245 }
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700246
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800247 if (evtType == null) {
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700248 return null;
249 }
Yuta HIGUCHI51b8f6a2014-11-22 03:05:51 -0800250 return new IntentEvent(evtType, intent);
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700251 }
252
253 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700254 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700255 installable.put(intentId, result);
256 }
257
258 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700259 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700260 return installable.get(intentId);
261 }
262
263 @Override
264 public void removeInstalledIntents(IntentId intentId) {
265 installable.remove(intentId);
266 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700267}