Simon Hunt

Implemented initial loading of ModelCache.

Created UiLinkId to canonicalize identifiers for UI links, based on src and dst elements.
Added idAsString() and name() methods to UiElement.
Added toString() to UiDevice, UiLink, UiHost.
Created Mock services for testing.

Change-Id: I4d27110e5aca08f29bb719f17e9ec65d6786e2c8
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.ui.model.topo;
18 -
19 -import org.onosproject.cluster.NodeId;
20 -
21 -import java.util.ArrayList;
22 -import java.util.HashMap;
23 -import java.util.List;
24 -import java.util.Map;
25 -
26 -/**
27 - * Encapsulates the notion of the ONOS cluster.
28 - */
29 -class UiCluster extends UiElement {
30 -
31 - private static final String DEFAULT_CLUSTER_ID = "CLUSTER-0";
32 -
33 - private final List<UiClusterMember> members = new ArrayList<>();
34 - private final Map<NodeId, UiClusterMember> lookup = new HashMap<>();
35 -
36 - @Override
37 - public String toString() {
38 - return String.valueOf(size()) + "-member cluster";
39 - }
40 -
41 - /**
42 - * Removes all cluster members.
43 - */
44 - void clear() {
45 - members.clear();
46 - }
47 -
48 - /**
49 - * Returns the cluster member with the given identifier, or null if no
50 - * such member exists.
51 - *
52 - * @param id identifier of member to find
53 - * @return corresponding member
54 - */
55 - public UiClusterMember find(NodeId id) {
56 - return lookup.get(id);
57 - }
58 -
59 - /**
60 - * Adds the given member to the cluster.
61 - *
62 - * @param member member to add
63 - */
64 - public void add(UiClusterMember member) {
65 - members.add(member);
66 - lookup.put(member.id(), member);
67 - }
68 -
69 - /**
70 - * Removes the given member from the cluster.
71 - *
72 - * @param member member to remove
73 - */
74 - public void remove(UiClusterMember member) {
75 - members.remove(member);
76 - lookup.remove(member.id());
77 - }
78 -
79 - /**
80 - * Returns the number of members in the cluster.
81 - *
82 - * @return number of members
83 - */
84 - public int size() {
85 - return members.size();
86 - }
87 -
88 - @Override
89 - public String idAsString() {
90 - return DEFAULT_CLUSTER_ID;
91 - }
92 -}
...@@ -19,6 +19,11 @@ package org.onosproject.ui.model.topo; ...@@ -19,6 +19,11 @@ package org.onosproject.ui.model.topo;
19 import org.onlab.packet.IpAddress; 19 import org.onlab.packet.IpAddress;
20 import org.onosproject.cluster.ControllerNode; 20 import org.onosproject.cluster.ControllerNode;
21 import org.onosproject.cluster.NodeId; 21 import org.onosproject.cluster.NodeId;
22 +import org.onosproject.net.DeviceId;
23 +
24 +import java.util.Collections;
25 +import java.util.HashSet;
26 +import java.util.Set;
22 27
23 import static org.onosproject.cluster.ControllerNode.State.INACTIVE; 28 import static org.onosproject.cluster.ControllerNode.State.INACTIVE;
24 29
...@@ -27,18 +32,21 @@ import static org.onosproject.cluster.ControllerNode.State.INACTIVE; ...@@ -27,18 +32,21 @@ import static org.onosproject.cluster.ControllerNode.State.INACTIVE;
27 */ 32 */
28 public class UiClusterMember extends UiElement { 33 public class UiClusterMember extends UiElement {
29 34
35 + private final UiTopology topology;
30 private final ControllerNode cnode; 36 private final ControllerNode cnode;
31 37
32 - private int deviceCount = 0;
33 private ControllerNode.State state = INACTIVE; 38 private ControllerNode.State state = INACTIVE;
39 + private final Set<DeviceId> mastership = new HashSet<>();
34 40
35 /** 41 /**
36 - * Constructs a cluster member, with a reference to the specified 42 + * Constructs a UI cluster member, with a reference to the parent
37 - * controller node instance. 43 + * topology instance and the specified controller node instance.
38 * 44 *
45 + * @param topology parent topology containing this cluster member
39 * @param cnode underlying controller node. 46 * @param cnode underlying controller node.
40 */ 47 */
41 - public UiClusterMember(ControllerNode cnode) { 48 + public UiClusterMember(UiTopology topology, ControllerNode cnode) {
49 + this.topology = topology;
42 this.cnode = cnode; 50 this.cnode = cnode;
43 } 51 }
44 52
...@@ -47,10 +55,23 @@ public class UiClusterMember extends UiElement { ...@@ -47,10 +55,23 @@ public class UiClusterMember extends UiElement {
47 return "UiClusterMember{" + cnode + 55 return "UiClusterMember{" + cnode +
48 ", online=" + isOnline() + 56 ", online=" + isOnline() +
49 ", ready=" + isReady() + 57 ", ready=" + isReady() +
50 - ", #devices=" + deviceCount + 58 + ", #devices=" + deviceCount() +
51 "}"; 59 "}";
52 } 60 }
53 61
62 + @Override
63 + public String idAsString() {
64 + return id().toString();
65 + }
66 +
67 + /**
68 + * Returns the controller node instance backing this UI cluster member.
69 + *
70 + * @return the backing controller node instance
71 + */
72 + public ControllerNode backingNode() {
73 + return cnode;
74 + }
54 75
55 /** 76 /**
56 * Sets the state of this cluster member. 77 * Sets the state of this cluster member.
...@@ -61,14 +82,15 @@ public class UiClusterMember extends UiElement { ...@@ -61,14 +82,15 @@ public class UiClusterMember extends UiElement {
61 this.state = state; 82 this.state = state;
62 } 83 }
63 84
64 -
65 /** 85 /**
66 - * Sets the number of devices for which this cluster member is master. 86 + * Sets the collection of identities of devices for which this
87 + * controller node is master.
67 * 88 *
68 - * @param deviceCount number of devices 89 + * @param mastership device IDs
69 */ 90 */
70 - public void setDeviceCount(int deviceCount) { 91 + public void setMastership(Set<DeviceId> mastership) {
71 - this.deviceCount = deviceCount; 92 + this.mastership.clear();
93 + this.mastership.addAll(mastership);
72 } 94 }
73 95
74 /** 96 /**
...@@ -113,11 +135,26 @@ public class UiClusterMember extends UiElement { ...@@ -113,11 +135,26 @@ public class UiClusterMember extends UiElement {
113 * @return number of devices for which this member is master 135 * @return number of devices for which this member is master
114 */ 136 */
115 public int deviceCount() { 137 public int deviceCount() {
116 - return deviceCount; 138 + return mastership.size();
117 } 139 }
118 140
119 - @Override 141 + /**
120 - public String idAsString() { 142 + * Returns the list of devices for which this cluster member is master.
121 - return id().toString(); 143 + *
144 + * @return list of devices for which this member is master
145 + */
146 + public Set<DeviceId> masterOf() {
147 + return Collections.unmodifiableSet(mastership);
148 + }
149 +
150 + /**
151 + * Returns true if the specified device is one for which this cluster
152 + * member is master.
153 + *
154 + * @param deviceId device ID
155 + * @return true if this cluster member is master for the given device
156 + */
157 + public boolean masterOf(DeviceId deviceId) {
158 + return mastership.contains(deviceId);
122 } 159 }
123 } 160 }
......
...@@ -16,21 +16,53 @@ ...@@ -16,21 +16,53 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 +import com.google.common.base.MoreObjects;
19 import org.onosproject.net.Device; 20 import org.onosproject.net.Device;
20 import org.onosproject.net.DeviceId; 21 import org.onosproject.net.DeviceId;
22 +import org.onosproject.net.region.RegionId;
21 23
22 /** 24 /**
23 * Represents a device. 25 * Represents a device.
24 */ 26 */
25 public class UiDevice extends UiNode { 27 public class UiDevice extends UiNode {
26 28
27 - private Device device; 29 + private final UiTopology topology;
30 + private final Device device;
31 +
32 + private RegionId regionId;
33 +
34 + /**
35 + * Creates a new UI device.
36 + *
37 + * @param topology parent topology
38 + * @param device backing device
39 + */
40 + public UiDevice(UiTopology topology, Device device) {
41 + this.topology = topology;
42 + this.device = device;
43 + }
44 +
45 + /**
46 + * Sets the ID of the region to which this device belongs.
47 + *
48 + * @param regionId region identifier
49 + */
50 + public void setRegionId(RegionId regionId) {
51 + this.regionId = regionId;
52 + }
28 53
29 @Override 54 @Override
30 - protected void destroy() { 55 + public String toString() {
31 - device = null; 56 + return MoreObjects.toStringHelper(this)
57 + .add("id", id())
58 + .add("region", regionId)
59 + .toString();
32 } 60 }
33 61
62 + // @Override
63 +// protected void destroy() {
64 +// }
65 +
34 /** 66 /**
35 * Returns the identity of the device. 67 * Returns the identity of the device.
36 * 68 *
...@@ -44,4 +76,22 @@ public class UiDevice extends UiNode { ...@@ -44,4 +76,22 @@ public class UiDevice extends UiNode {
44 public String idAsString() { 76 public String idAsString() {
45 return id().toString(); 77 return id().toString();
46 } 78 }
79 +
80 + /**
81 + * Returns the device instance backing this UI device.
82 + *
83 + * @return the backing device instance
84 + */
85 + public Device backingDevice() {
86 + return device;
87 + }
88 +
89 + /**
90 + * Returns the UI region to which this device belongs.
91 + *
92 + * @return the UI region
93 + */
94 + public UiRegion uiRegion() {
95 + return topology.findRegion(regionId);
96 + }
47 } 97 }
......
...@@ -35,4 +35,15 @@ public abstract class UiElement { ...@@ -35,4 +35,15 @@ public abstract class UiElement {
35 * @return the element unique identifier 35 * @return the element unique identifier
36 */ 36 */
37 public abstract String idAsString(); 37 public abstract String idAsString();
38 +
39 + /**
40 + * Returns a friendly name to be used for display purposes.
41 + * This default implementation returns the result of calling
42 + * {@link #idAsString()}.
43 + *
44 + * @return the friendly name
45 + */
46 + public String name() {
47 + return idAsString();
48 + }
38 } 49 }
......
...@@ -16,19 +16,49 @@ ...@@ -16,19 +16,49 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 +import org.onosproject.net.DeviceId;
19 import org.onosproject.net.Host; 20 import org.onosproject.net.Host;
20 import org.onosproject.net.HostId; 21 import org.onosproject.net.HostId;
22 +import org.onosproject.net.PortNumber;
23 +
24 +import static com.google.common.base.MoreObjects.toStringHelper;
21 25
22 /** 26 /**
23 * Represents an end-station host. 27 * Represents an end-station host.
24 */ 28 */
25 public class UiHost extends UiNode { 29 public class UiHost extends UiNode {
26 30
27 - private Host host; 31 + private final UiTopology topology;
32 + private final Host host;
33 +
34 + // Host location
35 + private DeviceId locDevice;
36 + private PortNumber locPort;
37 +
38 + private UiLinkId edgeLinkId;
39 +
40 + /**
41 + * Creates a new UI host.
42 + *
43 + * @param topology parent topology
44 + * @param host backing host
45 + */
46 + public UiHost(UiTopology topology, Host host) {
47 + this.topology = topology;
48 + this.host = host;
49 + }
50 +
51 +// @Override
52 +// protected void destroy() {
53 +// }
28 54
29 @Override 55 @Override
30 - protected void destroy() { 56 + public String toString() {
31 - host = null; 57 + return toStringHelper(this)
58 + .add("id", id())
59 + .add("dev", locDevice)
60 + .add("port", locPort)
61 + .toString();
32 } 62 }
33 63
34 /** 64 /**
...@@ -44,4 +74,44 @@ public class UiHost extends UiNode { ...@@ -44,4 +74,44 @@ public class UiHost extends UiNode {
44 public String idAsString() { 74 public String idAsString() {
45 return id().toString(); 75 return id().toString();
46 } 76 }
77 +
78 + /**
79 + * Sets the host's current location.
80 + *
81 + * @param deviceId ID of device
82 + * @param port port number
83 + */
84 + public void setLocation(DeviceId deviceId, PortNumber port) {
85 + locDevice = deviceId;
86 + locPort = port;
87 + }
88 +
89 + /**
90 + * Sets the ID of the edge link between this host and the device to which
91 + * it connects.
92 + *
93 + * @param id edge link identifier to set
94 + */
95 + public void setEdgeLinkId(UiLinkId id) {
96 + this.edgeLinkId = id;
97 + }
98 +
99 + /**
100 + * Returns the host instance backing this UI host.
101 + *
102 + * @return the backing host instance
103 + */
104 + public Host backingHost() {
105 + return host;
106 + }
107 +
108 + /**
109 + * Identifier for the edge link between this host and the device to which
110 + * it is connected.
111 + *
112 + * @return edge link identifier
113 + */
114 + public UiLinkId edgeLinkId() {
115 + return null;
116 + }
47 } 117 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +/**
20 + * Designates the logical layer of the network that an element belongs to.
21 + */
22 +public enum UiLayer {
23 + PACKET, OPTICAL;
24 +
25 + /**
26 + * Returns the default layer (for those elements that do not explicitly
27 + * define which layer they belong to).
28 + *
29 + * @return default layer
30 + */
31 + public static UiLayer defaultLayer() {
32 + return PACKET;
33 + }
34 +}
...@@ -16,26 +16,61 @@ ...@@ -16,26 +16,61 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 -import org.onosproject.net.Device; 19 +import org.onosproject.net.DeviceId;
20 import org.onosproject.net.EdgeLink; 20 import org.onosproject.net.EdgeLink;
21 import org.onosproject.net.Link; 21 import org.onosproject.net.Link;
22 22
23 import java.util.Set; 23 import java.util.Set;
24 24
25 +import static com.google.common.base.MoreObjects.toStringHelper;
26 +
25 /** 27 /**
26 - * Represents a bi-directional link backed by two uni-directional links. 28 + * Represents a link (line between two elements). This may have one of
29 + * several forms:
30 + * <ul>
31 + * <li>
32 + * An infrastructure link:
33 + * two backing unidirectional links between two devices.
34 + * </li>
35 + * <li>
36 + * An edge link:
37 + * representing the connection between a host and a device.
38 + * </li>
39 + * <li>
40 + * An aggregation link:
41 + * representing multiple underlying UI link instances.
42 + * </li>
43 + * </ul>
27 */ 44 */
28 public class UiLink extends UiElement { 45 public class UiLink extends UiElement {
29 46
47 + private static final String E_UNASSOC =
48 + "backing link not associated with this UI link: ";
49 +
50 + private final UiTopology topology;
51 + private final UiLinkId id;
52 +
53 + /**
54 + * Creates a UI link.
55 + *
56 + * @param topology parent topology
57 + * @param id canonicalized link identifier
58 + */
59 + public UiLink(UiTopology topology, UiLinkId id) {
60 + this.topology = topology;
61 + this.id = id;
62 + }
63 +
30 // devices at either end of this link 64 // devices at either end of this link
31 - private Device deviceA; 65 + private DeviceId deviceA;
32 - private Device deviceB; 66 + private DeviceId deviceB;
33 67
34 // two unidirectional links underlying this link... 68 // two unidirectional links underlying this link...
35 private Link linkAtoB; 69 private Link linkAtoB;
36 private Link linkBtoA; 70 private Link linkBtoA;
37 71
38 // ==OR== : private (synthetic) host link 72 // ==OR== : private (synthetic) host link
73 + private DeviceId edgeDevice;
39 private EdgeLink edgeLink; 74 private EdgeLink edgeLink;
40 75
41 // ==OR== : set of underlying UI links that this link aggregates 76 // ==OR== : set of underlying UI links that this link aggregates
...@@ -43,6 +78,13 @@ public class UiLink extends UiElement { ...@@ -43,6 +78,13 @@ public class UiLink extends UiElement {
43 78
44 79
45 @Override 80 @Override
81 + public String toString() {
82 + return toStringHelper(this)
83 + .add("id", id)
84 + .toString();
85 + }
86 +
87 + @Override
46 protected void destroy() { 88 protected void destroy() {
47 deviceA = null; 89 deviceA = null;
48 deviceB = null; 90 deviceB = null;
...@@ -55,9 +97,84 @@ public class UiLink extends UiElement { ...@@ -55,9 +97,84 @@ public class UiLink extends UiElement {
55 } 97 }
56 } 98 }
57 99
100 + /**
101 + * Returns the canonicalized link identifier for this link.
102 + *
103 + * @return the link identifier
104 + */
105 + public UiLinkId id() {
106 + return id;
107 + }
108 +
58 @Override 109 @Override
59 public String idAsString() { 110 public String idAsString() {
60 - // TODO 111 + return id.toString();
61 - return null; 112 + }
113 +
114 + /**
115 + * Attaches the given backing link to this UI link. This method will
116 + * throw an exception if this UI link is not representative of the
117 + * supplied link.
118 + *
119 + * @param link backing link to attach
120 + * @throws IllegalArgumentException if the link is not appropriate
121 + */
122 + public void attachBackingLink(Link link) {
123 + UiLinkId.Direction d = id.directionOf(link);
124 +
125 + if (d == UiLinkId.Direction.A_TO_B) {
126 + linkAtoB = link;
127 + deviceA = link.src().deviceId();
128 + deviceB = link.dst().deviceId();
129 +
130 + } else if (d == UiLinkId.Direction.B_TO_A) {
131 + linkBtoA = link;
132 + deviceB = link.src().deviceId();
133 + deviceA = link.dst().deviceId();
134 +
135 + } else {
136 + throw new IllegalArgumentException(E_UNASSOC + link);
137 + }
138 + }
139 +
140 + /**
141 + * Detaches the given backing link from this UI link, returning true if the
142 + * reverse link is still attached, or false otherwise.
143 + *
144 + * @param link the backing link to detach
145 + * @return true if other link still attached, false otherwise
146 + * @throws IllegalArgumentException if the link is not appropriate
147 + */
148 + public boolean detachBackingLink(Link link) {
149 + UiLinkId.Direction d = id.directionOf(link);
150 + if (d == UiLinkId.Direction.A_TO_B) {
151 + linkAtoB = null;
152 + return linkBtoA != null;
153 + }
154 + if (d == UiLinkId.Direction.B_TO_A) {
155 + linkBtoA = null;
156 + return linkAtoB != null;
157 + }
158 + throw new IllegalArgumentException(E_UNASSOC + link);
159 + }
160 +
161 + /**
162 + * Attaches the given edge link to this UI link. This method will
163 + * throw an exception if this UI link is not representative of the
164 + * supplied link.
165 + *
166 + * @param elink edge link to attach
167 + * @throws IllegalArgumentException if the link is not appropriate
168 + */
169 + public void attachEdgeLink(EdgeLink elink) {
170 + UiLinkId.Direction d = id.directionOf(elink);
171 + // Expected direction of edge links is A-to-B (Host to device)
172 + // but checking not null is sufficient
173 + if (d == null) {
174 + throw new IllegalArgumentException(E_UNASSOC + elink);
175 + }
176 +
177 + edgeLink = elink;
178 + edgeDevice = elink.hostLocation().deviceId();
62 } 179 }
63 } 180 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.ConnectPoint;
20 +import org.onosproject.net.ElementId;
21 +import org.onosproject.net.Link;
22 +
23 +/**
24 + * A canonical representation of an identifier for {@link UiLink}s.
25 + */
26 +public final class UiLinkId {
27 +
28 + /**
29 + * Designates the directionality of an underlying (uni-directional) link.
30 + */
31 + public enum Direction {
32 + A_TO_B,
33 + B_TO_A
34 + }
35 +
36 + private static final String ID_DELIMITER = "~";
37 +
38 + private final ElementId idA;
39 + private final ElementId idB;
40 + private final String idStr;
41 +
42 + /**
43 + * Creates a UI link identifier. It is expected that A comes before B when
44 + * the two identifiers are naturally sorted, thus providing a representation
45 + * which is invariant to whether A or B is source or destination of the
46 + * underlying link.
47 + *
48 + * @param a first element ID
49 + * @param b second element ID
50 + */
51 + private UiLinkId(ElementId a, ElementId b) {
52 + idA = a;
53 + idB = b;
54 +
55 + idStr = a.toString() + ID_DELIMITER + b.toString();
56 + }
57 +
58 + @Override
59 + public String toString() {
60 + return idStr;
61 + }
62 +
63 + /**
64 + * Returns the identifier of the first element.
65 + *
66 + * @return first element identity
67 + */
68 + public ElementId elementA() {
69 + return idA;
70 + }
71 +
72 + /**
73 + * Returns the identifier of the second element.
74 + *
75 + * @return second element identity
76 + */
77 + public ElementId elementB() {
78 + return idB;
79 + }
80 +
81 + @Override
82 + public boolean equals(Object o) {
83 + if (this == o) {
84 + return true;
85 + }
86 + if (o == null || getClass() != o.getClass()) {
87 + return false;
88 + }
89 +
90 + UiLinkId uiLinkId = (UiLinkId) o;
91 + return idStr.equals(uiLinkId.idStr);
92 + }
93 +
94 + @Override
95 + public int hashCode() {
96 + return idStr.hashCode();
97 + }
98 +
99 + /**
100 + * Returns the direction of the given link, or null if this link ID does
101 + * not correspond to the given link.
102 + *
103 + * @param link the link to examine
104 + * @return corresponding direction
105 + */
106 + Direction directionOf(Link link) {
107 + ConnectPoint src = link.src();
108 + ElementId srcId = src.elementId();
109 + return idA.equals(srcId) ? Direction.A_TO_B
110 + : idB.equals(srcId) ? Direction.B_TO_A
111 + : null;
112 + }
113 +
114 + /**
115 + * Generates the canonical link identifier for the given link.
116 + *
117 + * @param link link for which the identifier is required
118 + * @return link identifier
119 + * @throws NullPointerException if any of the required fields are null
120 + */
121 + public static UiLinkId uiLinkId(Link link) {
122 + ConnectPoint src = link.src();
123 + ConnectPoint dst = link.dst();
124 + if (src == null || dst == null) {
125 + throw new NullPointerException("null src or dst connect point: " + link);
126 + }
127 +
128 + ElementId srcId = src.elementId();
129 + ElementId dstId = dst.elementId();
130 + if (srcId == null || dstId == null) {
131 + throw new NullPointerException("null element ID in connect point: " + link);
132 + }
133 +
134 + // canonicalize
135 + int comp = srcId.toString().compareTo(dstId.toString());
136 + return comp <= 0 ? new UiLinkId(srcId, dstId)
137 + : new UiLinkId(dstId, srcId);
138 + }
139 +}
...@@ -16,35 +16,46 @@ ...@@ -16,35 +16,46 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 +import org.onosproject.net.DeviceId;
20 +import org.onosproject.net.HostId;
19 import org.onosproject.net.region.Region; 21 import org.onosproject.net.region.Region;
20 import org.onosproject.net.region.RegionId; 22 import org.onosproject.net.region.RegionId;
21 23
24 +import java.util.HashSet;
22 import java.util.Set; 25 import java.util.Set;
23 -import java.util.TreeSet; 26 +
27 +import static com.google.common.base.MoreObjects.toStringHelper;
24 28
25 /** 29 /**
26 * Represents a region. 30 * Represents a region.
27 */ 31 */
28 public class UiRegion extends UiNode { 32 public class UiRegion extends UiNode {
29 33
30 - private final Set<UiDevice> uiDevices = new TreeSet<>(); 34 + // loose bindings to things in this region
31 - private final Set<UiHost> uiHosts = new TreeSet<>(); 35 + private final Set<DeviceId> deviceIds = new HashSet<>();
32 - private final Set<UiLink> uiLinks = new TreeSet<>(); 36 + private final Set<HostId> hostIds = new HashSet<>();
37 + private final Set<UiLinkId> uiLinkIds = new HashSet<>();
38 +
39 + private final UiTopology topology;
33 40
34 - private Region region; 41 + private final Region region;
35 42
43 + /**
44 + * Constructs a UI region, with a reference to the specified backing region.
45 + *
46 + * @param topology parent topology
47 + * @param region backing region
48 + */
49 + public UiRegion(UiTopology topology, Region region) {
50 + this.topology = topology;
51 + this.region = region;
52 + }
36 53
37 @Override 54 @Override
38 protected void destroy() { 55 protected void destroy() {
39 - uiDevices.forEach(UiDevice::destroy); 56 + deviceIds.clear();
40 - uiHosts.forEach(UiHost::destroy); 57 + hostIds.clear();
41 - uiLinks.forEach(UiLink::destroy); 58 + uiLinkIds.clear();
42 -
43 - uiDevices.clear();
44 - uiHosts.clear();
45 - uiLinks.clear();
46 -
47 - region = null;
48 } 59 }
49 60
50 /** 61 /**
...@@ -60,4 +71,75 @@ public class UiRegion extends UiNode { ...@@ -60,4 +71,75 @@ public class UiRegion extends UiNode {
60 public String idAsString() { 71 public String idAsString() {
61 return id().toString(); 72 return id().toString();
62 } 73 }
74 +
75 + @Override
76 + public String name() {
77 + return region.name();
78 + }
79 +
80 + /**
81 + * Returns the region instance backing this UI region.
82 + *
83 + * @return the backing region instance
84 + */
85 + public Region backingRegion() {
86 + return region;
87 + }
88 +
89 + /**
90 + * Make sure we have only these devices in the region.
91 + *
92 + * @param devices devices in the region
93 + */
94 + public void reconcileDevices(Set<DeviceId> devices) {
95 + deviceIds.clear();
96 + deviceIds.addAll(devices);
97 + }
98 +
99 + @Override
100 + public String toString() {
101 + return toStringHelper(this)
102 + .add("id", id())
103 + .add("name", name())
104 + .add("devices", deviceIds)
105 + .add("#hosts", hostIds.size())
106 + .add("#links", uiLinkIds.size())
107 + .toString();
108 + }
109 +
110 + /**
111 + * Returns the region's type.
112 + *
113 + * @return region type
114 + */
115 + public Region.Type type() {
116 + return region.type();
117 + }
118 +
119 + /**
120 + * Returns the devices in this region.
121 + *
122 + * @return the devices in this region
123 + */
124 + public Set<UiDevice> devices() {
125 + return topology.deviceSet(deviceIds);
126 + }
127 +
128 + /**
129 + * Returns the hosts in this region.
130 + *
131 + * @return the hosts in this region
132 + */
133 + public Set<UiHost> hosts() {
134 + return topology.hostSet(hostIds);
135 + }
136 +
137 + /**
138 + * Returns the links in this region.
139 + *
140 + * @return the links in this region
141 + */
142 + public Set<UiLink> links() {
143 + return topology.linkSet(uiLinkIds);
144 + }
63 } 145 }
......
...@@ -17,27 +17,54 @@ ...@@ -17,27 +17,54 @@
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 import org.onosproject.cluster.NodeId; 19 import org.onosproject.cluster.NodeId;
20 +import org.onosproject.net.DeviceId;
21 +import org.onosproject.net.HostId;
22 +import org.onosproject.net.region.RegionId;
20 import org.slf4j.Logger; 23 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory; 24 import org.slf4j.LoggerFactory;
22 25
26 +import java.util.HashMap;
27 +import java.util.HashSet;
28 +import java.util.Map;
23 import java.util.Set; 29 import java.util.Set;
24 -import java.util.TreeSet; 30 +
31 +import static com.google.common.base.MoreObjects.toStringHelper;
25 32
26 /** 33 /**
27 * Represents the overall network topology. 34 * Represents the overall network topology.
28 */ 35 */
29 public class UiTopology extends UiElement { 36 public class UiTopology extends UiElement {
30 37
38 + private static final String E_UNMAPPED =
39 + "Attempting to retrieve unmapped {}: {}";
40 +
31 private static final String DEFAULT_TOPOLOGY_ID = "TOPOLOGY-0"; 41 private static final String DEFAULT_TOPOLOGY_ID = "TOPOLOGY-0";
32 42
33 private static final Logger log = LoggerFactory.getLogger(UiTopology.class); 43 private static final Logger log = LoggerFactory.getLogger(UiTopology.class);
34 44
35 - private final UiCluster uiCluster = new UiCluster(); 45 +
36 - private final Set<UiRegion> uiRegions = new TreeSet<>(); 46 + // top level mappings of topology elements by ID
47 + private final Map<NodeId, UiClusterMember> cnodeLookup = new HashMap<>();
48 + private final Map<RegionId, UiRegion> regionLookup = new HashMap<>();
49 + private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>();
50 + private final Map<HostId, UiHost> hostLookup = new HashMap<>();
51 + private final Map<UiLinkId, UiLink> linkLookup = new HashMap<>();
52 +
37 53
38 @Override 54 @Override
39 public String toString() { 55 public String toString() {
40 - return "Topology: " + uiCluster + ", " + uiRegions.size() + " regions"; 56 + return toStringHelper(this)
57 + .add("#cnodes", clusterMemberCount())
58 + .add("#regions", regionCount())
59 + .add("#devices", deviceLookup.size())
60 + .add("#hosts", hostLookup.size())
61 + .add("#links", linkLookup.size())
62 + .toString();
63 + }
64 +
65 + @Override
66 + public String idAsString() {
67 + return DEFAULT_TOPOLOGY_ID;
41 } 68 }
42 69
43 /** 70 /**
...@@ -46,19 +73,22 @@ public class UiTopology extends UiElement { ...@@ -46,19 +73,22 @@ public class UiTopology extends UiElement {
46 */ 73 */
47 public void clear() { 74 public void clear() {
48 log.debug("clearing topology model"); 75 log.debug("clearing topology model");
49 - uiRegions.clear(); 76 + cnodeLookup.clear();
50 - uiCluster.clear(); 77 + regionLookup.clear();
78 + deviceLookup.clear();
79 + hostLookup.clear();
80 + linkLookup.clear();
51 } 81 }
52 82
53 /** 83 /**
54 * Returns the cluster member with the given identifier, or null if no 84 * Returns the cluster member with the given identifier, or null if no
55 - * such member. 85 + * such member exists.
56 * 86 *
57 * @param id cluster node identifier 87 * @param id cluster node identifier
58 - * @return the cluster member with that identifier 88 + * @return corresponding UI cluster member
59 */ 89 */
60 public UiClusterMember findClusterMember(NodeId id) { 90 public UiClusterMember findClusterMember(NodeId id) {
61 - return uiCluster.find(id); 91 + return cnodeLookup.get(id);
62 } 92 }
63 93
64 /** 94 /**
...@@ -67,7 +97,7 @@ public class UiTopology extends UiElement { ...@@ -67,7 +97,7 @@ public class UiTopology extends UiElement {
67 * @param member cluster member to add 97 * @param member cluster member to add
68 */ 98 */
69 public void add(UiClusterMember member) { 99 public void add(UiClusterMember member) {
70 - uiCluster.add(member); 100 + cnodeLookup.put(member.id(), member);
71 } 101 }
72 102
73 /** 103 /**
...@@ -76,7 +106,10 @@ public class UiTopology extends UiElement { ...@@ -76,7 +106,10 @@ public class UiTopology extends UiElement {
76 * @param member cluster member to remove 106 * @param member cluster member to remove
77 */ 107 */
78 public void remove(UiClusterMember member) { 108 public void remove(UiClusterMember member) {
79 - uiCluster.remove(member); 109 + UiClusterMember m = cnodeLookup.remove(member.id());
110 + if (m != null) {
111 + m.destroy();
112 + }
80 } 113 }
81 114
82 /** 115 /**
...@@ -85,7 +118,18 @@ public class UiTopology extends UiElement { ...@@ -85,7 +118,18 @@ public class UiTopology extends UiElement {
85 * @return number of cluster members 118 * @return number of cluster members
86 */ 119 */
87 public int clusterMemberCount() { 120 public int clusterMemberCount() {
88 - return uiCluster.size(); 121 + return cnodeLookup.size();
122 + }
123 +
124 + /**
125 + * Returns the region with the specified identifier, or null if
126 + * no such region exists.
127 + *
128 + * @param id region identifier
129 + * @return corresponding UI region
130 + */
131 + public UiRegion findRegion(RegionId id) {
132 + return regionLookup.get(id);
89 } 133 }
90 134
91 /** 135 /**
...@@ -94,11 +138,182 @@ public class UiTopology extends UiElement { ...@@ -94,11 +138,182 @@ public class UiTopology extends UiElement {
94 * @return number of regions 138 * @return number of regions
95 */ 139 */
96 public int regionCount() { 140 public int regionCount() {
97 - return uiRegions.size(); 141 + return regionLookup.size();
98 } 142 }
99 143
100 - @Override 144 + /**
101 - public String idAsString() { 145 + * Adds the given region to the topology model.
102 - return DEFAULT_TOPOLOGY_ID; 146 + *
147 + * @param uiRegion region to add
148 + */
149 + public void add(UiRegion uiRegion) {
150 + regionLookup.put(uiRegion.id(), uiRegion);
103 } 151 }
152 +
153 + /**
154 + * Removes the given region from the topology model.
155 + *
156 + * @param uiRegion region to remove
157 + */
158 + public void remove(UiRegion uiRegion) {
159 + regionLookup.remove(uiRegion.id());
160 + }
161 +
162 + /**
163 + * Returns the device with the specified identifier, or null if
164 + * no such device exists.
165 + *
166 + * @param id device identifier
167 + * @return corresponding UI device
168 + */
169 + public UiDevice findDevice(DeviceId id) {
170 + return deviceLookup.get(id);
171 + }
172 +
173 + /**
174 + * Adds the given device to the topology model.
175 + *
176 + * @param uiDevice device to add
177 + */
178 + public void add(UiDevice uiDevice) {
179 + deviceLookup.put(uiDevice.id(), uiDevice);
180 + }
181 +
182 + /**
183 + * Removes the given device from the topology model.
184 + *
185 + * @param uiDevice device to remove
186 + */
187 + public void remove(UiDevice uiDevice) {
188 + UiDevice d = deviceLookup.remove(uiDevice.id());
189 + if (d != null) {
190 + d.destroy();
191 + }
192 + }
193 +
194 + /**
195 + * Returns the link with the specified identifier, or null if no such
196 + * link exists.
197 + *
198 + * @param id the canonicalized link identifier
199 + * @return corresponding UI link
200 + */
201 + public UiLink findLink(UiLinkId id) {
202 + return linkLookup.get(id);
203 + }
204 +
205 + /**
206 + * Adds the given UI link to the topology model.
207 + *
208 + * @param uiLink link to add
209 + */
210 + public void add(UiLink uiLink) {
211 + linkLookup.put(uiLink.id(), uiLink);
212 + }
213 +
214 + /**
215 + * Removes the given UI link from the model.
216 + *
217 + * @param uiLink link to remove
218 + */
219 + public void remove(UiLink uiLink) {
220 + UiLink link = linkLookup.get(uiLink.id());
221 + if (link != null) {
222 + link.destroy();
223 + }
224 + }
225 +
226 + /**
227 + * Returns the host with the specified identifier, or null if no such
228 + * host exists.
229 + *
230 + * @param id host identifier
231 + * @return corresponding UI host
232 + */
233 + public UiHost findHost(HostId id) {
234 + return hostLookup.get(id);
235 + }
236 +
237 + /**
238 + * Adds the given host to the topology model.
239 + *
240 + * @param uiHost host to add
241 + */
242 + public void add(UiHost uiHost) {
243 + hostLookup.put(uiHost.id(), uiHost);
244 + }
245 +
246 + /**
247 + * Removes the given host from the topology model.
248 + *
249 + * @param uiHost host to remove
250 + */
251 + public void remove(UiHost uiHost) {
252 + UiHost h = hostLookup.remove(uiHost.id());
253 + if (h != null) {
254 + h.destroy();
255 + }
256 + }
257 +
258 + // ==
259 + // package private methods for supporting linkage amongst topology entities
260 + // ==
261 +
262 + /**
263 + * Returns the set of UI devices with the given identifiers.
264 + *
265 + * @param deviceIds device identifiers
266 + * @return set of matching UI device instances
267 + */
268 + Set<UiDevice> deviceSet(Set<DeviceId> deviceIds) {
269 + Set<UiDevice> uiDevices = new HashSet<>();
270 + for (DeviceId id : deviceIds) {
271 + UiDevice d = deviceLookup.get(id);
272 + if (d != null) {
273 + uiDevices.add(d);
274 + } else {
275 + log.warn(E_UNMAPPED, "device", id);
276 + }
277 + }
278 + return uiDevices;
279 + }
280 +
281 + /**
282 + * Returns the set of UI hosts with the given identifiers.
283 + *
284 + * @param hostIds host identifiers
285 + * @return set of matching UI host instances
286 + */
287 + Set<UiHost> hostSet(Set<HostId> hostIds) {
288 + Set<UiHost> uiHosts = new HashSet<>();
289 + for (HostId id : hostIds) {
290 + UiHost h = hostLookup.get(id);
291 + if (h != null) {
292 + uiHosts.add(h);
293 + } else {
294 + log.warn(E_UNMAPPED, "host", id);
295 + }
296 + }
297 + return uiHosts;
298 + }
299 +
300 + /**
301 + * Returns the set of UI links with the given identifiers.
302 + *
303 + * @param uiLinkIds link identifiers
304 + * @return set of matching UI link instances
305 + */
306 + Set<UiLink> linkSet(Set<UiLinkId> uiLinkIds) {
307 + Set<UiLink> uiLinks = new HashSet<>();
308 + for (UiLinkId id : uiLinkIds) {
309 + UiLink link = linkLookup.get(id);
310 + if (link != null) {
311 + uiLinks.add(link);
312 + } else {
313 + log.warn(E_UNMAPPED, "link", id);
314 + }
315 + }
316 + return uiLinks;
317 + }
318 +
104 } 319 }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 +import org.junit.Before;
19 import org.junit.Test; 20 import org.junit.Test;
20 import org.onlab.packet.IpAddress; 21 import org.onlab.packet.IpAddress;
21 import org.onosproject.cluster.ControllerNode; 22 import org.onosproject.cluster.ControllerNode;
...@@ -36,12 +37,18 @@ public class UiClusterMemberTest extends AbstractUiModelTest { ...@@ -36,12 +37,18 @@ public class UiClusterMemberTest extends AbstractUiModelTest {
36 private static final ControllerNode CNODE_1 = 37 private static final ControllerNode CNODE_1 =
37 new DefaultControllerNode(NODE_ID, NODE_IP); 38 new DefaultControllerNode(NODE_ID, NODE_IP);
38 39
40 + private UiTopology topo;
39 private UiClusterMember member; 41 private UiClusterMember member;
40 42
43 + @Before
44 + public void setUp() {
45 + topo = new UiTopology();
46 + }
47 +
41 @Test 48 @Test
42 public void basic() { 49 public void basic() {
43 title("basic"); 50 title("basic");
44 - member = new UiClusterMember(CNODE_1); 51 + member = new UiClusterMember(topo, CNODE_1);
45 print(member); 52 print(member);
46 53
47 assertEquals("wrong id", NODE_ID, member.id()); 54 assertEquals("wrong id", NODE_ID, member.id());
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.junit.Test;
20 +import org.onosproject.net.ConnectPoint;
21 +import org.onosproject.net.DefaultLink;
22 +import org.onosproject.net.DeviceId;
23 +import org.onosproject.net.Link;
24 +import org.onosproject.net.PortNumber;
25 +import org.onosproject.net.provider.ProviderId;
26 +import org.onosproject.ui.model.AbstractUiModelTest;
27 +
28 +import static org.junit.Assert.assertEquals;
29 +import static org.onosproject.net.DeviceId.deviceId;
30 +import static org.onosproject.net.PortNumber.portNumber;
31 +
32 +/**
33 + * Unit tests for {@link UiLinkId}.
34 + */
35 +public class UiLinkIdTest extends AbstractUiModelTest {
36 +
37 +
38 + private static final ProviderId PROVIDER_ID = ProviderId.NONE;
39 +
40 + private static final DeviceId DEV_X = deviceId("device-X");
41 + private static final DeviceId DEV_Y = deviceId("device-Y");
42 + private static final PortNumber P1 = portNumber(1);
43 + private static final PortNumber P2 = portNumber(2);
44 +
45 + private static final ConnectPoint CP_X = new ConnectPoint(DEV_X, P1);
46 + private static final ConnectPoint CP_Y = new ConnectPoint(DEV_Y, P2);
47 +
48 + private static final Link LINK_X_TO_Y = DefaultLink.builder()
49 + .providerId(ProviderId.NONE)
50 + .src(CP_X)
51 + .dst(CP_Y)
52 + .type(Link.Type.DIRECT)
53 + .build();
54 +
55 + private static final Link LINK_Y_TO_X = DefaultLink.builder()
56 + .providerId(ProviderId.NONE)
57 + .src(CP_Y)
58 + .dst(CP_X)
59 + .type(Link.Type.DIRECT)
60 + .build();
61 +
62 +
63 + @Test
64 + public void canonical() {
65 + title("canonical");
66 + UiLinkId one = UiLinkId.uiLinkId(LINK_X_TO_Y);
67 + UiLinkId two = UiLinkId.uiLinkId(LINK_Y_TO_X);
68 + print("link one: %s", one);
69 + print("link two: %s", two);
70 + assertEquals("not equiv", one, two);
71 + }
72 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.junit.Test;
20 +import org.onosproject.ui.AbstractUiTest;
21 +
22 +/**
23 + * Unit tests for {@link UiTopology}.
24 + */
25 +public class UiTopologyTest extends AbstractUiTest {
26 +
27 + private UiTopology topo;
28 +
29 + @Test
30 + public void basic() {
31 + title("basic");
32 + topo = new UiTopology();
33 + print(topo);
34 + }
35 +}
...@@ -22,26 +22,48 @@ import org.onosproject.cluster.RoleInfo; ...@@ -22,26 +22,48 @@ import org.onosproject.cluster.RoleInfo;
22 import org.onosproject.event.EventDispatcher; 22 import org.onosproject.event.EventDispatcher;
23 import org.onosproject.net.Device; 23 import org.onosproject.net.Device;
24 import org.onosproject.net.DeviceId; 24 import org.onosproject.net.DeviceId;
25 +import org.onosproject.net.EdgeLink;
25 import org.onosproject.net.Host; 26 import org.onosproject.net.Host;
27 +import org.onosproject.net.HostId;
28 +import org.onosproject.net.HostLocation;
26 import org.onosproject.net.Link; 29 import org.onosproject.net.Link;
27 import org.onosproject.net.region.Region; 30 import org.onosproject.net.region.Region;
31 +import org.onosproject.net.region.RegionId;
28 import org.onosproject.ui.model.ServiceBundle; 32 import org.onosproject.ui.model.ServiceBundle;
29 import org.onosproject.ui.model.topo.UiClusterMember; 33 import org.onosproject.ui.model.topo.UiClusterMember;
30 import org.onosproject.ui.model.topo.UiDevice; 34 import org.onosproject.ui.model.topo.UiDevice;
35 +import org.onosproject.ui.model.topo.UiElement;
36 +import org.onosproject.ui.model.topo.UiHost;
37 +import org.onosproject.ui.model.topo.UiLink;
38 +import org.onosproject.ui.model.topo.UiLinkId;
39 +import org.onosproject.ui.model.topo.UiRegion;
31 import org.onosproject.ui.model.topo.UiTopology; 40 import org.onosproject.ui.model.topo.UiTopology;
32 import org.slf4j.Logger; 41 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory; 42 import org.slf4j.LoggerFactory;
34 43
44 +import java.util.Set;
45 +
46 +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
35 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED; 47 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
36 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED; 48 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
37 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED; 49 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
38 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED; 50 import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED;
51 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
52 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_MOVED;
53 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_REMOVED;
54 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
55 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_REMOVED;
56 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
57 +import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_REMOVED;
58 +import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
39 59
40 /** 60 /**
41 * UI Topology Model cache. 61 * UI Topology Model cache.
42 */ 62 */
43 class ModelCache { 63 class ModelCache {
44 64
65 + private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
66 +
45 private static final Logger log = LoggerFactory.getLogger(ModelCache.class); 67 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
46 68
47 private final ServiceBundle services; 69 private final ServiceBundle services;
...@@ -58,122 +80,310 @@ class ModelCache { ...@@ -58,122 +80,310 @@ class ModelCache {
58 return "ModelCache{" + uiTopology + "}"; 80 return "ModelCache{" + uiTopology + "}";
59 } 81 }
60 82
61 - /** 83 + private void postEvent(UiModelEvent.Type type, UiElement subject) {
62 - * Clear our model. 84 + dispatcher.post(new UiModelEvent(type, subject));
63 - */ 85 + }
86 +
64 void clear() { 87 void clear() {
65 uiTopology.clear(); 88 uiTopology.clear();
66 } 89 }
67 90
68 /** 91 /**
69 - * Create our internal model of the global topology. 92 + * Create our internal model of the global topology. An assumption we are
93 + * making is that the topology is empty to start.
70 */ 94 */
71 void load() { 95 void load() {
72 - // TODO - implement loading of initial state 96 + loadClusterMembers();
73 -// loadClusterMembers(); 97 + loadRegions();
74 -// loadRegions(); 98 + loadDevices();
75 -// loadDevices(); 99 + loadLinks();
76 -// loadHosts(); 100 + loadHosts();
77 -// loadLinks();
78 } 101 }
79 102
80 103
81 - /** 104 + // === CLUSTER MEMBERS
82 - * Updates the model (adds a new instance if necessary) with the given 105 +
83 - * controller node information. 106 + private UiClusterMember addNewClusterMember(ControllerNode n) {
84 - * 107 + UiClusterMember member = new UiClusterMember(uiTopology, n);
85 - * @param cnode controller node to be added/updated 108 + uiTopology.add(member);
86 - */ 109 + return member;
110 + }
111 +
112 + private void updateClusterMember(UiClusterMember member) {
113 + ControllerNode.State state = services.cluster().getState(member.id());
114 + member.setState(state);
115 + member.setMastership(services.mastership().getDevicesOf(member.id()));
116 + // NOTE: 'UI-attached' is session-based data, not global, so will
117 + // be set elsewhere
118 + }
119 +
120 + private void loadClusterMembers() {
121 + for (ControllerNode n : services.cluster().getNodes()) {
122 + UiClusterMember member = addNewClusterMember(n);
123 + updateClusterMember(member);
124 + }
125 + }
126 +
127 + // invoked from UiSharedTopologyModel cluster event listener
87 void addOrUpdateClusterMember(ControllerNode cnode) { 128 void addOrUpdateClusterMember(ControllerNode cnode) {
88 NodeId id = cnode.id(); 129 NodeId id = cnode.id();
89 UiClusterMember member = uiTopology.findClusterMember(id); 130 UiClusterMember member = uiTopology.findClusterMember(id);
90 if (member == null) { 131 if (member == null) {
91 - member = new UiClusterMember(cnode); 132 + member = addNewClusterMember(cnode);
92 - uiTopology.add(member);
93 } 133 }
134 + updateClusterMember(member);
94 135
95 - // inject computed data about the cluster node, into the model object 136 + postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member);
96 - ControllerNode.State state = services.cluster().getState(id); 137 + }
97 - member.setState(state);
98 - member.setDeviceCount(services.mastership().getDevicesOf(id).size());
99 - // NOTE: UI-attached is session-based data, not global
100 138
101 - dispatcher.post(new UiModelEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member)); 139 + // package private for unit test access
140 + UiClusterMember accessClusterMember(NodeId id) {
141 + return uiTopology.findClusterMember(id);
102 } 142 }
103 143
104 - /** 144 + // invoked from UiSharedTopologyModel cluster event listener
105 - * Removes from the model the specified controller node.
106 - *
107 - * @param cnode controller node to be removed
108 - */
109 void removeClusterMember(ControllerNode cnode) { 145 void removeClusterMember(ControllerNode cnode) {
110 NodeId id = cnode.id(); 146 NodeId id = cnode.id();
111 UiClusterMember member = uiTopology.findClusterMember(id); 147 UiClusterMember member = uiTopology.findClusterMember(id);
112 if (member != null) { 148 if (member != null) {
113 uiTopology.remove(member); 149 uiTopology.remove(member);
114 - dispatcher.post(new UiModelEvent(CLUSTER_MEMBER_REMOVED, member)); 150 + postEvent(CLUSTER_MEMBER_REMOVED, member);
115 } else { 151 } else {
116 - log.warn("Tried to remove non-member cluster node {}", id); 152 + log.warn(E_NO_ELEMENT, "cluster node", id);
117 } 153 }
118 } 154 }
119 155
156 +
157 + // === MASTERSHIP CHANGES
158 +
159 + // invoked from UiSharedTopologyModel mastership listener
120 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) { 160 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
161 + // To think about:: do we need to store mastership info?
162 + // or can we rely on looking it up live?
121 // TODO: store the updated mastership information 163 // TODO: store the updated mastership information
122 // TODO: post event 164 // TODO: post event
123 } 165 }
124 166
167 +
168 + // === REGIONS
169 +
170 + private UiRegion addNewRegion(Region r) {
171 + UiRegion region = new UiRegion(uiTopology, r);
172 + uiTopology.add(region);
173 + return region;
174 + }
175 +
176 + private void updateRegion(UiRegion region) {
177 + Set<DeviceId> devs = services.region().getRegionDevices(region.id());
178 + region.reconcileDevices(devs);
179 + }
180 +
181 + private void loadRegions() {
182 + for (Region r : services.region().getRegions()) {
183 + UiRegion region = addNewRegion(r);
184 + updateRegion(region);
185 + }
186 + }
187 +
188 + // invoked from UiSharedTopologyModel region listener
125 void addOrUpdateRegion(Region region) { 189 void addOrUpdateRegion(Region region) {
126 - // TODO: find or create region assoc. with parameter 190 + RegionId id = region.id();
127 - // TODO: post event 191 + UiRegion uiRegion = uiTopology.findRegion(id);
192 + if (uiRegion == null) {
193 + uiRegion = addNewRegion(region);
194 + }
195 + updateRegion(uiRegion);
196 +
197 + postEvent(REGION_ADDED_OR_UPDATED, uiRegion);
128 } 198 }
129 199
200 + // invoked from UiSharedTopologyModel region listener
130 void removeRegion(Region region) { 201 void removeRegion(Region region) {
131 - // TODO: find region assoc. with parameter; remove from model 202 + RegionId id = region.id();
132 - // TODO: post event 203 + UiRegion uiRegion = uiTopology.findRegion(id);
204 + if (uiRegion != null) {
205 + uiTopology.remove(uiRegion);
206 + postEvent(REGION_REMOVED, uiRegion);
207 + } else {
208 + log.warn(E_NO_ELEMENT, "region", id);
209 + }
210 + }
211 +
212 +
213 + // === DEVICES
214 +
215 + private UiDevice addNewDevice(Device d) {
216 + UiDevice device = new UiDevice(uiTopology, d);
217 + uiTopology.add(device);
218 + return device;
219 + }
220 +
221 + private void updateDevice(UiDevice device) {
222 + device.setRegionId(services.region().getRegionForDevice(device.id()).id());
133 } 223 }
134 224
225 + private void loadDevices() {
226 + for (Device d : services.device().getDevices()) {
227 + UiDevice device = addNewDevice(d);
228 + updateDevice(device);
229 + }
230 + }
231 +
232 + // invoked from UiSharedTopologyModel device listener
135 void addOrUpdateDevice(Device device) { 233 void addOrUpdateDevice(Device device) {
136 - // TODO: find or create device assoc. with parameter 234 + DeviceId id = device.id();
137 - // FIXME 235 + UiDevice uiDevice = uiTopology.findDevice(id);
138 - UiDevice uiDevice = new UiDevice(); 236 + if (uiDevice == null) {
237 + uiDevice = addNewDevice(device);
238 + }
239 + updateDevice(uiDevice);
139 240
140 - // TODO: post the (correct) event 241 + postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice);
141 - dispatcher.post(new UiModelEvent(DEVICE_ADDED_OR_UPDATED, uiDevice));
142 } 242 }
143 243
244 + // invoked from UiSharedTopologyModel device listener
144 void removeDevice(Device device) { 245 void removeDevice(Device device) {
145 - // TODO: get UiDevice associated with the given parameter; remove from model 246 + DeviceId id = device.id();
146 - // FIXME 247 + UiDevice uiDevice = uiTopology.findDevice(id);
147 - UiDevice uiDevice = new UiDevice(); 248 + if (uiDevice != null) {
249 + uiTopology.remove(uiDevice);
250 + postEvent(DEVICE_REMOVED, uiDevice);
251 + } else {
252 + log.warn(E_NO_ELEMENT, "device", id);
253 + }
254 + }
255 +
256 +
257 + // === LINKS
258 +
259 + private UiLink addNewLink(UiLinkId id) {
260 + UiLink uiLink = new UiLink(uiTopology, id);
261 + uiTopology.add(uiLink);
262 + return uiLink;
263 + }
264 +
265 + private void updateLink(UiLink uiLink, Link link) {
266 + uiLink.attachBackingLink(link);
267 + }
148 268
149 - // TODO: post the (correct) event 269 + private void loadLinks() {
150 - dispatcher.post(new UiModelEvent(DEVICE_REMOVED, uiDevice)); 270 + for (Link link : services.link().getLinks()) {
271 + UiLinkId id = uiLinkId(link);
151 272
273 + UiLink uiLink = uiTopology.findLink(id);
274 + if (uiLink == null) {
275 + uiLink = addNewLink(id);
276 + }
277 + updateLink(uiLink, link);
278 + }
152 } 279 }
153 280
281 + // invoked from UiSharedTopologyModel link listener
154 void addOrUpdateLink(Link link) { 282 void addOrUpdateLink(Link link) {
155 - // TODO: find ui-link assoc. with parameter; create or update. 283 + UiLinkId id = uiLinkId(link);
156 - // TODO: post event 284 + UiLink uiLink = uiTopology.findLink(id);
285 + if (uiLink == null) {
286 + uiLink = addNewLink(id);
287 + }
288 + updateLink(uiLink, link);
289 +
290 + postEvent(LINK_ADDED_OR_UPDATED, uiLink);
157 } 291 }
158 292
293 + // invoked from UiSharedTopologyModel link listener
159 void removeLink(Link link) { 294 void removeLink(Link link) {
160 - // TODO: find ui-link assoc. with parameter; update or remove. 295 + UiLinkId id = uiLinkId(link);
161 - // TODO: post event 296 + UiLink uiLink = uiTopology.findLink(id);
297 + if (uiLink != null) {
298 + boolean remaining = uiLink.detachBackingLink(link);
299 + if (remaining) {
300 + postEvent(LINK_ADDED_OR_UPDATED, uiLink);
301 + } else {
302 + uiTopology.remove(uiLink);
303 + postEvent(LINK_REMOVED, uiLink);
304 + }
305 + } else {
306 + log.warn(E_NO_ELEMENT, "link", id);
307 + }
308 + }
309 +
310 +
311 + // === HOSTS
312 +
313 + private UiHost addNewHost(Host h) {
314 + UiHost host = new UiHost(uiTopology, h);
315 + uiTopology.add(host);
316 +
317 + UiLink edgeLink = addNewEdgeLink(host);
318 + host.setEdgeLinkId(edgeLink.id());
319 +
320 + return host;
321 + }
322 +
323 + private void removeOldEdgeLink(UiHost uiHost) {
324 + UiLink old = uiTopology.findLink(uiHost.edgeLinkId());
325 + if (old != null) {
326 + uiTopology.remove(old);
327 + }
328 + }
329 +
330 + private UiLink addNewEdgeLink(UiHost uiHost) {
331 + EdgeLink elink = createEdgeLink(uiHost.backingHost(), true);
332 + UiLinkId elinkId = UiLinkId.uiLinkId(elink);
333 + UiLink uiLink = addNewLink(elinkId);
334 + uiLink.attachEdgeLink(elink);
335 + return uiLink;
336 + }
337 +
338 + private void updateHost(UiHost uiHost, Host h) {
339 + removeOldEdgeLink(uiHost);
340 + HostLocation hloc = h.location();
341 + uiHost.setLocation(hloc.deviceId(), hloc.port());
342 + addNewEdgeLink(uiHost);
162 } 343 }
163 344
345 + private void loadHosts() {
346 + for (Host h : services.host().getHosts()) {
347 + UiHost host = addNewHost(h);
348 + updateHost(host, h);
349 + }
350 + }
351 +
352 + // invoked from UiSharedTopologyModel host listener
164 void addOrUpdateHost(Host host) { 353 void addOrUpdateHost(Host host) {
165 - // TODO: find or create host assoc. with parameter 354 + HostId id = host.id();
166 - // TODO: post event 355 + UiHost uiHost = uiTopology.findHost(id);
356 + if (uiHost == null) {
357 + uiHost = addNewHost(host);
358 + }
359 + updateHost(uiHost, host);
360 +
361 + postEvent(HOST_ADDED_OR_UPDATED, uiHost);
167 } 362 }
168 363
364 + // invoked from UiSharedTopologyModel host listener
169 void moveHost(Host host, Host prevHost) { 365 void moveHost(Host host, Host prevHost) {
170 - // TODO: process host-move 366 + UiHost uiHost = uiTopology.findHost(prevHost.id());
171 - // TODO: post event 367 + updateHost(uiHost, host);
368 +
369 + postEvent(HOST_MOVED, uiHost);
172 } 370 }
173 371
372 + // invoked from UiSharedTopologyModel host listener
174 void removeHost(Host host) { 373 void removeHost(Host host) {
175 - // TODO: find host assoc. with parameter; remove from model 374 + HostId id = host.id();
375 + UiHost uiHost = uiTopology.findHost(id);
376 + if (uiHost != null) {
377 + uiTopology.remove(uiHost);
378 + removeOldEdgeLink(uiHost);
379 + postEvent(HOST_REMOVED, uiHost);
380 + } else {
381 + log.warn(E_NO_ELEMENT, "host", id);
176 } 382 }
383 + }
384 +
385 +
386 + // === CACHE STATISTICS
177 387
178 /** 388 /**
179 * Returns the number of members in the cluster. 389 * Returns the number of members in the cluster.
...@@ -185,7 +395,7 @@ class ModelCache { ...@@ -185,7 +395,7 @@ class ModelCache {
185 } 395 }
186 396
187 /** 397 /**
188 - * Returns the number of regions configured in the topology. 398 + * Returns the number of regions in the topology.
189 * 399 *
190 * @return number of regions 400 * @return number of regions
191 */ 401 */
......
...@@ -32,9 +32,17 @@ public class UiModelEvent extends AbstractEvent<UiModelEvent.Type, UiElement> { ...@@ -32,9 +32,17 @@ public class UiModelEvent extends AbstractEvent<UiModelEvent.Type, UiElement> {
32 CLUSTER_MEMBER_ADDED_OR_UPDATED, 32 CLUSTER_MEMBER_ADDED_OR_UPDATED,
33 CLUSTER_MEMBER_REMOVED, 33 CLUSTER_MEMBER_REMOVED,
34 34
35 + REGION_ADDED_OR_UPDATED,
36 + REGION_REMOVED,
37 +
35 DEVICE_ADDED_OR_UPDATED, 38 DEVICE_ADDED_OR_UPDATED,
36 DEVICE_REMOVED, 39 DEVICE_REMOVED,
37 40
38 - // TODO... 41 + LINK_ADDED_OR_UPDATED,
42 + LINK_REMOVED,
43 +
44 + HOST_ADDED_OR_UPDATED,
45 + HOST_MOVED,
46 + HOST_REMOVED
39 } 47 }
40 } 48 }
......
...@@ -16,38 +16,241 @@ ...@@ -16,38 +16,241 @@
16 16
17 package org.onosproject.ui.impl.topo.model; 17 package org.onosproject.ui.impl.topo.model;
18 18
19 +import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableSet; 20 import com.google.common.collect.ImmutableSet;
21 +import org.onlab.packet.IpAddress;
22 +import org.onlab.packet.MacAddress;
23 +import org.onlab.packet.VlanId;
20 import org.onosproject.cluster.ClusterService; 24 import org.onosproject.cluster.ClusterService;
21 import org.onosproject.cluster.ClusterServiceAdapter; 25 import org.onosproject.cluster.ClusterServiceAdapter;
22 import org.onosproject.cluster.ControllerNode; 26 import org.onosproject.cluster.ControllerNode;
27 +import org.onosproject.cluster.DefaultControllerNode;
23 import org.onosproject.cluster.NodeId; 28 import org.onosproject.cluster.NodeId;
29 +import org.onosproject.cluster.RoleInfo;
24 import org.onosproject.mastership.MastershipService; 30 import org.onosproject.mastership.MastershipService;
25 import org.onosproject.mastership.MastershipServiceAdapter; 31 import org.onosproject.mastership.MastershipServiceAdapter;
32 +import org.onosproject.net.ConnectPoint;
33 +import org.onosproject.net.DefaultDevice;
34 +import org.onosproject.net.DefaultHost;
35 +import org.onosproject.net.DefaultLink;
36 +import org.onosproject.net.Device;
26 import org.onosproject.net.DeviceId; 37 import org.onosproject.net.DeviceId;
38 +import org.onosproject.net.Host;
39 +import org.onosproject.net.HostId;
40 +import org.onosproject.net.HostLocation;
41 +import org.onosproject.net.Link;
42 +import org.onosproject.net.PortNumber;
27 import org.onosproject.net.device.DeviceService; 43 import org.onosproject.net.device.DeviceService;
44 +import org.onosproject.net.device.DeviceServiceAdapter;
28 import org.onosproject.net.flow.FlowRuleService; 45 import org.onosproject.net.flow.FlowRuleService;
29 import org.onosproject.net.host.HostService; 46 import org.onosproject.net.host.HostService;
47 +import org.onosproject.net.host.HostServiceAdapter;
30 import org.onosproject.net.intent.IntentService; 48 import org.onosproject.net.intent.IntentService;
31 import org.onosproject.net.link.LinkService; 49 import org.onosproject.net.link.LinkService;
50 +import org.onosproject.net.link.LinkServiceAdapter;
51 +import org.onosproject.net.provider.ProviderId;
52 +import org.onosproject.net.region.DefaultRegion;
53 +import org.onosproject.net.region.Region;
54 +import org.onosproject.net.region.RegionId;
55 +import org.onosproject.net.region.RegionListener;
32 import org.onosproject.net.region.RegionService; 56 import org.onosproject.net.region.RegionService;
33 import org.onosproject.ui.impl.AbstractUiImplTest; 57 import org.onosproject.ui.impl.AbstractUiImplTest;
34 import org.onosproject.ui.model.ServiceBundle; 58 import org.onosproject.ui.model.ServiceBundle;
35 59
60 +import java.util.ArrayList;
61 +import java.util.Collections;
36 import java.util.HashMap; 62 import java.util.HashMap;
63 +import java.util.HashSet;
64 +import java.util.List;
37 import java.util.Map; 65 import java.util.Map;
38 import java.util.Set; 66 import java.util.Set;
39 67
68 +import static org.onosproject.cluster.NodeId.nodeId;
40 import static org.onosproject.net.DeviceId.deviceId; 69 import static org.onosproject.net.DeviceId.deviceId;
70 +import static org.onosproject.net.HostId.hostId;
71 +import static org.onosproject.net.PortNumber.portNumber;
41 72
42 /** 73 /**
43 * Base class for model test classes. 74 * Base class for model test classes.
44 */ 75 */
45 abstract class AbstractTopoModelTest extends AbstractUiImplTest { 76 abstract class AbstractTopoModelTest extends AbstractUiImplTest {
46 77
78 + /*
79 + Our mock environment:
80 +
81 + Three controllers: C1, C2, C3
82 +
83 + Nine devices: D1 .. D9
84 +
85 + D4 ---+ +--- D7
86 + | |
87 + D5 --- D1 --- D2 --- D3 --- D8
88 + | |
89 + D6 ---+ +--- D9
90 +
91 + Twelve hosts (two per D4 ... D9) H4a, H4b, H5a, H5b, ...
92 +
93 + Regions:
94 + R1 : D1, D2, D3
95 + R2 : D4, D5, D6
96 + R3 : D7, D8, D9
97 +
98 + Mastership:
99 + C1 : D1, D2, D3
100 + C2 : D4, D5, D6
101 + C3 : D7, D8, D9
102 +
103 + Roles: (backups)
104 + C1 -> C2, C3
105 + C2 -> C1, C3
106 + C3 -> C1, C2
107 + */
108 +
109 + protected static final String C1 = "C1";
110 + protected static final String C2 = "C2";
111 + protected static final String C3 = "C3";
112 +
113 + protected static final NodeId CNID_1 = nodeId(C1);
114 + protected static final NodeId CNID_2 = nodeId(C2);
115 + protected static final NodeId CNID_3 = nodeId(C3);
116 +
117 + protected static final ControllerNode CNODE_1 = cnode(CNID_1, "10.0.0.1");
118 + protected static final ControllerNode CNODE_2 = cnode(CNID_2, "10.0.0.2");
119 + protected static final ControllerNode CNODE_3 = cnode(CNID_3, "10.0.0.3");
120 +
121 + protected static final String R1 = "R1";
122 + protected static final String R2 = "R2";
123 + protected static final String R3 = "R3";
124 +
125 + protected static final Set<NodeId> SET_C1 = ImmutableSet.of(CNID_1);
126 + protected static final Set<NodeId> SET_C2 = ImmutableSet.of(CNID_2);
127 + protected static final Set<NodeId> SET_C3 = ImmutableSet.of(CNID_3);
128 +
129 + protected static final Region REGION_1 =
130 + region(R1, Region.Type.METRO, ImmutableList.of(SET_C1, SET_C2));
131 + protected static final Region REGION_2 =
132 + region(R2, Region.Type.CAMPUS, ImmutableList.of(SET_C2, SET_C1));
133 + protected static final Region REGION_3 =
134 + region(R3, Region.Type.CAMPUS, ImmutableList.of(SET_C3, SET_C1));
135 +
136 + protected static final Set<Region> REGION_SET =
137 + ImmutableSet.of(REGION_1, REGION_2, REGION_3);
138 +
139 + protected static final String D1 = "D1";
140 + protected static final String D2 = "D2";
141 + protected static final String D3 = "D3";
142 + protected static final String D4 = "D4";
143 + protected static final String D5 = "D5";
144 + protected static final String D6 = "D6";
145 + protected static final String D7 = "D7";
146 + protected static final String D8 = "D8";
147 + protected static final String D9 = "D9";
148 +
149 + protected static final String MFR = "Mfr";
150 + protected static final String HW = "h/w";
151 + protected static final String SW = "s/w";
152 + protected static final String SERIAL = "ser123";
153 +
154 + protected static final DeviceId DEVID_1 = deviceId(D1);
155 + protected static final DeviceId DEVID_2 = deviceId(D2);
156 + protected static final DeviceId DEVID_3 = deviceId(D3);
157 + protected static final DeviceId DEVID_4 = deviceId(D4);
158 + protected static final DeviceId DEVID_5 = deviceId(D5);
159 + protected static final DeviceId DEVID_6 = deviceId(D6);
160 + protected static final DeviceId DEVID_7 = deviceId(D7);
161 + protected static final DeviceId DEVID_8 = deviceId(D8);
162 + protected static final DeviceId DEVID_9 = deviceId(D9);
163 +
164 + protected static final Device DEV_1 = device(D1);
165 + protected static final Device DEV_2 = device(D2);
166 + protected static final Device DEV_3 = device(D3);
167 + protected static final Device DEV_4 = device(D4);
168 + protected static final Device DEV_5 = device(D5);
169 + protected static final Device DEV_6 = device(D6);
170 + protected static final Device DEV_7 = device(D7);
171 + protected static final Device DEV_8 = device(D8);
172 + protected static final Device DEV_9 = device(D9);
173 +
174 + protected static final List<Device> ALL_DEVS =
175 + ImmutableList.of(
176 + DEV_1, DEV_2, DEV_3,
177 + DEV_4, DEV_5, DEV_6,
178 + DEV_7, DEV_8, DEV_9
179 + );
180 +
181 + private static final Set<DeviceId> DEVS_TRUNK =
182 + ImmutableSet.of(DEVID_1, DEVID_2, DEVID_3);
183 +
184 + private static final Set<DeviceId> DEVS_LEFT =
185 + ImmutableSet.of(DEVID_4, DEVID_5, DEVID_6);
186 +
187 + private static final Set<DeviceId> DEVS_RIGHT =
188 + ImmutableSet.of(DEVID_7, DEVID_8, DEVID_9);
189 +
190 + private static final String[][] LINK_CONNECT_DATA = {
191 + {D1, "12", D2, "21"},
192 + {D2, "23", D3, "32"},
193 + {D4, "41", D1, "14"},
194 + {D5, "51", D1, "15"},
195 + {D6, "61", D1, "16"},
196 + {D7, "73", D3, "37"},
197 + {D8, "83", D3, "38"},
198 + {D9, "93", D3, "39"},
199 + };
200 +
201 + private static final String HOST_MAC_PREFIX = "aa:00:00:00:00:";
202 +
203 + /**
204 + * Returns IP address instance for given string.
205 + *
206 + * @param s string
207 + * @return IP address
208 + */
209 + protected static IpAddress ip(String s) {
210 + return IpAddress.valueOf(s);
211 + }
212 +
213 + /**
214 + * Returns controller node instance for given ID and IP.
215 + *
216 + * @param id identifier
217 + * @param ip IP address
218 + * @return controller node instance
219 + */
220 + protected static ControllerNode cnode(NodeId id, String ip) {
221 + return new DefaultControllerNode(id, ip(ip));
222 + }
223 +
224 + /**
225 + * Returns a region instance with specified parameters.
226 + *
227 + * @param id region id
228 + * @param type region type
229 + * @param masters ordered list of master sets
230 + * @return region instance
231 + */
232 + protected static Region region(String id, Region.Type type,
233 + List<Set<NodeId>> masters) {
234 + return new DefaultRegion(RegionId.regionId(id), "Region-" + id,
235 + type, masters);
236 + }
237 +
238 + /**
239 + * Returns device with given ID.
240 + *
241 + * @param id device ID
242 + * @return device instance
243 + */
244 + protected static Device device(String id) {
245 + return new DefaultDevice(ProviderId.NONE, deviceId(id),
246 + Device.Type.SWITCH, MFR, HW, SW, SERIAL, null);
247 + }
248 +
47 /** 249 /**
48 * Returns canned results. 250 * Returns canned results.
251 + * <p>
49 * At some future point, we may make this "programmable", so that 252 * At some future point, we may make this "programmable", so that
50 - * it returns certain values based on element IDs etc. 253 + * its state can be changed over the course of a unit test.
51 */ 254 */
52 protected static final ServiceBundle MOCK_SERVICES = 255 protected static final ServiceBundle MOCK_SERVICES =
53 new ServiceBundle() { 256 new ServiceBundle() {
...@@ -63,22 +266,22 @@ abstract class AbstractTopoModelTest extends AbstractUiImplTest { ...@@ -63,22 +266,22 @@ abstract class AbstractTopoModelTest extends AbstractUiImplTest {
63 266
64 @Override 267 @Override
65 public RegionService region() { 268 public RegionService region() {
66 - return null; 269 + return MOCK_REGION;
67 } 270 }
68 271
69 @Override 272 @Override
70 public DeviceService device() { 273 public DeviceService device() {
71 - return null; 274 + return MOCK_DEVICE;
72 } 275 }
73 276
74 @Override 277 @Override
75 public LinkService link() { 278 public LinkService link() {
76 - return null; 279 + return MOCK_LINK;
77 } 280 }
78 281
79 @Override 282 @Override
80 public HostService host() { 283 public HostService host() {
81 - return null; 284 + return MOCK_HOST;
82 } 285 }
83 286
84 @Override 287 @Override
...@@ -94,64 +297,286 @@ abstract class AbstractTopoModelTest extends AbstractUiImplTest { ...@@ -94,64 +297,286 @@ abstract class AbstractTopoModelTest extends AbstractUiImplTest {
94 297
95 private static final ClusterService MOCK_CLUSTER = new MockClusterService(); 298 private static final ClusterService MOCK_CLUSTER = new MockClusterService();
96 private static final MastershipService MOCK_MASTER = new MockMasterService(); 299 private static final MastershipService MOCK_MASTER = new MockMasterService();
97 - // TODO: fill out as necessary 300 + private static final RegionService MOCK_REGION = new MockRegionService();
301 + private static final DeviceService MOCK_DEVICE = new MockDeviceService();
302 + private static final LinkService MOCK_LINK = new MockLinkService();
303 + private static final HostService MOCK_HOST = new MockHostService();
98 304
99 - /*
100 - Our mock environment:
101 305
102 - Three controllers: C1, C2, C3
103 306
104 - Nine devices: D1 .. D9 307 + private static class MockClusterService extends ClusterServiceAdapter {
105 - 308 + private final Map<NodeId, ControllerNode> nodes = new HashMap<>();
106 - D4 ---+ +--- D7 309 + private final Map<NodeId, ControllerNode.State> states = new HashMap<>();
107 - | |
108 - D5 --- D1 --- D2 --- D3 --- D8
109 - | |
110 - D6 ---+ +--- D9
111 310
112 - Twelve hosts (two per D4 ... D9) H41, H42, H51, H52, ... 311 + MockClusterService() {
113 - 312 + nodes.put(CNODE_1.id(), CNODE_1);
114 - Regions: 313 + nodes.put(CNODE_2.id(), CNODE_2);
115 - R1 : D1, D2, D3 314 + nodes.put(CNODE_3.id(), CNODE_3);
116 - R2 : D4, D5, D6
117 - R3 : D7, D8, D9
118 -
119 - Mastership:
120 - C1 : D1, D2, D3
121 - C2 : D4, D5, D6
122 - C3 : D7, D8, D9
123 - */
124 315
316 + states.put(CNODE_1.id(), ControllerNode.State.READY);
317 + states.put(CNODE_2.id(), ControllerNode.State.ACTIVE);
318 + states.put(CNODE_3.id(), ControllerNode.State.ACTIVE);
319 + }
125 320
126 - private static class MockClusterService extends ClusterServiceAdapter { 321 + @Override
127 - private final Map<NodeId, ControllerNode.State> states = new HashMap<>(); 322 + public Set<ControllerNode> getNodes() {
323 + return ImmutableSet.copyOf(nodes.values());
324 + }
128 325
326 + @Override
327 + public ControllerNode getNode(NodeId nodeId) {
328 + return nodes.get(nodeId);
329 + }
129 330
130 @Override 331 @Override
131 public ControllerNode.State getState(NodeId nodeId) { 332 public ControllerNode.State getState(NodeId nodeId) {
132 - // For now, a hardcoded state of ACTIVE (but not READY) 333 + return states.get(nodeId);
133 - // irrespective of the node ID.
134 - return ControllerNode.State.ACTIVE;
135 } 334 }
136 } 335 }
137 336
138 - protected static final DeviceId D1_ID = deviceId("D1");
139 - protected static final DeviceId D2_ID = deviceId("D2");
140 - protected static final DeviceId D3_ID = deviceId("D3");
141 - protected static final DeviceId D4_ID = deviceId("D4");
142 - protected static final DeviceId D5_ID = deviceId("D5");
143 - protected static final DeviceId D6_ID = deviceId("D6");
144 - protected static final DeviceId D7_ID = deviceId("D7");
145 - protected static final DeviceId D8_ID = deviceId("D8");
146 - protected static final DeviceId D9_ID = deviceId("D9");
147 337
148 private static class MockMasterService extends MastershipServiceAdapter { 338 private static class MockMasterService extends MastershipServiceAdapter {
339 + private final Map<NodeId, Set<DeviceId>> masterOf = new HashMap<>();
340 +
341 + MockMasterService() {
342 + masterOf.put(CNODE_1.id(), DEVS_TRUNK);
343 + masterOf.put(CNODE_2.id(), DEVS_LEFT);
344 + masterOf.put(CNODE_3.id(), DEVS_RIGHT);
345 + }
346 +
347 + @Override
348 + public NodeId getMasterFor(DeviceId deviceId) {
349 + if (DEVS_TRUNK.contains(deviceId)) {
350 + return CNID_1;
351 + }
352 + if (DEVS_LEFT.contains(deviceId)) {
353 + return CNID_2;
354 + }
355 + if (DEVS_RIGHT.contains(deviceId)) {
356 + return CNID_3;
357 + }
358 + return null;
359 + }
360 +
149 @Override 361 @Override
150 public Set<DeviceId> getDevicesOf(NodeId nodeId) { 362 public Set<DeviceId> getDevicesOf(NodeId nodeId) {
151 - // For now, a hard coded set of two device IDs 363 + return masterOf.get(nodeId);
152 - // irrespective of the node ID. 364 + }
153 - return ImmutableSet.of(D1_ID, D2_ID); 365 +
366 + @Override
367 + public RoleInfo getNodesFor(DeviceId deviceId) {
368 + NodeId master = null;
369 + List<NodeId> backups = new ArrayList<>();
370 +
371 + if (DEVS_TRUNK.contains(deviceId)) {
372 + master = CNID_1;
373 + backups.add(CNID_2);
374 + backups.add(CNID_3);
375 + } else if (DEVS_LEFT.contains(deviceId)) {
376 + master = CNID_2;
377 + backups.add(CNID_1);
378 + backups.add(CNID_3);
379 + } else if (DEVS_RIGHT.contains(deviceId)) {
380 + master = CNID_3;
381 + backups.add(CNID_1);
382 + backups.add(CNID_2);
383 + }
384 + return new RoleInfo(master, backups);
385 + }
386 + }
387 +
388 + // TODO: consider implementing RegionServiceAdapter and extending that here
389 + private static class MockRegionService implements RegionService {
390 +
391 + private final Map<RegionId, Region> lookup = new HashMap<>();
392 +
393 + MockRegionService() {
394 + lookup.put(REGION_1.id(), REGION_1);
395 + lookup.put(REGION_2.id(), REGION_2);
396 + lookup.put(REGION_3.id(), REGION_3);
397 + }
398 +
399 + @Override
400 + public Set<Region> getRegions() {
401 + return REGION_SET;
402 + }
403 +
404 + @Override
405 + public Region getRegion(RegionId regionId) {
406 + return lookup.get(regionId);
407 + }
408 +
409 + @Override
410 + public Region getRegionForDevice(DeviceId deviceId) {
411 + if (DEVS_TRUNK.contains(deviceId)) {
412 + return REGION_1;
413 + }
414 + if (DEVS_LEFT.contains(deviceId)) {
415 + return REGION_2;
416 + }
417 + if (DEVS_RIGHT.contains(deviceId)) {
418 + return REGION_3;
419 + }
420 + return null;
421 + }
422 +
423 + @Override
424 + public Set<DeviceId> getRegionDevices(RegionId regionId) {
425 + if (REGION_1.id().equals(regionId)) {
426 + return DEVS_TRUNK;
427 + }
428 + if (REGION_2.id().equals(regionId)) {
429 + return DEVS_LEFT;
430 + }
431 + if (REGION_3.id().equals(regionId)) {
432 + return DEVS_RIGHT;
433 + }
434 + return Collections.emptySet();
435 + }
436 +
437 + @Override
438 + public void addListener(RegionListener listener) {
439 + }
440 +
441 + @Override
442 + public void removeListener(RegionListener listener) {
443 + }
444 + }
445 +
446 +
447 + private static class MockDeviceService extends DeviceServiceAdapter {
448 + private final Map<DeviceId, Device> devices = new HashMap<>();
449 +
450 + MockDeviceService() {
451 + for (Device dev : ALL_DEVS) {
452 + devices.put(dev.id(), dev);
453 + }
454 + }
455 +
456 + @Override
457 + public int getDeviceCount() {
458 + return devices.size();
459 + }
460 +
461 + @Override
462 + public Iterable<Device> getDevices() {
463 + return ImmutableList.copyOf(devices.values());
464 + }
465 +
466 + @Override
467 + public Device getDevice(DeviceId deviceId) {
468 + return devices.get(deviceId);
469 + }
470 +
471 + }
472 +
473 +
474 + private static class MockLinkService extends LinkServiceAdapter {
475 + private final Set<Link> links = new HashSet<>();
476 +
477 + MockLinkService() {
478 + for (String[] linkPair : LINK_CONNECT_DATA) {
479 + links.addAll(makeLinks(linkPair));
480 + }
481 +
482 + }
483 +
484 + private Set<Link> makeLinks(String[] linkPair) {
485 + DeviceId devA = deviceId(linkPair[0]);
486 + PortNumber portA = portNumber(Long.valueOf(linkPair[1]));
487 + DeviceId devB = deviceId(linkPair[2]);
488 + PortNumber portB = portNumber(Long.valueOf(linkPair[3]));
489 +
490 + Link linkA = DefaultLink.builder()
491 + .providerId(ProviderId.NONE)
492 + .type(Link.Type.DIRECT)
493 + .src(new ConnectPoint(devA, portA))
494 + .dst(new ConnectPoint(devB, portB))
495 + .build();
496 +
497 + Link linkB = DefaultLink.builder()
498 + .providerId(ProviderId.NONE)
499 + .type(Link.Type.DIRECT)
500 + .src(new ConnectPoint(devB, portB))
501 + .dst(new ConnectPoint(devA, portA))
502 + .build();
503 +
504 + return ImmutableSet.of(linkA, linkB);
505 + }
506 +
507 + @Override
508 + public int getLinkCount() {
509 + return links.size();
510 + }
511 +
512 + @Override
513 + public Iterable<Link> getLinks() {
514 + return ImmutableSet.copyOf(links);
515 + }
516 +
517 + // TODO: possibly fill out other methods if we find the model uses them
154 } 518 }
519 +
520 +
521 + private static class MockHostService extends HostServiceAdapter {
522 + private final Map<HostId, Host> hosts = new HashMap<>();
523 +
524 + MockHostService() {
525 + for (Device d : ALL_DEVS) {
526 + // two hosts per device
527 + createHosts(hosts, d);
528 + }
529 + }
530 +
531 + private void createHosts(Map<HostId, Host> hosts, Device d) {
532 + DeviceId deviceId = d.id();
533 + String devNum = deviceId.toString().substring(1);
534 +
535 + String ha = devNum + "a";
536 + String hb = devNum + "b";
537 +
538 + MacAddress macA = MacAddress.valueOf(HOST_MAC_PREFIX + ha);
539 + MacAddress macB = MacAddress.valueOf(HOST_MAC_PREFIX + hb);
540 +
541 + HostId hostA = hostId(String.format("%s/-1", macA));
542 + HostId hostB = hostId(String.format("%s/-1", macB));
543 +
544 + PortNumber portA = portNumber(101);
545 + PortNumber portB = portNumber(102);
546 +
547 + HostLocation locA = new HostLocation(deviceId, portA, 0);
548 + HostLocation locB = new HostLocation(deviceId, portB, 0);
549 +
550 + IpAddress ipA = ip("10." + devNum + ".0.1");
551 + IpAddress ipB = ip("10." + devNum + ".0.2");
552 +
553 + Host host = new DefaultHost(ProviderId.NONE,
554 + hostA, macA, VlanId.NONE, locA,
555 + ImmutableSet.of(ipA));
556 + hosts.put(hostA, host);
557 +
558 + host = new DefaultHost(ProviderId.NONE,
559 + hostB, macB, VlanId.NONE, locB,
560 + ImmutableSet.of(ipB));
561 + hosts.put(hostB, host);
562 + }
563 +
564 + @Override
565 + public int getHostCount() {
566 + return hosts.size();
567 + }
568 +
569 + @Override
570 + public Iterable<Host> getHosts() {
571 + return ImmutableSet.copyOf(hosts.values());
572 + }
573 +
574 + @Override
575 + public Host getHost(HostId hostId) {
576 + return hosts.get(hostId);
577 + }
578 +
579 + // TODO: possibly fill out other methods, should the model require them
155 } 580 }
156 581
157 } 582 }
......
...@@ -18,16 +18,17 @@ package org.onosproject.ui.impl.topo.model; ...@@ -18,16 +18,17 @@ package org.onosproject.ui.impl.topo.model;
18 18
19 import org.junit.Before; 19 import org.junit.Before;
20 import org.junit.Test; 20 import org.junit.Test;
21 -import org.onlab.packet.IpAddress;
22 -import org.onosproject.cluster.ControllerNode;
23 -import org.onosproject.cluster.DefaultControllerNode;
24 import org.onosproject.event.Event; 21 import org.onosproject.event.Event;
25 import org.onosproject.event.EventDispatcher; 22 import org.onosproject.event.EventDispatcher;
23 +import org.onosproject.net.DeviceId;
26 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type; 24 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type;
25 +import org.onosproject.ui.model.topo.UiClusterMember;
27 import org.onosproject.ui.model.topo.UiElement; 26 import org.onosproject.ui.model.topo.UiElement;
28 27
29 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertEquals;
29 +import static org.junit.Assert.assertFalse;
30 import static org.junit.Assert.assertNotNull; 30 import static org.junit.Assert.assertNotNull;
31 +import static org.junit.Assert.assertTrue;
31 import static org.onosproject.cluster.NodeId.nodeId; 32 import static org.onosproject.cluster.NodeId.nodeId;
32 33
33 /** 34 /**
...@@ -58,22 +59,6 @@ public class ModelCacheTest extends AbstractTopoModelTest { ...@@ -58,22 +59,6 @@ public class ModelCacheTest extends AbstractTopoModelTest {
58 } 59 }
59 } 60 }
60 61
61 - private static IpAddress ip(String s) {
62 - return IpAddress.valueOf(s);
63 - }
64 -
65 - private static ControllerNode cnode(String id, String ip) {
66 - return new DefaultControllerNode(nodeId(id), ip(ip));
67 - }
68 -
69 - private static final String C1 = "C1";
70 - private static final String C2 = "C2";
71 - private static final String C3 = "C3";
72 -
73 - private static final ControllerNode NODE_1 = cnode(C1, "10.0.0.1");
74 - private static final ControllerNode NODE_2 = cnode(C2, "10.0.0.2");
75 - private static final ControllerNode NODE_3 = cnode(C3, "10.0.0.3");
76 -
77 62
78 private final TestEvDisp dispatcher = new TestEvDisp(); 63 private final TestEvDisp dispatcher = new TestEvDisp();
79 64
...@@ -99,13 +84,13 @@ public class ModelCacheTest extends AbstractTopoModelTest { ...@@ -99,13 +84,13 @@ public class ModelCacheTest extends AbstractTopoModelTest {
99 assertEquals("unex # members", 0, cache.clusterMemberCount()); 84 assertEquals("unex # members", 0, cache.clusterMemberCount());
100 dispatcher.assertEventCount(0); 85 dispatcher.assertEventCount(0);
101 86
102 - cache.addOrUpdateClusterMember(NODE_1); 87 + cache.addOrUpdateClusterMember(CNODE_1);
103 print(cache); 88 print(cache);
104 assertEquals("unex # members", 1, cache.clusterMemberCount()); 89 assertEquals("unex # members", 1, cache.clusterMemberCount());
105 dispatcher.assertEventCount(1); 90 dispatcher.assertEventCount(1);
106 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); 91 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1);
107 92
108 - cache.removeClusterMember(NODE_1); 93 + cache.removeClusterMember(CNODE_1);
109 print(cache); 94 print(cache);
110 assertEquals("unex # members", 0, cache.clusterMemberCount()); 95 assertEquals("unex # members", 0, cache.clusterMemberCount());
111 dispatcher.assertEventCount(2); 96 dispatcher.assertEventCount(2);
...@@ -115,14 +100,69 @@ public class ModelCacheTest extends AbstractTopoModelTest { ...@@ -115,14 +100,69 @@ public class ModelCacheTest extends AbstractTopoModelTest {
115 @Test 100 @Test
116 public void createThreeNodeCluster() { 101 public void createThreeNodeCluster() {
117 title("createThreeNodeCluster"); 102 title("createThreeNodeCluster");
118 - cache.addOrUpdateClusterMember(NODE_1); 103 + cache.addOrUpdateClusterMember(CNODE_1);
119 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1); 104 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1);
120 - cache.addOrUpdateClusterMember(NODE_2); 105 + cache.addOrUpdateClusterMember(CNODE_2);
121 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C2); 106 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C2);
122 - cache.addOrUpdateClusterMember(NODE_3); 107 + cache.addOrUpdateClusterMember(CNODE_3);
123 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C3); 108 dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C3);
124 dispatcher.assertEventCount(3); 109 dispatcher.assertEventCount(3);
125 print(cache); 110 print(cache);
126 } 111 }
127 112
113 + @Test
114 + public void addNodeThenExamineIt() {
115 + title("addNodeThenExamineIt");
116 + cache.addOrUpdateClusterMember(CNODE_1);
117 + dispatcher.assertLast(Type.CLUSTER_MEMBER_ADDED_OR_UPDATED, C1);
118 +
119 + UiClusterMember member = cache.accessClusterMember(nodeId(C1));
120 + print(member);
121 + // see AbstractUiImplTest Mock Environment for expected values...
122 + assertEquals("wrong id str", C1, member.idAsString());
123 + assertEquals("wrong id", nodeId(C1), member.id());
124 + assertEquals("wrong dev count", 3, member.deviceCount());
125 + assertEquals("not online", true, member.isOnline());
126 + assertEquals("not ready", true, member.isReady());
127 +
128 + assertMasterOf(member, DEVID_1, DEVID_2, DEVID_3);
129 + assertNotMasterOf(member, DEVID_4, DEVID_6, DEVID_9);
130 + }
131 +
132 + private void assertMasterOf(UiClusterMember member, DeviceId... ids) {
133 + for (DeviceId id : ids) {
134 + assertTrue("not master of " + id, member.masterOf(id));
135 + }
136 + }
137 +
138 + private void assertNotMasterOf(UiClusterMember member, DeviceId... ids) {
139 + for (DeviceId id : ids) {
140 + assertFalse("? master of " + id, member.masterOf(id));
141 + }
142 + }
143 +
144 +
145 + @Test
146 + public void addNodeAndDevices() {
147 + title("addNodeAndDevices");
148 + cache.addOrUpdateClusterMember(CNODE_1);
149 + cache.addOrUpdateDevice(DEV_1);
150 + cache.addOrUpdateDevice(DEV_2);
151 + cache.addOrUpdateDevice(DEV_3);
152 + print(cache);
153 + }
154 +
155 + @Test
156 + public void addRegions() {
157 + title("addRegions");
158 + cache.addOrUpdateRegion(REGION_1);
159 + print(cache);
160 + }
161 +
162 + @Test
163 + public void load() {
164 + title("load");
165 + cache.load();
166 + print(cache);
167 + }
128 } 168 }
......