Saurav Das
Committed by Ray Milkey

In this commit:

   Bug fix where filtering objectives are not installed due to available ports becoming enabled later.
   Bug fix where flow objective store had no listener for notifications from drivers across multiple instances of the controller.
   NPE fix in ofdpa driver for non-existing groups.
   Preventing ofdpa driver from sending spurious pass notification to app.
   Incrementing retry filter timer from 1 to 5 secs in default routing handler.
   Made several debug messages clearer.

Change-Id: I828671ee4c8bcfe03c946d051e1d1aac9d8f68dd
...@@ -47,7 +47,7 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -47,7 +47,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
47 * routing rule population. 47 * routing rule population.
48 */ 48 */
49 public class DefaultRoutingHandler { 49 public class DefaultRoutingHandler {
50 - private static final int MAX_RETRY_ATTEMPTS = 5; 50 + private static final int MAX_RETRY_ATTEMPTS = 25;
51 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found"; 51 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
52 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class); 52 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
53 53
...@@ -212,11 +212,11 @@ public class DefaultRoutingHandler { ...@@ -212,11 +212,11 @@ public class DefaultRoutingHandler {
212 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0)); 212 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
213 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager); 213 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
214 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) { 214 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
215 - log.debug("Populating flow rules from {} to all is successful", 215 + log.debug("Populating flow rules from all to dest:{} is successful",
216 link.get(0)); 216 link.get(0));
217 currentEcmpSpgMap.put(link.get(0), ecmpSpg); 217 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
218 } else { 218 } else {
219 - log.warn("Failed to populate the flow rules from {} to all", link.get(0)); 219 + log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
220 return false; 220 return false;
221 } 221 }
222 } else { 222 } else {
...@@ -463,9 +463,9 @@ public class DefaultRoutingHandler { ...@@ -463,9 +463,9 @@ public class DefaultRoutingHandler {
463 /** 463 /**
464 * Populate ECMP rules for subnets from target to destination via nexthops. 464 * Populate ECMP rules for subnets from target to destination via nexthops.
465 * 465 *
466 - * @param targetSw Device ID of target switch 466 + * @param targetSw Device ID of target switch in which rules will be programmed
467 - * @param destSw Device ID of destination switch 467 + * @param destSw Device ID of final destination switch to which the rules will forward
468 - * @param nextHops List of next hops 468 + * @param nextHops List of next hops via which destSw will be reached
469 * @param subnets Subnets to be populated. If empty, populate all configured subnets. 469 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
470 * @return true if succeed 470 * @return true if succeed
471 */ 471 */
...@@ -647,6 +647,8 @@ public class DefaultRoutingHandler { ...@@ -647,6 +647,8 @@ public class DefaultRoutingHandler {
647 647
648 @Override 648 @Override
649 public void run() { 649 public void run() {
650 + log.info("RETRY FILTER ATTEMPT# {} for dev:{}",
651 + MAX_RETRY_ATTEMPTS - attempts, devId);
650 boolean success = rulePopulator.populateRouterMacVlanFilters(devId); 652 boolean success = rulePopulator.populateRouterMacVlanFilters(devId);
651 if (!success && --attempts > 0) { 653 if (!success && --attempts > 0) {
652 executorService.schedule(this, 200, TimeUnit.MILLISECONDS); 654 executorService.schedule(this, 200, TimeUnit.MILLISECONDS);
......
...@@ -239,7 +239,8 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -239,7 +239,8 @@ public class FlowObjectiveManager implements FlowObjectiveService {
239 private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) { 239 private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
240 if (fwd.nextId() != null && 240 if (fwd.nextId() != null &&
241 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) { 241 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
242 - log.trace("Queuing forwarding objective for nextId {}", fwd.nextId()); 242 + log.debug("Queuing forwarding objective {} for nextId {} meant for device {}",
243 + fwd.id(), fwd.nextId(), deviceId);
243 // TODO: change to computeIfAbsent 244 // TODO: change to computeIfAbsent
244 Set<PendingNext> newset = Collections.newSetFromMap( 245 Set<PendingNext> newset = Collections.newSetFromMap(
245 new ConcurrentHashMap<PendingNext, Boolean>()); 246 new ConcurrentHashMap<PendingNext, Boolean>());
...@@ -398,7 +399,7 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -398,7 +399,7 @@ public class FlowObjectiveManager implements FlowObjectiveService {
398 Set<PendingNext> pending = pendingForwards.remove(event.subject()); 399 Set<PendingNext> pending = pendingForwards.remove(event.subject());
399 400
400 if (pending == null) { 401 if (pending == null) {
401 - log.warn("Nothing pending for this obj event {}", event); 402 + log.debug("Nothing pending for this obj event {}", event);
402 return; 403 return;
403 } 404 }
404 405
...@@ -457,7 +458,7 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -457,7 +458,7 @@ public class FlowObjectiveManager implements FlowObjectiveService {
457 public List<String> getNextMappings() { 458 public List<String> getNextMappings() {
458 List<String> mappings = new ArrayList<>(); 459 List<String> mappings = new ArrayList<>();
459 Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups(); 460 Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
460 - // XXX if the NextGroup upon decoding stored info of the deviceId 461 + // XXX if the NextGroup after de-serialization actually stored info of the deviceId
461 // then info on any nextObj could be retrieved from one controller instance. 462 // then info on any nextObj could be retrieved from one controller instance.
462 // Right now the drivers on one instance can only fetch for next-ids that came 463 // Right now the drivers on one instance can only fetch for next-ids that came
463 // to them. 464 // to them.
......
...@@ -30,15 +30,22 @@ import org.onosproject.net.flowobjective.ObjectiveEvent; ...@@ -30,15 +30,22 @@ import org.onosproject.net.flowobjective.ObjectiveEvent;
30 import org.onosproject.store.AbstractStore; 30 import org.onosproject.store.AbstractStore;
31 import org.onosproject.store.service.AtomicCounter; 31 import org.onosproject.store.service.AtomicCounter;
32 import org.onosproject.store.service.ConsistentMap; 32 import org.onosproject.store.service.ConsistentMap;
33 +import org.onosproject.store.service.MapEvent;
34 +import org.onosproject.store.service.MapEventListener;
33 import org.onosproject.store.service.Serializer; 35 import org.onosproject.store.service.Serializer;
34 import org.onosproject.store.service.StorageService; 36 import org.onosproject.store.service.StorageService;
35 import org.onosproject.store.service.Versioned; 37 import org.onosproject.store.service.Versioned;
36 import org.slf4j.Logger; 38 import org.slf4j.Logger;
37 39
40 +import static org.onlab.util.Tools.groupedThreads;
38 import static org.slf4j.LoggerFactory.getLogger; 41 import static org.slf4j.LoggerFactory.getLogger;
39 42
40 import java.util.HashMap; 43 import java.util.HashMap;
41 import java.util.Map; 44 import java.util.Map;
45 +import java.util.concurrent.BlockingQueue;
46 +import java.util.concurrent.ExecutorService;
47 +import java.util.concurrent.Executors;
48 +import java.util.concurrent.LinkedBlockingQueue;
42 49
43 /** 50 /**
44 * Manages the inventory of created next groups. 51 * Manages the inventory of created next groups.
...@@ -57,9 +64,16 @@ public class DistributedFlowObjectiveStore ...@@ -57,9 +64,16 @@ public class DistributedFlowObjectiveStore
57 protected StorageService storageService; 64 protected StorageService storageService;
58 65
59 private AtomicCounter nextIds; 66 private AtomicCounter nextIds;
67 + private MapEventListener<Integer, byte[]> mapListener = new NextGroupListener();
68 + // event queue to separate map-listener threads from event-handler threads (tpool)
69 + private BlockingQueue<ObjectiveEvent> eventQ;
70 + private ExecutorService tpool;
60 71
61 @Activate 72 @Activate
62 public void activate() { 73 public void activate() {
74 + tpool = Executors.newFixedThreadPool(4, groupedThreads("onos/flobj-notifier", "%d", log));
75 + eventQ = new LinkedBlockingQueue<ObjectiveEvent>();
76 + tpool.execute(new FlowObjectiveNotifier());
63 nextGroups = storageService.<Integer, byte[]>consistentMapBuilder() 77 nextGroups = storageService.<Integer, byte[]>consistentMapBuilder()
64 .withName("flowobjective-groups") 78 .withName("flowobjective-groups")
65 .withSerializer(Serializer.using( 79 .withSerializer(Serializer.using(
...@@ -68,7 +82,7 @@ public class DistributedFlowObjectiveStore ...@@ -68,7 +82,7 @@ public class DistributedFlowObjectiveStore
68 .register(Versioned.class) 82 .register(Versioned.class)
69 .build("DistributedFlowObjectiveStore"))) 83 .build("DistributedFlowObjectiveStore")))
70 .build(); 84 .build();
71 - 85 + nextGroups.addListener(mapListener);
72 nextIds = storageService.getAtomicCounter("next-objective-counter"); 86 nextIds = storageService.getAtomicCounter("next-objective-counter");
73 log.info("Started"); 87 log.info("Started");
74 } 88 }
...@@ -76,6 +90,7 @@ public class DistributedFlowObjectiveStore ...@@ -76,6 +90,7 @@ public class DistributedFlowObjectiveStore
76 90
77 @Deactivate 91 @Deactivate
78 public void deactivate() { 92 public void deactivate() {
93 + tpool.shutdown();
79 log.info("Stopped"); 94 log.info("Stopped");
80 } 95 }
81 96
...@@ -120,4 +135,37 @@ public class DistributedFlowObjectiveStore ...@@ -120,4 +135,37 @@ public class DistributedFlowObjectiveStore
120 public int allocateNextId() { 135 public int allocateNextId() {
121 return (int) nextIds.incrementAndGet(); 136 return (int) nextIds.incrementAndGet();
122 } 137 }
138 +
139 + private class FlowObjectiveNotifier implements Runnable {
140 + @Override
141 + public void run() {
142 + try {
143 + while (!Thread.currentThread().isInterrupted()) {
144 + notifyDelegate(eventQ.take());
145 + }
146 + } catch (InterruptedException ex) {
147 + Thread.currentThread().interrupt();
148 + }
149 + }
150 + }
151 +
152 + private class NextGroupListener implements MapEventListener<Integer, byte[]> {
153 + @Override
154 + public void event(MapEvent<Integer, byte[]> event) {
155 + switch (event.type()) {
156 + case INSERT:
157 + eventQ.add(new ObjectiveEvent(ObjectiveEvent.Type.ADD, event.key()));
158 + break;
159 + case REMOVE:
160 + eventQ.add(new ObjectiveEvent(ObjectiveEvent.Type.REMOVE, event.key()));
161 + break;
162 + case UPDATE:
163 + // TODO Introduce UPDATE ObjectiveEvent when the map is being updated
164 + break;
165 + default:
166 + break;
167 + }
168 + }
169 + }
170 +
123 } 171 }
......
...@@ -182,6 +182,12 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -182,6 +182,12 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
182 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); 182 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
183 183
184 rules = processForward(fwd); 184 rules = processForward(fwd);
185 + if (rules == null || rules.isEmpty()) {
186 + // Assumes fail message has already been generated to the objective
187 + // context. Returning here prevents spurious pass message to be
188 + // generated by FlowRule service for empty flowOps.
189 + return;
190 + }
185 switch (fwd.op()) { 191 switch (fwd.op()) {
186 case ADD: 192 case ADD:
187 rules.stream() 193 rules.stream()
...@@ -748,7 +754,7 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -748,7 +754,7 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
748 * returned if there is an issue in processing the objective. 754 * returned if there is an issue in processing the objective.
749 */ 755 */
750 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) { 756 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
751 - log.trace("Processing specific fwd objective:{} in dev:{} with next:{}", 757 + log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
752 fwd.id(), deviceId, fwd.nextId()); 758 fwd.id(), deviceId, fwd.nextId());
753 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd); 759 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
754 boolean isEthDstObj = isSupportedEthDstObjective(fwd); 760 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
...@@ -885,8 +891,8 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -885,8 +891,8 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
885 if (fwd.nextId() != null) { 891 if (fwd.nextId() != null) {
886 if (forTableId == MPLS_TABLE_1 && !popMpls) { 892 if (forTableId == MPLS_TABLE_1 && !popMpls) {
887 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP " 893 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
888 - + "is not implemented in OF-DPA yet. Aborting this flow " 894 + + "is not implemented in OF-DPA yet. Aborting this flow {} -> next:{}"
889 - + "in this device {}", deviceId); 895 + + "in this device {}", fwd.id(), fwd.nextId(), deviceId);
890 // XXX We could convert to forwarding to a single-port, via a 896 // XXX We could convert to forwarding to a single-port, via a
891 // MPLS interface, or a MPLS SWAP (with-same) but that would 897 // MPLS interface, or a MPLS SWAP (with-same) but that would
892 // have to be handled in the next-objective. Also the pop-mpls 898 // have to be handled in the next-objective. Also the pop-mpls
...@@ -907,6 +913,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -907,6 +913,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
907 return Collections.emptySet(); 913 return Collections.emptySet();
908 } 914 }
909 tb.deferred().group(group.id()); 915 tb.deferred().group(group.id());
916 + } else {
917 + log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
918 + fwd.nextId(), deviceId, fwd.id());
919 + fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
920 + return Collections.emptySet();
910 } 921 }
911 } 922 }
912 tb.transition(ACL_TABLE); 923 tb.transition(ACL_TABLE);
...@@ -1063,7 +1074,7 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1063,7 +1074,7 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1063 for (GroupKey gk : gkd) { 1074 for (GroupKey gk : gkd) {
1064 Group g = groupService.getGroup(deviceId, gk); 1075 Group g = groupService.getGroup(deviceId, gk);
1065 if (g == null) { 1076 if (g == null) {
1066 - gchain.append(" ERROR").append(" -->"); 1077 + gchain.append(" NoGrp").append(" -->");
1067 continue; 1078 continue;
1068 } 1079 }
1069 gchain.append(" 0x").append(Integer.toHexString(g.id().id())) 1080 gchain.append(" 0x").append(Integer.toHexString(g.id().id()))
...@@ -1071,7 +1082,12 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1071,7 +1082,12 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1071 lastGroup = g; 1082 lastGroup = g;
1072 } 1083 }
1073 // add port information for last group in group-chain 1084 // add port information for last group in group-chain
1074 - for (Instruction i: lastGroup.buckets().buckets().get(0).treatment().allInstructions()) { 1085 + List<Instruction> lastGroupIns = new ArrayList<Instruction>();
1086 + if (gchain != null) {
1087 + lastGroupIns = lastGroup.buckets().buckets().get(0)
1088 + .treatment().allInstructions();
1089 + }
1090 + for (Instruction i: lastGroupIns) {
1075 if (i instanceof OutputInstruction) { 1091 if (i instanceof OutputInstruction) {
1076 gchain.append(" port:").append(((OutputInstruction) i).port()); 1092 gchain.append(" port:").append(((OutputInstruction) i).port());
1077 } 1093 }
......