blob: 87ca8c7c81ac9900e51ee210a8f66d831d69c306 [file] [log] [blame]
alshabibab984662014-12-04 18:56:18 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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.net.intent.impl;
Brian O'Connor427a1762014-11-19 18:40:32 -080017
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080018import java.util.Arrays;
Ray Milkey43a28222015-02-23 13:57:58 -080019import java.util.Collection;
20import java.util.Collections;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.concurrent.CountDownLatch;
25import java.util.concurrent.TimeUnit;
26
Brian O'Connor427a1762014-11-19 18:40:32 -080027import org.hamcrest.Description;
Brian O'Connor427a1762014-11-19 18:40:32 -080028import org.hamcrest.TypeSafeMatcher;
29import org.junit.After;
30import org.junit.Before;
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080031import org.junit.Ignore;
Brian O'Connor427a1762014-11-19 18:40:32 -080032import org.junit.Test;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.TestApplicationId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.impl.TestCoreManager;
36import org.onosproject.event.impl.TestEventDispatcher;
37import org.onosproject.net.NetworkResource;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080038import org.onosproject.net.intent.FlowRuleIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.intent.Intent;
40import org.onosproject.net.intent.IntentCompiler;
41import org.onosproject.net.intent.IntentEvent;
42import org.onosproject.net.intent.IntentEvent.Type;
43import org.onosproject.net.intent.IntentExtensionService;
44import org.onosproject.net.intent.IntentId;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.intent.IntentListener;
46import org.onosproject.net.intent.IntentService;
47import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080048import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.resource.LinkResourceAllocations;
Jonathan Hart6a8fd1d2015-02-25 15:44:37 -080050import org.onosproject.store.trivial.impl.SimpleIntentStore;
Brian O'Connor427a1762014-11-19 18:40:32 -080051
Ray Milkey43a28222015-02-23 13:57:58 -080052import com.google.common.collect.HashMultimap;
Ray Milkey43a28222015-02-23 13:57:58 -080053import com.google.common.collect.Lists;
54import com.google.common.collect.Maps;
55import com.google.common.collect.Multimap;
56import com.google.common.collect.Sets;
Brian O'Connor427a1762014-11-19 18:40:32 -080057
Ray Milkeye9a3e222014-12-03 16:46:06 -080058import static org.hamcrest.MatcherAssert.assertThat;
59import static org.hamcrest.Matchers.hasSize;
Ray Milkey43a28222015-02-23 13:57:58 -080060import static org.junit.Assert.assertEquals;
61import static org.junit.Assert.assertNotNull;
62import static org.junit.Assert.assertTrue;
Ray Milkey9f74c082015-02-11 15:40:16 -080063import static org.onlab.junit.TestTools.assertAfter;
Brian O'Connor427a1762014-11-19 18:40:32 -080064import static org.onlab.util.Tools.delay;
Ray Milkey43a28222015-02-23 13:57:58 -080065import static org.onosproject.net.intent.IntentState.FAILED;
66import static org.onosproject.net.intent.IntentState.INSTALLED;
67import static org.onosproject.net.intent.IntentState.WITHDRAWN;
68import static org.onosproject.net.intent.IntentTestsMocks.MockFlowRule;
69import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
Brian O'Connor427a1762014-11-19 18:40:32 -080070
71/**
72 * Test intent manager and transitions.
73 *
74 * TODO implement the following tests:
75 * - {submit, withdraw, update, replace} intent
Sho SHIMIZU34660962015-01-22 17:58:44 -080076 * - {submit, update, recompiling} intent with failed compilation
Brian O'Connor427a1762014-11-19 18:40:32 -080077 * - failed reservation
78 * - push timeout recovery
79 * - failed items recovery
80 *
81 * in general, verify intents store, flow store, and work queue
82 */
Ray Milkey1c166dc2015-03-02 13:32:39 -080083
Brian O'Connor427a1762014-11-19 18:40:32 -080084public class IntentManagerTest {
85
Pavlin Radoslavov35b4ecb2015-03-03 15:06:04 -080086 private static final int SUBMIT_TIMEOUT_MS = 1000;
Brian O'Connor427a1762014-11-19 18:40:32 -080087 private static final ApplicationId APPID = new TestApplicationId("manager-test");
88
89 private IntentManager manager;
90 private MockFlowRuleService flowRuleService;
91
92 protected IntentService service;
93 protected IntentExtensionService extensionService;
94 protected TestListener listener = new TestListener();
95 protected TestIntentCompiler compiler = new TestIntentCompiler();
Brian O'Connor427a1762014-11-19 18:40:32 -080096
Ray Milkeye9a3e222014-12-03 16:46:06 -080097 private static class TestListener implements IntentListener {
98 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
99 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
100
101 @Override
102 public void event(IntentEvent event) {
103 events.put(event.type(), event);
104 if (latchMap.containsKey(event.type())) {
105 latchMap.get(event.type()).countDown();
106 }
107 }
108
109 public int getCounts(IntentEvent.Type type) {
110 return events.get(type).size();
111 }
112
113 public void setLatch(int count, IntentEvent.Type type) {
114 latchMap.put(type, new CountDownLatch(count));
115 }
116
117 public void await(IntentEvent.Type type) {
118 try {
119 assertTrue("Timed out waiting for: " + type,
120 latchMap.get(type).await(5, TimeUnit.SECONDS));
121 } catch (InterruptedException e) {
122 e.printStackTrace();
123 }
124 }
125 }
126
127 private static class TestIntentTracker implements ObjectiveTrackerService {
128 private TopologyChangeDelegate delegate;
129 @Override
130 public void setDelegate(TopologyChangeDelegate delegate) {
131 this.delegate = delegate;
132 }
133
134 @Override
135 public void unsetDelegate(TopologyChangeDelegate delegate) {
136 if (delegate.equals(this.delegate)) {
137 this.delegate = null;
138 }
139 }
140
141 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800142 public void addTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800143 //TODO
144 }
145
146 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800147 public void removeTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800148 //TODO
149 }
150 }
151
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800152 private static class MockInstallableIntent extends FlowRuleIntent {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800153
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800154 public MockInstallableIntent() {
155 super(APPID, Arrays.asList(new MockFlowRule(100)));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800156 }
157 }
158
159 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
160 @Override
161 public List<Intent> compile(MockIntent intent, List<Intent> installable,
162 Set<LinkResourceAllocations> resources) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800163 return Lists.newArrayList(new MockInstallableIntent());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800164 }
165 }
166
167 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
168 @Override
169 public List<Intent> compile(MockIntent intent, List<Intent> installable,
170 Set<LinkResourceAllocations> resources) {
171 throw new IntentCompilationException("Compilation always fails");
172 }
173 }
174
Ray Milkeye9a3e222014-12-03 16:46:06 -0800175 /**
176 * Hamcrest matcher to check that a conllection of Intents contains an
177 * Intent with the specified Intent Id.
178 */
179 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
180 private final IntentId id;
181
182 public EntryForIntentMatcher(IntentId idValue) {
183 id = idValue;
184 }
185
186 @Override
187 public boolean matchesSafely(Collection<Intent> intents) {
188 for (Intent intent : intents) {
189 if (intent.id().equals(id)) {
190 return true;
191 }
192 }
193 return false;
194 }
195
196 @Override
197 public void describeTo(Description description) {
198 description.appendText("an intent with id \" ").
199 appendText(id.toString()).
200 appendText("\"");
201 }
202 }
203
204 private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
205 return new EntryForIntentMatcher(id);
206 }
207
Brian O'Connor427a1762014-11-19 18:40:32 -0800208 @Before
209 public void setUp() {
210 manager = new IntentManager();
211 flowRuleService = new MockFlowRuleService();
212 manager.store = new SimpleIntentStore();
Brian O'Connor427a1762014-11-19 18:40:32 -0800213 manager.eventDispatcher = new TestEventDispatcher();
214 manager.trackerService = new TestIntentTracker();
215 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -0800216 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -0800217 service = manager;
218 extensionService = manager;
219
220 manager.activate();
221 service.addListener(listener);
222 extensionService.registerCompiler(MockIntent.class, compiler);
Brian O'Connor427a1762014-11-19 18:40:32 -0800223
224 assertTrue("store should be empty",
225 Sets.newHashSet(service.getIntents()).isEmpty());
226 assertEquals(0L, flowRuleService.getFlowRuleCount());
227 }
228
Ray Milkey9f74c082015-02-11 15:40:16 -0800229 public void verifyState() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800230 // verify that all intents are parked and the batch operation is unblocked
231 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
232 for (Intent i : service.getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800233 IntentState state = service.getIntentState(i.key());
Brian O'Connor427a1762014-11-19 18:40:32 -0800234 assertTrue("Intent " + i.id() + " is in invalid state " + state,
235 parked.contains(state));
236 }
237 //the batch has not yet been removed when we receive the last event
238 // FIXME: this doesn't guarantee to avoid the race
Brian O'Connorb499b352015-02-03 16:46:15 -0800239
240 //FIXME
241// for (int tries = 0; tries < 10; tries++) {
242// if (manager.batchService.getPendingOperations().isEmpty()) {
243// break;
244// }
245// delay(10);
246// }
247// assertTrue("There are still pending batch operations.",
248// manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800249
Ray Milkey9f74c082015-02-11 15:40:16 -0800250 }
251
252 @After
253 public void tearDown() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800254 extensionService.unregisterCompiler(MockIntent.class);
Brian O'Connor427a1762014-11-19 18:40:32 -0800255 service.removeListener(listener);
256 manager.deactivate();
257 // TODO null the other refs?
258 }
259
260 @Test
261 public void submitIntent() {
262 flowRuleService.setFuture(true);
263
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800264 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800265 listener.setLatch(1, Type.INSTALLED);
266 Intent intent = new MockIntent(MockIntent.nextId());
267 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800268 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800269 listener.await(Type.INSTALLED);
270 assertEquals(1L, service.getIntentCount());
271 assertEquals(1L, flowRuleService.getFlowRuleCount());
Ray Milkey9f74c082015-02-11 15:40:16 -0800272 verifyState();
Brian O'Connor427a1762014-11-19 18:40:32 -0800273 }
274
275 @Test
276 public void withdrawIntent() {
277 flowRuleService.setFuture(true);
278
279 listener.setLatch(1, Type.INSTALLED);
280 Intent intent = new MockIntent(MockIntent.nextId());
281 service.submit(intent);
282 listener.await(Type.INSTALLED);
283 assertEquals(1L, service.getIntentCount());
284 assertEquals(1L, flowRuleService.getFlowRuleCount());
285
286 listener.setLatch(1, Type.WITHDRAWN);
287 service.withdraw(intent);
288 listener.await(Type.WITHDRAWN);
Brian O'Connor427a1762014-11-19 18:40:32 -0800289 assertEquals(0L, flowRuleService.getFlowRuleCount());
Ray Milkey9f74c082015-02-11 15:40:16 -0800290 verifyState();
Brian O'Connor427a1762014-11-19 18:40:32 -0800291 }
292
293 @Test
Ray Milkey0811bdd2015-03-11 10:21:55 -0700294 @Ignore("This is disabled because we are seeing intermittent failures on Jenkins")
Ray Milkey9f74c082015-02-11 15:40:16 -0800295 public void stressSubmitWithdrawUnique() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800296 flowRuleService.setFuture(true);
297
298 int count = 500;
Ray Milkey9f74c082015-02-11 15:40:16 -0800299 Intent[] intents = new Intent[count];
Brian O'Connor427a1762014-11-19 18:40:32 -0800300
Brian O'Connor427a1762014-11-19 18:40:32 -0800301 listener.setLatch(count, Type.WITHDRAWN);
302
Ray Milkey9f74c082015-02-11 15:40:16 -0800303 for (int i = 0; i < count; i++) {
304 intents[i] = new MockIntent(MockIntent.nextId());
305 service.submit(intents[i]);
306 }
307
308 for (int i = 0; i < count; i++) {
309 service.withdraw(intents[i]);
310 }
311
312 listener.await(Type.WITHDRAWN);
313 assertEquals(0L, flowRuleService.getFlowRuleCount());
314 verifyState();
315 }
316
317 @Test
318 public void stressSubmitWithdrawSame() {
319 flowRuleService.setFuture(true);
320
321 int count = 50;
322
Brian O'Connor427a1762014-11-19 18:40:32 -0800323 Intent intent = new MockIntent(MockIntent.nextId());
324 for (int i = 0; i < count; i++) {
325 service.submit(intent);
326 service.withdraw(intent);
327 }
328
Pavlin Radoslavov35b4ecb2015-03-03 15:06:04 -0800329 assertAfter(SUBMIT_TIMEOUT_MS, () -> {
Ray Milkey9f74c082015-02-11 15:40:16 -0800330 assertEquals(1L, service.getIntentCount());
331 assertEquals(0L, flowRuleService.getFlowRuleCount());
332 });
333 verifyState();
Brian O'Connor427a1762014-11-19 18:40:32 -0800334 }
335
Ray Milkey9f74c082015-02-11 15:40:16 -0800336
Ray Milkey93508c22014-12-02 11:35:56 -0800337 /**
338 * Tests for proper behavior of installation of an intent that triggers
339 * a compilation error.
340 */
341 @Test
342 public void errorIntentCompile() {
343 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
344 extensionService.registerCompiler(MockIntent.class, errorCompiler);
345 MockIntent intent = new MockIntent(MockIntent.nextId());
346 listener.setLatch(1, Type.INSTALL_REQ);
347 listener.setLatch(1, Type.FAILED);
348 service.submit(intent);
349 listener.await(Type.INSTALL_REQ);
350 listener.await(Type.FAILED);
Ray Milkey9f74c082015-02-11 15:40:16 -0800351 verifyState();
Ray Milkey93508c22014-12-02 11:35:56 -0800352 }
353
354 /**
355 * Tests handling a future that contains an error as a result of
356 * installing an intent.
357 */
Ray Milkey9f74c082015-02-11 15:40:16 -0800358 @Ignore("skipping until we fix update ordering problem")
Ray Milkey93508c22014-12-02 11:35:56 -0800359 @Test
360 public void errorIntentInstallFromFlows() {
361 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800362 flowRuleService.setFuture(false);
Ray Milkey93508c22014-12-02 11:35:56 -0800363 MockIntent intent = new MockIntent(id);
364 listener.setLatch(1, Type.FAILED);
365 listener.setLatch(1, Type.INSTALL_REQ);
366 service.submit(intent);
367 listener.await(Type.INSTALL_REQ);
Ray Milkey93508c22014-12-02 11:35:56 -0800368 listener.await(Type.FAILED);
Ray Milkey9f74c082015-02-11 15:40:16 -0800369 // FIXME the intent will be moved into INSTALLED immediately which overrides FAILED
370 // ... the updates come out of order
371 verifyState();
Ray Milkey93508c22014-12-02 11:35:56 -0800372 }
373
374 /**
Ray Milkeye9a3e222014-12-03 16:46:06 -0800375 * Tests handling a future that contains an unresolvable error as a result of
376 * installing an intent.
Brian O'Connor427a1762014-11-19 18:40:32 -0800377 */
Ray Milkey9f74c082015-02-11 15:40:16 -0800378 @Ignore("test needs to be rewritten, so that the intent is resubmitted")
Ray Milkeye9a3e222014-12-03 16:46:06 -0800379 @Test
380 public void errorIntentInstallNeverTrue() {
381 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800382 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800383 MockIntent intent = new MockIntent(id);
Ray Milkey9f74c082015-02-11 15:40:16 -0800384 listener.setLatch(1, Type.FAILED);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800385 listener.setLatch(1, Type.INSTALL_REQ);
386 service.submit(intent);
387 listener.await(Type.INSTALL_REQ);
388 // The delay here forces the retry loop in the intent manager to time out
389 delay(100);
Brian O'Connor5811ac22015-02-09 19:17:07 -0800390 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800391 service.withdraw(intent);
Ray Milkey9f74c082015-02-11 15:40:16 -0800392 listener.await(Type.FAILED);
393 verifyState();
Ray Milkeye9a3e222014-12-03 16:46:06 -0800394 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800395
Ray Milkeye9a3e222014-12-03 16:46:06 -0800396 /**
397 * Tests that a compiler for a subclass of an intent that already has a
398 * compiler is automatically added.
399 */
400 @Test
401 public void intentSubclassCompile() {
402 class MockIntentSubclass extends MockIntent {
403 public MockIntentSubclass(Long number) {
404 super(number);
405 }
406 }
407 flowRuleService.setFuture(true);
408
409 listener.setLatch(1, Type.INSTALL_REQ);
410 listener.setLatch(1, Type.INSTALLED);
411 Intent intent = new MockIntentSubclass(MockIntent.nextId());
412 service.submit(intent);
413 listener.await(Type.INSTALL_REQ);
414 listener.await(Type.INSTALLED);
415 assertEquals(1L, service.getIntentCount());
416 assertEquals(1L, flowRuleService.getFlowRuleCount());
417
418 final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers =
419 extensionService.getCompilers();
420 assertEquals(2, compilers.size());
421 assertNotNull(compilers.get(MockIntentSubclass.class));
422 assertNotNull(compilers.get(MockIntent.class));
Ray Milkey9f74c082015-02-11 15:40:16 -0800423 verifyState();
Ray Milkeye9a3e222014-12-03 16:46:06 -0800424 }
425
426 /**
427 * Tests an intent with no compiler.
428 */
429 @Test
430 public void intentWithoutCompiler() {
431 class IntentNoCompiler extends Intent {
432 IntentNoCompiler() {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700433 super(APPID, null, Collections.emptyList(),
434 Intent.DEFAULT_INTENT_PRIORITY);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800435 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800436 }
437
Ray Milkeye9a3e222014-12-03 16:46:06 -0800438 Intent intent = new IntentNoCompiler();
439 listener.setLatch(1, Type.INSTALL_REQ);
440 listener.setLatch(1, Type.FAILED);
441 service.submit(intent);
442 listener.await(Type.INSTALL_REQ);
443 listener.await(Type.FAILED);
Ray Milkey9f74c082015-02-11 15:40:16 -0800444 verifyState();
Ray Milkeye9a3e222014-12-03 16:46:06 -0800445 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800446
Ray Milkeye9a3e222014-12-03 16:46:06 -0800447 /**
448 * Tests an intent with no installer.
449 */
450 @Test
451 public void intentWithoutInstaller() {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800452 MockIntent intent = new MockIntent(MockIntent.nextId());
453 listener.setLatch(1, Type.INSTALL_REQ);
454 listener.setLatch(1, Type.FAILED);
455 service.submit(intent);
456 listener.await(Type.INSTALL_REQ);
457 listener.await(Type.FAILED);
Ray Milkey9f74c082015-02-11 15:40:16 -0800458 verifyState();
Ray Milkeye9a3e222014-12-03 16:46:06 -0800459 }
460
461 /**
462 * Tests that the intent fetching methods are correct.
463 */
464 @Test
465 public void testIntentFetching() {
466 List<Intent> intents;
467
468 flowRuleService.setFuture(true);
469
470 intents = Lists.newArrayList(service.getIntents());
471 assertThat(intents, hasSize(0));
472
473 final MockIntent intent1 = new MockIntent(MockIntent.nextId());
474 final MockIntent intent2 = new MockIntent(MockIntent.nextId());
475
476 listener.setLatch(2, Type.INSTALL_REQ);
477 listener.setLatch(2, Type.INSTALLED);
478 service.submit(intent1);
479 service.submit(intent2);
480 listener.await(Type.INSTALL_REQ);
481 listener.await(Type.INSTALL_REQ);
482 listener.await(Type.INSTALLED);
483 listener.await(Type.INSTALLED);
484
485 intents = Lists.newArrayList(service.getIntents());
486 assertThat(intents, hasSize(2));
487
488 assertThat(intents, hasIntentWithId(intent1.id()));
489 assertThat(intents, hasIntentWithId(intent2.id()));
Ray Milkey9f74c082015-02-11 15:40:16 -0800490 verifyState();
Brian O'Connor427a1762014-11-19 18:40:32 -0800491 }
492}