blob: 3375570550d0bd04a4800549f7c693204f18a6fd [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 *
251 * @deprecated in 1.11.0 use {@link #compiled(IntentData, List)} instead
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800252 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700253 // used to create an instance who reached stable state
254 // note that state is mutable field, so it gets altered else where
255 // (probably that design is mother of all intent bugs)
256 @Deprecated
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800257 public IntentData(IntentData original, List<Intent> installables) {
258 this(original);
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700259 this.internalStateVersion++;
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800260
Brian O'Connora78f0602016-09-22 10:56:08 -0700261 this.installables = checkNotNull(installables).isEmpty() ?
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700262 ImmutableList.of() : ImmutableList.copyOf(installables);
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800263 }
264
Jonathan Hart0d18df32015-03-21 08:42:59 -0700265 // kryo constructor
266 protected IntentData() {
267 intent = null;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700268 request = null;
Sho SHIMIZUcbcc02b2016-03-31 13:22:42 -0700269 version = null;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700270 }
271
272 /**
273 * Returns the intent this metadata references.
274 *
275 * @return intent
276 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800277 public Intent intent() {
278 return intent;
279 }
280
Jonathan Hart0d18df32015-03-21 08:42:59 -0700281 /**
282 * Returns the state of the intent.
283 *
284 * @return intent state
285 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800286 public IntentState state() {
287 return state;
288 }
289
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700290 public IntentState request() {
291 return request;
292 }
293
Jonathan Hart0d18df32015-03-21 08:42:59 -0700294 /**
295 * Returns the intent key.
296 *
297 * @return intent key
298 */
Ray Milkey5b3717e2015-02-05 11:44:08 -0800299 public Key key() {
Brian O'Connorcff03322015-02-03 15:28:59 -0800300 return intent.key();
301 }
302
Jonathan Hart0d18df32015-03-21 08:42:59 -0700303 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700304 * Returns the request version of the intent for this key.
Jonathan Hart0d18df32015-03-21 08:42:59 -0700305 *
306 * @return intent version
307 */
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800308 public Timestamp version() {
309 return version;
310 }
311
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700312 // had to be made public for the store timestamp provider
313 public int internalStateVersion() {
314 return internalStateVersion;
315 }
316
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800317 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700318 * Returns the origin node that created this intent.
319 *
320 * @return origin node ID
321 */
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800322 public NodeId origin() {
323 return origin;
324 }
325
Jonathan Hart0d18df32015-03-21 08:42:59 -0700326 /**
327 * Updates the state of the intent to the given new state.
328 *
329 * @param newState new state of the intent
330 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800331 public void setState(IntentState newState) {
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700332 this.internalStateVersion++;
Brian O'Connorcff03322015-02-03 15:28:59 -0800333 this.state = newState;
334 }
335
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800336 /**
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700337 * Increments the error count for this intent.
338 */
339 public void incrementErrorCount() {
340 errorCount++;
341 }
342
343 /**
344 * Sets the error count for this intent.
345 *
346 * @param newCount new count
347 */
348 public void setErrorCount(int newCount) {
349 errorCount = newCount;
350 }
351
352 /**
353 * Returns the number of times that this intent has encountered an error
354 * during installation or withdrawal.
355 *
356 * @return error count
357 */
358 public int errorCount() {
359 return errorCount;
360 }
361
362 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700363 * Returns the installables associated with this intent.
364 *
365 * @return list of installable intents
366 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800367 public List<Intent> installables() {
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700368 return installables != null ? installables : Collections.emptyList();
Brian O'Connorcff03322015-02-03 15:28:59 -0800369 }
370
Jonathan Hart72175c22015-03-24 18:55:58 -0700371 /**
372 * Determines whether an intent data update is allowed. The update must
373 * either have a higher version than the current data, or the state
374 * transition between two updates of the same version must be sane.
375 *
376 * @param currentData existing intent data in the store
377 * @param newData new intent data update proposal
378 * @return true if we can apply the update, otherwise false
379 */
380 public static boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
381
382 if (currentData == null) {
383 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700384 } else if (currentData.version().isOlderThan(newData.version())) {
Jonathan Hart72175c22015-03-24 18:55:58 -0700385 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700386 } else if (currentData.version().isNewerThan(newData.version())) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700387 log.trace("{} update not acceptable: current is newer", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700388 return false;
389 }
390
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700391 assert (currentData.version().equals(newData.version()));
392 if (currentData.internalStateVersion >= newData.internalStateVersion) {
393 log.trace("{} update not acceptable: current is newer internally", newData.key());
394 return false;
395 }
396
Jonathan Hart72175c22015-03-24 18:55:58 -0700397 // current and new data versions are the same
398 IntentState currentState = currentData.state();
399 IntentState newState = newData.state();
400
401 switch (newState) {
402 case INSTALLING:
403 if (currentState == INSTALLING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700404 log.trace("{} update not acceptable: no-op INSTALLING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700405 return false;
406 }
407 // FALLTHROUGH
Antonio Marsico4f68ec92017-03-09 11:16:32 +0100408 case REALLOCATING:
409 if (currentState == REALLOCATING) {
410 log.trace("{} update not acceptable: no-op REALLOCATING", newData.key());
411 return false;
412 } else if (currentState == INSTALLED) {
413 return true;
414 }
Ray Milkey7dac7da2017-08-01 16:56:05 -0700415 // FALLTHROUGH
Jonathan Hart72175c22015-03-24 18:55:58 -0700416 case INSTALLED:
417 if (currentState == INSTALLED) {
418 return false;
419 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN
420 || currentState == PURGE_REQ) {
421 log.warn("Invalid state transition from {} to {} for intent {}",
422 currentState, newState, newData.key());
423 return false;
424 }
425 return true;
426
427 case WITHDRAWING:
428 if (currentState == WITHDRAWING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700429 log.trace("{} update not acceptable: no-op WITHDRAWING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700430 return false;
431 }
432 // FALLTHROUGH
433 case WITHDRAWN:
434 if (currentState == WITHDRAWN) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700435 log.trace("{} update not acceptable: no-op WITHDRAWN", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700436 return false;
437 } else if (currentState == INSTALLING || currentState == INSTALLED
438 || currentState == PURGE_REQ) {
439 log.warn("Invalid state transition from {} to {} for intent {}",
440 currentState, newState, newData.key());
441 return false;
442 }
443 return true;
444
445 case FAILED:
446 if (currentState == FAILED) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700447 log.trace("{} update not acceptable: no-op FAILED", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700448 return false;
449 }
450 return true;
451
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700452 case CORRUPT:
453 if (currentState == CORRUPT) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700454 log.trace("{} update not acceptable: no-op CORRUPT", newData.key());
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700455 return false;
456 }
457 return true;
458
Jonathan Hart72175c22015-03-24 18:55:58 -0700459 case PURGE_REQ:
Brian O'Connor597934f2015-07-16 11:44:03 -0700460 // TODO we should enforce that only WITHDRAWN intents can be purged
Jonathan Hart72175c22015-03-24 18:55:58 -0700461 return true;
462
463 case COMPILING:
464 case RECOMPILING:
465 case INSTALL_REQ:
466 case WITHDRAW_REQ:
467 default:
468 log.warn("Invalid state {} for intent {}", newState, newData.key());
469 return false;
470 }
471 }
472
Brian O'Connorcff03322015-02-03 15:28:59 -0800473 @Override
474 public int hashCode() {
475 return Objects.hash(intent, version);
476 }
477
478 @Override
479 public boolean equals(Object obj) {
480 if (this == obj) {
481 return true;
482 }
483 if (obj == null || getClass() != obj.getClass()) {
484 return false;
485 }
486 final IntentData other = (IntentData) obj;
487 return Objects.equals(this.intent, other.intent)
488 && Objects.equals(this.version, other.version);
489 }
Ray Milkey65cb59a2015-02-10 15:18:26 -0800490
Jonathan Hart0d18df32015-03-21 08:42:59 -0700491 @Override
Ray Milkey65cb59a2015-02-10 15:18:26 -0800492 public String toString() {
493 return MoreObjects.toStringHelper(getClass())
494 .add("key", key())
495 .add("state", state())
496 .add("version", version())
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700497 .add("internalStateVersion", internalStateVersion)
Ray Milkey9f74c082015-02-11 15:40:16 -0800498 .add("intent", intent())
Jonathan Hart0d18df32015-03-21 08:42:59 -0700499 .add("origin", origin())
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800500 .add("installables", installables())
Ray Milkey65cb59a2015-02-10 15:18:26 -0800501 .toString();
502 }
503
Brian O'Connorcff03322015-02-03 15:28:59 -0800504}