blob: 8a26a929760e29c8395ef78f2e4750742c928746 [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
19import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.ObjectNode;
22import org.onlab.osgi.ServiceDirectory;
23import org.onosproject.cluster.ClusterService;
24import org.onosproject.cluster.NodeId;
25import org.onosproject.incubator.net.PortStatisticsService;
26import org.onosproject.incubator.net.tunnel.TunnelService;
27import org.onosproject.mastership.MastershipService;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.flow.FlowRuleService;
30import org.onosproject.net.host.HostService;
31import org.onosproject.net.intent.IntentService;
32import org.onosproject.net.link.LinkService;
33import org.onosproject.net.region.Region;
34import org.onosproject.net.statistic.StatisticService;
35import org.onosproject.net.topology.TopologyService;
36import org.onosproject.ui.model.topo.UiClusterMember;
37import org.onosproject.ui.model.topo.UiDevice;
38import org.onosproject.ui.model.topo.UiHost;
39import org.onosproject.ui.model.topo.UiLink;
Simon Hunt977aa052016-07-20 17:08:29 -070040import org.onosproject.ui.model.topo.UiNode;
Simon Huntd5b96732016-07-08 13:22:27 -070041import org.onosproject.ui.model.topo.UiRegion;
42import org.onosproject.ui.model.topo.UiTopoLayout;
Simon Hunt98189192016-07-29 19:02:27 -070043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070045
Simon Hunt977aa052016-07-20 17:08:29 -070046import java.util.ArrayList;
47import java.util.HashMap;
48import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070049import java.util.List;
Simon Hunt977aa052016-07-20 17:08:29 -070050import java.util.Map;
51import java.util.Set;
Simon Huntd5b96732016-07-08 13:22:27 -070052
53import static com.google.common.base.Preconditions.checkNotNull;
Simon Hunt977aa052016-07-20 17:08:29 -070054import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
Simon Huntd5b96732016-07-08 13:22:27 -070055
56/**
57 * Facility for creating JSON messages to send to the topology view in the
58 * Web client.
59 */
60class Topo2Jsonifier {
61
Simon Hunt977aa052016-07-20 17:08:29 -070062 private static final String E_DEF_NOT_LAST =
63 "UiNode.LAYER_DEFAULT not last in layer list";
64 private static final String E_UNKNOWN_UI_NODE =
65 "Unknown subclass of UiNode: ";
66
Simon Hunt98189192016-07-29 19:02:27 -070067 private static final String REGION = "region";
68 private static final String DEVICE = "device";
69 private static final String HOST = "host";
70
71 private final Logger log = LoggerFactory.getLogger(getClass());
72
Simon Huntd5b96732016-07-08 13:22:27 -070073 private final ObjectMapper mapper = new ObjectMapper();
74
75 private ServiceDirectory directory;
76 private ClusterService clusterService;
77 private DeviceService deviceService;
78 private LinkService linkService;
79 private HostService hostService;
80 private MastershipService mastershipService;
81 private IntentService intentService;
82 private FlowRuleService flowService;
83 private StatisticService flowStatsService;
84 private PortStatisticsService portStatsService;
85 private TopologyService topologyService;
86 private TunnelService tunnelService;
87
88
89 /**
90 * Creates an instance with a reference to the services directory, so that
91 * additional information about network elements may be looked up on
92 * on the fly.
93 *
94 * @param directory service directory
95 */
96 Topo2Jsonifier(ServiceDirectory directory) {
97 this.directory = checkNotNull(directory, "Directory cannot be null");
98
99 clusterService = directory.get(ClusterService.class);
100 deviceService = directory.get(DeviceService.class);
101 linkService = directory.get(LinkService.class);
102 hostService = directory.get(HostService.class);
103 mastershipService = directory.get(MastershipService.class);
104 intentService = directory.get(IntentService.class);
105 flowService = directory.get(FlowRuleService.class);
106 flowStatsService = directory.get(StatisticService.class);
107 portStatsService = directory.get(PortStatisticsService.class);
108 topologyService = directory.get(TopologyService.class);
109 tunnelService = directory.get(TunnelService.class);
Simon Hunt977aa052016-07-20 17:08:29 -0700110 }
Simon Huntd5b96732016-07-08 13:22:27 -0700111
Simon Hunt977aa052016-07-20 17:08:29 -0700112 // for unit testing
113 Topo2Jsonifier() {
Simon Huntd5b96732016-07-08 13:22:27 -0700114 }
115
116 private ObjectNode objectNode() {
117 return mapper.createObjectNode();
118 }
119
120 private ArrayNode arrayNode() {
121 return mapper.createArrayNode();
122 }
123
124 private String nullIsEmpty(Object o) {
125 return o == null ? "" : o.toString();
126 }
127
128
129 /**
130 * Returns a JSON representation of the cluster members (ONOS instances).
131 *
132 * @param instances the instance model objects
133 * @return a JSON representation of the data
134 */
135 ObjectNode instances(List<UiClusterMember> instances) {
136 NodeId local = clusterService.getLocalNode().id();
137 ObjectNode payload = objectNode();
138
139 ArrayNode members = arrayNode();
140 payload.set("members", members);
141 for (UiClusterMember member : instances) {
142 members.add(json(member, member.id().equals(local)));
143 }
144
145 return payload;
146 }
147
148 private ObjectNode json(UiClusterMember member, boolean isUiAttached) {
149 return objectNode()
150 .put("id", member.id().toString())
151 .put("ip", member.ip().toString())
152 .put("online", member.isOnline())
153 .put("ready", member.isReady())
154 .put("uiAttached", isUiAttached)
155 .put("switches", member.deviceCount());
156 }
157
158 /**
159 * Returns a JSON representation of the layout to use for displaying in
160 * the topology view.
161 *
162 * @param layout the layout to transform
163 * @return a JSON representation of the data
164 */
165 ObjectNode layout(UiTopoLayout layout) {
166 return objectNode()
167 .put("id", layout.id().toString())
168 .put("parent", nullIsEmpty(layout.parent()))
169 .put("region", nullIsEmpty(layout.regionId()))
170 .put("regionName", regionName(layout.region()));
171 }
172
173 private String regionName(Region region) {
174 return region == null ? "" : region.name();
175 }
176
177 /**
178 * Returns a JSON representation of the region to display in the topology
179 * view.
180 *
Simon Hunt977aa052016-07-20 17:08:29 -0700181 * @param region the region to transform to JSON
182 * @param subRegions the subregions within this region
Simon Huntd5b96732016-07-08 13:22:27 -0700183 * @return a JSON representation of the data
184 */
Simon Hunt977aa052016-07-20 17:08:29 -0700185 ObjectNode region(UiRegion region, Set<UiRegion> subRegions) {
Simon Huntd5b96732016-07-08 13:22:27 -0700186 ObjectNode payload = objectNode();
Simon Huntd5b96732016-07-08 13:22:27 -0700187 if (region == null) {
188 payload.put("note", "no-region");
189 return payload;
190 }
Simon Hunt977aa052016-07-20 17:08:29 -0700191 payload.put("id", region.idAsString());
Simon Huntb1ce2602016-07-23 14:04:31 -0700192 if (subRegions != null) {
193 payload.set("subregions", jsonSubRegions(subRegions));
194 }
Simon Huntd5b96732016-07-08 13:22:27 -0700195
Simon Hunt977aa052016-07-20 17:08:29 -0700196 List<String> layerTags = region.layerOrder();
197 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
198 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
199 Set<UiLink> links = region.links();
Simon Huntd5b96732016-07-08 13:22:27 -0700200
Simon Hunt977aa052016-07-20 17:08:29 -0700201 payload.set("devices", jsonGrouped(splitDevices));
202 payload.set("hosts", jsonGrouped(splitHosts));
203 payload.set("links", jsonLinks(links));
204 payload.set("layerOrder", jsonStrings(layerTags));
Simon Huntd5b96732016-07-08 13:22:27 -0700205
206 return payload;
207 }
208
Simon Hunt977aa052016-07-20 17:08:29 -0700209 private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
210 ArrayNode kids = arrayNode();
211 if (subregions != null) {
212 subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
213 }
214 return kids;
215 }
216
217 private ArrayNode jsonStrings(List<String> strings) {
218 ArrayNode array = arrayNode();
219 strings.forEach(array::add);
220 return array;
221 }
222
223 private ArrayNode jsonLinks(Set<UiLink> links) {
224 ArrayNode result = arrayNode();
225 links.forEach(lnk -> result.add(json(lnk)));
226 return result;
227 }
228
229 private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
230 ArrayNode result = arrayNode();
231 groupedNodes.forEach(g -> {
232 ArrayNode subset = arrayNode();
233 g.forEach(n -> subset.add(json(n)));
234 result.add(subset);
235 });
236 return result;
237 }
238
Simon Hunt977aa052016-07-20 17:08:29 -0700239
240 private ObjectNode json(UiNode node) {
241 if (node instanceof UiRegion) {
242 return jsonClosedRegion((UiRegion) node);
243 }
244 if (node instanceof UiDevice) {
245 return json((UiDevice) node);
246 }
247 if (node instanceof UiHost) {
248 return json((UiHost) node);
249 }
250 throw new IllegalStateException(E_UNKNOWN_UI_NODE + node.getClass());
251 }
252
Simon Huntd5b96732016-07-08 13:22:27 -0700253 private ObjectNode json(UiDevice device) {
254 ObjectNode node = objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700255 .put("id", device.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700256 .put("nodeType", DEVICE)
Simon Huntd5b96732016-07-08 13:22:27 -0700257 .put("type", device.type())
258 .put("online", device.isOnline())
Simon Huntb1ce2602016-07-23 14:04:31 -0700259 .put("master", nullIsEmpty(device.master()))
Simon Huntd5b96732016-07-08 13:22:27 -0700260 .put("layer", device.layer());
261
262 // TODO: complete device details
263// addLabels(node, device);
264// addProps(node, device);
265// addGeoLocation(node, device);
266// addMetaUi(node, device);
267
268 return node;
269 }
270
271 private void addLabels(ObjectNode node, UiDevice device) {
272
273 }
274
275 private ObjectNode json(UiHost host) {
276 return objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700277 .put("id", host.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700278 .put("nodeType", HOST)
Simon Huntd5b96732016-07-08 13:22:27 -0700279 .put("layer", host.layer());
280 // TODO: complete host details
281 }
282
283
284 private ObjectNode json(UiLink link) {
285 return objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700286 .put("id", link.idAsString());
Simon Huntd5b96732016-07-08 13:22:27 -0700287 // TODO: complete link details
288 }
289
290
Simon Hunt977aa052016-07-20 17:08:29 -0700291 private ObjectNode jsonClosedRegion(UiRegion region) {
292 return objectNode()
Simon Huntb1ce2602016-07-23 14:04:31 -0700293 .put("id", region.idAsString())
Simon Hunt98189192016-07-29 19:02:27 -0700294 .put("nodeType", REGION)
Simon Huntb1ce2602016-07-23 14:04:31 -0700295 .put("nDevs", region.deviceCount());
Simon Hunt977aa052016-07-20 17:08:29 -0700296 // TODO: complete closed-region details
297 }
298
Simon Hunt98189192016-07-29 19:02:27 -0700299 /**
300 * Returns a JSON array representation of a set of regions/devices. Note
301 * that the information is sufficient for showing regions as nodes.
302 *
303 * @param nodes the nodes
304 * @return a JSON representation of the nodes
305 */
306 public ArrayNode closedNodes(Set<UiNode> nodes) {
307 ArrayNode array = arrayNode();
308 for (UiNode node: nodes) {
309 if (node instanceof UiRegion) {
310 array.add(jsonClosedRegion((UiRegion) node));
311 } else if (node instanceof UiDevice) {
312 array.add(json((UiDevice) node));
313 } else {
314 log.warn("Unexpected node instance: {}", node.getClass());
315 }
316 }
317 return array;
318 }
Simon Hunt977aa052016-07-20 17:08:29 -0700319
320 /**
321 * Returns a JSON array representation of a list of regions. Note that the
322 * information about each region is limited to what needs to be used to
323 * show the regions as nodes on the view.
324 *
325 * @param regions the regions
326 * @return a JSON representation of the minimal region information
327 */
328 public ArrayNode closedRegions(Set<UiRegion> regions) {
329 ArrayNode array = arrayNode();
330 for (UiRegion r : regions) {
331 array.add(jsonClosedRegion(r));
332 }
333 return array;
334 }
335
336 /**
337 * Returns a JSON array representation of a list of devices.
338 *
339 * @param devices the devices
340 * @return a JSON representation of the devices
341 */
342 public ArrayNode devices(Set<UiDevice> devices) {
343 ArrayNode array = arrayNode();
344 for (UiDevice device : devices) {
345 array.add(json(device));
346 }
347 return array;
348 }
349
350 /**
351 * Returns a JSON array representation of a list of hosts.
352 *
353 * @param hosts the hosts
354 * @return a JSON representation of the hosts
355 */
356 public ArrayNode hosts(Set<UiHost> hosts) {
357 ArrayNode array = arrayNode();
358 for (UiHost host : hosts) {
359 array.add(json(host));
360 }
361 return array;
362 }
363
364 /**
365 * Returns a JSON array representation of a list of links.
366 *
367 * @param links the links
368 * @return a JSON representation of the links
369 */
370 public ArrayNode links(Set<UiLink> links) {
371 ArrayNode array = arrayNode();
372 for (UiLink link : links) {
373 array.add(json(link));
374 }
375 return array;
376 }
377
378 // package-private for unit testing
379 List<Set<UiNode>> splitByLayer(List<String> layerTags,
380 Set<? extends UiNode> nodes) {
381 final int nLayers = layerTags.size();
382 if (!layerTags.get(nLayers - 1).equals(LAYER_DEFAULT)) {
383 throw new IllegalArgumentException(E_DEF_NOT_LAST);
384 }
385
386 List<Set<UiNode>> splitList = new ArrayList<>(layerTags.size());
387 Map<String, Set<UiNode>> byLayer = new HashMap<>(layerTags.size());
388
389 for (String tag : layerTags) {
390 Set<UiNode> set = new HashSet<>();
391 byLayer.put(tag, set);
392 splitList.add(set);
393 }
394
395 for (UiNode n : nodes) {
396 String which = n.layer();
397 if (!layerTags.contains(which)) {
398 which = LAYER_DEFAULT;
399 }
400 byLayer.get(which).add(n);
401 }
402
403 return splitList;
404 }
Simon Huntd5b96732016-07-08 13:22:27 -0700405}