Madan Jampani

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 106 changed files with 2482 additions and 429 deletions
...@@ -100,6 +100,7 @@ public class ReactiveForwarding { ...@@ -100,6 +100,7 @@ public class ReactiveForwarding {
100 context.block(); 100 context.block();
101 return; 101 return;
102 } 102 }
103 +
103 HostId id = HostId.hostId(ethPkt.getDestinationMAC()); 104 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
104 105
105 // Do we know who this is for? If not, flood and bail. 106 // Do we know who this is for? If not, flood and bail.
...@@ -112,7 +113,9 @@ public class ReactiveForwarding { ...@@ -112,7 +113,9 @@ public class ReactiveForwarding {
112 // Are we on an edge switch that our destination is on? If so, 113 // Are we on an edge switch that our destination is on? If so,
113 // simply forward out to the destination and bail. 114 // simply forward out to the destination and bail.
114 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) { 115 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
116 + if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
115 installRule(context, dst.location().port()); 117 installRule(context, dst.location().port());
118 + }
116 return; 119 return;
117 } 120 }
118 121
...@@ -175,6 +178,8 @@ public class ReactiveForwarding { ...@@ -175,6 +178,8 @@ public class ReactiveForwarding {
175 // We don't yet support bufferids in the flowservice so packet out first. 178 // We don't yet support bufferids in the flowservice so packet out first.
176 packetOut(context, portNumber); 179 packetOut(context, portNumber);
177 180
181 + if (context.inPacket().parsed().getEtherType() == Ethernet.TYPE_IPV4) {
182 +
178 // Install the flow rule to handle this type of message from now on. 183 // Install the flow rule to handle this type of message from now on.
179 Ethernet inPkt = context.inPacket().parsed(); 184 Ethernet inPkt = context.inPacket().parsed();
180 TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder(); 185 TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
...@@ -191,6 +196,7 @@ public class ReactiveForwarding { ...@@ -191,6 +196,7 @@ public class ReactiveForwarding {
191 196
192 flowRuleService.applyFlowRules(f); 197 flowRuleService.applyFlowRules(f);
193 } 198 }
199 + }
194 200
195 } 201 }
196 202
......
...@@ -56,7 +56,8 @@ public interface MastershipService { ...@@ -56,7 +56,8 @@ public interface MastershipService {
56 Set<DeviceId> getDevicesOf(NodeId nodeId); 56 Set<DeviceId> getDevicesOf(NodeId nodeId);
57 57
58 /** 58 /**
59 - * Returns the mastership term service for getting term information. 59 + * Returns the mastership term service for getting read-only
60 + * term information.
60 * 61 *
61 * @return the MastershipTermService for this mastership manager 62 * @return the MastershipTermService for this mastership manager
62 */ 63 */
......
...@@ -64,4 +64,14 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -64,4 +64,14 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
64 * @return the current master's ID and the term value for device, or null 64 * @return the current master's ID and the term value for device, or null
65 */ 65 */
66 MastershipTerm getTermFor(DeviceId deviceId); 66 MastershipTerm getTermFor(DeviceId deviceId);
67 +
68 + /**
69 + * Revokes a controller instance's mastership over a device and hands
70 + * over mastership to another controller instance.
71 + *
72 + * @param nodeId the controller instance identifier
73 + * @param deviceId device to revoke mastership for
74 + * @return a mastership event
75 + */
76 + MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId);
67 } 77 }
......
1 +package org.onlab.onos.net;
2 +
3 +import com.google.common.collect.ImmutableSet;
4 +
5 +import java.util.HashMap;
6 +import java.util.Map;
7 +import java.util.Set;
8 +
9 +import static com.google.common.base.Preconditions.checkArgument;
10 +
11 +/**
12 + * Base abstraction of an annotated entity.
13 + */
14 +public class AbstractAnnotated implements Annotated {
15 +
16 + private static final Map<String, String> EMPTY = new HashMap<>();
17 +
18 + private final Map<String, String> annotations;
19 +
20 + // For serialization
21 + protected AbstractAnnotated() {
22 + this.annotations = EMPTY;
23 + }
24 +
25 + /**
26 + * Creates a new entity, annotated with the specified annotations.
27 + *
28 + * @param annotations optional key/value annotations map
29 + */
30 + protected AbstractAnnotated(Map<String, String>[] annotations) {
31 + checkArgument(annotations.length <= 1, "Only one set of annotations is expected");
32 + this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
33 + }
34 +
35 + @Override
36 + public Set<String> annotationKeys() {
37 + return ImmutableSet.copyOf(annotations.keySet());
38 + }
39 +
40 + @Override
41 + public String annotation(String key) {
42 + return annotations.get(key);
43 + }
44 +
45 +}
...@@ -2,10 +2,12 @@ package org.onlab.onos.net; ...@@ -2,10 +2,12 @@ package org.onlab.onos.net;
2 2
3 import org.onlab.onos.net.provider.ProviderId; 3 import org.onlab.onos.net.provider.ProviderId;
4 4
5 +import java.util.Map;
6 +
5 /** 7 /**
6 * Base implementation of a network model entity. 8 * Base implementation of a network model entity.
7 */ 9 */
8 -public class AbstractModel implements Provided { 10 +public class AbstractModel extends AbstractAnnotated implements Provided {
9 11
10 private final ProviderId providerId; 12 private final ProviderId providerId;
11 13
...@@ -15,11 +17,16 @@ public class AbstractModel implements Provided { ...@@ -15,11 +17,16 @@ public class AbstractModel implements Provided {
15 } 17 }
16 18
17 /** 19 /**
18 - * Creates a model entity attributed to the specified provider. 20 + * Creates a model entity attributed to the specified provider and
21 + * optionally annotated.
19 * 22 *
20 * @param providerId identity of the provider 23 * @param providerId identity of the provider
24 + * @param annotations optional key/value annotations
21 */ 25 */
22 - protected AbstractModel(ProviderId providerId) { 26 + @SafeVarargs
27 + protected AbstractModel(ProviderId providerId,
28 + Map<String, String>... annotations) {
29 + super(annotations);
23 this.providerId = providerId; 30 this.providerId = providerId;
24 } 31 }
25 32
......
1 +package org.onlab.onos.net;
2 +
3 +import java.util.Set;
4 +
5 +/**
6 + * Represents an entity that carries arbitrary annotations.
7 + */
8 +public interface Annotated {
9 +
10 + /**
11 + * Returns the set of annotation keys currently available.
12 + *
13 + * @return set of annotation keys
14 + */
15 + Set<String> annotationKeys();
16 +
17 + /**
18 + * Returns the annotation value for the specified key.
19 + *
20 + * @param key annotation key
21 + * @return annotation value; null if there is no annotation
22 + */
23 + String annotation(String key);
24 +
25 +}
...@@ -3,5 +3,5 @@ package org.onlab.onos.net; ...@@ -3,5 +3,5 @@ package org.onlab.onos.net;
3 /** 3 /**
4 * Base abstraction of a piece of information about network elements. 4 * Base abstraction of a piece of information about network elements.
5 */ 5 */
6 -public interface Description { 6 +public interface Description extends Annotated {
7 } 7 }
......
1 package org.onlab.onos.net.device; 1 package org.onlab.onos.net.device;
2 2
3 +import org.onlab.onos.net.AbstractAnnotated;
4 +
3 import java.net.URI; 5 import java.net.URI;
6 +import java.util.Map;
4 7
5 import static com.google.common.base.MoreObjects.toStringHelper; 8 import static com.google.common.base.MoreObjects.toStringHelper;
6 import static com.google.common.base.Preconditions.checkNotNull; 9 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -9,7 +12,8 @@ import static org.onlab.onos.net.Device.Type; ...@@ -9,7 +12,8 @@ import static org.onlab.onos.net.Device.Type;
9 /** 12 /**
10 * Default implementation of immutable device description entity. 13 * Default implementation of immutable device description entity.
11 */ 14 */
12 -public class DefaultDeviceDescription implements DeviceDescription { 15 +public class DefaultDeviceDescription extends AbstractAnnotated
16 + implements DeviceDescription {
13 private final URI uri; 17 private final URI uri;
14 private final Type type; 18 private final Type type;
15 private final String manufacturer; 19 private final String manufacturer;
...@@ -26,10 +30,14 @@ public class DefaultDeviceDescription implements DeviceDescription { ...@@ -26,10 +30,14 @@ public class DefaultDeviceDescription implements DeviceDescription {
26 * @param hwVersion device HW version 30 * @param hwVersion device HW version
27 * @param swVersion device SW version 31 * @param swVersion device SW version
28 * @param serialNumber device serial number 32 * @param serialNumber device serial number
33 + * @param annotations optional key/value annotations map
29 */ 34 */
35 + @SafeVarargs
30 public DefaultDeviceDescription(URI uri, Type type, String manufacturer, 36 public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
31 String hwVersion, String swVersion, 37 String hwVersion, String swVersion,
32 - String serialNumber) { 38 + String serialNumber,
39 + Map<String, String>... annotations) {
40 + super(annotations);
33 this.uri = checkNotNull(uri, "Device URI cannot be null"); 41 this.uri = checkNotNull(uri, "Device URI cannot be null");
34 this.type = checkNotNull(type, "Device type cannot be null"); 42 this.type = checkNotNull(type, "Device type cannot be null");
35 this.manufacturer = manufacturer; 43 this.manufacturer = manufacturer;
......
...@@ -48,6 +48,7 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> { ...@@ -48,6 +48,7 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> {
48 DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId, 48 DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
49 DeviceDescription deviceDescription); 49 DeviceDescription deviceDescription);
50 50
51 + // TODO: We may need to enforce that ancillary cannot interfere this state
51 /** 52 /**
52 * Removes the specified infrastructure device. 53 * Removes the specified infrastructure device.
53 * 54 *
...@@ -60,22 +61,24 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> { ...@@ -60,22 +61,24 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> {
60 * Updates the ports of the specified infrastructure device using the given 61 * Updates the ports of the specified infrastructure device using the given
61 * list of port descriptions. The list is assumed to be comprehensive. 62 * list of port descriptions. The list is assumed to be comprehensive.
62 * 63 *
64 + * @param providerId provider identifier
63 * @param deviceId device identifier 65 * @param deviceId device identifier
64 * @param portDescriptions list of port descriptions 66 * @param portDescriptions list of port descriptions
65 * @return ready to send events describing what occurred; empty list if no change 67 * @return ready to send events describing what occurred; empty list if no change
66 */ 68 */
67 - List<DeviceEvent> updatePorts(DeviceId deviceId, 69 + List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
68 List<PortDescription> portDescriptions); 70 List<PortDescription> portDescriptions);
69 71
70 /** 72 /**
71 * Updates the port status of the specified infrastructure device using the 73 * Updates the port status of the specified infrastructure device using the
72 * given port description. 74 * given port description.
73 * 75 *
76 + * @param providerId provider identifier
74 * @param deviceId device identifier 77 * @param deviceId device identifier
75 * @param portDescription port description 78 * @param portDescription port description
76 * @return ready to send event describing what occurred; null if no change 79 * @return ready to send event describing what occurred; null if no change
77 */ 80 */
78 - DeviceEvent updatePortStatus(DeviceId deviceId, 81 + DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
79 PortDescription portDescription); 82 PortDescription portDescription);
80 83
81 /** 84 /**
......
...@@ -27,9 +27,11 @@ public class DefaultFlowRule implements FlowRule { ...@@ -27,9 +27,11 @@ public class DefaultFlowRule implements FlowRule {
27 27
28 private final ApplicationId appId; 28 private final ApplicationId appId;
29 29
30 + private boolean expired;
31 +
30 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, 32 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
31 TrafficTreatment treatment, int priority, FlowRuleState state, 33 TrafficTreatment treatment, int priority, FlowRuleState state,
32 - long life, long packets, long bytes, long flowId) { 34 + long life, long packets, long bytes, long flowId, boolean expired) {
33 this.deviceId = deviceId; 35 this.deviceId = deviceId;
34 this.priority = priority; 36 this.priority = priority;
35 this.selector = selector; 37 this.selector = selector;
...@@ -37,7 +39,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -37,7 +39,7 @@ public class DefaultFlowRule implements FlowRule {
37 this.state = state; 39 this.state = state;
38 this.appId = ApplicationId.valueOf((int) (flowId >> 32)); 40 this.appId = ApplicationId.valueOf((int) (flowId >> 32));
39 this.id = FlowId.valueOf(flowId); 41 this.id = FlowId.valueOf(flowId);
40 - 42 + this.expired = expired;
41 this.life = life; 43 this.life = life;
42 this.packets = packets; 44 this.packets = packets;
43 this.bytes = bytes; 45 this.bytes = bytes;
...@@ -186,4 +188,9 @@ public class DefaultFlowRule implements FlowRule { ...@@ -186,4 +188,9 @@ public class DefaultFlowRule implements FlowRule {
186 .toString(); 188 .toString();
187 } 189 }
188 190
191 + @Override
192 + public boolean expired() {
193 + return expired;
194 + }
195 +
189 } 196 }
......
...@@ -111,4 +111,11 @@ public interface FlowRule { ...@@ -111,4 +111,11 @@ public interface FlowRule {
111 */ 111 */
112 long bytes(); 112 long bytes();
113 113
114 + /**
115 + * Indicates that this flow has expired at the device.
116 + *
117 + * @return true if it has expired, false otherwise
118 + */
119 + boolean expired();
120 +
114 } 121 }
......
1 package org.onlab.onos.net.host; 1 package org.onlab.onos.net.host;
2 2
3 -import static com.google.common.base.MoreObjects.toStringHelper; 3 +import com.google.common.collect.ImmutableSet;
4 - 4 +import org.onlab.onos.net.AbstractAnnotated;
5 -import java.util.HashSet;
6 -import java.util.Set;
7 -
8 import org.onlab.onos.net.HostLocation; 5 import org.onlab.onos.net.HostLocation;
9 import org.onlab.packet.IpPrefix; 6 import org.onlab.packet.IpPrefix;
10 import org.onlab.packet.MacAddress; 7 import org.onlab.packet.MacAddress;
11 import org.onlab.packet.VlanId; 8 import org.onlab.packet.VlanId;
12 9
13 -import com.google.common.collect.ImmutableSet; 10 +import java.util.HashSet;
11 +import java.util.Map;
12 +import java.util.Set;
13 +
14 +import static com.google.common.base.MoreObjects.toStringHelper;
14 15
15 -public class DefaultHostDescription implements HostDescription { 16 +/**
17 + * Default implementation of an immutable host description.
18 + */
19 +public class DefaultHostDescription extends AbstractAnnotated
20 + implements HostDescription {
16 21
17 private final MacAddress mac; 22 private final MacAddress mac;
18 private final VlanId vlan; 23 private final VlanId vlan;
19 private final HostLocation location; 24 private final HostLocation location;
20 private final Set<IpPrefix> ips; 25 private final Set<IpPrefix> ips;
21 26
27 + /**
28 + * Creates a host description using the supplied information.
29 + *
30 + * @param mac host MAC address
31 + * @param vlan host VLAN identifier
32 + * @param location host location
33 + * @param annotations optional key/value annotations map
34 + */
35 + @SafeVarargs
22 public DefaultHostDescription(MacAddress mac, VlanId vlan, 36 public DefaultHostDescription(MacAddress mac, VlanId vlan,
23 - HostLocation loc) { 37 + HostLocation location,
24 - this.mac = mac; 38 + Map<String, String>... annotations) {
25 - this.vlan = vlan; 39 + this(mac, vlan, location, new HashSet<IpPrefix>(), annotations);
26 - this.location = loc;
27 - this.ips = new HashSet<IpPrefix>();
28 } 40 }
29 41
42 + /**
43 + * Creates a host description using the supplied information.
44 + *
45 + * @param mac host MAC address
46 + * @param vlan host VLAN identifier
47 + * @param location host location
48 + * @param ips of host IP addresses
49 + * @param annotations optional key/value annotations map
50 + */
51 + @SafeVarargs
30 public DefaultHostDescription(MacAddress mac, VlanId vlan, 52 public DefaultHostDescription(MacAddress mac, VlanId vlan,
31 - HostLocation loc, Set<IpPrefix> ips) { 53 + HostLocation location, Set<IpPrefix> ips,
54 + Map<String, String>... annotations) {
55 + super(annotations);
32 this.mac = mac; 56 this.mac = mac;
33 this.vlan = vlan; 57 this.vlan = vlan;
34 - this.location = loc; 58 + this.location = location;
35 - this.ips = new HashSet<IpPrefix>(ips); 59 + this.ips = new HashSet<>(ips);
36 } 60 }
37 61
38 @Override 62 @Override
......
...@@ -35,10 +35,22 @@ public abstract class AbstractProviderRegistry<P extends Provider, S extends Pro ...@@ -35,10 +35,22 @@ public abstract class AbstractProviderRegistry<P extends Provider, S extends Pro
35 public synchronized S register(P provider) { 35 public synchronized S register(P provider) {
36 checkNotNull(provider, "Provider cannot be null"); 36 checkNotNull(provider, "Provider cannot be null");
37 checkState(!services.containsKey(provider.id()), "Provider %s already registered", provider.id()); 37 checkState(!services.containsKey(provider.id()), "Provider %s already registered", provider.id());
38 +
39 + // If the provider is a primary one, check for a conflict.
40 + ProviderId pid = provider.id();
41 + checkState(pid.isAncillary() || !providersByScheme.containsKey(pid.scheme()),
42 + "A primary provider with id %s is already registered",
43 + providersByScheme.get(pid.scheme()));
44 +
38 S service = createProviderService(provider); 45 S service = createProviderService(provider);
39 services.put(provider.id(), service); 46 services.put(provider.id(), service);
40 providers.put(provider.id(), provider); 47 providers.put(provider.id(), provider);
41 - // FIXME populate scheme look-up 48 +
49 + // Register the provider by URI scheme only if it is not ancillary.
50 + if (!pid.isAncillary()) {
51 + providersByScheme.put(pid.scheme(), provider);
52 + }
53 +
42 return service; 54 return service;
43 } 55 }
44 56
......
...@@ -5,15 +5,26 @@ import java.util.Objects; ...@@ -5,15 +5,26 @@ import java.util.Objects;
5 import static com.google.common.base.MoreObjects.toStringHelper; 5 import static com.google.common.base.MoreObjects.toStringHelper;
6 6
7 /** 7 /**
8 - * Notion of provider identity. 8 + * External identity of a {@link org.onlab.onos.net.provider.Provider} family.
9 + * It also carriers two designations of external characteristics, the URI
10 + * scheme and primary/ancillary indicator.
11 + * <p/>
12 + * The device URI scheme is used to determine applicability of a provider to
13 + * operations on a specific device. The ancillary indicator serves to designate
14 + * a provider as a primary or ancillary.
15 + *
16 + * A {@link org.onlab.onos.net.provider.ProviderRegistry} uses this designation
17 + * to permit only one primary provider per device URI scheme. Multiple
18 + * ancillary providers can register with the same device URI scheme however.
9 */ 19 */
10 public class ProviderId { 20 public class ProviderId {
11 21
12 private final String scheme; 22 private final String scheme;
13 private final String id; 23 private final String id;
24 + private final boolean ancillary;
14 25
15 /** 26 /**
16 - * Creates a new provider identifier from the specified string. 27 + * Creates a new primary provider identifier from the specified string.
17 * The providers are expected to follow the reverse DNS convention, e.g. 28 * The providers are expected to follow the reverse DNS convention, e.g.
18 * {@code org.onlab.onos.provider.of.device} 29 * {@code org.onlab.onos.provider.of.device}
19 * 30 *
...@@ -21,8 +32,22 @@ public class ProviderId { ...@@ -21,8 +32,22 @@ public class ProviderId {
21 * @param id string identifier 32 * @param id string identifier
22 */ 33 */
23 public ProviderId(String scheme, String id) { 34 public ProviderId(String scheme, String id) {
35 + this(scheme, id, false);
36 + }
37 +
38 + /**
39 + * Creates a new provider identifier from the specified string.
40 + * The providers are expected to follow the reverse DNS convention, e.g.
41 + * {@code org.onlab.onos.provider.of.device}
42 + *
43 + * @param scheme device URI scheme to which this provider is bound, e.g. "of", "snmp"
44 + * @param id string identifier
45 + * @param ancillary ancillary provider indicator
46 + */
47 + public ProviderId(String scheme, String id, boolean ancillary) {
24 this.scheme = scheme; 48 this.scheme = scheme;
25 this.id = id; 49 this.id = id;
50 + this.ancillary = ancillary;
26 } 51 }
27 52
28 /** 53 /**
...@@ -43,6 +68,15 @@ public class ProviderId { ...@@ -43,6 +68,15 @@ public class ProviderId {
43 return id; 68 return id;
44 } 69 }
45 70
71 + /**
72 + * Indicates whether this identifier designates an ancillary providers.
73 + *
74 + * @return true if the provider is ancillary; false if primary
75 + */
76 + public boolean isAncillary() {
77 + return ancillary;
78 + }
79 +
46 @Override 80 @Override
47 public int hashCode() { 81 public int hashCode() {
48 return Objects.hash(scheme, id); 82 return Objects.hash(scheme, id);
...@@ -56,14 +90,16 @@ public class ProviderId { ...@@ -56,14 +90,16 @@ public class ProviderId {
56 if (obj instanceof ProviderId) { 90 if (obj instanceof ProviderId) {
57 final ProviderId other = (ProviderId) obj; 91 final ProviderId other = (ProviderId) obj;
58 return Objects.equals(this.scheme, other.scheme) && 92 return Objects.equals(this.scheme, other.scheme) &&
59 - Objects.equals(this.id, other.id); 93 + Objects.equals(this.id, other.id) &&
94 + this.ancillary == other.ancillary;
60 } 95 }
61 return false; 96 return false;
62 } 97 }
63 98
64 @Override 99 @Override
65 public String toString() { 100 public String toString() {
66 - return toStringHelper(this).add("scheme", scheme).add("id", id).toString(); 101 + return toStringHelper(this).add("scheme", scheme).add("id", id)
102 + .add("ancillary", ancillary).toString();
67 } 103 }
68 104
69 } 105 }
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.net.topology; ...@@ -2,6 +2,7 @@ package org.onlab.onos.net.topology;
2 2
3 import com.google.common.collect.ImmutableSet; 3 import com.google.common.collect.ImmutableSet;
4 import com.google.common.collect.Maps; 4 import com.google.common.collect.Maps;
5 +import org.onlab.onos.net.AbstractAnnotated;
5 import org.onlab.onos.net.ConnectPoint; 6 import org.onlab.onos.net.ConnectPoint;
6 import org.onlab.onos.net.Device; 7 import org.onlab.onos.net.Device;
7 import org.onlab.onos.net.DeviceId; 8 import org.onlab.onos.net.DeviceId;
...@@ -12,7 +13,8 @@ import java.util.Map; ...@@ -12,7 +13,8 @@ import java.util.Map;
12 /** 13 /**
13 * Default implementation of an immutable topology graph data carrier. 14 * Default implementation of an immutable topology graph data carrier.
14 */ 15 */
15 -public class DefaultGraphDescription implements GraphDescription { 16 +public class DefaultGraphDescription extends AbstractAnnotated
17 + implements GraphDescription {
16 18
17 private final long nanos; 19 private final long nanos;
18 private final ImmutableSet<TopologyVertex> vertexes; 20 private final ImmutableSet<TopologyVertex> vertexes;
...@@ -28,8 +30,13 @@ public class DefaultGraphDescription implements GraphDescription { ...@@ -28,8 +30,13 @@ public class DefaultGraphDescription implements GraphDescription {
28 * @param nanos time in nanos of when the topology description was created 30 * @param nanos time in nanos of when the topology description was created
29 * @param devices collection of infrastructure devices 31 * @param devices collection of infrastructure devices
30 * @param links collection of infrastructure links 32 * @param links collection of infrastructure links
33 + * @param annotations optional key/value annotations map
31 */ 34 */
32 - public DefaultGraphDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) { 35 + @SafeVarargs
36 + public DefaultGraphDescription(long nanos, Iterable<Device> devices,
37 + Iterable<Link> links,
38 + Map<String, String>... annotations) {
39 + super(annotations);
33 this.nanos = nanos; 40 this.nanos = nanos;
34 this.vertexes = buildVertexes(devices); 41 this.vertexes = buildVertexes(devices);
35 this.edges = buildEdges(links); 42 this.edges = buildEdges(links);
......
1 +package org.onlab.onos.cluster;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +
5 +import org.junit.Test;
6 +
7 +import com.google.common.testing.EqualsTester;
8 +
9 +public class MastershipTermTest {
10 +
11 + private static final NodeId N1 = new NodeId("foo");
12 + private static final NodeId N2 = new NodeId("bar");
13 +
14 + private static final MastershipTerm TERM1 = MastershipTerm.of(N1, 0);
15 + private static final MastershipTerm TERM2 = MastershipTerm.of(N2, 1);
16 + private static final MastershipTerm TERM3 = MastershipTerm.of(N2, 1);
17 + private static final MastershipTerm TERM4 = MastershipTerm.of(N1, 1);
18 +
19 + @Test
20 + public void basics() {
21 + assertEquals("incorrect term number", 0, TERM1.termNumber());
22 + assertEquals("incorrect master", new NodeId("foo"), TERM1.master());
23 + }
24 +
25 + @Test
26 + public void testEquality() {
27 + new EqualsTester().addEqualityGroup(MastershipTerm.of(N1, 0), TERM1)
28 + .addEqualityGroup(TERM2, TERM3)
29 + .addEqualityGroup(TERM4);
30 + }
31 +
32 +}
...@@ -35,7 +35,7 @@ public class AbstractProviderRegistryTest { ...@@ -35,7 +35,7 @@ public class AbstractProviderRegistryTest {
35 assertThat("provider not found", registry.getProviders().contains(fooId)); 35 assertThat("provider not found", registry.getProviders().contains(fooId));
36 assertEquals("incorrect provider", psFoo.provider(), pFoo); 36 assertEquals("incorrect provider", psFoo.provider(), pFoo);
37 37
38 - ProviderId barId = new ProviderId("of", "bar"); 38 + ProviderId barId = new ProviderId("snmp", "bar");
39 TestProvider pBar = new TestProvider(barId); 39 TestProvider pBar = new TestProvider(barId);
40 TestProviderService psBar = registry.register(pBar); 40 TestProviderService psBar = registry.register(pBar);
41 assertEquals("incorrect provider count", 2, registry.getProviders().size()); 41 assertEquals("incorrect provider count", 2, registry.getProviders().size());
...@@ -49,6 +49,16 @@ public class AbstractProviderRegistryTest { ...@@ -49,6 +49,16 @@ public class AbstractProviderRegistryTest {
49 assertThat("provider not found", registry.getProviders().contains(barId)); 49 assertThat("provider not found", registry.getProviders().contains(barId));
50 } 50 }
51 51
52 + @Test
53 + public void ancillaryProviders() {
54 + TestProviderRegistry registry = new TestProviderRegistry();
55 + TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
56 + TestProvider pBar = new TestProvider(new ProviderId("of", "bar", true));
57 + registry.register(pFoo);
58 + registry.register(pBar);
59 + assertEquals("incorrect provider count", 2, registry.getProviders().size());
60 + }
61 +
52 @Test(expected = IllegalStateException.class) 62 @Test(expected = IllegalStateException.class)
53 public void duplicateRegistration() { 63 public void duplicateRegistration() {
54 TestProviderRegistry registry = new TestProviderRegistry(); 64 TestProviderRegistry registry = new TestProviderRegistry();
...@@ -57,6 +67,15 @@ public class AbstractProviderRegistryTest { ...@@ -57,6 +67,15 @@ public class AbstractProviderRegistryTest {
57 registry.register(pFoo); 67 registry.register(pFoo);
58 } 68 }
59 69
70 + @Test(expected = IllegalStateException.class)
71 + public void duplicateSchemeRegistration() {
72 + TestProviderRegistry registry = new TestProviderRegistry();
73 + TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
74 + TestProvider pBar = new TestProvider(new ProviderId("of", "bar"));
75 + registry.register(pFoo);
76 + registry.register(pBar);
77 + }
78 +
60 @Test 79 @Test
61 public void voidUnregistration() { 80 public void voidUnregistration() {
62 TestProviderRegistry registry = new TestProviderRegistry(); 81 TestProviderRegistry registry = new TestProviderRegistry();
......
...@@ -11,6 +11,8 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -11,6 +11,8 @@ import org.apache.felix.scr.annotations.Deactivate;
11 import org.apache.felix.scr.annotations.Reference; 11 import org.apache.felix.scr.annotations.Reference;
12 import org.apache.felix.scr.annotations.ReferenceCardinality; 12 import org.apache.felix.scr.annotations.ReferenceCardinality;
13 import org.apache.felix.scr.annotations.Service; 13 import org.apache.felix.scr.annotations.Service;
14 +import org.onlab.onos.cluster.ClusterEvent;
15 +import org.onlab.onos.cluster.ClusterEventListener;
14 import org.onlab.onos.cluster.ClusterService; 16 import org.onlab.onos.cluster.ClusterService;
15 import org.onlab.onos.cluster.MastershipAdminService; 17 import org.onlab.onos.cluster.MastershipAdminService;
16 import org.onlab.onos.cluster.MastershipEvent; 18 import org.onlab.onos.cluster.MastershipEvent;
...@@ -52,9 +54,12 @@ implements MastershipService, MastershipAdminService { ...@@ -52,9 +54,12 @@ implements MastershipService, MastershipAdminService {
52 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 54 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
53 protected ClusterService clusterService; 55 protected ClusterService clusterService;
54 56
57 + private ClusterEventListener clusterListener = new InternalClusterEventListener();
58 +
55 @Activate 59 @Activate
56 public void activate() { 60 public void activate() {
57 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry); 61 eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
62 + clusterService.addListener(clusterListener);
58 store.setDelegate(delegate); 63 store.setDelegate(delegate);
59 log.info("Started"); 64 log.info("Started");
60 } 65 }
...@@ -62,6 +67,7 @@ implements MastershipService, MastershipAdminService { ...@@ -62,6 +67,7 @@ implements MastershipService, MastershipAdminService {
62 @Deactivate 67 @Deactivate
63 public void deactivate() { 68 public void deactivate() {
64 eventDispatcher.removeSink(MastershipEvent.class); 69 eventDispatcher.removeSink(MastershipEvent.class);
70 + clusterService.removeListener(clusterListener);
65 store.unsetDelegate(delegate); 71 store.unsetDelegate(delegate);
66 log.info("Stopped"); 72 log.info("Stopped");
67 } 73 }
...@@ -71,14 +77,18 @@ implements MastershipService, MastershipAdminService { ...@@ -71,14 +77,18 @@ implements MastershipService, MastershipAdminService {
71 checkNotNull(nodeId, NODE_ID_NULL); 77 checkNotNull(nodeId, NODE_ID_NULL);
72 checkNotNull(deviceId, DEVICE_ID_NULL); 78 checkNotNull(deviceId, DEVICE_ID_NULL);
73 checkNotNull(role, ROLE_NULL); 79 checkNotNull(role, ROLE_NULL);
74 - //TODO figure out appropriate action for non-MASTER roles, if we even set those 80 +
81 + MastershipEvent event = null;
75 if (role.equals(MastershipRole.MASTER)) { 82 if (role.equals(MastershipRole.MASTER)) {
76 - MastershipEvent event = store.setMaster(nodeId, deviceId); 83 + event = store.setMaster(nodeId, deviceId);
84 + } else {
85 + event = store.unsetMaster(nodeId, deviceId);
86 + }
87 +
77 if (event != null) { 88 if (event != null) {
78 post(event); 89 post(event);
79 } 90 }
80 } 91 }
81 - }
82 92
83 @Override 93 @Override
84 public MastershipRole getLocalRole(DeviceId deviceId) { 94 public MastershipRole getLocalRole(DeviceId deviceId) {
...@@ -88,8 +98,16 @@ implements MastershipService, MastershipAdminService { ...@@ -88,8 +98,16 @@ implements MastershipService, MastershipAdminService {
88 98
89 @Override 99 @Override
90 public void relinquishMastership(DeviceId deviceId) { 100 public void relinquishMastership(DeviceId deviceId) {
91 - checkNotNull(deviceId, DEVICE_ID_NULL); 101 + MastershipRole role = getLocalRole(deviceId);
92 - // FIXME: add method to store to give up mastership and trigger new master selection process 102 + if (!role.equals(MastershipRole.MASTER)) {
103 + return;
104 + }
105 +
106 + MastershipEvent event = store.unsetMaster(
107 + clusterService.getLocalNode().id(), deviceId);
108 + if (event != null) {
109 + post(event);
110 + }
93 } 111 }
94 112
95 @Override 113 @Override
...@@ -146,6 +164,26 @@ implements MastershipService, MastershipAdminService { ...@@ -146,6 +164,26 @@ implements MastershipService, MastershipAdminService {
146 164
147 } 165 }
148 166
167 + //callback for reacting to cluster events
168 + private class InternalClusterEventListener implements ClusterEventListener {
169 +
170 + @Override
171 + public void event(ClusterEvent event) {
172 + switch (event.type()) {
173 + //FIXME: worry about addition when the time comes
174 + case INSTANCE_ADDED:
175 + case INSTANCE_ACTIVATED:
176 + break;
177 + case INSTANCE_REMOVED:
178 + case INSTANCE_DEACTIVATED:
179 + break;
180 + default:
181 + log.warn("unknown cluster event {}", event);
182 + }
183 + }
184 +
185 + }
186 +
149 public class InternalDelegate implements MastershipStoreDelegate { 187 public class InternalDelegate implements MastershipStoreDelegate {
150 188
151 @Override 189 @Override
......
...@@ -16,6 +16,7 @@ import org.onlab.onos.cluster.ClusterService; ...@@ -16,6 +16,7 @@ import org.onlab.onos.cluster.ClusterService;
16 import org.onlab.onos.cluster.MastershipEvent; 16 import org.onlab.onos.cluster.MastershipEvent;
17 import org.onlab.onos.cluster.MastershipListener; 17 import org.onlab.onos.cluster.MastershipListener;
18 import org.onlab.onos.cluster.MastershipService; 18 import org.onlab.onos.cluster.MastershipService;
19 +import org.onlab.onos.cluster.MastershipTermService;
19 import org.onlab.onos.cluster.MastershipTerm; 20 import org.onlab.onos.cluster.MastershipTerm;
20 import org.onlab.onos.event.AbstractListenerRegistry; 21 import org.onlab.onos.event.AbstractListenerRegistry;
21 import org.onlab.onos.event.EventDeliveryService; 22 import org.onlab.onos.event.EventDeliveryService;
...@@ -76,6 +77,8 @@ public class DeviceManager ...@@ -76,6 +77,8 @@ public class DeviceManager
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected MastershipService mastershipService; 78 protected MastershipService mastershipService;
78 79
80 + protected MastershipTermService termService;
81 +
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected ClockService clockService; 83 protected ClockService clockService;
81 84
...@@ -84,6 +87,7 @@ public class DeviceManager ...@@ -84,6 +87,7 @@ public class DeviceManager
84 store.setDelegate(delegate); 87 store.setDelegate(delegate);
85 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry); 88 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
86 mastershipService.addListener(mastershipListener); 89 mastershipService.addListener(mastershipListener);
90 + termService = mastershipService.requestTermService();
87 log.info("Started"); 91 log.info("Started");
88 } 92 }
89 93
...@@ -198,7 +202,7 @@ public class DeviceManager ...@@ -198,7 +202,7 @@ public class DeviceManager
198 log.info("Device {} connected", deviceId); 202 log.info("Device {} connected", deviceId);
199 mastershipService.requestRoleFor(deviceId); 203 mastershipService.requestRoleFor(deviceId);
200 provider().roleChanged(event.subject(), 204 provider().roleChanged(event.subject(),
201 - mastershipService.getLocalRole(deviceId)); 205 + mastershipService.requestRoleFor(deviceId));
202 post(event); 206 post(event);
203 } 207 }
204 } 208 }
...@@ -208,8 +212,11 @@ public class DeviceManager ...@@ -208,8 +212,11 @@ public class DeviceManager
208 checkNotNull(deviceId, DEVICE_ID_NULL); 212 checkNotNull(deviceId, DEVICE_ID_NULL);
209 checkValidity(); 213 checkValidity();
210 DeviceEvent event = store.markOffline(deviceId); 214 DeviceEvent event = store.markOffline(deviceId);
215 +
216 + //we're no longer capable of mastership.
211 if (event != null) { 217 if (event != null) {
212 log.info("Device {} disconnected", deviceId); 218 log.info("Device {} disconnected", deviceId);
219 + mastershipService.relinquishMastership(deviceId);
213 post(event); 220 post(event);
214 } 221 }
215 } 222 }
...@@ -221,8 +228,9 @@ public class DeviceManager ...@@ -221,8 +228,9 @@ public class DeviceManager
221 checkNotNull(portDescriptions, 228 checkNotNull(portDescriptions,
222 "Port descriptions list cannot be null"); 229 "Port descriptions list cannot be null");
223 checkValidity(); 230 checkValidity();
224 - List<DeviceEvent> events = store.updatePorts(deviceId, 231 + this.provider().id();
225 - portDescriptions); 232 + List<DeviceEvent> events = store.updatePorts(this.provider().id(),
233 + deviceId, portDescriptions);
226 for (DeviceEvent event : events) { 234 for (DeviceEvent event : events) {
227 post(event); 235 post(event);
228 } 236 }
...@@ -234,8 +242,8 @@ public class DeviceManager ...@@ -234,8 +242,8 @@ public class DeviceManager
234 checkNotNull(deviceId, DEVICE_ID_NULL); 242 checkNotNull(deviceId, DEVICE_ID_NULL);
235 checkNotNull(portDescription, PORT_DESCRIPTION_NULL); 243 checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
236 checkValidity(); 244 checkValidity();
237 - DeviceEvent event = store.updatePortStatus(deviceId, 245 + DeviceEvent event = store.updatePortStatus(this.provider().id(),
238 - portDescription); 246 + deviceId, portDescription);
239 if (event != null) { 247 if (event != null) {
240 log.info("Device {} port {} status changed", deviceId, event 248 log.info("Device {} port {} status changed", deviceId, event
241 .port().number()); 249 .port().number());
......
...@@ -161,7 +161,11 @@ implements FlowRuleService, FlowRuleProviderRegistry { ...@@ -161,7 +161,11 @@ implements FlowRuleService, FlowRuleProviderRegistry {
161 switch (stored.state()) { 161 switch (stored.state()) {
162 case ADDED: 162 case ADDED:
163 case PENDING_ADD: 163 case PENDING_ADD:
164 + if (flowRule.expired()) {
165 + event = store.removeFlowRule(flowRule);
166 + } else {
164 frp.applyFlowRule(stored); 167 frp.applyFlowRule(stored);
168 + }
165 break; 169 break;
166 case PENDING_REMOVE: 170 case PENDING_REMOVE:
167 case REMOVED: 171 case REMOVED:
......
...@@ -231,6 +231,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -231,6 +231,7 @@ public class ProxyArpManager implements ProxyArpService {
231 arp.setOpCode(ARP.OP_REPLY); 231 arp.setOpCode(ARP.OP_REPLY);
232 arp.setProtocolType(ARP.PROTO_TYPE_IP); 232 arp.setProtocolType(ARP.PROTO_TYPE_IP);
233 arp.setHardwareType(ARP.HW_TYPE_ETHERNET); 233 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
234 +
234 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN); 235 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
235 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); 236 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
236 arp.setSenderHardwareAddress(h.mac().getAddress()); 237 arp.setSenderHardwareAddress(h.mac().getAddress());
...@@ -238,7 +239,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -238,7 +239,7 @@ public class ProxyArpManager implements ProxyArpService {
238 239
239 arp.setTargetProtocolAddress(((ARP) request.getPayload()) 240 arp.setTargetProtocolAddress(((ARP) request.getPayload())
240 .getSenderProtocolAddress()); 241 .getSenderProtocolAddress());
241 - arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toInt()); 242 + arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt());
242 eth.setPayload(arp); 243 eth.setPayload(arp);
243 return eth; 244 return eth;
244 } 245 }
...@@ -291,7 +292,6 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -291,7 +292,6 @@ public class ProxyArpManager implements ProxyArpService {
291 case DEVICE_MASTERSHIP_CHANGED: 292 case DEVICE_MASTERSHIP_CHANGED:
292 case DEVICE_SUSPENDED: 293 case DEVICE_SUSPENDED:
293 case DEVICE_UPDATED: 294 case DEVICE_UPDATED:
294 - case PORT_UPDATED:
295 // nothing to do in these cases; handled when links get reported 295 // nothing to do in these cases; handled when links get reported
296 break; 296 break;
297 case DEVICE_REMOVED: 297 case DEVICE_REMOVED:
...@@ -301,10 +301,13 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -301,10 +301,13 @@ public class ProxyArpManager implements ProxyArpService {
301 } 301 }
302 break; 302 break;
303 case PORT_ADDED: 303 case PORT_ADDED:
304 + case PORT_UPDATED:
304 synchronized (externalPorts) { 305 synchronized (externalPorts) {
306 + if (event.port().isEnabled()) {
305 externalPorts.put(device, event.port().number()); 307 externalPorts.put(device, event.port().number());
306 internalPorts.remove(device, event.port().number()); 308 internalPorts.remove(device, event.port().number());
307 } 309 }
310 + }
308 break; 311 break;
309 case PORT_REMOVED: 312 case PORT_REMOVED:
310 synchronized (externalPorts) { 313 synchronized (externalPorts) {
......
...@@ -65,8 +65,8 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -65,8 +65,8 @@ public class DefaultTopologyProvider extends AbstractProvider
65 private volatile boolean isStarted = false; 65 private volatile boolean isStarted = false;
66 66
67 private TopologyProviderService providerService; 67 private TopologyProviderService providerService;
68 - private DeviceListener deviceListener = new InnerDeviceListener(); 68 + private DeviceListener deviceListener = new InternalDeviceListener();
69 - private LinkListener linkListener = new InnerLinkListener(); 69 + private LinkListener linkListener = new InternalLinkListener();
70 70
71 private EventAccumulator accumulator; 71 private EventAccumulator accumulator;
72 private ExecutorService executor; 72 private ExecutorService executor;
...@@ -132,7 +132,7 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -132,7 +132,7 @@ public class DefaultTopologyProvider extends AbstractProvider
132 } 132 }
133 133
134 // Callback for device events 134 // Callback for device events
135 - private class InnerDeviceListener implements DeviceListener { 135 + private class InternalDeviceListener implements DeviceListener {
136 @Override 136 @Override
137 public void event(DeviceEvent event) { 137 public void event(DeviceEvent event) {
138 DeviceEvent.Type type = event.type(); 138 DeviceEvent.Type type = event.type();
...@@ -144,7 +144,7 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -144,7 +144,7 @@ public class DefaultTopologyProvider extends AbstractProvider
144 } 144 }
145 145
146 // Callback for link events 146 // Callback for link events
147 - private class InnerLinkListener implements LinkListener { 147 + private class InternalLinkListener implements LinkListener {
148 @Override 148 @Override
149 public void event(LinkEvent event) { 149 public void event(LinkEvent event) {
150 accumulator.add(event); 150 accumulator.add(event);
......
...@@ -15,10 +15,11 @@ import org.onlab.onos.cluster.MastershipTermService; ...@@ -15,10 +15,11 @@ import org.onlab.onos.cluster.MastershipTermService;
15 import org.onlab.onos.cluster.NodeId; 15 import org.onlab.onos.cluster.NodeId;
16 import org.onlab.onos.event.impl.TestEventDispatcher; 16 import org.onlab.onos.event.impl.TestEventDispatcher;
17 import org.onlab.onos.net.DeviceId; 17 import org.onlab.onos.net.DeviceId;
18 -import org.onlab.onos.net.trivial.impl.SimpleMastershipStore; 18 +import org.onlab.onos.store.trivial.impl.SimpleMastershipStore;
19 import org.onlab.packet.IpPrefix; 19 import org.onlab.packet.IpPrefix;
20 20
21 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertEquals;
22 +import static org.junit.Assert.assertNull;
22 import static org.onlab.onos.net.MastershipRole.*; 23 import static org.onlab.onos.net.MastershipRole.*;
23 24
24 /** 25 /**
...@@ -65,7 +66,24 @@ public class MastershipManagerTest { ...@@ -65,7 +66,24 @@ public class MastershipManagerTest {
65 66
66 @Test 67 @Test
67 public void relinquishMastership() { 68 public void relinquishMastership() {
68 - //TODO 69 + //no backups - should turn to standby and no master for device
70 + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
71 + assertEquals("wrong role:", MASTER, mgr.getLocalRole(DEV_MASTER));
72 + mgr.relinquishMastership(DEV_MASTER);
73 + assertNull("wrong master:", mgr.getMasterFor(DEV_OTHER));
74 + assertEquals("wrong role:", STANDBY, mgr.getLocalRole(DEV_MASTER));
75 +
76 + //not master, nothing should happen
77 + mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY);
78 + mgr.relinquishMastership(DEV_OTHER);
79 + assertNull("wrong role:", mgr.getMasterFor(DEV_OTHER));
80 +
81 + //provide NID_OTHER as backup and relinquish
82 + mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
83 + assertEquals("wrong master:", NID_LOCAL, mgr.getMasterFor(DEV_MASTER));
84 + mgr.setRole(NID_OTHER, DEV_MASTER, STANDBY);
85 + mgr.relinquishMastership(DEV_MASTER);
86 + assertEquals("wrong master:", NID_OTHER, mgr.getMasterFor(DEV_MASTER));
69 } 87 }
70 88
71 @Test 89 @Test
...@@ -95,7 +113,6 @@ public class MastershipManagerTest { ...@@ -95,7 +113,6 @@ public class MastershipManagerTest {
95 mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER); 113 mgr.setRole(NID_LOCAL, DEV_MASTER, MASTER);
96 mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY); 114 mgr.setRole(NID_LOCAL, DEV_OTHER, STANDBY);
97 assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size()); 115 assertEquals("should be one device:", 1, mgr.getDevicesOf(NID_LOCAL).size());
98 -
99 //hand both devices to NID_LOCAL 116 //hand both devices to NID_LOCAL
100 mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER); 117 mgr.setRole(NID_LOCAL, DEV_OTHER, MASTER);
101 assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size()); 118 assertEquals("should be two devices:", 2, mgr.getDevicesOf(NID_LOCAL).size());
......
...@@ -27,7 +27,7 @@ import org.onlab.onos.net.device.DeviceService; ...@@ -27,7 +27,7 @@ import org.onlab.onos.net.device.DeviceService;
27 import org.onlab.onos.net.device.PortDescription; 27 import org.onlab.onos.net.device.PortDescription;
28 import org.onlab.onos.net.provider.AbstractProvider; 28 import org.onlab.onos.net.provider.AbstractProvider;
29 import org.onlab.onos.net.provider.ProviderId; 29 import org.onlab.onos.net.provider.ProviderId;
30 -import org.onlab.onos.net.trivial.impl.SimpleDeviceStore; 30 +import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
31 31
32 import java.util.ArrayList; 32 import java.util.ArrayList;
33 import java.util.Iterator; 33 import java.util.Iterator;
......
...@@ -40,7 +40,7 @@ import org.onlab.onos.net.flow.criteria.Criterion; ...@@ -40,7 +40,7 @@ import org.onlab.onos.net.flow.criteria.Criterion;
40 import org.onlab.onos.net.flow.instructions.Instruction; 40 import org.onlab.onos.net.flow.instructions.Instruction;
41 import org.onlab.onos.net.provider.AbstractProvider; 41 import org.onlab.onos.net.provider.AbstractProvider;
42 import org.onlab.onos.net.provider.ProviderId; 42 import org.onlab.onos.net.provider.ProviderId;
43 -import org.onlab.onos.net.trivial.impl.SimpleFlowRuleStore; 43 +import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
44 44
45 import com.google.common.collect.Lists; 45 import com.google.common.collect.Lists;
46 import com.google.common.collect.Sets; 46 import com.google.common.collect.Sets;
......
...@@ -34,7 +34,7 @@ import org.onlab.onos.net.host.HostProviderService; ...@@ -34,7 +34,7 @@ import org.onlab.onos.net.host.HostProviderService;
34 import org.onlab.onos.net.host.PortAddresses; 34 import org.onlab.onos.net.host.PortAddresses;
35 import org.onlab.onos.net.provider.AbstractProvider; 35 import org.onlab.onos.net.provider.AbstractProvider;
36 import org.onlab.onos.net.provider.ProviderId; 36 import org.onlab.onos.net.provider.ProviderId;
37 -import org.onlab.onos.net.trivial.impl.SimpleHostStore; 37 +import org.onlab.onos.store.trivial.impl.SimpleHostStore;
38 import org.onlab.packet.IpPrefix; 38 import org.onlab.packet.IpPrefix;
39 import org.onlab.packet.MacAddress; 39 import org.onlab.packet.MacAddress;
40 import org.onlab.packet.VlanId; 40 import org.onlab.packet.VlanId;
......
...@@ -23,7 +23,7 @@ import org.onlab.onos.net.provider.AbstractProvider; ...@@ -23,7 +23,7 @@ import org.onlab.onos.net.provider.AbstractProvider;
23 import org.onlab.onos.net.provider.ProviderId; 23 import org.onlab.onos.net.provider.ProviderId;
24 import org.onlab.onos.event.impl.TestEventDispatcher; 24 import org.onlab.onos.event.impl.TestEventDispatcher;
25 import org.onlab.onos.net.device.impl.DeviceManager; 25 import org.onlab.onos.net.device.impl.DeviceManager;
26 -import org.onlab.onos.net.trivial.impl.SimpleLinkStore; 26 +import org.onlab.onos.store.trivial.impl.SimpleLinkStore;
27 27
28 import java.util.ArrayList; 28 import java.util.ArrayList;
29 import java.util.Iterator; 29 import java.util.Iterator;
......
...@@ -24,7 +24,7 @@ import org.onlab.onos.net.topology.TopologyProvider; ...@@ -24,7 +24,7 @@ import org.onlab.onos.net.topology.TopologyProvider;
24 import org.onlab.onos.net.topology.TopologyProviderRegistry; 24 import org.onlab.onos.net.topology.TopologyProviderRegistry;
25 import org.onlab.onos.net.topology.TopologyProviderService; 25 import org.onlab.onos.net.topology.TopologyProviderService;
26 import org.onlab.onos.net.topology.TopologyService; 26 import org.onlab.onos.net.topology.TopologyService;
27 -import org.onlab.onos.net.trivial.impl.SimpleTopologyStore; 27 +import org.onlab.onos.store.trivial.impl.SimpleTopologyStore;
28 28
29 import java.util.ArrayList; 29 import java.util.ArrayList;
30 import java.util.List; 30 import java.util.List;
......
1 +package org.onlab.onos.store.cluster.messaging;
2 +
3 +import static org.onlab.onos.store.cluster.messaging.MessageSubject.AE_ADVERTISEMENT;
4 +import java.util.Map;
5 +
6 +import org.onlab.onos.cluster.NodeId;
7 +import org.onlab.onos.store.Timestamp;
8 +
9 +import com.google.common.collect.ImmutableMap;
10 +
11 +/**
12 + * Anti-Entropy advertisement message.
13 + * <p>
14 + * Message to advertise the information this node holds.
15 + *
16 + * @param <ID> ID type
17 + */
18 +public class AntiEntropyAdvertisement<ID> extends ClusterMessage {
19 +
20 + private final NodeId sender;
21 + private final ImmutableMap<ID, Timestamp> advertisement;
22 +
23 + /**
24 + * Creates anti-entropy advertisement message.
25 + *
26 + * @param sender sender of this message
27 + * @param advertisement timestamp information of the data sender holds
28 + */
29 + public AntiEntropyAdvertisement(NodeId sender, Map<ID, Timestamp> advertisement) {
30 + super(AE_ADVERTISEMENT);
31 + this.sender = sender;
32 + this.advertisement = ImmutableMap.copyOf(advertisement);
33 + }
34 +
35 + public NodeId sender() {
36 + return sender;
37 + }
38 +
39 + public ImmutableMap<ID, Timestamp> advertisement() {
40 + return advertisement;
41 + }
42 +
43 + // Default constructor for serializer
44 + protected AntiEntropyAdvertisement() {
45 + super(AE_ADVERTISEMENT);
46 + this.sender = null;
47 + this.advertisement = null;
48 + }
49 +}
1 +package org.onlab.onos.store.cluster.messaging;
2 +
3 +import static org.onlab.onos.store.cluster.messaging.MessageSubject.AE_REPLY;
4 +
5 +import java.util.Map;
6 +import java.util.Set;
7 +
8 +import org.onlab.onos.cluster.NodeId;
9 +import org.onlab.onos.store.device.impl.VersionedValue;
10 +
11 +import com.google.common.collect.ImmutableMap;
12 +import com.google.common.collect.ImmutableSet;
13 +
14 +/**
15 + * Anti-Entropy reply message.
16 + * <p>
17 + * Message to send in reply to advertisement or another reply.
18 + * Suggest to the sender about the more up-to-date data this node has,
19 + * and request for more recent data that the receiver has.
20 + */
21 +public class AntiEntropyReply<ID, V extends VersionedValue<?>> extends ClusterMessage {
22 +
23 + private final NodeId sender;
24 + private final ImmutableMap<ID, V> suggestion;
25 + private final ImmutableSet<ID> request;
26 +
27 + /**
28 + * Creates a reply to anti-entropy message.
29 + *
30 + * @param sender sender of this message
31 + * @param suggestion collection of more recent values, sender had
32 + * @param request Collection of identifiers
33 + */
34 + public AntiEntropyReply(NodeId sender,
35 + Map<ID, V> suggestion,
36 + Set<ID> request) {
37 + super(AE_REPLY);
38 + this.sender = sender;
39 + this.suggestion = ImmutableMap.copyOf(suggestion);
40 + this.request = ImmutableSet.copyOf(request);
41 + }
42 +
43 + public NodeId sender() {
44 + return sender;
45 + }
46 +
47 + /**
48 + * Returns collection of values, which the recipient of this reply is likely
49 + * to be missing or has outdated version.
50 + *
51 + * @return
52 + */
53 + public ImmutableMap<ID, V> suggestion() {
54 + return suggestion;
55 + }
56 +
57 + /**
58 + * Returns collection of identifier to request.
59 + *
60 + * @return collection of identifier to request
61 + */
62 + public ImmutableSet<ID> request() {
63 + return request;
64 + }
65 +
66 + /**
67 + * Checks if reply contains any suggestion or request.
68 + *
69 + * @return true if nothing is suggested and requested
70 + */
71 + public boolean isEmpty() {
72 + return suggestion.isEmpty() && request.isEmpty();
73 + }
74 +
75 + // Default constructor for serializer
76 + protected AntiEntropyReply() {
77 + super(AE_REPLY);
78 + this.sender = null;
79 + this.suggestion = null;
80 + this.request = null;
81 + }
82 +}
...@@ -15,6 +15,12 @@ public enum MessageSubject { ...@@ -15,6 +15,12 @@ public enum MessageSubject {
15 LEAVING_MEMBER, 15 LEAVING_MEMBER,
16 16
17 /** Signifies a heart-beat message. */ 17 /** Signifies a heart-beat message. */
18 - ECHO 18 + ECHO,
19 +
20 + /** Anti-Entropy advertisement message. */
21 + AE_ADVERTISEMENT,
22 +
23 + /** Anti-Entropy reply message. */
24 + AE_REPLY,
19 25
20 } 26 }
......
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import java.util.Collection;
4 +import java.util.HashMap;
5 +import java.util.Map;
6 +
7 +import org.onlab.onos.cluster.NodeId;
8 +import org.onlab.onos.net.Device;
9 +import org.onlab.onos.net.DeviceId;
10 +import org.onlab.onos.store.Timestamp;
11 +import org.onlab.onos.store.cluster.messaging.AntiEntropyAdvertisement;
12 +
13 +// TODO DeviceID needs to be changed to something like (ProviderID, DeviceID)
14 +// TODO: Handle Port as part of these messages, or separate messages for Ports?
15 +
16 +public class DeviceAntiEntropyAdvertisement
17 + extends AntiEntropyAdvertisement<DeviceId> {
18 +
19 +
20 + public DeviceAntiEntropyAdvertisement(NodeId sender,
21 + Map<DeviceId, Timestamp> advertisement) {
22 + super(sender, advertisement);
23 + }
24 +
25 + // May need to add ProviderID, etc.
26 + public static DeviceAntiEntropyAdvertisement create(
27 + NodeId self,
28 + Collection<VersionedValue<Device>> localValues) {
29 +
30 + Map<DeviceId, Timestamp> ads = new HashMap<>(localValues.size());
31 + for (VersionedValue<Device> e : localValues) {
32 + ads.put(e.entity().id(), e.timestamp());
33 + }
34 + return new DeviceAntiEntropyAdvertisement(self, ads);
35 + }
36 +
37 + // For serializer
38 + protected DeviceAntiEntropyAdvertisement() {}
39 +}
1 +package org.onlab.onos.store.device.impl;
2 +
3 +import java.util.Collection;
4 +import java.util.HashMap;
5 +import java.util.HashSet;
6 +import java.util.Map;
7 +import java.util.Set;
8 +
9 +import org.onlab.onos.cluster.NodeId;
10 +import org.onlab.onos.net.Device;
11 +import org.onlab.onos.net.DeviceId;
12 +import org.onlab.onos.store.Timestamp;
13 +import org.onlab.onos.store.cluster.messaging.AntiEntropyReply;
14 +
15 +import com.google.common.collect.ImmutableMap;
16 +import com.google.common.collect.ImmutableSet;
17 +
18 +public class DeviceAntiEntropyReply
19 + extends AntiEntropyReply<DeviceId, VersionedValue<Device>> {
20 +
21 +
22 + public DeviceAntiEntropyReply(NodeId sender,
23 + Map<DeviceId, VersionedValue<Device>> suggestion,
24 + Set<DeviceId> request) {
25 + super(sender, suggestion, request);
26 + }
27 +
28 + /**
29 + * Creates a reply to Anti-Entropy advertisement.
30 + *
31 + * @param advertisement to respond to
32 + * @param self node identifier representing local node
33 + * @param localValues local values held on this node
34 + * @return reply message
35 + */
36 + public static DeviceAntiEntropyReply reply(
37 + DeviceAntiEntropyAdvertisement advertisement,
38 + NodeId self,
39 + Collection<VersionedValue<Device>> localValues
40 + ) {
41 +
42 + ImmutableMap<DeviceId, Timestamp> ads = advertisement.advertisement();
43 +
44 + ImmutableMap.Builder<DeviceId, VersionedValue<Device>>
45 + sug = ImmutableMap.builder();
46 +
47 + Set<DeviceId> req = new HashSet<>(ads.keySet());
48 +
49 + for (VersionedValue<Device> e : localValues) {
50 + final DeviceId id = e.entity().id();
51 + final Timestamp local = e.timestamp();
52 + final Timestamp theirs = ads.get(id);
53 + if (theirs == null) {
54 + // they don't have it, suggest
55 + sug.put(id, e);
56 + // don't need theirs
57 + req.remove(id);
58 + } else if (local.compareTo(theirs) < 0) {
59 + // they got older one, suggest
60 + sug.put(id, e);
61 + // don't need theirs
62 + req.remove(id);
63 + } else if (local.equals(theirs)) {
64 + // same, don't need theirs
65 + req.remove(id);
66 + }
67 + }
68 +
69 + return new DeviceAntiEntropyReply(self, sug.build(), req);
70 + }
71 +
72 + /**
73 + * Creates a reply to request for values held locally.
74 + *
75 + * @param requests message containing the request
76 + * @param self node identifier representing local node
77 + * @param localValues local valeds held on this node
78 + * @return reply message
79 + */
80 + public static DeviceAntiEntropyReply reply(
81 + DeviceAntiEntropyReply requests,
82 + NodeId self,
83 + Map<DeviceId, VersionedValue<Device>> localValues
84 + ) {
85 +
86 + Set<DeviceId> reqs = requests.request();
87 +
88 + Map<DeviceId, VersionedValue<Device>> requested = new HashMap<>(reqs.size());
89 + for (DeviceId id : reqs) {
90 + final VersionedValue<Device> value = localValues.get(id);
91 + if (value != null) {
92 + requested.put(id, value);
93 + }
94 + }
95 +
96 + Set<DeviceId> empty = ImmutableSet.of();
97 + return new DeviceAntiEntropyReply(self, requested, empty);
98 + }
99 +
100 + // For serializer
101 + protected DeviceAntiEntropyReply() {}
102 +}
...@@ -40,6 +40,7 @@ import java.util.Map; ...@@ -40,6 +40,7 @@ import java.util.Map;
40 import java.util.Objects; 40 import java.util.Objects;
41 import java.util.Set; 41 import java.util.Set;
42 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentHashMap;
43 +import java.util.concurrent.ConcurrentMap;
43 44
44 import static com.google.common.base.Preconditions.checkArgument; 45 import static com.google.common.base.Preconditions.checkArgument;
45 import static org.onlab.onos.net.device.DeviceEvent.Type.*; 46 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
...@@ -59,8 +60,8 @@ public class OnosDistributedDeviceStore ...@@ -59,8 +60,8 @@ public class OnosDistributedDeviceStore
59 60
60 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; 61 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
61 62
62 - private ConcurrentHashMap<DeviceId, VersionedValue<Device>> devices; 63 + private ConcurrentMap<DeviceId, VersionedValue<Device>> devices;
63 - private ConcurrentHashMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts; 64 + private ConcurrentMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
64 65
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected ClockService clockService; 67 protected ClockService clockService;
...@@ -191,7 +192,7 @@ public class OnosDistributedDeviceStore ...@@ -191,7 +192,7 @@ public class OnosDistributedDeviceStore
191 } 192 }
192 193
193 @Override 194 @Override
194 - public List<DeviceEvent> updatePorts(DeviceId deviceId, 195 + public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
195 List<PortDescription> portDescriptions) { 196 List<PortDescription> portDescriptions) {
196 List<DeviceEvent> events = new ArrayList<>(); 197 List<DeviceEvent> events = new ArrayList<>();
197 synchronized (this) { 198 synchronized (this) {
...@@ -295,7 +296,7 @@ public class OnosDistributedDeviceStore ...@@ -295,7 +296,7 @@ public class OnosDistributedDeviceStore
295 } 296 }
296 297
297 @Override 298 @Override
298 - public DeviceEvent updatePortStatus(DeviceId deviceId, 299 + public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
299 PortDescription portDescription) { 300 PortDescription portDescription) {
300 VersionedValue<Device> device = devices.get(deviceId); 301 VersionedValue<Device> device = devices.get(deviceId);
301 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 302 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
......
1 package org.onlab.onos.store.device.impl; 1 package org.onlab.onos.store.device.impl;
2 2
3 +import java.util.Objects;
4 +
3 import org.onlab.onos.store.Timestamp; 5 import org.onlab.onos.store.Timestamp;
4 6
5 /** 7 /**
...@@ -42,4 +44,35 @@ public class VersionedValue<T> { ...@@ -42,4 +44,35 @@ public class VersionedValue<T> {
42 public Timestamp timestamp() { 44 public Timestamp timestamp() {
43 return timestamp; 45 return timestamp;
44 } 46 }
47 +
48 +
49 + @Override
50 + public int hashCode() {
51 + return Objects.hash(entity, timestamp, isUp);
52 + }
53 +
54 + @Override
55 + public boolean equals(Object obj) {
56 + if (this == obj) {
57 + return true;
58 + }
59 + if (obj == null) {
60 + return false;
61 + }
62 + if (getClass() != obj.getClass()) {
63 + return false;
64 + }
65 + @SuppressWarnings("unchecked")
66 + VersionedValue<T> that = (VersionedValue<T>) obj;
67 + return Objects.equals(this.entity, that.entity) &&
68 + Objects.equals(this.timestamp, that.timestamp) &&
69 + Objects.equals(this.isUp, that.isUp);
70 + }
71 +
72 + // Default constructor for serializer
73 + protected VersionedValue() {
74 + this.entity = null;
75 + this.isUp = false;
76 + this.timestamp = null;
77 + }
45 } 78 }
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.store.cluster.impl; ...@@ -2,6 +2,7 @@ package org.onlab.onos.store.cluster.impl;
2 2
3 import org.junit.After; 3 import org.junit.After;
4 import org.junit.Before; 4 import org.junit.Before;
5 +import org.junit.Ignore;
5 import org.junit.Test; 6 import org.junit.Test;
6 import org.onlab.onos.cluster.DefaultControllerNode; 7 import org.onlab.onos.cluster.DefaultControllerNode;
7 import org.onlab.onos.cluster.NodeId; 8 import org.onlab.onos.cluster.NodeId;
...@@ -58,6 +59,7 @@ public class ClusterCommunicationManagerTest { ...@@ -58,6 +59,7 @@ public class ClusterCommunicationManagerTest {
58 ccm2.deactivate(); 59 ccm2.deactivate();
59 } 60 }
60 61
62 + @Ignore("FIXME: failing randomly?")
61 @Test 63 @Test
62 public void connect() throws Exception { 64 public void connect() throws Exception {
63 cnd1.latch = new CountDownLatch(1); 65 cnd1.latch = new CountDownLatch(1);
......
...@@ -123,6 +123,12 @@ implements MastershipStore { ...@@ -123,6 +123,12 @@ implements MastershipStore {
123 return null; 123 return null;
124 } 124 }
125 125
126 + @Override
127 + public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
128 + // TODO Auto-generated method stub
129 + return null;
130 + }
131 +
126 private class RemoteMasterShipEventHandler extends RemoteCacheEventHandler<DeviceId, NodeId> { 132 private class RemoteMasterShipEventHandler extends RemoteCacheEventHandler<DeviceId, NodeId> {
127 public RemoteMasterShipEventHandler(LoadingCache<DeviceId, Optional<NodeId>> cache) { 133 public RemoteMasterShipEventHandler(LoadingCache<DeviceId, Optional<NodeId>> cache) {
128 super(cache); 134 super(cache);
......
...@@ -221,7 +221,7 @@ public class DistributedDeviceStore ...@@ -221,7 +221,7 @@ public class DistributedDeviceStore
221 } 221 }
222 222
223 @Override 223 @Override
224 - public List<DeviceEvent> updatePorts(DeviceId deviceId, 224 + public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
225 List<PortDescription> portDescriptions) { 225 List<PortDescription> portDescriptions) {
226 List<DeviceEvent> events = new ArrayList<>(); 226 List<DeviceEvent> events = new ArrayList<>();
227 synchronized (this) { 227 synchronized (this) {
...@@ -319,7 +319,7 @@ public class DistributedDeviceStore ...@@ -319,7 +319,7 @@ public class DistributedDeviceStore
319 } 319 }
320 320
321 @Override 321 @Override
322 - public DeviceEvent updatePortStatus(DeviceId deviceId, 322 + public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
323 PortDescription portDescription) { 323 PortDescription portDescription) {
324 synchronized (this) { 324 synchronized (this) {
325 Device device = devices.getUnchecked(deviceId).orNull(); 325 Device device = devices.getUnchecked(deviceId).orNull();
......
...@@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableSet; ...@@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Multimap; 28 import com.google.common.collect.Multimap;
29 29
30 /** 30 /**
31 - * Manages inventory of flow rules using trivial in-memory implementation. 31 + * TEMPORARY: Manages inventory of flow rules using distributed store implementation.
32 */ 32 */
33 //FIXME: I LIE I AM NOT DISTRIBUTED 33 //FIXME: I LIE I AM NOT DISTRIBUTED
34 @Component(immediate = true) 34 @Component(immediate = true)
......
1 +/**
2 + * Implementation of flow store using Hazelcast distributed structures.
3 + */
4 +package org.onlab.onos.store.flow.impl;
...@@ -39,8 +39,8 @@ import com.google.common.collect.Multimap; ...@@ -39,8 +39,8 @@ import com.google.common.collect.Multimap;
39 import com.google.common.collect.Sets; 39 import com.google.common.collect.Sets;
40 40
41 /** 41 /**
42 - * Manages inventory of end-station hosts using trivial in-memory 42 + * TEMPORARY: Manages inventory of end-station hosts using distributed
43 - * implementation. 43 + * structures implementation.
44 */ 44 */
45 //FIXME: I LIE I AM NOT DISTRIBUTED 45 //FIXME: I LIE I AM NOT DISTRIBUTED
46 @Component(immediate = true) 46 @Component(immediate = true)
......
1 +/**
2 + * Implementation of host store using Hazelcast distributed structures.
3 + */
4 +package org.onlab.onos.store.host.impl;
...@@ -28,7 +28,7 @@ import org.onlab.onos.store.AbstractStore; ...@@ -28,7 +28,7 @@ import org.onlab.onos.store.AbstractStore;
28 import org.slf4j.Logger; 28 import org.slf4j.Logger;
29 29
30 /** 30 /**
31 - * Manages inventory of topology snapshots using trivial in-memory 31 + * TEMPORARY: Manages inventory of topology snapshots using distributed
32 * structures implementation. 32 * structures implementation.
33 */ 33 */
34 //FIXME: I LIE I AM NOT DISTRIBUTED 34 //FIXME: I LIE I AM NOT DISTRIBUTED
......
1 +/**
2 + * Implementation of topology store using Hazelcast distributed structures.
3 + */
4 +package org.onlab.onos.store.topology.impl;
...@@ -201,7 +201,7 @@ public class DistributedDeviceStoreTest { ...@@ -201,7 +201,7 @@ public class DistributedDeviceStoreTest {
201 new DefaultPortDescription(P2, true) 201 new DefaultPortDescription(P2, true)
202 ); 202 );
203 203
204 - List<DeviceEvent> events = deviceStore.updatePorts(DID1, pds); 204 + List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
205 205
206 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2); 206 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
207 for (DeviceEvent event : events) { 207 for (DeviceEvent event : events) {
...@@ -220,7 +220,7 @@ public class DistributedDeviceStoreTest { ...@@ -220,7 +220,7 @@ public class DistributedDeviceStoreTest {
220 new DefaultPortDescription(P3, true) 220 new DefaultPortDescription(P3, true)
221 ); 221 );
222 222
223 - events = deviceStore.updatePorts(DID1, pds2); 223 + events = deviceStore.updatePorts(PID, DID1, pds2);
224 assertFalse("event should be triggered", events.isEmpty()); 224 assertFalse("event should be triggered", events.isEmpty());
225 for (DeviceEvent event : events) { 225 for (DeviceEvent event : events) {
226 PortNumber num = event.port().number(); 226 PortNumber num = event.port().number();
...@@ -243,7 +243,7 @@ public class DistributedDeviceStoreTest { ...@@ -243,7 +243,7 @@ public class DistributedDeviceStoreTest {
243 new DefaultPortDescription(P1, false), 243 new DefaultPortDescription(P1, false),
244 new DefaultPortDescription(P2, true) 244 new DefaultPortDescription(P2, true)
245 ); 245 );
246 - events = deviceStore.updatePorts(DID1, pds3); 246 + events = deviceStore.updatePorts(PID, DID1, pds3);
247 assertFalse("event should be triggered", events.isEmpty()); 247 assertFalse("event should be triggered", events.isEmpty());
248 for (DeviceEvent event : events) { 248 for (DeviceEvent event : events) {
249 PortNumber num = event.port().number(); 249 PortNumber num = event.port().number();
...@@ -268,9 +268,9 @@ public class DistributedDeviceStoreTest { ...@@ -268,9 +268,9 @@ public class DistributedDeviceStoreTest {
268 List<PortDescription> pds = Arrays.<PortDescription>asList( 268 List<PortDescription> pds = Arrays.<PortDescription>asList(
269 new DefaultPortDescription(P1, true) 269 new DefaultPortDescription(P1, true)
270 ); 270 );
271 - deviceStore.updatePorts(DID1, pds); 271 + deviceStore.updatePorts(PID, DID1, pds);
272 272
273 - DeviceEvent event = deviceStore.updatePortStatus(DID1, 273 + DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
274 new DefaultPortDescription(P1, false)); 274 new DefaultPortDescription(P1, false));
275 assertEquals(PORT_UPDATED, event.type()); 275 assertEquals(PORT_UPDATED, event.type());
276 assertDevice(DID1, SW1, event.subject()); 276 assertDevice(DID1, SW1, event.subject());
...@@ -286,7 +286,7 @@ public class DistributedDeviceStoreTest { ...@@ -286,7 +286,7 @@ public class DistributedDeviceStoreTest {
286 new DefaultPortDescription(P1, true), 286 new DefaultPortDescription(P1, true),
287 new DefaultPortDescription(P2, true) 287 new DefaultPortDescription(P2, true)
288 ); 288 );
289 - deviceStore.updatePorts(DID1, pds); 289 + deviceStore.updatePorts(PID, DID1, pds);
290 290
291 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2); 291 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
292 List<Port> ports = deviceStore.getPorts(DID1); 292 List<Port> ports = deviceStore.getPorts(DID1);
...@@ -309,7 +309,7 @@ public class DistributedDeviceStoreTest { ...@@ -309,7 +309,7 @@ public class DistributedDeviceStoreTest {
309 new DefaultPortDescription(P1, true), 309 new DefaultPortDescription(P1, true),
310 new DefaultPortDescription(P2, false) 310 new DefaultPortDescription(P2, false)
311 ); 311 );
312 - deviceStore.updatePorts(DID1, pds); 312 + deviceStore.updatePorts(PID, DID1, pds);
313 313
314 Port port1 = deviceStore.getPort(DID1, P1); 314 Port port1 = deviceStore.getPort(DID1, P1);
315 assertEquals(P1, port1.number()); 315 assertEquals(P1, port1.number());
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import java.util.Collections;
4 +import java.util.HashMap;
5 +import java.util.Map;
6 +
7 +import org.onlab.util.KryoPool.FamilySerializer;
8 +
9 +import com.esotericsoftware.kryo.Kryo;
10 +import com.esotericsoftware.kryo.io.Input;
11 +import com.esotericsoftware.kryo.io.Output;
12 +import com.esotericsoftware.kryo.serializers.MapSerializer;
13 +import com.google.common.collect.ImmutableMap;
14 +
15 +/**
16 +* Kryo Serializer for {@link ImmutableMap}.
17 +*/
18 +public class ImmutableMapSerializer extends FamilySerializer<ImmutableMap<?, ?>> {
19 +
20 + private final MapSerializer mapSerializer = new MapSerializer();
21 +
22 + public ImmutableMapSerializer() {
23 + // non-null, immutable
24 + super(false, true);
25 + }
26 +
27 + @Override
28 + public void write(Kryo kryo, Output output, ImmutableMap<?, ?> object) {
29 + // wrapping with unmodifiableMap proxy
30 + // to avoid Kryo from writing only the reference marker of this instance,
31 + // which will be embedded right before this method call.
32 + kryo.writeObject(output, Collections.unmodifiableMap(object), mapSerializer);
33 + }
34 +
35 + @Override
36 + public ImmutableMap<?, ?> read(Kryo kryo, Input input,
37 + Class<ImmutableMap<?, ?>> type) {
38 + Map<?, ?> map = kryo.readObject(input, HashMap.class, mapSerializer);
39 + return ImmutableMap.copyOf(map);
40 + }
41 +
42 + @Override
43 + public void registerFamilies(Kryo kryo) {
44 + kryo.register(ImmutableMap.of().getClass(), this);
45 + kryo.register(ImmutableMap.of(1, 2).getClass(), this);
46 + kryo.register(ImmutableMap.of(1, 2, 3, 4).getClass(), this);
47 + // TODO register required ImmutableMap variants
48 + }
49 +}
1 +package org.onlab.onos.store.serializers;
2 +
3 +import java.util.ArrayList;
4 +import java.util.List;
5 +
6 +import org.onlab.util.KryoPool.FamilySerializer;
7 +
8 +import com.esotericsoftware.kryo.Kryo;
9 +import com.esotericsoftware.kryo.io.Input;
10 +import com.esotericsoftware.kryo.io.Output;
11 +import com.esotericsoftware.kryo.serializers.CollectionSerializer;
12 +import com.google.common.collect.ImmutableSet;
13 +
14 +/**
15 +* Kryo Serializer for {@link ImmutableSet}.
16 +*/
17 +public class ImmutableSetSerializer extends FamilySerializer<ImmutableSet<?>> {
18 +
19 + private final CollectionSerializer serializer = new CollectionSerializer();
20 +
21 + public ImmutableSetSerializer() {
22 + // non-null, immutable
23 + super(false, true);
24 + }
25 +
26 + @Override
27 + public void write(Kryo kryo, Output output, ImmutableSet<?> object) {
28 + kryo.writeObject(output, object.asList(), serializer);
29 + }
30 +
31 + @Override
32 + public ImmutableSet<?> read(Kryo kryo, Input input,
33 + Class<ImmutableSet<?>> type) {
34 + List<?> elms = kryo.readObject(input, ArrayList.class, serializer);
35 + return ImmutableSet.copyOf(elms);
36 + }
37 +
38 + @Override
39 + public void registerFamilies(Kryo kryo) {
40 + kryo.register(ImmutableSet.of().getClass(), this);
41 + kryo.register(ImmutableSet.of(1).getClass(), this);
42 + kryo.register(ImmutableSet.of(1, 2).getClass(), this);
43 + // TODO register required ImmutableSet variants
44 + }
45 +}
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 import java.net.URI; 3 import java.net.URI;
4 +import java.nio.ByteBuffer;
4 import java.util.ArrayList; 5 import java.util.ArrayList;
5 import java.util.HashMap; 6 import java.util.HashMap;
6 7
...@@ -100,4 +101,14 @@ public class KryoSerializationManager implements KryoSerializationService { ...@@ -100,4 +101,14 @@ public class KryoSerializationManager implements KryoSerializationService {
100 return serializerPool.deserialize(bytes); 101 return serializerPool.deserialize(bytes);
101 } 102 }
102 103
104 + @Override
105 + public void serialize(Object obj, ByteBuffer buffer) {
106 + serializerPool.serialize(obj, buffer);
107 + }
108 +
109 + @Override
110 + public <T> T deserialize(ByteBuffer buffer) {
111 + return serializerPool.deserialize(buffer);
112 + }
113 +
103 } 114 }
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 +import java.nio.ByteBuffer;
4 +
3 // TODO: To be replaced with SerializationService from IOLoop activity 5 // TODO: To be replaced with SerializationService from IOLoop activity
4 /** 6 /**
5 * Service to serialize Objects into byte array. 7 * Service to serialize Objects into byte array.
...@@ -16,6 +18,15 @@ public interface KryoSerializationService { ...@@ -16,6 +18,15 @@ public interface KryoSerializationService {
16 public byte[] serialize(final Object obj); 18 public byte[] serialize(final Object obj);
17 19
18 /** 20 /**
21 + * Serializes the specified object into bytes using one of the
22 + * pre-registered serializers.
23 + *
24 + * @param obj object to be serialized
25 + * @param buffer to write serialized bytes
26 + */
27 + public void serialize(final Object obj, ByteBuffer buffer);
28 +
29 + /**
19 * Deserializes the specified bytes into an object using one of the 30 * Deserializes the specified bytes into an object using one of the
20 * pre-registered serializers. 31 * pre-registered serializers.
21 * 32 *
...@@ -24,4 +35,12 @@ public interface KryoSerializationService { ...@@ -24,4 +35,12 @@ public interface KryoSerializationService {
24 */ 35 */
25 public <T> T deserialize(final byte[] bytes); 36 public <T> T deserialize(final byte[] bytes);
26 37
38 + /**
39 + * Deserializes the specified bytes into an object using one of the
40 + * pre-registered serializers.
41 + *
42 + * @param buffer bytes to be deserialized
43 + * @return deserialized object
44 + */
45 + public <T> T deserialize(final ByteBuffer buffer);
27 } 46 }
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.net.MastershipRole;
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 org.onlab.onos.net.MastershipRole}.
12 + */
13 +public class MastershipRoleSerializer extends Serializer<MastershipRole> {
14 +
15 + @Override
16 + public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) {
17 + final String role = kryo.readObject(input, String.class);
18 + return MastershipRole.valueOf(role);
19 + }
20 +
21 + @Override
22 + public void write(Kryo kryo, Output output, MastershipRole object) {
23 + kryo.writeObject(output, object.toString());
24 + }
25 +
26 +}
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.cluster.MastershipTerm;
4 +import org.onlab.onos.cluster.NodeId;
5 +
6 +import com.esotericsoftware.kryo.Kryo;
7 +import com.esotericsoftware.kryo.Serializer;
8 +import com.esotericsoftware.kryo.io.Input;
9 +import com.esotericsoftware.kryo.io.Output;
10 +
11 +/**
12 + * Kryo Serializer for {@link org.onlab.onos.cluster.MastershipTerm}.
13 + */
14 +public class MastershipTermSerializer extends Serializer<MastershipTerm> {
15 +
16 + @Override
17 + public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
18 + final NodeId node = new NodeId(kryo.readObject(input, String.class));
19 + final int term = input.readInt();
20 + return MastershipTerm.of(node, term);
21 + }
22 +
23 + @Override
24 + public void write(Kryo kryo, Output output, MastershipTerm object) {
25 + output.writeString(object.master().toString());
26 + output.writeInt(object.termNumber());
27 + }
28 +
29 +}
1 +package org.onlab.onos.store.serializers;
2 +
3 +import static org.onlab.onos.net.DeviceId.deviceId;
4 +import static org.onlab.onos.net.PortNumber.portNumber;
5 +
6 +import java.net.URI;
7 +import java.nio.ByteBuffer;
8 +import java.util.ArrayList;
9 +import java.util.HashMap;
10 +
11 +import org.junit.After;
12 +import org.junit.Before;
13 +import org.junit.BeforeClass;
14 +import org.junit.Test;
15 +import org.onlab.onos.cluster.MastershipTerm;
16 +import org.onlab.onos.cluster.NodeId;
17 +import org.onlab.onos.net.ConnectPoint;
18 +import org.onlab.onos.net.DefaultDevice;
19 +import org.onlab.onos.net.DefaultLink;
20 +import org.onlab.onos.net.DefaultPort;
21 +import org.onlab.onos.net.Device;
22 +import org.onlab.onos.net.DeviceId;
23 +import org.onlab.onos.net.Link;
24 +import org.onlab.onos.net.LinkKey;
25 +import org.onlab.onos.net.MastershipRole;
26 +import org.onlab.onos.net.PortNumber;
27 +import org.onlab.onos.net.provider.ProviderId;
28 +import org.onlab.packet.IpPrefix;
29 +import org.onlab.util.KryoPool;
30 +
31 +import com.google.common.collect.ImmutableMap;
32 +import com.google.common.collect.ImmutableSet;
33 +import com.google.common.testing.EqualsTester;
34 +
35 +import de.javakaffee.kryoserializers.URISerializer;
36 +
37 +public class KryoSerializerTests {
38 + private static final ProviderId PID = new ProviderId("of", "foo");
39 + private static final DeviceId DID1 = deviceId("of:foo");
40 + private static final DeviceId DID2 = deviceId("of:bar");
41 + private static final PortNumber P1 = portNumber(1);
42 + private static final PortNumber P2 = portNumber(2);
43 + private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
44 + private static final ConnectPoint CP2 = new ConnectPoint(DID2, P2);
45 + private static final String MFR = "whitebox";
46 + private static final String HW = "1.1.x";
47 + private static final String SW1 = "3.8.1";
48 + private static final String SW2 = "3.9.5";
49 + private static final String SN = "43311-12345";
50 + private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN);
51 +
52 + private static KryoPool kryos;
53 +
54 + @BeforeClass
55 + public static void setUpBeforeClass() throws Exception {
56 + kryos = KryoPool.newBuilder()
57 + .register(
58 + ArrayList.class,
59 + HashMap.class
60 + )
61 + .register(
62 + Device.Type.class,
63 + Link.Type.class
64 +
65 +// ControllerNode.State.class,
66 +// DefaultControllerNode.class,
67 +// MastershipRole.class,
68 +// Port.class,
69 +// Element.class,
70 + )
71 + .register(ConnectPoint.class, new ConnectPointSerializer())
72 + .register(DefaultLink.class, new DefaultLinkSerializer())
73 + .register(DefaultPort.class, new DefaultPortSerializer())
74 + .register(DeviceId.class, new DeviceIdSerializer())
75 + .register(ImmutableMap.class, new ImmutableMapSerializer())
76 + .register(ImmutableSet.class, new ImmutableSetSerializer())
77 + .register(IpPrefix.class, new IpPrefixSerializer())
78 + .register(LinkKey.class, new LinkKeySerializer())
79 + .register(NodeId.class, new NodeIdSerializer())
80 + .register(PortNumber.class, new PortNumberSerializer())
81 + .register(ProviderId.class, new ProviderIdSerializer())
82 +
83 + .register(DefaultDevice.class)
84 +
85 + .register(URI.class, new URISerializer())
86 +
87 + .register(MastershipRole.class, new MastershipRoleSerializer())
88 + .register(MastershipTerm.class, new MastershipTermSerializer())
89 + .build();
90 + }
91 +
92 + @Before
93 + public void setUp() throws Exception {
94 + }
95 +
96 + @After
97 + public void tearDown() throws Exception {
98 + // removing Kryo instance to use fresh Kryo on each tests
99 + kryos.getKryo();
100 + }
101 +
102 + private static <T> void testSerialized(T original) {
103 + ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
104 + kryos.serialize(original, buffer);
105 + buffer.flip();
106 + T copy = kryos.deserialize(buffer);
107 +
108 + new EqualsTester()
109 + .addEqualityGroup(original, copy)
110 + .testEquals();
111 + }
112 +
113 +
114 + @Test
115 + public final void test() {
116 + testSerialized(new ConnectPoint(DID1, P1));
117 + testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT));
118 + testSerialized(new DefaultPort(DEV1, P1, true));
119 + testSerialized(DID1);
120 + testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1));
121 + testSerialized(ImmutableMap.of(DID1, DEV1));
122 + testSerialized(ImmutableMap.of());
123 + testSerialized(ImmutableSet.of(DID1, DID2));
124 + testSerialized(ImmutableSet.of(DID1));
125 + testSerialized(ImmutableSet.of());
126 + testSerialized(IpPrefix.valueOf("192.168.0.1/24"));
127 + testSerialized(new LinkKey(CP1, CP2));
128 + testSerialized(new NodeId("SomeNodeIdentifier"));
129 + testSerialized(P1);
130 + testSerialized(PID);
131 + }
132 +
133 +}
...@@ -25,6 +25,10 @@ ...@@ -25,6 +25,10 @@
25 <groupId>org.apache.felix</groupId> 25 <groupId>org.apache.felix</groupId>
26 <artifactId>org.apache.felix.scr.annotations</artifactId> 26 <artifactId>org.apache.felix.scr.annotations</artifactId>
27 </dependency> 27 </dependency>
28 + <dependency>
29 + <groupId>org.apache.commons</groupId>
30 + <artifactId>commons-lang3</artifactId>
31 + </dependency>
28 </dependencies> 32 </dependencies>
29 33
30 <build> 34 <build>
......
1 -package org.onlab.onos.net.trivial.impl;
2 -
3 -import com.google.common.collect.FluentIterable;
4 -import com.google.common.collect.ImmutableList;
5 -
6 -import org.apache.felix.scr.annotations.Activate;
7 -import org.apache.felix.scr.annotations.Component;
8 -import org.apache.felix.scr.annotations.Deactivate;
9 -import org.apache.felix.scr.annotations.Service;
10 -import org.onlab.onos.net.DefaultDevice;
11 -import org.onlab.onos.net.DefaultPort;
12 -import org.onlab.onos.net.Device;
13 -import org.onlab.onos.net.DeviceId;
14 -import org.onlab.onos.net.Port;
15 -import org.onlab.onos.net.PortNumber;
16 -import org.onlab.onos.net.device.DeviceDescription;
17 -import org.onlab.onos.net.device.DeviceEvent;
18 -import org.onlab.onos.net.device.DeviceStore;
19 -import org.onlab.onos.net.device.DeviceStoreDelegate;
20 -import org.onlab.onos.net.device.PortDescription;
21 -import org.onlab.onos.net.provider.ProviderId;
22 -import org.onlab.onos.store.AbstractStore;
23 -import org.slf4j.Logger;
24 -
25 -import java.util.ArrayList;
26 -import java.util.Collections;
27 -import java.util.HashMap;
28 -import java.util.HashSet;
29 -import java.util.Iterator;
30 -import java.util.List;
31 -import java.util.Map;
32 -import java.util.Objects;
33 -import java.util.Set;
34 -import java.util.concurrent.ConcurrentHashMap;
35 -
36 -import static com.google.common.base.Preconditions.checkArgument;
37 -import static com.google.common.base.Predicates.notNull;
38 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
39 -import static org.slf4j.LoggerFactory.getLogger;
40 -
41 -/**
42 - * Manages inventory of infrastructure devices using trivial in-memory
43 - * structures implementation.
44 - */
45 -@Component(immediate = true)
46 -@Service
47 -public class SimpleDeviceStore
48 - extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
49 - implements DeviceStore {
50 -
51 - private final Logger log = getLogger(getClass());
52 -
53 - public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
54 -
55 - private final Map<DeviceId, DefaultDevice> devices = new ConcurrentHashMap<>();
56 - private final Set<DeviceId> availableDevices = new HashSet<>();
57 - private final Map<DeviceId, Map<PortNumber, Port>> devicePorts = new HashMap<>();
58 -
59 - @Activate
60 - public void activate() {
61 - log.info("Started");
62 - }
63 -
64 - @Deactivate
65 - public void deactivate() {
66 - log.info("Stopped");
67 - }
68 -
69 - @Override
70 - public int getDeviceCount() {
71 - return devices.size();
72 - }
73 -
74 - @Override
75 - public Iterable<Device> getDevices() {
76 - return Collections.unmodifiableSet(new HashSet<Device>(devices.values()));
77 - }
78 -
79 - @Override
80 - public Device getDevice(DeviceId deviceId) {
81 - return devices.get(deviceId);
82 - }
83 -
84 - @Override
85 - public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
86 - DeviceDescription deviceDescription) {
87 - DefaultDevice device = devices.get(deviceId);
88 - if (device == null) {
89 - return createDevice(providerId, deviceId, deviceDescription);
90 - }
91 - return updateDevice(providerId, device, deviceDescription);
92 - }
93 -
94 - // Creates the device and returns the appropriate event if necessary.
95 - private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
96 - DeviceDescription desc) {
97 - DefaultDevice device = new DefaultDevice(providerId, deviceId, desc.type(),
98 - desc.manufacturer(),
99 - desc.hwVersion(), desc.swVersion(),
100 - desc.serialNumber());
101 - synchronized (this) {
102 - devices.put(deviceId, device);
103 - availableDevices.add(deviceId);
104 - }
105 - return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null);
106 - }
107 -
108 - // Updates the device and returns the appropriate event if necessary.
109 - private DeviceEvent updateDevice(ProviderId providerId, DefaultDevice device,
110 - DeviceDescription desc) {
111 - // We allow only certain attributes to trigger update
112 - if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
113 - !Objects.equals(device.swVersion(), desc.swVersion())) {
114 - DefaultDevice updated = new DefaultDevice(providerId, device.id(),
115 - desc.type(),
116 - desc.manufacturer(),
117 - desc.hwVersion(),
118 - desc.swVersion(),
119 - desc.serialNumber());
120 - synchronized (this) {
121 - devices.put(device.id(), updated);
122 - availableDevices.add(device.id());
123 - }
124 - return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
125 - }
126 -
127 - // Otherwise merely attempt to change availability
128 - synchronized (this) {
129 - boolean added = availableDevices.add(device.id());
130 - return !added ? null :
131 - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
132 - }
133 - }
134 -
135 - @Override
136 - public DeviceEvent markOffline(DeviceId deviceId) {
137 - synchronized (this) {
138 - Device device = devices.get(deviceId);
139 - boolean removed = device != null && availableDevices.remove(deviceId);
140 - return !removed ? null :
141 - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
142 - }
143 - }
144 -
145 - @Override
146 - public List<DeviceEvent> updatePorts(DeviceId deviceId,
147 - List<PortDescription> portDescriptions) {
148 - List<DeviceEvent> events = new ArrayList<>();
149 - synchronized (this) {
150 - Device device = devices.get(deviceId);
151 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
152 - Map<PortNumber, Port> ports = getPortMap(deviceId);
153 -
154 - // Add new ports
155 - Set<PortNumber> processed = new HashSet<>();
156 - for (PortDescription portDescription : portDescriptions) {
157 - Port port = ports.get(portDescription.portNumber());
158 - events.add(port == null ?
159 - createPort(device, portDescription, ports) :
160 - updatePort(device, port, portDescription, ports));
161 - processed.add(portDescription.portNumber());
162 - }
163 -
164 - events.addAll(pruneOldPorts(device, ports, processed));
165 - }
166 - return FluentIterable.from(events).filter(notNull()).toList();
167 - }
168 -
169 - // Creates a new port based on the port description adds it to the map and
170 - // Returns corresponding event.
171 - private DeviceEvent createPort(Device device, PortDescription portDescription,
172 - Map<PortNumber, Port> ports) {
173 - DefaultPort port = new DefaultPort(device, portDescription.portNumber(),
174 - portDescription.isEnabled());
175 - ports.put(port.number(), port);
176 - return new DeviceEvent(PORT_ADDED, device, port);
177 - }
178 -
179 - // CHecks if the specified port requires update and if so, it replaces the
180 - // existing entry in the map and returns corresponding event.
181 - private DeviceEvent updatePort(Device device, Port port,
182 - PortDescription portDescription,
183 - Map<PortNumber, Port> ports) {
184 - if (port.isEnabled() != portDescription.isEnabled()) {
185 - DefaultPort updatedPort =
186 - new DefaultPort(device, portDescription.portNumber(),
187 - portDescription.isEnabled());
188 - ports.put(port.number(), updatedPort);
189 - return new DeviceEvent(PORT_UPDATED, device, updatedPort);
190 - }
191 - return null;
192 - }
193 -
194 - // Prunes the specified list of ports based on which ports are in the
195 - // processed list and returns list of corresponding events.
196 - private List<DeviceEvent> pruneOldPorts(Device device,
197 - Map<PortNumber, Port> ports,
198 - Set<PortNumber> processed) {
199 - List<DeviceEvent> events = new ArrayList<>();
200 - Iterator<PortNumber> iterator = ports.keySet().iterator();
201 - while (iterator.hasNext()) {
202 - PortNumber portNumber = iterator.next();
203 - if (!processed.contains(portNumber)) {
204 - events.add(new DeviceEvent(PORT_REMOVED, device,
205 - ports.get(portNumber)));
206 - iterator.remove();
207 - }
208 - }
209 - return events;
210 - }
211 -
212 - // Gets the map of ports for the specified device; if one does not already
213 - // exist, it creates and registers a new one.
214 - private Map<PortNumber, Port> getPortMap(DeviceId deviceId) {
215 - Map<PortNumber, Port> ports = devicePorts.get(deviceId);
216 - if (ports == null) {
217 - ports = new HashMap<>();
218 - devicePorts.put(deviceId, ports);
219 - }
220 - return ports;
221 - }
222 -
223 - @Override
224 - public DeviceEvent updatePortStatus(DeviceId deviceId,
225 - PortDescription portDescription) {
226 - synchronized (this) {
227 - Device device = devices.get(deviceId);
228 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
229 - Map<PortNumber, Port> ports = getPortMap(deviceId);
230 - Port port = ports.get(portDescription.portNumber());
231 - return updatePort(device, port, portDescription, ports);
232 - }
233 - }
234 -
235 - @Override
236 - public List<Port> getPorts(DeviceId deviceId) {
237 - Map<PortNumber, Port> ports = devicePorts.get(deviceId);
238 - return ports == null ? new ArrayList<Port>() : ImmutableList.copyOf(ports.values());
239 - }
240 -
241 - @Override
242 - public Port getPort(DeviceId deviceId, PortNumber portNumber) {
243 - Map<PortNumber, Port> ports = devicePorts.get(deviceId);
244 - return ports == null ? null : ports.get(portNumber);
245 - }
246 -
247 - @Override
248 - public boolean isAvailable(DeviceId deviceId) {
249 - return availableDevices.contains(deviceId);
250 - }
251 -
252 - @Override
253 - public DeviceEvent removeDevice(DeviceId deviceId) {
254 - synchronized (this) {
255 - Device device = devices.remove(deviceId);
256 - return device == null ? null :
257 - new DeviceEvent(DEVICE_REMOVED, device, null);
258 - }
259 - }
260 -}
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import com.google.common.collect.ImmutableMap; 3 import com.google.common.collect.ImmutableMap;
4 import com.google.common.collect.ImmutableSet; 4 import com.google.common.collect.ImmutableSet;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import org.onlab.graph.AdjacencyListsGraph; 3 import org.onlab.graph.AdjacencyListsGraph;
4 import org.onlab.onos.net.topology.TopologyEdge; 4 import org.onlab.onos.net.topology.TopologyEdge;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import org.apache.felix.scr.annotations.Component; 3 import org.apache.felix.scr.annotations.Component;
4 import org.apache.felix.scr.annotations.Service; 4 import org.apache.felix.scr.annotations.Service;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
4 4
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import com.google.common.collect.ImmutableSet; 3 import com.google.common.collect.ImmutableSet;
4 import org.apache.felix.scr.annotations.Activate; 4 import org.apache.felix.scr.annotations.Activate;
......
1 +package org.onlab.onos.store.trivial.impl;
2 +
3 +import com.google.common.collect.FluentIterable;
4 +import com.google.common.collect.ImmutableList;
5 +
6 +import org.apache.commons.lang3.concurrent.ConcurrentException;
7 +import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
8 +import org.apache.felix.scr.annotations.Activate;
9 +import org.apache.felix.scr.annotations.Component;
10 +import org.apache.felix.scr.annotations.Deactivate;
11 +import org.apache.felix.scr.annotations.Service;
12 +import org.onlab.onos.net.DefaultDevice;
13 +import org.onlab.onos.net.DefaultPort;
14 +import org.onlab.onos.net.Device;
15 +import org.onlab.onos.net.Device.Type;
16 +import org.onlab.onos.net.DeviceId;
17 +import org.onlab.onos.net.Port;
18 +import org.onlab.onos.net.PortNumber;
19 +import org.onlab.onos.net.device.DeviceDescription;
20 +import org.onlab.onos.net.device.DeviceEvent;
21 +import org.onlab.onos.net.device.DeviceStore;
22 +import org.onlab.onos.net.device.DeviceStoreDelegate;
23 +import org.onlab.onos.net.device.PortDescription;
24 +import org.onlab.onos.net.provider.ProviderId;
25 +import org.onlab.onos.store.AbstractStore;
26 +import org.slf4j.Logger;
27 +
28 +import java.util.ArrayList;
29 +import java.util.Collection;
30 +import java.util.Collections;
31 +import java.util.HashSet;
32 +import java.util.Iterator;
33 +import java.util.List;
34 +import java.util.Map;
35 +import java.util.Map.Entry;
36 +import java.util.Objects;
37 +import java.util.Set;
38 +import java.util.concurrent.ConcurrentHashMap;
39 +import java.util.concurrent.ConcurrentMap;
40 +import java.util.concurrent.atomic.AtomicReference;
41 +
42 +import static com.google.common.base.Preconditions.checkArgument;
43 +import static com.google.common.base.Preconditions.checkNotNull;
44 +import static com.google.common.base.Predicates.notNull;
45 +import static org.onlab.onos.net.device.DeviceEvent.Type.*;
46 +import static org.slf4j.LoggerFactory.getLogger;
47 +import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
48 +
49 +// TODO: synchronization should be done in more fine-grained manner.
50 +/**
51 + * Manages inventory of infrastructure devices using trivial in-memory
52 + * structures implementation.
53 + */
54 +@Component(immediate = true)
55 +@Service
56 +public class SimpleDeviceStore
57 + extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
58 + implements DeviceStore {
59 +
60 + private final Logger log = getLogger(getClass());
61 +
62 + public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
63 +
64 + // collection of Description given from various providers
65 + private final ConcurrentMap<DeviceId,
66 + ConcurrentMap<ProviderId, DeviceDescriptions>>
67 + deviceDescs = new ConcurrentHashMap<>();
68 +
69 + // cache of Device and Ports generated by compositing descriptions from providers
70 + private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
71 + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
72 +
73 + // available(=UP) devices
74 + private final Set<DeviceId> availableDevices = new HashSet<>();
75 +
76 +
77 + @Activate
78 + public void activate() {
79 + log.info("Started");
80 + }
81 +
82 + @Deactivate
83 + public void deactivate() {
84 + log.info("Stopped");
85 + }
86 +
87 + @Override
88 + public int getDeviceCount() {
89 + return devices.size();
90 + }
91 +
92 + @Override
93 + public Iterable<Device> getDevices() {
94 + return Collections.unmodifiableCollection(devices.values());
95 + }
96 +
97 + @Override
98 + public Device getDevice(DeviceId deviceId) {
99 + return devices.get(deviceId);
100 + }
101 +
102 + @Override
103 + public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
104 + DeviceDescription deviceDescription) {
105 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
106 + = createIfAbsentUnchecked(deviceDescs, deviceId,
107 + new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
108 +
109 + Device oldDevice = devices.get(deviceId);
110 +
111 + DeviceDescriptions descs
112 + = createIfAbsentUnchecked(providerDescs, providerId,
113 + new InitDeviceDescs(deviceDescription));
114 +
115 + descs.putDeviceDesc(deviceDescription);
116 +
117 + Device newDevice = composeDevice(deviceId, providerDescs);
118 +
119 + if (oldDevice == null) {
120 + // ADD
121 + return createDevice(providerId, newDevice);
122 + } else {
123 + // UPDATE or ignore (no change or stale)
124 + return updateDevice(providerId, oldDevice, newDevice);
125 + }
126 + }
127 +
128 + // Creates the device and returns the appropriate event if necessary.
129 + private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
130 +
131 + // update composed device cache
132 + synchronized (this) {
133 + devices.putIfAbsent(newDevice.id(), newDevice);
134 + if (!providerId.isAncillary()) {
135 + availableDevices.add(newDevice.id());
136 + }
137 + }
138 +
139 + return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
140 + }
141 +
142 + // Updates the device and returns the appropriate event if necessary.
143 + private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
144 +
145 + // We allow only certain attributes to trigger update
146 + if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
147 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion())) {
148 +
149 + synchronized (this) {
150 + devices.replace(newDevice.id(), oldDevice, newDevice);
151 + if (!providerId.isAncillary()) {
152 + availableDevices.add(newDevice.id());
153 + }
154 + }
155 + return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
156 + }
157 +
158 + // Otherwise merely attempt to change availability if primary provider
159 + if (!providerId.isAncillary()) {
160 + synchronized (this) {
161 + boolean added = availableDevices.add(newDevice.id());
162 + return !added ? null :
163 + new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
164 + }
165 + }
166 + return null;
167 + }
168 +
169 + @Override
170 + public DeviceEvent markOffline(DeviceId deviceId) {
171 + synchronized (this) {
172 + Device device = devices.get(deviceId);
173 + boolean removed = (device != null) && availableDevices.remove(deviceId);
174 + return !removed ? null :
175 + new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
176 + }
177 + }
178 +
179 + @Override
180 + public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
181 + List<PortDescription> portDescriptions) {
182 +
183 + // TODO: implement multi-provider
184 + Device device = devices.get(deviceId);
185 + checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
186 +
187 + ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
188 + checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
189 +
190 + DeviceDescriptions descs = descsMap.get(providerId);
191 + checkArgument(descs != null,
192 + "Device description for Device ID %s from Provider %s was not found",
193 + deviceId, providerId);
194 +
195 +
196 + List<DeviceEvent> events = new ArrayList<>();
197 + synchronized (this) {
198 + ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
199 +
200 + // Add new ports
201 + Set<PortNumber> processed = new HashSet<>();
202 + for (PortDescription portDescription : portDescriptions) {
203 + PortNumber number = portDescription.portNumber();
204 + Port oldPort = ports.get(number);
205 + // update description
206 + descs.putPortDesc(number, portDescription);
207 + Port newPort = composePort(device, number, descsMap);
208 +
209 + events.add(oldPort == null ?
210 + createPort(device, newPort, ports) :
211 + updatePort(device, oldPort, newPort, ports));
212 + processed.add(portDescription.portNumber());
213 + }
214 +
215 + events.addAll(pruneOldPorts(device, ports, processed));
216 + }
217 + return FluentIterable.from(events).filter(notNull()).toList();
218 + }
219 +
220 + // Creates a new port based on the port description adds it to the map and
221 + // Returns corresponding event.
222 + private DeviceEvent createPort(Device device, Port newPort,
223 + ConcurrentMap<PortNumber, Port> ports) {
224 + ports.put(newPort.number(), newPort);
225 + return new DeviceEvent(PORT_ADDED, device, newPort);
226 + }
227 +
228 + // CHecks if the specified port requires update and if so, it replaces the
229 + // existing entry in the map and returns corresponding event.
230 + private DeviceEvent updatePort(Device device, Port oldPort,
231 + Port newPort,
232 + ConcurrentMap<PortNumber, Port> ports) {
233 + if (oldPort.isEnabled() != newPort.isEnabled()) {
234 + ports.put(oldPort.number(), newPort);
235 + return new DeviceEvent(PORT_UPDATED, device, newPort);
236 + }
237 + return null;
238 + }
239 +
240 + // Prunes the specified list of ports based on which ports are in the
241 + // processed list and returns list of corresponding events.
242 + private List<DeviceEvent> pruneOldPorts(Device device,
243 + Map<PortNumber, Port> ports,
244 + Set<PortNumber> processed) {
245 + List<DeviceEvent> events = new ArrayList<>();
246 + Iterator<PortNumber> iterator = ports.keySet().iterator();
247 + while (iterator.hasNext()) {
248 + PortNumber portNumber = iterator.next();
249 + if (!processed.contains(portNumber)) {
250 + events.add(new DeviceEvent(PORT_REMOVED, device,
251 + ports.get(portNumber)));
252 + iterator.remove();
253 + }
254 + }
255 + return events;
256 + }
257 +
258 + // Gets the map of ports for the specified device; if one does not already
259 + // exist, it creates and registers a new one.
260 + private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
261 + return createIfAbsentUnchecked(devicePorts, deviceId,
262 + new InitConcurrentHashMap<PortNumber, Port>());
263 + }
264 +
265 + @Override
266 + public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
267 + PortDescription portDescription) {
268 + Device device = devices.get(deviceId);
269 + checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
270 +
271 + ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
272 + checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
273 +
274 + DeviceDescriptions descs = descsMap.get(providerId);
275 + checkArgument(descs != null,
276 + "Device description for Device ID %s from Provider %s was not found",
277 + deviceId, providerId);
278 +
279 + // TODO: implement multi-provider
280 + synchronized (this) {
281 + ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
282 + final PortNumber number = portDescription.portNumber();
283 + Port oldPort = ports.get(number);
284 + // update description
285 + descs.putPortDesc(number, portDescription);
286 + Port newPort = composePort(device, number, descsMap);
287 + if (oldPort == null) {
288 + return createPort(device, newPort, ports);
289 + } else {
290 + return updatePort(device, oldPort, newPort, ports);
291 + }
292 + }
293 + }
294 +
295 + @Override
296 + public List<Port> getPorts(DeviceId deviceId) {
297 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
298 + if (ports == null) {
299 + return Collections.emptyList();
300 + }
301 + return ImmutableList.copyOf(ports.values());
302 + }
303 +
304 + @Override
305 + public Port getPort(DeviceId deviceId, PortNumber portNumber) {
306 + Map<PortNumber, Port> ports = devicePorts.get(deviceId);
307 + return ports == null ? null : ports.get(portNumber);
308 + }
309 +
310 + @Override
311 + public boolean isAvailable(DeviceId deviceId) {
312 + return availableDevices.contains(deviceId);
313 + }
314 +
315 + @Override
316 + public DeviceEvent removeDevice(DeviceId deviceId) {
317 + synchronized (this) {
318 + Device device = devices.remove(deviceId);
319 + return device == null ? null :
320 + new DeviceEvent(DEVICE_REMOVED, device, null);
321 + }
322 + }
323 +
324 + /**
325 + * Returns a Device, merging description given from multiple Providers.
326 + *
327 + * @param deviceId device identifier
328 + * @param providerDescs Collection of Descriptions from multiple providers
329 + * @return Device instance
330 + */
331 + private Device composeDevice(DeviceId deviceId,
332 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
333 +
334 + checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
335 +
336 + ProviderId primary = pickPrimaryPID(providerDescs);
337 +
338 + DeviceDescriptions desc = providerDescs.get(primary);
339 + Type type = desc.getDeviceDesc().type();
340 + String manufacturer = desc.getDeviceDesc().manufacturer();
341 + String hwVersion = desc.getDeviceDesc().hwVersion();
342 + String swVersion = desc.getDeviceDesc().swVersion();
343 + String serialNumber = desc.getDeviceDesc().serialNumber();
344 +
345 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
346 + if (e.getKey().equals(primary)) {
347 + continue;
348 + }
349 + // FIXME: implement attribute merging once we have K-V attributes
350 + }
351 +
352 + return new DefaultDevice(primary, deviceId , type, manufacturer, hwVersion, swVersion, serialNumber);
353 + }
354 +
355 + // probably want composePorts
356 + private Port composePort(Device device, PortNumber number,
357 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
358 +
359 + ProviderId primary = pickPrimaryPID(providerDescs);
360 + DeviceDescriptions primDescs = providerDescs.get(primary);
361 + final PortDescription portDesc = primDescs.getPortDesc(number);
362 + boolean isEnabled;
363 + if (portDesc != null) {
364 + isEnabled = portDesc.isEnabled();
365 + } else {
366 + // if no primary, assume not enabled
367 + // TODO: revisit this port enabled/disabled behavior
368 + isEnabled = false;
369 + }
370 +
371 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
372 + if (e.getKey().equals(primary)) {
373 + continue;
374 + }
375 + // FIXME: implement attribute merging once we have K-V attributes
376 + }
377 +
378 + return new DefaultPort(device, number, isEnabled);
379 + }
380 +
381 + /**
382 + * @return primary ProviderID, or randomly chosen one if none exists
383 + */
384 + private ProviderId pickPrimaryPID(
385 + ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
386 + ProviderId fallBackPrimary = null;
387 + for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
388 + if (!e.getKey().isAncillary()) {
389 + return e.getKey();
390 + } else if (fallBackPrimary == null) {
391 + // pick randomly as a fallback in case there is no primary
392 + fallBackPrimary = e.getKey();
393 + }
394 + }
395 + return fallBackPrimary;
396 + }
397 +
398 + // TODO: can be made generic
399 + private static final class InitConcurrentHashMap<K, V> implements
400 + ConcurrentInitializer<ConcurrentMap<K, V>> {
401 + @Override
402 + public ConcurrentMap<K, V> get() throws ConcurrentException {
403 + return new ConcurrentHashMap<>();
404 + }
405 + }
406 +
407 + public static final class InitDeviceDescs
408 + implements ConcurrentInitializer<DeviceDescriptions> {
409 + private final DeviceDescription deviceDesc;
410 + public InitDeviceDescs(DeviceDescription deviceDesc) {
411 + this.deviceDesc = checkNotNull(deviceDesc);
412 + }
413 + @Override
414 + public DeviceDescriptions get() throws ConcurrentException {
415 + return new DeviceDescriptions(deviceDesc);
416 + }
417 + }
418 +
419 +
420 + /**
421 + * Collection of Description of a Device and it's Ports given from a Provider.
422 + */
423 + private static class DeviceDescriptions {
424 + // private final DeviceId id;
425 + // private final ProviderId pid;
426 +
427 + private final AtomicReference<DeviceDescription> deviceDesc;
428 + private final ConcurrentMap<PortNumber, PortDescription> portDescs;
429 +
430 + public DeviceDescriptions(DeviceDescription desc) {
431 + this.deviceDesc = new AtomicReference<>(desc);
432 + this.portDescs = new ConcurrentHashMap<>();
433 + }
434 +
435 + public DeviceDescription getDeviceDesc() {
436 + return deviceDesc.get();
437 + }
438 +
439 + public PortDescription getPortDesc(PortNumber number) {
440 + return portDescs.get(number);
441 + }
442 +
443 + public Collection<PortDescription> getPortDescs() {
444 + return Collections.unmodifiableCollection(portDescs.values());
445 + }
446 +
447 + public DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
448 + return deviceDesc.getAndSet(newDesc);
449 + }
450 +
451 + public PortDescription putPortDesc(PortNumber number, PortDescription newDesc) {
452 + return portDescs.put(number, newDesc);
453 + }
454 + }
455 +}
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
4 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 4 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED; 3 import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
4 import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED; 4 import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import com.google.common.collect.HashMultimap; 3 import com.google.common.collect.HashMultimap;
4 import com.google.common.collect.ImmutableSet; 4 import com.google.common.collect.ImmutableSet;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
...@@ -7,8 +7,6 @@ import java.util.HashMap; ...@@ -7,8 +7,6 @@ import java.util.HashMap;
7 import java.util.HashSet; 7 import java.util.HashSet;
8 import java.util.Map; 8 import java.util.Map;
9 import java.util.Set; 9 import java.util.Set;
10 -import java.util.concurrent.ConcurrentHashMap;
11 -import java.util.concurrent.ConcurrentMap;
12 import java.util.concurrent.atomic.AtomicInteger; 10 import java.util.concurrent.atomic.AtomicInteger;
13 11
14 import org.apache.felix.scr.annotations.Activate; 12 import org.apache.felix.scr.annotations.Activate;
...@@ -48,8 +46,10 @@ public class SimpleMastershipStore ...@@ -48,8 +46,10 @@ public class SimpleMastershipStore
48 new DefaultControllerNode(new NodeId("local"), LOCALHOST); 46 new DefaultControllerNode(new NodeId("local"), LOCALHOST);
49 47
50 //devices mapped to their masters, to emulate multiple nodes 48 //devices mapped to their masters, to emulate multiple nodes
51 - protected final ConcurrentMap<DeviceId, NodeId> masterMap = 49 + protected final Map<DeviceId, NodeId> masterMap = new HashMap<>();
52 - new ConcurrentHashMap<>(); 50 + //emulate backups with pile of nodes
51 + protected final Set<NodeId> backups = new HashSet<>();
52 + //terms
53 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>(); 53 protected final Map<DeviceId, AtomicInteger> termMap = new HashMap<>();
54 54
55 @Activate 55 @Activate
...@@ -64,26 +64,30 @@ public class SimpleMastershipStore ...@@ -64,26 +64,30 @@ public class SimpleMastershipStore
64 64
65 @Override 65 @Override
66 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 66 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
67 + MastershipRole role = getRole(nodeId, deviceId);
67 68
68 - NodeId node = masterMap.get(deviceId);
69 - if (node == null) {
70 synchronized (this) { 69 synchronized (this) {
70 + switch (role) {
71 + case MASTER:
72 + return null;
73 + case STANDBY:
74 + masterMap.put(deviceId, nodeId);
75 + termMap.get(deviceId).incrementAndGet();
76 + backups.add(nodeId);
77 + break;
78 + case NONE:
71 masterMap.put(deviceId, nodeId); 79 masterMap.put(deviceId, nodeId);
72 termMap.put(deviceId, new AtomicInteger()); 80 termMap.put(deviceId, new AtomicInteger());
81 + backups.add(nodeId);
82 + break;
83 + default:
84 + log.warn("unknown Mastership Role {}", role);
85 + return null;
73 } 86 }
74 - return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
75 } 87 }
76 88
77 - if (node.equals(nodeId)) {
78 - return null;
79 - } else {
80 - synchronized (this) {
81 - masterMap.put(deviceId, nodeId);
82 - termMap.get(deviceId).incrementAndGet();
83 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); 89 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
84 } 90 }
85 - }
86 - }
87 91
88 @Override 92 @Override
89 public NodeId getMaster(DeviceId deviceId) { 93 public NodeId getMaster(DeviceId deviceId) {
...@@ -103,34 +107,111 @@ public class SimpleMastershipStore ...@@ -103,34 +107,111 @@ public class SimpleMastershipStore
103 107
104 @Override 108 @Override
105 public MastershipRole requestRole(DeviceId deviceId) { 109 public MastershipRole requestRole(DeviceId deviceId) {
106 - return getRole(instance.id(), deviceId); 110 + //query+possible reelection
111 + NodeId node = instance.id();
112 + MastershipRole role = getRole(node, deviceId);
113 +
114 + switch (role) {
115 + case MASTER:
116 + break;
117 + case STANDBY:
118 + synchronized (this) {
119 + //try to "re-elect", since we're really not distributed
120 + NodeId rel = reelect(node);
121 + if (rel == null) {
122 + masterMap.put(deviceId, node);
123 + termMap.put(deviceId, new AtomicInteger());
124 + role = MastershipRole.MASTER;
125 + }
126 + backups.add(node);
127 + }
128 + break;
129 + case NONE:
130 + //first to get to it, say we are master
131 + synchronized (this) {
132 + masterMap.put(deviceId, node);
133 + termMap.put(deviceId, new AtomicInteger());
134 + backups.add(node);
135 + role = MastershipRole.MASTER;
136 + }
137 + break;
138 + default:
139 + log.warn("unknown Mastership Role {}", role);
140 + }
141 + return role;
107 } 142 }
108 143
109 @Override 144 @Override
110 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { 145 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
111 - NodeId node = masterMap.get(deviceId); 146 + //just query
147 + NodeId current = masterMap.get(deviceId);
112 MastershipRole role; 148 MastershipRole role;
113 - if (node != null) { 149 +
114 - if (node.equals(nodeId)) { 150 + if (current == null) {
115 - role = MastershipRole.MASTER; 151 + if (backups.contains(nodeId)) {
116 - } else {
117 role = MastershipRole.STANDBY; 152 role = MastershipRole.STANDBY;
153 + } else {
154 + role = MastershipRole.NONE;
118 } 155 }
119 } else { 156 } else {
120 - //masterMap doesn't contain it. 157 + if (current.equals(nodeId)) {
121 role = MastershipRole.MASTER; 158 role = MastershipRole.MASTER;
122 - masterMap.put(deviceId, nodeId); 159 + } else {
160 + role = MastershipRole.STANDBY;
161 + }
123 } 162 }
124 return role; 163 return role;
125 } 164 }
126 165
127 @Override 166 @Override
128 public MastershipTerm getTermFor(DeviceId deviceId) { 167 public MastershipTerm getTermFor(DeviceId deviceId) {
129 - if (masterMap.get(deviceId) == null) { 168 + if ((masterMap.get(deviceId) == null) ||
169 + (termMap.get(deviceId) == null)) {
130 return null; 170 return null;
131 } 171 }
132 return MastershipTerm.of( 172 return MastershipTerm.of(
133 masterMap.get(deviceId), termMap.get(deviceId).get()); 173 masterMap.get(deviceId), termMap.get(deviceId).get());
134 } 174 }
135 175
176 + @Override
177 + public MastershipEvent unsetMaster(NodeId nodeId, DeviceId deviceId) {
178 + MastershipRole role = getRole(nodeId, deviceId);
179 + synchronized (this) {
180 + switch (role) {
181 + case MASTER:
182 + NodeId backup = reelect(nodeId);
183 + if (backup == null) {
184 + masterMap.remove(deviceId);
185 + } else {
186 + masterMap.put(deviceId, backup);
187 + termMap.get(deviceId).incrementAndGet();
188 + return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
189 + }
190 + case STANDBY:
191 + case NONE:
192 + if (!termMap.containsKey(deviceId)) {
193 + termMap.put(deviceId, new AtomicInteger());
194 + }
195 + backups.add(nodeId);
196 + break;
197 + default:
198 + log.warn("unknown Mastership Role {}", role);
199 + }
200 + }
201 + return null;
202 + }
203 +
204 + //dumbly selects next-available node that's not the current one
205 + //emulate leader election
206 + private NodeId reelect(NodeId nodeId) {
207 + NodeId backup = null;
208 + for (NodeId n : backups) {
209 + if (!n.equals(nodeId)) {
210 + backup = n;
211 + break;
212 + }
213 + }
214 + return backup;
215 + }
216 +
136 } 217 }
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import org.apache.felix.scr.annotations.Activate; 3 import org.apache.felix.scr.annotations.Activate;
4 import org.apache.felix.scr.annotations.Component; 4 import org.apache.felix.scr.annotations.Component;
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
2 * Implementations of in-memory stores suitable for unit testing and 2 * Implementations of in-memory stores suitable for unit testing and
3 * experimentation; not for production use. 3 * experimentation; not for production use.
4 */ 4 */
5 -package org.onlab.onos.net.trivial.impl; 5 +package org.onlab.onos.store.trivial.impl;
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import org.junit.Before; 3 import org.junit.Before;
4 import org.junit.Test; 4 import org.junit.Test;
......
1 /** 1 /**
2 * 2 *
3 */ 3 */
4 -package org.onlab.onos.net.trivial.impl; 4 +package org.onlab.onos.store.trivial.impl;
5 5
6 import static org.junit.Assert.*; 6 import static org.junit.Assert.*;
7 import static org.onlab.onos.net.Device.Type.SWITCH; 7 import static org.onlab.onos.net.Device.Type.SWITCH;
...@@ -44,6 +44,7 @@ import com.google.common.collect.Sets; ...@@ -44,6 +44,7 @@ import com.google.common.collect.Sets;
44 public class SimpleDeviceStoreTest { 44 public class SimpleDeviceStoreTest {
45 45
46 private static final ProviderId PID = new ProviderId("of", "foo"); 46 private static final ProviderId PID = new ProviderId("of", "foo");
47 + private static final ProviderId PIDA = new ProviderId("of", "bar", true);
47 private static final DeviceId DID1 = deviceId("of:foo"); 48 private static final DeviceId DID1 = deviceId("of:foo");
48 private static final DeviceId DID2 = deviceId("of:bar"); 49 private static final DeviceId DID2 = deviceId("of:bar");
49 private static final String MFR = "whitebox"; 50 private static final String MFR = "whitebox";
...@@ -89,6 +90,13 @@ public class SimpleDeviceStoreTest { ...@@ -89,6 +90,13 @@ public class SimpleDeviceStoreTest {
89 deviceStore.createOrUpdateDevice(PID, deviceId, description); 90 deviceStore.createOrUpdateDevice(PID, deviceId, description);
90 } 91 }
91 92
93 + private void putDeviceAncillary(DeviceId deviceId, String swVersion) {
94 + DeviceDescription description =
95 + new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
96 + HW, swVersion, SN);
97 + deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
98 + }
99 +
92 private static void assertDevice(DeviceId id, String swVersion, Device device) { 100 private static void assertDevice(DeviceId id, String swVersion, Device device) {
93 assertNotNull(device); 101 assertNotNull(device);
94 assertEquals(id, device.id()); 102 assertEquals(id, device.id());
...@@ -160,6 +168,33 @@ public class SimpleDeviceStoreTest { ...@@ -160,6 +168,33 @@ public class SimpleDeviceStoreTest {
160 } 168 }
161 169
162 @Test 170 @Test
171 + public final void testCreateOrUpdateDeviceAncillary() {
172 + DeviceDescription description =
173 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
174 + HW, SW1, SN);
175 + DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
176 + assertEquals(DEVICE_ADDED, event.type());
177 + assertDevice(DID1, SW1, event.subject());
178 + assertEquals(PIDA, event.subject().providerId());
179 + assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
180 +
181 + DeviceDescription description2 =
182 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
183 + HW, SW2, SN);
184 + DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
185 + assertEquals(DEVICE_UPDATED, event2.type());
186 + assertDevice(DID1, SW2, event2.subject());
187 + assertEquals(PID, event2.subject().providerId());
188 + assertTrue(deviceStore.isAvailable(DID1));
189 +
190 + assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
191 +
192 + // For now, Ancillary is ignored once primary appears
193 + assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
194 + }
195 +
196 +
197 + @Test
163 public final void testMarkOffline() { 198 public final void testMarkOffline() {
164 199
165 putDevice(DID1, SW1); 200 putDevice(DID1, SW1);
...@@ -182,7 +217,7 @@ public class SimpleDeviceStoreTest { ...@@ -182,7 +217,7 @@ public class SimpleDeviceStoreTest {
182 new DefaultPortDescription(P2, true) 217 new DefaultPortDescription(P2, true)
183 ); 218 );
184 219
185 - List<DeviceEvent> events = deviceStore.updatePorts(DID1, pds); 220 + List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
186 221
187 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2); 222 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
188 for (DeviceEvent event : events) { 223 for (DeviceEvent event : events) {
...@@ -201,7 +236,7 @@ public class SimpleDeviceStoreTest { ...@@ -201,7 +236,7 @@ public class SimpleDeviceStoreTest {
201 new DefaultPortDescription(P3, true) 236 new DefaultPortDescription(P3, true)
202 ); 237 );
203 238
204 - events = deviceStore.updatePorts(DID1, pds2); 239 + events = deviceStore.updatePorts(PID, DID1, pds2);
205 assertFalse("event should be triggered", events.isEmpty()); 240 assertFalse("event should be triggered", events.isEmpty());
206 for (DeviceEvent event : events) { 241 for (DeviceEvent event : events) {
207 PortNumber num = event.port().number(); 242 PortNumber num = event.port().number();
...@@ -224,7 +259,7 @@ public class SimpleDeviceStoreTest { ...@@ -224,7 +259,7 @@ public class SimpleDeviceStoreTest {
224 new DefaultPortDescription(P1, false), 259 new DefaultPortDescription(P1, false),
225 new DefaultPortDescription(P2, true) 260 new DefaultPortDescription(P2, true)
226 ); 261 );
227 - events = deviceStore.updatePorts(DID1, pds3); 262 + events = deviceStore.updatePorts(PID, DID1, pds3);
228 assertFalse("event should be triggered", events.isEmpty()); 263 assertFalse("event should be triggered", events.isEmpty());
229 for (DeviceEvent event : events) { 264 for (DeviceEvent event : events) {
230 PortNumber num = event.port().number(); 265 PortNumber num = event.port().number();
...@@ -249,14 +284,42 @@ public class SimpleDeviceStoreTest { ...@@ -249,14 +284,42 @@ public class SimpleDeviceStoreTest {
249 List<PortDescription> pds = Arrays.<PortDescription>asList( 284 List<PortDescription> pds = Arrays.<PortDescription>asList(
250 new DefaultPortDescription(P1, true) 285 new DefaultPortDescription(P1, true)
251 ); 286 );
252 - deviceStore.updatePorts(DID1, pds); 287 + deviceStore.updatePorts(PID, DID1, pds);
288 +
289 + DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
290 + new DefaultPortDescription(P1, false));
291 + assertEquals(PORT_UPDATED, event.type());
292 + assertDevice(DID1, SW1, event.subject());
293 + assertEquals(P1, event.port().number());
294 + assertFalse("Port is disabled", event.port().isEnabled());
295 +
296 + }
297 + @Test
298 + public final void testUpdatePortStatusAncillary() {
299 + putDeviceAncillary(DID1, SW1);
300 + putDevice(DID1, SW1);
301 + List<PortDescription> pds = Arrays.<PortDescription>asList(
302 + new DefaultPortDescription(P1, true)
303 + );
304 + deviceStore.updatePorts(PID, DID1, pds);
253 305
254 - DeviceEvent event = deviceStore.updatePortStatus(DID1, 306 + DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
255 new DefaultPortDescription(P1, false)); 307 new DefaultPortDescription(P1, false));
256 assertEquals(PORT_UPDATED, event.type()); 308 assertEquals(PORT_UPDATED, event.type());
257 assertDevice(DID1, SW1, event.subject()); 309 assertDevice(DID1, SW1, event.subject());
258 assertEquals(P1, event.port().number()); 310 assertEquals(P1, event.port().number());
259 assertFalse("Port is disabled", event.port().isEnabled()); 311 assertFalse("Port is disabled", event.port().isEnabled());
312 +
313 + DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
314 + new DefaultPortDescription(P1, true));
315 + assertNull("Ancillary is ignored if primary exists", event2);
316 +
317 + DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
318 + new DefaultPortDescription(P2, true));
319 + assertEquals(PORT_ADDED, event3.type());
320 + assertDevice(DID1, SW1, event3.subject());
321 + assertEquals(P2, event3.port().number());
322 + assertFalse("Port is disabled if not given from provider", event3.port().isEnabled());
260 } 323 }
261 324
262 @Test 325 @Test
...@@ -267,7 +330,7 @@ public class SimpleDeviceStoreTest { ...@@ -267,7 +330,7 @@ public class SimpleDeviceStoreTest {
267 new DefaultPortDescription(P1, true), 330 new DefaultPortDescription(P1, true),
268 new DefaultPortDescription(P2, true) 331 new DefaultPortDescription(P2, true)
269 ); 332 );
270 - deviceStore.updatePorts(DID1, pds); 333 + deviceStore.updatePorts(PID, DID1, pds);
271 334
272 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2); 335 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
273 List<Port> ports = deviceStore.getPorts(DID1); 336 List<Port> ports = deviceStore.getPorts(DID1);
...@@ -290,7 +353,7 @@ public class SimpleDeviceStoreTest { ...@@ -290,7 +353,7 @@ public class SimpleDeviceStoreTest {
290 new DefaultPortDescription(P1, true), 353 new DefaultPortDescription(P1, true),
291 new DefaultPortDescription(P2, false) 354 new DefaultPortDescription(P2, false)
292 ); 355 );
293 - deviceStore.updatePorts(DID1, pds); 356 + deviceStore.updatePorts(PID, DID1, pds);
294 357
295 Port port1 = deviceStore.getPort(DID1, P1); 358 Port port1 = deviceStore.getPort(DID1, P1);
296 assertEquals(P1, port1.number()); 359 assertEquals(P1, port1.number());
......
1 -package org.onlab.onos.net.trivial.impl; 1 +package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.junit.Assert.*; 3 import static org.junit.Assert.*;
4 import static org.onlab.onos.net.DeviceId.deviceId; 4 import static org.onlab.onos.net.DeviceId.deviceId;
......
1 +package org.onlab.onos.store.trivial.impl;
2 +
3 +import java.util.Set;
4 +import java.util.concurrent.atomic.AtomicInteger;
5 +
6 +import org.junit.After;
7 +import org.junit.Before;
8 +import org.junit.Test;
9 +import org.onlab.onos.cluster.MastershipTerm;
10 +import org.onlab.onos.cluster.NodeId;
11 +import org.onlab.onos.net.DeviceId;
12 +
13 +import com.google.common.collect.Sets;
14 +
15 +import static org.junit.Assert.assertEquals;
16 +import static org.junit.Assert.assertNull;
17 +import static org.junit.Assert.assertTrue;
18 +import static org.onlab.onos.net.MastershipRole.*;
19 +import static org.onlab.onos.cluster.MastershipEvent.Type.*;
20 +
21 +/**
22 + * Test for the simple MastershipStore implementation.
23 + */
24 +public class SimpleMastershipStoreTest {
25 +
26 + private static final DeviceId DID1 = DeviceId.deviceId("of:01");
27 + private static final DeviceId DID2 = DeviceId.deviceId("of:02");
28 + private static final DeviceId DID3 = DeviceId.deviceId("of:03");
29 + private static final DeviceId DID4 = DeviceId.deviceId("of:04");
30 +
31 + private static final NodeId N1 = new NodeId("local");
32 + private static final NodeId N2 = new NodeId("other");
33 +
34 + private SimpleMastershipStore sms;
35 +
36 + @Before
37 + public void setUp() throws Exception {
38 + sms = new SimpleMastershipStore();
39 + sms.activate();
40 + }
41 +
42 + @After
43 + public void tearDown() throws Exception {
44 + sms.deactivate();
45 + }
46 +
47 + @Test
48 + public void getRole() {
49 + //special case, no backup or master
50 + put(DID1, N1, false, false);
51 + assertEquals("wrong role", NONE, sms.getRole(N1, DID1));
52 +
53 + //backup exists but we aren't mapped
54 + put(DID2, N1, false, true);
55 + assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2));
56 +
57 + //N2 is master
58 + put(DID3, N2, true, true);
59 + assertEquals("wrong role", MASTER, sms.getRole(N2, DID3));
60 +
61 + //N2 is master but N1 is only in backups set
62 + put(DID4, N2, true, false);
63 + assertEquals("wrong role", STANDBY, sms.getRole(N1, DID4));
64 + }
65 +
66 + @Test
67 + public void getMaster() {
68 + put(DID3, N2, true, true);
69 + assertEquals("wrong role", MASTER, sms.getRole(N2, DID3));
70 + assertEquals("wrong device", N2, sms.getMaster(DID3));
71 + }
72 +
73 + @Test
74 + public void setMaster() {
75 + put(DID1, N1, false, false);
76 + assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID1).type());
77 + assertEquals("wrong role", MASTER, sms.getRole(N1, DID1));
78 + //set node that's already master - should be ignored
79 + assertNull("wrong event", sms.setMaster(N1, DID1));
80 +
81 + //set STANDBY to MASTER
82 + put(DID2, N1, false, true);
83 + assertEquals("wrong role", STANDBY, sms.getRole(N1, DID2));
84 + assertEquals("wrong event", MASTER_CHANGED, sms.setMaster(N1, DID2).type());
85 + assertEquals("wrong role", MASTER, sms.getRole(N1, DID2));
86 + }
87 +
88 + @Test
89 + public void getDevices() {
90 + Set<DeviceId> d = Sets.newHashSet(DID1, DID2);
91 +
92 + put(DID1, N2, true, true);
93 + put(DID2, N2, true, true);
94 + put(DID3, N1, true, true);
95 + assertTrue("wrong devices", d.equals(sms.getDevices(N2)));
96 + }
97 +
98 + @Test
99 + public void getTermFor() {
100 + put(DID1, N1, true, true);
101 + assertEquals("wrong term", MastershipTerm.of(N1, 0), sms.getTermFor(DID1));
102 +
103 + //switch to N2 and back - 2 term switches
104 + sms.setMaster(N2, DID1);
105 + sms.setMaster(N1, DID1);
106 + assertEquals("wrong term", MastershipTerm.of(N1, 2), sms.getTermFor(DID1));
107 + }
108 +
109 + @Test
110 + public void requestRole() {
111 + //NONE - become MASTER
112 + put(DID1, N1, false, false);
113 + assertEquals("wrong role", MASTER, sms.requestRole(DID1));
114 +
115 + //STANDBY without backup - become MASTER
116 + put(DID2, N1, false, true);
117 + assertEquals("wrong role", MASTER, sms.requestRole(DID2));
118 +
119 + //STANDBY with backup - stay STANDBY
120 + put(DID3, N2, false, true);
121 + assertEquals("wrong role", STANDBY, sms.requestRole(DID3));
122 +
123 + //local (N1) is MASTER - stay MASTER
124 + put(DID4, N1, true, true);
125 + assertEquals("wrong role", MASTER, sms.requestRole(DID4));
126 + }
127 +
128 + @Test
129 + public void unsetMaster() {
130 + //NONE - record backup but take no other action
131 + put(DID1, N1, false, false);
132 + sms.unsetMaster(N1, DID1);
133 + assertTrue("not backed up", sms.backups.contains(N1));
134 + sms.termMap.clear();
135 + sms.unsetMaster(N1, DID1);
136 + assertTrue("term not set", sms.termMap.containsKey(DID1));
137 +
138 + //no backup, MASTER
139 + put(DID1, N1, true, true);
140 + assertNull("wrong event", sms.unsetMaster(N1, DID1));
141 + assertNull("wrong node", sms.masterMap.get(DID1));
142 +
143 + //backup, switch
144 + sms.masterMap.clear();
145 + put(DID1, N1, true, true);
146 + put(DID2, N2, true, true);
147 + assertEquals("wrong event", MASTER_CHANGED, sms.unsetMaster(N1, DID1).type());
148 + }
149 +
150 + //helper to populate master/backup structures
151 + private void put(DeviceId dev, NodeId node, boolean store, boolean backup) {
152 + if (store) {
153 + sms.masterMap.put(dev, node);
154 + }
155 + if (backup) {
156 + sms.backups.add(node);
157 + }
158 + sms.termMap.put(dev, new AtomicInteger());
159 + }
160 +}
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle> 11 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
12 12
13 <bundle>mvn:com.hazelcast/hazelcast/3.3</bundle> 13 <bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
14 + <bundle>mvn:com.codahale.metrics/metrics-core/3.0.2</bundle>
14 <bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle> 15 <bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle>
15 16
16 <bundle>mvn:com.esotericsoftware.kryo/kryo/2.24.0</bundle> 17 <bundle>mvn:com.esotericsoftware.kryo/kryo/2.24.0</bundle>
......
...@@ -48,7 +48,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext ...@@ -48,7 +48,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext
48 OFPacketOut.Builder builder = sw.factory().buildPacketOut(); 48 OFPacketOut.Builder builder = sw.factory().buildPacketOut();
49 OFAction act = buildOutput(outPort.getPortNumber()); 49 OFAction act = buildOutput(outPort.getPortNumber());
50 pktout = builder.setXid(pktin.getXid()) 50 pktout = builder.setXid(pktin.getXid())
51 - .setInPort(pktin.getInPort()) 51 + .setInPort(inport())
52 .setBufferId(pktin.getBufferId()) 52 .setBufferId(pktin.getBufferId())
53 .setActions(Collections.singletonList(act)) 53 .setActions(Collections.singletonList(act))
54 .build(); 54 .build();
...@@ -63,7 +63,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext ...@@ -63,7 +63,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext
63 OFAction act = buildOutput(outPort.getPortNumber()); 63 OFAction act = buildOutput(outPort.getPortNumber());
64 pktout = builder.setXid(pktin.getXid()) 64 pktout = builder.setXid(pktin.getXid())
65 .setBufferId(OFBufferId.NO_BUFFER) 65 .setBufferId(OFBufferId.NO_BUFFER)
66 - .setInPort(pktin.getInPort()) 66 + .setInPort(inport())
67 .setActions(Collections.singletonList(act)) 67 .setActions(Collections.singletonList(act))
68 .setData(ethFrame.serialize()) 68 .setData(ethFrame.serialize())
69 .build(); 69 .build();
...@@ -88,10 +88,16 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext ...@@ -88,10 +88,16 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext
88 88
89 @Override 89 @Override
90 public Integer inPort() { 90 public Integer inPort() {
91 + return inport().getPortNumber();
92 + }
93 +
94 +
95 + private OFPort inport() {
96 + //FIXME: this has to change in fucking loxi
91 try { 97 try {
92 - return pktin.getInPort().getPortNumber(); 98 + return pktin.getInPort();
93 } catch (UnsupportedOperationException e) { 99 } catch (UnsupportedOperationException e) {
94 - return pktin.getMatch().get(MatchField.IN_PORT).getPortNumber(); 100 + return pktin.getMatch().get(MatchField.IN_PORT);
95 } 101 }
96 } 102 }
97 103
......
...@@ -243,6 +243,8 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitchDriver { ...@@ -243,6 +243,8 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitchDriver {
243 if (role == RoleState.SLAVE || role == RoleState.EQUAL) { 243 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
244 this.role = role; 244 this.role = role;
245 } 245 }
246 + } else {
247 + this.role = role;
246 } 248 }
247 } catch (IOException e) { 249 } catch (IOException e) {
248 log.error("Unable to write to switch {}.", this.dpid); 250 log.error("Unable to write to switch {}.", this.dpid);
......
...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
651 * @param error The error message 651 * @param error The error message
652 */ 652 */
653 protected void logError(OFChannelHandler h, OFErrorMsg error) { 653 protected void logError(OFChannelHandler h, OFErrorMsg error) {
654 - log.error("{} from switch {} in state {}", 654 + log.info("{} from switch {} in state {}",
655 new Object[] { 655 new Object[] {
656 error, 656 error,
657 h.getSwitchInfoString(), 657 h.getSwitchInfoString(),
......
...@@ -6,6 +6,7 @@ import java.util.Collections; ...@@ -6,6 +6,7 @@ import java.util.Collections;
6 import java.util.List; 6 import java.util.List;
7 7
8 import org.onlab.onos.openflow.controller.Dpid; 8 import org.onlab.onos.openflow.controller.Dpid;
9 +import org.onlab.onos.openflow.controller.RoleState;
9 import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch; 10 import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch;
10 import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver; 11 import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
11 import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriverFactory; 12 import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriverFactory;
...@@ -61,6 +62,11 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory { ...@@ -61,6 +62,11 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory {
61 return new AbstractOpenFlowSwitch(dpid, desc) { 62 return new AbstractOpenFlowSwitch(dpid, desc) {
62 63
63 @Override 64 @Override
65 + public void setRole(RoleState state) {
66 + this.role = RoleState.MASTER;
67 + }
68 +
69 + @Override
64 public void write(List<OFMessage> msgs) { 70 public void write(List<OFMessage> msgs) {
65 channel.write(msgs); 71 channel.write(msgs);
66 } 72 }
......
...@@ -425,7 +425,7 @@ ...@@ -425,7 +425,7 @@
425 <group> 425 <group>
426 <title>Core Subsystems</title> 426 <title>Core Subsystems</title>
427 <packages> 427 <packages>
428 - org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.net.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.* 428 + org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*
429 </packages> 429 </packages>
430 </group> 430 </group>
431 <group> 431 <group>
......
...@@ -73,10 +73,11 @@ public class FlowModBuilder { ...@@ -73,10 +73,11 @@ public class FlowModBuilder {
73 List<OFAction> actions = buildActions(); 73 List<OFAction> actions = buildActions();
74 74
75 //TODO: what to do without bufferid? do we assume that there will be a pktout as well? 75 //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
76 - OFFlowMod fm = factory.buildFlowModify() 76 + OFFlowMod fm = factory.buildFlowAdd()
77 .setCookie(U64.of(cookie.value())) 77 .setCookie(U64.of(cookie.value()))
78 .setBufferId(OFBufferId.NO_BUFFER) 78 .setBufferId(OFBufferId.NO_BUFFER)
79 .setActions(actions) 79 .setActions(actions)
80 + .setIdleTimeout(10)
80 .setMatch(match) 81 .setMatch(match)
81 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) 82 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
82 .setPriority(priority) 83 .setPriority(priority)
...@@ -93,7 +94,7 @@ public class FlowModBuilder { ...@@ -93,7 +94,7 @@ public class FlowModBuilder {
93 OFFlowMod fm = factory.buildFlowDelete() 94 OFFlowMod fm = factory.buildFlowDelete()
94 .setCookie(U64.of(cookie.value())) 95 .setCookie(U64.of(cookie.value()))
95 .setBufferId(OFBufferId.NO_BUFFER) 96 .setBufferId(OFBufferId.NO_BUFFER)
96 - //.setActions(actions) 97 + .setActions(actions)
97 .setMatch(match) 98 .setMatch(match)
98 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) 99 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
99 .setPriority(priority) 100 .setPriority(priority)
......
...@@ -18,6 +18,7 @@ import org.onlab.packet.IpPrefix; ...@@ -18,6 +18,7 @@ import org.onlab.packet.IpPrefix;
18 import org.onlab.packet.MacAddress; 18 import org.onlab.packet.MacAddress;
19 import org.onlab.packet.VlanId; 19 import org.onlab.packet.VlanId;
20 import org.projectfloodlight.openflow.protocol.OFFlowRemoved; 20 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
21 +import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
21 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; 22 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
22 import org.projectfloodlight.openflow.protocol.action.OFAction; 23 import org.projectfloodlight.openflow.protocol.action.OFAction;
23 import org.projectfloodlight.openflow.protocol.action.OFActionOutput; 24 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
...@@ -70,14 +71,15 @@ public class FlowRuleBuilder { ...@@ -70,14 +71,15 @@ public class FlowRuleBuilder {
70 buildSelector(), buildTreatment(), stat.getPriority(), 71 buildSelector(), buildTreatment(), stat.getPriority(),
71 FlowRuleState.ADDED, stat.getDurationNsec() / 1000000, 72 FlowRuleState.ADDED, stat.getDurationNsec() / 1000000,
72 stat.getPacketCount().getValue(), stat.getByteCount().getValue(), 73 stat.getPacketCount().getValue(), stat.getByteCount().getValue(),
73 - stat.getCookie().getValue()); 74 + stat.getCookie().getValue(), false);
74 } else { 75 } else {
75 // TODO: revisit potentially. 76 // TODO: revisit potentially.
76 return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)), 77 return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
77 buildSelector(), null, removed.getPriority(), 78 buildSelector(), null, removed.getPriority(),
78 FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000, 79 FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000,
79 removed.getPacketCount().getValue(), removed.getByteCount().getValue(), 80 removed.getPacketCount().getValue(), removed.getByteCount().getValue(),
80 - removed.getCookie().getValue()); 81 + removed.getCookie().getValue(),
82 + removed.getReason() == OFFlowRemovedReason.IDLE_TIMEOUT.ordinal());
81 } 83 }
82 } 84 }
83 85
......
...@@ -158,7 +158,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -158,7 +158,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
158 case BARRIER_REPLY: 158 case BARRIER_REPLY:
159 case ERROR: 159 case ERROR:
160 default: 160 default:
161 - log.warn("Unhandled message type: {}", msg.getType()); 161 + log.debug("Unhandled message type: {}", msg.getType());
162 } 162 }
163 163
164 } 164 }
......
...@@ -131,7 +131,7 @@ public class LinkDiscovery implements TimerTask { ...@@ -131,7 +131,7 @@ public class LinkDiscovery implements TimerTask {
131 } 131 }
132 timeout = Timer.getTimer().newTimeout(this, 0, 132 timeout = Timer.getTimer().newTimeout(this, 0,
133 TimeUnit.MILLISECONDS); 133 TimeUnit.MILLISECONDS);
134 - this.log.debug("Started discovery manager for switch {}", 134 + this.log.info("Started discovery manager for switch {}",
135 sw.getId()); 135 sw.getId());
136 136
137 } 137 }
......
1 package org.onlab.onos.provider.of.packet.impl; 1 package org.onlab.onos.provider.of.packet.impl;
2 2
3 -import static org.onlab.onos.openflow.controller.RoleState.SLAVE;
4 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
5 4
6 import java.nio.ByteBuffer; 5 import java.nio.ByteBuffer;
...@@ -95,9 +94,6 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr ...@@ -95,9 +94,6 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
95 if (sw == null) { 94 if (sw == null) {
96 log.warn("Device {} isn't available?", devId); 95 log.warn("Device {} isn't available?", devId);
97 return; 96 return;
98 - } else if (sw.getRole().equals(SLAVE)) {
99 - log.warn("Can't write to Device {} as slave", devId);
100 - return;
101 } 97 }
102 98
103 Ethernet eth = new Ethernet(); 99 Ethernet eth = new Ethernet();
......
...@@ -140,12 +140,12 @@ public class OpenFlowPacketProviderTest { ...@@ -140,12 +140,12 @@ public class OpenFlowPacketProviderTest {
140 sw.sent.clear(); 140 sw.sent.clear();
141 141
142 //wrong Role 142 //wrong Role
143 - sw.setRole(RoleState.SLAVE); 143 + //sw.setRole(RoleState.SLAVE);
144 - provider.emit(passPkt); 144 + //provider.emit(passPkt);
145 - assertEquals("invalid switch", sw, controller.current); 145 + //assertEquals("invalid switch", sw, controller.current);
146 - assertEquals("message sent incorrectly", 0, sw.sent.size()); 146 + //assertEquals("message sent incorrectly", 0, sw.sent.size());
147 147
148 - sw.setRole(RoleState.MASTER); 148 + //sw.setRole(RoleState.MASTER);
149 149
150 //missing switch 150 //missing switch
151 OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth); 151 OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth);
......
...@@ -34,6 +34,8 @@ cp -r $ONOS_ROOT/tools/package/etc/* $KARAF_DIST/etc ...@@ -34,6 +34,8 @@ cp -r $ONOS_ROOT/tools/package/etc/* $KARAF_DIST/etc
34 mkdir -p $KARAF_DIST/system/org/onlab 34 mkdir -p $KARAF_DIST/system/org/onlab
35 cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/ 35 cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/
36 36
37 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo}"
38 +
37 # Cellar Patching -------------------------------------------------------------- 39 # Cellar Patching --------------------------------------------------------------
38 40
39 # Patch the Apache Karaf distribution file to add Cellar features repository 41 # Patch the Apache Karaf distribution file to add Cellar features repository
...@@ -51,7 +53,7 @@ perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-feature ...@@ -51,7 +53,7 @@ perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.onlab.onos/onos-feature
51 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 53 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
52 54
53 # Patch the Apache Karaf distribution file to load ONOS features 55 # Patch the Apache Karaf distribution file to load ONOS features
54 -perl -pi.old -e 's|^(featuresBoot=.*)|\1,webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo|' \ 56 +perl -pi.old -e "s|^(featuresBoot=.*)|\1,$ONOS_FEATURES|" \
55 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg 57 $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
56 58
57 # Patch the Apache Karaf distribution with ONOS branding bundle 59 # Patch the Apache Karaf distribution with ONOS branding bundle
......
...@@ -9,5 +9,10 @@ ...@@ -9,5 +9,10 @@
9 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2) 9 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
10 10
11 onos-package 11 onos-package
12 -for node in $nodes; do (printf "%s: %s\n" "$node" "`onos-install -f $node`")& done 12 +for node in $nodes; do onos-install -f $node 1>/dev/null & done
13 +
14 +# Wait for shutdown before waiting for restart
15 +sleep 3
16 +
13 for node in $nodes; do onos-wait-for-start $node; done 17 for node in $nodes; do onos-wait-for-start $node; done
18 +for node in $nodes; do onos-check-logs $node; done
......
...@@ -6,22 +6,21 @@ ...@@ -6,22 +6,21 @@
6 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next} 6 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
7 7
8 # Setup some environmental context for developers 8 # Setup some environmental context for developers
9 -export JAVA_HOME=$(/usr/libexec/java_home) 9 +export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
10 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2} 10 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
11 export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1} 11 export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1}
12 export KARAF_LOG=$KARAF/data/log/karaf.log 12 export KARAF_LOG=$KARAF/data/log/karaf.log
13 13
14 # Setup a path 14 # Setup a path
15 -export PS=":" 15 +export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin"
16 -export PATH="$PATH:$ONOS_ROOT/tools/dev:$ONOS_ROOT/tools/build" 16 +export PATH="$PATH:$ONOS_ROOT/tools/build"
17 -export PATH="$PATH:$ONOS_ROOT/tools/test/bin"
18 export PATH="$PATH:$MAVEN/bin:$KARAF/bin" 17 export PATH="$PATH:$MAVEN/bin:$KARAF/bin"
19 export PATH="$PATH:." 18 export PATH="$PATH:."
20 19
21 # Convenience utility to warp to various ONOS source projects 20 # Convenience utility to warp to various ONOS source projects
22 # e.g. 'o api', 'o dev', 'o' 21 # e.g. 'o api', 'o dev', 'o'
23 function o { 22 function o {
24 - cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target|src' | \ 23 + cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \
25 egrep "${1:-$ONOS_ROOT}" | head -n 1) 24 egrep "${1:-$ONOS_ROOT}" | head -n 1)
26 } 25 }
27 26
...@@ -30,11 +29,12 @@ alias mci='mvn clean install' ...@@ -30,11 +29,12 @@ alias mci='mvn clean install'
30 29
31 # Short-hand for ONOS build, package and test. 30 # Short-hand for ONOS build, package and test.
32 alias ob='onos-build' 31 alias ob='onos-build'
32 +alias obs='onos-build-selective'
33 alias op='onos-package' 33 alias op='onos-package'
34 alias ot='onos-test' 34 alias ot='onos-test'
35 35
36 # Short-hand for tailing the ONOS (karaf) log 36 # Short-hand for tailing the ONOS (karaf) log
37 -alias tl='$ONOS_ROOT/tools/dev/watchLog' 37 +alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
38 alias tlo='tl | grep --colour=always org.onlab' 38 alias tlo='tl | grep --colour=always org.onlab'
39 39
40 # Pretty-print JSON output 40 # Pretty-print JSON output
...@@ -57,6 +57,7 @@ function cell { ...@@ -57,6 +57,7 @@ function cell {
57 if [ -n "$1" ]; then 57 if [ -n "$1" ]; then
58 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \ 58 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
59 echo "No such cell: $1" >&2 && return 1 59 echo "No such cell: $1" >&2 && return 1
60 + unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
60 . $ONOS_ROOT/tools/test/cells/$1 61 . $ONOS_ROOT/tools/test/cells/$1
61 export OCI=$OC1 62 export OCI=$OC1
62 export ONOS_CELL=$1 63 export ONOS_CELL=$1
...@@ -66,6 +67,7 @@ function cell { ...@@ -66,6 +67,7 @@ function cell {
66 env | egrep "OCI" 67 env | egrep "OCI"
67 env | egrep "OC[0-9]+" | sort 68 env | egrep "OC[0-9]+" | sort
68 env | egrep "OCN" 69 env | egrep "OCN"
70 + env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL'
69 fi 71 fi
70 } 72 }
71 73
...@@ -73,7 +75,11 @@ cell local >/dev/null # Default cell is the local VMs ...@@ -73,7 +75,11 @@ cell local >/dev/null # Default cell is the local VMs
73 75
74 # Lists available cells 76 # Lists available cells
75 function cells { 77 function cells {
76 - ls -1 $ONOS_ROOT/tools/test/cells 78 + for cell in $(ls -1 $ONOS_ROOT/tools/test/cells); do
79 + printf "%-12s %s\n" \
80 + "$([ $cell = $ONOS_CELL ] && echo $cell '*' || echo $cell)" \
81 + "$(grep '^#' $ONOS_ROOT/tools/test/cells/$cell | head -n 1)"
82 + done
77 } 83 }
78 84
79 # Miscellaneous 85 # Miscellaneous
......
1 +#!/bin/bash
2 +#------------------------------------------------------------------------------
3 +# Selectively builds only those projects that contained modified Java files.
4 +#------------------------------------------------------------------------------
5 +
6 +projects=$(find $ONOS_ROOT -name '*.java' \
7 + -not -path '*/openflowj/*' -and -not -path '.git/*' \
8 + -exec $ONOS_ROOT/tools/dev/bin/onos-build-selective-hook {} \; | \
9 + sort -u | sed "s:$ONOS_ROOT::g" | tr '\n' ',' | \
10 + sed 's:/,:,:g;s:,/:,:g;s:^/::g;s:,$::g')
11 +
12 +[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install}
...\ No newline at end of file ...\ No newline at end of file
1 +#------------------------------------------------------------------------------
2 +# Echoes project-level directory if a Java file within is newer than its
3 +# class file counterpart
4 +#------------------------------------------------------------------------------
5 +
6 +javaFile=${1#*\/src\/*\/java/}
7 +basename=${1/*\//}
8 +
9 +[ $basename = "package-info.java" ] && exit 0
10 +
11 +src=${1/$javaFile/}
12 +project=${src/src*/}
13 +classFile=${javaFile/.java/.class}
14 +
15 +[ ${project}target/classes/$classFile -nt ${src}$javaFile -o \
16 + ${project}target/test-classes/$classFile -nt ${src}$javaFile ] \
17 + || echo ${src/src*/}
18 +
1 -unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN ONOS_NIC
1 -# Default virtual box ONOS instances 1,2 & ONOS mininet box 1 +# Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box
2 -. $ONOS_ROOT/tools/test/cells/.reset
3 2
4 export ONOS_NIC=192.168.56.* 3 export ONOS_NIC=192.168.56.*
5 -
6 export OC1="192.168.56.101" 4 export OC1="192.168.56.101"
7 export OC2="192.168.56.102" 5 export OC2="192.168.56.102"
8 6
......
1 +# ProxMox-based cell of ONOS instance; no mininet-box
2 +
3 +export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue"
4 +
5 +export ONOS_NIC="10.128.4.*"
6 +export OC1="10.128.4.60"
7 +
1 # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box 1 # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box
2 -. $ONOS_ROOT/tools/test/cells/.reset
3 2
4 export ONOS_NIC="10.1.9.*" 3 export ONOS_NIC="10.1.9.*"
5 -
6 export OC1="10.1.9.94" 4 export OC1="10.1.9.94"
7 export OC2="10.1.9.82" 5 export OC2="10.1.9.82"
8 6
......
1 +# Local VirtualBox-based single ONOS instance & ONOS mininet box
2 +
3 +export ONOS_NIC=192.168.56.*
4 +export OC1="192.168.56.101"
5 +export OCN="192.168.56.103"
6 +
1 -# Default virtual box ONOS instances 1,2 & ONOS mininet box
2 -
3 -export ONOS_NIC=192.168.56.*
4 -
5 -export OC1="192.168.56.11"
6 -export OC2="192.168.56.12"
7 -
8 -export OCN="192.168.56.7"
9 -
10 -
1 +# Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box
2 +
3 +export ONOS_NIC=192.168.56.*
4 +export OC1="192.168.56.101"
5 +export OC2="192.168.56.102"
6 +export OC3="192.168.56.104"
7 +
8 +export OCN="192.168.56.103"
9 +
...@@ -55,6 +55,11 @@ ...@@ -55,6 +55,11 @@
55 <groupId>org.objenesis</groupId> 55 <groupId>org.objenesis</groupId>
56 <artifactId>objenesis</artifactId> 56 <artifactId>objenesis</artifactId>
57 </dependency> 57 </dependency>
58 + <dependency>
59 + <groupId>com.codahale.metrics</groupId>
60 + <artifactId>metrics-core</artifactId>
61 + <version>3.0.2</version>
62 + </dependency>
58 </dependencies> 63 </dependencies>
59 64
60 </project> 65 </project>
......
1 +package org.onlab.metrics;
2 +
3 +import java.util.concurrent.ConcurrentHashMap;
4 +import java.util.concurrent.ConcurrentMap;
5 +
6 +/**
7 + * Components to register for metrics.
8 + */
9 +public class MetricsComponent implements MetricsComponentRegistry {
10 + private final String name;
11 +
12 + /**
13 + * Registry to hold the Features defined in this Component.
14 + */
15 + private final ConcurrentMap<String, MetricsFeature> featuresRegistry =
16 + new ConcurrentHashMap<>();
17 +
18 + /**
19 + * Constructs a component from a name.
20 + *
21 + * @param newName name of the component
22 + */
23 + MetricsComponent(final String newName) {
24 + name = newName;
25 + }
26 +
27 + @Override public String getName() {
28 + return name;
29 + }
30 +
31 + @Override public MetricsFeature registerFeature(final String featureName) {
32 + MetricsFeature feature = featuresRegistry.get(featureName);
33 + if (feature == null) {
34 + final MetricsFeature createdFeature = new MetricsFeature(featureName);
35 + feature = featuresRegistry.putIfAbsent(featureName, createdFeature);
36 + if (feature == null) {
37 + feature = createdFeature;
38 + }
39 + }
40 + return feature;
41 + }
42 +}
1 +package org.onlab.metrics;
2 +
3 +/**
4 + * Registry Entry for Metrics Components.
5 + */
6 +public interface MetricsComponentRegistry {
7 + String getName();
8 +
9 + MetricsFeature registerFeature(String featureName);
10 +}
1 +package org.onlab.metrics;
2 +
3 +/**
4 + * Features to tag metrics.
5 + */
6 +public class MetricsFeature {
7 + private final String name;
8 +
9 + /**
10 + * Constructs a Feature from a name.
11 + *
12 + * @param newName name of the Feature
13 + */
14 + MetricsFeature(final String newName) {
15 + name = newName;
16 + }
17 +
18 + public String getName() {
19 + return name;
20 + }
21 +}
1 +package org.onlab.metrics;
2 +
3 +import java.util.Map;
4 +import java.util.concurrent.ConcurrentHashMap;
5 +import java.util.concurrent.ConcurrentMap;
6 +
7 +import com.codahale.metrics.Counter;
8 +import com.codahale.metrics.Gauge;
9 +import com.codahale.metrics.Histogram;
10 +import com.codahale.metrics.Meter;
11 +import com.codahale.metrics.Metric;
12 +import com.codahale.metrics.MetricFilter;
13 +import com.codahale.metrics.MetricRegistry;
14 +import com.codahale.metrics.Timer;
15 +
16 +/**
17 + * This class holds the Metrics registry for ONOS.
18 + * All metrics (Counter, Histogram, Timer, Meter, Gauge) use a hierarchical
19 + * string-based naming scheme: COMPONENT.FEATURE.NAME.
20 + * Example: "Topology.Counters.TopologyUpdates".
21 + * The COMPONENT and FEATURE names have to be registered in advance before
22 + * a metric can be created. Example:
23 + * <pre>
24 + * <code>
25 + * private final MetricsManager.MetricsComponent COMPONENT =
26 + * MetricsManager.registerComponent("Topology");
27 + * private final MetricsManager.MetricsFeature FEATURE =
28 + * COMPONENT.registerFeature("Counters");
29 + * private final Counter counterTopologyUpdates =
30 + * MetricsManager.createCounter(COMPONENT, FEATURE, "TopologyUpdates");
31 + * </code>
32 + * </pre>
33 + * Gauges are slightly different because they are not created directly in
34 + * this class, but are allocated by the caller and passed in for registration:
35 + * <pre>
36 + * <code>
37 + * private final Gauge<Long> gauge =
38 + * new {@literal Gauge<Long>}() {
39 + * {@literal @}Override
40 + * public Long getValue() {
41 + * return gaugeValue;
42 + * }
43 + * };
44 + * MetricsManager.registerMetric(COMPONENT, FEATURE, GAUGE_NAME, gauge);
45 + * </code>
46 + * </pre>
47 + */
48 +public final class MetricsManager implements MetricsService {
49 +
50 + /**
51 + * Registry to hold the Components defined in the system.
52 + */
53 + private ConcurrentMap<String, MetricsComponent> componentsRegistry =
54 + new ConcurrentHashMap<>();
55 +
56 + /**
57 + * Registry for the Metrics objects created in the system.
58 + */
59 + private final MetricRegistry metricsRegistry = new MetricRegistry();
60 +
61 + /**
62 + * Hide constructor. The only way to get the registry is through the
63 + * singleton getter.
64 + */
65 + private MetricsManager() {}
66 +
67 + /**
68 + * Registers a component.
69 + *
70 + * @param name name of the Component to register
71 + * @return MetricsComponent object that can be used to create Metrics.
72 + */
73 + @Override
74 + public MetricsComponent registerComponent(final String name) {
75 + MetricsComponent component = componentsRegistry.get(name);
76 + if (component == null) {
77 + final MetricsComponent createdComponent = new MetricsComponent(name);
78 + component = componentsRegistry.putIfAbsent(name, createdComponent);
79 + if (component == null) {
80 + component = createdComponent;
81 + }
82 + }
83 + return component;
84 + }
85 +
86 + /**
87 + * Generates a name for a Metric from its component and feature.
88 + *
89 + * @param component component the metric is defined in
90 + * @param feature feature the metric is defined in
91 + * @param metricName local name of the metric
92 + *
93 + * @return full name of the metric
94 + */
95 + private String generateName(final MetricsComponent component,
96 + final MetricsFeature feature,
97 + final String metricName) {
98 + return MetricRegistry.name(component.getName(),
99 + feature.getName(),
100 + metricName);
101 + }
102 +
103 + /**
104 + * Creates a Counter metric.
105 + *
106 + * @param component component the Counter is defined in
107 + * @param feature feature the Counter is defined in
108 + * @param metricName local name of the metric
109 + * @return the created Counter Meteric
110 + */
111 + @Override
112 + public Counter createCounter(final MetricsComponent component,
113 + final MetricsFeature feature,
114 + final String metricName) {
115 + final String name = generateName(component, feature, metricName);
116 + return metricsRegistry.counter(name);
117 + }
118 +
119 + /**
120 + * Creates a Histogram metric.
121 + *
122 + * @param component component the Histogram is defined in
123 + * @param feature feature the Histogram is defined in
124 + * @param metricName local name of the metric
125 + * @return the created Histogram Metric
126 + */
127 + @Override
128 + public Histogram createHistogram(final MetricsComponent component,
129 + final MetricsFeature feature,
130 + final String metricName) {
131 + final String name = generateName(component, feature, metricName);
132 + return metricsRegistry.histogram(name);
133 + }
134 +
135 + /**
136 + * Creates a Timer metric.
137 + *
138 + * @param component component the Timer is defined in
139 + * @param feature feature the Timeer is defined in
140 + * @param metricName local name of the metric
141 + * @return the created Timer Metric
142 + */
143 + @Override
144 + public Timer createTimer(final MetricsComponent component,
145 + final MetricsFeature feature,
146 + final String metricName) {
147 + final String name = generateName(component, feature, metricName);
148 + return metricsRegistry.timer(name);
149 + }
150 +
151 + /**
152 + * Creates a Meter metric.
153 + *
154 + * @param component component the Meter is defined in
155 + * @param feature feature the Meter is defined in
156 + * @param metricName local name of the metric
157 + * @return the created Meter Metric
158 + */
159 + @Override
160 + public Meter createMeter(final MetricsComponent component,
161 + final MetricsFeature feature,
162 + final String metricName) {
163 + final String name = generateName(component, feature, metricName);
164 + return metricsRegistry.meter(name);
165 + }
166 +
167 + /**
168 + * Registers an already created Metric. This is used for situation where a
169 + * caller needs to allocate its own Metric, but still register it with the
170 + * system.
171 + *
172 + * @param <T> Metric type
173 + * @param component component the Metric is defined in
174 + * @param feature feature the Metric is defined in
175 + * @param metricName local name of the metric
176 + * @param metric Metric to register
177 + * @return the registered Metric
178 + */
179 + @Override
180 + public <T extends Metric> T registerMetric(
181 + final MetricsComponent component,
182 + final MetricsFeature feature,
183 + final String metricName,
184 + final T metric) {
185 + final String name = generateName(component, feature, metricName);
186 + metricsRegistry.register(name, metric);
187 + return metric;
188 + }
189 +
190 + /**
191 + * Fetches the existing Timers.
192 + *
193 + * @param filter filter to use to select Timers
194 + * @return a map of the Timers that match the filter, with the key as the
195 + * name String to the Timer.
196 + */
197 + @Override
198 + public Map<String, Timer> getTimers(final MetricFilter filter) {
199 + return metricsRegistry.getTimers(filter);
200 + }
201 +
202 + /**
203 + * Fetches the existing Gauges.
204 + *
205 + * @param filter filter to use to select Gauges
206 + * @return a map of the Gauges that match the filter, with the key as the
207 + * name String to the Gauge.
208 + */
209 + @Override
210 + public Map<String, Gauge> getGauges(final MetricFilter filter) {
211 + return metricsRegistry.getGauges(filter);
212 + }
213 +
214 + /**
215 + * Fetches the existing Counters.
216 + *
217 + * @param filter filter to use to select Counters
218 + * @return a map of the Counters that match the filter, with the key as the
219 + * name String to the Counter.
220 + */
221 + @Override
222 + public Map<String, Counter> getCounters(final MetricFilter filter) {
223 + return metricsRegistry.getCounters(filter);
224 + }
225 +
226 + /**
227 + * Fetches the existing Meters.
228 + *
229 + * @param filter filter to use to select Meters
230 + * @return a map of the Meters that match the filter, with the key as the
231 + * name String to the Meter.
232 + */
233 + @Override
234 + public Map<String, Meter> getMeters(final MetricFilter filter) {
235 + return metricsRegistry.getMeters(filter);
236 + }
237 +
238 + /**
239 + * Fetches the existing Histograms.
240 + *
241 + * @param filter filter to use to select Histograms
242 + * @return a map of the Histograms that match the filter, with the key as the
243 + * name String to the Histogram.
244 + */
245 + @Override
246 + public Map<String, Histogram> getHistograms(final MetricFilter filter) {
247 + return metricsRegistry.getHistograms(filter);
248 + }
249 +
250 + /**
251 + * Removes all Metrics that match a given filter.
252 + *
253 + * @param filter filter to use to select the Metrics to remove.
254 + */
255 + @Override
256 + public void removeMatching(final MetricFilter filter) {
257 + metricsRegistry.removeMatching(filter);
258 + }
259 +}
260 +
1 +package org.onlab.metrics;
2 +
3 +import java.util.Map;
4 +
5 +import com.codahale.metrics.Counter;
6 +import com.codahale.metrics.Gauge;
7 +import com.codahale.metrics.Histogram;
8 +import com.codahale.metrics.Meter;
9 +import com.codahale.metrics.Metric;
10 +import com.codahale.metrics.MetricFilter;
11 +import com.codahale.metrics.Timer;
12 +
13 +/**
14 + * Metrics Service to collect metrics.
15 + */
16 +interface MetricsService {
17 +
18 + /**
19 + * Registers a component.
20 + *
21 + * @param name name of the Component to register
22 + * @return MetricsComponent object that can be used to create Metrics.
23 + */
24 + MetricsComponent registerComponent(String name);
25 +
26 + /**
27 + * Creates a Counter metric.
28 + *
29 + * @param component component the Counter is defined in
30 + * @param feature feature the Counter is defined in
31 + * @param metricName local name of the metric
32 + * @return the created Counter Meteric
33 + */
34 + Counter createCounter(MetricsComponent component,
35 + MetricsFeature feature,
36 + String metricName);
37 +
38 + /**
39 + * Creates a Histogram metric.
40 + *
41 + * @param component component the Histogram is defined in
42 + * @param feature feature the Histogram is defined in
43 + * @param metricName local name of the metric
44 + * @return the created Histogram Metric
45 + */
46 + Histogram createHistogram(MetricsComponent component,
47 + MetricsFeature feature,
48 + String metricName);
49 +
50 + /**
51 + * Creates a Timer metric.
52 + *
53 + * @param component component the Timer is defined in
54 + * @param feature feature the Timer is defined in
55 + * @param metricName local name of the metric
56 + * @return the created Timer Metric
57 + */
58 + Timer createTimer(MetricsComponent component,
59 + MetricsFeature feature,
60 + String metricName);
61 +
62 + /**
63 + * Creates a Meter metric.
64 + *
65 + * @param component component the Meter is defined in
66 + * @param feature feature the Meter is defined in
67 + * @param metricName local name of the metric
68 + * @return the created Meter Metric
69 + */
70 + Meter createMeter(MetricsComponent component,
71 + MetricsFeature feature,
72 + String metricName);
73 +
74 + /**
75 + * Registers an already created Metric. This is used for situation where a
76 + * caller needs to allocate its own Metric, but still register it with the
77 + * system.
78 + *
79 + * @param <T> Metric type
80 + * @param component component the Metric is defined in
81 + * @param feature feature the Metric is defined in
82 + * @param metricName local name of the metric
83 + * @param metric Metric to register
84 + * @return the registered Metric
85 + */
86 + <T extends Metric> T registerMetric(
87 + MetricsComponent component,
88 + MetricsFeature feature,
89 + String metricName,
90 + T metric);
91 +
92 + /**
93 + * Fetches the existing Timers.
94 + *
95 + * @param filter filter to use to select Timers
96 + * @return a map of the Timers that match the filter, with the key as the
97 + * name String to the Timer.
98 + */
99 + Map<String, Timer> getTimers(MetricFilter filter);
100 +
101 + /**
102 + * Fetches the existing Gauges.
103 + *
104 + * @param filter filter to use to select Gauges
105 + * @return a map of the Gauges that match the filter, with the key as the
106 + * name String to the Gauge.
107 + */
108 + Map<String, Gauge> getGauges(MetricFilter filter);
109 +
110 + /**
111 + * Fetches the existing Counters.
112 + *
113 + * @param filter filter to use to select Counters
114 + * @return a map of the Counters that match the filter, with the key as the
115 + * name String to the Counter.
116 + */
117 + Map<String, Counter> getCounters(MetricFilter filter);
118 +
119 + /**
120 + * Fetches the existing Meters.
121 + *
122 + * @param filter filter to use to select Meters
123 + * @return a map of the Meters that match the filter, with the key as the
124 + * name String to the Meter.
125 + */
126 + Map<String, Meter> getMeters(MetricFilter filter);
127 +
128 + /**
129 + * Fetches the existing Histograms.
130 + *
131 + * @param filter filter to use to select Histograms
132 + * @return a map of the Histograms that match the filter, with the key as the
133 + * name String to the Histogram.
134 + */
135 + Map<String, Histogram> getHistograms(MetricFilter filter);
136 + /**
137 + * Removes all Metrics that match a given filter.
138 + *
139 + * @param filter filter to use to select the Metrics to remove.
140 + */
141 + void removeMatching(MetricFilter filter);
142 +
143 +}
1 +/**
2 + * Misc utils for various performance metrics.
3 + */
4 +package org.onlab.metrics;
...@@ -180,6 +180,15 @@ public final class IpPrefix { ...@@ -180,6 +180,15 @@ public final class IpPrefix {
180 return address; 180 return address;
181 } 181 }
182 182
183 + public int toRealInt() {
184 + int val = 0;
185 + for (int i = 0; i < octets.length; i++) {
186 + val <<= 8;
187 + val |= octets[i] & 0xff;
188 + }
189 + return val;
190 + }
191 +
183 /** 192 /**
184 * Helper for computing the mask value from CIDR. 193 * Helper for computing the mask value from CIDR.
185 * 194 *
......
...@@ -239,12 +239,41 @@ public final class KryoPool { ...@@ -239,12 +239,41 @@ public final class KryoPool {
239 Kryo kryo = new Kryo(); 239 Kryo kryo = new Kryo();
240 kryo.setRegistrationRequired(registrationRequired); 240 kryo.setRegistrationRequired(registrationRequired);
241 for (Pair<Class<?>, Serializer<?>> registry : registeredTypes) { 241 for (Pair<Class<?>, Serializer<?>> registry : registeredTypes) {
242 - if (registry.getRight() == null) { 242 + final Serializer<?> serializer = registry.getRight();
243 + if (serializer == null) {
243 kryo.register(registry.getLeft()); 244 kryo.register(registry.getLeft());
244 } else { 245 } else {
245 - kryo.register(registry.getLeft(), registry.getRight()); 246 + kryo.register(registry.getLeft(), serializer);
247 + if (serializer instanceof FamilySerializer) {
248 + FamilySerializer<?> fser = (FamilySerializer<?>) serializer;
249 + fser.registerFamilies(kryo);
250 + }
246 } 251 }
247 } 252 }
248 return kryo; 253 return kryo;
249 } 254 }
255 +
256 + /**
257 + * Serializer implementation, which required registration of family of Classes.
258 + * @param <T> base type of this serializer.
259 + */
260 + public abstract static class FamilySerializer<T> extends Serializer<T> {
261 +
262 +
263 + public FamilySerializer(boolean acceptsNull) {
264 + super(acceptsNull);
265 + }
266 +
267 + public FamilySerializer(boolean acceptsNull, boolean immutable) {
268 + super(acceptsNull, immutable);
269 + }
270 +
271 + /**
272 + * Registers other classes this Serializer supports.
273 + *
274 + * @param kryo instance to register classes to
275 + */
276 + public void registerFamilies(Kryo kryo) {
277 + }
278 + }
250 } 279 }
......