Committed by
Gerrit Code Review
Moved duplicated isUpdateAcceptable method to IntentData
and wrote a unit test for it. Change-Id: I8b38476c41fba70abbba7ed0b37364696f17966d
Showing
5 changed files
with
168 additions
and
148 deletions
... | @@ -19,11 +19,19 @@ import com.google.common.base.MoreObjects; | ... | @@ -19,11 +19,19 @@ import com.google.common.base.MoreObjects; |
19 | import com.google.common.collect.ImmutableList; | 19 | import com.google.common.collect.ImmutableList; |
20 | import org.onosproject.cluster.NodeId; | 20 | import org.onosproject.cluster.NodeId; |
21 | import org.onosproject.store.Timestamp; | 21 | import org.onosproject.store.Timestamp; |
22 | +import org.slf4j.Logger; | ||
23 | +import org.slf4j.LoggerFactory; | ||
22 | 24 | ||
23 | import java.util.List; | 25 | import java.util.List; |
24 | import java.util.Objects; | 26 | import java.util.Objects; |
25 | 27 | ||
26 | import static com.google.common.base.Preconditions.checkNotNull; | 28 | import static com.google.common.base.Preconditions.checkNotNull; |
29 | +import static org.onosproject.net.intent.IntentState.FAILED; | ||
30 | +import static org.onosproject.net.intent.IntentState.INSTALLED; | ||
31 | +import static org.onosproject.net.intent.IntentState.INSTALLING; | ||
32 | +import static org.onosproject.net.intent.IntentState.PURGE_REQ; | ||
33 | +import static org.onosproject.net.intent.IntentState.WITHDRAWING; | ||
34 | +import static org.onosproject.net.intent.IntentState.WITHDRAWN; | ||
27 | 35 | ||
28 | /** | 36 | /** |
29 | * A wrapper class that contains an intents, its state, and other metadata for | 37 | * A wrapper class that contains an intents, its state, and other metadata for |
... | @@ -31,6 +39,9 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -31,6 +39,9 @@ import static com.google.common.base.Preconditions.checkNotNull; |
31 | */ | 39 | */ |
32 | public class IntentData { //FIXME need to make this "immutable" | 40 | public class IntentData { //FIXME need to make this "immutable" |
33 | // manager should be able to mutate a local copy while processing | 41 | // manager should be able to mutate a local copy while processing |
42 | + | ||
43 | + private static final Logger log = LoggerFactory.getLogger(IntentData.class); | ||
44 | + | ||
34 | private final Intent intent; | 45 | private final Intent intent; |
35 | 46 | ||
36 | private IntentState state; | 47 | private IntentState state; |
... | @@ -167,6 +178,81 @@ public class IntentData { //FIXME need to make this "immutable" | ... | @@ -167,6 +178,81 @@ public class IntentData { //FIXME need to make this "immutable" |
167 | return installables; | 178 | return installables; |
168 | } | 179 | } |
169 | 180 | ||
181 | + /** | ||
182 | + * Determines whether an intent data update is allowed. The update must | ||
183 | + * either have a higher version than the current data, or the state | ||
184 | + * transition between two updates of the same version must be sane. | ||
185 | + * | ||
186 | + * @param currentData existing intent data in the store | ||
187 | + * @param newData new intent data update proposal | ||
188 | + * @return true if we can apply the update, otherwise false | ||
189 | + */ | ||
190 | + public static boolean isUpdateAcceptable(IntentData currentData, IntentData newData) { | ||
191 | + | ||
192 | + if (currentData == null) { | ||
193 | + return true; | ||
194 | + } else if (currentData.version().compareTo(newData.version()) < 0) { | ||
195 | + return true; | ||
196 | + } else if (currentData.version().compareTo(newData.version()) > 0) { | ||
197 | + return false; | ||
198 | + } | ||
199 | + | ||
200 | + // current and new data versions are the same | ||
201 | + IntentState currentState = currentData.state(); | ||
202 | + IntentState newState = newData.state(); | ||
203 | + | ||
204 | + switch (newState) { | ||
205 | + case INSTALLING: | ||
206 | + if (currentState == INSTALLING) { | ||
207 | + return false; | ||
208 | + } | ||
209 | + // FALLTHROUGH | ||
210 | + case INSTALLED: | ||
211 | + if (currentState == INSTALLED) { | ||
212 | + return false; | ||
213 | + } else if (currentState == WITHDRAWING || currentState == WITHDRAWN | ||
214 | + || currentState == PURGE_REQ) { | ||
215 | + log.warn("Invalid state transition from {} to {} for intent {}", | ||
216 | + currentState, newState, newData.key()); | ||
217 | + return false; | ||
218 | + } | ||
219 | + return true; | ||
220 | + | ||
221 | + case WITHDRAWING: | ||
222 | + if (currentState == WITHDRAWING) { | ||
223 | + return false; | ||
224 | + } | ||
225 | + // FALLTHROUGH | ||
226 | + case WITHDRAWN: | ||
227 | + if (currentState == WITHDRAWN) { | ||
228 | + return false; | ||
229 | + } else if (currentState == INSTALLING || currentState == INSTALLED | ||
230 | + || currentState == PURGE_REQ) { | ||
231 | + log.warn("Invalid state transition from {} to {} for intent {}", | ||
232 | + currentState, newState, newData.key()); | ||
233 | + return false; | ||
234 | + } | ||
235 | + return true; | ||
236 | + | ||
237 | + case FAILED: | ||
238 | + if (currentState == FAILED) { | ||
239 | + return false; | ||
240 | + } | ||
241 | + return true; | ||
242 | + | ||
243 | + case PURGE_REQ: | ||
244 | + return true; | ||
245 | + | ||
246 | + case COMPILING: | ||
247 | + case RECOMPILING: | ||
248 | + case INSTALL_REQ: | ||
249 | + case WITHDRAW_REQ: | ||
250 | + default: | ||
251 | + log.warn("Invalid state {} for intent {}", newState, newData.key()); | ||
252 | + return false; | ||
253 | + } | ||
254 | + } | ||
255 | + | ||
170 | @Override | 256 | @Override |
171 | public int hashCode() { | 257 | public int hashCode() { |
172 | return Objects.hash(intent, version); | 258 | return Objects.hash(intent, version); | ... | ... |
... | @@ -23,8 +23,10 @@ import org.onosproject.store.Timestamp; | ... | @@ -23,8 +23,10 @@ import org.onosproject.store.Timestamp; |
23 | 23 | ||
24 | import com.google.common.testing.EqualsTester; | 24 | import com.google.common.testing.EqualsTester; |
25 | 25 | ||
26 | +import static junit.framework.TestCase.assertFalse; | ||
26 | import static org.hamcrest.MatcherAssert.assertThat; | 27 | import static org.hamcrest.MatcherAssert.assertThat; |
27 | import static org.hamcrest.Matchers.is; | 28 | import static org.hamcrest.Matchers.is; |
29 | +import static org.junit.Assert.assertTrue; | ||
28 | import static org.onosproject.net.intent.IntentTestsMocks.MockIntent; | 30 | import static org.onosproject.net.intent.IntentTestsMocks.MockIntent; |
29 | import static org.onosproject.net.intent.IntentTestsMocks.MockTimestamp; | 31 | import static org.onosproject.net.intent.IntentTestsMocks.MockTimestamp; |
30 | 32 | ||
... | @@ -97,4 +99,81 @@ public class IntentDataTest { | ... | @@ -97,4 +99,81 @@ public class IntentDataTest { |
97 | .addEqualityGroup(data3, data3Copy) | 99 | .addEqualityGroup(data3, data3Copy) |
98 | .testEquals(); | 100 | .testEquals(); |
99 | } | 101 | } |
102 | + | ||
103 | + @Test | ||
104 | + public void testIsUpdateAcceptable() { | ||
105 | + // Going from null to something is always allowed | ||
106 | + assertTrue(IntentData.isUpdateAcceptable(null, data1)); | ||
107 | + | ||
108 | + // we can go from older version to newer but not they other way | ||
109 | + assertTrue(IntentData.isUpdateAcceptable(data1, data2)); | ||
110 | + assertFalse(IntentData.isUpdateAcceptable(data2, data1)); | ||
111 | + | ||
112 | + IntentData installing = new IntentData(intent1, IntentState.INSTALLING, timestamp1); | ||
113 | + IntentData installed = new IntentData(intent1, IntentState.INSTALLED, timestamp1); | ||
114 | + IntentData withdrawing = new IntentData(intent1, IntentState.WITHDRAWING, timestamp1); | ||
115 | + IntentData withdrawn = new IntentData(intent1, IntentState.WITHDRAWN, timestamp1); | ||
116 | + | ||
117 | + IntentData failed = new IntentData(intent1, IntentState.FAILED, timestamp1); | ||
118 | + IntentData purgeReq = new IntentData(intent1, IntentState.PURGE_REQ, timestamp1); | ||
119 | + | ||
120 | + IntentData compiling = new IntentData(intent1, IntentState.COMPILING, timestamp1); | ||
121 | + IntentData recompiling = new IntentData(intent1, IntentState.RECOMPILING, timestamp1); | ||
122 | + IntentData installReq = new IntentData(intent1, IntentState.INSTALL_REQ, timestamp1); | ||
123 | + IntentData withdrawReq = new IntentData(intent1, IntentState.WITHDRAW_REQ, timestamp1); | ||
124 | + | ||
125 | + // We can't change to the same state | ||
126 | + assertFalse(IntentData.isUpdateAcceptable(installing, installing)); | ||
127 | + assertFalse(IntentData.isUpdateAcceptable(installed, installed)); | ||
128 | + | ||
129 | + // From installing we can change to installed | ||
130 | + assertTrue(IntentData.isUpdateAcceptable(installing, installed)); | ||
131 | + | ||
132 | + // Sanity checks in case the manager submits bogus state transitions | ||
133 | + assertFalse(IntentData.isUpdateAcceptable(installing, withdrawing)); | ||
134 | + assertFalse(IntentData.isUpdateAcceptable(installing, withdrawn)); | ||
135 | + assertFalse(IntentData.isUpdateAcceptable(installed, withdrawing)); | ||
136 | + assertFalse(IntentData.isUpdateAcceptable(installed, withdrawn)); | ||
137 | + | ||
138 | + // We can't change to the same state | ||
139 | + assertFalse(IntentData.isUpdateAcceptable(withdrawing, withdrawing)); | ||
140 | + assertFalse(IntentData.isUpdateAcceptable(withdrawn, withdrawn)); | ||
141 | + | ||
142 | + // From withdrawing we can change to withdrawn | ||
143 | + assertTrue(IntentData.isUpdateAcceptable(withdrawing, withdrawn)); | ||
144 | + | ||
145 | + // Sanity checks in case the manager submits bogus state transitions | ||
146 | + assertFalse(IntentData.isUpdateAcceptable(withdrawing, installing)); | ||
147 | + assertFalse(IntentData.isUpdateAcceptable(withdrawing, installed)); | ||
148 | + assertFalse(IntentData.isUpdateAcceptable(withdrawn, installing)); | ||
149 | + assertFalse(IntentData.isUpdateAcceptable(withdrawn, installed)); | ||
150 | + | ||
151 | + // We can't go from failed to failed | ||
152 | + assertFalse(IntentData.isUpdateAcceptable(failed, failed)); | ||
153 | + | ||
154 | + // But we can go from any install* or withdraw* state to failed | ||
155 | + assertTrue(IntentData.isUpdateAcceptable(installing, failed)); | ||
156 | + assertTrue(IntentData.isUpdateAcceptable(installed, failed)); | ||
157 | + assertTrue(IntentData.isUpdateAcceptable(withdrawing, failed)); | ||
158 | + assertTrue(IntentData.isUpdateAcceptable(withdrawn, failed)); | ||
159 | + | ||
160 | + // We can go from anything to purgeReq | ||
161 | + assertTrue(IntentData.isUpdateAcceptable(installing, purgeReq)); | ||
162 | + assertTrue(IntentData.isUpdateAcceptable(installed, purgeReq)); | ||
163 | + assertTrue(IntentData.isUpdateAcceptable(withdrawing, purgeReq)); | ||
164 | + assertTrue(IntentData.isUpdateAcceptable(withdrawn, purgeReq)); | ||
165 | + assertTrue(IntentData.isUpdateAcceptable(failed, purgeReq)); | ||
166 | + | ||
167 | + // We can't go from purgeReq back to anything else | ||
168 | + assertFalse(IntentData.isUpdateAcceptable(purgeReq, withdrawn)); | ||
169 | + assertFalse(IntentData.isUpdateAcceptable(purgeReq, withdrawing)); | ||
170 | + assertFalse(IntentData.isUpdateAcceptable(purgeReq, installed)); | ||
171 | + assertFalse(IntentData.isUpdateAcceptable(purgeReq, installing)); | ||
172 | + | ||
173 | + // We're never allowed to store transient states | ||
174 | + assertFalse(IntentData.isUpdateAcceptable(installing, compiling)); | ||
175 | + assertFalse(IntentData.isUpdateAcceptable(installing, recompiling)); | ||
176 | + assertFalse(IntentData.isUpdateAcceptable(installing, installReq)); | ||
177 | + assertFalse(IntentData.isUpdateAcceptable(installing, withdrawReq)); | ||
178 | + } | ||
100 | } | 179 | } | ... | ... |
... | @@ -454,9 +454,8 @@ public class IntentTestsMocks { | ... | @@ -454,9 +454,8 @@ public class IntentTestsMocks { |
454 | return -1; | 454 | return -1; |
455 | } | 455 | } |
456 | MockTimestamp that = (MockTimestamp) o; | 456 | MockTimestamp that = (MockTimestamp) o; |
457 | - return (this.value > that.value ? -1 : (this.value == that.value ? 0 : 1)); | 457 | + return this.value - that.value; |
458 | } | 458 | } |
459 | } | 459 | } |
460 | 460 | ||
461 | - | ||
462 | } | 461 | } | ... | ... |
... | @@ -150,86 +150,14 @@ public class GossipIntentStore | ... | @@ -150,86 +150,14 @@ public class GossipIntentStore |
150 | return null; | 150 | return null; |
151 | } | 151 | } |
152 | 152 | ||
153 | - /** | ||
154 | - * Determines whether an intent data update is allowed. The update must | ||
155 | - * either have a higher version than the current data, or the state | ||
156 | - * transition between two updates of the same version must be sane. | ||
157 | - * | ||
158 | - * @param currentData existing intent data in the store | ||
159 | - * @param newData new intent data update proposal | ||
160 | - * @return true if we can apply the update, otherwise false | ||
161 | - */ | ||
162 | - private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) { | ||
163 | - | ||
164 | - if (currentData == null) { | ||
165 | - return true; | ||
166 | - } else if (currentData.version().compareTo(newData.version()) < 0) { | ||
167 | - return true; | ||
168 | - } else if (currentData.version().compareTo(newData.version()) > 0) { | ||
169 | - return false; | ||
170 | - } | ||
171 | - | ||
172 | - // current and new data versions are the same | ||
173 | - IntentState currentState = currentData.state(); | ||
174 | - IntentState newState = newData.state(); | ||
175 | 153 | ||
176 | - switch (newState) { | ||
177 | - case INSTALLING: | ||
178 | - if (currentState == INSTALLING) { | ||
179 | - return false; | ||
180 | - } | ||
181 | - // FALLTHROUGH | ||
182 | - case INSTALLED: | ||
183 | - if (currentState == INSTALLED) { | ||
184 | - return false; | ||
185 | - } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) { | ||
186 | - log.warn("Invalid state transition from {} to {} for intent {}", | ||
187 | - currentState, newState, newData.key()); | ||
188 | - return false; | ||
189 | - } | ||
190 | - return true; | ||
191 | - | ||
192 | - case WITHDRAWING: | ||
193 | - if (currentState == WITHDRAWING) { | ||
194 | - return false; | ||
195 | - } | ||
196 | - // FALLTHROUGH | ||
197 | - case WITHDRAWN: | ||
198 | - if (currentState == WITHDRAWN) { | ||
199 | - return false; | ||
200 | - } else if (currentState == INSTALLING || currentState == INSTALLED) { | ||
201 | - log.warn("Invalid state transition from {} to {} for intent {}", | ||
202 | - currentState, newState, newData.key()); | ||
203 | - return false; | ||
204 | - } | ||
205 | - return true; | ||
206 | - | ||
207 | - | ||
208 | - case FAILED: | ||
209 | - if (currentState == FAILED) { | ||
210 | - return false; | ||
211 | - } | ||
212 | - return true; | ||
213 | - | ||
214 | - case PURGE_REQ: | ||
215 | - return true; | ||
216 | - | ||
217 | - case COMPILING: | ||
218 | - case RECOMPILING: | ||
219 | - case INSTALL_REQ: | ||
220 | - case WITHDRAW_REQ: | ||
221 | - default: | ||
222 | - log.warn("Invalid state {} for intent {}", newState, newData.key()); | ||
223 | - return false; | ||
224 | - } | ||
225 | - } | ||
226 | 154 | ||
227 | @Override | 155 | @Override |
228 | public void write(IntentData newData) { | 156 | public void write(IntentData newData) { |
229 | checkNotNull(newData); | 157 | checkNotNull(newData); |
230 | 158 | ||
231 | IntentData currentData = currentMap.get(newData.key()); | 159 | IntentData currentData = currentMap.get(newData.key()); |
232 | - if (isUpdateAcceptable(currentData, newData)) { | 160 | + if (IntentData.isUpdateAcceptable(currentData, newData)) { |
233 | // Only the master is modifying the current state. Therefore assume | 161 | // Only the master is modifying the current state. Therefore assume |
234 | // this always succeeds | 162 | // this always succeeds |
235 | if (newData.state() == PURGE_REQ) { | 163 | if (newData.state() == PURGE_REQ) { | ... | ... |
... | @@ -89,78 +89,6 @@ public class SimpleIntentStore | ... | @@ -89,78 +89,6 @@ public class SimpleIntentStore |
89 | return null; | 89 | return null; |
90 | } | 90 | } |
91 | 91 | ||
92 | - /** | ||
93 | - * Determines whether an intent data update is allowed. The update must | ||
94 | - * either have a higher version than the current data, or the state | ||
95 | - * transition between two updates of the same version must be sane. | ||
96 | - * | ||
97 | - * @param currentData existing intent data in the store | ||
98 | - * @param newData new intent data update proposal | ||
99 | - * @return true if we can apply the update, otherwise false | ||
100 | - */ | ||
101 | - private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) { | ||
102 | - | ||
103 | - if (currentData == null) { | ||
104 | - return true; | ||
105 | - } else if (currentData.version().compareTo(newData.version()) < 0) { | ||
106 | - return true; | ||
107 | - } else if (currentData.version().compareTo(newData.version()) > 0) { | ||
108 | - return false; | ||
109 | - } | ||
110 | - | ||
111 | - // current and new data versions are the same | ||
112 | - IntentState currentState = currentData.state(); | ||
113 | - IntentState newState = newData.state(); | ||
114 | - | ||
115 | - switch (newState) { | ||
116 | - case INSTALLING: | ||
117 | - if (currentState == INSTALLING) { | ||
118 | - return false; | ||
119 | - } | ||
120 | - // FALLTHROUGH | ||
121 | - case INSTALLED: | ||
122 | - if (currentState == INSTALLED) { | ||
123 | - return false; | ||
124 | - } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) { | ||
125 | - log.warn("Invalid state transition from {} to {} for intent {}", | ||
126 | - currentState, newState, newData.key()); | ||
127 | - return false; | ||
128 | - } | ||
129 | - return true; | ||
130 | - | ||
131 | - case WITHDRAWING: | ||
132 | - if (currentState == WITHDRAWING) { | ||
133 | - return false; | ||
134 | - } | ||
135 | - // FALLTHOUGH | ||
136 | - case WITHDRAWN: | ||
137 | - if (currentState == WITHDRAWN) { | ||
138 | - return false; | ||
139 | - } else if (currentState == INSTALLING || currentState == INSTALLED) { | ||
140 | - log.warn("Invalid state transition from {} to {} for intent {}", | ||
141 | - currentState, newState, newData.key()); | ||
142 | - return false; | ||
143 | - } | ||
144 | - return true; | ||
145 | - | ||
146 | - | ||
147 | - case FAILED: | ||
148 | - if (currentState == FAILED) { | ||
149 | - return false; | ||
150 | - } | ||
151 | - return true; | ||
152 | - | ||
153 | - | ||
154 | - case COMPILING: | ||
155 | - case RECOMPILING: | ||
156 | - case INSTALL_REQ: | ||
157 | - case WITHDRAW_REQ: | ||
158 | - default: | ||
159 | - log.warn("Invalid state {} for intent {}", newState, newData.key()); | ||
160 | - return false; | ||
161 | - } | ||
162 | - } | ||
163 | - | ||
164 | @Override | 92 | @Override |
165 | public void write(IntentData newData) { | 93 | public void write(IntentData newData) { |
166 | checkNotNull(newData); | 94 | checkNotNull(newData); |
... | @@ -170,7 +98,7 @@ public class SimpleIntentStore | ... | @@ -170,7 +98,7 @@ public class SimpleIntentStore |
170 | IntentData currentData = current.get(newData.key()); | 98 | IntentData currentData = current.get(newData.key()); |
171 | IntentData pendingData = pending.get(newData.key()); | 99 | IntentData pendingData = pending.get(newData.key()); |
172 | 100 | ||
173 | - if (isUpdateAcceptable(currentData, newData)) { | 101 | + if (IntentData.isUpdateAcceptable(currentData, newData)) { |
174 | if (pendingData.state() == PURGE_REQ) { | 102 | if (pendingData.state() == PURGE_REQ) { |
175 | current.remove(newData.key(), newData); | 103 | current.remove(newData.key(), newData); |
176 | } else { | 104 | } else { | ... | ... |
-
Please register or login to post a comment