Jonathan Hart
Committed by Gerrit Code Review

Distinguish between route added and route updated

Change-Id: Ia82ccf8e457bf07c9a8eed8141df013030eb8389
...@@ -65,6 +65,7 @@ public class RouteManagerTest { ...@@ -65,6 +65,7 @@ public class RouteManagerTest {
65 PortNumber.portNumber(1)); 65 PortNumber.portNumber(1));
66 66
67 private static final IpPrefix V4_PREFIX1 = Ip4Prefix.valueOf("1.1.1.0/24"); 67 private static final IpPrefix V4_PREFIX1 = Ip4Prefix.valueOf("1.1.1.0/24");
68 + private static final IpPrefix V4_PREFIX2 = Ip4Prefix.valueOf("2.2.2.0/24");
68 private static final IpPrefix V6_PREFIX1 = Ip6Prefix.valueOf("4000::/64"); 69 private static final IpPrefix V6_PREFIX1 = Ip6Prefix.valueOf("4000::/64");
69 70
70 private static final IpAddress V4_NEXT_HOP1 = Ip4Address.valueOf("192.168.10.1"); 71 private static final IpAddress V4_NEXT_HOP1 = Ip4Address.valueOf("192.168.10.1");
...@@ -175,16 +176,16 @@ public class RouteManagerTest { ...@@ -175,16 +176,16 @@ public class RouteManagerTest {
175 * Tests adding routes to the route manager. 176 * Tests adding routes to the route manager.
176 */ 177 */
177 @Test 178 @Test
178 - public void testIpv4RouteAdd() { 179 + public void testRouteAdd() {
179 Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1); 180 Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
180 ResolvedRoute resolvedRoute = new ResolvedRoute(route, MAC1); 181 ResolvedRoute resolvedRoute = new ResolvedRoute(route, MAC1);
181 182
182 - testRouteAdd(route, resolvedRoute); 183 + verifyRouteAdd(route, resolvedRoute);
183 184
184 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1); 185 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
185 resolvedRoute = new ResolvedRoute(route, MAC3); 186 resolvedRoute = new ResolvedRoute(route, MAC3);
186 187
187 - testRouteAdd(route, resolvedRoute); 188 + verifyRouteAdd(route, resolvedRoute);
188 } 189 }
189 190
190 /** 191 /**
...@@ -195,10 +196,10 @@ public class RouteManagerTest { ...@@ -195,10 +196,10 @@ public class RouteManagerTest {
195 * @param resolvedRoute resolved route that should be sent to the route 196 * @param resolvedRoute resolved route that should be sent to the route
196 * listener 197 * listener
197 */ 198 */
198 - private void testRouteAdd(Route route, ResolvedRoute resolvedRoute) { 199 + private void verifyRouteAdd(Route route, ResolvedRoute resolvedRoute) {
199 reset(routeListener); 200 reset(routeListener);
200 201
201 - routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, resolvedRoute)); 202 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_ADDED, resolvedRoute));
202 203
203 replay(routeListener); 204 replay(routeListener);
204 205
...@@ -216,31 +217,63 @@ public class RouteManagerTest { ...@@ -216,31 +217,63 @@ public class RouteManagerTest {
216 Route updatedRoute = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP2); 217 Route updatedRoute = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP2);
217 ResolvedRoute updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC2); 218 ResolvedRoute updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC2);
218 219
219 - testRouteUpdated(route, updatedRoute, updatedResolvedRoute); 220 + verifyRouteRemoveThenAdd(route, updatedRoute, updatedResolvedRoute);
221 +
222 + // Different prefix pointing to the same next hop.
223 + // In this case we expect to receive a ROUTE_UPDATED event.
224 + route = new Route(Route.Source.STATIC, V4_PREFIX2, V4_NEXT_HOP1);
225 + updatedRoute = new Route(Route.Source.STATIC, V4_PREFIX2, V4_NEXT_HOP2);
226 + updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC2);
227 +
228 + verifyRouteUpdated(route, updatedRoute, updatedResolvedRoute);
220 229
221 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1); 230 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
222 updatedRoute = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP2); 231 updatedRoute = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP2);
223 updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC4); 232 updatedResolvedRoute = new ResolvedRoute(updatedRoute, MAC4);
224 233
225 - testRouteUpdated(route, updatedRoute, updatedResolvedRoute); 234 + verifyRouteRemoveThenAdd(route, updatedRoute, updatedResolvedRoute);
226 } 235 }
227 236
228 /** 237 /**
229 - * Tests updating a route and verifies that the correct events are sent to 238 + * Tests updating a route and verifies that the route listener receives a
230 - * the route listener. 239 + * route remove event followed by a route add event.
231 * 240 *
232 * @param original original route 241 * @param original original route
233 * @param updated updated route 242 * @param updated updated route
234 * @param updatedResolvedRoute resolved route that is expected to be sent to 243 * @param updatedResolvedRoute resolved route that is expected to be sent to
235 - * the routelistener 244 + * the route listener
236 */ 245 */
237 - private void testRouteUpdated(Route original, Route updated, 246 + private void verifyRouteRemoveThenAdd(Route original, Route updated,
238 - ResolvedRoute updatedResolvedRoute) { 247 + ResolvedRoute updatedResolvedRoute) {
239 // First add the original route 248 // First add the original route
240 addRoute(original); 249 addRoute(original);
241 250
242 routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(original, null))); 251 routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(original, null)));
243 expectLastCall().once(); 252 expectLastCall().once();
253 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_ADDED, updatedResolvedRoute));
254 + expectLastCall().once();
255 +
256 + replay(routeListener);
257 +
258 + routeManager.update(Collections.singleton(updated));
259 +
260 + verify(routeListener);
261 + }
262 +
263 + /**
264 + * Tests updating a route and verifies that the route listener receives a
265 + * route updated event.
266 + *
267 + * @param original original route
268 + * @param updated updated route
269 + * @param updatedResolvedRoute resolved route that is expected to be sent to
270 + * the route listener
271 + */
272 + private void verifyRouteUpdated(Route original, Route updated,
273 + ResolvedRoute updatedResolvedRoute) {
274 + // First add the original route
275 + addRoute(original);
276 +
244 routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, updatedResolvedRoute)); 277 routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, updatedResolvedRoute));
245 expectLastCall().once(); 278 expectLastCall().once();
246 279
...@@ -255,14 +288,14 @@ public class RouteManagerTest { ...@@ -255,14 +288,14 @@ public class RouteManagerTest {
255 * Tests deleting routes from the route manager. 288 * Tests deleting routes from the route manager.
256 */ 289 */
257 @Test 290 @Test
258 - public void testIpv4RouteDelete() { 291 + public void testRouteDelete() {
259 Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1); 292 Route route = new Route(Route.Source.STATIC, V4_PREFIX1, V4_NEXT_HOP1);
260 293
261 - testDelete(route); 294 + verifyDelete(route);
262 295
263 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1); 296 route = new Route(Route.Source.STATIC, V6_PREFIX1, V6_NEXT_HOP1);
264 297
265 - testDelete(route); 298 + verifyDelete(route);
266 } 299 }
267 300
268 /** 301 /**
...@@ -271,7 +304,7 @@ public class RouteManagerTest { ...@@ -271,7 +304,7 @@ public class RouteManagerTest {
271 * 304 *
272 * @param route route to delete 305 * @param route route to delete
273 */ 306 */
274 - private void testDelete(Route route) { 307 + private void verifyDelete(Route route) {
275 addRoute(route); 308 addRoute(route);
276 309
277 RouteEvent withdrawRouteEvent = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, 310 RouteEvent withdrawRouteEvent = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED,
...@@ -288,7 +321,8 @@ public class RouteManagerTest { ...@@ -288,7 +321,8 @@ public class RouteManagerTest {
288 } 321 }
289 322
290 /** 323 /**
291 - * Tests adding a route entry with asynchronous HostService replies. 324 + * Tests adding a route entry where the HostService does not immediately
325 + * know the MAC address of the next hop, but this is learnt later.
292 */ 326 */
293 @Test 327 @Test
294 public void testAsyncRouteAdd() { 328 public void testAsyncRouteAdd() {
...@@ -311,7 +345,7 @@ public class RouteManagerTest { ...@@ -311,7 +345,7 @@ public class RouteManagerTest {
311 345
312 // Now when we send the event, we expect the FIB update to be sent 346 // Now when we send the event, we expect the FIB update to be sent
313 reset(routeListener); 347 reset(routeListener);
314 - routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, 348 + routeListener.event(new RouteEvent(RouteEvent.Type.ROUTE_ADDED,
315 new ResolvedRoute(route, MAC1))); 349 new ResolvedRoute(route, MAC1)));
316 replay(routeListener); 350 replay(routeListener);
317 351
......
...@@ -118,10 +118,16 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -118,10 +118,16 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
118 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip); 118 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
119 119
120 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) { 120 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) {
121 - nextHops.put(ip, mac); 121 + MacAddress oldMac = nextHops.put(ip, mac);
122 122
123 for (Route route : routes) { 123 for (Route route : routes) {
124 - notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, mac))); 124 + if (oldMac == null) {
125 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_ADDED,
126 + new ResolvedRoute(route, mac)));
127 + } else {
128 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED,
129 + new ResolvedRoute(route, mac)));
130 + }
125 } 131 }
126 } 132 }
127 } 133 }
...@@ -131,7 +137,8 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -131,7 +137,8 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
131 if (nextHops.remove(ip, mac)) { 137 if (nextHops.remove(ip, mac)) {
132 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip); 138 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
133 for (Route route : routes) { 139 for (Route route : routes) {
134 - notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null))); 140 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED,
141 + new ResolvedRoute(route, null)));
135 } 142 }
136 } 143 }
137 } 144 }
...@@ -211,15 +218,30 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -211,15 +218,30 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
211 } 218 }
212 } 219 }
213 220
214 - if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) { 221 + if (route.equals(oldRoute)) {
215 - // Remove old route because new one is different 222 + // No need to send events if the new route is the same
216 - // TODO ROUTE_UPDATED? 223 + return;
217 - notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(oldRoute, null)));
218 } 224 }
219 225
220 MacAddress nextHopMac = nextHops.get(route.nextHop()); 226 MacAddress nextHopMac = nextHops.get(route.nextHop());
227 +
228 + if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) {
229 + if (nextHopMac == null) {
230 + // We don't know the new MAC address yet so delete the route
231 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED,
232 + new ResolvedRoute(oldRoute, null)));
233 + } else {
234 + // We know the new MAC address so update the route
235 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED,
236 + new ResolvedRoute(route, nextHopMac)));
237 + }
238 + return;
239 + }
240 +
241 +
221 if (nextHopMac != null) { 242 if (nextHopMac != null) {
222 - notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, nextHopMac))); 243 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_ADDED,
244 + new ResolvedRoute(route, nextHopMac)));
223 } 245 }
224 } 246 }
225 } 247 }
...@@ -236,7 +258,8 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -236,7 +258,8 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
236 258
237 if (removed != null) { 259 if (removed != null) {
238 reverseIndex.remove(removed.nextHop(), removed); 260 reverseIndex.remove(removed.nextHop(), removed);
239 - notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null))); 261 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED,
262 + new ResolvedRoute(route, null)));
240 } 263 }
241 } 264 }
242 } 265 }
......