Yuta HIGUCHI

GossipHostStore: add AE support

- modified HostDescription family to hold Set of IpAddresses

Change-Id: Id920fdc83817802885e8528af185a5ad590bf999
1 package org.onlab.onos.net.host; 1 package org.onlab.onos.net.host;
2 2
3 +import java.util.Collections;
4 +import java.util.Set;
5 +
3 import org.onlab.onos.net.AbstractDescription; 6 import org.onlab.onos.net.AbstractDescription;
4 import org.onlab.onos.net.HostLocation; 7 import org.onlab.onos.net.HostLocation;
5 import org.onlab.onos.net.SparseAnnotations; 8 import org.onlab.onos.net.SparseAnnotations;
...@@ -7,6 +10,8 @@ import org.onlab.packet.IpPrefix; ...@@ -7,6 +10,8 @@ import org.onlab.packet.IpPrefix;
7 import org.onlab.packet.MacAddress; 10 import org.onlab.packet.MacAddress;
8 import org.onlab.packet.VlanId; 11 import org.onlab.packet.VlanId;
9 12
13 +import com.google.common.collect.ImmutableSet;
14 +
10 import static com.google.common.base.MoreObjects.toStringHelper; 15 import static com.google.common.base.MoreObjects.toStringHelper;
11 16
12 /** 17 /**
...@@ -18,7 +23,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -18,7 +23,7 @@ public class DefaultHostDescription extends AbstractDescription
18 private final MacAddress mac; 23 private final MacAddress mac;
19 private final VlanId vlan; 24 private final VlanId vlan;
20 private final HostLocation location; 25 private final HostLocation location;
21 - private final IpPrefix ip; 26 + private final Set<IpPrefix> ip;
22 27
23 /** 28 /**
24 * Creates a host description using the supplied information. 29 * Creates a host description using the supplied information.
...@@ -31,7 +36,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -31,7 +36,7 @@ public class DefaultHostDescription extends AbstractDescription
31 public DefaultHostDescription(MacAddress mac, VlanId vlan, 36 public DefaultHostDescription(MacAddress mac, VlanId vlan,
32 HostLocation location, 37 HostLocation location,
33 SparseAnnotations... annotations) { 38 SparseAnnotations... annotations) {
34 - this(mac, vlan, location, null, annotations); 39 + this(mac, vlan, location, Collections.<IpPrefix>emptySet(), annotations);
35 } 40 }
36 41
37 /** 42 /**
...@@ -46,11 +51,26 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -46,11 +51,26 @@ public class DefaultHostDescription extends AbstractDescription
46 public DefaultHostDescription(MacAddress mac, VlanId vlan, 51 public DefaultHostDescription(MacAddress mac, VlanId vlan,
47 HostLocation location, IpPrefix ip, 52 HostLocation location, IpPrefix ip,
48 SparseAnnotations... annotations) { 53 SparseAnnotations... annotations) {
54 + this(mac, vlan, location, ImmutableSet.of(ip), annotations);
55 + }
56 +
57 + /**
58 + * Creates a host description using the supplied information.
59 + *
60 + * @param mac host MAC address
61 + * @param vlan host VLAN identifier
62 + * @param location host location
63 + * @param ip host IP addresses
64 + * @param annotations optional key/value annotations map
65 + */
66 + public DefaultHostDescription(MacAddress mac, VlanId vlan,
67 + HostLocation location, Set<IpPrefix> ip,
68 + SparseAnnotations... annotations) {
49 super(annotations); 69 super(annotations);
50 this.mac = mac; 70 this.mac = mac;
51 this.vlan = vlan; 71 this.vlan = vlan;
52 this.location = location; 72 this.location = location;
53 - this.ip = ip; 73 + this.ip = ImmutableSet.copyOf(ip);
54 } 74 }
55 75
56 @Override 76 @Override
...@@ -69,7 +89,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -69,7 +89,7 @@ public class DefaultHostDescription extends AbstractDescription
69 } 89 }
70 90
71 @Override 91 @Override
72 - public IpPrefix ipAddress() { 92 + public Set<IpPrefix> ipAddress() {
73 return ip; 93 return ip;
74 } 94 }
75 95
......
1 package org.onlab.onos.net.host; 1 package org.onlab.onos.net.host;
2 2
3 +import java.util.Set;
4 +
3 import org.onlab.onos.net.Description; 5 import org.onlab.onos.net.Description;
4 import org.onlab.onos.net.HostLocation; 6 import org.onlab.onos.net.HostLocation;
5 import org.onlab.packet.IpPrefix; 7 import org.onlab.packet.IpPrefix;
...@@ -38,6 +40,6 @@ public interface HostDescription extends Description { ...@@ -38,6 +40,6 @@ public interface HostDescription extends Description {
38 * @return host IP address 40 * @return host IP address
39 */ 41 */
40 // FIXME: Switch to IpAddress 42 // FIXME: Switch to IpAddress
41 - IpPrefix ipAddress(); 43 + Set<IpPrefix> ipAddress();
42 44
43 } 45 }
......
...@@ -8,6 +8,8 @@ import org.onlab.packet.IpPrefix; ...@@ -8,6 +8,8 @@ import org.onlab.packet.IpPrefix;
8 import org.onlab.packet.MacAddress; 8 import org.onlab.packet.MacAddress;
9 import org.onlab.packet.VlanId; 9 import org.onlab.packet.VlanId;
10 10
11 +import com.google.common.collect.ImmutableSet;
12 +
11 import static org.junit.Assert.assertEquals; 13 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue; 14 import static org.junit.Assert.assertTrue;
13 15
...@@ -33,7 +35,7 @@ public class DefualtHostDecriptionTest { ...@@ -33,7 +35,7 @@ public class DefualtHostDecriptionTest {
33 assertEquals("incorrect mac", MAC, host.hwAddress()); 35 assertEquals("incorrect mac", MAC, host.hwAddress());
34 assertEquals("incorrect vlan", VLAN, host.vlan()); 36 assertEquals("incorrect vlan", VLAN, host.vlan());
35 assertEquals("incorrect location", LOC, host.location()); 37 assertEquals("incorrect location", LOC, host.location());
36 - assertEquals("incorrect ip's", IP, host.ipAddress()); 38 + assertEquals("incorrect ip's", ImmutableSet.of(IP), host.ipAddress());
37 assertTrue("incorrect toString", host.toString().contains("vlan=10")); 39 assertTrue("incorrect toString", host.toString().contains("vlan=10"));
38 } 40 }
39 41
......
1 package org.onlab.onos.store.host.impl; 1 package org.onlab.onos.store.host.impl;
2 2
3 +import com.google.common.collect.FluentIterable;
3 import com.google.common.collect.HashMultimap; 4 import com.google.common.collect.HashMultimap;
5 +import com.google.common.collect.ImmutableList;
4 import com.google.common.collect.ImmutableSet; 6 import com.google.common.collect.ImmutableSet;
5 import com.google.common.collect.Multimap; 7 import com.google.common.collect.Multimap;
6 import com.google.common.collect.Sets; 8 import com.google.common.collect.Sets;
7 9
10 +import org.apache.commons.lang3.RandomUtils;
8 import org.apache.felix.scr.annotations.Activate; 11 import org.apache.felix.scr.annotations.Activate;
9 import org.apache.felix.scr.annotations.Component; 12 import org.apache.felix.scr.annotations.Component;
10 import org.apache.felix.scr.annotations.Deactivate; 13 import org.apache.felix.scr.annotations.Deactivate;
...@@ -12,6 +15,8 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -12,6 +15,8 @@ import org.apache.felix.scr.annotations.Reference;
12 import org.apache.felix.scr.annotations.ReferenceCardinality; 15 import org.apache.felix.scr.annotations.ReferenceCardinality;
13 import org.apache.felix.scr.annotations.Service; 16 import org.apache.felix.scr.annotations.Service;
14 import org.onlab.onos.cluster.ClusterService; 17 import org.onlab.onos.cluster.ClusterService;
18 +import org.onlab.onos.cluster.ControllerNode;
19 +import org.onlab.onos.cluster.NodeId;
15 import org.onlab.onos.net.Annotations; 20 import org.onlab.onos.net.Annotations;
16 import org.onlab.onos.net.ConnectPoint; 21 import org.onlab.onos.net.ConnectPoint;
17 import org.onlab.onos.net.DefaultHost; 22 import org.onlab.onos.net.DefaultHost;
...@@ -19,6 +24,7 @@ import org.onlab.onos.net.DeviceId; ...@@ -19,6 +24,7 @@ import org.onlab.onos.net.DeviceId;
19 import org.onlab.onos.net.Host; 24 import org.onlab.onos.net.Host;
20 import org.onlab.onos.net.HostId; 25 import org.onlab.onos.net.HostId;
21 import org.onlab.onos.net.HostLocation; 26 import org.onlab.onos.net.HostLocation;
27 +import org.onlab.onos.net.host.DefaultHostDescription;
22 import org.onlab.onos.net.host.HostClockService; 28 import org.onlab.onos.net.host.HostClockService;
23 import org.onlab.onos.net.host.HostDescription; 29 import org.onlab.onos.net.host.HostDescription;
24 import org.onlab.onos.net.host.HostEvent; 30 import org.onlab.onos.net.host.HostEvent;
...@@ -42,12 +48,19 @@ import org.onlab.util.KryoPool; ...@@ -42,12 +48,19 @@ import org.onlab.util.KryoPool;
42 import org.slf4j.Logger; 48 import org.slf4j.Logger;
43 49
44 import java.io.IOException; 50 import java.io.IOException;
51 +import java.util.HashMap;
45 import java.util.HashSet; 52 import java.util.HashSet;
46 import java.util.Map; 53 import java.util.Map;
47 import java.util.Set; 54 import java.util.Set;
55 +import java.util.Map.Entry;
48 import java.util.concurrent.ConcurrentHashMap; 56 import java.util.concurrent.ConcurrentHashMap;
57 +import java.util.concurrent.ScheduledExecutorService;
58 +import java.util.concurrent.TimeUnit;
49 59
60 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
61 +import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
50 import static org.onlab.onos.net.host.HostEvent.Type.*; 62 import static org.onlab.onos.net.host.HostEvent.Type.*;
63 +import static org.onlab.util.Tools.namedThreads;
51 import static org.slf4j.LoggerFactory.getLogger; 64 import static org.slf4j.LoggerFactory.getLogger;
52 65
53 //TODO: multi-provider, annotation not supported. 66 //TODO: multi-provider, annotation not supported.
...@@ -88,24 +101,58 @@ public class GossipHostStore ...@@ -88,24 +101,58 @@ public class GossipHostStore
88 protected void setupKryoPool() { 101 protected void setupKryoPool() {
89 serializerPool = KryoPool.newBuilder() 102 serializerPool = KryoPool.newBuilder()
90 .register(DistributedStoreSerializers.COMMON) 103 .register(DistributedStoreSerializers.COMMON)
104 + .register(InternalHostEvent.class)
91 .register(InternalHostRemovedEvent.class) 105 .register(InternalHostRemovedEvent.class)
106 + .register(HostFragmentId.class)
107 + .register(HostAntiEntropyAdvertisement.class)
92 .build() 108 .build()
93 .populate(1); 109 .populate(1);
94 } 110 }
95 }; 111 };
96 112
113 + private ScheduledExecutorService executor;
114 +
97 @Activate 115 @Activate
98 public void activate() { 116 public void activate() {
99 clusterCommunicator.addSubscriber( 117 clusterCommunicator.addSubscriber(
100 - GossipHostStoreMessageSubjects.HOST_UPDATED, new InternalHostEventListener()); 118 + GossipHostStoreMessageSubjects.HOST_UPDATED,
119 + new InternalHostEventListener());
120 + clusterCommunicator.addSubscriber(
121 + GossipHostStoreMessageSubjects.HOST_REMOVED,
122 + new InternalHostRemovedEventListener());
101 clusterCommunicator.addSubscriber( 123 clusterCommunicator.addSubscriber(
102 - GossipHostStoreMessageSubjects.HOST_REMOVED, new InternalHostRemovedEventListener()); 124 + GossipHostStoreMessageSubjects.HOST_ANTI_ENTROPY_ADVERTISEMENT,
125 + new InternalHostAntiEntropyAdvertisementListener());
126 +
127 + executor =
128 + newSingleThreadScheduledExecutor(namedThreads("link-anti-entropy-%d"));
129 +
130 + // TODO: Make these configurable
131 + long initialDelaySec = 5;
132 + long periodSec = 5;
133 + // start anti-entropy thread
134 + executor.scheduleAtFixedRate(new SendAdvertisementTask(),
135 + initialDelaySec, periodSec, TimeUnit.SECONDS);
103 136
104 log.info("Started"); 137 log.info("Started");
105 } 138 }
106 139
107 @Deactivate 140 @Deactivate
108 public void deactivate() { 141 public void deactivate() {
142 + executor.shutdownNow();
143 + try {
144 + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
145 + log.error("Timeout during executor shutdown");
146 + }
147 + } catch (InterruptedException e) {
148 + log.error("Error during executor shutdown", e);
149 + }
150 +
151 + hosts.clear();
152 + removedHosts.clear();
153 + locations.clear();
154 + portAddresses.clear();
155 +
109 log.info("Stopped"); 156 log.info("Stopped");
110 } 157 }
111 158
...@@ -153,7 +200,7 @@ public class GossipHostStore ...@@ -153,7 +200,7 @@ public class GossipHostStore
153 descr.hwAddress(), 200 descr.hwAddress(),
154 descr.vlan(), 201 descr.vlan(),
155 new Timestamped<>(descr.location(), timestamp), 202 new Timestamped<>(descr.location(), timestamp),
156 - ImmutableSet.of(descr.ipAddress())); 203 + ImmutableSet.copyOf(descr.ipAddress()));
157 hosts.put(hostId, newhost); 204 hosts.put(hostId, newhost);
158 locations.put(descr.location(), newhost); 205 locations.put(descr.location(), newhost);
159 return new HostEvent(HOST_ADDED, newhost); 206 return new HostEvent(HOST_ADDED, newhost);
...@@ -169,12 +216,12 @@ public class GossipHostStore ...@@ -169,12 +216,12 @@ public class GossipHostStore
169 return new HostEvent(HOST_MOVED, host); 216 return new HostEvent(HOST_MOVED, host);
170 } 217 }
171 218
172 - if (host.ipAddresses().contains(descr.ipAddress())) { 219 + if (host.ipAddresses().containsAll(descr.ipAddress())) {
173 return null; 220 return null;
174 } 221 }
175 222
176 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); 223 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
177 - addresses.add(descr.ipAddress()); 224 + addresses.addAll(descr.ipAddress());
178 StoredHost updated = new StoredHost(providerId, host.id(), 225 StoredHost updated = new StoredHost(providerId, host.id(),
179 host.mac(), host.vlan(), 226 host.mac(), host.vlan(),
180 host.location, addresses); 227 host.location, addresses);
...@@ -381,6 +428,10 @@ public class GossipHostStore ...@@ -381,6 +428,10 @@ public class GossipHostStore
381 public HostLocation location() { 428 public HostLocation location() {
382 return location.value(); 429 return location.value();
383 } 430 }
431 +
432 + public Timestamp timestamp() {
433 + return location.timestamp();
434 + }
384 } 435 }
385 436
386 private void notifyPeers(InternalHostRemovedEvent event) throws IOException { 437 private void notifyPeers(InternalHostRemovedEvent event) throws IOException {
...@@ -399,6 +450,16 @@ public class GossipHostStore ...@@ -399,6 +450,16 @@ public class GossipHostStore
399 clusterCommunicator.broadcast(message); 450 clusterCommunicator.broadcast(message);
400 } 451 }
401 452
453 + private void unicastMessage(NodeId peer,
454 + MessageSubject subject,
455 + Object event) throws IOException {
456 + ClusterMessage message = new ClusterMessage(
457 + clusterService.getLocalNode().id(),
458 + subject,
459 + SERIALIZER.encode(event));
460 + clusterCommunicator.unicast(message, peer);
461 + }
462 +
402 private void notifyDelegateIfNotNull(HostEvent event) { 463 private void notifyDelegateIfNotNull(HostEvent event) {
403 if (event != null) { 464 if (event != null) {
404 notifyDelegate(event); 465 notifyDelegate(event);
...@@ -434,4 +495,165 @@ public class GossipHostStore ...@@ -434,4 +495,165 @@ public class GossipHostStore
434 notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp)); 495 notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp));
435 } 496 }
436 } 497 }
498 +
499 + private final class SendAdvertisementTask implements Runnable {
500 +
501 + @Override
502 + public void run() {
503 + if (Thread.currentThread().isInterrupted()) {
504 + log.info("Interrupted, quitting");
505 + return;
506 + }
507 +
508 + try {
509 + final NodeId self = clusterService.getLocalNode().id();
510 + Set<ControllerNode> nodes = clusterService.getNodes();
511 +
512 + ImmutableList<NodeId> nodeIds = FluentIterable.from(nodes)
513 + .transform(toNodeId())
514 + .toList();
515 +
516 + if (nodeIds.size() == 1 && nodeIds.get(0).equals(self)) {
517 + log.debug("No other peers in the cluster.");
518 + return;
519 + }
520 +
521 + NodeId peer;
522 + do {
523 + int idx = RandomUtils.nextInt(0, nodeIds.size());
524 + peer = nodeIds.get(idx);
525 + } while (peer.equals(self));
526 +
527 + HostAntiEntropyAdvertisement ad = createAdvertisement();
528 +
529 + if (Thread.currentThread().isInterrupted()) {
530 + log.info("Interrupted, quitting");
531 + return;
532 + }
533 +
534 + try {
535 + unicastMessage(peer, GossipHostStoreMessageSubjects.HOST_ANTI_ENTROPY_ADVERTISEMENT, ad);
536 + } catch (IOException e) {
537 + log.debug("Failed to send anti-entropy advertisement", e);
538 + return;
539 + }
540 + } catch (Exception e) {
541 + // catch all Exception to avoid Scheduled task being suppressed.
542 + log.error("Exception thrown while sending advertisement", e);
543 + }
544 + }
545 + }
546 +
547 + private HostAntiEntropyAdvertisement createAdvertisement() {
548 + final NodeId self = clusterService.getLocalNode().id();
549 +
550 + Map<HostFragmentId, Timestamp> timestamps = new HashMap<>(hosts.size());
551 + Map<HostId, Timestamp> tombstones = new HashMap<>(removedHosts.size());
552 +
553 + for (Entry<HostId, StoredHost> e : hosts.entrySet()) {
554 +
555 + final HostId hostId = e.getKey();
556 + final StoredHost hostInfo = e.getValue();
557 + final ProviderId providerId = hostInfo.providerId();
558 + timestamps.put(new HostFragmentId(hostId, providerId), hostInfo.timestamp());
559 + }
560 +
561 + for (Entry<HostId, Timestamped<Host>> e : removedHosts.entrySet()) {
562 + tombstones.put(e.getKey(), e.getValue().timestamp());
563 + }
564 +
565 + return new HostAntiEntropyAdvertisement(self, timestamps, tombstones);
566 + }
567 +
568 + private synchronized void handleAntiEntropyAdvertisement(HostAntiEntropyAdvertisement ad) {
569 +
570 + final NodeId sender = ad.sender();
571 +
572 + for (Entry<HostId, StoredHost> host : hosts.entrySet()) {
573 + // for each locally live Hosts...
574 + final HostId hostId = host.getKey();
575 + final StoredHost localHost = host.getValue();
576 + final ProviderId providerId = localHost.providerId();
577 + final HostFragmentId hostFragId = new HostFragmentId(hostId, providerId);
578 + final Timestamp localLiveTimestamp = localHost.timestamp();
579 +
580 + Timestamp remoteTimestamp = ad.timestamps().get(hostFragId);
581 + if (remoteTimestamp == null) {
582 + remoteTimestamp = ad.tombstones().get(hostId);
583 + }
584 + if (remoteTimestamp == null ||
585 + localLiveTimestamp.compareTo(remoteTimestamp) > 0) {
586 +
587 + // local is more recent, push
588 + // TODO: annotation is lost
589 + final HostDescription desc = new DefaultHostDescription(
590 + localHost.mac(),
591 + localHost.vlan(),
592 + localHost.location(),
593 + localHost.ipAddresses());
594 + try {
595 + unicastMessage(sender, GossipHostStoreMessageSubjects.HOST_UPDATED,
596 + new InternalHostEvent(providerId, hostId, desc, localHost.timestamp()));
597 + } catch (IOException e1) {
598 + log.debug("Failed to send advertisement response", e1);
599 + }
600 + }
601 +
602 + final Timestamp remoteDeadTimestamp = ad.tombstones().get(hostId);
603 + if (remoteDeadTimestamp != null &&
604 + remoteDeadTimestamp.compareTo(localLiveTimestamp) > 0) {
605 + // sender has recent remove
606 + notifyDelegateIfNotNull(removeHostInternal(hostId, remoteDeadTimestamp));
607 + }
608 + }
609 +
610 + for (Entry<HostId, Timestamped<Host>> dead : removedHosts.entrySet()) {
611 + // for each locally dead Hosts
612 + final HostId hostId = dead.getKey();
613 + final Timestamp localDeadTimestamp = dead.getValue().timestamp();
614 +
615 + // TODO: pick proper ProviderId, when supporting multi-provider
616 + final ProviderId providerId = dead.getValue().value().providerId();
617 + final HostFragmentId hostFragId = new HostFragmentId(hostId, providerId);
618 +
619 + final Timestamp remoteLiveTimestamp = ad.timestamps().get(hostFragId);
620 + if (remoteLiveTimestamp != null &&
621 + localDeadTimestamp.compareTo(remoteLiveTimestamp) > 0) {
622 + // sender has zombie, push
623 + try {
624 + unicastMessage(sender, GossipHostStoreMessageSubjects.HOST_REMOVED,
625 + new InternalHostRemovedEvent(hostId, localDeadTimestamp));
626 + } catch (IOException e1) {
627 + log.debug("Failed to send advertisement response", e1);
628 + }
629 + }
630 + }
631 +
632 +
633 + for (Entry<HostId, Timestamp> e : ad.tombstones().entrySet()) {
634 + // for each remote tombstone advertisement...
635 + final HostId hostId = e.getKey();
636 + final Timestamp adRemoveTimestamp = e.getValue();
637 +
638 + final StoredHost storedHost = hosts.get(hostId);
639 + if (storedHost == null) {
640 + continue;
641 + }
642 + if (adRemoveTimestamp.compareTo(storedHost.timestamp()) > 0) {
643 + // sender has recent remove info, locally remove
644 + notifyDelegateIfNotNull(removeHostInternal(hostId, adRemoveTimestamp));
645 + }
646 + }
647 + }
648 +
649 + private final class InternalHostAntiEntropyAdvertisementListener implements
650 + ClusterMessageHandler {
651 +
652 + @Override
653 + public void handle(ClusterMessage message) {
654 + log.debug("Received Host Anti-Entropy advertisement from peer: {}", message.sender());
655 + HostAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
656 + handleAntiEntropyAdvertisement(advertisement);
657 + }
658 + }
437 } 659 }
......
...@@ -4,6 +4,11 @@ import org.onlab.onos.store.cluster.messaging.MessageSubject; ...@@ -4,6 +4,11 @@ import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 4
5 public final class GossipHostStoreMessageSubjects { 5 public final class GossipHostStoreMessageSubjects {
6 private GossipHostStoreMessageSubjects() {} 6 private GossipHostStoreMessageSubjects() {}
7 - public static final MessageSubject HOST_UPDATED = new MessageSubject("peer-host-updated"); 7 +
8 - public static final MessageSubject HOST_REMOVED = new MessageSubject("peer-host-removed"); 8 + public static final MessageSubject HOST_UPDATED
9 + = new MessageSubject("peer-host-updated");
10 + public static final MessageSubject HOST_REMOVED
11 + = new MessageSubject("peer-host-removed");
12 + public static final MessageSubject HOST_ANTI_ENTROPY_ADVERTISEMENT
13 + = new MessageSubject("host-enti-entropy-advertisement");;
9 } 14 }
......
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Map;
6 +
7 +import org.onlab.onos.cluster.NodeId;
8 +import org.onlab.onos.net.HostId;
9 +import org.onlab.onos.store.Timestamp;
10 +
11 +/**
12 + * Host AE Advertisement message.
13 + */
14 +public final class HostAntiEntropyAdvertisement {
15 +
16 + private final NodeId sender;
17 + private final Map<HostFragmentId, Timestamp> timestamps;
18 + private final Map<HostId, Timestamp> tombstones;
19 +
20 +
21 + public HostAntiEntropyAdvertisement(NodeId sender,
22 + Map<HostFragmentId, Timestamp> timestamps,
23 + Map<HostId, Timestamp> tombstones) {
24 + this.sender = checkNotNull(sender);
25 + this.timestamps = checkNotNull(timestamps);
26 + this.tombstones = checkNotNull(tombstones);
27 + }
28 +
29 + public NodeId sender() {
30 + return sender;
31 + }
32 +
33 + public Map<HostFragmentId, Timestamp> timestamps() {
34 + return timestamps;
35 + }
36 +
37 + public Map<HostId, Timestamp> tombstones() {
38 + return tombstones;
39 + }
40 +
41 + // For serializer
42 + @SuppressWarnings("unused")
43 + private HostAntiEntropyAdvertisement() {
44 + this.sender = null;
45 + this.timestamps = null;
46 + this.tombstones = null;
47 + }
48 +}
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import java.util.Objects;
4 +
5 +import org.onlab.onos.net.HostId;
6 +import org.onlab.onos.net.provider.ProviderId;
7 +
8 +import com.google.common.base.MoreObjects;
9 +
10 +/**
11 + * Identifier for HostDescription from a Provider.
12 + */
13 +public final class HostFragmentId {
14 + public final ProviderId providerId;
15 + public final HostId hostId;
16 +
17 + public HostFragmentId(HostId hostId, ProviderId providerId) {
18 + this.providerId = providerId;
19 + this.hostId = hostId;
20 + }
21 +
22 + public HostId hostId() {
23 + return hostId;
24 + }
25 +
26 + public ProviderId providerId() {
27 + return providerId;
28 + }
29 +
30 + @Override
31 + public int hashCode() {
32 + return Objects.hash(providerId, hostId);
33 + }
34 +
35 + @Override
36 + public boolean equals(Object obj) {
37 + if (this == obj) {
38 + return true;
39 + }
40 + if (!(obj instanceof HostFragmentId)) {
41 + return false;
42 + }
43 + HostFragmentId that = (HostFragmentId) obj;
44 + return Objects.equals(this.hostId, that.hostId) &&
45 + Objects.equals(this.providerId, that.providerId);
46 + }
47 +
48 + @Override
49 + public String toString() {
50 + return MoreObjects.toStringHelper(getClass())
51 + .add("providerId", providerId)
52 + .add("hostId", hostId)
53 + .toString();
54 + }
55 +
56 + // for serializer
57 + @SuppressWarnings("unused")
58 + private HostFragmentId() {
59 + this.providerId = null;
60 + this.hostId = null;
61 + }
62 +}
...@@ -84,7 +84,7 @@ public class DistributedHostStore ...@@ -84,7 +84,7 @@ public class DistributedHostStore
84 descr.hwAddress(), 84 descr.hwAddress(),
85 descr.vlan(), 85 descr.vlan(),
86 descr.location(), 86 descr.location(),
87 - ImmutableSet.of(descr.ipAddress())); 87 + ImmutableSet.copyOf(descr.ipAddress()));
88 synchronized (this) { 88 synchronized (this) {
89 hosts.put(hostId, newhost); 89 hosts.put(hostId, newhost);
90 locations.put(descr.location(), newhost); 90 locations.put(descr.location(), newhost);
...@@ -101,12 +101,12 @@ public class DistributedHostStore ...@@ -101,12 +101,12 @@ public class DistributedHostStore
101 return new HostEvent(HOST_MOVED, host); 101 return new HostEvent(HOST_MOVED, host);
102 } 102 }
103 103
104 - if (host.ipAddresses().contains(descr.ipAddress())) { 104 + if (host.ipAddresses().containsAll(descr.ipAddress())) {
105 return null; 105 return null;
106 } 106 }
107 107
108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); 108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
109 - addresses.add(descr.ipAddress()); 109 + addresses.addAll(descr.ipAddress());
110 StoredHost updated = new StoredHost(providerId, host.id(), 110 StoredHost updated = new StoredHost(providerId, host.id(),
111 host.mac(), host.vlan(), 111 host.mac(), host.vlan(),
112 descr.location(), addresses); 112 descr.location(), addresses);
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.net.HostLocation;
5 +import org.onlab.onos.net.PortNumber;
6 +
7 +import com.esotericsoftware.kryo.Kryo;
8 +import com.esotericsoftware.kryo.Serializer;
9 +import com.esotericsoftware.kryo.io.Input;
10 +import com.esotericsoftware.kryo.io.Output;
11 +
12 +/**
13 +* Kryo Serializer for {@link HostLocation}.
14 +*/
15 +public class HostLocationSerializer extends Serializer<HostLocation> {
16 +
17 + /**
18 + * Creates {@link HostLocation} serializer instance.
19 + */
20 + public HostLocationSerializer() {
21 + // non-null, immutable
22 + super(false, true);
23 + }
24 +
25 + @Override
26 + public void write(Kryo kryo, Output output, HostLocation object) {
27 + kryo.writeClassAndObject(output, object.deviceId());
28 + kryo.writeClassAndObject(output, object.port());
29 + output.writeLong(object.time());
30 + }
31 +
32 + @Override
33 + public HostLocation read(Kryo kryo, Input input, Class<HostLocation> type) {
34 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
35 + PortNumber portNumber = (PortNumber) kryo.readClassAndObject(input);
36 + long time = input.readLong();
37 + return new HostLocation(deviceId, portNumber, time);
38 + }
39 +
40 +}
...@@ -17,6 +17,8 @@ import org.onlab.onos.net.DefaultPort; ...@@ -17,6 +17,8 @@ import org.onlab.onos.net.DefaultPort;
17 import org.onlab.onos.net.Device; 17 import org.onlab.onos.net.Device;
18 import org.onlab.onos.net.DeviceId; 18 import org.onlab.onos.net.DeviceId;
19 import org.onlab.onos.net.Element; 19 import org.onlab.onos.net.Element;
20 +import org.onlab.onos.net.HostId;
21 +import org.onlab.onos.net.HostLocation;
20 import org.onlab.onos.net.Link; 22 import org.onlab.onos.net.Link;
21 import org.onlab.onos.net.LinkKey; 23 import org.onlab.onos.net.LinkKey;
22 import org.onlab.onos.net.MastershipRole; 24 import org.onlab.onos.net.MastershipRole;
...@@ -24,15 +26,20 @@ import org.onlab.onos.net.Port; ...@@ -24,15 +26,20 @@ import org.onlab.onos.net.Port;
24 import org.onlab.onos.net.PortNumber; 26 import org.onlab.onos.net.PortNumber;
25 import org.onlab.onos.net.device.DefaultDeviceDescription; 27 import org.onlab.onos.net.device.DefaultDeviceDescription;
26 import org.onlab.onos.net.device.DefaultPortDescription; 28 import org.onlab.onos.net.device.DefaultPortDescription;
29 +import org.onlab.onos.net.host.DefaultHostDescription;
30 +import org.onlab.onos.net.host.HostDescription;
27 import org.onlab.onos.net.link.DefaultLinkDescription; 31 import org.onlab.onos.net.link.DefaultLinkDescription;
28 import org.onlab.onos.net.provider.ProviderId; 32 import org.onlab.onos.net.provider.ProviderId;
29 import org.onlab.onos.store.Timestamp; 33 import org.onlab.onos.store.Timestamp;
30 import org.onlab.packet.IpAddress; 34 import org.onlab.packet.IpAddress;
31 import org.onlab.packet.IpPrefix; 35 import org.onlab.packet.IpPrefix;
36 +import org.onlab.packet.MacAddress;
37 +import org.onlab.packet.VlanId;
32 import org.onlab.util.KryoPool; 38 import org.onlab.util.KryoPool;
33 39
34 import com.google.common.collect.ImmutableList; 40 import com.google.common.collect.ImmutableList;
35 import com.google.common.collect.ImmutableMap; 41 import com.google.common.collect.ImmutableMap;
42 +import com.google.common.collect.ImmutableSet;
36 43
37 public final class KryoPoolUtil { 44 public final class KryoPoolUtil {
38 45
...@@ -42,6 +49,8 @@ public final class KryoPoolUtil { ...@@ -42,6 +49,8 @@ public final class KryoPoolUtil {
42 public static final KryoPool MISC = KryoPool.newBuilder() 49 public static final KryoPool MISC = KryoPool.newBuilder()
43 .register(IpPrefix.class, new IpPrefixSerializer()) 50 .register(IpPrefix.class, new IpPrefixSerializer())
44 .register(IpAddress.class, new IpAddressSerializer()) 51 .register(IpAddress.class, new IpAddressSerializer())
52 + .register(MacAddress.class, new MacAddressSerializer())
53 + .register(VlanId.class)
45 .build(); 54 .build();
46 55
47 // TODO: Populate other classes 56 // TODO: Populate other classes
...@@ -52,6 +61,7 @@ public final class KryoPoolUtil { ...@@ -52,6 +61,7 @@ public final class KryoPoolUtil {
52 .register(MISC) 61 .register(MISC)
53 .register(ImmutableMap.class, new ImmutableMapSerializer()) 62 .register(ImmutableMap.class, new ImmutableMapSerializer())
54 .register(ImmutableList.class, new ImmutableListSerializer()) 63 .register(ImmutableList.class, new ImmutableListSerializer())
64 + .register(ImmutableSet.class, new ImmutableSetSerializer())
55 .register( 65 .register(
56 // 66 //
57 ArrayList.class, 67 ArrayList.class,
...@@ -71,8 +81,10 @@ public final class KryoPoolUtil { ...@@ -71,8 +81,10 @@ public final class KryoPoolUtil {
71 DefaultPortDescription.class, 81 DefaultPortDescription.class,
72 Element.class, 82 Element.class,
73 Link.Type.class, 83 Link.Type.class,
74 - Timestamp.class 84 + Timestamp.class,
75 - 85 + HostId.class,
86 + HostDescription.class,
87 + DefaultHostDescription.class
76 ) 88 )
77 .register(URI.class, new URISerializer()) 89 .register(URI.class, new URISerializer())
78 .register(NodeId.class, new NodeIdSerializer()) 90 .register(NodeId.class, new NodeIdSerializer())
...@@ -85,6 +97,7 @@ public final class KryoPoolUtil { ...@@ -85,6 +97,7 @@ public final class KryoPoolUtil {
85 .register(DefaultLink.class, new DefaultLinkSerializer()) 97 .register(DefaultLink.class, new DefaultLinkSerializer())
86 .register(MastershipTerm.class, new MastershipTermSerializer()) 98 .register(MastershipTerm.class, new MastershipTermSerializer())
87 .register(MastershipRole.class, new MastershipRoleSerializer()) 99 .register(MastershipRole.class, new MastershipRoleSerializer())
100 + .register(HostLocation.class, new HostLocationSerializer())
88 101
89 .build(); 102 .build();
90 103
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.packet.MacAddress;
4 +
5 +import com.esotericsoftware.kryo.Kryo;
6 +import com.esotericsoftware.kryo.Serializer;
7 +import com.esotericsoftware.kryo.io.Input;
8 +import com.esotericsoftware.kryo.io.Output;
9 +
10 +/**
11 + * Kryo Serializer for {@link MacAddress}.
12 + */
13 +public class MacAddressSerializer extends Serializer<MacAddress> {
14 +
15 + /**
16 + * Creates {@link MacAddress} serializer instance.
17 + */
18 + public MacAddressSerializer() {
19 + super(false, true);
20 + }
21 +
22 + @Override
23 + public void write(Kryo kryo, Output output, MacAddress object) {
24 + output.writeBytes(object.getAddress());
25 + }
26 +
27 + @Override
28 + public MacAddress read(Kryo kryo, Input input, Class<MacAddress> type) {
29 + return MacAddress.valueOf(input.readBytes(MacAddress.MAC_ADDRESS_LENGTH));
30 + }
31 +
32 +}
...@@ -84,7 +84,7 @@ public class SimpleHostStore ...@@ -84,7 +84,7 @@ public class SimpleHostStore
84 descr.hwAddress(), 84 descr.hwAddress(),
85 descr.vlan(), 85 descr.vlan(),
86 descr.location(), 86 descr.location(),
87 - ImmutableSet.of(descr.ipAddress())); 87 + ImmutableSet.copyOf(descr.ipAddress()));
88 synchronized (this) { 88 synchronized (this) {
89 hosts.put(hostId, newhost); 89 hosts.put(hostId, newhost);
90 locations.put(descr.location(), newhost); 90 locations.put(descr.location(), newhost);
...@@ -101,12 +101,12 @@ public class SimpleHostStore ...@@ -101,12 +101,12 @@ public class SimpleHostStore
101 return new HostEvent(HOST_MOVED, host); 101 return new HostEvent(HOST_MOVED, host);
102 } 102 }
103 103
104 - if (host.ipAddresses().contains(descr.ipAddress())) { 104 + if (host.ipAddresses().containsAll(descr.ipAddress())) {
105 return null; 105 return null;
106 } 106 }
107 107
108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); 108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
109 - addresses.add(descr.ipAddress()); 109 + addresses.addAll(descr.ipAddress());
110 StoredHost updated = new StoredHost(providerId, host.id(), 110 StoredHost updated = new StoredHost(providerId, host.id(),
111 host.mac(), host.vlan(), 111 host.mac(), host.vlan(),
112 descr.location(), addresses); 112 descr.location(), addresses);
......