Simon Hunt
Committed by Gerrit Code Review

ONOS-4326: Focusing on add/remove cluster member. (WIP).

If reviewing this, please refer to http://tinyurl.com/onos-ui-topo-model

Change-Id: Ic6568074ac768ec828f9103e92caab5e9a06ade6
/*
* Copyright 2016-present 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.ui.model;
import org.onosproject.cluster.ClusterService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.region.RegionService;
/**
* A bundle of services to pass to elements that might need a reference to them.
*/
public interface ServiceBundle {
/**
* Reference to a cluster service implementation.
*
* @return cluster service
*/
ClusterService cluster();
/**
* Reference to a mastership service implementation.
*
* @return mastership service
*/
MastershipService mastership();
/**
* Reference to a region service implementation.
*
* @return region service
*/
RegionService region();
/**
* Reference to a device service implementation.
*
* @return device service
*/
DeviceService device();
/**
* Reference to a link service implementation.
*
* @return link service
*/
LinkService link();
/**
* Reference to a host service implementation.
*
* @return host service
*/
HostService host();
/**
* Reference to a intent service implementation.
*
* @return intent service
*/
IntentService intent();
/**
* Reference to a flow service implementation.
*
* @return flow service
*/
FlowRuleService flow();
}
......@@ -28,12 +28,14 @@ import java.util.Map;
*/
class UiCluster extends UiElement {
private static final String DEFAULT_CLUSTER_ID = "CLUSTER-0";
private final List<UiClusterMember> members = new ArrayList<>();
private final Map<NodeId, UiClusterMember> lookup = new HashMap<>();
@Override
public String toString() {
return String.valueOf(members.size()) + "-member cluster";
return String.valueOf(size()) + "-member cluster";
}
/**
......@@ -65,6 +67,16 @@ class UiCluster extends UiElement {
}
/**
* Removes the given member from the cluster.
*
* @param member member to remove
*/
public void remove(UiClusterMember member) {
members.remove(member);
lookup.remove(member.id());
}
/**
* Returns the number of members in the cluster.
*
* @return number of members
......@@ -72,4 +84,9 @@ class UiCluster extends UiElement {
public int size() {
return members.size();
}
@Override
public String idAsString() {
return DEFAULT_CLUSTER_ID;
}
}
......
......@@ -16,9 +16,12 @@
package org.onosproject.ui.model.topo;
import org.onlab.packet.IpAddress;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import static org.onosproject.cluster.ControllerNode.State.INACTIVE;
/**
* Represents an individual member of the cluster (ONOS instance).
*/
......@@ -26,6 +29,9 @@ public class UiClusterMember extends UiElement {
private final ControllerNode cnode;
private int deviceCount = 0;
private ControllerNode.State state = INACTIVE;
/**
* Constructs a cluster member, with a reference to the specified
* controller node instance.
......@@ -36,13 +42,33 @@ public class UiClusterMember extends UiElement {
this.cnode = cnode;
}
@Override
public String toString() {
return "UiClusterMember{" + cnode +
", online=" + isOnline() +
", ready=" + isReady() +
", #devices=" + deviceCount +
"}";
}
/**
* Updates the information about this cluster member.
* Sets the state of this cluster member.
*
* @param cnode underlying controller node
* @param state the state
*/
public void update(ControllerNode cnode) {
// TODO: update our information cache appropriately
public void setState(ControllerNode.State state) {
this.state = state;
}
/**
* Sets the number of devices for which this cluster member is master.
*
* @param deviceCount number of devices
*/
public void setDeviceCount(int deviceCount) {
this.deviceCount = deviceCount;
}
/**
......@@ -53,4 +79,45 @@ public class UiClusterMember extends UiElement {
public NodeId id() {
return cnode.id();
}
/**
* Returns the IP address of the cluster member.
*
* @return the IP address
*/
public IpAddress ip() {
return cnode.ip();
}
/**
* Returns true if this cluster member is online (active).
*
* @return true if online, false otherwise
*/
public boolean isOnline() {
return state.isActive();
}
/**
* Returns true if this cluster member is considered ready.
*
* @return true if ready, false otherwise
*/
public boolean isReady() {
return state.isReady();
}
/**
* Returns the number of devices for which this cluster member is master.
*
* @return number of devices for which this member is master
*/
public int deviceCount() {
return deviceCount;
}
@Override
public String idAsString() {
return id().toString();
}
}
......
......@@ -17,6 +17,7 @@
package org.onosproject.ui.model.topo;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
/**
* Represents a device.
......@@ -29,4 +30,18 @@ public class UiDevice extends UiNode {
protected void destroy() {
device = null;
}
/**
* Returns the identity of the device.
*
* @return device ID
*/
public DeviceId id() {
return device.id();
}
@Override
public String idAsString() {
return id().toString();
}
}
......
......@@ -19,7 +19,7 @@ package org.onosproject.ui.model.topo;
/**
* Abstract base class of all elements in the UI topology model.
*/
public class UiElement {
public abstract class UiElement {
/**
* Removes all external references, and prepares the instance for
......@@ -28,4 +28,11 @@ public class UiElement {
protected void destroy() {
// does nothing
}
/**
* Returns a string representation of the element identifier.
*
* @return the element unique identifier
*/
public abstract String idAsString();
}
......
......@@ -17,6 +17,7 @@
package org.onosproject.ui.model.topo;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
/**
* Represents an end-station host.
......@@ -29,4 +30,18 @@ public class UiHost extends UiNode {
protected void destroy() {
host = null;
}
/**
* Returns the identity of the host.
*
* @return host ID
*/
public HostId id() {
return host.id();
}
@Override
public String idAsString() {
return id().toString();
}
}
......
......@@ -54,4 +54,10 @@ public class UiLink extends UiElement {
children = null;
}
}
@Override
public String idAsString() {
// TODO
return null;
}
}
......
......@@ -17,6 +17,7 @@
package org.onosproject.ui.model.topo;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionId;
import java.util.Set;
import java.util.TreeSet;
......@@ -46,4 +47,17 @@ public class UiRegion extends UiNode {
region = null;
}
/**
* Returns the identity of the region.
*
* @return region ID
*/
public RegionId id() {
return region.id();
}
@Override
public String idAsString() {
return id().toString();
}
}
......
......@@ -28,6 +28,8 @@ import java.util.TreeSet;
*/
public class UiTopology extends UiElement {
private static final String DEFAULT_TOPOLOGY_ID = "TOPOLOGY-0";
private static final Logger log = LoggerFactory.getLogger(UiTopology.class);
private final UiCluster uiCluster = new UiCluster();
......@@ -69,6 +71,15 @@ public class UiTopology extends UiElement {
}
/**
* Removes the given cluster member from the topology model.
*
* @param member cluster member to remove
*/
public void remove(UiClusterMember member) {
uiCluster.remove(member);
}
/**
* Returns the number of members in the cluster.
*
* @return number of cluster members
......@@ -85,4 +96,9 @@ public class UiTopology extends UiElement {
public int regionCount() {
return uiRegions.size();
}
@Override
public String idAsString() {
return DEFAULT_TOPOLOGY_ID;
}
}
......
/*
* Copyright 2016-present 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.ui;
/**
* Abstract base class for UI tests.
*/
public abstract class AbstractUiTest {
/**
* System agnostic end-of-line character.
*/
protected static final String EOL = String.format("%n");
/**
* Prints the given string to stdout.
*
* @param s string to print
*/
protected static void print(String s) {
System.out.println(s);
}
/**
* Prints the toString() of the given object to stdout.
*
* @param o object to print
*/
protected static void print(Object o) {
if (o == null) {
print("<null>");
} else {
print(o.toString());
}
}
/**
* Prints the formatted string to stdout.
*
* @param fmt format string
* @param params parameters
* @see String#format(String, Object...)
*/
protected static void print(String fmt, Object... params) {
print(String.format(fmt, params));
}
/**
* Prints a title, to delimit individual unit test output.
*
* @param s a title for the test
*/
protected static void title(String s) {
print(EOL + "=== %s ===", s);
}
}
/*
* Copyright 2016-present 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.ui.model;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ClusterServiceAdapter;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.region.RegionService;
import org.onosproject.ui.AbstractUiTest;
/**
* Base class for UI model unit tests.
*/
public class AbstractUiModelTest extends AbstractUiTest {
/**
* Returns canned results.
* At some future point, we may make this "programmable", so that
* it returns certain values based on element IDs etc.
*/
protected static final ServiceBundle MOCK_SERVICES =
new ServiceBundle() {
@Override
public ClusterService cluster() {
return MOCK_CLUSTER;
}
@Override
public MastershipService mastership() {
return null;
}
@Override
public RegionService region() {
return null;
}
@Override
public DeviceService device() {
return null;
}
@Override
public LinkService link() {
return null;
}
@Override
public HostService host() {
return null;
}
@Override
public IntentService intent() {
return null;
}
@Override
public FlowRuleService flow() {
return null;
}
};
private static final ClusterService MOCK_CLUSTER = new MockClusterService();
private static class MockClusterService extends ClusterServiceAdapter {
@Override
public ControllerNode.State getState(NodeId nodeId) {
// For now, a hardcoded state of ACTIVE (but not READY)
// irrespective of the node ID.
return ControllerNode.State.ACTIVE;
}
}
}
/*
* Copyright 2016-present 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.ui.model.topo;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.DefaultControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.ui.model.AbstractUiModelTest;
import static org.junit.Assert.assertEquals;
/**
* Unit tests for {@link UiClusterMember}.
*/
public class UiClusterMemberTest extends AbstractUiModelTest {
private static final NodeId NODE_ID = NodeId.nodeId("Node-1");
private static final IpAddress NODE_IP = IpAddress.valueOf("1.2.3.4");
private static final ControllerNode CNODE_1 =
new DefaultControllerNode(NODE_ID, NODE_IP);
private UiClusterMember member;
@Test
public void basic() {
title("basic");
member = new UiClusterMember(CNODE_1);
print(member);
assertEquals("wrong id", NODE_ID, member.id());
assertEquals("wrong IP", NODE_IP, member.ip());
assertEquals("unex. online", false, member.isOnline());
assertEquals("unex. ready", false, member.isReady());
assertEquals("unex. device count", 0, member.deviceCount());
}
}
......@@ -17,6 +17,7 @@
package org.onosproject.ui.impl.topo.model;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.cluster.RoleInfo;
import org.onosproject.event.EventDispatcher;
import org.onosproject.net.Device;
......@@ -24,11 +25,16 @@ import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
import org.onosproject.net.region.Region;
import org.onosproject.ui.model.ServiceBundle;
import org.onosproject.ui.model.topo.UiClusterMember;
import org.onosproject.ui.model.topo.UiDevice;
import org.onosproject.ui.model.topo.UiTopology;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED;
import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED;
/**
......@@ -36,10 +42,14 @@ import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVE
*/
class ModelCache {
private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
private final ServiceBundle services;
private final EventDispatcher dispatcher;
private final UiTopology uiTopology = new UiTopology();
ModelCache(EventDispatcher eventDispatcher) {
ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
this.services = services;
this.dispatcher = eventDispatcher;
}
......@@ -59,6 +69,7 @@ class ModelCache {
* Create our internal model of the global topology.
*/
void load() {
// TODO - implement loading of initial state
// loadClusterMembers();
// loadRegions();
// loadDevices();
......@@ -74,20 +85,36 @@ class ModelCache {
* @param cnode controller node to be added/updated
*/
void addOrUpdateClusterMember(ControllerNode cnode) {
UiClusterMember member = uiTopology.findClusterMember(cnode.id());
if (member != null) {
member.update(cnode);
} else {
NodeId id = cnode.id();
UiClusterMember member = uiTopology.findClusterMember(id);
if (member == null) {
member = new UiClusterMember(cnode);
uiTopology.add(member);
}
// TODO: post event
// inject computed data about the cluster node, into the model object
ControllerNode.State state = services.cluster().getState(id);
member.setState(state);
member.setDeviceCount(services.mastership().getDevicesOf(id).size());
// NOTE: UI-attached is session-based data, not global
dispatcher.post(new UiModelEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member));
}
/**
* Removes from the model the specified controller node.
*
* @param cnode controller node to be removed
*/
void removeClusterMember(ControllerNode cnode) {
// TODO: find cluster member assoc. with parameter; remove from model
// TODO: post event
NodeId id = cnode.id();
UiClusterMember member = uiTopology.findClusterMember(id);
if (member != null) {
uiTopology.remove(member);
dispatcher.post(new UiModelEvent(CLUSTER_MEMBER_REMOVED, member));
} else {
log.warn("Tried to remove non-member cluster node {}", id);
}
}
void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
......@@ -111,7 +138,7 @@ class ModelCache {
UiDevice uiDevice = new UiDevice();
// TODO: post the (correct) event
dispatcher.post(new UiModelEvent(DEVICE_ADDED, uiDevice));
dispatcher.post(new UiModelEvent(DEVICE_ADDED_OR_UPDATED, uiDevice));
}
void removeDevice(Device device) {
......
......@@ -29,8 +29,12 @@ public class UiModelEvent extends AbstractEvent<UiModelEvent.Type, UiElement> {
}
enum Type {
DEVICE_ADDED,
CLUSTER_MEMBER_ADDED_OR_UPDATED,
CLUSTER_MEMBER_REMOVED,
DEVICE_ADDED_OR_UPDATED,
DEVICE_REMOVED,
// TODO...
}
}
......
......@@ -59,6 +59,7 @@ import org.onosproject.net.region.RegionService;
import org.onosproject.net.statistic.StatisticService;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.ui.impl.topo.UiTopoSession;
import org.onosproject.ui.model.ServiceBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -122,7 +123,7 @@ public final class UiSharedTopologyModel
@Activate
protected void activate() {
cache = new ModelCache(eventDispatcher);
cache = new ModelCache(new DefaultServiceBundle(), eventDispatcher);
eventDispatcher.addSink(UiModelEvent.class, listenerRegistry);
......@@ -180,6 +181,52 @@ public final class UiSharedTopologyModel
removeListener(session);
}
/**
* Default implementation of service bundle to return references to our
* dynamically injected services.
*/
private class DefaultServiceBundle implements ServiceBundle {
@Override
public ClusterService cluster() {
return clusterService;
}
@Override
public MastershipService mastership() {
return mastershipService;
}
@Override
public RegionService region() {
return regionService;
}
@Override
public DeviceService device() {
return deviceService;
}
@Override
public LinkService link() {
return linkService;
}
@Override
public HostService host() {
return hostService;
}
@Override
public IntentService intent() {
return intentService;
}
@Override
public FlowRuleService flow() {
return flowService;
}
}
private class InternalClusterListener implements ClusterEventListener {
@Override
......
......@@ -14,12 +14,12 @@
* limitations under the License.
*/
package org.onosproject.ui.impl.topo.model;
package org.onosproject.ui.impl;
/**
* Base class for model test classes.
* Base class for unit tests.
*/
public abstract class AbstractModelTest {
public class AbstractUiImplTest {
/**
* System agnostic end-of-line character.
......@@ -41,7 +41,11 @@ public abstract class AbstractModelTest {
* @param o object to print
*/
protected void print(Object o) {
print(o.toString());
if (o == null) {
print("<null>");
} else {
print(o.toString());
}
}
/**
......@@ -55,4 +59,12 @@ public abstract class AbstractModelTest {
print(String.format(fmt, params));
}
/**
* Prints a title, to delimit individual unit test output.
*
* @param s a title for the test
*/
protected void title(String s) {
print(EOL + "=== %s ===", s);
}
}
......
/*
* Copyright 2016-present 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.ui.impl.topo.model;
import com.google.common.collect.ImmutableSet;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ClusterServiceAdapter;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.mastership.MastershipServiceAdapter;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.region.RegionService;
import org.onosproject.ui.impl.AbstractUiImplTest;
import org.onosproject.ui.model.ServiceBundle;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.onosproject.net.DeviceId.deviceId;
/**
* Base class for model test classes.
*/
abstract class AbstractTopoModelTest extends AbstractUiImplTest {
/**
* Returns canned results.
* At some future point, we may make this "programmable", so that
* it returns certain values based on element IDs etc.
*/
protected static final ServiceBundle MOCK_SERVICES =
new ServiceBundle() {
@Override
public ClusterService cluster() {
return MOCK_CLUSTER;
}
@Override
public MastershipService mastership() {
return MOCK_MASTER;
}
@Override
public RegionService region() {
return null;
}
@Override
public DeviceService device() {
return null;
}
@Override
public LinkService link() {
return null;
}
@Override
public HostService host() {
return null;
}
@Override
public IntentService intent() {
return null;
}
@Override
public FlowRuleService flow() {
return null;
}
};
private static final ClusterService MOCK_CLUSTER = new MockClusterService();
private static final MastershipService MOCK_MASTER = new MockMasterService();
// TODO: fill out as necessary
/*
Our mock environment:
Three controllers: C1, C2, C3
Nine devices: D1 .. D9
D4 ---+ +--- D7
| |
D5 --- D1 --- D2 --- D3 --- D8
| |
D6 ---+ +--- D9
Twelve hosts (two per D4 ... D9) H41, H42, H51, H52, ...
Regions:
R1 : D1, D2, D3
R2 : D4, D5, D6
R3 : D7, D8, D9
Mastership:
C1 : D1, D2, D3
C2 : D4, D5, D6
C3 : D7, D8, D9
*/
private static class MockClusterService extends ClusterServiceAdapter {
private final Map<NodeId, ControllerNode.State> states = new HashMap<>();
@Override
public ControllerNode.State getState(NodeId nodeId) {
// For now, a hardcoded state of ACTIVE (but not READY)
// irrespective of the node ID.
return ControllerNode.State.ACTIVE;
}
}
protected static final DeviceId D1_ID = deviceId("D1");
protected static final DeviceId D2_ID = deviceId("D2");
protected static final DeviceId D3_ID = deviceId("D3");
protected static final DeviceId D4_ID = deviceId("D4");
protected static final DeviceId D5_ID = deviceId("D5");
protected static final DeviceId D6_ID = deviceId("D6");
protected static final DeviceId D7_ID = deviceId("D7");
protected static final DeviceId D8_ID = deviceId("D8");
protected static final DeviceId D9_ID = deviceId("D9");
private static class MockMasterService extends MastershipServiceAdapter {
@Override
public Set<DeviceId> getDevicesOf(NodeId nodeId) {
// For now, a hard coded set of two device IDs
// irrespective of the node ID.
return ImmutableSet.of(D1_ID, D2_ID);
}
}
}
......@@ -16,28 +16,113 @@
package org.onosproject.ui.impl.topo.model;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.DefaultControllerNode;
import org.onosproject.event.Event;
import org.onosproject.event.EventDispatcher;
import org.onosproject.ui.impl.topo.model.UiModelEvent.Type;
import org.onosproject.ui.model.topo.UiElement;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.onosproject.cluster.NodeId.nodeId;
/**
* Unit tests for {@link ModelCache}.
*/
public class ModelCacheTest extends AbstractModelTest {
public class ModelCacheTest extends AbstractTopoModelTest {
private static final EventDispatcher DISPATCHER = event -> {
// Do we care?
};
private class TestEvDisp implements EventDispatcher {
private Event<Type, UiElement> lastEvent = null;
private int eventCount = 0;
@Override
public void post(Event event) {
lastEvent = event;
eventCount++;
// print("Event dispatched: %s", event);
}
private void assertEventCount(int exp) {
assertEquals("unex event count", exp, eventCount);
}
private void assertLast(Type expEventType, String expId) {
assertNotNull("no last event", lastEvent);
assertEquals("unex event type", expEventType, lastEvent.type());
assertEquals("unex element ID", expId, lastEvent.subject().idAsString());
}
}
private static IpAddress ip(String s) {
return IpAddress.valueOf(s);
}
private static ControllerNode cnode(String id, String ip) {
return new DefaultControllerNode(nodeId(id), ip(ip));
}
private static final String C1 = "C1";
private static final String C2 = "C2";
private static final String C3 = "C3";
private static final ControllerNode NODE_1 = cnode(C1, "10.0.0.1");
private static final ControllerNode NODE_2 = cnode(C2, "10.0.0.2");
private static final ControllerNode NODE_3 = cnode(C3, "10.0.0.3");
private final TestEvDisp dispatcher = new TestEvDisp();
private ModelCache cache;
@Before
public void setUp() {
cache = new ModelCache(MOCK_SERVICES, dispatcher);
}
@Test
public void basic() {
cache = new ModelCache(DISPATCHER);
title("basic");
print(cache);
assertEquals("unex # members", 0, cache.clusterMemberCount());
assertEquals("unex # regions", 0, cache.regionCount());
}
@Test
public void addAndRemoveClusterMember() {
title("addAndRemoveClusterMember");
print(cache);
assertEquals("unex # members", 0, cache.clusterMemberCount());
dispatcher.assertEventCount(0);
cache.addOrUpdateClusterMember(NODE_1);
print(cache);
assertEquals("unex # members", 1, cache.clusterMemberCount());
dispatcher.assertEventCount(1);
dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1);
cache.removeClusterMember(NODE_1);
print(cache);
assertEquals("unex # members", 0, cache.clusterMemberCount());
dispatcher.assertEventCount(2);
dispatcher.assertLast(Type.CLUSTER_MEMBER_REMOVED, C1);
}
@Test
public void createThreeNodeCluster() {
title("createThreeNodeCluster");
cache.addOrUpdateClusterMember(NODE_1);
dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1);
cache.addOrUpdateClusterMember(NODE_2);
dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C2);
cache.addOrUpdateClusterMember(NODE_3);
dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C3);
dispatcher.assertEventCount(3);
print(cache);
}
}
......