Thomas Vachuska

Introducing concept of a physical or logical region to facilitate

support of geographically distributed cluster and to lay ground
for multiple/filtered topology layouts.

Added implementation of manager and store; unit-tests included.

Change-Id: Ia01673a0b711b8785c0ea68768552c2f61d7ea6d
Showing 22 changed files with 1678 additions and 83 deletions
......@@ -15,14 +15,19 @@
*/
package org.onosproject.cluster;
import java.util.Objects;
import org.onlab.util.Identifier;
/**
* Controller cluster identity.
*/
public class NodeId implements Comparable<NodeId> {
public final class NodeId extends Identifier<String> implements Comparable<NodeId> {
private final String id;
/**
* Constructor for serialization.
*/
private NodeId() {
super("");
}
/**
* Creates a new cluster node identifier from the specified string.
......@@ -30,34 +35,22 @@ public class NodeId implements Comparable<NodeId> {
* @param id string identifier
*/
public NodeId(String id) {
this.id = id;
super(id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof NodeId) {
final NodeId other = (NodeId) obj;
return Objects.equals(this.id, other.id);
}
return false;
}
@Override
public String toString() {
return id;
/**
* Creates a new cluster node identifier from the specified string.
*
* @param id string identifier
* @return node id
*/
public static NodeId nodeId(String id) {
return new NodeId(id);
}
@Override
public int compareTo(NodeId that) {
return this.id.compareTo(that.id);
return identifier.compareTo(that.identifier);
}
}
......
......@@ -16,21 +16,18 @@
package org.onosproject.net.key;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
import org.onlab.util.Identifier;
/**
* Device key Id definition.
* Device key identifier backed by a string value.
*/
public final class DeviceKeyId {
private final String identifier;
public final class DeviceKeyId extends Identifier<String> {
/**
* Constructor for serialization.
*/
private DeviceKeyId() {
this.identifier = null;
super();
}
/**
......@@ -39,63 +36,17 @@ public final class DeviceKeyId {
* @param value the underlying value of this ID
*/
private DeviceKeyId(String value) {
this.identifier = checkNotNull(value, "Device Key Id cannot be null.");
super(value);
}
/**
* Static method to construct a device key identifier.
* Creates a new device key identifier.
*
* @param id for the device key identifier
* @param id backing identifier value
* @return device key identifier
*/
public static final DeviceKeyId deviceKeyId(String id) {
public static DeviceKeyId deviceKeyId(String id) {
return new DeviceKeyId(id);
}
/**
* Returns the identifier of the device key identifier.
*
* @return identifier
*/
public String id() {
return identifier;
}
/**
* Returns the hashcode of the identifier.
*
* @return hashcode
*/
@Override
public int hashCode() {
return identifier.hashCode();
}
/**
* Compares two device key identifiers for equality.
*
* @param obj to compare against
* @return true if the objects are equal, false otherwise.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DeviceKeyId) {
final DeviceKeyId that = (DeviceKeyId) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.identifier, that.identifier);
}
return false;
}
/**
* Returns a string representation of a DeviceKeyId.
*
* @return string
*/
public String toString() {
return identifier;
}
}
......
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import org.onosproject.cluster.NodeId;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Default implementation of a region.
*/
public final class DefaultRegion implements Region {
private final RegionId id;
private final String name;
private final Type type;
private final List<Set<NodeId>> masters;
/**
* Creates a region using the supplied information.
*
* @param id region identifier
* @param name friendly name
* @param type region type
* @param masters list of sets of cluster node identifiers; in order of mastership
*/
public DefaultRegion(RegionId id, String name, Type type, List<Set<NodeId>> masters) {
this.id = id;
this.name = name;
this.type = type;
this.masters = masters != null ? ImmutableList.copyOf(masters) : ImmutableList.of();
}
@Override
public RegionId id() {
return id;
}
@Override
public String name() {
return name;
}
@Override
public Type type() {
return type;
}
@Override
public List<Set<NodeId>> masters() {
return masters;
}
@Override
public int hashCode() {
return Objects.hash(id, name, type, masters);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DefaultRegion) {
final DefaultRegion that = (DefaultRegion) obj;
return Objects.equals(this.id, that.id)
&& Objects.equals(this.name, that.name)
&& Objects.equals(this.type, that.type)
&& Objects.equals(this.masters, that.masters);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("name", name)
.add("type", type)
.add("masters", masters)
.toString();
}
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.cluster.NodeId;
import java.util.List;
import java.util.Set;
/**
* Representation of a group of devices located in a common physical or
* logical region. Optionally, devices in the region can share the same
* cluster nodes mastership affinities.
*/
public interface Region {
/**
* Coarse representation of the type of the region.
*/
enum Type {
/**
* Region represents an entire continent.
*/
CONTINENT,
/**
* Region represents an entire country.
*/
COUNTRY,
/**
* Region represents a metropolitan area.
*/
METRO,
/**
* Region represents a campus.
*/
CAMPUS,
/**
* Region represents a building.
*/
BUILDING,
/**
* Region represents a building floor.
*/
FLOOR,
/**
* Region represents a room.
*/
ROOM,
/**
* Region represents a rack.
*/
RACK,
/**
* Region represents a logical grouping.
*/
LOGICAL_GROUP
}
/**
* Returns the unique identifier of the region.
*
* @return region identifier
*/
RegionId id();
/**
* Returns the friendly region name that can be used for display purposes.
*
* @return friendly name of the region
*/
String name();
/**
* Returns the region type.
*
* @return region type
*/
Type type();
/**
* Returns the list of master node sets. The sets of cluster node identifiers
* should be listed in the order of preferred mastership. Nodes specified
* in each sets should be considered with equally priority and devices in
* the region can be balanced between them based on other criteria, e.g. load.
*
* @return list of preferred master node sets
*/
List<Set<NodeId>> masters();
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Service for interacting with inventory of network control regions.
*/
public interface RegionAdminService extends RegionService {
/**
* Creates a new region using the supplied data.
*
* @param regionId region identifier
* @param name friendly name
* @param type region type
* @param masterNodeIds list of sets of master nodes; null implies empty list
* @return new region descriptor
* @throws IllegalArgumentException if region already exists
*/
Region createRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds);
/**
* Update the specified region using the new set of data.
*
* @param regionId region identifier
* @param name friendly name
* @param type region type
* @param masterNodeIds list of sets of master nodes; null implies empty list
* @return new region descriptor
*/
Region updateRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds);
/**
* Removes the specified region using the new set of data.
*
* @param regionId region identifier
*/
void removeRegion(RegionId regionId);
/**
* Adds the specified collection of devices to the region.
*
* @param regionId region identifier
* @param deviceIds list of device identifiers
*/
void addDevices(RegionId regionId, Collection<DeviceId> deviceIds);
/**
* Removes the specified collection of devices from the region.
*
* @param regionId region identifier
* @param deviceIds list of device identifiers
*/
void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds);
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import com.google.common.collect.ImmutableSet;
import org.onosproject.event.AbstractEvent;
import org.onosproject.net.DeviceId;
import java.util.Set;
/**
* Describes region event.
*/
public class RegionEvent extends AbstractEvent<RegionEvent.Type, Region> {
private final Set<DeviceId> deviceIds;
public enum Type {
/**
* Signifies that a new region was created.
*/
REGION_ADDED,
/**
* Signifies that a region was updated.
*/
REGION_REMOVED,
/**
* Signifies that a region was removed.
*/
REGION_UPDATED,
/**
* Signifies that a region device membership has changed.
*/
REGION_MEMBERSHIP_CHANGED
}
/**
* Creates an event of a given type and for the specified region and the
* current time.
*
* @param type device event type
* @param region event region subject
*/
public RegionEvent(Type type, Region region) {
this(type, region, null);
}
/**
* Creates an event of a given type and for the specified region, device
* id list and the current time.
*
* @param type device event type
* @param region event region subject
* @param deviceIds optional set of device ids
*/
public RegionEvent(Type type, Region region, Set<DeviceId> deviceIds) {
super(type, region);
this.deviceIds = deviceIds != null ? ImmutableSet.copyOf(deviceIds) : ImmutableSet.of();
}
/**
* Creates an event of a given type and for the specified device and time.
*
* @param type device event type
* @param region event region subject
* @param deviceIds optional set of device ids
* @param time occurrence time
*/
public RegionEvent(Type type, Region region, Set<DeviceId> deviceIds, long time) {
super(type, region, time);
this.deviceIds = deviceIds != null ? ImmutableSet.copyOf(deviceIds) : ImmutableSet.of();
}
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onlab.util.Identifier;
/**
* Region identifier backed by a string value.
*/
public final class RegionId extends Identifier<String> {
/**
* Constructor for serialization.
*/
private RegionId() {
super();
}
/**
* Constructs the ID corresponding to a given string value.
*
* @param value the underlying value of this ID
*/
private RegionId(String value) {
super(value);
}
/**
* Creates a new region identifier.
*
* @param id backing identifier value
* @return region identifier
*/
public static RegionId regionId(String id) {
return new RegionId(id);
}
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.event.EventListener;
/**
* Entity capable of receiving region related events.
*/
public interface RegionListener extends EventListener<RegionEvent> {
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.net.DeviceId;
import java.util.Set;
/**
* Service for interacting with inventory of network control regions.
*/
public interface RegionService {
/**
* Returns set of all regions.
*
* @return set of regions
*/
Set<Region> getRegions();
/**
* Returns the region with the specified identifier.
*
* @param regionId region identifier
* @return region
* @throws org.onlab.util.ItemNotFoundException if region with given
* id does not exist
*/
Region getRegion(RegionId regionId);
/**
* Returns the region to which the specified device belongs.
*
* @param deviceId device identifier
* @return region or null if device does not belong to any region
*/
Region getRegionForDevice(DeviceId deviceId);
/**
* Returns the set of devices that belong to the specified region.
*
* @param regionId region identifier
* @return set of identifiers for devices in the given region
*/
Set<DeviceId> getRegionDevices(RegionId regionId);
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import org.onosproject.store.Store;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Manages inventory of regions of devices; not intended for direct use.
*/
public interface RegionStore extends Store<RegionEvent, RegionStoreDelegate> {
/**
* Returns set of all regions.
*
* @return set of regions
*/
Set<Region> getRegions();
/**
* Returns the region with the specified identifier.
*
* @param regionId region identifier
* @return region
* @throws org.onlab.util.ItemNotFoundException if region with given
* id does not exist
*/
Region getRegion(RegionId regionId);
/**
* Returns the region to which the specified device belongs.
*
* @param deviceId device identifier
* @return region or null if device does not belong to any region
*/
Region getRegionForDevice(DeviceId deviceId);
/**
* Returns the set of devices that belong to the specified region.
*
* @param regionId region identifier
* @return set of identifiers for devices in the given region
*/
Set<DeviceId> getRegionDevices(RegionId regionId);
/**
* Creates a new region using the supplied data.
*
* @param regionId region identifier
* @param name friendly name
* @param type region type
* @param masterNodeIds list of master nodes; null implies empty list
* @return new region descriptor
* @throws IllegalArgumentException if item already exists
*/
Region createRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds);
/**
* Updates the specified new region using the supplied data.
*
* @param regionId region identifier
* @param name friendly name
* @param type region type
* @param masterNodeIds list of master nodes; null implies empty list
* @return new region descriptor
* @throws IllegalArgumentException if item already exists
*/
Region updateRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds);
/**
* Removes the specified region using the new set of data.
*
* @param regionId region identifier
*/
void removeRegion(RegionId regionId);
/**
* Adds the specified collection of devices to the region.
*
* @param regionId region identifier
* @param deviceIds list of device identifiers
*/
void addDevices(RegionId regionId, Collection<DeviceId> deviceIds);
/**
* Removes the specified collection of devices from the region.
*
* @param regionId region identifier
* @param deviceIds list of device identifiers
*/
void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds);
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import org.onosproject.store.StoreDelegate;
/**
* Region store delegate abstraction.
*/
public interface RegionStoreDelegate extends StoreDelegate<RegionEvent> {
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Subsystem for tracking inventory of network control regions.
*/
package org.onosproject.net.region;
\ No newline at end of file
/*
* Copyright 2014-2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onosproject.cluster.NodeId;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.onosproject.cluster.NodeId.nodeId;
import static org.onosproject.net.region.Region.Type.METRO;
/**
* Suite of tests of the default region implementation.
*/
public class DefaultRegionTest {
private static final RegionId ID1 = RegionId.regionId("r1");
@Test
public void basics() {
ImmutableList<Set<NodeId>> masters = ImmutableList
.of(ImmutableSet.of(nodeId("n1"), nodeId("n2")),
ImmutableSet.of(nodeId("n3"), nodeId("n4")));
Region r = new DefaultRegion(ID1, "R1", METRO, masters);
assertEquals("incorrect id", ID1, r.id());
assertEquals("incorrect name", "R1", r.name());
assertEquals("incorrect type", METRO, r.type());
assertEquals("incorrect masters", masters, r.masters());
}
@Test
public void equality() {
Region a = new DefaultRegion(ID1, "R1", METRO, null);
Region b = new DefaultRegion(ID1, "R1", METRO, null);
Region c = new DefaultRegion(ID1, "R2", METRO, null);
new EqualsTester().addEqualityGroup(a, b).addEqualityGroup(c).testEquals();
}
}
\ No newline at end of file
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.cluster.NodeId;
import org.onosproject.event.AbstractListenerManager;
import org.onosproject.net.DeviceId;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionAdminService;
import org.onosproject.net.region.RegionEvent;
import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionListener;
import org.onosproject.net.region.RegionService;
import org.onosproject.net.region.RegionStore;
import org.onosproject.net.region.RegionStoreDelegate;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.of;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides implementation of the region service APIs.
*/
public class RegionManager extends AbstractListenerManager<RegionEvent, RegionListener>
implements RegionAdminService, RegionService {
private static final String REGION_ID_NULL = "Region ID cannot be null";
private static final String REGION_TYPE_NULL = "Region type cannot be null";
private static final String DEVICE_ID_NULL = "Device ID cannot be null";
private static final String DEVICE_IDS_NULL = "Device IDs cannot be null";
private static final String DEVICE_IDS_EMPTY = "Device IDs cannot be empty";
private static final String NAME_NULL = "Name cannot be null";
private final Logger log = getLogger(getClass());
private RegionStoreDelegate delegate = this::post;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RegionStore store;
@Activate
public void activate() {
store.setDelegate(delegate);
eventDispatcher.addSink(RegionEvent.class, listenerRegistry);
log.info("Started");
}
@Deactivate
public void deactivate() {
store.unsetDelegate(delegate);
eventDispatcher.removeSink(RegionEvent.class);
log.info("Stopped");
}
@Override
public Region createRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds) {
checkNotNull(regionId, REGION_ID_NULL);
checkNotNull(name, NAME_NULL);
checkNotNull(name, REGION_TYPE_NULL);
return store.createRegion(regionId, name, type, masterNodeIds == null ? of() : masterNodeIds);
}
@Override
public Region updateRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds) {
checkNotNull(regionId, REGION_ID_NULL);
checkNotNull(name, NAME_NULL);
checkNotNull(name, REGION_TYPE_NULL);
return store.updateRegion(regionId, name, type, masterNodeIds == null ? of() : masterNodeIds);
}
@Override
public void removeRegion(RegionId regionId) {
checkNotNull(regionId, REGION_ID_NULL);
store.removeRegion(regionId);
}
@Override
public void addDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
checkNotNull(regionId, REGION_ID_NULL);
checkNotNull(deviceIds, DEVICE_IDS_NULL);
checkState(!deviceIds.isEmpty(), DEVICE_IDS_EMPTY);
store.addDevices(regionId, deviceIds);
}
@Override
public void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
checkNotNull(regionId, REGION_ID_NULL);
checkNotNull(deviceIds, DEVICE_IDS_NULL);
checkState(!deviceIds.isEmpty(), DEVICE_IDS_EMPTY);
store.removeDevices(regionId, deviceIds);
}
@Override
public Set<Region> getRegions() {
return store.getRegions();
}
@Override
public Region getRegion(RegionId regionId) {
checkNotNull(regionId, REGION_ID_NULL);
return store.getRegion(regionId);
}
@Override
public Region getRegionForDevice(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
return store.getRegionForDevice(deviceId);
}
@Override
public Set<DeviceId> getRegionDevices(RegionId regionId) {
checkNotNull(regionId, REGION_ID_NULL);
return store.getRegionDevices(regionId);
}
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Core subsystem for managing region definitions.
*/
package org.onosproject.net.region.impl;
\ No newline at end of file
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.region.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.junit.TestUtils;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.cluster.NodeId;
import org.onosproject.common.event.impl.TestEventDispatcher;
import org.onosproject.net.DeviceId;
import org.onosproject.net.NetTestTools;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionAdminService;
import org.onosproject.net.region.RegionEvent;
import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionListener;
import org.onosproject.store.region.impl.DistributedRegionStore;
import org.onosproject.store.service.TestStorageService;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.*;
import static org.onosproject.net.region.Region.Type.*;
import static org.onosproject.net.region.RegionEvent.Type.*;
/**
* Tests of the region service implementation.
*/
public class RegionManagerTest {
private static final RegionId RID1 = RegionId.regionId("r1");
private static final RegionId RID2 = RegionId.regionId("r2");
private static final DeviceId DID1 = DeviceId.deviceId("foo:d1");
private static final DeviceId DID2 = DeviceId.deviceId("foo:d2");
private static final DeviceId DID3 = DeviceId.deviceId("foo:d3");
private static final NodeId NID1 = NodeId.nodeId("n1");
private static final List<Set<NodeId>> MASTERS = ImmutableList.of(ImmutableSet.of(NID1));
private TestManager manager = new TestManager();
private RegionAdminService service;
private TestStore store = new TestStore();
private TestListener listener = new TestListener();
@Before
public void setUp() throws Exception {
TestUtils.setField(store, "storageService", new TestStorageService());
store.activate();
manager.store = store;
manager.addListener(listener);
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
service = manager;
}
@After
public void tearDown() {
store.deactivate();
manager.removeListener(listener);
manager.deactivate();
NetTestTools.injectEventDispatcher(manager, null);
}
@Test
public void basics() {
Region r1 = service.createRegion(RID1, "R1", METRO, MASTERS);
assertEquals("incorrect id", RID1, r1.id());
assertEquals("incorrect event", REGION_ADDED, listener.event.type());
Region r2 = service.createRegion(RID2, "R2", CAMPUS, MASTERS);
assertEquals("incorrect id", RID2, r2.id());
assertEquals("incorrect type", CAMPUS, r2.type());
assertEquals("incorrect event", REGION_ADDED, listener.event.type());
r2 = service.updateRegion(RID2, "R2", COUNTRY, MASTERS);
assertEquals("incorrect type", COUNTRY, r2.type());
assertEquals("incorrect event", REGION_UPDATED, listener.event.type());
Set<Region> regions = service.getRegions();
assertEquals("incorrect size", 2, regions.size());
assertTrue("missing r1", regions.contains(r1));
assertTrue("missing r2", regions.contains(r2));
r1 = service.getRegion(RID1);
assertEquals("incorrect id", RID1, r1.id());
service.removeRegion(RID1);
regions = service.getRegions();
assertEquals("incorrect size", 1, regions.size());
assertTrue("missing r2", regions.contains(r2));
assertEquals("incorrect event", REGION_REMOVED, listener.event.type());
}
@Test(expected = IllegalArgumentException.class)
public void duplicateCreate() {
service.createRegion(RID1, "R1", METRO, MASTERS);
service.createRegion(RID1, "R2", CAMPUS, MASTERS);
}
@Test(expected = ItemNotFoundException.class)
public void missingUpdate() {
service.updateRegion(RID1, "R1", METRO, MASTERS);
}
@Test
public void membership() {
Region r = service.createRegion(RID1, "R1", METRO, MASTERS);
assertTrue("no devices expected", service.getRegionDevices(RID1).isEmpty());
assertNull("no region expected", service.getRegionForDevice(DID1));
service.addDevices(RID1, ImmutableSet.of(DID1, DID2));
Set<DeviceId> deviceIds = service.getRegionDevices(RID1);
assertEquals("incorrect device count", 2, deviceIds.size());
assertTrue("missing d1", deviceIds.contains(DID1));
assertTrue("missing d2", deviceIds.contains(DID2));
assertEquals("wrong region", r, service.getRegionForDevice(DID1));
assertEquals("incorrect event", REGION_MEMBERSHIP_CHANGED, listener.event.type());
service.addDevices(RID1, ImmutableSet.of(DID3));
deviceIds = service.getRegionDevices(RID1);
assertEquals("incorrect device count", 3, deviceIds.size());
assertTrue("missing d3", deviceIds.contains(DID3));
assertEquals("incorrect event", REGION_MEMBERSHIP_CHANGED, listener.event.type());
service.addDevices(RID1, ImmutableSet.of(DID3, DID1));
deviceIds = service.getRegionDevices(RID1);
assertEquals("incorrect device count", 3, deviceIds.size());
service.removeDevices(RID1, ImmutableSet.of(DID2, DID3));
deviceIds = service.getRegionDevices(RID1);
assertEquals("incorrect device count", 1, deviceIds.size());
assertTrue("missing d1", deviceIds.contains(DID1));
service.removeDevices(RID1, ImmutableSet.of(DID1, DID3));
assertTrue("no devices expected", service.getRegionDevices(RID1).isEmpty());
service.removeDevices(RID1, ImmutableSet.of(DID2));
assertTrue("no devices expected", service.getRegionDevices(RID1).isEmpty());
}
private class TestStore extends DistributedRegionStore {
@Override
protected void activate() {
super.activate();
}
@Override
protected void deactivate() {
super.deactivate();
}
}
private class TestManager extends RegionManager {
TestManager() {
eventDispatcher = new TestEventDispatcher();
}
}
private class TestListener implements RegionListener {
RegionEvent event;
@Override
public void event(RegionEvent event) {
this.event = event;
}
}
}
\ No newline at end of file
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.store.region.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.util.Identifier;
import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.region.DefaultRegion;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionEvent;
import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionStore;
import org.onosproject.net.region.RegionStoreDelegate;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.nullIsNotFound;
import static org.onosproject.net.region.RegionEvent.Type.REGION_MEMBERSHIP_CHANGED;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Consistent store implementation for tracking region definitions and device
* region affiliation.
*/
@Component(immediate = true)
@Service
public class DistributedRegionStore
extends AbstractStore<RegionEvent, RegionStoreDelegate>
implements RegionStore {
private static final String NO_REGION = "Region does not exist";
private static final String DUPLICATE_REGION = "Region already exists";
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
private ConsistentMap<RegionId, Region> regionsRepo;
private Map<RegionId, Region> regionsById;
private ConsistentMap<RegionId, Set<DeviceId>> membershipRepo;
private Map<RegionId, Set<DeviceId>> regionDevices;
private Map<DeviceId, Region> regionsByDevice = new HashMap<>();
private final MapEventListener<RegionId, Region> listener =
new InternalRegionListener();
private final MapEventListener<RegionId, Set<DeviceId>> membershipListener =
new InternalMembershipListener();
@Activate
protected void activate() {
Serializer serializer =
Serializer.using(Arrays.asList(KryoNamespaces.API),
Identifier.class,
RegionId.class,
Region.class,
DefaultRegion.class,
Region.Type.class);
regionsRepo = storageService.<RegionId, Region>consistentMapBuilder()
.withSerializer(serializer)
.withName("onos-regions")
.withRelaxedReadConsistency()
.build();
regionsRepo.addListener(listener);
regionsById = regionsRepo.asJavaMap();
membershipRepo = storageService.<RegionId, Set<DeviceId>>consistentMapBuilder()
.withSerializer(serializer)
.withName("onos-region-devices")
.withRelaxedReadConsistency()
.build();
membershipRepo.addListener(membershipListener);
regionDevices = membershipRepo.asJavaMap();
log.info("Started");
}
@Deactivate
protected void deactivate() {
regionsRepo.removeListener(listener);
membershipRepo.removeListener(membershipListener);
regionsByDevice.clear();
log.info("Stopped");
}
@Override
public Set<Region> getRegions() {
return ImmutableSet.copyOf(regionsById.values());
}
@Override
public Region getRegion(RegionId regionId) {
return nullIsNotFound(regionsById.get(regionId), NO_REGION);
}
@Override
public Region getRegionForDevice(DeviceId deviceId) {
return regionsByDevice.get(deviceId);
}
@Override
public Set<DeviceId> getRegionDevices(RegionId regionId) {
Set<DeviceId> deviceIds = regionDevices.get(regionId);
return deviceIds != null ? ImmutableSet.copyOf(deviceIds) : ImmutableSet.of();
}
@Override
public Region createRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds) {
return regionsRepo.compute(regionId, (id, region) -> {
checkArgument(region == null, DUPLICATE_REGION);
return new DefaultRegion(regionId, name, type, masterNodeIds);
}).value();
}
@Override
public Region updateRegion(RegionId regionId, String name, Region.Type type,
List<Set<NodeId>> masterNodeIds) {
return regionsRepo.compute(regionId, (id, region) -> {
nullIsNotFound(region, NO_REGION);
return new DefaultRegion(regionId, name, type, masterNodeIds);
}).value();
}
@Override
public void removeRegion(RegionId regionId) {
membershipRepo.remove(regionId);
regionsRepo.remove(regionId);
}
@Override
public void addDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
membershipRepo.compute(regionId, (id, existingDevices) -> {
if (existingDevices == null) {
return ImmutableSet.copyOf(deviceIds);
} else if (!existingDevices.containsAll(deviceIds)) {
return ImmutableSet.<DeviceId>builder()
.addAll(existingDevices)
.addAll(deviceIds)
.build();
} else {
return existingDevices;
}
});
Region region = regionsById.get(regionId);
deviceIds.forEach(deviceId -> regionsByDevice.put(deviceId, region));
}
@Override
public void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
membershipRepo.compute(regionId, (id, existingDevices) -> {
if (existingDevices == null || existingDevices.isEmpty()) {
return ImmutableSet.of();
} else {
return ImmutableSet.<DeviceId>builder()
.addAll(Sets.difference(existingDevices,
ImmutableSet.copyOf(deviceIds)))
.build();
}
});
deviceIds.forEach(deviceId -> regionsByDevice.remove(deviceId));
}
/**
* Listener class to map listener events to the region inventory events.
*/
private class InternalRegionListener implements MapEventListener<RegionId, Region> {
@Override
public void event(MapEvent<RegionId, Region> event) {
Region region = null;
RegionEvent.Type type = null;
switch (event.type()) {
case INSERT:
type = RegionEvent.Type.REGION_ADDED;
region = checkNotNull(event.newValue().value());
break;
case UPDATE:
type = RegionEvent.Type.REGION_UPDATED;
region = checkNotNull(event.newValue().value());
break;
case REMOVE:
type = RegionEvent.Type.REGION_REMOVED;
region = checkNotNull(event.oldValue().value());
break;
default:
log.error("Unsupported event type: " + event.type());
}
notifyDelegate(new RegionEvent(type, region));
}
}
/**
* Listener class to map listener events to the region membership events.
*/
private class InternalMembershipListener implements MapEventListener<RegionId, Set<DeviceId>> {
@Override
public void event(MapEvent<RegionId, Set<DeviceId>> event) {
if (event.type() != MapEvent.Type.REMOVE) {
notifyDelegate(new RegionEvent(REGION_MEMBERSHIP_CHANGED,
regionsById.get(event.key()),
event.newValue().value()));
}
}
}
}
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A distributed store implementation for tracking region definitions
* consistently across the cluster.
*/
package org.onosproject.store.region.impl;
\ No newline at end of file
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.store.region.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionEvent;
import org.onosproject.net.region.RegionId;
import org.onosproject.store.service.TestStorageService;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.*;
import static org.onosproject.net.region.Region.Type.*;
import static org.onosproject.net.region.RegionEvent.Type.*;
/**
* Test of the distributed region store implementation.
*/
public class DistributedRegionStoreTest {
private static final RegionId RID1 = RegionId.regionId("r1");
private static final RegionId RID2 = RegionId.regionId("r2");
private static final DeviceId DID1 = DeviceId.deviceId("foo:d1");
private static final DeviceId DID2 = DeviceId.deviceId("foo:d2");
private static final DeviceId DID3 = DeviceId.deviceId("foo:d3");
private static final NodeId NID1 = NodeId.nodeId("n1");
private static final List<Set<NodeId>> MASTERS = ImmutableList.of(ImmutableSet.of(NID1));
private TestStore store;
private RegionEvent event;
/**
* Sets up the device key store and the storage service test harness.
*/
@Before
public void setUp() {
store = new TestStore();
store.storageService = new TestStorageService();
store.setDelegate(e -> this.event = e);
store.activate();
}
/**
* Tears down the device key store.
*/
@After
public void tearDown() {
store.deactivate();
}
@Test
public void basics() {
Region r1 = store.createRegion(RID1, "R1", METRO, MASTERS);
assertEquals("incorrect id", RID1, r1.id());
assertEquals("incorrect event", REGION_ADDED, event.type());
Region r2 = store.createRegion(RID2, "R2", CAMPUS, MASTERS);
assertEquals("incorrect id", RID2, r2.id());
assertEquals("incorrect type", CAMPUS, r2.type());
assertEquals("incorrect event", REGION_ADDED, event.type());
r2 = store.updateRegion(RID2, "R2", COUNTRY, MASTERS);
assertEquals("incorrect type", COUNTRY, r2.type());
assertEquals("incorrect event", REGION_UPDATED, event.type());
Set<Region> regions = store.getRegions();
assertEquals("incorrect size", 2, regions.size());
assertTrue("missing r1", regions.contains(r1));
assertTrue("missing r2", regions.contains(r2));
r1 = store.getRegion(RID1);
assertEquals("incorrect id", RID1, r1.id());
store.removeRegion(RID1);
regions = store.getRegions();
assertEquals("incorrect size", 1, regions.size());
assertTrue("missing r2", regions.contains(r2));
assertEquals("incorrect event", REGION_REMOVED, event.type());
}
@Test(expected = IllegalArgumentException.class)
public void duplicateCreate() {
store.createRegion(RID1, "R1", METRO, MASTERS);
store.createRegion(RID1, "R2", CAMPUS, MASTERS);
}
@Test(expected = ItemNotFoundException.class)
public void missingUpdate() {
store.updateRegion(RID1, "R1", METRO, MASTERS);
}
@Test
public void membership() {
Region r = store.createRegion(RID1, "R1", METRO, MASTERS);
assertTrue("no devices expected", store.getRegionDevices(RID1).isEmpty());
assertNull("no region expected", store.getRegionForDevice(DID1));
store.addDevices(RID1, ImmutableSet.of(DID1, DID2));
Set<DeviceId> deviceIds = store.getRegionDevices(RID1);
assertEquals("incorrect device count", 2, deviceIds.size());
assertTrue("missing d1", deviceIds.contains(DID1));
assertTrue("missing d2", deviceIds.contains(DID2));
assertEquals("wrong region", r, store.getRegionForDevice(DID1));
store.addDevices(RID1, ImmutableSet.of(DID3));
deviceIds = store.getRegionDevices(RID1);
assertEquals("incorrect device count", 3, deviceIds.size());
assertTrue("missing d3", deviceIds.contains(DID3));
store.addDevices(RID1, ImmutableSet.of(DID3, DID1));
deviceIds = store.getRegionDevices(RID1);
assertEquals("incorrect device count", 3, deviceIds.size());
store.removeDevices(RID1, ImmutableSet.of(DID2, DID3));
deviceIds = store.getRegionDevices(RID1);
assertEquals("incorrect device count", 1, deviceIds.size());
assertTrue("missing d1", deviceIds.contains(DID1));
store.removeDevices(RID1, ImmutableSet.of(DID1, DID3));
assertTrue("no devices expected", store.getRegionDevices(RID1).isEmpty());
store.removeDevices(RID1, ImmutableSet.of(DID2));
assertTrue("no devices expected", store.getRegionDevices(RID1).isEmpty());
}
class TestStore extends DistributedRegionStore {
}
}
\ No newline at end of file
Copyright 2014-$today.year Open Networking Laboratory
Copyright $today.year Open Networking Laboratory
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......
/*
* Copyright 2014-2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.util;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstract identifier backed by another value, e.g. string, int.
*/
public class Identifier<T> {
protected final T identifier; // backing identifier value
/**
* Constructor for serialization.
*/
protected Identifier() {
this.identifier = null;
}
/**
* Constructs an identifier backed by the specified value.
*
* @param value the backing value
*/
protected Identifier(T value) {
this.identifier = checkNotNull(value, "Identifier cannot be null.");
}
/**
* Returns the backing identifier value.
*
* @return identifier
*/
public T id() {
return identifier;
}
/**
* Returns the hashcode of the identifier.
*
* @return hashcode
*/
@Override
public int hashCode() {
return identifier.hashCode();
}
/**
* Compares two device key identifiers for equality.
*
* @param obj to compare against
* @return true if the objects are equal, false otherwise.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Identifier) {
Identifier that = (Identifier) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.identifier, that.identifier);
}
return false;
}
/**
* Returns a string representation of a DeviceKeyId.
*
* @return string
*/
public String toString() {
return identifier.toString();
}
}
/*
* Copyright 2014-2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.util;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test of the base identifier.
*/
public class IdentifierTest {
@Test
public void basics() {
FooId id = new FooId(123);
assertEquals("incorrect value", 123, (int) id.id());
}
@Test
public void equality() {
FooId a = new FooId(1);
FooId b = new FooId(1);
FooId c = new FooId(2);
new EqualsTester().addEqualityGroup(a, b).addEqualityGroup(c).testEquals();
}
static class FooId extends Identifier<Integer> {
FooId(int id) {
super(id);
}
}
}
\ No newline at end of file