Committed by
Brian O'Connor
[Blackbird] Fixing Intent purge case (ONOS-1207)
Master needs to remove from the current map. Change-Id: I30eccbe188997949ef2d63d6dbd37b0d8d4b3f5e
Showing
13 changed files
with
164 additions
and
57 deletions
| ... | @@ -85,35 +85,61 @@ public class IntentRemoveCommand extends AbstractShellCommand { | ... | @@ -85,35 +85,61 @@ public class IntentRemoveCommand extends AbstractShellCommand { |
| 85 | Intent intent = intentService.getIntent(key); | 85 | Intent intent = intentService.getIntent(key); |
| 86 | 86 | ||
| 87 | if (intent != null) { | 87 | if (intent != null) { |
| 88 | + IntentListener listener = null; | ||
| 89 | + final CountDownLatch withdrawLatch, purgeLatch; | ||
| 90 | + if (purgeAfterRemove || sync) { | ||
| 88 | // set up latch and listener to track uninstall progress | 91 | // set up latch and listener to track uninstall progress |
| 89 | - CountDownLatch latch = new CountDownLatch(1); | 92 | + withdrawLatch = new CountDownLatch(1); |
| 90 | - IntentListener listener = (IntentEvent event) -> { | 93 | + purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null; |
| 91 | - if (Objects.equals(event.subject().key(), key) && | 94 | + listener = (IntentEvent event) -> { |
| 92 | - (event.type() == IntentEvent.Type.WITHDRAWN || | 95 | + if (Objects.equals(event.subject().key(), key)) { |
| 93 | - event.type() == IntentEvent.Type.FAILED)) { | 96 | + if (event.type() == IntentEvent.Type.WITHDRAWN || |
| 94 | - latch.countDown(); | 97 | + event.type() == IntentEvent.Type.FAILED) { |
| 98 | + withdrawLatch.countDown(); | ||
| 99 | + } else if (purgeAfterRemove && | ||
| 100 | + event.type() == IntentEvent.Type.PURGED) { | ||
| 101 | + purgeLatch.countDown(); | ||
| 102 | + } | ||
| 95 | } | 103 | } |
| 96 | }; | 104 | }; |
| 97 | intentService.addListener(listener); | 105 | intentService.addListener(listener); |
| 106 | + } else { | ||
| 107 | + purgeLatch = null; | ||
| 108 | + withdrawLatch = null; | ||
| 109 | + } | ||
| 98 | 110 | ||
| 99 | // request the withdraw | 111 | // request the withdraw |
| 100 | intentService.withdraw(intent); | 112 | intentService.withdraw(intent); |
| 101 | 113 | ||
| 102 | if (purgeAfterRemove || sync) { | 114 | if (purgeAfterRemove || sync) { |
| 103 | - try { | 115 | + try { // wait for withdraw event |
| 104 | - latch.await(5, TimeUnit.SECONDS); | 116 | + withdrawLatch.await(5, TimeUnit.SECONDS); |
| 105 | } catch (InterruptedException e) { | 117 | } catch (InterruptedException e) { |
| 106 | - print("Timed out waiting for intent {}", key); | 118 | + print("Timed out waiting for intent {} withdraw", key); |
| 107 | } | 119 | } |
| 108 | // double check the state | 120 | // double check the state |
| 109 | IntentState state = intentService.getIntentState(key); | 121 | IntentState state = intentService.getIntentState(key); |
| 110 | if (purgeAfterRemove && (state == WITHDRAWN || state == FAILED)) { | 122 | if (purgeAfterRemove && (state == WITHDRAWN || state == FAILED)) { |
| 111 | - intentService.purge(key); | 123 | + intentService.purge(intent); |
| 124 | + } | ||
| 125 | + if (sync) { // wait for purge event | ||
| 126 | + /* TODO | ||
| 127 | + Technically, the event comes before map.remove() is called. | ||
| 128 | + If we depend on sync and purge working together, we will | ||
| 129 | + need to address this. | ||
| 130 | + */ | ||
| 131 | + try { | ||
| 132 | + purgeLatch.await(5, TimeUnit.SECONDS); | ||
| 133 | + } catch (InterruptedException e) { | ||
| 134 | + print("Timed out waiting for intent {} purge", key); | ||
| 135 | + } | ||
| 112 | } | 136 | } |
| 113 | } | 137 | } |
| 138 | + | ||
| 139 | + if (listener != null) { | ||
| 114 | // clean up the listener | 140 | // clean up the listener |
| 115 | intentService.removeListener(listener); | 141 | intentService.removeListener(listener); |
| 116 | } | 142 | } |
| 117 | - | 143 | + } |
| 118 | } | 144 | } |
| 119 | } | 145 | } | ... | ... |
| ... | @@ -46,7 +46,12 @@ public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> { | ... | @@ -46,7 +46,12 @@ public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> { |
| 46 | /** | 46 | /** |
| 47 | * Signifies that an intent has been withdrawn from the system. | 47 | * Signifies that an intent has been withdrawn from the system. |
| 48 | */ | 48 | */ |
| 49 | - WITHDRAWN | 49 | + WITHDRAWN, |
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * Signifies that an intent has been purged from the system. | ||
| 53 | + */ | ||
| 54 | + PURGED | ||
| 50 | } | 55 | } |
| 51 | 56 | ||
| 52 | /** | 57 | /** |
| ... | @@ -110,6 +115,9 @@ public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> { | ... | @@ -110,6 +115,9 @@ public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> { |
| 110 | case FAILED: | 115 | case FAILED: |
| 111 | type = Type.FAILED; | 116 | type = Type.FAILED; |
| 112 | break; | 117 | break; |
| 118 | + case PURGE_REQ: | ||
| 119 | + type = Type.PURGED; | ||
| 120 | + break; | ||
| 113 | 121 | ||
| 114 | // fallthrough to default from here | 122 | // fallthrough to default from here |
| 115 | case COMPILING: | 123 | case COMPILING: | ... | ... |
| ... | @@ -43,6 +43,14 @@ public interface IntentService { | ... | @@ -43,6 +43,14 @@ public interface IntentService { |
| 43 | void withdraw(Intent intent); | 43 | void withdraw(Intent intent); |
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | + * Purges a specific intent from the system if it is <b>FAILED</b> or | ||
| 47 | + * <b>WITHDRAWN</b>. Otherwise, the intent remains in its current state. | ||
| 48 | + * | ||
| 49 | + * @param intent intent to purge | ||
| 50 | + */ | ||
| 51 | + void purge(Intent intent); | ||
| 52 | + | ||
| 53 | + /** | ||
| 46 | * Fetches an intent based on its key. | 54 | * Fetches an intent based on its key. |
| 47 | * | 55 | * |
| 48 | * @param key key of the intent | 56 | * @param key key of the intent |
| ... | @@ -112,11 +120,4 @@ public interface IntentService { | ... | @@ -112,11 +120,4 @@ public interface IntentService { |
| 112 | * @param listener listener to be removed | 120 | * @param listener listener to be removed |
| 113 | */ | 121 | */ |
| 114 | void removeListener(IntentListener listener); | 122 | void removeListener(IntentListener listener); |
| 115 | - | ||
| 116 | - /** | ||
| 117 | - * Purges a specific intent from the system if it is FAILED or WITHDRAWN. | ||
| 118 | - * | ||
| 119 | - * @param key key of the intent to purge | ||
| 120 | - */ | ||
| 121 | - void purge(Key key); | ||
| 122 | } | 123 | } | ... | ... |
| ... | @@ -92,5 +92,14 @@ public enum IntentState { | ... | @@ -92,5 +92,14 @@ public enum IntentState { |
| 92 | * Signifies that the intent has failed compiling, installing or | 92 | * Signifies that the intent has failed compiling, installing or |
| 93 | * recompiling states. | 93 | * recompiling states. |
| 94 | */ | 94 | */ |
| 95 | - FAILED //TODO consider renaming to UNSAT. | 95 | + FAILED, //TODO consider renaming to UNSAT. |
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * Indicates that the intent should be purged from the database. | ||
| 99 | + * <p> | ||
| 100 | + * Note: This operation will only be performed if the intent is already | ||
| 101 | + * in WITHDRAWN or FAILED. | ||
| 102 | + * </p> | ||
| 103 | + */ | ||
| 104 | + PURGE_REQ | ||
| 96 | } | 105 | } | ... | ... |
| ... | @@ -109,10 +109,4 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> { | ... | @@ -109,10 +109,4 @@ public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> { |
| 109 | * @return pending intents | 109 | * @return pending intents |
| 110 | */ | 110 | */ |
| 111 | Iterable<Intent> getPending(); | 111 | Iterable<Intent> getPending(); |
| 112 | - | ||
| 113 | - /** Purges a specific intent from the system if it is FAILED or WITHDRAWN. | ||
| 114 | - * | ||
| 115 | - * @param key key of the intent to purge | ||
| 116 | - */ | ||
| 117 | - void purge(Key key); | ||
| 118 | } | 112 | } | ... | ... |
| ... | @@ -186,6 +186,17 @@ public class FakeIntentManager implements TestableIntentService { | ... | @@ -186,6 +186,17 @@ public class FakeIntentManager implements TestableIntentService { |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | @Override | 188 | @Override |
| 189 | + public void purge(Intent intent) { | ||
| 190 | + IntentState currentState = intentStates.get(intent.key()); | ||
| 191 | + if (currentState == IntentState.WITHDRAWN | ||
| 192 | + || currentState == IntentState.FAILED) { | ||
| 193 | + intents.remove(intent.key()); | ||
| 194 | + installables.remove(intent.key()); | ||
| 195 | + intentStates.remove(intent.key()); | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + @Override | ||
| 189 | public Set<Intent> getIntents() { | 200 | public Set<Intent> getIntents() { |
| 190 | return Collections.unmodifiableSet(new HashSet<>(intents.values())); | 201 | return Collections.unmodifiableSet(new HashSet<>(intents.values())); |
| 191 | } | 202 | } |
| ... | @@ -230,11 +241,6 @@ public class FakeIntentManager implements TestableIntentService { | ... | @@ -230,11 +241,6 @@ public class FakeIntentManager implements TestableIntentService { |
| 230 | listeners.remove(listener); | 241 | listeners.remove(listener); |
| 231 | } | 242 | } |
| 232 | 243 | ||
| 233 | - @Override | ||
| 234 | - public void purge(Key key) { | ||
| 235 | - // FIXME implement this | ||
| 236 | - } | ||
| 237 | - | ||
| 238 | private void dispatch(IntentEvent event) { | 244 | private void dispatch(IntentEvent event) { |
| 239 | for (IntentListener listener : listeners) { | 245 | for (IntentListener listener : listeners) { |
| 240 | listener.event(event); | 246 | listener.event(event); | ... | ... |
| ... | @@ -33,6 +33,11 @@ public class IntentServiceAdapter implements IntentService { | ... | @@ -33,6 +33,11 @@ public class IntentServiceAdapter implements IntentService { |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | @Override | 35 | @Override |
| 36 | + public void purge(Intent intent) { | ||
| 37 | + | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 36 | public Iterable<Intent> getIntents() { | 41 | public Iterable<Intent> getIntents() { |
| 37 | return null; | 42 | return null; |
| 38 | } | 43 | } |
| ... | @@ -76,9 +81,4 @@ public class IntentServiceAdapter implements IntentService { | ... | @@ -76,9 +81,4 @@ public class IntentServiceAdapter implements IntentService { |
| 76 | public void removeListener(IntentListener listener) { | 81 | public void removeListener(IntentListener listener) { |
| 77 | 82 | ||
| 78 | } | 83 | } |
| 79 | - | ||
| 80 | - @Override | ||
| 81 | - public void purge(Key key) { | ||
| 82 | - | ||
| 83 | - } | ||
| 84 | } | 84 | } | ... | ... |
| ... | @@ -151,6 +151,13 @@ public class IntentManager | ... | @@ -151,6 +151,13 @@ public class IntentManager |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | @Override | 153 | @Override |
| 154 | + public void purge(Intent intent) { | ||
| 155 | + checkNotNull(intent, INTENT_NULL); | ||
| 156 | + IntentData data = new IntentData(intent, IntentState.PURGE_REQ, null); | ||
| 157 | + store.addPending(data); | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + @Override | ||
| 154 | public Intent getIntent(Key key) { | 161 | public Intent getIntent(Key key) { |
| 155 | return store.getIntent(key); | 162 | return store.getIntent(key); |
| 156 | } | 163 | } |
| ... | @@ -193,11 +200,6 @@ public class IntentManager | ... | @@ -193,11 +200,6 @@ public class IntentManager |
| 193 | } | 200 | } |
| 194 | 201 | ||
| 195 | @Override | 202 | @Override |
| 196 | - public void purge(Key key) { | ||
| 197 | - store.purge(key); | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - @Override | ||
| 201 | public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) { | 203 | public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) { |
| 202 | compilerRegistry.registerCompiler(cls, compiler); | 204 | compilerRegistry.registerCompiler(cls, compiler); |
| 203 | } | 205 | } | ... | ... |
| ... | @@ -56,6 +56,8 @@ public interface IntentProcessPhase { | ... | @@ -56,6 +56,8 @@ public interface IntentProcessPhase { |
| 56 | } else { | 56 | } else { |
| 57 | return new WithdrawRequest(processor, data, current); | 57 | return new WithdrawRequest(processor, data, current); |
| 58 | } | 58 | } |
| 59 | + case PURGE_REQ: | ||
| 60 | + return new PurgeRequest(data, current); | ||
| 59 | default: | 61 | default: |
| 60 | // illegal state | 62 | // illegal state |
| 61 | return new CompilingFailed(data); | 63 | return new CompilingFailed(data); | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2015 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 | + */ | ||
| 16 | +package org.onosproject.net.intent.impl.phase; | ||
| 17 | + | ||
| 18 | +import org.onosproject.net.intent.IntentData; | ||
| 19 | +import org.onosproject.net.intent.IntentState; | ||
| 20 | +import org.slf4j.Logger; | ||
| 21 | + | ||
| 22 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 23 | +import static org.slf4j.LoggerFactory.getLogger; | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * Represents a phase of requesting a purge of an intent. | ||
| 27 | + * <p> | ||
| 28 | + * Note: The purge will only succeed if the intent is FAILED or WITHDRAWN. | ||
| 29 | + * </p> | ||
| 30 | + */ | ||
| 31 | +final class PurgeRequest extends FinalIntentProcessPhase { | ||
| 32 | + | ||
| 33 | + private static final Logger log = getLogger(PurgeRequest.class); | ||
| 34 | + | ||
| 35 | + private final IntentData pending; | ||
| 36 | + private final IntentData current; | ||
| 37 | + | ||
| 38 | + PurgeRequest(IntentData intentData, IntentData current) { | ||
| 39 | + this.pending = checkNotNull(intentData); | ||
| 40 | + this.current = checkNotNull(current); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + private boolean shouldAcceptPurge() { | ||
| 44 | + if (current.state() == IntentState.WITHDRAWN | ||
| 45 | + || current.state() == IntentState.FAILED) { | ||
| 46 | + return true; | ||
| 47 | + } | ||
| 48 | + log.info("Purge for intent {} is rejected", pending.key()); | ||
| 49 | + return false; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Override | ||
| 53 | + public IntentData data() { | ||
| 54 | + if (shouldAcceptPurge()) { | ||
| 55 | + return pending; | ||
| 56 | + } else { | ||
| 57 | + return current; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | +} |
| ... | @@ -412,6 +412,11 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -412,6 +412,11 @@ public class EventuallyConsistentMapImpl<K, V> |
| 412 | peerUpdateFunction.apply(key, value)); | 412 | peerUpdateFunction.apply(key, value)); |
| 413 | notifyListeners(new EventuallyConsistentMapEvent<>( | 413 | notifyListeners(new EventuallyConsistentMapEvent<>( |
| 414 | EventuallyConsistentMapEvent.Type.REMOVE, key, value)); | 414 | EventuallyConsistentMapEvent.Type.REMOVE, key, value)); |
| 415 | + } else { | ||
| 416 | + // TODO remove this extra call when ONOS-1207 is resolved | ||
| 417 | + Timestamped<V> latest = (Timestamped) items.get(key); | ||
| 418 | + log.info("Remove of intent {} failed; request time {} vs. latest time {}", | ||
| 419 | + key, timestamp, latest.timestamp()); | ||
| 415 | } | 420 | } |
| 416 | } | 421 | } |
| 417 | 422 | ... | ... |
| ... | @@ -224,6 +224,8 @@ public class GossipIntentStore | ... | @@ -224,6 +224,8 @@ public class GossipIntentStore |
| 224 | } | 224 | } |
| 225 | return true; | 225 | return true; |
| 226 | 226 | ||
| 227 | + case PURGE_REQ: | ||
| 228 | + return true; | ||
| 227 | 229 | ||
| 228 | case COMPILING: | 230 | case COMPILING: |
| 229 | case RECOMPILING: | 231 | case RECOMPILING: |
| ... | @@ -241,7 +243,11 @@ public class GossipIntentStore | ... | @@ -241,7 +243,11 @@ public class GossipIntentStore |
| 241 | if (isUpdateAcceptable(currentData, newData)) { | 243 | if (isUpdateAcceptable(currentData, newData)) { |
| 242 | // Only the master is modifying the current state. Therefore assume | 244 | // Only the master is modifying the current state. Therefore assume |
| 243 | // this always succeeds | 245 | // this always succeeds |
| 246 | + if (newData.state() == PURGE_REQ) { | ||
| 247 | + currentMap.remove(newData.key(), newData); | ||
| 248 | + } else { | ||
| 244 | currentMap.put(newData.key(), copyData(newData)); | 249 | currentMap.put(newData.key(), copyData(newData)); |
| 250 | + } | ||
| 245 | 251 | ||
| 246 | // if current.put succeeded | 252 | // if current.put succeeded |
| 247 | pendingMap.remove(newData.key(), newData); | 253 | pendingMap.remove(newData.key(), newData); |
| ... | @@ -325,14 +331,6 @@ public class GossipIntentStore | ... | @@ -325,14 +331,6 @@ public class GossipIntentStore |
| 325 | .collect(Collectors.toList()); | 331 | .collect(Collectors.toList()); |
| 326 | } | 332 | } |
| 327 | 333 | ||
| 328 | - @Override | ||
| 329 | - public void purge(Key key) { | ||
| 330 | - IntentData data = currentMap.get(key); | ||
| 331 | - if (data.state() == WITHDRAWN || data.state() == FAILED) { | ||
| 332 | - currentMap.remove(key, data); | ||
| 333 | - } | ||
| 334 | - } | ||
| 335 | - | ||
| 336 | private void notifyDelegateIfNotNull(IntentEvent event) { | 334 | private void notifyDelegateIfNotNull(IntentEvent event) { |
| 337 | if (event != null) { | 335 | if (event != null) { |
| 338 | notifyDelegate(event); | 336 | notifyDelegate(event); | ... | ... |
| ... | @@ -180,7 +180,11 @@ public class SimpleIntentStore | ... | @@ -180,7 +180,11 @@ public class SimpleIntentStore |
| 180 | IntentData pendingData = pending.get(newData.key()); | 180 | IntentData pendingData = pending.get(newData.key()); |
| 181 | 181 | ||
| 182 | if (isUpdateAcceptable(currentData, newData)) { | 182 | if (isUpdateAcceptable(currentData, newData)) { |
| 183 | + if (pendingData.state() == PURGE_REQ) { | ||
| 184 | + current.remove(newData.key(), newData); | ||
| 185 | + } else { | ||
| 183 | current.put(newData.key(), copyData(newData)); | 186 | current.put(newData.key(), copyData(newData)); |
| 187 | + } | ||
| 184 | 188 | ||
| 185 | if (pendingData != null | 189 | if (pendingData != null |
| 186 | // pendingData version is less than or equal to newData's | 190 | // pendingData version is less than or equal to newData's |
| ... | @@ -253,12 +257,4 @@ public class SimpleIntentStore | ... | @@ -253,12 +257,4 @@ public class SimpleIntentStore |
| 253 | .map(IntentData::intent) | 257 | .map(IntentData::intent) |
| 254 | .collect(Collectors.toList()); | 258 | .collect(Collectors.toList()); |
| 255 | } | 259 | } |
| 256 | - | ||
| 257 | - @Override | ||
| 258 | - public void purge(Key key) { | ||
| 259 | - IntentData data = current.get(key); | ||
| 260 | - if (data.state() == IntentState.WITHDRAWN || data.state() == IntentState.FAILED) { | ||
| 261 | - current.remove(key, data); | ||
| 262 | - } | ||
| 263 | - } | ||
| 264 | } | 260 | } | ... | ... |
-
Please register or login to post a comment