blob: 0a4fcb53da39e0cc5adc0ebdd4244d8354a765f0 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -08002 * Copyright 2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.trivial.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Brian O'Connorb499b352015-02-03 16:46:15 -080018import com.google.common.collect.Maps;
Brian O'Connor66630c82014-10-02 21:08:19 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.intent.Intent;
Brian O'Connorcff03322015-02-03 15:28:59 -080024import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.net.intent.IntentEvent;
Brian O'Connor7775bda2015-02-06 15:01:18 -080026import org.onosproject.net.intent.IntentState;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.net.intent.IntentStore;
28import org.onosproject.net.intent.IntentStoreDelegate;
Ray Milkey5b3717e2015-02-05 11:44:08 -080029import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.store.AbstractStore;
Brian O'Connor66630c82014-10-02 21:08:19 -070031import org.slf4j.Logger;
32
Thomas Vachuskac96058a2014-10-20 23:00:16 -070033import java.util.List;
34import java.util.Map;
Brian O'Connorb499b352015-02-03 16:46:15 -080035import java.util.stream.Collectors;
Thomas Vachuskac96058a2014-10-20 23:00:16 -070036
Brian O'Connorb499b352015-02-03 16:46:15 -080037import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080038import static org.onosproject.net.intent.IntentState.*;
Thomas Vachuskac96058a2014-10-20 23:00:16 -070039import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070040
41@Component(immediate = true)
42@Service
43public class SimpleIntentStore
tom85258ee2014-10-07 00:10:02 -070044 extends AbstractStore<IntentEvent, IntentStoreDelegate>
45 implements IntentStore {
Brian O'Connor66630c82014-10-02 21:08:19 -070046
47 private final Logger log = getLogger(getClass());
Brian O'Connorcff03322015-02-03 15:28:59 -080048
Ray Milkey5b3717e2015-02-05 11:44:08 -080049 private final Map<Key, IntentData> current = Maps.newConcurrentMap();
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080050 private final Map<Key, IntentData> pending = Maps.newConcurrentMap();
51
52 private IntentData copyData(IntentData original) {
53 if (original == null) {
54 return null;
55 }
56 IntentData result =
57 new IntentData(original.intent(), original.state(), original.version());
58
59 if (original.installables() != null) {
60 result.setInstallables(original.installables());
61 }
62 return result;
63 }
Brian O'Connor66630c82014-10-02 21:08:19 -070064
65 @Activate
66 public void activate() {
67 log.info("Started");
68 }
69
70 @Deactivate
71 public void deactivate() {
72 log.info("Stopped");
73 }
74
Brian O'Connor66630c82014-10-02 21:08:19 -070075 @Override
76 public long getIntentCount() {
Brian O'Connorb499b352015-02-03 16:46:15 -080077 return current.size();
Brian O'Connor66630c82014-10-02 21:08:19 -070078 }
79
80 @Override
81 public Iterable<Intent> getIntents() {
Brian O'Connorb499b352015-02-03 16:46:15 -080082 return current.values().stream()
83 .map(IntentData::intent)
84 .collect(Collectors.toList());
Brian O'Connor66630c82014-10-02 21:08:19 -070085 }
86
87 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -080088 public IntentState getIntentState(Key intentKey) {
89 IntentData data = current.get(intentKey);
90 return (data != null) ? data.state() : null;
Brian O'Connor7775bda2015-02-06 15:01:18 -080091 }
92
93 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -080094 public List<Intent> getInstallableIntents(Key intentKey) {
95 IntentData data = current.get(intentKey);
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080096 if (data != null) {
97 return data.installables();
98 }
99 return null;
100 }
101
102
103 /**
104 * Determines whether an intent data update is allowed. The update must
105 * either have a higher version than the current data, or the state
106 * transition between two updates of the same version must be sane.
107 *
108 * @param currentData existing intent data in the store
109 * @param newData new intent data update proposal
110 * @return true if we can apply the update, otherwise false
111 */
112 private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
113
114 if (currentData == null) {
115 return true;
116 } else if (currentData.version().compareTo(newData.version()) < 0) {
117 return true;
118 } else if (currentData.version().compareTo(newData.version()) > 0) {
119 return false;
120 }
121
122 // current and new data versions are the same
123 IntentState currentState = currentData.state();
124 IntentState newState = newData.state();
125
126 switch (newState) {
127 case INSTALLING:
128 if (currentState == INSTALLING) {
129 return false;
130 }
131 // FALLTHROUGH
132 case INSTALLED:
133 if (currentState == INSTALLED) {
134 return false;
135 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) {
136 log.warn("Invalid state transition from {} to {} for intent {}",
137 currentState, newState, newData.key());
138 return false;
139 }
140 return true;
141
142 case WITHDRAWING:
143 if (currentState == WITHDRAWING) {
144 return false;
145 }
146 // FALLTHOUGH
147 case WITHDRAWN:
148 if (currentState == WITHDRAWN) {
149 return false;
150 } else if (currentState == INSTALLING || currentState == INSTALLED) {
151 log.warn("Invalid state transition from {} to {} for intent {}",
152 currentState, newState, newData.key());
153 return false;
154 }
155 return true;
156
157
158 case FAILED:
159 if (currentState == FAILED) {
160 return false;
161 }
162 return true;
163
164
165 case COMPILING:
166 case RECOMPILING:
167 case INSTALL_REQ:
168 case WITHDRAW_REQ:
169 default:
170 log.warn("Invalid state {} for intent {}", newState, newData.key());
171 return false;
172 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700173 }
174
Brian O'Connorcff03322015-02-03 15:28:59 -0800175 @Override
Brian O'Connor03406a42015-02-03 17:28:57 -0800176 public void write(IntentData newData) {
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800177 synchronized (this) {
178 // TODO this could be refactored/cleaned up
179 IntentData currentData = current.get(newData.key());
180 IntentData pendingData = pending.get(newData.key());
181
182 if (isUpdateAcceptable(currentData, newData)) {
183 current.put(newData.key(), copyData(newData));
184
185 if (pendingData != null
186 // pendingData version is less than or equal to newData's
187 // Note: a new update for this key could be pending (it's version will be greater)
188 && pendingData.version().compareTo(newData.version()) <= 0) {
189 pending.remove(newData.key());
190 }
191
192 notifyDelegateIfNotNull(IntentEvent.getEvent(newData));
193 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800194 }
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800195 }
196
197 private void notifyDelegateIfNotNull(IntentEvent event) {
198 if (event != null) {
199 notifyDelegate(event);
Brian O'Connor03406a42015-02-03 17:28:57 -0800200 }
201 }
202
203 @Override
204 public void batchWrite(Iterable<IntentData> updates) {
205 for (IntentData data : updates) {
206 write(data);
207 }
208 }
209
Brian O'Connor7775bda2015-02-06 15:01:18 -0800210 @Override
211 public Intent getIntent(Key key) {
212 IntentData data = current.get(key);
213 return (data != null) ? data.intent() : null;
214 }
215
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800216 @Override
217 public IntentData getIntentData(Key key) {
218 return copyData(current.get(key));
219 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800220
221 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800222 public void addPending(IntentData data) {
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800223 if (data.version() == null) { // recompiled intents will already have a version
224 data.setVersion(new SystemClockTimestamp());
225 }
226 synchronized (this) {
227 IntentData existingData = pending.get(data.key());
228 if (existingData == null ||
229 // existing version is strictly less than data's version
230 // Note: if they are equal, we already have the update
231 // TODO maybe we should still make this <= to be safe?
232 existingData.version().compareTo(data.version()) < 0) {
233 pending.put(data.key(), data);
234 checkNotNull(delegate, "Store delegate is not set")
235 .process(data);
236 notifyDelegateIfNotNull(IntentEvent.getEvent(data));
237 } else {
238 log.debug("IntentData {} is older than existing: {}",
239 data, existingData);
240 }
241 //TODO consider also checking the current map at this point
242 }
Brian O'Connorcff03322015-02-03 15:28:59 -0800243 }
Brian O'Connorcff03322015-02-03 15:28:59 -0800244
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800245 @Override
246 public boolean isMaster(Key intentKey) {
247 return true;
248 }
249
250 @Override
251 public Iterable<Intent> getPending() {
252 return pending.values().stream()
253 .map(IntentData::intent)
254 .collect(Collectors.toList());
255 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700256}