Committed by
Gerrit Code Review
GUI -- Major re-write of server-side Topology View code. WIP.
Change-Id: I332e8e59dc2271d3897c3a5c2fafa775324ef72d
Showing
10 changed files
with
873 additions
and
0 deletions
... | @@ -39,6 +39,7 @@ public final class JsonUtils { | ... | @@ -39,6 +39,7 @@ public final class JsonUtils { |
39 | * @param payload event payload | 39 | * @param payload event payload |
40 | * @return the object node representation | 40 | * @return the object node representation |
41 | */ | 41 | */ |
42 | + @Deprecated | ||
42 | public static ObjectNode envelope(String type, long sid, ObjectNode payload) { | 43 | public static ObjectNode envelope(String type, long sid, ObjectNode payload) { |
43 | ObjectNode event = MAPPER.createObjectNode(); | 44 | ObjectNode event = MAPPER.createObjectNode(); |
44 | event.put("event", type); | 45 | event.put("event", type); |
... | @@ -50,6 +51,20 @@ public final class JsonUtils { | ... | @@ -50,6 +51,20 @@ public final class JsonUtils { |
50 | } | 51 | } |
51 | 52 | ||
52 | /** | 53 | /** |
54 | + * Composes a message structure for the given message type and payload. | ||
55 | + * | ||
56 | + * @param type message type | ||
57 | + * @param payload message payload | ||
58 | + * @return the object node representation | ||
59 | + */ | ||
60 | + public static ObjectNode envelope(String type, ObjectNode payload) { | ||
61 | + ObjectNode event = MAPPER.createObjectNode(); | ||
62 | + event.put("event", type); | ||
63 | + event.set("payload", payload); | ||
64 | + return event; | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
53 | * Returns the event type from the specified event. | 68 | * Returns the event type from the specified event. |
54 | * If the node does not have an "event" property, "unknown" is returned. | 69 | * If the node does not have an "event" property, "unknown" is returned. |
55 | * | 70 | * | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import com.google.common.collect.ImmutableSet; | ||
22 | +import org.onlab.osgi.ServiceDirectory; | ||
23 | +import org.onosproject.core.CoreService; | ||
24 | +import org.onosproject.ui.RequestHandler; | ||
25 | +import org.onosproject.ui.UiConnection; | ||
26 | +import org.onosproject.ui.UiMessageHandler; | ||
27 | +import org.onosproject.ui.impl.topo.TopoUiEvent; | ||
28 | +import org.onosproject.ui.impl.topo.TopoUiListener; | ||
29 | +import org.onosproject.ui.impl.topo.TopoUiModelService; | ||
30 | +import org.slf4j.Logger; | ||
31 | +import org.slf4j.LoggerFactory; | ||
32 | + | ||
33 | +import java.util.Collection; | ||
34 | + | ||
35 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
36 | + | ||
37 | +/** | ||
38 | + * Facility for handling inbound messages from the topology view, and | ||
39 | + * generating outbound messages for the same. | ||
40 | + */ | ||
41 | +public class AltTopoViewMessageHandler extends UiMessageHandler { | ||
42 | + | ||
43 | + private static final String TOPO_START = "topoStart"; | ||
44 | + private static final String TOPO_STOP = "topoStop"; | ||
45 | + | ||
46 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
47 | + | ||
48 | + protected ServiceDirectory directory; | ||
49 | + protected TopoUiModelService modelService; | ||
50 | + | ||
51 | + private TopoUiListener modelListener; | ||
52 | + private String version; | ||
53 | + | ||
54 | + private boolean topoActive = false; | ||
55 | + | ||
56 | + @Override | ||
57 | + public void init(UiConnection connection, ServiceDirectory directory) { | ||
58 | + super.init(connection, directory); | ||
59 | + this.directory = checkNotNull(directory, "Directory cannot be null"); | ||
60 | + modelService = directory.get(TopoUiModelService.class); | ||
61 | + | ||
62 | + modelListener = new ModelListener(); | ||
63 | + version = getVersion(); | ||
64 | + } | ||
65 | + | ||
66 | + | ||
67 | + private String getVersion() { | ||
68 | + String ver = directory.get(CoreService.class).version().toString(); | ||
69 | + return ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", ""); | ||
70 | + } | ||
71 | + | ||
72 | + | ||
73 | + @Override | ||
74 | + protected Collection<RequestHandler> getHandlers() { | ||
75 | + return ImmutableSet.of( | ||
76 | + new TopoStart(), | ||
77 | + new TopoStop() | ||
78 | + ); | ||
79 | + } | ||
80 | + | ||
81 | + // ===================================================================== | ||
82 | + // Request Handlers for (topo view) events from the UI... | ||
83 | + | ||
84 | + private final class TopoStart extends RequestHandler { | ||
85 | + private TopoStart() { | ||
86 | + super(TOPO_START); | ||
87 | + } | ||
88 | + | ||
89 | + @Override | ||
90 | + public void process(long sid, ObjectNode payload) { | ||
91 | + topoActive = true; | ||
92 | + modelService.addListener(modelListener); | ||
93 | + sendMessages(modelService.getInitialState()); | ||
94 | + log.debug(TOPO_START); | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + private final class TopoStop extends RequestHandler { | ||
99 | + private TopoStop() { | ||
100 | + super(TOPO_STOP); | ||
101 | + } | ||
102 | + | ||
103 | + @Override | ||
104 | + public void process(long sid, ObjectNode payload) { | ||
105 | + topoActive = false; | ||
106 | + modelService.removeListener(modelListener); | ||
107 | + log.debug(TOPO_STOP); | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + // ===================================================================== | ||
112 | + // Private Helper Methods... | ||
113 | + | ||
114 | + private void sendMessages(Collection<ObjectNode> messages) { | ||
115 | + if (topoActive) { | ||
116 | + UiConnection connection = connection(); | ||
117 | + if (connection != null) { | ||
118 | + messages.forEach(connection::sendMessage); | ||
119 | + } | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + // ===================================================================== | ||
124 | + // Our listener for model events so we can push changes out to the UI... | ||
125 | + | ||
126 | + private class ModelListener implements TopoUiListener { | ||
127 | + @Override | ||
128 | + public void event(TopoUiEvent event) { | ||
129 | + log.debug("Handle Event: {}", event); | ||
130 | + // TODO: handle event | ||
131 | + } | ||
132 | + } | ||
133 | +} |
... | @@ -101,6 +101,7 @@ import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED; | ... | @@ -101,6 +101,7 @@ import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED; |
101 | /** | 101 | /** |
102 | * Facility for creating messages bound for the topology viewer. | 102 | * Facility for creating messages bound for the topology viewer. |
103 | */ | 103 | */ |
104 | +@Deprecated | ||
104 | public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | 105 | public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
105 | 106 | ||
106 | protected static final Logger log = | 107 | protected static final Logger log = | ... | ... |
... | @@ -78,6 +78,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { | ... | @@ -78,6 +78,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { |
78 | UiMessageHandlerFactory messageHandlerFactory = | 78 | UiMessageHandlerFactory messageHandlerFactory = |
79 | () -> ImmutableList.of( | 79 | () -> ImmutableList.of( |
80 | new TopologyViewMessageHandler(), | 80 | new TopologyViewMessageHandler(), |
81 | +// new AltTopoViewMessageHandler(), | ||
81 | new DeviceViewMessageHandler(), | 82 | new DeviceViewMessageHandler(), |
82 | new LinkViewMessageHandler(), | 83 | new LinkViewMessageHandler(), |
83 | new HostViewMessageHandler(), | 84 | new HostViewMessageHandler(), | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | + | ||
22 | +import java.util.Map; | ||
23 | +import java.util.concurrent.ConcurrentHashMap; | ||
24 | + | ||
25 | +/** | ||
26 | + * A database of meta information stored for topology objects. | ||
27 | + */ | ||
28 | +// package private | ||
29 | +class MetaDb { | ||
30 | + | ||
31 | + private static Map<String, ObjectNode> metaCache = new ConcurrentHashMap<>(); | ||
32 | + | ||
33 | + /** | ||
34 | + * Adds meta UI information about the specified object to the given payload. | ||
35 | + * | ||
36 | + * @param id object identifier | ||
37 | + * @param payload payload to which the info should be added | ||
38 | + */ | ||
39 | + public void addMetaUi(String id, ObjectNode payload) { | ||
40 | + ObjectNode meta = metaCache.get(id); | ||
41 | + if (meta != null) { | ||
42 | + payload.set("metaUi", meta); | ||
43 | + } | ||
44 | + } | ||
45 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
22 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
23 | +import org.onlab.packet.IpAddress; | ||
24 | +import org.onosproject.cluster.ClusterEvent; | ||
25 | +import org.onosproject.cluster.ClusterService; | ||
26 | +import org.onosproject.cluster.ControllerNode; | ||
27 | +import org.onosproject.cluster.NodeId; | ||
28 | +import org.onosproject.mastership.MastershipService; | ||
29 | +import org.onosproject.net.Annotated; | ||
30 | +import org.onosproject.net.AnnotationKeys; | ||
31 | +import org.onosproject.net.Annotations; | ||
32 | +import org.onosproject.net.ConnectPoint; | ||
33 | +import org.onosproject.net.DefaultEdgeLink; | ||
34 | +import org.onosproject.net.Device; | ||
35 | +import org.onosproject.net.DeviceId; | ||
36 | +import org.onosproject.net.EdgeLink; | ||
37 | +import org.onosproject.net.Host; | ||
38 | +import org.onosproject.net.HostId; | ||
39 | +import org.onosproject.net.HostLocation; | ||
40 | +import org.onosproject.net.Link; | ||
41 | +import org.onosproject.net.PortNumber; | ||
42 | +import org.onosproject.net.device.DeviceEvent; | ||
43 | +import org.onosproject.net.device.DeviceService; | ||
44 | +import org.onosproject.net.host.HostEvent; | ||
45 | +import org.onosproject.net.host.HostService; | ||
46 | +import org.onosproject.net.link.LinkEvent; | ||
47 | +import org.onosproject.net.link.LinkService; | ||
48 | +import org.onosproject.net.provider.ProviderId; | ||
49 | +import org.onosproject.ui.JsonUtils; | ||
50 | +import org.slf4j.Logger; | ||
51 | +import org.slf4j.LoggerFactory; | ||
52 | + | ||
53 | +import java.util.HashMap; | ||
54 | +import java.util.Iterator; | ||
55 | +import java.util.Map; | ||
56 | +import java.util.Set; | ||
57 | + | ||
58 | +import static com.google.common.base.Strings.isNullOrEmpty; | ||
59 | +import static org.onosproject.cluster.ControllerNode.State.ACTIVE; | ||
60 | +import static org.onosproject.net.PortNumber.portNumber; | ||
61 | + | ||
62 | +/** | ||
63 | + * Facility for generating messages in {@link ObjectNode} form from | ||
64 | + * ONOS model events. | ||
65 | + */ | ||
66 | +// package private | ||
67 | +class TopoMessageFactory { | ||
68 | + | ||
69 | + private static final ProviderId PROVIDER_ID = | ||
70 | + new ProviderId("core", "org.onosproject.core", true); | ||
71 | + private static final String COMPACT = "%s/%s-%s/%s"; | ||
72 | + private static final PortNumber PORT_ZERO = portNumber(0); | ||
73 | + | ||
74 | + private static final Map<Enum<?>, String> LOOKUP = new HashMap<>(); | ||
75 | + | ||
76 | + static { | ||
77 | + LOOKUP.put(ClusterEvent.Type.INSTANCE_ADDED, "addInstance"); | ||
78 | + LOOKUP.put(ClusterEvent.Type.INSTANCE_REMOVED, "removeInstance"); | ||
79 | + LOOKUP.put(DeviceEvent.Type.DEVICE_ADDED, "addDevice"); | ||
80 | + LOOKUP.put(DeviceEvent.Type.DEVICE_UPDATED, "updateDevice"); | ||
81 | + LOOKUP.put(DeviceEvent.Type.DEVICE_REMOVED, "removeDevice"); | ||
82 | + LOOKUP.put(LinkEvent.Type.LINK_ADDED, "addLink"); | ||
83 | + LOOKUP.put(LinkEvent.Type.LINK_UPDATED, "updateLink"); | ||
84 | + LOOKUP.put(LinkEvent.Type.LINK_REMOVED, "removeLink"); | ||
85 | + LOOKUP.put(HostEvent.Type.HOST_ADDED, "addHost"); | ||
86 | + LOOKUP.put(HostEvent.Type.HOST_UPDATED, "updateHost"); | ||
87 | + LOOKUP.put(HostEvent.Type.HOST_REMOVED, "removeHost"); | ||
88 | + } | ||
89 | + | ||
90 | + private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
91 | + | ||
92 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
93 | + | ||
94 | + private MetaDb metaDb; | ||
95 | + | ||
96 | + private ClusterService clusterService; | ||
97 | + private DeviceService deviceService; | ||
98 | + private LinkService linkService; | ||
99 | + private HostService hostService; | ||
100 | + private MastershipService mastershipService; | ||
101 | + | ||
102 | + | ||
103 | + // =================================================================== | ||
104 | + // Private helper methods | ||
105 | + | ||
106 | + private ObjectNode objectNode() { | ||
107 | + return MAPPER.createObjectNode(); | ||
108 | + } | ||
109 | + | ||
110 | + private ArrayNode arrayNode() { | ||
111 | + return MAPPER.createArrayNode(); | ||
112 | + } | ||
113 | + | ||
114 | + private String toLc(Object o) { | ||
115 | + return o.toString().toLowerCase(); | ||
116 | + } | ||
117 | + | ||
118 | + // Event type to message type lookup (with fallback). | ||
119 | + private String messageTypeLookup(Enum<?> type, String fallback) { | ||
120 | + String msgType = LOOKUP.get(type); | ||
121 | + return msgType == null ? fallback : msgType; | ||
122 | + } | ||
123 | + | ||
124 | + // Returns the name of the master node for the specified device ID. | ||
125 | + private String master(DeviceId deviceId) { | ||
126 | + NodeId master = mastershipService.getMasterFor(deviceId); | ||
127 | + return master != null ? master.toString() : ""; | ||
128 | + } | ||
129 | + | ||
130 | + // Produces JSON structure from annotations. | ||
131 | + private ObjectNode props(Annotations annotations) { | ||
132 | + ObjectNode props = objectNode(); | ||
133 | + if (annotations != null) { | ||
134 | + for (String key : annotations.keys()) { | ||
135 | + props.put(key, annotations.value(key)); | ||
136 | + } | ||
137 | + } | ||
138 | + return props; | ||
139 | + } | ||
140 | + | ||
141 | + // Adds a geo location JSON to the specified payload object. | ||
142 | + private void addGeoLocation(Annotated annotated, ObjectNode payload) { | ||
143 | + Annotations annot = annotated.annotations(); | ||
144 | + if (annot == null) { | ||
145 | + return; | ||
146 | + } | ||
147 | + | ||
148 | + String slat = annot.value(AnnotationKeys.LATITUDE); | ||
149 | + String slng = annot.value(AnnotationKeys.LONGITUDE); | ||
150 | + try { | ||
151 | + if (!isNullOrEmpty(slat) && !isNullOrEmpty(slng)) { | ||
152 | + double lat = Double.parseDouble(slat); | ||
153 | + double lng = Double.parseDouble(slng); | ||
154 | + ObjectNode loc = objectNode() | ||
155 | + .put("type", "latlng") | ||
156 | + .put("lat", lat) | ||
157 | + .put("lng", lng); | ||
158 | + payload.set("location", loc); | ||
159 | + } | ||
160 | + } catch (NumberFormatException e) { | ||
161 | + log.warn("Invalid geo data latitude={}; longitude={}", slat, slng); | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + // Produces compact string representation of a link. | ||
166 | + private String compactLinkString(Link link) { | ||
167 | + return String.format(COMPACT, link.src().elementId(), link.src().port(), | ||
168 | + link.dst().elementId(), link.dst().port()); | ||
169 | + } | ||
170 | + | ||
171 | + // Generates an edge link from the specified host location. | ||
172 | + private EdgeLink edgeLink(Host host, boolean isIngress) { | ||
173 | + ConnectPoint cp = new ConnectPoint(host.id(), PORT_ZERO); | ||
174 | + return new DefaultEdgeLink(PROVIDER_ID, cp, host.location(), isIngress); | ||
175 | + } | ||
176 | + | ||
177 | + // Encodes the specified host location into a JSON object. | ||
178 | + private ObjectNode hostConnect(HostLocation loc) { | ||
179 | + return objectNode() | ||
180 | + .put("device", loc.deviceId().toString()) | ||
181 | + .put("port", loc.port().toLong()); | ||
182 | + } | ||
183 | + | ||
184 | + // Returns the first IP address from the specified set. | ||
185 | + private String firstIp(Set<IpAddress> addresses) { | ||
186 | + Iterator<IpAddress> it = addresses.iterator(); | ||
187 | + return it.hasNext() ? it.next().toString() : "unknown"; | ||
188 | + } | ||
189 | + | ||
190 | + // Returns a JSON array of the specified strings. | ||
191 | + private ArrayNode labels(String... labels) { | ||
192 | + ArrayNode array = arrayNode(); | ||
193 | + for (String label : labels) { | ||
194 | + array.add(label); | ||
195 | + } | ||
196 | + return array; | ||
197 | + } | ||
198 | + | ||
199 | + // =================================================================== | ||
200 | + // API for generating messages | ||
201 | + | ||
202 | + /** | ||
203 | + * Injects service references so that the message compilation methods | ||
204 | + * can do required lookups when needed. | ||
205 | + * | ||
206 | + * @param meta meta DB | ||
207 | + * @param cs cluster service | ||
208 | + * @param ds device service | ||
209 | + * @param ls link service | ||
210 | + * @param hs host service | ||
211 | + * @param ms mastership service | ||
212 | + */ | ||
213 | + public void injectServices(MetaDb meta, ClusterService cs, DeviceService ds, | ||
214 | + LinkService ls, HostService hs, | ||
215 | + MastershipService ms) { | ||
216 | + metaDb = meta; | ||
217 | + clusterService = cs; | ||
218 | + deviceService = ds; | ||
219 | + linkService = ls; | ||
220 | + hostService = hs; | ||
221 | + mastershipService = ms; | ||
222 | + } | ||
223 | + | ||
224 | + /** | ||
225 | + * Transforms a cluster event into an object-node-based message. | ||
226 | + * | ||
227 | + * @param ev cluster event | ||
228 | + * @return marshaled event message | ||
229 | + */ | ||
230 | + public ObjectNode instanceMessage(ClusterEvent ev) { | ||
231 | + ControllerNode node = ev.subject(); | ||
232 | + NodeId nid = node.id(); | ||
233 | + String id = nid.toString(); | ||
234 | + String ip = node.ip().toString(); | ||
235 | + int switchCount = mastershipService.getDevicesOf(nid).size(); | ||
236 | + | ||
237 | + ObjectNode payload = objectNode() | ||
238 | + .put("id", id) | ||
239 | + .put("ip", ip) | ||
240 | + .put("online", clusterService.getState(nid) == ACTIVE) | ||
241 | + .put("uiAttached", node.equals(clusterService.getLocalNode())) | ||
242 | + .put("switches", switchCount); | ||
243 | + | ||
244 | + ArrayNode labels = arrayNode().add(id).add(ip); | ||
245 | + | ||
246 | + payload.set("labels", labels); | ||
247 | + metaDb.addMetaUi(id, payload); | ||
248 | + | ||
249 | + String msgType = messageTypeLookup(ev.type(), "addInstance"); | ||
250 | + return JsonUtils.envelope(msgType, payload); | ||
251 | + } | ||
252 | + | ||
253 | + /** | ||
254 | + * Transforms a device event into an object-node-based message. | ||
255 | + * | ||
256 | + * @param ev device event | ||
257 | + * @return marshaled event message | ||
258 | + */ | ||
259 | + public ObjectNode deviceMessage(DeviceEvent ev) { | ||
260 | + Device device = ev.subject(); | ||
261 | + DeviceId did = device.id(); | ||
262 | + String id = did.toString(); | ||
263 | + | ||
264 | + ObjectNode payload = objectNode() | ||
265 | + .put("id", id) | ||
266 | + .put("type", toLc(device.type())) | ||
267 | + .put("online", deviceService.isAvailable(did)) | ||
268 | + .put("master", master(did)); | ||
269 | + | ||
270 | + Annotations annot = device.annotations(); | ||
271 | + String name = annot.value(AnnotationKeys.NAME); | ||
272 | + String friendly = isNullOrEmpty(name) ? id : name; | ||
273 | + payload.set("labels", labels("", friendly, id)); | ||
274 | + payload.set("props", props(annot)); | ||
275 | + | ||
276 | + addGeoLocation(device, payload); | ||
277 | + metaDb.addMetaUi(id, payload); | ||
278 | + | ||
279 | + String msgType = messageTypeLookup(ev.type(), "updateDevice"); | ||
280 | + return JsonUtils.envelope(msgType, payload); | ||
281 | + } | ||
282 | + | ||
283 | + /** | ||
284 | + * Transforms a link event into an object-node-based message. | ||
285 | + * | ||
286 | + * @param ev link event | ||
287 | + * @return marshaled event message | ||
288 | + */ | ||
289 | + public ObjectNode linkMessage(LinkEvent ev) { | ||
290 | + Link link = ev.subject(); | ||
291 | + ObjectNode payload = objectNode() | ||
292 | + .put("id", compactLinkString(link)) | ||
293 | + .put("type", toLc(link.type())) | ||
294 | + .put("online", link.state() == Link.State.ACTIVE) | ||
295 | + .put("linkWidth", 1.2) | ||
296 | + .put("src", link.src().deviceId().toString()) | ||
297 | + .put("srcPort", link.src().port().toString()) | ||
298 | + .put("dst", link.dst().deviceId().toString()) | ||
299 | + .put("dstPort", link.dst().port().toString()); | ||
300 | + | ||
301 | + String msgType = messageTypeLookup(ev.type(), "updateLink"); | ||
302 | + return JsonUtils.envelope(msgType, payload); | ||
303 | + } | ||
304 | + | ||
305 | + /** | ||
306 | + * Transforms a host event into an object-node-based message. | ||
307 | + * | ||
308 | + * @param ev host event | ||
309 | + * @return marshaled event message | ||
310 | + */ | ||
311 | + public ObjectNode hostMessage(HostEvent ev) { | ||
312 | + Host host = ev.subject(); | ||
313 | + HostId hid = host.id(); | ||
314 | + String id = hid.toString(); | ||
315 | + Annotations annot = host.annotations(); | ||
316 | + | ||
317 | + String hostType = annot.value(AnnotationKeys.TYPE); | ||
318 | + | ||
319 | + ObjectNode payload = objectNode() | ||
320 | + .put("id", id) | ||
321 | + .put("type", isNullOrEmpty(hostType) ? "endstation" : hostType) | ||
322 | + .put("ingress", compactLinkString(edgeLink(host, true))) | ||
323 | + .put("egress", compactLinkString(edgeLink(host, false))); | ||
324 | + | ||
325 | + // TODO: make cp an array of connect point objects (multi-homed) | ||
326 | + payload.set("cp", hostConnect(host.location())); | ||
327 | + String ipStr = firstIp(host.ipAddresses()); | ||
328 | + String macStr = host.mac().toString(); | ||
329 | + payload.set("labels", labels(ipStr, macStr)); | ||
330 | + payload.set("props", props(annot)); | ||
331 | + addGeoLocation(host, payload); | ||
332 | + metaDb.addMetaUi(id, payload); | ||
333 | + | ||
334 | + String mstType = messageTypeLookup(ev.type(), "updateHost"); | ||
335 | + return JsonUtils.envelope(mstType, payload); | ||
336 | + } | ||
337 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import org.onosproject.event.AbstractEvent; | ||
22 | + | ||
23 | +/** | ||
24 | + * Describes Topology UI Model events. | ||
25 | + */ | ||
26 | +public class TopoUiEvent extends AbstractEvent<TopoUiEvent.Type, ObjectNode> { | ||
27 | + | ||
28 | + /** | ||
29 | + * Type of Topology UI Model events. | ||
30 | + */ | ||
31 | + public enum Type { | ||
32 | + INSTANCE_ADDED, | ||
33 | + INSTANCE_REMOVED, | ||
34 | + DEVICE_ADDED, | ||
35 | + DEVICE_UPDATED, | ||
36 | + DEVICE_REMOVED, | ||
37 | + LINK_ADDED, | ||
38 | + LINK_UPDATED, | ||
39 | + LINK_REMOVED, | ||
40 | + HOST_ADDED, | ||
41 | + HOST_UPDATED, | ||
42 | + HOST_REMOVED | ||
43 | + } | ||
44 | + | ||
45 | + | ||
46 | + protected TopoUiEvent(Type type, ObjectNode subject) { | ||
47 | + super(type, subject); | ||
48 | + } | ||
49 | + | ||
50 | + protected TopoUiEvent(Type type, ObjectNode subject, long time) { | ||
51 | + super(type, subject, time); | ||
52 | + } | ||
53 | + | ||
54 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import org.onosproject.event.EventListener; | ||
21 | + | ||
22 | +/** | ||
23 | + * Defines a listener of Topology UI Model events. | ||
24 | + */ | ||
25 | +public interface TopoUiListener extends EventListener<TopoUiEvent> { | ||
26 | + | ||
27 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import org.apache.felix.scr.annotations.Activate; | ||
22 | +import org.apache.felix.scr.annotations.Component; | ||
23 | +import org.apache.felix.scr.annotations.Deactivate; | ||
24 | +import org.apache.felix.scr.annotations.Reference; | ||
25 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
26 | +import org.apache.felix.scr.annotations.Service; | ||
27 | +import org.onosproject.cluster.ClusterEvent; | ||
28 | +import org.onosproject.cluster.ClusterService; | ||
29 | +import org.onosproject.cluster.ControllerNode; | ||
30 | +import org.onosproject.event.AbstractListenerRegistry; | ||
31 | +import org.onosproject.event.EventDeliveryService; | ||
32 | +import org.onosproject.mastership.MastershipService; | ||
33 | +import org.onosproject.net.Device; | ||
34 | +import org.onosproject.net.Host; | ||
35 | +import org.onosproject.net.Link; | ||
36 | +import org.onosproject.net.device.DeviceEvent; | ||
37 | +import org.onosproject.net.device.DeviceService; | ||
38 | +import org.onosproject.net.host.HostEvent; | ||
39 | +import org.onosproject.net.host.HostService; | ||
40 | +import org.onosproject.net.link.LinkEvent; | ||
41 | +import org.onosproject.net.link.LinkService; | ||
42 | +import org.slf4j.Logger; | ||
43 | +import org.slf4j.LoggerFactory; | ||
44 | + | ||
45 | +import java.util.ArrayList; | ||
46 | +import java.util.Collections; | ||
47 | +import java.util.Comparator; | ||
48 | +import java.util.List; | ||
49 | + | ||
50 | +import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED; | ||
51 | +import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED; | ||
52 | +import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; | ||
53 | +import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; | ||
54 | + | ||
55 | + | ||
56 | +/** | ||
57 | + * Maintains a UI-centric model of the topology, as inferred from interactions | ||
58 | + * with the different (device, host, link, ...) services. Will serve up this | ||
59 | + * model to anyone who cares to {@link TopoUiListener listen}. | ||
60 | + */ | ||
61 | +@Component(immediate = true) | ||
62 | +@Service | ||
63 | +public class TopoUiModelManager implements TopoUiModelService { | ||
64 | + | ||
65 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
66 | + | ||
67 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
68 | + protected ClusterService clusterService; | ||
69 | + | ||
70 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
71 | + protected DeviceService deviceService; | ||
72 | + | ||
73 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
74 | + protected LinkService linkService; | ||
75 | + | ||
76 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
77 | + protected HostService hostService; | ||
78 | + | ||
79 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
80 | + protected MastershipService mastershipService; | ||
81 | + | ||
82 | + | ||
83 | + | ||
84 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
85 | + protected EventDeliveryService eventDispatcher; | ||
86 | + | ||
87 | + | ||
88 | + private AbstractListenerRegistry<TopoUiEvent, TopoUiListener> | ||
89 | + listenerRegistry = new AbstractListenerRegistry<>(); | ||
90 | + | ||
91 | + | ||
92 | + private final TopoMessageFactory messageFactory = new TopoMessageFactory(); | ||
93 | + private final MetaDb metaDb = new MetaDb(); | ||
94 | + | ||
95 | + | ||
96 | + @Activate | ||
97 | + public void activate() { | ||
98 | + eventDispatcher.addSink(TopoUiEvent.class, listenerRegistry); | ||
99 | + messageFactory.injectServices( | ||
100 | + metaDb, | ||
101 | + clusterService, | ||
102 | + deviceService, | ||
103 | + linkService, | ||
104 | + hostService, | ||
105 | + mastershipService | ||
106 | + ); | ||
107 | + log.info("Started"); | ||
108 | + } | ||
109 | + | ||
110 | + @Deactivate | ||
111 | + public void deactivate() { | ||
112 | + eventDispatcher.removeSink(TopoUiEvent.class); | ||
113 | + log.info("Stopped"); | ||
114 | + } | ||
115 | + | ||
116 | + @Override | ||
117 | + public void addListener(TopoUiListener listener) { | ||
118 | + listenerRegistry.addListener(listener); | ||
119 | + } | ||
120 | + | ||
121 | + @Override | ||
122 | + public void removeListener(TopoUiListener listener) { | ||
123 | + listenerRegistry.removeListener(listener); | ||
124 | + } | ||
125 | + | ||
126 | + @Override | ||
127 | + public List<ObjectNode> getInitialState() { | ||
128 | + List<ObjectNode> results = new ArrayList<>(); | ||
129 | + addInstances(results); | ||
130 | + addDevices(results); | ||
131 | + addLinks(results); | ||
132 | + addHosts(results); | ||
133 | + return results; | ||
134 | + } | ||
135 | + | ||
136 | + // ===================================================================== | ||
137 | + | ||
138 | + private static final Comparator<? super ControllerNode> NODE_COMPARATOR = | ||
139 | + (o1, o2) -> o1.id().toString().compareTo(o2.id().toString()); | ||
140 | + | ||
141 | + // ===================================================================== | ||
142 | + | ||
143 | + private void addInstances(List<ObjectNode> results) { | ||
144 | + List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes()); | ||
145 | + Collections.sort(nodes, NODE_COMPARATOR); | ||
146 | + for (ControllerNode node : nodes) { | ||
147 | + ClusterEvent ev = new ClusterEvent(INSTANCE_ADDED, node); | ||
148 | + results.add(messageFactory.instanceMessage(ev)); | ||
149 | + } | ||
150 | + } | ||
151 | + | ||
152 | + private void addDevices(List<ObjectNode> results) { | ||
153 | + // Send optical first, others later -- for layered rendering | ||
154 | + List<DeviceEvent> deferred = new ArrayList<>(); | ||
155 | + | ||
156 | + for (Device device : deviceService.getDevices()) { | ||
157 | + DeviceEvent ev = new DeviceEvent(DEVICE_ADDED, device); | ||
158 | + if (device.type() == Device.Type.ROADM) { | ||
159 | + results.add(messageFactory.deviceMessage(ev)); | ||
160 | + } else { | ||
161 | + deferred.add(ev); | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + for (DeviceEvent ev : deferred) { | ||
166 | + results.add(messageFactory.deviceMessage(ev)); | ||
167 | + } | ||
168 | + } | ||
169 | + | ||
170 | + private void addLinks(List<ObjectNode> results) { | ||
171 | + // Send optical first, others later -- for layered rendering | ||
172 | + List<LinkEvent> deferred = new ArrayList<>(); | ||
173 | + | ||
174 | + for (Link link : linkService.getLinks()) { | ||
175 | + LinkEvent ev = new LinkEvent(LINK_ADDED, link); | ||
176 | + if (link.type() == Link.Type.OPTICAL) { | ||
177 | + results.add(messageFactory.linkMessage(ev)); | ||
178 | + } else { | ||
179 | + deferred.add(ev); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + for (LinkEvent ev : deferred) { | ||
184 | + results.add(messageFactory.linkMessage(ev)); | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + private void addHosts(List<ObjectNode> results) { | ||
189 | + for (Host host : hostService.getHosts()) { | ||
190 | + HostEvent ev = new HostEvent(HOST_ADDED, host); | ||
191 | + results.add(messageFactory.hostMessage(ev)); | ||
192 | + } | ||
193 | + } | ||
194 | + | ||
195 | + // ===================================================================== | ||
196 | + | ||
197 | + private void post(TopoUiEvent event) { | ||
198 | + if (event != null) { | ||
199 | + eventDispatcher.post(event); | ||
200 | + } | ||
201 | + } | ||
202 | + | ||
203 | + // NOTE: session-independent state only | ||
204 | + // private inner classes to listen to device/host/link events | ||
205 | + // TODO.. | ||
206 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | + | ||
18 | +package org.onosproject.ui.impl.topo; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | + | ||
22 | +import java.util.List; | ||
23 | + | ||
24 | +/**t | ||
25 | + * Defines the API for the Topology UI Model. | ||
26 | + */ | ||
27 | +public interface TopoUiModelService { | ||
28 | + | ||
29 | + /** | ||
30 | + * Registers the specified listener for Topology UI Model events. | ||
31 | + * | ||
32 | + * @param listener the listener | ||
33 | + */ | ||
34 | + void addListener(TopoUiListener listener); | ||
35 | + | ||
36 | + /** | ||
37 | + * Unregister the specified listener. | ||
38 | + * | ||
39 | + * @param listener the listener | ||
40 | + */ | ||
41 | + void removeListener(TopoUiListener listener); | ||
42 | + | ||
43 | + | ||
44 | + /** | ||
45 | + * Returns events describing the current state of the model. | ||
46 | + * <p> | ||
47 | + * These will be in the form of "addInstance", "addDevice", "addLink", | ||
48 | + * and "addHost" events, as appropriate. | ||
49 | + * | ||
50 | + * @return initial state events | ||
51 | + */ | ||
52 | + List<ObjectNode> getInitialState(); | ||
53 | + | ||
54 | +} |
-
Please register or login to post a comment