Brian O'Connor

ONOS-2003 Fixing intent reroute after cluster change

Objective trackers update when partitions are shuffled to
track "local" intents.

Change-Id: I7cd9e4a935ddbc94813d5067d4febc084a89f508
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;
17 +
18 +import org.onosproject.event.AbstractEvent;
19 +
20 +/**
21 + * Partition event.
22 + */
23 +//TODO change String into a proper object type
24 +public class PartitionEvent extends AbstractEvent<PartitionEvent.Type, String> {
25 +
26 + public enum Type {
27 + LEADER_CHANGED
28 + }
29 +
30 + public PartitionEvent(Type type, String partition) {
31 + super(type, partition);
32 + }
33 +}
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;
17 +
18 +import org.onosproject.event.EventListener;
19 +
20 +/**
21 + * Entity capable of receiving device partition-related events.
22 + */
23 +public interface PartitionEventListener extends EventListener<PartitionEvent> {
24 +}
...@@ -40,4 +40,18 @@ public interface PartitionService { ...@@ -40,4 +40,18 @@ public interface PartitionService {
40 NodeId getLeader(Key intentKey); 40 NodeId getLeader(Key intentKey);
41 41
42 // TODO add API for rebalancing partitions 42 // TODO add API for rebalancing partitions
43 +
44 + /**
45 + * Registers a event listener to be notified of partition events.
46 + *
47 + * @param listener listener that will asynchronously notified of partition events.
48 + */
49 + void addListener(PartitionEventListener listener);
50 +
51 + /**
52 + * Unregisters a event listener for partition events.
53 + *
54 + * @param listener listener to be removed.
55 + */
56 + void removeListener(PartitionEventListener listener);
43 } 57 }
......
...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Component; ...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate; 23 import org.apache.felix.scr.annotations.Deactivate;
24 import org.apache.felix.scr.annotations.Reference; 24 import org.apache.felix.scr.annotations.Reference;
25 import org.apache.felix.scr.annotations.ReferenceCardinality; 25 import org.apache.felix.scr.annotations.ReferenceCardinality;
26 +import org.apache.felix.scr.annotations.ReferencePolicy;
26 import org.apache.felix.scr.annotations.Service; 27 import org.apache.felix.scr.annotations.Service;
27 import org.onosproject.core.ApplicationId; 28 import org.onosproject.core.ApplicationId;
28 import org.onosproject.event.Event; 29 import org.onosproject.event.Event;
...@@ -38,8 +39,12 @@ import org.onosproject.net.device.DeviceService; ...@@ -38,8 +39,12 @@ import org.onosproject.net.device.DeviceService;
38 import org.onosproject.net.host.HostEvent; 39 import org.onosproject.net.host.HostEvent;
39 import org.onosproject.net.host.HostListener; 40 import org.onosproject.net.host.HostListener;
40 import org.onosproject.net.host.HostService; 41 import org.onosproject.net.host.HostService;
42 +import org.onosproject.net.intent.Intent;
41 import org.onosproject.net.intent.IntentService; 43 import org.onosproject.net.intent.IntentService;
42 import org.onosproject.net.intent.Key; 44 import org.onosproject.net.intent.Key;
45 +import org.onosproject.net.intent.PartitionEvent;
46 +import org.onosproject.net.intent.PartitionEventListener;
47 +import org.onosproject.net.intent.PartitionService;
43 import org.onosproject.net.link.LinkEvent; 48 import org.onosproject.net.link.LinkEvent;
44 import org.onosproject.net.resource.link.LinkResourceEvent; 49 import org.onosproject.net.resource.link.LinkResourceEvent;
45 import org.onosproject.net.resource.link.LinkResourceListener; 50 import org.onosproject.net.resource.link.LinkResourceListener;
...@@ -54,6 +59,10 @@ import java.util.Collections; ...@@ -54,6 +59,10 @@ import java.util.Collections;
54 import java.util.HashSet; 59 import java.util.HashSet;
55 import java.util.Set; 60 import java.util.Set;
56 import java.util.concurrent.ExecutorService; 61 import java.util.concurrent.ExecutorService;
62 +import java.util.concurrent.Executors;
63 +import java.util.concurrent.ScheduledExecutorService;
64 +import java.util.concurrent.TimeUnit;
65 +import java.util.concurrent.atomic.AtomicBoolean;
57 66
58 import static com.google.common.base.Preconditions.checkArgument; 67 import static com.google.common.base.Preconditions.checkArgument;
59 import static com.google.common.base.Preconditions.checkNotNull; 68 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -94,25 +103,35 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -94,25 +103,35 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected HostService hostService; 104 protected HostService hostService;
96 105
97 - @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) 106 + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
107 + policy = ReferencePolicy.DYNAMIC)
98 protected IntentService intentService; 108 protected IntentService intentService;
99 109
110 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 + protected PartitionService partitionService;
112 +
100 private ExecutorService executorService = 113 private ExecutorService executorService =
101 newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker")); 114 newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker"));
115 + private ScheduledExecutorService executor = Executors
116 + .newScheduledThreadPool(1);
102 117
103 private TopologyListener listener = new InternalTopologyListener(); 118 private TopologyListener listener = new InternalTopologyListener();
104 private LinkResourceListener linkResourceListener = 119 private LinkResourceListener linkResourceListener =
105 new InternalLinkResourceListener(); 120 new InternalLinkResourceListener();
106 private DeviceListener deviceListener = new InternalDeviceListener(); 121 private DeviceListener deviceListener = new InternalDeviceListener();
107 private HostListener hostListener = new InternalHostListener(); 122 private HostListener hostListener = new InternalHostListener();
123 + private PartitionEventListener partitionListener = new InternalPartitionListener();
108 private TopologyChangeDelegate delegate; 124 private TopologyChangeDelegate delegate;
109 125
126 + protected final AtomicBoolean updateScheduled = new AtomicBoolean(false);
127 +
110 @Activate 128 @Activate
111 public void activate() { 129 public void activate() {
112 topologyService.addListener(listener); 130 topologyService.addListener(listener);
113 resourceManager.addListener(linkResourceListener); 131 resourceManager.addListener(linkResourceListener);
114 deviceService.addListener(deviceListener); 132 deviceService.addListener(deviceListener);
115 hostService.addListener(hostListener); 133 hostService.addListener(hostListener);
134 + partitionService.addListener(partitionListener);
116 log.info("Started"); 135 log.info("Started");
117 } 136 }
118 137
...@@ -122,6 +141,7 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -122,6 +141,7 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
122 resourceManager.removeListener(linkResourceListener); 141 resourceManager.removeListener(linkResourceListener);
123 deviceService.removeListener(deviceListener); 142 deviceService.removeListener(deviceListener);
124 hostService.removeListener(hostListener); 143 hostService.removeListener(hostListener);
144 + partitionService.removeListener(partitionListener);
125 log.info("Stopped"); 145 log.info("Stopped");
126 } 146 }
127 147
...@@ -268,7 +288,7 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -268,7 +288,7 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
268 288
269 private void updateTrackedResources(ApplicationId appId, boolean track) { 289 private void updateTrackedResources(ApplicationId appId, boolean track) {
270 if (intentService == null) { 290 if (intentService == null) {
271 - log.debug("Intent service is not bound yet"); 291 + log.warn("Intent service is not bound yet");
272 return; 292 return;
273 } 293 }
274 intentService.getIntents().forEach(intent -> { 294 intentService.getIntents().forEach(intent -> {
...@@ -342,4 +362,52 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -342,4 +362,52 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
342 executorService.execute(new DeviceAvailabilityHandler(id, available)); 362 executorService.execute(new DeviceAvailabilityHandler(id, available));
343 } 363 }
344 } 364 }
365 +
366 + protected void doIntentUpdate() {
367 + updateScheduled.set(false);
368 + if (intentService == null) {
369 + log.warn("Intent service is not bound yet");
370 + return;
371 + }
372 + try {
373 + //FIXME very inefficient
374 + for (Intent intent : intentService.getIntents()) {
375 + try {
376 + if (intentService.isLocal(intent.key())) {
377 + log.warn("intent {}, old: {}, new: {}",
378 + intent.key(), intentsByDevice.values().contains(intent.key()), true);
379 + addTrackedResources(intent.key(), intent.resources());
380 + intentService.getInstallableIntents(intent.key()).stream()
381 + .forEach(installable ->
382 + addTrackedResources(intent.key(), installable.resources()));
383 + } else {
384 + log.warn("intent {}, old: {}, new: {}",
385 + intent.key(), intentsByDevice.values().contains(intent.key()), false);
386 + removeTrackedResources(intent.key(), intent.resources());
387 + intentService.getInstallableIntents(intent.key()).stream()
388 + .forEach(installable ->
389 + removeTrackedResources(intent.key(), installable.resources()));
390 + }
391 + } catch (NullPointerException npe) {
392 + log.warn("intent error {}", intent.key(), npe);
393 + }
394 + }
395 + } catch (Exception e) {
396 + log.warn("Exception caught during update task", e);
397 + }
398 + }
399 +
400 + private void scheduleIntentUpdate(int afterDelaySec) {
401 + if (updateScheduled.compareAndSet(false, true)) {
402 + executor.schedule(this::doIntentUpdate, afterDelaySec, TimeUnit.SECONDS);
403 + }
404 + }
405 +
406 + private final class InternalPartitionListener implements PartitionEventListener {
407 + @Override
408 + public void event(PartitionEvent event) {
409 + log.warn("got message {}", event.subject());
410 + scheduleIntentUpdate(1);
411 + }
412 + }
345 } 413 }
......
...@@ -30,7 +30,11 @@ import org.onosproject.cluster.LeadershipEvent; ...@@ -30,7 +30,11 @@ import org.onosproject.cluster.LeadershipEvent;
30 import org.onosproject.cluster.LeadershipEventListener; 30 import org.onosproject.cluster.LeadershipEventListener;
31 import org.onosproject.cluster.LeadershipService; 31 import org.onosproject.cluster.LeadershipService;
32 import org.onosproject.cluster.NodeId; 32 import org.onosproject.cluster.NodeId;
33 +import org.onosproject.event.EventDeliveryService;
34 +import org.onosproject.event.ListenerRegistry;
33 import org.onosproject.net.intent.Key; 35 import org.onosproject.net.intent.Key;
36 +import org.onosproject.net.intent.PartitionEvent;
37 +import org.onosproject.net.intent.PartitionEventListener;
34 import org.onosproject.net.intent.PartitionService; 38 import org.onosproject.net.intent.PartitionService;
35 import org.slf4j.Logger; 39 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory; 40 import org.slf4j.LoggerFactory;
...@@ -58,6 +62,9 @@ public class PartitionManager implements PartitionService { ...@@ -58,6 +62,9 @@ public class PartitionManager implements PartitionService {
58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 62 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 protected ClusterService clusterService; 63 protected ClusterService clusterService;
60 64
65 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 + protected EventDeliveryService eventDispatcher;
67 +
61 protected final AtomicBoolean rebalanceScheduled = new AtomicBoolean(false); 68 protected final AtomicBoolean rebalanceScheduled = new AtomicBoolean(false);
62 69
63 static final int NUM_PARTITIONS = 14; 70 static final int NUM_PARTITIONS = 14;
...@@ -67,6 +74,7 @@ public class PartitionManager implements PartitionService { ...@@ -67,6 +74,7 @@ public class PartitionManager implements PartitionService {
67 74
68 private static final String ELECTION_PREFIX = "intent-partition-"; 75 private static final String ELECTION_PREFIX = "intent-partition-";
69 76
77 + private ListenerRegistry<PartitionEvent, PartitionEventListener> listenerRegistry;
70 private LeadershipEventListener leaderListener = new InternalLeadershipListener(); 78 private LeadershipEventListener leaderListener = new InternalLeadershipListener();
71 private ClusterEventListener clusterListener = new InternalClusterEventListener(); 79 private ClusterEventListener clusterListener = new InternalClusterEventListener();
72 80
...@@ -78,6 +86,9 @@ public class PartitionManager implements PartitionService { ...@@ -78,6 +86,9 @@ public class PartitionManager implements PartitionService {
78 leadershipService.addListener(leaderListener); 86 leadershipService.addListener(leaderListener);
79 clusterService.addListener(clusterListener); 87 clusterService.addListener(clusterListener);
80 88
89 + listenerRegistry = new ListenerRegistry<>();
90 + eventDispatcher.addSink(PartitionEvent.class, listenerRegistry);
91 +
81 for (int i = 0; i < NUM_PARTITIONS; i++) { 92 for (int i = 0; i < NUM_PARTITIONS; i++) {
82 leadershipService.runForLeadership(getPartitionPath(i)); 93 leadershipService.runForLeadership(getPartitionPath(i));
83 } 94 }
...@@ -90,6 +101,7 @@ public class PartitionManager implements PartitionService { ...@@ -90,6 +101,7 @@ public class PartitionManager implements PartitionService {
90 public void deactivate() { 101 public void deactivate() {
91 executor.shutdownNow(); 102 executor.shutdownNow();
92 103
104 + eventDispatcher.removeSink(PartitionEvent.class);
93 leadershipService.removeListener(leaderListener); 105 leadershipService.removeListener(leaderListener);
94 clusterService.removeListener(clusterListener); 106 clusterService.removeListener(clusterListener);
95 } 107 }
...@@ -133,6 +145,16 @@ public class PartitionManager implements PartitionService { ...@@ -133,6 +145,16 @@ public class PartitionManager implements PartitionService {
133 return leadershipService.getLeader(getPartitionPath(getPartitionForKey(intentKey))); 145 return leadershipService.getLeader(getPartitionPath(getPartitionForKey(intentKey)));
134 } 146 }
135 147
148 + @Override
149 + public void addListener(PartitionEventListener listener) {
150 + listenerRegistry.addListener(listener);
151 + }
152 +
153 + @Override
154 + public void removeListener(PartitionEventListener listener) {
155 + listenerRegistry.removeListener(listener);
156 + }
157 +
136 protected void doRebalance() { 158 protected void doRebalance() {
137 rebalanceScheduled.set(false); 159 rebalanceScheduled.set(false);
138 try { 160 try {
...@@ -203,6 +225,9 @@ public class PartitionManager implements PartitionService { ...@@ -203,6 +225,9 @@ public class PartitionManager implements PartitionService {
203 225
204 // See if we need to let some partitions go 226 // See if we need to let some partitions go
205 scheduleRebalance(0); 227 scheduleRebalance(0);
228 +
229 + eventDispatcher.post(new PartitionEvent(PartitionEvent.Type.LEADER_CHANGED,
230 + leadership.topic()));
206 } 231 }
207 } 232 }
208 } 233 }
......
...@@ -28,6 +28,7 @@ import org.onosproject.cluster.LeadershipEventListener; ...@@ -28,6 +28,7 @@ import org.onosproject.cluster.LeadershipEventListener;
28 import org.onosproject.cluster.LeadershipService; 28 import org.onosproject.cluster.LeadershipService;
29 import org.onosproject.cluster.LeadershipServiceAdapter; 29 import org.onosproject.cluster.LeadershipServiceAdapter;
30 import org.onosproject.cluster.NodeId; 30 import org.onosproject.cluster.NodeId;
31 +import org.onosproject.common.event.impl.TestEventDispatcher;
31 import org.onosproject.net.intent.Key; 32 import org.onosproject.net.intent.Key;
32 33
33 import java.util.HashMap; 34 import java.util.HashMap;
...@@ -86,6 +87,7 @@ public class PartitionManagerTest { ...@@ -86,6 +87,7 @@ public class PartitionManagerTest {
86 87
87 partitionManager.clusterService = new TestClusterService(); 88 partitionManager.clusterService = new TestClusterService();
88 partitionManager.leadershipService = leadershipService; 89 partitionManager.leadershipService = leadershipService;
90 + partitionManager.eventDispatcher = new TestEventDispatcher();
89 } 91 }
90 92
91 /** 93 /**
......
...@@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableSet; ...@@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableSet;
19 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
20 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
21 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Reference;
23 +import org.apache.felix.scr.annotations.ReferenceCardinality;
22 import org.apache.felix.scr.annotations.Service; 24 import org.apache.felix.scr.annotations.Service;
23 import org.joda.time.DateTime; 25 import org.joda.time.DateTime;
24 import org.onlab.packet.IpAddress; 26 import org.onlab.packet.IpAddress;
...@@ -28,7 +30,11 @@ import org.onosproject.cluster.ClusterStoreDelegate; ...@@ -28,7 +30,11 @@ import org.onosproject.cluster.ClusterStoreDelegate;
28 import org.onosproject.cluster.ControllerNode; 30 import org.onosproject.cluster.ControllerNode;
29 import org.onosproject.cluster.DefaultControllerNode; 31 import org.onosproject.cluster.DefaultControllerNode;
30 import org.onosproject.cluster.NodeId; 32 import org.onosproject.cluster.NodeId;
33 +import org.onosproject.event.EventDeliveryService;
34 +import org.onosproject.event.ListenerRegistry;
31 import org.onosproject.net.intent.Key; 35 import org.onosproject.net.intent.Key;
36 +import org.onosproject.net.intent.PartitionEvent;
37 +import org.onosproject.net.intent.PartitionEventListener;
32 import org.onosproject.net.intent.PartitionService; 38 import org.onosproject.net.intent.PartitionService;
33 import org.onosproject.store.AbstractStore; 39 import org.onosproject.store.AbstractStore;
34 import org.slf4j.Logger; 40 import org.slf4j.Logger;
...@@ -55,14 +61,24 @@ public class SimpleClusterStore ...@@ -55,14 +61,24 @@ public class SimpleClusterStore
55 61
56 private final DateTime creationTime = DateTime.now(); 62 private final DateTime creationTime = DateTime.now();
57 63
64 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 + protected EventDeliveryService eventDispatcher;
66 +
67 + private ListenerRegistry<PartitionEvent, PartitionEventListener> listenerRegistry;
68 +
58 @Activate 69 @Activate
59 public void activate() { 70 public void activate() {
60 instance = new DefaultControllerNode(new NodeId("local"), LOCALHOST); 71 instance = new DefaultControllerNode(new NodeId("local"), LOCALHOST);
72 +
73 + listenerRegistry = new ListenerRegistry<>();
74 + eventDispatcher.addSink(PartitionEvent.class, listenerRegistry);
75 +
61 log.info("Started"); 76 log.info("Started");
62 } 77 }
63 78
64 @Deactivate 79 @Deactivate
65 public void deactivate() { 80 public void deactivate() {
81 + eventDispatcher.removeSink(PartitionEvent.class);
66 log.info("Stopped"); 82 log.info("Stopped");
67 } 83 }
68 84
...@@ -110,4 +126,14 @@ public class SimpleClusterStore ...@@ -110,4 +126,14 @@ public class SimpleClusterStore
110 public NodeId getLeader(Key intentKey) { 126 public NodeId getLeader(Key intentKey) {
111 return instance.id(); 127 return instance.id();
112 } 128 }
129 +
130 + @Override
131 + public void addListener(PartitionEventListener listener) {
132 + listenerRegistry.addListener(listener);
133 + }
134 +
135 + @Override
136 + public void removeListener(PartitionEventListener listener) {
137 + listenerRegistry.removeListener(listener);
138 + }
113 } 139 }
......