Jonathan Hart
Committed by Gerrit Code Review

Moved duplicated isUpdateAcceptable method to IntentData

and wrote a unit test for it.

Change-Id: I8b38476c41fba70abbba7ed0b37364696f17966d
...@@ -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 {
......