blob: 2c996c25f7cc74916224ee690a658d50cd9040e7 [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 /**
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800226 * Creates a new intent data object.
227 *
228 * @param intent intent this metadata references
229 * @param state intent state
230 * @param version version of the intent for this key
231 * @param origin ID of the node where the data was originally created
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700232 *
233 * @deprecated in 1.11.0
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800234 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700235 // No longer used in the code base anywhere
236 @Deprecated
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800237 public IntentData(Intent intent, IntentState state, Timestamp version, NodeId origin) {
238 checkNotNull(intent);
239 checkNotNull(state);
240 checkNotNull(version);
241 checkNotNull(origin);
242
243 this.intent = intent;
244 this.state = state;
245 this.request = state;
246 this.version = version;
247 this.origin = origin;
248 }
249
250 /**
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800251 * Creates a new intent data object.
252 *
253 * @param intent intent this metadata references
254 * @param state intent state
255 * @param request intent request
256 * @param version version of the intent for this key
257 * @param origin ID of the node where the data was originally created
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700258 *
259 * @deprecated in 1.11.0
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800260 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700261 // No longer used in the code base anywhere
262 // was used when IntentData is picked up by some of the node and was assigned with a version
263 @Deprecated
Brian O'Connorc590ebb2016-12-08 18:16:41 -0800264 public IntentData(Intent intent, IntentState state, IntentState request, Timestamp version, NodeId origin) {
265 checkNotNull(intent);
266 checkNotNull(state);
267 checkNotNull(request);
268 checkNotNull(version);
269 checkNotNull(origin);
270
271 this.intent = intent;
272 this.state = state;
273 this.request = request;
274 this.version = version;
275 this.origin = origin;
276 }
277
278 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700279 * Copy constructor.
280 *
281 * @param intentData intent data to copy
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700282 *
283 * @deprecated in 1.11.0 use {@link #copy(IntentData)} instead
Jonathan Hart0d18df32015-03-21 08:42:59 -0700284 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700285 // used to create a defensive copy
286 // to be made private
287 @Deprecated
Jonathan Hart0d18df32015-03-21 08:42:59 -0700288 public IntentData(IntentData intentData) {
289 checkNotNull(intentData);
290
291 intent = intentData.intent;
292 state = intentData.state;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700293 request = intentData.request;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700294 version = intentData.version;
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700295 internalStateVersion = intentData.internalStateVersion;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700296 origin = intentData.origin;
297 installables = intentData.installables;
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700298 errorCount = intentData.errorCount;
Brian O'Connorcff03322015-02-03 15:28:59 -0800299 }
300
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800301 /**
302 * Create a new instance based on the original instance with new installables.
303 *
304 * @param original original data
305 * @param installables new installable intents to set
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700306 *
307 * @deprecated in 1.11.0 use {@link #compiled(IntentData, List)} instead
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800308 */
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700309 // used to create an instance who reached stable state
310 // note that state is mutable field, so it gets altered else where
311 // (probably that design is mother of all intent bugs)
312 @Deprecated
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800313 public IntentData(IntentData original, List<Intent> installables) {
314 this(original);
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700315 this.internalStateVersion++;
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800316
Brian O'Connora78f0602016-09-22 10:56:08 -0700317 this.installables = checkNotNull(installables).isEmpty() ?
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700318 ImmutableList.of() : ImmutableList.copyOf(installables);
Sho SHIMIZU2f26fb22016-01-19 14:04:46 -0800319 }
320
Jonathan Hart0d18df32015-03-21 08:42:59 -0700321 // kryo constructor
322 protected IntentData() {
323 intent = null;
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700324 request = null;
Sho SHIMIZUcbcc02b2016-03-31 13:22:42 -0700325 version = null;
Jonathan Hart0d18df32015-03-21 08:42:59 -0700326 }
327
328 /**
329 * Returns the intent this metadata references.
330 *
331 * @return intent
332 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800333 public Intent intent() {
334 return intent;
335 }
336
Jonathan Hart0d18df32015-03-21 08:42:59 -0700337 /**
338 * Returns the state of the intent.
339 *
340 * @return intent state
341 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800342 public IntentState state() {
343 return state;
344 }
345
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700346 public IntentState request() {
347 return request;
348 }
349
Jonathan Hart0d18df32015-03-21 08:42:59 -0700350 /**
351 * Returns the intent key.
352 *
353 * @return intent key
354 */
Ray Milkey5b3717e2015-02-05 11:44:08 -0800355 public Key key() {
Brian O'Connorcff03322015-02-03 15:28:59 -0800356 return intent.key();
357 }
358
Jonathan Hart0d18df32015-03-21 08:42:59 -0700359 /**
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700360 * Returns the request version of the intent for this key.
Jonathan Hart0d18df32015-03-21 08:42:59 -0700361 *
362 * @return intent version
363 */
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800364 public Timestamp version() {
365 return version;
366 }
367
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700368 // had to be made public for the store timestamp provider
369 public int internalStateVersion() {
370 return internalStateVersion;
371 }
372
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800373 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700374 * Returns the origin node that created this intent.
375 *
376 * @return origin node ID
377 */
Brian O'Connor5eb77c82015-03-02 18:09:39 -0800378 public NodeId origin() {
379 return origin;
380 }
381
Jonathan Hart0d18df32015-03-21 08:42:59 -0700382 /**
383 * Updates the state of the intent to the given new state.
384 *
385 * @param newState new state of the intent
386 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800387 public void setState(IntentState newState) {
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700388 this.internalStateVersion++;
Brian O'Connorcff03322015-02-03 15:28:59 -0800389 this.state = newState;
390 }
391
Brian O'Connor2ba63fd2015-02-09 22:48:11 -0800392 /**
Brian O'Connor6d8e3172015-04-30 15:43:57 -0700393 * Increments the error count for this intent.
394 */
395 public void incrementErrorCount() {
396 errorCount++;
397 }
398
399 /**
400 * Sets the error count for this intent.
401 *
402 * @param newCount new count
403 */
404 public void setErrorCount(int newCount) {
405 errorCount = newCount;
406 }
407
408 /**
409 * Returns the number of times that this intent has encountered an error
410 * during installation or withdrawal.
411 *
412 * @return error count
413 */
414 public int errorCount() {
415 return errorCount;
416 }
417
418 /**
Jonathan Hart0d18df32015-03-21 08:42:59 -0700419 * Returns the installables associated with this intent.
420 *
421 * @return list of installable intents
422 */
Brian O'Connorcff03322015-02-03 15:28:59 -0800423 public List<Intent> installables() {
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700424 return installables != null ? installables : Collections.emptyList();
Brian O'Connorcff03322015-02-03 15:28:59 -0800425 }
426
Jonathan Hart72175c22015-03-24 18:55:58 -0700427 /**
428 * Determines whether an intent data update is allowed. The update must
429 * either have a higher version than the current data, or the state
430 * transition between two updates of the same version must be sane.
431 *
432 * @param currentData existing intent data in the store
433 * @param newData new intent data update proposal
434 * @return true if we can apply the update, otherwise false
435 */
436 public static boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
437
438 if (currentData == null) {
439 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700440 } else if (currentData.version().isOlderThan(newData.version())) {
Jonathan Hart72175c22015-03-24 18:55:58 -0700441 return true;
Sho SHIMIZU2c05f912015-04-10 14:23:16 -0700442 } else if (currentData.version().isNewerThan(newData.version())) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700443 log.trace("{} update not acceptable: current is newer", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700444 return false;
445 }
446
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700447 assert (currentData.version().equals(newData.version()));
448 if (currentData.internalStateVersion >= newData.internalStateVersion) {
449 log.trace("{} update not acceptable: current is newer internally", newData.key());
450 return false;
451 }
452
Jonathan Hart72175c22015-03-24 18:55:58 -0700453 // current and new data versions are the same
454 IntentState currentState = currentData.state();
455 IntentState newState = newData.state();
456
457 switch (newState) {
458 case INSTALLING:
459 if (currentState == INSTALLING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700460 log.trace("{} update not acceptable: no-op INSTALLING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700461 return false;
462 }
463 // FALLTHROUGH
Antonio Marsico4f68ec92017-03-09 11:16:32 +0100464 case REALLOCATING:
465 if (currentState == REALLOCATING) {
466 log.trace("{} update not acceptable: no-op REALLOCATING", newData.key());
467 return false;
468 } else if (currentState == INSTALLED) {
469 return true;
470 }
Ray Milkey7dac7da2017-08-01 16:56:05 -0700471 // FALLTHROUGH
Jonathan Hart72175c22015-03-24 18:55:58 -0700472 case INSTALLED:
473 if (currentState == INSTALLED) {
474 return false;
475 } else if (currentState == WITHDRAWING || currentState == WITHDRAWN
476 || currentState == PURGE_REQ) {
477 log.warn("Invalid state transition from {} to {} for intent {}",
478 currentState, newState, newData.key());
479 return false;
480 }
481 return true;
482
483 case WITHDRAWING:
484 if (currentState == WITHDRAWING) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700485 log.trace("{} update not acceptable: no-op WITHDRAWING", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700486 return false;
487 }
488 // FALLTHROUGH
489 case WITHDRAWN:
490 if (currentState == WITHDRAWN) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700491 log.trace("{} update not acceptable: no-op WITHDRAWN", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700492 return false;
493 } else if (currentState == INSTALLING || currentState == INSTALLED
494 || currentState == PURGE_REQ) {
495 log.warn("Invalid state transition from {} to {} for intent {}",
496 currentState, newState, newData.key());
497 return false;
498 }
499 return true;
500
501 case FAILED:
502 if (currentState == FAILED) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700503 log.trace("{} update not acceptable: no-op FAILED", newData.key());
Jonathan Hart72175c22015-03-24 18:55:58 -0700504 return false;
505 }
506 return true;
507
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700508 case CORRUPT:
509 if (currentState == CORRUPT) {
Yuta HIGUCHIf76f6d52017-05-15 18:02:09 -0700510 log.trace("{} update not acceptable: no-op CORRUPT", newData.key());
Brian O'Connorf0c5a052015-04-27 00:34:53 -0700511 return false;
512 }
513 return true;
514
Jonathan Hart72175c22015-03-24 18:55:58 -0700515 case PURGE_REQ:
Brian O'Connor597934f2015-07-16 11:44:03 -0700516 // TODO we should enforce that only WITHDRAWN intents can be purged
Jonathan Hart72175c22015-03-24 18:55:58 -0700517 return true;
518
519 case COMPILING:
520 case RECOMPILING:
521 case INSTALL_REQ:
522 case WITHDRAW_REQ:
523 default:
524 log.warn("Invalid state {} for intent {}", newState, newData.key());
525 return false;
526 }
527 }
528
Brian O'Connorcff03322015-02-03 15:28:59 -0800529 @Override
530 public int hashCode() {
531 return Objects.hash(intent, version);
532 }
533
534 @Override
535 public boolean equals(Object obj) {
536 if (this == obj) {
537 return true;
538 }
539 if (obj == null || getClass() != obj.getClass()) {
540 return false;
541 }
542 final IntentData other = (IntentData) obj;
543 return Objects.equals(this.intent, other.intent)
544 && Objects.equals(this.version, other.version);
545 }
Ray Milkey65cb59a2015-02-10 15:18:26 -0800546
Jonathan Hart0d18df32015-03-21 08:42:59 -0700547 @Override
Ray Milkey65cb59a2015-02-10 15:18:26 -0800548 public String toString() {
549 return MoreObjects.toStringHelper(getClass())
550 .add("key", key())
551 .add("state", state())
552 .add("version", version())
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700553 .add("internalStateVersion", internalStateVersion)
Ray Milkey9f74c082015-02-11 15:40:16 -0800554 .add("intent", intent())
Jonathan Hart0d18df32015-03-21 08:42:59 -0700555 .add("origin", origin())
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800556 .add("installables", installables())
Ray Milkey65cb59a2015-02-10 15:18:26 -0800557 .toString();
558 }
559
Brian O'Connorcff03322015-02-03 15:28:59 -0800560}