blob: cdb5bf3de6689aac0494c8a2442a18ef95a90c3e [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.Intent;
Jonathan Hart74c83132015-02-02 18:37:57 -080027import org.onosproject.net.intent.IntentData;
Jonathan Hart5573d322015-01-21 10:13:25 -080028import org.onosproject.net.intent.IntentEvent;
Jonathan Hart5573d322015-01-21 10:13:25 -080029import org.onosproject.net.intent.IntentState;
30import org.onosproject.net.intent.IntentStore;
31import org.onosproject.net.intent.IntentStoreDelegate;
Jonathan Hart74c83132015-02-02 18:37:57 -080032import org.onosproject.net.intent.Key;
Jonathan Hart5573d322015-01-21 10:13:25 -080033import org.onosproject.store.AbstractStore;
Jonathan Hart5573d322015-01-21 10:13:25 -080034import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Jonathan Hart77bdd262015-02-03 09:07:48 -080035import org.onosproject.store.ecmap.EventuallyConsistentMap;
36import org.onosproject.store.ecmap.EventuallyConsistentMapEvent;
37import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
38import org.onosproject.store.ecmap.EventuallyConsistentMapListener;
Jonathan Hart34f1e382015-02-24 16:52:23 -080039import org.onosproject.store.impl.MultiValuedTimestamp;
40import org.onosproject.store.impl.SystemClockTimestamp;
Jonathan Hart539a6462015-01-27 17:05:43 -080041import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart5573d322015-01-21 10:13:25 -080042import org.slf4j.Logger;
43
Jonathan Hart5573d322015-01-21 10:13:25 -080044import java.util.List;
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080045import java.util.stream.Collectors;
Jonathan Hart5573d322015-01-21 10:13:25 -080046
Jonathan Hart2085e072015-02-12 11:44:03 -080047import static org.onosproject.net.intent.IntentState.*;
Jonathan Hart5573d322015-01-21 10:13:25 -080048import static org.slf4j.LoggerFactory.getLogger;
49
50/**
51 * Manages inventory of Intents in a distributed data store that uses optimistic
52 * replication and gossip based techniques.
53 */
Jonathan Hart07e58be2015-02-12 09:57:16 -080054@Component(immediate = false, enabled = true)
Jonathan Hart5573d322015-01-21 10:13:25 -080055@Service
56public class GossipIntentStore
57 extends AbstractStore<IntentEvent, IntentStoreDelegate>
58 implements IntentStore {
59
60 private final Logger log = getLogger(getClass());
61
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080062 // Map of intent key => current intent state
63 private EventuallyConsistentMap<Key, IntentData> currentState;
Jonathan Hart5573d322015-01-21 10:13:25 -080064
Jonathan Hart74c83132015-02-02 18:37:57 -080065 // Map of intent key => pending intent operation
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080066 private EventuallyConsistentMap<Key, IntentData> pending;
Jonathan Hart74c83132015-02-02 18:37:57 -080067
Jonathan Hart5573d322015-01-21 10:13:25 -080068 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected ClusterCommunicationService clusterCommunicator;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected ClusterService clusterService;
73
Jonathan Hart74c83132015-02-02 18:37:57 -080074 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected PartitionService partitionService;
76
Jonathan Hart5573d322015-01-21 10:13:25 -080077 @Activate
78 public void activate() {
Jonathan Hart539a6462015-01-27 17:05:43 -080079 KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder()
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080080 .register(KryoNamespaces.API)
81 .register(IntentData.class)
82 .register(MultiValuedTimestamp.class)
83 .register(SystemClockTimestamp.class);
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080084
85 currentState = new EventuallyConsistentMapImpl<>("intent-current",
86 clusterService,
87 clusterCommunicator,
88 intentSerializer,
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080089 new IntentDataLogicalClockManager<>());
Jonathan Hart5573d322015-01-21 10:13:25 -080090
Jonathan Hart74c83132015-02-02 18:37:57 -080091 pending = new EventuallyConsistentMapImpl<>("intent-pending",
92 clusterService,
93 clusterCommunicator,
94 intentSerializer, // TODO
Jonathan Hart5ec32ba2015-02-05 13:33:58 -080095 new IntentDataClockManager<>());
Jonathan Hart74c83132015-02-02 18:37:57 -080096
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080097 currentState.addListener(new InternalIntentStatesListener());
Jonathan Hart74c83132015-02-02 18:37:57 -080098 pending.addListener(new InternalPendingListener());
Jonathan Hart5573d322015-01-21 10:13:25 -080099
100 log.info("Started");
101 }
102
103 @Deactivate
104 public void deactivate() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800105 currentState.destroy();
Jonathan Hart74c83132015-02-02 18:37:57 -0800106 pending.destroy();
Jonathan Hart5573d322015-01-21 10:13:25 -0800107
108 log.info("Stopped");
109 }
110
111 @Override
112 public long getIntentCount() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800113 return currentState.size();
Jonathan Hart5573d322015-01-21 10:13:25 -0800114 }
115
116 @Override
117 public Iterable<Intent> getIntents() {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800118 return currentState.values().stream()
119 .map(IntentData::intent)
120 .collect(Collectors.toList());
Jonathan Hart5573d322015-01-21 10:13:25 -0800121 }
122
123 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800124 public IntentState getIntentState(Key intentKey) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800125 IntentData data = currentState.get(intentKey);
126 if (data != null) {
127 return data.state();
128 }
129 return null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800130 }
131
132 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800133 public List<Intent> getInstallableIntents(Key intentKey) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800134 IntentData data = currentState.get(intentKey);
135 if (data != null) {
136 return data.installables();
137 }
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800138 return null;
Jonathan Hart5573d322015-01-21 10:13:25 -0800139 }
140
Jonathan Hart07e58be2015-02-12 09:57:16 -0800141 private IntentData copyData(IntentData original) {
142 if (original == null) {
143 return null;
144 }
145 IntentData result =
146 new IntentData(original.intent(), original.state(), original.version());
147
148 if (original.installables() != null) {
149 result.setInstallables(original.installables());
150 }
151 return result;
152 }
153
154 /**
Jonathan Hart2085e072015-02-12 11:44:03 -0800155 * Determines whether an intent data update is allowed. The update must
156 * either have a higher version than the current data, or the state
157 * transition between two updates of the same version must be sane.
158 *
159 * @param currentData existing intent data in the store
160 * @param newData new intent data update proposal
161 * @return true if we can apply the update, otherwise false
Jonathan Hart07e58be2015-02-12 09:57:16 -0800162 */
163 private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
164
165 if (currentData == null) {
166 return true;
167 } else if (currentData.version().compareTo(newData.version()) < 0) {
168 return true;
169 } else if (currentData.version().compareTo(newData.version()) > 0) {
170 return false;
171 }
172
173 // current and new data versions are the same
174 IntentState currentState = currentData.state();
175 IntentState newState = newData.state();
176
177 switch (newState) {
178 case INSTALLING:
179 if (currentState == INSTALLING) {
180 return false;
181 }
182 // FALLTHROUGH
183 case INSTALLED:
184 if (currentState == INSTALLED) {
185 return false;
186 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) {
187 log.warn("Invalid state transition from {} to {} for intent {}",
188 currentState, newState, newData.key());
189 return false;
190 }
191 return true;
192
193 case WITHDRAWING:
194 if (currentState == WITHDRAWING) {
195 return false;
196 }
197 // FALLTHROUGH
198 case WITHDRAWN:
199 if (currentState == WITHDRAWN) {
200 return false;
201 } else if (currentState == INSTALLING || currentState == INSTALLED) {
202 log.warn("Invalid state transition from {} to {} for intent {}",
203 currentState, newState, newData.key());
204 return false;
205 }
206 return true;
207
208
209 case FAILED:
210 if (currentState == FAILED) {
211 return false;
212 }
213 return true;
214
215
216 case COMPILING:
217 case RECOMPILING:
218 case INSTALL_REQ:
219 case WITHDRAW_REQ:
220 default:
221 log.warn("Invalid state {} for intent {}", newState, newData.key());
222 return false;
223 }
224 }
225
Jonathan Hart74c83132015-02-02 18:37:57 -0800226 @Override
227 public void write(IntentData newData) {
Jonathan Hart07e58be2015-02-12 09:57:16 -0800228 //log.debug("writing intent {}", newData);
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800229
Jonathan Hart07e58be2015-02-12 09:57:16 -0800230 IntentData currentData = currentState.get(newData.key());
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800231
Jonathan Hart07e58be2015-02-12 09:57:16 -0800232 if (isUpdateAcceptable(currentData, newData)) {
233 // Only the master is modifying the current state. Therefore assume
234 // this always succeeds
235 currentState.put(newData.key(), copyData(newData));
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800236
Jonathan Hart07e58be2015-02-12 09:57:16 -0800237 // if current.put succeeded
238 pending.remove(newData.key(), newData);
239 } else {
Jonathan Hart34f1e382015-02-24 16:52:23 -0800240 log.debug("not writing update: current {}, new {}", currentData, newData);
Jonathan Hart07e58be2015-02-12 09:57:16 -0800241 }
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800242 /*try {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800243 notifyDelegate(IntentEvent.getEvent(newData));
244 } catch (IllegalArgumentException e) {
245 //no-op
246 log.trace("ignore this exception: {}", e);
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800247 }*/
Jonathan Hart74c83132015-02-02 18:37:57 -0800248 }
249
250 @Override
251 public void batchWrite(Iterable<IntentData> updates) {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800252 updates.forEach(this::write);
253 }
254
255 @Override
256 public Intent getIntent(Key key) {
257 IntentData data = currentState.get(key);
258 if (data != null) {
259 return data.intent();
260 }
261 return null;
Jonathan Hart74c83132015-02-02 18:37:57 -0800262 }
263
264 @Override
Jonathan Hart74c83132015-02-02 18:37:57 -0800265 public IntentData getIntentData(Key key) {
Jonathan Hart07e58be2015-02-12 09:57:16 -0800266 return copyData(currentState.get(key));
Jonathan Hart74c83132015-02-02 18:37:57 -0800267 }
268
269 @Override
270 public void addPending(IntentData data) {
Jonathan Hart92888362015-02-13 13:14:59 -0800271 log.debug("new pending {} {} {}", data.key(), data.state(), data.version());
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800272 if (data.version() == null) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800273 data.setVersion(new SystemClockTimestamp());
274 }
Jonathan Hart07e58be2015-02-12 09:57:16 -0800275 pending.put(data.key(), copyData(data));
Jonathan Hart74c83132015-02-02 18:37:57 -0800276 }
277
278 @Override
Brian O'Connorbe28a872015-02-19 21:44:37 -0800279 public boolean isMaster(Key intentKey) {
280 return partitionService.isMine(intentKey);
Jonathan Hart74c83132015-02-02 18:37:57 -0800281 }
282
Jonathan Hart34f1e382015-02-24 16:52:23 -0800283 @Override
284 public Iterable<Intent> getPending() {
285 return pending.values().stream()
286 .map(IntentData::intent)
287 .collect(Collectors.toList());
288 }
289
Jonathan Hart5573d322015-01-21 10:13:25 -0800290 private void notifyDelegateIfNotNull(IntentEvent event) {
291 if (event != null) {
292 notifyDelegate(event);
293 }
294 }
295
Jonathan Hart539a6462015-01-27 17:05:43 -0800296 private final class InternalIntentStatesListener implements
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800297 EventuallyConsistentMapListener<Key, IntentData> {
Jonathan Hart5573d322015-01-21 10:13:25 -0800298 @Override
Jonathan Hart539a6462015-01-27 17:05:43 -0800299 public void event(
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800300 EventuallyConsistentMapEvent<Key, IntentData> event) {
Jonathan Hart539a6462015-01-27 17:05:43 -0800301 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800302 IntentData intentData = event.value();
Jonathan Hart5573d322015-01-21 10:13:25 -0800303
Jonathan Hart92888362015-02-13 13:14:59 -0800304 notifyDelegateIfNotNull(IntentEvent.getEvent(intentData));
Jonathan Hart539a6462015-01-27 17:05:43 -0800305 }
Jonathan Hart5573d322015-01-21 10:13:25 -0800306 }
307 }
308
Jonathan Hart74c83132015-02-02 18:37:57 -0800309 private final class InternalPendingListener implements
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800310 EventuallyConsistentMapListener<Key, IntentData> {
Jonathan Hart74c83132015-02-02 18:37:57 -0800311 @Override
312 public void event(
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800313 EventuallyConsistentMapEvent<Key, IntentData> event) {
Jonathan Hart74c83132015-02-02 18:37:57 -0800314 if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
315 // The pending intents map has been updated. If we are master for
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800316 // this intent's partition, notify the Manager that it should do
Jonathan Hart74c83132015-02-02 18:37:57 -0800317 // some work.
Brian O'Connorbe28a872015-02-19 21:44:37 -0800318 if (isMaster(event.value().intent().key())) {
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800319 if (delegate != null) {
Jonathan Hart34f1e382015-02-24 16:52:23 -0800320 log.debug("processing {}", event.key());
Jonathan Hart07e58be2015-02-12 09:57:16 -0800321 delegate.process(copyData(event.value()));
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800322 }
Jonathan Hart74c83132015-02-02 18:37:57 -0800323 }
Jonathan Hart5ec32ba2015-02-05 13:33:58 -0800324
Jonathan Hart92888362015-02-13 13:14:59 -0800325 notifyDelegateIfNotNull(IntentEvent.getEvent(event.value()));
Jonathan Hart74c83132015-02-02 18:37:57 -0800326 }
327 }
328 }
329
Jonathan Hart5573d322015-01-21 10:13:25 -0800330}
331