blob: a5a344a1670ad4ce2f513c9879c00a07ec84c366 [file] [log] [blame]
Simon Huntd5b96732016-07-08 13:22:27 -07001/*
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
17package org.onosproject.ui.impl.topo;
18
Simon Huntc13082f2016-08-03 21:20:23 -070019import com.fasterxml.jackson.databind.JsonNode;
Simon Huntd5b96732016-07-08 13:22:27 -070020import com.fasterxml.jackson.databind.ObjectMapper;
21import com.fasterxml.jackson.databind.node.ArrayNode;
22import com.fasterxml.jackson.databind.node.ObjectNode;
23import org.onlab.osgi.ServiceDirectory;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.NodeId;
26import org.onosproject.incubator.net.PortStatisticsService;
27import org.onosproject.incubator.net.tunnel.TunnelService;
28import org.onosproject.mastership.MastershipService;
29import org.onosproject.net.device.DeviceService;
30import org.onosproject.net.flow.FlowRuleService;
31import org.onosproject.net.host.HostService;
32import org.onosproject.net.intent.IntentService;
33import org.onosproject.net.link.LinkService;
34import org.onosproject.net.region.Region;
35import org.onosproject.net.statistic.StatisticService;
36import org.onosproject.net.topology.TopologyService;
37import org.onosproject.ui.model.topo.UiClusterMember;
38import org.onosproject.ui.model.topo.UiDevice;
39import org.onosproject.ui.model.topo.UiHost;
40import org.onosproject.ui.model.topo.UiLink;
Simon Hunt977aa052016-07-20 17:08:29 -070041import org.onosproject.ui.model.topo.UiNode;
Simon Huntd5b96732016-07-08 13:22:27 -070042import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070043import org.onosproject.ui.model.topo.UiSynthLink;
Simon Huntd5b96732016-07-08 13:22:27 -070044import org.onosproject.ui.model.topo.UiTopoLayout;
Simon Hunt98189192016-07-29 19:02:27 -070045import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070047
Simon Hunt977aa052016-07-20 17:08:29 -070048import java.util.ArrayList;
49import java.util.HashMap;
50import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070051import java.util.List;
Simon Hunt977aa052016-07-20 17:08:29 -070052import java.util.Map;
53import java.util.Set;
Simon Huntd5b96732016-07-08 13:22:27 -070054
55import static com.google.common.base.Preconditions.checkNotNull;
Simon Hunt977aa052016-07-20 17:08:29 -070056import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
Simon Huntd5b96732016-07-08 13:22:27 -070057
58/**
59 * Facility for creating JSON messages to send to the topology view in the
60 * Web client.
61 */
62class Topo2Jsonifier {
63
Simon Hunt977aa052016-07-20 17:08:29 -070064 private static final String E_DEF_NOT_LAST =
65 "UiNode.LAYER_DEFAULT not last in layer list";
66 private static final String E_UNKNOWN_UI_NODE =
67 "Unknown subclass of UiNode: ";
68
Simon Hunt98189192016-07-29 19:02:27 -070069 private static final String REGION = "region";
70 private static final String DEVICE = "device";
71 private static final String HOST = "host";
72
73 private final Logger log = LoggerFactory.getLogger(getClass());
74
Simon Huntd5b96732016-07-08 13:22:27 -070075 private final ObjectMapper mapper = new ObjectMapper();
76
77 private ServiceDirectory directory;
78 private ClusterService clusterService;
79 private DeviceService deviceService;
80 private LinkService linkService;
81 private HostService hostService;
82 private MastershipService mastershipService;
83 private IntentService intentService;
84 private FlowRuleService flowService;
85 private StatisticService flowStatsService;
86 private PortStatisticsService portStatsService;
87 private TopologyService topologyService;
88 private TunnelService tunnelService;
89
90
91 /**
92 * Creates an instance with a reference to the services directory, so that
93 * additional information about network elements may be looked up on
94 * on the fly.
95 *
96 * @param directory service directory
97 */
98 Topo2Jsonifier(ServiceDirectory directory) {
99 this.directory = checkNotNull(directory, "Directory cannot be null");
100
101 clusterService = directory.get(ClusterService.class);
102 deviceService = directory.get(DeviceService.class);
103 linkService = directory.get(LinkService.class);
104 hostService = directory.get(HostService.class);
105 mastershipService = directory.get(MastershipService.class);
106 intentService = directory.get(IntentService.class);
107 flowService = directory.get(FlowRuleService.class);
108 flowStatsService = directory.get(StatisticService.class);
109 portStatsService = directory.get(PortStatisticsService.class);
110 topologyService = directory.get(TopologyService.class);
111 tunnelService = directory.get(TunnelService.class);
Simon Hunt977aa052016-07-20 17:08:29 -0700112 }
Simon Huntd5b96732016-07-08 13:22:27 -0700113
Simon Hunt977aa052016-07-20 17:08:29 -0700114 // for unit testing
115 Topo2Jsonifier() {
Simon Huntd5b96732016-07-08 13:22:27 -0700116 }
117
118 private ObjectNode objectNode() {
119 return mapper.createObjectNode();
120 }
121
122 private ArrayNode arrayNode() {
123 return mapper.createArrayNode();
124 }
125
126 private String nullIsEmpty(Object o) {
127 return o == null ? "" : o.toString();
128 }
129
130
131 /**
132 * Returns a JSON representation of the cluster members (ONOS instances).
133 *
134 * @param instances the instance model objects
135 * @return a JSON representation of the data
136 */
137 ObjectNode instances(List<UiClusterMember> instances) {
138 NodeId local = clusterService.getLocalNode().id();
139 ObjectNode payload = objectNode();
140
141 ArrayNode members = arrayNode();
142 payload.set("members", members);
143 for (UiClusterMember member : instances) {
144 members.add(json(member, member.id().equals(local)));
145 }
146
147 return payload;
148 }
149
150 private ObjectNode json(UiClusterMember member, boolean isUiAttached) {
151 return objectNode()
152 .put("id", member.id().toString())
153 .put("ip", member.ip().toString())
154 .put("online", member.isOnline())
155 .put("ready", member.isReady())
156 .put("uiAttached", isUiAttached)
157 .put("switches", member.deviceCount());
158 }
159
160 /**
161 * Returns a JSON representation of the layout to use for displaying in
162 * the topology view.
163 *
164 * @param layout the layout to transform
165 * @return a JSON representation of the data
166 */
167 ObjectNode layout(UiTopoLayout layout) {
168 return objectNode()
169 .put("id", layout.id().toString())
170 .put("parent", nullIsEmpty(layout.parent()))
171 .put("region", nullIsEmpty(layout.regionId()))
172 .put("regionName", regionName(layout.region()));
173 }
174
175 private String regionName(Region region) {
176 return region == null ? "" : region.name();
177 }
178
179 /**
180 * Returns a JSON representation of the region to display in the topology
181 * view.
182 *
Simon Hunt977aa052016-07-20 17:08:29 -0700183 * @param region the region to transform to JSON
184 * @param subRegions the subregions within this region
Simon Huntc13082f2016-08-03 21:20:23 -0700185 * @param links the links within this region
Simon Huntd5b96732016-07-08 13:22:27 -0700186 * @return a JSON representation of the data
187 */
Simon Huntc13082f2016-08-03 21:20:23 -0700188 ObjectNode region(UiRegion region, Set<UiRegion> subRegions,
189 List<UiSynthLink> links) {
Simon Huntd5b96732016-07-08 13:22:27 -0700190 ObjectNode payload = objectNode();
Simon Huntd5b96732016-07-08 13:22:27 -0700191 if (region == null) {
192 payload.put("note", "no-region");
193 return payload;
194 }
Simon Hunt977aa052016-07-20 17:08:29 -0700195 payload.put("id", region.idAsString());
Simon Huntb1ce2602016-07-23 14:04:31 -0700196 if (subRegions != null) {
197 payload.set("subregions", jsonSubRegions(subRegions));
198 }
Simon Huntd5b96732016-07-08 13:22:27 -0700199
Simon Huntc13082f2016-08-03 21:20:23 -0700200 if (links != null) {
201 payload.set("links", jsonLinks(links));
202 }
203
Simon Hunt977aa052016-07-20 17:08:29 -0700204 List<String> layerTags = region.layerOrder();
205 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
206 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
Simon Huntd5b96732016-07-08 13:22:27 -0700207
Simon Hunt977aa052016-07-20 17:08:29 -0700208 payload.set("devices", jsonGrouped(splitDevices));
209 payload.set("hosts", jsonGrouped(splitHosts));
Simon Hunt977aa052016-07-20 17:08:29 -0700210 payload.set("layerOrder", jsonStrings(layerTags));
Simon Huntd5b96732016-07-08 13:22:27 -0700211
212 return payload;
213 }
214
Simon Hunt977aa052016-07-20 17:08:29 -0700215 private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
216 ArrayNode kids = arrayNode();
Simon Huntc13082f2016-08-03 21:20:23 -0700217 subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
Simon Hunt977aa052016-07-20 17:08:29 -0700218 return kids;
219 }
220
Simon Huntc13082f2016-08-03 21:20:23 -0700221 private JsonNode jsonLinks(List<UiSynthLink> links) {
222 ArrayNode synthLinks = arrayNode();
223 links.forEach(l -> synthLinks.add(json(l)));
224 return synthLinks;
225 }
226
Simon Hunt977aa052016-07-20 17:08:29 -0700227 private ArrayNode jsonStrings(List<String> strings) {
228 ArrayNode array = arrayNode();
229 strings.forEach(array::add);
230 return array;
231 }
232
Simon Hunt977aa052016-07-20 17:08:29 -0700233 private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
234 ArrayNode result = arrayNode();
235 groupedNodes.forEach(g -> {
236 ArrayNode subset = arrayNode();
237 g.forEach(n -> subset.add(json(n)));
238 result.add(subset);
239 });
240 return result;
241 }
242
Simon Hunt977aa052016-07-20 17:08:29 -0700243
244 private ObjectNode json(UiNode node) {
245 if (node instanceof UiRegion) {
246 return jsonClosedRegion((UiRegion) node);
247 }
248 if (node instanceof UiDevice) {
249 return json((UiDevice) node);
250 }
251 if (node instanceof UiHost) {
252 return json((UiHost) node);
253 }
254 throw new IllegalStateException(E_UNKNOWN_UI_NODE + node.getClass());
255 }
256
Simon Huntd5b96732016-07-08 13:22:27 -0700257 private ObjectNode json(UiDevice device) {
258 ObjectNode node = objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700259 .put("id", device.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700260 .put("nodeType", DEVICE)
Simon Huntd5b96732016-07-08 13:22:27 -0700261 .put("type", device.type())
262 .put("online", device.isOnline())
Simon Huntb1ce2602016-07-23 14:04:31 -0700263 .put("master", nullIsEmpty(device.master()))
Simon Huntd5b96732016-07-08 13:22:27 -0700264 .put("layer", device.layer());
265
266 // TODO: complete device details
267// addLabels(node, device);
268// addProps(node, device);
269// addGeoLocation(node, device);
270// addMetaUi(node, device);
271
272 return node;
273 }
274
275 private void addLabels(ObjectNode node, UiDevice device) {
276
277 }
278
279 private ObjectNode json(UiHost host) {
280 return objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700281 .put("id", host.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700282 .put("nodeType", HOST)
Simon Huntd5b96732016-07-08 13:22:27 -0700283 .put("layer", host.layer());
284 // TODO: complete host details
285 }
286
Simon Huntc13082f2016-08-03 21:20:23 -0700287 private ObjectNode json(UiSynthLink sLink) {
288 UiLink uLink = sLink.link();
Simon Huntd5b96732016-07-08 13:22:27 -0700289 return objectNode()
Simon Huntc13082f2016-08-03 21:20:23 -0700290 .put("id", uLink.idAsString())
291 .put("epA", uLink.endPointA())
292 .put("epB", uLink.endPointB())
293 .put("type", uLink.type());
Simon Huntd5b96732016-07-08 13:22:27 -0700294 }
295
296
Simon Hunt977aa052016-07-20 17:08:29 -0700297 private ObjectNode jsonClosedRegion(UiRegion region) {
298 return objectNode()
Simon Huntb1ce2602016-07-23 14:04:31 -0700299 .put("id", region.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700300 .put("nodeType", REGION)
Simon Huntb1ce2602016-07-23 14:04:31 -0700301 .put("nDevs", region.deviceCount());
Simon Hunt977aa052016-07-20 17:08:29 -0700302 // TODO: complete closed-region details
303 }
304
Simon Hunt98189192016-07-29 19:02:27 -0700305 /**
306 * Returns a JSON array representation of a set of regions/devices. Note
307 * that the information is sufficient for showing regions as nodes.
308 *
309 * @param nodes the nodes
310 * @return a JSON representation of the nodes
311 */
312 public ArrayNode closedNodes(Set<UiNode> nodes) {
313 ArrayNode array = arrayNode();
Simon Huntc13082f2016-08-03 21:20:23 -0700314 for (UiNode node : nodes) {
Simon Hunt98189192016-07-29 19:02:27 -0700315 if (node instanceof UiRegion) {
316 array.add(jsonClosedRegion((UiRegion) node));
317 } else if (node instanceof UiDevice) {
318 array.add(json((UiDevice) node));
319 } else {
320 log.warn("Unexpected node instance: {}", node.getClass());
321 }
322 }
323 return array;
324 }
Simon Hunt977aa052016-07-20 17:08:29 -0700325
326 /**
327 * Returns a JSON array representation of a list of regions. Note that the
328 * information about each region is limited to what needs to be used to
329 * show the regions as nodes on the view.
330 *
331 * @param regions the regions
332 * @return a JSON representation of the minimal region information
333 */
334 public ArrayNode closedRegions(Set<UiRegion> regions) {
335 ArrayNode array = arrayNode();
336 for (UiRegion r : regions) {
337 array.add(jsonClosedRegion(r));
338 }
339 return array;
340 }
341
342 /**
343 * Returns a JSON array representation of a list of devices.
344 *
345 * @param devices the devices
346 * @return a JSON representation of the devices
347 */
348 public ArrayNode devices(Set<UiDevice> devices) {
349 ArrayNode array = arrayNode();
350 for (UiDevice device : devices) {
351 array.add(json(device));
352 }
353 return array;
354 }
355
356 /**
357 * Returns a JSON array representation of a list of hosts.
358 *
359 * @param hosts the hosts
360 * @return a JSON representation of the hosts
361 */
362 public ArrayNode hosts(Set<UiHost> hosts) {
363 ArrayNode array = arrayNode();
364 for (UiHost host : hosts) {
365 array.add(json(host));
366 }
367 return array;
368 }
369
Simon Hunt977aa052016-07-20 17:08:29 -0700370 // package-private for unit testing
371 List<Set<UiNode>> splitByLayer(List<String> layerTags,
372 Set<? extends UiNode> nodes) {
373 final int nLayers = layerTags.size();
374 if (!layerTags.get(nLayers - 1).equals(LAYER_DEFAULT)) {
375 throw new IllegalArgumentException(E_DEF_NOT_LAST);
376 }
377
378 List<Set<UiNode>> splitList = new ArrayList<>(layerTags.size());
379 Map<String, Set<UiNode>> byLayer = new HashMap<>(layerTags.size());
380
381 for (String tag : layerTags) {
382 Set<UiNode> set = new HashSet<>();
383 byLayer.put(tag, set);
384 splitList.add(set);
385 }
386
387 for (UiNode n : nodes) {
388 String which = n.layer();
389 if (!layerTags.contains(which)) {
390 which = LAYER_DEFAULT;
391 }
392 byLayer.get(which).add(n);
393 }
394
395 return splitList;
396 }
Simon Huntd5b96732016-07-08 13:22:27 -0700397}