blob: d81435fd2e67734b8cc38e2c80070d3497e9e803 [file] [log] [blame]
Jonathan Hart5573d322015-01-21 10:13:25 -08001/*
2 * Copyright 2015 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.onosproject.store.intent.impl;
17
Jonathan Hart5573d322015-01-21 10:13:25 -080018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
24import org.onlab.util.KryoNamespace;
25import org.onosproject.cluster.ClusterService;
Jonathan Hart5573d322015-01-21 10:13:25 -080026import org.onosproject.net.intent.BatchWrite;
27import org.onosproject.net.intent.Intent;
Jonathan Hart74c83132015-02-02 18:37:57 -080028import org.onosproject.net.intent.IntentData;
Jonathan Hart5573d322015-01-21 10:13:25 -080029import org.onosproject.net.intent.IntentEvent;
Jonathan Hart5573d322015-01-21 10:13:25 -080030import org.onosproject.net.intent.IntentState;
31import org.onosproject.net.intent.IntentStore;
32import org.onosproject.net.intent.IntentStoreDelegate;
Jonathan Hart74c83132015-02-02 18:37:57 -080033import org.onosproject.net.intent.Key;
Jonathan Hart5573d322015-01-21 10:13:25 -080034import org.onosproject.store.AbstractStore;
Jonathan Hart5573d322015-01-21 10:13:25 -080035import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Jonathan Hart539a6462015-01-27 17:05:43 -080036import org.onosproject.store.impl.EventuallyConsistentMap;
37import org.onosproject.store.impl.EventuallyConsistentMapEvent;
38import org.onosproject.store.impl.EventuallyConsistentMapImpl;
39import org.onosproject.store.impl.EventuallyConsistentMapListener;
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080040import org.onosproject.store.impl.MultiValuedTimestamp;
41import org.onosproject.store.impl.SystemClockTimestamp;
Jonathan Hart539a6462015-01-27 17:05:43 -080042import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart5573d322015-01-21 10:13:25 -080043import org.slf4j.Logger;
44
Jonathan Hart5573d322015-01-21 10:13:25 -080045import java.util.List;
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080046import java.util.stream.Collectors;
Jonathan Hart5573d322015-01-21 10:13:25 -080047
Jonathan Hart07e58be2015-02-12 09:57:16 -080048import static org.onosproject.net.intent.IntentState.FAILED;
49import static org.onosproject.net.intent.IntentState.INSTALLED;
50import static org.onosproject.net.intent.IntentState.INSTALLING;
51import static org.onosproject.net.intent.IntentState.WITHDRAWING;
52import static org.onosproject.net.intent.IntentState.WITHDRAWN;
Jonathan Hart5573d322015-01-21 10:13:25 -080053import static org.slf4j.LoggerFactory.getLogger;
54
55/**
56 * Manages inventory of Intents in a distributed data store that uses optimistic
57 * replication and gossip based techniques.
58 */
Jonathan Hart07e58be2015-02-12 09:57:16 -080059@Component(immediate = false, enabled = true)
Jonathan Hart5573d322015-01-21 10:13:25 -080060@Service
61public class GossipIntentStore
62 extends AbstractStore<IntentEvent, IntentStoreDelegate>
63 implements IntentStore {
64
65 private final Logger log = getLogger(getClass());
66
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080067 // Map of intent key => current intent state
68 private EventuallyConsistentMap<Key, IntentData> currentState;
Jonathan Hart5573d322015-01-21 10:13:25 -080069
Jonathan Hart74c83132015-02-02 18:37:57 -080070 // Map of intent key => pending intent operation
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080071 private EventuallyConsistentMap<Key, IntentData> pending;
Jonathan Hart74c83132015-02-02 18:37:57 -080072
Jonathan Hart5573d322015-01-21 10:13:25 -080073 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected ClusterCommunicationService clusterCommunicator;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected ClusterService clusterService;
78
Jonathan Hart74c83132015-02-02 18:37:57 -080079 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected PartitionService partitionService;
81
Jonathan Hart5573d322015-01-21 10:13:25 -080082 @Activate
83 public void activate() {
Jonathan Hart539a6462015-01-27 17:05:43 -080084 KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder()
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080085 .register(KryoNamespaces.API)
86 .register(IntentData.class)
87 .register(MultiValuedTimestamp.class)
88 .register(SystemClockTimestamp.class);
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080089
90 currentState = new EventuallyConsistentMapImpl<>("intent-current",
91 clusterService,
92 clusterCommunicator,
93 intentSerializer,
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080094 new IntentDataLogicalClockManager<>());
Jonathan Hart5573d322015-01-21 10:13:25 -080095
Jonathan Hart74c83132015-02-02 18:37:57 -080096 pending = new EventuallyConsistentMapImpl<>("intent-pending",
97 clusterService,
98 clusterCommunicator,
99 intentSerializer, // TODO
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800100 new IntentDataClockManager<>());
Jonathan Hart74c83132015-02-02 18:37:57 -0800101
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800102 currentState.addListener(new InternalIntentStatesListener());
Jonathan Hart74c83132015-02-02 18:37:57 -0800103 pending.addListener(new InternalPendingListener());
Jonathan Hart5573d322015-01-21 10:13:25 -0800104
105 log.info("Started");
106 }
107
108 @Deactivate
109 public void deactivate() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800110 currentState.destroy();
Jonathan Hart74c83132015-02-02 18:37:57 -0800111 pending.destroy();
Jonathan Hart5573d322015-01-21 10:13:25 -0800112
113 log.info("Stopped");
114 }
115
116 @Override
117 public long getIntentCount() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800118 return currentState.size();
Jonathan Hart5573d322015-01-21 10:13:25 -0800119 }
120
121 @Override
122 public Iterable<Intent> getIntents() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800123 return currentState.values().stream()
124 .map(IntentData::intent)
125 .collect(Collectors.toList());
Jonathan Hart5573d322015-01-21 10:13:25 -0800126 }
127
128 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800129 public IntentState getIntentState(Key intentKey) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800130 IntentData data = currentState.get(intentKey);
131 if (data != null) {
132 return data.state();
133 }
134 return null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800135 }
136
137 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800138 public List<Intent> getInstallableIntents(Key intentKey) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800139 IntentData data = currentState.get(intentKey);
140 if (data != null) {
141 return data.installables();
142 }
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800143 return null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800144 }
145
146 @Override
Jonathan Hart5573d322015-01-21 10:13:25 -0800147 public List<BatchWrite.Operation> batchWrite(BatchWrite batch) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800148 // Deprecated
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800149 return null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800150 }
151
Jonathan Hart07e58be2015-02-12 09:57:16 -0800152 private IntentData copyData(IntentData original) {
153 if (original == null) {
154 return null;
155 }
156 IntentData result =
157 new IntentData(original.intent(), original.state(), original.version());
158
159 if (original.installables() != null) {
160 result.setInstallables(original.installables());
161 }
162 return result;
163 }
164
165 /**
166 * TODO.
167 * @param currentData
168 * @param newData
169 * @return
170 */
171 private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
172
173 if (currentData == null) {
174 return true;
175 } else if (currentData.version().compareTo(newData.version()) < 0) {
176 return true;
177 } else if (currentData.version().compareTo(newData.version()) > 0) {
178 return false;
179 }
180
181 // current and new data versions are the same
182 IntentState currentState = currentData.state();
183 IntentState newState = newData.state();
184
185 switch (newState) {
186 case INSTALLING:
187 if (currentState == INSTALLING) {
188 return false;
189 }
190 // FALLTHROUGH
191 case INSTALLED:
192 if (currentState == INSTALLED) {
193 return false;
194 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) {
195 log.warn("Invalid state transition from {} to {} for intent {}",
196 currentState, newState, newData.key());
197 return false;
198 }
199 return true;
200
201 case WITHDRAWING:
202 if (currentState == WITHDRAWING) {
203 return false;
204 }
205 // FALLTHROUGH
206 case WITHDRAWN:
207 if (currentState == WITHDRAWN) {
208 return false;
209 } else if (currentState == INSTALLING || currentState == INSTALLED) {
210 log.warn("Invalid state transition from {} to {} for intent {}",
211 currentState, newState, newData.key());
212 return false;
213 }
214 return true;
215
216
217 case FAILED:
218 if (currentState == FAILED) {
219 return false;
220 }
221 return true;
222
223
224 case COMPILING:
225 case RECOMPILING:
226 case INSTALL_REQ:
227 case WITHDRAW_REQ:
228 default:
229 log.warn("Invalid state {} for intent {}", newState, newData.key());
230 return false;
231 }
232 }
233
Jonathan Hart74c83132015-02-02 18:37:57 -0800234 @Override
235 public void write(IntentData newData) {
Jonathan Hart07e58be2015-02-12 09:57:16 -0800236 //log.debug("writing intent {}", newData);
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800237
Jonathan Hart07e58be2015-02-12 09:57:16 -0800238 IntentData currentData = currentState.get(newData.key());
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800239
Jonathan Hart07e58be2015-02-12 09:57:16 -0800240 if (isUpdateAcceptable(currentData, newData)) {
241 // Only the master is modifying the current state. Therefore assume
242 // this always succeeds
243 currentState.put(newData.key(), copyData(newData));
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800244
Jonathan Hart07e58be2015-02-12 09:57:16 -0800245 // if current.put succeeded
246 pending.remove(newData.key(), newData);
247 } else {
248 log.debug("not writing update: {}", newData);
249 }
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800250 /*try {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800251 notifyDelegate(IntentEvent.getEvent(newData));
252 } catch (IllegalArgumentException e) {
253 //no-op
254 log.trace("ignore this exception: {}", e);
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800255 }*/
Jonathan Hart74c83132015-02-02 18:37:57 -0800256 }
257
258 @Override
259 public void batchWrite(Iterable<IntentData> updates) {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800260 updates.forEach(this::write);
261 }
262
263 @Override
264 public Intent getIntent(Key key) {
265 IntentData data = currentState.get(key);
266 if (data != null) {
267 return data.intent();
268 }
269 return null;
Jonathan Hart74c83132015-02-02 18:37:57 -0800270 }
271
272 @Override
Jonathan Hart74c83132015-02-02 18:37:57 -0800273 public IntentData getIntentData(Key key) {
Jonathan Hart07e58be2015-02-12 09:57:16 -0800274 return copyData(currentState.get(key));
Jonathan Hart74c83132015-02-02 18:37:57 -0800275 }
276
277 @Override
278 public void addPending(IntentData data) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800279 log.debug("new call to pending {}", data);
280 if (data.version() == null) {
281 log.debug("updating timestamp");
282 data.setVersion(new SystemClockTimestamp());
283 }
Jonathan Hart07e58be2015-02-12 09:57:16 -0800284 pending.put(data.key(), copyData(data));
Jonathan Hart74c83132015-02-02 18:37:57 -0800285 }
286
287 @Override
288 public boolean isMaster(Intent intent) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800289 return partitionService.isMine(intent.key());
Jonathan Hart74c83132015-02-02 18:37:57 -0800290 }
291
Jonathan Hart5573d322015-01-21 10:13:25 -0800292 private void notifyDelegateIfNotNull(IntentEvent event) {
293 if (event != null) {
294 notifyDelegate(event);
295 }
296 }
297
Jonathan Hart539a6462015-01-27 17:05:43 -0800298 private final class InternalIntentStatesListener implements
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800299 EventuallyConsistentMapListener<Key, IntentData> {
Jonathan Hart5573d322015-01-21 10:13:25 -0800300 @Override
Jonathan Hart539a6462015-01-27 17:05:43 -0800301 public void event(
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800302 EventuallyConsistentMapEvent<Key, IntentData> event) {
Jonathan Hart539a6462015-01-27 17:05:43 -0800303 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
304 IntentEvent externalEvent;
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800305 IntentData intentData = event.value();
Jonathan Hart5573d322015-01-21 10:13:25 -0800306
Jonathan Hart5573d322015-01-21 10:13:25 -0800307 try {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800308 externalEvent = IntentEvent.getEvent(intentData.state(), intentData.intent());
Jonathan Hart539a6462015-01-27 17:05:43 -0800309 } catch (IllegalArgumentException e) {
310 externalEvent = null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800311 }
Jonathan Hart539a6462015-01-27 17:05:43 -0800312
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800313 notifyDelegateIfNotNull(externalEvent);
Jonathan Hart539a6462015-01-27 17:05:43 -0800314 }
Jonathan Hart5573d322015-01-21 10:13:25 -0800315 }
316 }
317
Jonathan Hart74c83132015-02-02 18:37:57 -0800318 private final class InternalPendingListener implements
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800319 EventuallyConsistentMapListener<Key, IntentData> {
Jonathan Hart74c83132015-02-02 18:37:57 -0800320 @Override
321 public void event(
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800322 EventuallyConsistentMapEvent<Key, IntentData> event) {
Jonathan Hart74c83132015-02-02 18:37:57 -0800323 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
324 // The pending intents map has been updated. If we are master for
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800325 // this intent's partition, notify the Manager that it should do
Jonathan Hart74c83132015-02-02 18:37:57 -0800326 // some work.
327 if (isMaster(event.value().intent())) {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800328 if (delegate != null) {
Jonathan Hart07e58be2015-02-12 09:57:16 -0800329 delegate.process(copyData(event.value()));
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800330 }
Jonathan Hart74c83132015-02-02 18:37:57 -0800331 }
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800332
333 try {
334 notifyDelegate(IntentEvent.getEvent(event.value()));
335 } catch (IllegalArgumentException e) {
336 //no-op
337 log.trace("ignore this exception: {}", e);
338 }
Jonathan Hart74c83132015-02-02 18:37:57 -0800339 }
340 }
341 }
342
Jonathan Hart5573d322015-01-21 10:13:25 -0800343}
344