Ray Milkey
Committed by Brian O'Connor

[Blackbird] Fixing Intent purge case (ONOS-1207)

Master needs to remove from the current map.

Change-Id: I30eccbe188997949ef2d63d6dbd37b0d8d4b3f5e
...@@ -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 - // set up latch and listener to track uninstall progress 88 + IntentListener listener = null;
89 - CountDownLatch latch = new CountDownLatch(1); 89 + final CountDownLatch withdrawLatch, purgeLatch;
90 - IntentListener listener = (IntentEvent event) -> { 90 + if (purgeAfterRemove || sync) {
91 - if (Objects.equals(event.subject().key(), key) && 91 + // set up latch and listener to track uninstall progress
92 - (event.type() == IntentEvent.Type.WITHDRAWN || 92 + withdrawLatch = new CountDownLatch(1);
93 - event.type() == IntentEvent.Type.FAILED)) { 93 + purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
94 - latch.countDown(); 94 + listener = (IntentEvent event) -> {
95 - } 95 + if (Objects.equals(event.subject().key(), key)) {
96 - }; 96 + if (event.type() == IntentEvent.Type.WITHDRAWN ||
97 - intentService.addListener(listener); 97 + event.type() == IntentEvent.Type.FAILED) {
98 + withdrawLatch.countDown();
99 + } else if (purgeAfterRemove &&
100 + event.type() == IntentEvent.Type.PURGED) {
101 + purgeLatch.countDown();
102 + }
103 + }
104 + };
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 }
114 - // clean up the listener
115 - intentService.removeListener(listener);
116 - }
117 138
139 + if (listener != null) {
140 + // clean up the listener
141 + intentService.removeListener(listener);
142 + }
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
244 - currentMap.put(newData.key(), copyData(newData)); 246 + if (newData.state() == PURGE_REQ) {
247 + currentMap.remove(newData.key(), newData);
248 + } else {
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 - current.put(newData.key(), copyData(newData)); 183 + if (pendingData.state() == PURGE_REQ) {
184 + current.remove(newData.key(), newData);
185 + } else {
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 }
......