blob: 8ca5182ab7c7d07c98c45edf50f5e14bb1c1d43f [file] [log] [blame]
Brian O'Connorcff03322015-02-03 15:28:59 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Brian O'Connorcff03322015-02-03 15:28:59 -08003 *
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.net.intent;
17
Brian O'Connor9476fa12015-06-25 15:17:17 -040018import com.google.common.annotations.Beta;
Ray Milkey65cb59a2015-02-10 15:18:26 -080019import com.google.common.base.MoreObjects;
Brian O'Connorcff03322015-02-03 15:28:59 -080020import com.google.common.collect.ImmutableList;
Brian O'Connor5eb77c82015-03-02 18:09:39 -080021import org.onosproject.cluster.NodeId;
Brian O'Connorcff03322015-02-03 15:28:59 -080022import org.onosproject.store.Timestamp;
Jonathan Hart72175c22015-03-24 18:55:58 -070023import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
Brian O'Connorcff03322015-02-03 15:28:59 -080025
Brian O'Connorf0c5a052015-04-27 00:34:53 -070026import java.util.Collections;
Brian O'Connorcff03322015-02-03 15:28:59 -080027import java.util.List;
28import java.util.Objects;
29
Jonathan Hart0d18df32015-03-21 08:42:59 -070030import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorf0c5a052015-04-27 00:34:53 -070031import static org.onosproject.net.intent.IntentState.*;
Jonathan Hart0d18df32015-03-21 08:42:59 -070032
Brian O'Connorcff03322015-02-03 15:28:59 -080033/**
34 * A wrapper class that contains an intents, its state, and other metadata for
35 * internal use.
36 */
Brian O'Connor9476fa12015-06-25 15:17:17 -040037@Beta
Brian O'Connorcff03322015-02-03 15:28:59 -080038public class IntentData { //FIXME need to make this "immutable"
39 // manager should be able to mutate a local copy while processing
Jonathan Hart72175c22015-03-24 18:55:58 -070040
41 private static final Logger log = LoggerFactory.getLogger(IntentData.class);
42
Jonathan Hart0d18df32015-03-21 08:42:59 -070043 private final Intent intent;
Brian O'Connorcff03322015-02-03 15:28:59 -080044
Brian O'Connorf0c5a052015-04-27 00:34:53 -070045 private final IntentState request; //TODO perhaps we want a full fledged object for requests
Brian O'Connorcff03322015-02-03 15:28:59 -080046 private IntentState state;
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -070047 /**
48 * Intent's user request version.
49 * <p>
50 * version is assigned when an Intent was picked up by batch worker
51 * and added to pending map.
52 */
Sho SHIMIZUcbcc02b2016-03-31 13:22:42 -070053 private final Timestamp version;
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -070054 /**
55 * Intent's internal state version.
56 */
57 // ~= mutation count
58 private int internalStateVersion;
Brian O'Connor5eb77c82015-03-02 18:09:39 -080059 private NodeId origin;
Brian O'Connor6d8e3172015-04-30 15:43:57 -070060 private int errorCount;
Brian O'Connorcff03322015-02-03 15:28:59 -080061
62 private List<Intent> installables;
63
Jonathan Hart0d18df32015-03-21 08:42:59 -070064 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -070065 * Creates IntentData for Intent submit request.
66 *
67 * @param intent to request
68 * @return IntentData
69 */
70 public static IntentData submit(Intent intent) {
71 return new IntentData(checkNotNull(intent), INSTALL_REQ);
72 }
73
74 /**
75 * Creates IntentData for Intent withdraw request.
76 *
77 * @param intent to request
78 * @return IntentData
79 */
80 public static IntentData withdraw(Intent intent) {
81 return new IntentData(checkNotNull(intent), WITHDRAW_REQ);
82 }
83
84 /**
85 * Creates IntentData for Intent purge request.
86 *
87 * @param intent to request
88 * @return IntentData
89 */
90 public static IntentData purge(Intent intent) {
91 return new IntentData(checkNotNull(intent), PURGE_REQ);
92 }
93
94 /**
95 * Creates updated IntentData after assigning task to a node.
96 *
97 * @param data IntentData to update work assignment
98 * @param timestamp to assign to current request
99 * @param node node which was assigned to handle this request (local node id)
100 * @return updated IntentData object
101 */
102 public static IntentData assign(IntentData data, Timestamp timestamp, NodeId node) {
103 IntentData assigned = new IntentData(data, checkNotNull(timestamp));
104 assigned.origin = checkNotNull(node);
105 assigned.internalStateVersion++;
106 return assigned;
107 }
108
109 /**
110 * Creates a copy of given IntentData.
111 *
112 * @param data intent data to copy
113 * @return copy
114 */
115 public static IntentData copy(IntentData data) {
116 return new IntentData(data);
117 }
118
119 /**
jaegonkimcbe1c5e2018-05-20 15:11:18 +0900120 * Creates a copy of given IntentData, and update request version.
121 *
122 * @param data intent data to copy
123 * @param reqVersion request version to be updated
124 * @return copy
125 */
126 public static IntentData copy(IntentData data, Timestamp reqVersion) {
127 return new IntentData(data, checkNotNull(reqVersion));
128 }
129
130 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700131 * Create a copy of IntentData in next state.
132 *
133 * @param data intent data to copy
134 * @param nextState to transition to
135 * @return next state
136 */
137 public static IntentData nextState(IntentData data, IntentState nextState) {
138 IntentData next = new IntentData(data);
139 // TODO state machine sanity check
140 next.setState(checkNotNull(nextState));
141 return next;
142 }
143
144 // TODO Should this be method of it's own, or
145 // should nextState(*, CORRUPT) call increment error count?
146 /**
147 * Creates a copy of IntentData in corrupt state,
148 * incrementing error count.
149 *
150 * @param data intent data to copy
151 * @return next state
152 */
153 public static IntentData corrupt(IntentData data) {
154 IntentData next = new IntentData(data);
155 next.setState(IntentState.CORRUPT);
156 next.incrementErrorCount();
157 return next;
158 }
159
160 /**
161 * Creates updated IntentData with compilation result.
162 *
163 * @param data IntentData to update
164 * @param installables compilation result
165 * @return updated IntentData object
166 */
167 public static IntentData compiled(IntentData data, List<Intent> installables) {
168 return new IntentData(data, checkNotNull(installables));
169 }
170
171
172 /**
173 * Constructor for creating IntentData representing user request.
174 *
175 * @param intent this metadata references
176 * @param reqState request state
177 */
178 private IntentData(Intent intent,
179 IntentState reqState) {
180 this.intent = checkNotNull(intent);
181 this.request = checkNotNull(reqState);
182 this.version = null;
183 this.state = reqState;
184 this.installables = ImmutableList.of();
185 }
186
187 /**
188 * Constructor for creating updated IntentData.
189 *
190 * @param original IntentData to copy from
191 * @param newReqVersion new request version
192 */
193 private IntentData(IntentData original, Timestamp newReqVersion) {
194 intent = original.intent;
195 state = original.state;
196 request = original.request;
197 version = newReqVersion;
198 internalStateVersion = original.internalStateVersion;
199 origin = original.origin;
200 installables = original.installables;
201 errorCount = original.errorCount;
202 }
203
204 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700205 * Creates a new intent data object.
206 *
207 * @param intent intent this metadata references
208 * @param state intent state
209 * @param version version of the intent for this key
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700210 *
211 * @deprecated in 1.11.0
Jonathan Hart0d18df32015-03-21 08:42:59 -0700212 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700213 // used to create initial IntentData (version = null)
214 @Deprecated
Brian O'Connorcff03322015-02-03 15:28:59 -0800215 public IntentData(Intent intent, IntentState state, Timestamp version) {
Sho SHIMIZU70b88d82016-01-19 14:27:22 -0800216 checkNotNull(intent);
217 checkNotNull(state);
218
Brian O'Connorcff03322015-02-03 15:28:59 -0800219 this.intent = intent;
220 this.state = state;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700221 this.request = state;
Brian O'Connorcff03322015-02-03 15:28:59 -0800222 this.version = version;
223 }
224
Jonathan Hart0d18df32015-03-21 08:42:59 -0700225 /**
226 * Copy constructor.
227 *
228 * @param intentData intent data to copy
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700229 *
Jonathan Hart0d18df32015-03-21 08:42:59 -0700230 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700231 // used to create a defensive copy
Ray Milkey8efe2782019-02-08 12:29:39 -0800232 private IntentData(IntentData intentData) {
Jonathan Hart0d18df32015-03-21 08:42:59 -0700233 checkNotNull(intentData);
234
235 intent = intentData.intent;
236 state = intentData.state;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700237 request = intentData.request;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700238 version = intentData.version;
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700239 internalStateVersion = intentData.internalStateVersion;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700240 origin = intentData.origin;
241 installables = intentData.installables;
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700242 errorCount = intentData.errorCount;
Brian O'Connorcff03322015-02-03 15:28:59 -0800243 }
244
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800245 /**
246 * Create a new instance based on the original instance with new installables.
247 *
248 * @param original original data
249 * @param installables new installable intents to set
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700250 *
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800251 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700252 // used to create an instance who reached stable state
253 // note that state is mutable field, so it gets altered else where
254 // (probably that design is mother of all intent bugs)
Ray Milkeye1077f82019-02-20 09:31:12 -0800255 private IntentData(IntentData original, List<Intent> installables) {
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800256 this(original);
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700257 this.internalStateVersion++;
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800258
Brian O'Connora78f0602016-09-22 10:56:08 -0700259 this.installables = checkNotNull(installables).isEmpty() ?
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700260 ImmutableList.of() : ImmutableList.copyOf(installables);
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800261 }
262
Jonathan Hart0d18df32015-03-21 08:42:59 -0700263 // kryo constructor
264 protected IntentData() {
265 intent = null;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700266 request = null;
Sho SHIMIZUcbcc02b2016-03-31 13:22:42 -0700267 version = null;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700268 }
269
270 /**
271 * Returns the intent this metadata references.
272 *
273 * @return intent
274 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800275 public Intent intent() {
276 return intent;
277 }
278
Jonathan Hart0d18df32015-03-21 08:42:59 -0700279 /**
280 * Returns the state of the intent.
281 *
282 * @return intent state
283 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800284 public IntentState state() {
285 return state;
286 }
287
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700288 public IntentState request() {
289 return request;
290 }
291
Jonathan Hart0d18df32015-03-21 08:42:59 -0700292 /**
293 * Returns the intent key.
294 *
295 * @return intent key
296 */
Ray Milkey5b3717e2015-02-05 11:44:08 -0800297 public Key key() {
Brian O'Connorcff03322015-02-03 15:28:59 -0800298 return intent.key();
299 }
300
Jonathan Hart0d18df32015-03-21 08:42:59 -0700301 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700302 * Returns the request version of the intent for this key.
Jonathan Hart0d18df32015-03-21 08:42:59 -0700303 *
304 * @return intent version
305 */
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800306 public Timestamp version() {
307 return version;
308 }
309
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700310 // had to be made public for the store timestamp provider
311 public int internalStateVersion() {
312 return internalStateVersion;
313 }
314
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800315 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700316 * Returns the origin node that created this intent.
317 *
318 * @return origin node ID
319 */
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800320 public NodeId origin() {
321 return origin;
322 }
323
Jonathan Hart0d18df32015-03-21 08:42:59 -0700324 /**
325 * Updates the state of the intent to the given new state.
326 *
327 * @param newState new state of the intent
328 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800329 public void setState(IntentState newState) {
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700330 this.internalStateVersion++;
Brian O'Connorcff03322015-02-03 15:28:59 -0800331 this.state = newState;
332 }
333
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800334 /**
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700335 * Increments the error count for this intent.
336 */
337 public void incrementErrorCount() {
338 errorCount++;
339 }
340
341 /**
342 * Sets the error count for this intent.
343 *
344 * @param newCount new count
345 */
346 public void setErrorCount(int newCount) {
347 errorCount = newCount;
348 }
349
350 /**
351 * Returns the number of times that this intent has encountered an error
352 * during installation or withdrawal.
353 *
354 * @return error count
355 */
356 public int errorCount() {
357 return errorCount;
358 }
359
360 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700361 * Returns the installables associated with this intent.
362 *
363 * @return list of installable intents
364 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800365 public List<Intent> installables() {
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700366 return installables != null ? installables : Collections.emptyList();
Brian O'Connorcff03322015-02-03 15:28:59 -0800367 }
368
Jonathan Hart72175c22015-03-24 18:55:58 -0700369 /**
370 * Determines whether an intent data update is allowed. The update must
371 * either have a higher version than the current data, or the state
372 * transition between two updates of the same version must be sane.
373 *
374 * @param currentData existing intent data in the store
375 * @param newData new intent data update proposal
376 * @return true if we can apply the update, otherwise false
377 */
378 public static boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
379
380 if (currentData == null) {
381 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700382 } else if (currentData.version().isOlderThan(newData.version())) {
Jonathan Hart72175c22015-03-24 18:55:58 -0700383 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700384 } else if (currentData.version().isNewerThan(newData.version())) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700385 log.trace("{} update not acceptable: current is newer", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700386 return false;
387 }
388
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700389 assert (currentData.version().equals(newData.version()));
390 if (currentData.internalStateVersion >= newData.internalStateVersion) {
391 log.trace("{} update not acceptable: current is newer internally", newData.key());
392 return false;
393 }
394
Jonathan Hart72175c22015-03-24 18:55:58 -0700395 // current and new data versions are the same
396 IntentState currentState = currentData.state();
397 IntentState newState = newData.state();
398
399 switch (newState) {
400 case INSTALLING:
401 if (currentState == INSTALLING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700402 log.trace("{} update not acceptable: no-op INSTALLING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700403 return false;
404 }
405 // FALLTHROUGH
Antonio Marsico4f68ec92017-03-09 11:16:32 +0100406 case REALLOCATING:
407 if (currentState == REALLOCATING) {
408 log.trace("{} update not acceptable: no-op REALLOCATING", newData.key());
409 return false;
410 } else if (currentState == INSTALLED) {
411 return true;
412 }
Ray Milkey7dac7da2017-08-01 16:56:05 -0700413 // FALLTHROUGH
Jonathan Hart72175c22015-03-24 18:55:58 -0700414 case INSTALLED:
415 if (currentState == INSTALLED) {
416 return false;
417 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN
418 || currentState == PURGE_REQ) {
419 log.warn("Invalid state transition from {} to {} for intent {}",
420 currentState, newState, newData.key());
421 return false;
422 }
423 return true;
424
425 case WITHDRAWING:
426 if (currentState == WITHDRAWING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700427 log.trace("{} update not acceptable: no-op WITHDRAWING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700428 return false;
429 }
430 // FALLTHROUGH
431 case WITHDRAWN:
432 if (currentState == WITHDRAWN) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700433 log.trace("{} update not acceptable: no-op WITHDRAWN", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700434 return false;
435 } else if (currentState == INSTALLING || currentState == INSTALLED
436 || currentState == PURGE_REQ) {
437 log.warn("Invalid state transition from {} to {} for intent {}",
438 currentState, newState, newData.key());
439 return false;
440 }
441 return true;
442
443 case FAILED:
444 if (currentState == FAILED) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700445 log.trace("{} update not acceptable: no-op FAILED", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700446 return false;
447 }
448 return true;
449
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700450 case CORRUPT:
451 if (currentState == CORRUPT) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700452 log.trace("{} update not acceptable: no-op CORRUPT", newData.key());
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700453 return false;
454 }
455 return true;
456
Jonathan Hart72175c22015-03-24 18:55:58 -0700457 case PURGE_REQ:
Brian O'Connor597934f2015-07-16 11:44:03 -0700458 // TODO we should enforce that only WITHDRAWN intents can be purged
Jonathan Hart72175c22015-03-24 18:55:58 -0700459 return true;
460
461 case COMPILING:
462 case RECOMPILING:
463 case INSTALL_REQ:
464 case WITHDRAW_REQ:
465 default:
466 log.warn("Invalid state {} for intent {}", newState, newData.key());
467 return false;
468 }
469 }
470
Brian O'Connorcff03322015-02-03 15:28:59 -0800471 @Override
472 public int hashCode() {
473 return Objects.hash(intent, version);
474 }
475
476 @Override
477 public boolean equals(Object obj) {
478 if (this == obj) {
479 return true;
480 }
481 if (obj == null || getClass() != obj.getClass()) {
482 return false;
483 }
484 final IntentData other = (IntentData) obj;
485 return Objects.equals(this.intent, other.intent)
486 && Objects.equals(this.version, other.version);
487 }
Ray Milkey65cb59a2015-02-10 15:18:26 -0800488
Jonathan Hart0d18df32015-03-21 08:42:59 -0700489 @Override
Ray Milkey65cb59a2015-02-10 15:18:26 -0800490 public String toString() {
491 return MoreObjects.toStringHelper(getClass())
492 .add("key", key())
493 .add("state", state())
494 .add("version", version())
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700495 .add("internalStateVersion", internalStateVersion)
Ray Milkey9f74c082015-02-11 15:40:16 -0800496 .add("intent", intent())
Jonathan Hart0d18df32015-03-21 08:42:59 -0700497 .add("origin", origin())
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800498 .add("installables", installables())
Ray Milkey65cb59a2015-02-10 15:18:26 -0800499 .toString();
500 }
501
Brian O'Connorcff03322015-02-03 15:28:59 -0800502}