blob: a4af213547e10fdd8ef741cab52eff68c627755b [file] [log] [blame]
Brian O'Connorcff03322015-02-03 15:28:59 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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 /**
120 * Create a copy of IntentData in next state.
121 *
122 * @param data intent data to copy
123 * @param nextState to transition to
124 * @return next state
125 */
126 public static IntentData nextState(IntentData data, IntentState nextState) {
127 IntentData next = new IntentData(data);
128 // TODO state machine sanity check
129 next.setState(checkNotNull(nextState));
130 return next;
131 }
132
133 // TODO Should this be method of it's own, or
134 // should nextState(*, CORRUPT) call increment error count?
135 /**
136 * Creates a copy of IntentData in corrupt state,
137 * incrementing error count.
138 *
139 * @param data intent data to copy
140 * @return next state
141 */
142 public static IntentData corrupt(IntentData data) {
143 IntentData next = new IntentData(data);
144 next.setState(IntentState.CORRUPT);
145 next.incrementErrorCount();
146 return next;
147 }
148
149 /**
150 * Creates updated IntentData with compilation result.
151 *
152 * @param data IntentData to update
153 * @param installables compilation result
154 * @return updated IntentData object
155 */
156 public static IntentData compiled(IntentData data, List<Intent> installables) {
157 return new IntentData(data, checkNotNull(installables));
158 }
159
160
161 /**
162 * Constructor for creating IntentData representing user request.
163 *
164 * @param intent this metadata references
165 * @param reqState request state
166 */
167 private IntentData(Intent intent,
168 IntentState reqState) {
169 this.intent = checkNotNull(intent);
170 this.request = checkNotNull(reqState);
171 this.version = null;
172 this.state = reqState;
173 this.installables = ImmutableList.of();
174 }
175
176 /**
177 * Constructor for creating updated IntentData.
178 *
179 * @param original IntentData to copy from
180 * @param newReqVersion new request version
181 */
182 private IntentData(IntentData original, Timestamp newReqVersion) {
183 intent = original.intent;
184 state = original.state;
185 request = original.request;
186 version = newReqVersion;
187 internalStateVersion = original.internalStateVersion;
188 origin = original.origin;
189 installables = original.installables;
190 errorCount = original.errorCount;
191 }
192
193 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700194 * Creates a new intent data object.
195 *
196 * @param intent intent this metadata references
197 * @param state intent state
198 * @param version version of the intent for this key
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700199 *
200 * @deprecated in 1.11.0
Jonathan Hart0d18df32015-03-21 08:42:59 -0700201 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700202 // used to create initial IntentData (version = null)
203 @Deprecated
Brian O'Connorcff03322015-02-03 15:28:59 -0800204 public IntentData(Intent intent, IntentState state, Timestamp version) {
Sho SHIMIZU70b88d82016-01-19 14:27:22 -0800205 checkNotNull(intent);
206 checkNotNull(state);
207
Brian O'Connorcff03322015-02-03 15:28:59 -0800208 this.intent = intent;
209 this.state = state;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700210 this.request = state;
Brian O'Connorcff03322015-02-03 15:28:59 -0800211 this.version = version;
212 }
213
Jonathan Hart0d18df32015-03-21 08:42:59 -0700214 /**
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800215 * Creates a new intent data object.
216 *
217 * @param intent intent this metadata references
218 * @param state intent state
219 * @param version version of the intent for this key
220 * @param origin ID of the node where the data was originally created
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700221 *
222 * @deprecated in 1.11.0
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800223 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700224 // No longer used in the code base anywhere
225 @Deprecated
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800226 public IntentData(Intent intent, IntentState state, Timestamp version, NodeId origin) {
227 checkNotNull(intent);
228 checkNotNull(state);
229 checkNotNull(version);
230 checkNotNull(origin);
231
232 this.intent = intent;
233 this.state = state;
234 this.request = state;
235 this.version = version;
236 this.origin = origin;
237 }
238
239 /**
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800240 * Creates a new intent data object.
241 *
242 * @param intent intent this metadata references
243 * @param state intent state
244 * @param request intent request
245 * @param version version of the intent for this key
246 * @param origin ID of the node where the data was originally created
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700247 *
248 * @deprecated in 1.11.0
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800249 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700250 // No longer used in the code base anywhere
251 // was used when IntentData is picked up by some of the node and was assigned with a version
252 @Deprecated
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800253 public IntentData(Intent intent, IntentState state, IntentState request, Timestamp version, NodeId origin) {
254 checkNotNull(intent);
255 checkNotNull(state);
256 checkNotNull(request);
257 checkNotNull(version);
258 checkNotNull(origin);
259
260 this.intent = intent;
261 this.state = state;
262 this.request = request;
263 this.version = version;
264 this.origin = origin;
265 }
266
267 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700268 * Copy constructor.
269 *
270 * @param intentData intent data to copy
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700271 *
272 * @deprecated in 1.11.0 use {@link #copy(IntentData)} instead
Jonathan Hart0d18df32015-03-21 08:42:59 -0700273 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700274 // used to create a defensive copy
275 // to be made private
276 @Deprecated
Jonathan Hart0d18df32015-03-21 08:42:59 -0700277 public IntentData(IntentData intentData) {
278 checkNotNull(intentData);
279
280 intent = intentData.intent;
281 state = intentData.state;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700282 request = intentData.request;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700283 version = intentData.version;
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700284 internalStateVersion = intentData.internalStateVersion;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700285 origin = intentData.origin;
286 installables = intentData.installables;
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700287 errorCount = intentData.errorCount;
Brian O'Connorcff03322015-02-03 15:28:59 -0800288 }
289
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800290 /**
291 * Create a new instance based on the original instance with new installables.
292 *
293 * @param original original data
294 * @param installables new installable intents to set
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700295 *
296 * @deprecated in 1.11.0 use {@link #compiled(IntentData, List)} instead
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800297 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700298 // used to create an instance who reached stable state
299 // note that state is mutable field, so it gets altered else where
300 // (probably that design is mother of all intent bugs)
301 @Deprecated
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800302 public IntentData(IntentData original, List<Intent> installables) {
303 this(original);
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700304 this.internalStateVersion++;
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800305
Brian O'Connora78f0602016-09-22 10:56:08 -0700306 this.installables = checkNotNull(installables).isEmpty() ?
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700307 ImmutableList.of() : ImmutableList.copyOf(installables);
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800308 }
309
Jonathan Hart0d18df32015-03-21 08:42:59 -0700310 // kryo constructor
311 protected IntentData() {
312 intent = null;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700313 request = null;
Sho SHIMIZUcbcc02b2016-03-31 13:22:42 -0700314 version = null;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700315 }
316
317 /**
318 * Returns the intent this metadata references.
319 *
320 * @return intent
321 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800322 public Intent intent() {
323 return intent;
324 }
325
Jonathan Hart0d18df32015-03-21 08:42:59 -0700326 /**
327 * Returns the state of the intent.
328 *
329 * @return intent state
330 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800331 public IntentState state() {
332 return state;
333 }
334
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700335 public IntentState request() {
336 return request;
337 }
338
Jonathan Hart0d18df32015-03-21 08:42:59 -0700339 /**
340 * Returns the intent key.
341 *
342 * @return intent key
343 */
Ray Milkey5b3717e2015-02-05 11:44:08 -0800344 public Key key() {
Brian O'Connorcff03322015-02-03 15:28:59 -0800345 return intent.key();
346 }
347
Jonathan Hart0d18df32015-03-21 08:42:59 -0700348 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700349 * Returns the request version of the intent for this key.
Jonathan Hart0d18df32015-03-21 08:42:59 -0700350 *
351 * @return intent version
352 */
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800353 public Timestamp version() {
354 return version;
355 }
356
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700357 // had to be made public for the store timestamp provider
358 public int internalStateVersion() {
359 return internalStateVersion;
360 }
361
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800362 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700363 * Returns the origin node that created this intent.
364 *
365 * @return origin node ID
366 */
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800367 public NodeId origin() {
368 return origin;
369 }
370
Jonathan Hart0d18df32015-03-21 08:42:59 -0700371 /**
372 * Updates the state of the intent to the given new state.
373 *
374 * @param newState new state of the intent
375 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800376 public void setState(IntentState newState) {
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700377 this.internalStateVersion++;
Brian O'Connorcff03322015-02-03 15:28:59 -0800378 this.state = newState;
379 }
380
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800381 /**
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700382 * Increments the error count for this intent.
383 */
384 public void incrementErrorCount() {
385 errorCount++;
386 }
387
388 /**
389 * Sets the error count for this intent.
390 *
391 * @param newCount new count
392 */
393 public void setErrorCount(int newCount) {
394 errorCount = newCount;
395 }
396
397 /**
398 * Returns the number of times that this intent has encountered an error
399 * during installation or withdrawal.
400 *
401 * @return error count
402 */
403 public int errorCount() {
404 return errorCount;
405 }
406
407 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700408 * Returns the installables associated with this intent.
409 *
410 * @return list of installable intents
411 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800412 public List<Intent> installables() {
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700413 return installables != null ? installables : Collections.emptyList();
Brian O'Connorcff03322015-02-03 15:28:59 -0800414 }
415
Jonathan Hart72175c22015-03-24 18:55:58 -0700416 /**
417 * Determines whether an intent data update is allowed. The update must
418 * either have a higher version than the current data, or the state
419 * transition between two updates of the same version must be sane.
420 *
421 * @param currentData existing intent data in the store
422 * @param newData new intent data update proposal
423 * @return true if we can apply the update, otherwise false
424 */
425 public static boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
426
427 if (currentData == null) {
428 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700429 } else if (currentData.version().isOlderThan(newData.version())) {
Jonathan Hart72175c22015-03-24 18:55:58 -0700430 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700431 } else if (currentData.version().isNewerThan(newData.version())) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700432 log.trace("{} update not acceptable: current is newer", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700433 return false;
434 }
435
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700436 assert (currentData.version().equals(newData.version()));
437 if (currentData.internalStateVersion >= newData.internalStateVersion) {
438 log.trace("{} update not acceptable: current is newer internally", newData.key());
439 return false;
440 }
441
Jonathan Hart72175c22015-03-24 18:55:58 -0700442 // current and new data versions are the same
443 IntentState currentState = currentData.state();
444 IntentState newState = newData.state();
445
446 switch (newState) {
447 case INSTALLING:
448 if (currentState == INSTALLING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700449 log.trace("{} update not acceptable: no-op INSTALLING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700450 return false;
451 }
452 // FALLTHROUGH
453 case INSTALLED:
454 if (currentState == INSTALLED) {
455 return false;
456 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN
457 || currentState == PURGE_REQ) {
458 log.warn("Invalid state transition from {} to {} for intent {}",
459 currentState, newState, newData.key());
460 return false;
461 }
462 return true;
463
464 case WITHDRAWING:
465 if (currentState == WITHDRAWING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700466 log.trace("{} update not acceptable: no-op WITHDRAWING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700467 return false;
468 }
469 // FALLTHROUGH
470 case WITHDRAWN:
471 if (currentState == WITHDRAWN) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700472 log.trace("{} update not acceptable: no-op WITHDRAWN", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700473 return false;
474 } else if (currentState == INSTALLING || currentState == INSTALLED
475 || currentState == PURGE_REQ) {
476 log.warn("Invalid state transition from {} to {} for intent {}",
477 currentState, newState, newData.key());
478 return false;
479 }
480 return true;
481
482 case FAILED:
483 if (currentState == FAILED) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700484 log.trace("{} update not acceptable: no-op FAILED", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700485 return false;
486 }
487 return true;
488
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700489 case CORRUPT:
490 if (currentState == CORRUPT) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700491 log.trace("{} update not acceptable: no-op CORRUPT", newData.key());
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700492 return false;
493 }
494 return true;
495
Jonathan Hart72175c22015-03-24 18:55:58 -0700496 case PURGE_REQ:
Brian O'Connor597934f2015-07-16 11:44:03 -0700497 // TODO we should enforce that only WITHDRAWN intents can be purged
Jonathan Hart72175c22015-03-24 18:55:58 -0700498 return true;
499
500 case COMPILING:
501 case RECOMPILING:
502 case INSTALL_REQ:
503 case WITHDRAW_REQ:
504 default:
505 log.warn("Invalid state {} for intent {}", newState, newData.key());
506 return false;
507 }
508 }
509
Brian O'Connorcff03322015-02-03 15:28:59 -0800510 @Override
511 public int hashCode() {
512 return Objects.hash(intent, version);
513 }
514
515 @Override
516 public boolean equals(Object obj) {
517 if (this == obj) {
518 return true;
519 }
520 if (obj == null || getClass() != obj.getClass()) {
521 return false;
522 }
523 final IntentData other = (IntentData) obj;
524 return Objects.equals(this.intent, other.intent)
525 && Objects.equals(this.version, other.version);
526 }
Ray Milkey65cb59a2015-02-10 15:18:26 -0800527
Jonathan Hart0d18df32015-03-21 08:42:59 -0700528 @Override
Ray Milkey65cb59a2015-02-10 15:18:26 -0800529 public String toString() {
530 return MoreObjects.toStringHelper(getClass())
531 .add("key", key())
532 .add("state", state())
533 .add("version", version())
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700534 .add("internalStateVersion", internalStateVersion)
Ray Milkey9f74c082015-02-11 15:40:16 -0800535 .add("intent", intent())
Jonathan Hart0d18df32015-03-21 08:42:59 -0700536 .add("origin", origin())
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800537 .add("installables", installables())
Ray Milkey65cb59a2015-02-10 15:18:26 -0800538 .toString();
539 }
540
Brian O'Connorcff03322015-02-03 15:28:59 -0800541}