Marc De Leenheer
Committed by Gerrit Code Review

Injecting topology through JSON ConfigProvider works for multi-instance (ONOS-490).

Change-Id: Ib977f4cf9a59ddec360072891fd803c6f9ee84f1

Injecting optical device annotations and ports works for multi-instance (ONOS-870).

Change-Id: Icdde16ef72fc4e47eec7213250b04902083f0537
...@@ -179,7 +179,7 @@ public class OpticalPathProvisioner { ...@@ -179,7 +179,7 @@ public class OpticalPathProvisioner {
179 179
180 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) { 180 if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
181 return; 181 return;
182 - } 182 + }
183 183
184 List<Intent> intents = Lists.newArrayList(); 184 List<Intent> intents = Lists.newArrayList();
185 if (intent instanceof HostToHostIntent) { 185 if (intent instanceof HostToHostIntent) {
......
...@@ -15,10 +15,10 @@ ...@@ -15,10 +15,10 @@
15 */ 15 */
16 package org.onosproject.event; 16 package org.onosproject.event;
17 17
18 -import static com.google.common.base.MoreObjects.toStringHelper;
19 -
20 import org.joda.time.LocalDateTime; 18 import org.joda.time.LocalDateTime;
21 19
20 +import static com.google.common.base.MoreObjects.toStringHelper;
21 +
22 /** 22 /**
23 * Base event implementation. 23 * Base event implementation.
24 */ 24 */
...@@ -75,5 +75,4 @@ public class AbstractEvent<T extends Enum, S> implements Event<T, S> { ...@@ -75,5 +75,4 @@ public class AbstractEvent<T extends Enum, S> implements Event<T, S> {
75 .add("subject", subject()) 75 .add("subject", subject())
76 .toString(); 76 .toString();
77 } 77 }
78 -
79 } 78 }
......
...@@ -47,7 +47,7 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -47,7 +47,7 @@ public class DefaultDeviceDescription extends AbstractDescription
47 * @param hwVersion device HW version 47 * @param hwVersion device HW version
48 * @param swVersion device SW version 48 * @param swVersion device SW version
49 * @param serialNumber device serial number 49 * @param serialNumber device serial number
50 - * @param chassis chasis id 50 + * @param chassis chassis id
51 * @param annotations optional key/value annotations map 51 * @param annotations optional key/value annotations map
52 */ 52 */
53 public DefaultDeviceDescription(URI uri, Type type, String manufacturer, 53 public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
......
...@@ -306,18 +306,16 @@ public class DeviceManager ...@@ -306,18 +306,16 @@ public class DeviceManager
306 // TODO: Do we need to explicitly tell the Provider that 306 // TODO: Do we need to explicitly tell the Provider that
307 // this instance is not the MASTER 307 // this instance is not the MASTER
308 applyRole(deviceId, MastershipRole.STANDBY); 308 applyRole(deviceId, MastershipRole.STANDBY);
309 - return; 309 + } else {
310 + log.info("Role of this node is MASTER for {}", deviceId);
311 + // tell clock provider if this instance is the master
312 + deviceClockProviderService.setMastershipTerm(deviceId, term);
313 + applyRole(deviceId, MastershipRole.MASTER);
310 } 314 }
311 - log.info("Role of this node is MASTER for {}", deviceId);
312 -
313 - // tell clock provider if this instance is the master
314 - deviceClockProviderService.setMastershipTerm(deviceId, term);
315 315
316 DeviceEvent event = store.createOrUpdateDevice(provider().id(), 316 DeviceEvent event = store.createOrUpdateDevice(provider().id(),
317 deviceId, deviceDescription); 317 deviceId, deviceDescription);
318 318
319 - applyRole(deviceId, MastershipRole.MASTER);
320 -
321 // If there was a change of any kind, tell the provider 319 // If there was a change of any kind, tell the provider
322 // that this instance is the master. 320 // that this instance is the master.
323 if (event != null) { 321 if (event != null) {
......
1 +package org.onosproject.store.device.impl;
2 +
3 +import com.google.common.base.MoreObjects;
4 +import org.onosproject.net.DeviceId;
5 +import org.onosproject.net.device.DeviceDescription;
6 +import org.onosproject.net.provider.ProviderId;
7 +
8 +public class DeviceInjectedEvent {
9 + private final ProviderId providerId;
10 + private final DeviceId deviceId;
11 + private final DeviceDescription deviceDescription;
12 +
13 + protected DeviceInjectedEvent(
14 + ProviderId providerId,
15 + DeviceId deviceId,
16 + DeviceDescription deviceDescription) {
17 + this.providerId = providerId;
18 + this.deviceId = deviceId;
19 + this.deviceDescription = deviceDescription;
20 + }
21 +
22 + public DeviceId deviceId() {
23 + return deviceId;
24 + }
25 +
26 + public ProviderId providerId() {
27 + return providerId;
28 + }
29 +
30 + public DeviceDescription deviceDescription() {
31 + return deviceDescription;
32 + }
33 +
34 + @Override
35 + public String toString() {
36 + return MoreObjects.toStringHelper(getClass())
37 + .add("providerId", providerId)
38 + .add("deviceId", deviceId)
39 + .add("deviceDescription", deviceDescription)
40 + .toString();
41 + }
42 +
43 + // for serializer
44 + protected DeviceInjectedEvent() {
45 + this.providerId = null;
46 + this.deviceId = null;
47 + this.deviceDescription = null;
48 + }
49 +}
...@@ -21,7 +21,6 @@ import com.google.common.collect.FluentIterable; ...@@ -21,7 +21,6 @@ import com.google.common.collect.FluentIterable;
21 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Maps; 22 import com.google.common.collect.Maps;
23 import com.google.common.collect.Sets; 23 import com.google.common.collect.Sets;
24 -
25 import org.apache.commons.lang3.RandomUtils; 24 import org.apache.commons.lang3.RandomUtils;
26 import org.apache.felix.scr.annotations.Activate; 25 import org.apache.felix.scr.annotations.Activate;
27 import org.apache.felix.scr.annotations.Component; 26 import org.apache.felix.scr.annotations.Component;
...@@ -29,6 +28,9 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -29,6 +28,9 @@ import org.apache.felix.scr.annotations.Deactivate;
29 import org.apache.felix.scr.annotations.Reference; 28 import org.apache.felix.scr.annotations.Reference;
30 import org.apache.felix.scr.annotations.ReferenceCardinality; 29 import org.apache.felix.scr.annotations.ReferenceCardinality;
31 import org.apache.felix.scr.annotations.Service; 30 import org.apache.felix.scr.annotations.Service;
31 +import org.onlab.packet.ChassisId;
32 +import org.onlab.util.KryoNamespace;
33 +import org.onlab.util.NewConcurrentHashMap;
32 import org.onosproject.cluster.ClusterService; 34 import org.onosproject.cluster.ClusterService;
33 import org.onosproject.cluster.ControllerNode; 35 import org.onosproject.cluster.ControllerNode;
34 import org.onosproject.cluster.NodeId; 36 import org.onosproject.cluster.NodeId;
...@@ -61,9 +63,6 @@ import org.onosproject.store.cluster.messaging.MessageSubject; ...@@ -61,9 +63,6 @@ import org.onosproject.store.cluster.messaging.MessageSubject;
61 import org.onosproject.store.impl.Timestamped; 63 import org.onosproject.store.impl.Timestamped;
62 import org.onosproject.store.serializers.KryoSerializer; 64 import org.onosproject.store.serializers.KryoSerializer;
63 import org.onosproject.store.serializers.impl.DistributedStoreSerializers; 65 import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
64 -import org.onlab.packet.ChassisId;
65 -import org.onlab.util.KryoNamespace;
66 -import org.onlab.util.NewConcurrentHashMap;
67 import org.slf4j.Logger; 66 import org.slf4j.Logger;
68 67
69 import java.io.IOException; 68 import java.io.IOException;
...@@ -86,17 +85,17 @@ import java.util.concurrent.TimeUnit; ...@@ -86,17 +85,17 @@ import java.util.concurrent.TimeUnit;
86 85
87 import static com.google.common.base.Preconditions.checkArgument; 86 import static com.google.common.base.Preconditions.checkArgument;
88 import static com.google.common.base.Predicates.notNull; 87 import static com.google.common.base.Predicates.notNull;
89 -import static org.onosproject.cluster.ControllerNodeToNodeId.toNodeId;
90 -import static org.onosproject.net.device.DeviceEvent.Type.*;
91 -import static org.slf4j.LoggerFactory.getLogger;
92 -import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
93 -import static org.onosproject.net.DefaultAnnotations.merge;
94 import static com.google.common.base.Verify.verify; 88 import static com.google.common.base.Verify.verify;
89 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
90 +import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
95 import static org.onlab.util.Tools.minPriority; 91 import static org.onlab.util.Tools.minPriority;
96 import static org.onlab.util.Tools.namedThreads; 92 import static org.onlab.util.Tools.namedThreads;
97 -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 93 +import static org.onosproject.cluster.ControllerNodeToNodeId.toNodeId;
98 -import static org.onosproject.store.device.impl.GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE; 94 +import static org.onosproject.net.DefaultAnnotations.merge;
99 -import static org.onosproject.store.device.impl.GossipDeviceStoreMessageSubjects.DEVICE_REMOVE_REQ; 95 +import static org.onosproject.net.device.DeviceEvent.Type.*;
96 +import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
97 +import static org.onosproject.store.device.impl.GossipDeviceStoreMessageSubjects.*;
98 +import static org.slf4j.LoggerFactory.getLogger;
100 99
101 /** 100 /**
102 * Manages inventory of infrastructure devices using gossip protocol to distribute 101 * Manages inventory of infrastructure devices using gossip protocol to distribute
...@@ -111,6 +110,8 @@ public class GossipDeviceStore ...@@ -111,6 +110,8 @@ public class GossipDeviceStore
111 private final Logger log = getLogger(getClass()); 110 private final Logger log = getLogger(getClass());
112 111
113 private static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; 112 private static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
113 + // Timeout in milliseconds to process device or ports on remote master node
114 + private static final int REMOTE_MASTER_TIMEOUT = 1000;
114 115
115 // innerMap is used to lock a Device, thus instance should never be replaced. 116 // innerMap is used to lock a Device, thus instance should never be replaced.
116 // collection of Description given from various providers 117 // collection of Description given from various providers
...@@ -158,6 +159,8 @@ public class GossipDeviceStore ...@@ -158,6 +159,8 @@ public class GossipDeviceStore
158 .register(DeviceAntiEntropyAdvertisement.class) 159 .register(DeviceAntiEntropyAdvertisement.class)
159 .register(DeviceFragmentId.class) 160 .register(DeviceFragmentId.class)
160 .register(PortFragmentId.class) 161 .register(PortFragmentId.class)
162 + .register(DeviceInjectedEvent.class)
163 + .register(PortInjectedEvent.class)
161 .build(); 164 .build();
162 } 165 }
163 }; 166 };
...@@ -186,6 +189,10 @@ public class GossipDeviceStore ...@@ -186,6 +189,10 @@ public class GossipDeviceStore
186 GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener()); 189 GossipDeviceStoreMessageSubjects.PORT_STATUS_UPDATE, new InternalPortStatusEventListener());
187 clusterCommunicator.addSubscriber( 190 clusterCommunicator.addSubscriber(
188 GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE, new InternalDeviceAdvertisementListener()); 191 GossipDeviceStoreMessageSubjects.DEVICE_ADVERTISE, new InternalDeviceAdvertisementListener());
192 + clusterCommunicator.addSubscriber(
193 + GossipDeviceStoreMessageSubjects.DEVICE_INJECTED, new DeviceInjectedEventListener());
194 + clusterCommunicator.addSubscriber(
195 + GossipDeviceStoreMessageSubjects.PORT_INJECTED, new PortInjectedEventListener());
189 196
190 executor = Executors.newCachedThreadPool(namedThreads("onos-device-fg-%d")); 197 executor = Executors.newCachedThreadPool(namedThreads("onos-device-fg-%d"));
191 198
...@@ -251,21 +258,48 @@ public class GossipDeviceStore ...@@ -251,21 +258,48 @@ public class GossipDeviceStore
251 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, 258 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId,
252 DeviceId deviceId, 259 DeviceId deviceId,
253 DeviceDescription deviceDescription) { 260 DeviceDescription deviceDescription) {
254 - final Timestamp newTimestamp = deviceClockService.getTimestamp(deviceId); 261 + NodeId localNode = clusterService.getLocalNode().id();
255 - final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp); 262 + NodeId deviceNode = mastershipService.getMasterFor(deviceId);
256 - final DeviceEvent event; 263 +
257 - final Timestamped<DeviceDescription> mergedDesc; 264 + // Process device update only if we're the master,
258 - final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId); 265 + // otherwise signal the actual master.
259 - synchronized (device) { 266 + DeviceEvent deviceEvent = null;
260 - event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc); 267 + if (localNode.equals(deviceNode)) {
261 - mergedDesc = device.get(providerId).getDeviceDesc(); 268 +
262 - } 269 + final Timestamp newTimestamp = deviceClockService.getTimestamp(deviceId);
263 - if (event != null) { 270 + final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
264 - log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}", 271 + final Timestamped<DeviceDescription> mergedDesc;
265 - providerId, deviceId); 272 + final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);
266 - notifyPeers(new InternalDeviceEvent(providerId, deviceId, mergedDesc)); 273 +
274 + synchronized (device) {
275 + deviceEvent = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
276 + mergedDesc = device.get(providerId).getDeviceDesc();
277 + }
278 +
279 + if (deviceEvent != null) {
280 + log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}",
281 + providerId, deviceId);
282 + notifyPeers(new InternalDeviceEvent(providerId, deviceId, mergedDesc));
283 + }
284 +
285 + } else {
286 +
287 + DeviceInjectedEvent deviceInjectedEvent = new DeviceInjectedEvent(
288 + providerId, deviceId, deviceDescription);
289 + ClusterMessage clusterMessage = new ClusterMessage(localNode, DEVICE_INJECTED,
290 + SERIALIZER.encode(deviceInjectedEvent));
291 +
292 + try {
293 + clusterCommunicator.unicast(clusterMessage, deviceNode);
294 + } catch (IOException e) {
295 + log.warn("Failed to process injected device id: {} desc: {} " +
296 + "(cluster messaging failed: {})",
297 + deviceId, deviceDescription, e);
298 + }
299 +
267 } 300 }
268 - return event; 301 +
302 + return deviceEvent;
269 } 303 }
270 304
271 private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId, 305 private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId,
...@@ -434,52 +468,81 @@ public class GossipDeviceStore ...@@ -434,52 +468,81 @@ public class GossipDeviceStore
434 DeviceId deviceId, 468 DeviceId deviceId,
435 List<PortDescription> portDescriptions) { 469 List<PortDescription> portDescriptions) {
436 470
437 - final Timestamp newTimestamp; 471 + NodeId localNode = clusterService.getLocalNode().id();
438 - try { 472 + // TODO: It might be negligible, but this will have negative impact to topology discovery performance,
439 - newTimestamp = deviceClockService.getTimestamp(deviceId); 473 + // since it will trigger distributed store read.
440 - } catch (IllegalStateException e) { 474 + // Also, it'll probably be better if side-way communication happened on ConfigurationProvider, etc.
441 - log.info("Timestamp was not available for device {}", deviceId); 475 + // outside Device subsystem. so that we don't have to modify both Device and Link stores.
442 - log.debug(" discarding {}", portDescriptions); 476 + // If we don't care much about topology performance, then it might be OK.
443 - // Failed to generate timestamp. 477 + NodeId deviceNode = mastershipService.getMasterFor(deviceId);
444 478
445 - // Possible situation: 479 + // Process port update only if we're the master of the device,
446 - // Device connected and became master for short period of time, 480 + // otherwise signal the actual master.
447 - // but lost mastership before this instance had the chance to 481 + List<DeviceEvent> deviceEvents = null;
448 - // retrieve term information. 482 + if (localNode.equals(deviceNode)) {
449 483
450 - // Information dropped here is expected to be recoverable by 484 + final Timestamp newTimestamp;
451 - // device probing after mastership change 485 + try {
486 + newTimestamp = deviceClockService.getTimestamp(deviceId);
487 + } catch (IllegalStateException e) {
488 + log.info("Timestamp was not available for device {}", deviceId);
489 + log.debug(" discarding {}", portDescriptions);
490 + // Failed to generate timestamp.
452 491
453 - return Collections.emptyList(); 492 + // Possible situation:
454 - } 493 + // Device connected and became master for short period of time,
455 - log.debug("timestamp for {} {}", deviceId, newTimestamp); 494 + // but lost mastership before this instance had the chance to
495 + // retrieve term information.
456 496
457 - final Timestamped<List<PortDescription>> timestampedInput 497 + // Information dropped here is expected to be recoverable by
458 - = new Timestamped<>(portDescriptions, newTimestamp); 498 + // device probing after mastership change
459 - final List<DeviceEvent> events;
460 - final Timestamped<List<PortDescription>> merged;
461 499
462 - final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId); 500 + return Collections.emptyList();
463 - synchronized (device) { 501 + }
464 - events = updatePortsInternal(providerId, deviceId, timestampedInput); 502 + log.debug("timestamp for {} {}", deviceId, newTimestamp);
465 - final DeviceDescriptions descs = device.get(providerId); 503 +
466 - List<PortDescription> mergedList = 504 + final Timestamped<List<PortDescription>> timestampedInput
467 - FluentIterable.from(portDescriptions) 505 + = new Timestamped<>(portDescriptions, newTimestamp);
468 - .transform(new Function<PortDescription, PortDescription>() { 506 + final Timestamped<List<PortDescription>> merged;
469 - @Override 507 +
470 - public PortDescription apply(PortDescription input) { 508 + final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);
471 - // lookup merged port description 509 +
472 - return descs.getPortDesc(input.portNumber()).value(); 510 + synchronized (device) {
473 - } 511 + deviceEvents = updatePortsInternal(providerId, deviceId, timestampedInput);
474 - }).toList(); 512 + final DeviceDescriptions descs = device.get(providerId);
475 - merged = new Timestamped<List<PortDescription>>(mergedList, newTimestamp); 513 + List<PortDescription> mergedList =
476 - } 514 + FluentIterable.from(portDescriptions)
477 - if (!events.isEmpty()) { 515 + .transform(new Function<PortDescription, PortDescription>() {
478 - log.info("Notifying peers of a ports update topology event for providerId: {} and deviceId: {}", 516 + @Override
479 - providerId, deviceId); 517 + public PortDescription apply(PortDescription input) {
480 - notifyPeers(new InternalPortEvent(providerId, deviceId, merged)); 518 + // lookup merged port description
519 + return descs.getPortDesc(input.portNumber()).value();
520 + }
521 + }).toList();
522 + merged = new Timestamped<List<PortDescription>>(mergedList, newTimestamp);
523 + }
524 +
525 + if (!deviceEvents.isEmpty()) {
526 + log.info("Notifying peers of a ports update topology event for providerId: {} and deviceId: {}",
527 + providerId, deviceId);
528 + notifyPeers(new InternalPortEvent(providerId, deviceId, merged));
529 + }
530 +
531 + } else {
532 +
533 + PortInjectedEvent portInjectedEvent = new PortInjectedEvent(providerId, deviceId, portDescriptions);
534 + ClusterMessage clusterMessage = new ClusterMessage(
535 + localNode, PORT_INJECTED, SERIALIZER.encode(portInjectedEvent));
536 + try {
537 + clusterCommunicator.unicast(clusterMessage, deviceNode);
538 + } catch (IOException e) {
539 + log.warn("Failed to process injected ports of device id: {} " +
540 + "(cluster messaging failed: {})",
541 + deviceId, e);
542 + }
481 } 543 }
482 - return events; 544 +
545 + return deviceEvents;
483 } 546 }
484 547
485 private List<DeviceEvent> updatePortsInternal(ProviderId providerId, 548 private List<DeviceEvent> updatePortsInternal(ProviderId providerId,
...@@ -1431,4 +1494,48 @@ public class GossipDeviceStore ...@@ -1431,4 +1494,48 @@ public class GossipDeviceStore
1431 }); 1494 });
1432 } 1495 }
1433 } 1496 }
1497 +
1498 + private final class DeviceInjectedEventListener
1499 + implements ClusterMessageHandler {
1500 + @Override
1501 + public void handle(ClusterMessage message) {
1502 +
1503 + log.debug("Received injected device event from peer: {}", message.sender());
1504 + DeviceInjectedEvent event = SERIALIZER.decode(message.payload());
1505 +
1506 + ProviderId providerId = event.providerId();
1507 + DeviceId deviceId = event.deviceId();
1508 + DeviceDescription deviceDescription = event.deviceDescription();
1509 +
1510 + executor.submit(new Runnable() {
1511 +
1512 + @Override
1513 + public void run() {
1514 + createOrUpdateDevice(providerId, deviceId, deviceDescription);
1515 + }
1516 + });
1517 + }
1518 + }
1519 +
1520 + private final class PortInjectedEventListener
1521 + implements ClusterMessageHandler {
1522 + @Override
1523 + public void handle(ClusterMessage message) {
1524 +
1525 + log.debug("Received injected port event from peer: {}", message.sender());
1526 + PortInjectedEvent event = SERIALIZER.decode(message.payload());
1527 +
1528 + ProviderId providerId = event.providerId();
1529 + DeviceId deviceId = event.deviceId();
1530 + List<PortDescription> portDescriptions = event.portDescriptions();
1531 +
1532 + executor.submit(new Runnable() {
1533 +
1534 + @Override
1535 + public void run() {
1536 + updatePorts(providerId, deviceId, portDescriptions);
1537 + }
1538 + });
1539 + }
1540 + }
1434 } 1541 }
......
...@@ -34,4 +34,8 @@ public final class GossipDeviceStoreMessageSubjects { ...@@ -34,4 +34,8 @@ public final class GossipDeviceStoreMessageSubjects {
34 public static final MessageSubject DEVICE_ADVERTISE = new MessageSubject("peer-device-advertisements"); 34 public static final MessageSubject DEVICE_ADVERTISE = new MessageSubject("peer-device-advertisements");
35 // to be used with 3-way anti-entropy process 35 // to be used with 3-way anti-entropy process
36 public static final MessageSubject DEVICE_REQUEST = new MessageSubject("peer-device-request"); 36 public static final MessageSubject DEVICE_REQUEST = new MessageSubject("peer-device-request");
37 +
38 + // Network elements injected (not discovered) by ConfigProvider
39 + public static final MessageSubject DEVICE_INJECTED = new MessageSubject("peer-device-injected");
40 + public static final MessageSubject PORT_INJECTED = new MessageSubject("peer-port-injected");
37 } 41 }
......
1 +package org.onosproject.store.device.impl;
2 +
3 +import com.google.common.base.MoreObjects;
4 +import org.onosproject.net.DeviceId;
5 +import org.onosproject.net.device.PortDescription;
6 +import org.onosproject.net.provider.ProviderId;
7 +
8 +import java.util.List;
9 +
10 +public class PortInjectedEvent {
11 +
12 + private ProviderId providerId;
13 + private DeviceId deviceId;
14 + private List<PortDescription> portDescriptions;
15 +
16 + protected PortInjectedEvent(ProviderId providerId, DeviceId deviceId, List<PortDescription> portDescriptions) {
17 + this.providerId = providerId;
18 + this.deviceId = deviceId;
19 + this.portDescriptions = portDescriptions;
20 + }
21 +
22 + public DeviceId deviceId() {
23 + return deviceId;
24 + }
25 +
26 + public ProviderId providerId() {
27 + return providerId;
28 + }
29 +
30 + public List<PortDescription> portDescriptions() {
31 + return portDescriptions;
32 + }
33 +
34 + @Override
35 + public String toString() {
36 + return MoreObjects.toStringHelper(getClass())
37 + .add("providerId", providerId)
38 + .add("deviceId", deviceId)
39 + .add("portDescriptions", portDescriptions)
40 + .toString();
41 + }
42 +
43 + // for serializer
44 + protected PortInjectedEvent() {
45 + this.providerId = null;
46 + this.deviceId = null;
47 + this.portDescriptions = null;
48 + }
49 +
50 +}
...@@ -32,6 +32,7 @@ import org.onlab.util.KryoNamespace; ...@@ -32,6 +32,7 @@ import org.onlab.util.KryoNamespace;
32 import org.onosproject.cluster.ClusterService; 32 import org.onosproject.cluster.ClusterService;
33 import org.onosproject.cluster.ControllerNode; 33 import org.onosproject.cluster.ControllerNode;
34 import org.onosproject.cluster.NodeId; 34 import org.onosproject.cluster.NodeId;
35 +import org.onosproject.mastership.MastershipService;
35 import org.onosproject.net.AnnotationKeys; 36 import org.onosproject.net.AnnotationKeys;
36 import org.onosproject.net.AnnotationsUtil; 37 import org.onosproject.net.AnnotationsUtil;
37 import org.onosproject.net.ConnectPoint; 38 import org.onosproject.net.ConnectPoint;
...@@ -90,9 +91,7 @@ import static org.onosproject.net.Link.State.INACTIVE; ...@@ -90,9 +91,7 @@ import static org.onosproject.net.Link.State.INACTIVE;
90 import static org.onosproject.net.Link.Type.DIRECT; 91 import static org.onosproject.net.Link.Type.DIRECT;
91 import static org.onosproject.net.Link.Type.INDIRECT; 92 import static org.onosproject.net.Link.Type.INDIRECT;
92 import static org.onosproject.net.LinkKey.linkKey; 93 import static org.onosproject.net.LinkKey.linkKey;
93 -import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; 94 +import static org.onosproject.net.link.LinkEvent.Type.*;
94 -import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
95 -import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
96 import static org.onosproject.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT; 95 import static org.onosproject.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
97 import static org.slf4j.LoggerFactory.getLogger; 96 import static org.slf4j.LoggerFactory.getLogger;
98 97
...@@ -106,6 +105,9 @@ public class GossipLinkStore ...@@ -106,6 +105,9 @@ public class GossipLinkStore
106 extends AbstractStore<LinkEvent, LinkStoreDelegate> 105 extends AbstractStore<LinkEvent, LinkStoreDelegate>
107 implements LinkStore { 106 implements LinkStore {
108 107
108 + // Timeout in milliseconds to process links on remote master node
109 + private static final int REMOTE_MASTER_TIMEOUT = 1000;
110 +
109 private final Logger log = getLogger(getClass()); 111 private final Logger log = getLogger(getClass());
110 112
111 // Link inventory 113 // Link inventory
...@@ -131,6 +133,9 @@ public class GossipLinkStore ...@@ -131,6 +133,9 @@ public class GossipLinkStore
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected ClusterService clusterService; 134 protected ClusterService clusterService;
133 135
136 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 + protected MastershipService mastershipService;
138 +
134 protected static final KryoSerializer SERIALIZER = new KryoSerializer() { 139 protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
135 @Override 140 @Override
136 protected void setupKryoPool() { 141 protected void setupKryoPool() {
...@@ -141,6 +146,7 @@ public class GossipLinkStore ...@@ -141,6 +146,7 @@ public class GossipLinkStore
141 .register(InternalLinkRemovedEvent.class) 146 .register(InternalLinkRemovedEvent.class)
142 .register(LinkAntiEntropyAdvertisement.class) 147 .register(LinkAntiEntropyAdvertisement.class)
143 .register(LinkFragmentId.class) 148 .register(LinkFragmentId.class)
149 + .register(LinkInjectedEvent.class)
144 .build(); 150 .build();
145 } 151 }
146 }; 152 };
...@@ -161,6 +167,9 @@ public class GossipLinkStore ...@@ -161,6 +167,9 @@ public class GossipLinkStore
161 clusterCommunicator.addSubscriber( 167 clusterCommunicator.addSubscriber(
162 GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT, 168 GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT,
163 new InternalLinkAntiEntropyAdvertisementListener()); 169 new InternalLinkAntiEntropyAdvertisementListener());
170 + clusterCommunicator.addSubscriber(
171 + GossipLinkStoreMessageSubjects.LINK_INJECTED,
172 + new LinkInjectedEventListener());
164 173
165 executor = Executors.newCachedThreadPool(namedThreads("onos-link-fg-%d")); 174 executor = Executors.newCachedThreadPool(namedThreads("onos-link-fg-%d"));
166 175
...@@ -270,27 +279,52 @@ public class GossipLinkStore ...@@ -270,27 +279,52 @@ public class GossipLinkStore
270 public LinkEvent createOrUpdateLink(ProviderId providerId, 279 public LinkEvent createOrUpdateLink(ProviderId providerId,
271 LinkDescription linkDescription) { 280 LinkDescription linkDescription) {
272 281
273 - DeviceId dstDeviceId = linkDescription.dst().deviceId(); 282 + final DeviceId dstDeviceId = linkDescription.dst().deviceId();
274 - Timestamp newTimestamp = deviceClockService.getTimestamp(dstDeviceId); 283 + final NodeId localNode = clusterService.getLocalNode().id();
284 + final NodeId dstNode = mastershipService.getMasterFor(dstDeviceId);
275 285
276 - final Timestamped<LinkDescription> deltaDesc = new Timestamped<>(linkDescription, newTimestamp); 286 + // Process link update only if we're the master of the destination node,
287 + // otherwise signal the actual master.
288 + LinkEvent linkEvent = null;
289 + if (localNode.equals(dstNode)) {
277 290
278 - LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); 291 + Timestamp newTimestamp = deviceClockService.getTimestamp(dstDeviceId);
279 - final LinkEvent event; 292 +
280 - final Timestamped<LinkDescription> mergedDesc; 293 + final Timestamped<LinkDescription> deltaDesc = new Timestamped<>(linkDescription, newTimestamp);
281 - Map<ProviderId, Timestamped<LinkDescription>> map = getOrCreateLinkDescriptions(key); 294 +
282 - synchronized (map) { 295 + LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
283 - event = createOrUpdateLinkInternal(providerId, deltaDesc); 296 + final Timestamped<LinkDescription> mergedDesc;
284 - mergedDesc = map.get(providerId); 297 + Map<ProviderId, Timestamped<LinkDescription>> map = getOrCreateLinkDescriptions(key);
285 - } 298 +
299 + synchronized (map) {
300 + linkEvent = createOrUpdateLinkInternal(providerId, deltaDesc);
301 + mergedDesc = map.get(providerId);
302 + }
303 +
304 + if (linkEvent != null) {
305 + log.info("Notifying peers of a link update topology event from providerId: "
306 + + "{} between src: {} and dst: {}",
307 + providerId, linkDescription.src(), linkDescription.dst());
308 + notifyPeers(new InternalLinkEvent(providerId, mergedDesc));
309 + }
310 +
311 + } else {
312 +
313 + LinkInjectedEvent linkInjectedEvent = new LinkInjectedEvent(providerId, linkDescription);
314 + ClusterMessage linkInjectedMessage = new ClusterMessage(localNode,
315 + GossipLinkStoreMessageSubjects.LINK_INJECTED, SERIALIZER.encode(linkInjectedEvent));
316 +
317 + try {
318 + clusterCommunicator.unicast(linkInjectedMessage, dstNode);
319 + } catch (IOException e) {
320 + log.warn("Failed to process link update between src: {} and dst: {} " +
321 + "(cluster messaging failed: {})",
322 + linkDescription.src(), linkDescription.dst(), e);
323 + }
286 324
287 - if (event != null) {
288 - log.info("Notifying peers of a link update topology event from providerId: "
289 - + "{} between src: {} and dst: {}",
290 - providerId, linkDescription.src(), linkDescription.dst());
291 - notifyPeers(new InternalLinkEvent(providerId, mergedDesc));
292 } 325 }
293 - return event; 326 +
327 + return linkEvent;
294 } 328 }
295 329
296 @Override 330 @Override
...@@ -318,7 +352,7 @@ public class GossipLinkStore ...@@ -318,7 +352,7 @@ public class GossipLinkStore
318 Timestamped<LinkDescription> linkDescription) { 352 Timestamped<LinkDescription> linkDescription) {
319 353
320 final LinkKey key = linkKey(linkDescription.value().src(), 354 final LinkKey key = linkKey(linkDescription.value().src(),
321 - linkDescription.value().dst()); 355 + linkDescription.value().dst());
322 Map<ProviderId, Timestamped<LinkDescription>> descs = getOrCreateLinkDescriptions(key); 356 Map<ProviderId, Timestamped<LinkDescription>> descs = getOrCreateLinkDescriptions(key);
323 357
324 synchronized (descs) { 358 synchronized (descs) {
...@@ -397,7 +431,7 @@ public class GossipLinkStore ...@@ -397,7 +431,7 @@ public class GossipLinkStore
397 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { 431 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
398 432
399 links.put(key, newLink); 433 links.put(key, newLink);
400 - // strictly speaking following can be ommitted 434 + // strictly speaking following can be omitted
401 srcLinks.put(oldLink.src().deviceId(), key); 435 srcLinks.put(oldLink.src().deviceId(), key);
402 dstLinks.put(oldLink.dst().deviceId(), key); 436 dstLinks.put(oldLink.dst().deviceId(), key);
403 return new LinkEvent(LINK_UPDATED, newLink); 437 return new LinkEvent(LINK_UPDATED, newLink);
...@@ -848,4 +882,25 @@ public class GossipLinkStore ...@@ -848,4 +882,25 @@ public class GossipLinkStore
848 }); 882 });
849 } 883 }
850 } 884 }
885 +
886 + private final class LinkInjectedEventListener
887 + implements ClusterMessageHandler {
888 + @Override
889 + public void handle(ClusterMessage message) {
890 +
891 + log.trace("Received injected link event from peer: {}", message.sender());
892 + LinkInjectedEvent linkInjectedEvent = SERIALIZER.decode(message.payload());
893 +
894 + ProviderId providerId = linkInjectedEvent.providerId();
895 + LinkDescription linkDescription = linkInjectedEvent.linkDescription();
896 +
897 + executor.submit(new Runnable() {
898 +
899 + @Override
900 + public void run() {
901 + createOrUpdateLink(providerId, linkDescription);
902 + }
903 + });
904 + }
905 + }
851 } 906 }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
15 */ 15 */
16 package org.onosproject.store.link.impl; 16 package org.onosproject.store.link.impl;
17 17
18 -import org.onosproject.store.cluster.messaging.MessageSubject; 18 + import org.onosproject.store.cluster.messaging.MessageSubject;
19 19
20 /** 20 /**
21 * MessageSubjects used by GossipLinkStore peer-peer communication. 21 * MessageSubjects used by GossipLinkStore peer-peer communication.
...@@ -30,4 +30,6 @@ public final class GossipLinkStoreMessageSubjects { ...@@ -30,4 +30,6 @@ public final class GossipLinkStoreMessageSubjects {
30 new MessageSubject("peer-link-removed"); 30 new MessageSubject("peer-link-removed");
31 public static final MessageSubject LINK_ANTI_ENTROPY_ADVERTISEMENT = 31 public static final MessageSubject LINK_ANTI_ENTROPY_ADVERTISEMENT =
32 new MessageSubject("link-enti-entropy-advertisement"); 32 new MessageSubject("link-enti-entropy-advertisement");
33 + public static final MessageSubject LINK_INJECTED =
34 + new MessageSubject("peer-link-injected");
33 } 35 }
......
1 +package org.onosproject.store.link.impl;
2 +
3 +import com.google.common.base.MoreObjects;
4 +import org.onosproject.net.link.LinkDescription;
5 +import org.onosproject.net.provider.ProviderId;
6 +
7 +public class LinkInjectedEvent {
8 +
9 + ProviderId providerId;
10 + LinkDescription linkDescription;
11 +
12 + public LinkInjectedEvent(ProviderId providerId, LinkDescription linkDescription) {
13 + this.providerId = providerId;
14 + this.linkDescription = linkDescription;
15 + }
16 +
17 + public ProviderId providerId() {
18 + return providerId;
19 + }
20 +
21 + public LinkDescription linkDescription() {
22 + return linkDescription;
23 + }
24 +
25 + @Override
26 + public String toString() {
27 + return MoreObjects.toStringHelper(getClass())
28 + .add("providerId", providerId)
29 + .add("linkDescription", linkDescription)
30 + .toString();
31 + }
32 +
33 + // for serializer
34 + protected LinkInjectedEvent() {
35 + this.providerId = null;
36 + this.linkDescription = null;
37 + }
38 +}
...@@ -27,6 +27,8 @@ import org.onlab.packet.IpAddress; ...@@ -27,6 +27,8 @@ import org.onlab.packet.IpAddress;
27 import org.onosproject.cluster.ControllerNode; 27 import org.onosproject.cluster.ControllerNode;
28 import org.onosproject.cluster.DefaultControllerNode; 28 import org.onosproject.cluster.DefaultControllerNode;
29 import org.onosproject.cluster.NodeId; 29 import org.onosproject.cluster.NodeId;
30 +import org.onosproject.mastership.MastershipService;
31 +import org.onosproject.mastership.MastershipServiceAdapter;
30 import org.onosproject.mastership.MastershipTerm; 32 import org.onosproject.mastership.MastershipTerm;
31 import org.onosproject.net.ConnectPoint; 33 import org.onosproject.net.ConnectPoint;
32 import org.onosproject.net.DefaultAnnotations; 34 import org.onosproject.net.DefaultAnnotations;
...@@ -115,7 +117,7 @@ public class GossipLinkStoreTest { ...@@ -115,7 +117,7 @@ public class GossipLinkStoreTest {
115 private DeviceClockManager deviceClockManager; 117 private DeviceClockManager deviceClockManager;
116 private DeviceClockService deviceClockService; 118 private DeviceClockService deviceClockService;
117 private ClusterCommunicationService clusterCommunicator; 119 private ClusterCommunicationService clusterCommunicator;
118 - 120 + private MastershipService mastershipService;
119 121
120 @BeforeClass 122 @BeforeClass
121 public static void setUpBeforeClass() throws Exception { 123 public static void setUpBeforeClass() throws Exception {
...@@ -146,11 +148,13 @@ public class GossipLinkStoreTest { ...@@ -146,11 +148,13 @@ public class GossipLinkStoreTest {
146 linkStoreImpl.deviceClockService = deviceClockService; 148 linkStoreImpl.deviceClockService = deviceClockService;
147 linkStoreImpl.clusterCommunicator = clusterCommunicator; 149 linkStoreImpl.clusterCommunicator = clusterCommunicator;
148 linkStoreImpl.clusterService = new TestClusterService(); 150 linkStoreImpl.clusterService = new TestClusterService();
151 + linkStoreImpl.mastershipService = new TestMastershipService();
149 linkStoreImpl.activate(); 152 linkStoreImpl.activate();
150 linkStore = linkStoreImpl; 153 linkStore = linkStoreImpl;
151 154
152 verify(clusterCommunicator); 155 verify(clusterCommunicator);
153 reset(clusterCommunicator); 156 reset(clusterCommunicator);
157 +
154 } 158 }
155 159
156 @After 160 @After
...@@ -602,4 +606,11 @@ public class GossipLinkStoreTest { ...@@ -602,4 +606,11 @@ public class GossipLinkStoreTest {
602 nodeStates.put(NID2, ACTIVE); 606 nodeStates.put(NID2, ACTIVE);
603 } 607 }
604 } 608 }
609 +
610 + private final class TestMastershipService extends MastershipServiceAdapter {
611 + @Override
612 + public NodeId getMasterFor(DeviceId deviceId) {
613 + return NID1;
614 + }
615 + }
605 } 616 }
......