blob: 29bf15fedd450731ef7971b7e635eb45f81d7e13 [file] [log] [blame]
Yuta HIGUCHI4490a732014-11-18 20:20:30 -08001/*
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 */
16package 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;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080021import com.google.common.collect.ImmutableSet;
22import com.hazelcast.core.EntryAdapter;
23import com.hazelcast.core.EntryEvent;
24import com.hazelcast.core.EntryListener;
25import com.hazelcast.core.IMap;
26import com.hazelcast.core.Member;
27
28import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080031import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080033import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080034import org.onlab.metrics.MetricsService;
35import org.onlab.onos.core.MetricsHelper;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080036import org.onlab.onos.net.intent.Intent;
37import org.onlab.onos.net.intent.IntentEvent;
38import org.onlab.onos.net.intent.IntentId;
39import org.onlab.onos.net.intent.IntentState;
40import org.onlab.onos.net.intent.IntentStore;
41import org.onlab.onos.net.intent.IntentStoreDelegate;
42import org.onlab.onos.store.hz.AbstractHazelcastStore;
43import org.onlab.onos.store.hz.SMap;
44import org.onlab.onos.store.serializers.KryoNamespaces;
45import org.onlab.onos.store.serializers.KryoSerializer;
46import org.onlab.util.KryoNamespace;
47import org.slf4j.Logger;
48
49import java.util.EnumSet;
50import java.util.List;
51import java.util.Map;
52import java.util.Set;
53import java.util.concurrent.ConcurrentHashMap;
54
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -080055import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080056import static org.onlab.onos.net.intent.IntentState.*;
57import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080058import static org.onlab.metrics.MetricsUtil.*;
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080059
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080060@Component(immediate = true, enabled = false)
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080061@Service
62public class HazelcastIntentStore
63 extends AbstractHazelcastStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080064 implements IntentStore, MetricsHelper {
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080065
66 /** Valid parking state, which can transition to INSTALLED. */
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -080067 private static final Set<IntentState> PRE_INSTALLED = EnumSet.of(SUBMITTED, INSTALLED, FAILED);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -080068
69 /** Valid parking state, which can transition to WITHDRAWN. */
70 private static final Set<IntentState> PRE_WITHDRAWN = EnumSet.of(INSTALLED, FAILED);
71
72 private final Logger log = getLogger(getClass());
73
74 // Assumption: IntentId will not have synonyms
75 private SMap<IntentId, Intent> intents;
76 private SMap<IntentId, IntentState> states;
77
78 // Map to store instance local intermediate state transition
79 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
80
81 private SMap<IntentId, List<Intent>> installable;
82
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080083 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected MetricsService metricsService;
85
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -080086 // TODO make this configurable
87 private boolean onlyLogTransitionError = true;
88
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -080089 private Timer createIntentTimer;
90 private Timer removeIntentTimer;
91 private Timer setInstallableIntentsTimer;
92 private Timer getInstallableIntentsTimer;
93 private Timer removeInstalledIntentsTimer;
94 private Timer setStateTimer;
95 private Timer getIntentCountTimer;
96 private Timer getIntentsTimer;
97 private Timer getIntentTimer;
98 private Timer getIntentStateTimer;
99
100 private Timer createResponseTimer(String methodName) {
101 return createTimer("IntentStore", methodName, "responseTime");
102 }
103
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800104 @Override
105 @Activate
106 public void activate() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800107 createIntentTimer = createResponseTimer("createIntent");
108 removeIntentTimer = createResponseTimer("removeIntent");
109 setInstallableIntentsTimer = createResponseTimer("setInstallableIntents");
110 getInstallableIntentsTimer = createResponseTimer("getInstallableIntents");
111 removeInstalledIntentsTimer = createResponseTimer("removeInstalledIntents");
112 setStateTimer = createResponseTimer("setState");
113 getIntentCountTimer = createResponseTimer("getIntentCount");
114 getIntentsTimer = createResponseTimer("getIntents");
115 getIntentTimer = createResponseTimer("getIntent");
116 getIntentStateTimer = createResponseTimer("getIntentState");
117
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800118 // FIXME: We need a way to add serializer for intents which has been plugged-in.
119 // As a short term workaround, relax Kryo config to
120 // registrationRequired=false
121 super.activate();
122 super.serializer = new KryoSerializer() {
123
124 @Override
125 protected void setupKryoPool() {
126 serializerPool = KryoNamespace.newBuilder()
127 .setRegistrationRequired(false)
128 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800129 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
130 .build();
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800131 }
132
133 };
134
135 // TODO: enable near cache, allow read from backup for this IMap
136 IMap<byte[], byte[]> rawIntents = super.theInstance.getMap("intents");
137 intents = new SMap<>(rawIntents , super.serializer);
138
139 // TODO: disable near cache, disable read from backup for this IMap
140 IMap<byte[], byte[]> rawStates = super.theInstance.getMap("intent-states");
141 states = new SMap<>(rawStates , super.serializer);
142 EntryListener<IntentId, IntentState> listener = new RemoteIntentStateListener();
143 states.addEntryListener(listener , false);
144
145 transientStates.clear();
146
147 // TODO: disable near cache, disable read from backup for this IMap
148 IMap<byte[], byte[]> rawInstallables = super.theInstance.getMap("installable-intents");
149 installable = new SMap<>(rawInstallables , super.serializer);
150
151 log.info("Started");
152 }
153
154 @Deactivate
155 public void deactivate() {
156 log.info("Stopped");
157 }
158
159 @Override
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800160 public MetricsService metricsService() {
161 return metricsService;
162 }
163
164 @Override
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800165 public IntentEvent createIntent(Intent intent) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800166 Context timer = startTimer(createIntentTimer);
167 try {
168 Intent existing = intents.putIfAbsent(intent.id(), intent);
169 if (existing != null) {
170 // duplicate, ignore
171 return null;
172 } else {
173 return this.setState(intent, IntentState.SUBMITTED);
174 }
175 } finally {
176 stopTimer(timer);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800177 }
178 }
179
180 @Override
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800181 public void removeIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800182 Context timer = startTimer(removeIntentTimer);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800183 checkState(getIntentState(intentId) == WITHDRAWN,
184 "Intent state for {} is not WITHDRAWN.", intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800185 try {
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800186 intents.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800187 installable.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800188 states.remove(intentId);
189 transientStates.remove(intentId);
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800190 } finally {
191 stopTimer(timer);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800192 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800193 }
194
195 @Override
196 public long getIntentCount() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800197 Context timer = startTimer(getIntentCountTimer);
198 try {
199 return intents.size();
200 } finally {
201 stopTimer(timer);
202 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800203 }
204
205 @Override
206 public Iterable<Intent> getIntents() {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800207 Context timer = startTimer(getIntentsTimer);
208 try {
209 return ImmutableSet.copyOf(intents.values());
210 } finally {
211 stopTimer(timer);
212 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800213 }
214
215 @Override
216 public Intent getIntent(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800217 Context timer = startTimer(getIntentTimer);
218 try {
219 return intents.get(intentId);
220 } finally {
221 stopTimer(timer);
222 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800223 }
224
225 @Override
226 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800227 Context timer = startTimer(getIntentStateTimer);
228 try {
229 final IntentState localState = transientStates.get(id);
230 if (localState != null) {
231 return localState;
232 }
233 return states.get(id);
234 } finally {
235 stopTimer(timer);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800236 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800237 }
238
Yuta HIGUCHIc8f30262014-11-20 19:14:42 -0800239 private void verify(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
240 if (onlyLogTransitionError) {
241 if (!expression) {
242 log.error(errorMessageTemplate.replace("%s", "{}"), errorMessageArgs);
243 }
244 } else {
245 Verify.verify(expression, errorMessageTemplate, errorMessageArgs);
246 }
247 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800248
249 @Override
250 public IntentEvent setState(Intent intent, IntentState state) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800251 Context timer = startTimer(setStateTimer);
252 try {
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800253
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800254 final IntentId id = intent.id();
255 IntentEvent.Type type = null;
256 final IntentState prevParking;
257 boolean transientStateChangeOnly = false;
258
259 // parking state transition
260 switch (state) {
261 case SUBMITTED:
262 prevParking = states.get(id);
263 if (prevParking == null) {
264 IntentState existing = states.putIfAbsent(id, SUBMITTED);
265 verify(existing == null, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
266 } else {
267 verify(prevParking == WITHDRAWN,
268 "Illegal state transition attempted from %s to SUBMITTED",
269 prevParking);
270 boolean updated = states.replace(id, prevParking, SUBMITTED);
271 verify(updated, "Conditional replace %s => %s failed", prevParking, SUBMITTED);
272 }
273 type = IntentEvent.Type.SUBMITTED;
274 break;
275 case INSTALLED:
276 prevParking = states.replace(id, INSTALLED);
277 verify(PRE_INSTALLED.contains(prevParking),
278 "Illegal state transition attempted from %s to INSTALLED",
279 prevParking);
280 type = IntentEvent.Type.INSTALLED;
281 break;
282 case FAILED:
283 prevParking = states.replace(id, FAILED);
284 type = IntentEvent.Type.FAILED;
285 break;
286 case WITHDRAWN:
287 prevParking = states.replace(id, WITHDRAWN);
288 verify(PRE_WITHDRAWN.contains(prevParking),
289 "Illegal state transition attempted from %s to WITHDRAWN",
290 prevParking);
291 type = IntentEvent.Type.WITHDRAWN;
292 break;
293 default:
294 transientStateChangeOnly = true;
295 prevParking = null;
296 break;
Yuta HIGUCHI89a7f472014-11-21 14:50:24 -0800297 }
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800298 if (!transientStateChangeOnly) {
299 log.debug("Parking State change: {} {}=>{}", id, prevParking, state);
300 }
301 // Update instance local state, which includes non-parking state transition
302 final IntentState prevTransient = transientStates.put(id, state);
303 log.debug("Transient State change: {} {}=>{}", id, prevTransient, state);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800304
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800305 if (type == null) {
306 return null;
307 }
308 return new IntentEvent(type, intent);
309 } finally {
310 stopTimer(timer);
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800311 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800312 }
313
314 @Override
315 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800316 Context timer = startTimer(setInstallableIntentsTimer);
317 try {
318 installable.put(intentId, result);
319 } finally {
320 stopTimer(timer);
321 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800322 }
323
324 @Override
325 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800326 Context timer = startTimer(getInstallableIntentsTimer);
327 try {
328 return installable.get(intentId);
329 } finally {
330 stopTimer(timer);
331 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800332 }
333
334 @Override
335 public void removeInstalledIntents(IntentId intentId) {
Yuta HIGUCHIe367fb92014-11-24 22:26:58 -0800336 Context timer = startTimer(removeInstalledIntentsTimer);
337 try {
338 installable.remove(intentId);
339 } finally {
340 stopTimer(timer);
341 }
Yuta HIGUCHI4490a732014-11-18 20:20:30 -0800342 }
343
344 public final class RemoteIntentStateListener extends EntryAdapter<IntentId, IntentState> {
345
346 @Override
347 public void onEntryEvent(EntryEvent<IntentId, IntentState> event) {
348 final Member myself = theInstance.getCluster().getLocalMember();
349 if (!myself.equals(event.getMember())) {
350 // When Intent state was modified by remote node,
351 // clear local transient state.
352 final IntentId intentId = event.getKey();
353 IntentState oldState = transientStates.remove(intentId);
354 if (oldState != null) {
355 log.debug("{} state updated remotely, removing transient state {}",
356 intentId, oldState);
357 }
358 }
359 }
360 }
361}