blob: d786a6c9963615f0a459342c4bd705056d51709b [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
Thomas Vachuskab97cf282014-10-20 23:31:12 -070018import com.google.common.collect.ImmutableSet;
Yuta HIGUCHIfded1b32014-11-09 23:54:03 -080019import com.hazelcast.core.EntryAdapter;
20import com.hazelcast.core.EntryEvent;
21import com.hazelcast.core.EntryListener;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070022import com.hazelcast.core.IMap;
Yuta HIGUCHIfded1b32014-11-09 23:54:03 -080023import com.hazelcast.core.Member;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070024
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070029import org.onlab.onos.net.intent.Intent;
30import org.onlab.onos.net.intent.IntentEvent;
31import org.onlab.onos.net.intent.IntentId;
32import org.onlab.onos.net.intent.IntentState;
33import org.onlab.onos.net.intent.IntentStore;
34import org.onlab.onos.net.intent.IntentStoreDelegate;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070035import org.onlab.onos.store.hz.AbstractHazelcastStore;
36import org.onlab.onos.store.hz.SMap;
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070037import org.onlab.onos.store.serializers.KryoNamespaces;
38import org.onlab.onos.store.serializers.KryoSerializer;
39import org.onlab.util.KryoNamespace;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070040import org.slf4j.Logger;
41
Thomas Vachuskab97cf282014-10-20 23:31:12 -070042import java.util.List;
43import java.util.Map;
44import java.util.concurrent.ConcurrentHashMap;
45
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070046import static com.google.common.base.Verify.verify;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070047import static org.onlab.onos.net.intent.IntentState.*;
48import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070049
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070050@Component(immediate = true)
51@Service
52public class DistributedIntentStore
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070053 extends AbstractHazelcastStore<IntentEvent, IntentStoreDelegate>
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070054 implements IntentStore {
55
56 private final Logger log = getLogger(getClass());
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070057
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070058 // Assumption: IntentId will not have synonyms
59 private SMap<IntentId, Intent> intents;
60 private SMap<IntentId, IntentState> states;
61
62 // Map to store instance local intermediate state transition
63 private transient Map<IntentId, IntentState> transientStates = new ConcurrentHashMap<>();
64
65 private SMap<IntentId, List<Intent>> installable;
66
67 @Override
Yuta HIGUCHI406a5652014-10-20 22:18:16 -070068 @Activate
69 public void activate() {
Yuta HIGUCHI71fa4932014-10-28 22:27:49 -070070 // FIXME: We need a way to add serializer for intents which has been plugged-in.
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070071 // As a short term workaround, relax Kryo config to
72 // registrationRequired=false
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070073 super.activate();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070074 super.serializer = new KryoSerializer() {
75
76 @Override
77 protected void setupKryoPool() {
78 serializerPool = KryoNamespace.newBuilder()
79 .setRegistrationRequired(false)
80 .register(KryoNamespaces.API)
81 .build()
82 .populate(1);
83 }
84
85 };
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070086
87 // TODO: enable near cache, allow read from backup for this IMap
88 IMap<byte[], byte[]> rawIntents = super.theInstance.getMap("intents");
89 intents = new SMap<>(rawIntents , super.serializer);
90
91 // TODO: disable near cache, disable read from backup for this IMap
92 IMap<byte[], byte[]> rawStates = super.theInstance.getMap("intent-states");
93 states = new SMap<>(rawStates , super.serializer);
Yuta HIGUCHIfded1b32014-11-09 23:54:03 -080094 EntryListener<IntentId, IntentState> listener = new RemoteIntentStateListener();
95 states.addEntryListener(listener , false);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -070096
97 transientStates.clear();
98
99 // TODO: disable near cache, disable read from backup for this IMap
100 IMap<byte[], byte[]> rawInstallables = super.theInstance.getMap("installable-intents");
101 installable = new SMap<>(rawInstallables , super.serializer);
102
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700103 log.info("Started");
104 }
105
106 @Deactivate
107 public void deactivate() {
108 log.info("Stopped");
109 }
110
111 @Override
112 public IntentEvent createIntent(Intent intent) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700113 Intent existing = intents.putIfAbsent(intent.id(), intent);
114 if (existing != null) {
115 // duplicate, ignore
116 return null;
117 } else {
118 return this.setState(intent, IntentState.SUBMITTED);
119 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700120 }
121
122 @Override
123 public IntentEvent removeIntent(IntentId intentId) {
124 Intent intent = intents.remove(intentId);
125 installable.remove(intentId);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700126 if (intent == null) {
127 // was already removed
128 return null;
129 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700130 IntentEvent event = this.setState(intent, WITHDRAWN);
131 states.remove(intentId);
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700132 transientStates.remove(intentId);
133 // TODO: Should we callremoveInstalledIntents if this Intent was
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700134 return event;
135 }
136
137 @Override
138 public long getIntentCount() {
139 return intents.size();
140 }
141
142 @Override
143 public Iterable<Intent> getIntents() {
144 return ImmutableSet.copyOf(intents.values());
145 }
146
147 @Override
148 public Intent getIntent(IntentId intentId) {
149 return intents.get(intentId);
150 }
151
152 @Override
153 public IntentState getIntentState(IntentId id) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700154 final IntentState localState = transientStates.get(id);
155 if (localState != null) {
156 return localState;
157 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700158 return states.get(id);
159 }
160
161 @Override
162 public IntentEvent setState(Intent intent, IntentState state) {
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700163 final IntentId id = intent.id();
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700164 IntentEvent.Type type = null;
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700165 IntentState prev = null;
166 boolean transientStateChangeOnly = false;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700167
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700168 // TODO: enable sanity checking if Debug enabled, etc.
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700169 switch (state) {
170 case SUBMITTED:
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700171 prev = states.putIfAbsent(id, SUBMITTED);
172 verify(prev == null, "Illegal state transition attempted from %s to SUBMITTED", prev);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700173 type = IntentEvent.Type.SUBMITTED;
174 break;
175 case INSTALLED:
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700176 // parking state transition
177 prev = states.replace(id, INSTALLED);
178 verify(prev != null, "Illegal state transition attempted from non-SUBMITTED to INSTALLED");
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700179 type = IntentEvent.Type.INSTALLED;
180 break;
181 case FAILED:
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700182 prev = states.replace(id, FAILED);
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700183 type = IntentEvent.Type.FAILED;
184 break;
185 case WITHDRAWN:
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700186 prev = states.replace(id, WITHDRAWN);
187 verify(prev != null, "Illegal state transition attempted from non-WITHDRAWING to WITHDRAWN");
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700188 type = IntentEvent.Type.WITHDRAWN;
189 break;
190 default:
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700191 transientStateChangeOnly = true;
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700192 break;
193 }
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700194 if (!transientStateChangeOnly) {
195 log.debug("Parking State change: {} {}=>{}", id, prev, state);
196 }
197 // Update instance local state, which includes non-parking state transition
198 prev = transientStates.put(id, state);
199 log.debug("Transient State change: {} {}=>{}", id, prev, state);
200
Pavlin Radoslavovc8ccbd92014-10-22 09:59:37 -0700201 if (type == null) {
202 return null;
203 }
204 return new IntentEvent(type, intent);
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700205 }
206
207 @Override
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700208 public void setInstallableIntents(IntentId intentId, List<Intent> result) {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700209 installable.put(intentId, result);
210 }
211
212 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700213 public List<Intent> getInstallableIntents(IntentId intentId) {
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700214 return installable.get(intentId);
215 }
216
217 @Override
218 public void removeInstalledIntents(IntentId intentId) {
219 installable.remove(intentId);
220 }
221
Yuta HIGUCHIfded1b32014-11-09 23:54:03 -0800222 public final class RemoteIntentStateListener extends EntryAdapter<IntentId, IntentState> {
223
224 @Override
225 public void onEntryEvent(EntryEvent<IntentId, IntentState> event) {
226 final Member myself = theInstance.getCluster().getLocalMember();
227 if (!myself.equals(event.getMember())) {
228 // When Intent state was modified by remote node,
229 // clear local transient state.
230 final IntentId intentId = event.getKey();
231 IntentState oldState = transientStates.remove(intentId);
232 if (oldState != null) {
233 log.debug("{} state updated remotely, removing transient state {}",
234 intentId, oldState);
235 }
236 }
237 }
238 }
Yuta HIGUCHI406a5652014-10-20 22:18:16 -0700239}