blob: 7ef624730beee5088c6cf48698c95ed05f43677d [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
Jonathan Hart0d18df32015-03-21 08:42:59 -070041/**
42 * Simple single-instance implementation of the intent store.
43 */
Brian O'Connor66630c82014-10-02 21:08:19 -070044@Component(immediate = true)
45@Service
46public class SimpleIntentStore
tom85258ee2014-10-07 00:10:02 -070047 extends AbstractStore<IntentEvent, IntentStoreDelegate>
48 implements IntentStore {
Brian O'Connor66630c82014-10-02 21:08:19 -070049
50 private final Logger log = getLogger(getClass());
Brian O'Connorcff03322015-02-03 15:28:59 -080051
Ray Milkey5b3717e2015-02-05 11:44:08 -080052 private final Map<Key, IntentData> current = Maps.newConcurrentMap();
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080053 private final Map<Key, IntentData> pending = Maps.newConcurrentMap();
54
Brian O'Connor66630c82014-10-02 21:08:19 -070055 @Activate
56 public void activate() {
57 log.info("Started");
58 }
59
60 @Deactivate
61 public void deactivate() {
62 log.info("Stopped");
63 }
64
Brian O'Connor66630c82014-10-02 21:08:19 -070065 @Override
66 public long getIntentCount() {
Brian O'Connorb499b352015-02-03 16:46:15 -080067 return current.size();
Brian O'Connor66630c82014-10-02 21:08:19 -070068 }
69
70 @Override
71 public Iterable<Intent> getIntents() {
Brian O'Connorb499b352015-02-03 16:46:15 -080072 return current.values().stream()
73 .map(IntentData::intent)
74 .collect(Collectors.toList());
Brian O'Connor66630c82014-10-02 21:08:19 -070075 }
76
77 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -080078 public IntentState getIntentState(Key intentKey) {
79 IntentData data = current.get(intentKey);
80 return (data != null) ? data.state() : null;
Brian O'Connor7775bda2015-02-06 15:01:18 -080081 }
82
83 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -080084 public List<Intent> getInstallableIntents(Key intentKey) {
85 IntentData data = current.get(intentKey);
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080086 if (data != null) {
87 return data.installables();
88 }
89 return null;
90 }
91
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080092 /**
93 * Determines whether an intent data update is allowed. The update must
94 * either have a higher version than the current data, or the state
95 * transition between two updates of the same version must be sane.
96 *
97 * @param currentData existing intent data in the store
98 * @param newData new intent data update proposal
99 * @return true if we can apply the update, otherwise false
100 */
101 private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
102
103 if (currentData == null) {
104 return true;
105 } else if (currentData.version().compareTo(newData.version()) < 0) {
106 return true;
107 } else if (currentData.version().compareTo(newData.version()) > 0) {
108 return false;
109 }
110
111 // current and new data versions are the same
112 IntentState currentState = currentData.state();
113 IntentState newState = newData.state();
114
115 switch (newState) {
116 case INSTALLING:
117 if (currentState == INSTALLING) {
118 return false;
119 }
120 // FALLTHROUGH
121 case INSTALLED:
122 if (currentState == INSTALLED) {
123 return false;
124 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) {
125 log.warn("Invalid state transition from {} to {} for intent {}",
126 currentState, newState, newData.key());
127 return false;
128 }
129 return true;
130
131 case WITHDRAWING:
132 if (currentState == WITHDRAWING) {
133 return false;
134 }
135 // FALLTHOUGH
136 case WITHDRAWN:
137 if (currentState == WITHDRAWN) {
138 return false;
139 } else if (currentState == INSTALLING || currentState == INSTALLED) {
140 log.warn("Invalid state transition from {} to {} for intent {}",
141 currentState, newState, newData.key());
142 return false;
143 }
144 return true;
145
146
147 case FAILED:
148 if (currentState == FAILED) {
149 return false;
150 }
151 return true;
152
153
154 case COMPILING:
155 case RECOMPILING:
156 case INSTALL_REQ:
157 case WITHDRAW_REQ:
158 default:
159 log.warn("Invalid state {} for intent {}", newState, newData.key());
160 return false;
161 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700162 }
163
Brian O'Connorcff03322015-02-03 15:28:59 -0800164 @Override
Brian O'Connor03406a42015-02-03 17:28:57 -0800165 public void write(IntentData newData) {
Jonathan Hart0d18df32015-03-21 08:42:59 -0700166 checkNotNull(newData);
167
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800168 synchronized (this) {
169 // TODO this could be refactored/cleaned up
170 IntentData currentData = current.get(newData.key());
171 IntentData pendingData = pending.get(newData.key());
172
173 if (isUpdateAcceptable(currentData, newData)) {
Ray Milkey8c6d00e2015-03-13 14:14:34 -0700174 if (pendingData.state() == PURGE_REQ) {
175 current.remove(newData.key(), newData);
176 } else {
Jonathan Hart0d18df32015-03-21 08:42:59 -0700177 current.put(newData.key(), new IntentData(newData));
Ray Milkey8c6d00e2015-03-13 14:14:34 -0700178 }
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800179
180 if (pendingData != null
181 // pendingData version is less than or equal to newData's
182 // Note: a new update for this key could be pending (it's version will be greater)
183 && pendingData.version().compareTo(newData.version()) <= 0) {
184 pending.remove(newData.key());
185 }
186
187 notifyDelegateIfNotNull(IntentEvent.getEvent(newData));
188 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800189 }
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800190 }
191
192 private void notifyDelegateIfNotNull(IntentEvent event) {
193 if (event != null) {
194 notifyDelegate(event);
Brian O'Connor03406a42015-02-03 17:28:57 -0800195 }
196 }
197
198 @Override
199 public void batchWrite(Iterable<IntentData> updates) {
200 for (IntentData data : updates) {
201 write(data);
202 }
203 }
204
Brian O'Connor7775bda2015-02-06 15:01:18 -0800205 @Override
206 public Intent getIntent(Key key) {
207 IntentData data = current.get(key);
208 return (data != null) ? data.intent() : null;
209 }
210
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800211 @Override
212 public IntentData getIntentData(Key key) {
Jonathan Hart0d18df32015-03-21 08:42:59 -0700213 IntentData currentData = current.get(key);
214 if (currentData == null) {
215 return null;
216 }
217 return new IntentData(currentData);
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800218 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800219
220 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800221 public void addPending(IntentData data) {
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800222 if (data.version() == null) { // recompiled intents will already have a version
223 data.setVersion(new SystemClockTimestamp());
224 }
225 synchronized (this) {
226 IntentData existingData = pending.get(data.key());
227 if (existingData == null ||
228 // existing version is strictly less than data's version
229 // Note: if they are equal, we already have the update
230 // TODO maybe we should still make this <= to be safe?
231 existingData.version().compareTo(data.version()) < 0) {
232 pending.put(data.key(), data);
233 checkNotNull(delegate, "Store delegate is not set")
234 .process(data);
235 notifyDelegateIfNotNull(IntentEvent.getEvent(data));
236 } else {
237 log.debug("IntentData {} is older than existing: {}",
238 data, existingData);
239 }
240 //TODO consider also checking the current map at this point
241 }
Brian O'Connorcff03322015-02-03 15:28:59 -0800242 }
Brian O'Connorcff03322015-02-03 15:28:59 -0800243
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -0800244 @Override
245 public boolean isMaster(Key intentKey) {
246 return true;
247 }
248
249 @Override
250 public Iterable<Intent> getPending() {
251 return pending.values().stream()
252 .map(IntentData::intent)
253 .collect(Collectors.toList());
254 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700255}