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
Showing
17 changed files
with
1734 additions
and
274 deletions
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 | } | ... | ... |
-
Please register or login to post a comment