blob: df6dba961dd38c7e4c213d3924b6c29855b9c156 [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;
43
Simon Hunt977aa052016-07-20 17:08:29 -070044import java.util.ArrayList;
45import java.util.HashMap;
46import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070047import java.util.List;
Simon Hunt977aa052016-07-20 17:08:29 -070048import java.util.Map;
49import java.util.Set;
Simon Huntd5b96732016-07-08 13:22:27 -070050
51import static com.google.common.base.Preconditions.checkNotNull;
Simon Hunt977aa052016-07-20 17:08:29 -070052import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
Simon Huntd5b96732016-07-08 13:22:27 -070053
54/**
55 * Facility for creating JSON messages to send to the topology view in the
56 * Web client.
57 */
58class Topo2Jsonifier {
59
Simon Hunt977aa052016-07-20 17:08:29 -070060 private static final String E_DEF_NOT_LAST =
61 "UiNode.LAYER_DEFAULT not last in layer list";
62 private static final String E_UNKNOWN_UI_NODE =
63 "Unknown subclass of UiNode: ";
64
Simon Huntd5b96732016-07-08 13:22:27 -070065 private final ObjectMapper mapper = new ObjectMapper();
66
67 private ServiceDirectory directory;
68 private ClusterService clusterService;
69 private DeviceService deviceService;
70 private LinkService linkService;
71 private HostService hostService;
72 private MastershipService mastershipService;
73 private IntentService intentService;
74 private FlowRuleService flowService;
75 private StatisticService flowStatsService;
76 private PortStatisticsService portStatsService;
77 private TopologyService topologyService;
78 private TunnelService tunnelService;
79
80
81 /**
82 * Creates an instance with a reference to the services directory, so that
83 * additional information about network elements may be looked up on
84 * on the fly.
85 *
86 * @param directory service directory
87 */
88 Topo2Jsonifier(ServiceDirectory directory) {
89 this.directory = checkNotNull(directory, "Directory cannot be null");
90
91 clusterService = directory.get(ClusterService.class);
92 deviceService = directory.get(DeviceService.class);
93 linkService = directory.get(LinkService.class);
94 hostService = directory.get(HostService.class);
95 mastershipService = directory.get(MastershipService.class);
96 intentService = directory.get(IntentService.class);
97 flowService = directory.get(FlowRuleService.class);
98 flowStatsService = directory.get(StatisticService.class);
99 portStatsService = directory.get(PortStatisticsService.class);
100 topologyService = directory.get(TopologyService.class);
101 tunnelService = directory.get(TunnelService.class);
Simon Hunt977aa052016-07-20 17:08:29 -0700102 }
Simon Huntd5b96732016-07-08 13:22:27 -0700103
Simon Hunt977aa052016-07-20 17:08:29 -0700104 // for unit testing
105 Topo2Jsonifier() {
Simon Huntd5b96732016-07-08 13:22:27 -0700106 }
107
108 private ObjectNode objectNode() {
109 return mapper.createObjectNode();
110 }
111
112 private ArrayNode arrayNode() {
113 return mapper.createArrayNode();
114 }
115
116 private String nullIsEmpty(Object o) {
117 return o == null ? "" : o.toString();
118 }
119
120
121 /**
122 * Returns a JSON representation of the cluster members (ONOS instances).
123 *
124 * @param instances the instance model objects
125 * @return a JSON representation of the data
126 */
127 ObjectNode instances(List<UiClusterMember> instances) {
128 NodeId local = clusterService.getLocalNode().id();
129 ObjectNode payload = objectNode();
130
131 ArrayNode members = arrayNode();
132 payload.set("members", members);
133 for (UiClusterMember member : instances) {
134 members.add(json(member, member.id().equals(local)));
135 }
136
137 return payload;
138 }
139
140 private ObjectNode json(UiClusterMember member, boolean isUiAttached) {
141 return objectNode()
142 .put("id", member.id().toString())
143 .put("ip", member.ip().toString())
144 .put("online", member.isOnline())
145 .put("ready", member.isReady())
146 .put("uiAttached", isUiAttached)
147 .put("switches", member.deviceCount());
148 }
149
150 /**
151 * Returns a JSON representation of the layout to use for displaying in
152 * the topology view.
153 *
154 * @param layout the layout to transform
155 * @return a JSON representation of the data
156 */
157 ObjectNode layout(UiTopoLayout layout) {
158 return objectNode()
159 .put("id", layout.id().toString())
160 .put("parent", nullIsEmpty(layout.parent()))
161 .put("region", nullIsEmpty(layout.regionId()))
162 .put("regionName", regionName(layout.region()));
163 }
164
165 private String regionName(Region region) {
166 return region == null ? "" : region.name();
167 }
168
169 /**
170 * Returns a JSON representation of the region to display in the topology
171 * view.
172 *
Simon Hunt977aa052016-07-20 17:08:29 -0700173 * @param region the region to transform to JSON
174 * @param subRegions the subregions within this region
Simon Huntd5b96732016-07-08 13:22:27 -0700175 * @return a JSON representation of the data
176 */
Simon Hunt977aa052016-07-20 17:08:29 -0700177 ObjectNode region(UiRegion region, Set<UiRegion> subRegions) {
Simon Huntd5b96732016-07-08 13:22:27 -0700178 ObjectNode payload = objectNode();
Simon Huntd5b96732016-07-08 13:22:27 -0700179 if (region == null) {
180 payload.put("note", "no-region");
181 return payload;
182 }
Simon Hunt977aa052016-07-20 17:08:29 -0700183 payload.put("id", region.idAsString());
184 payload.set("subregions", jsonSubRegions(subRegions));
Simon Huntd5b96732016-07-08 13:22:27 -0700185
Simon Hunt977aa052016-07-20 17:08:29 -0700186 List<String> layerTags = region.layerOrder();
187 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
188 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
189 Set<UiLink> links = region.links();
Simon Huntd5b96732016-07-08 13:22:27 -0700190
Simon Hunt977aa052016-07-20 17:08:29 -0700191 payload.set("devices", jsonGrouped(splitDevices));
192 payload.set("hosts", jsonGrouped(splitHosts));
193 payload.set("links", jsonLinks(links));
194 payload.set("layerOrder", jsonStrings(layerTags));
Simon Huntd5b96732016-07-08 13:22:27 -0700195
196 return payload;
197 }
198
Simon Hunt977aa052016-07-20 17:08:29 -0700199 private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
200 ArrayNode kids = arrayNode();
201 if (subregions != null) {
202 subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
203 }
204 return kids;
205 }
206
207 private ArrayNode jsonStrings(List<String> strings) {
208 ArrayNode array = arrayNode();
209 strings.forEach(array::add);
210 return array;
211 }
212
213 private ArrayNode jsonLinks(Set<UiLink> links) {
214 ArrayNode result = arrayNode();
215 links.forEach(lnk -> result.add(json(lnk)));
216 return result;
217 }
218
219 private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
220 ArrayNode result = arrayNode();
221 groupedNodes.forEach(g -> {
222 ArrayNode subset = arrayNode();
223 g.forEach(n -> subset.add(json(n)));
224 result.add(subset);
225 });
226 return result;
227 }
228
229 /**
230 * Returns a JSON payload that encapsulates the devices, hosts, links that
231 * do not belong to any region.
232 *
233 * @param oDevices orphan devices
234 * @param oHosts orphan hosts
235 * @param oLinks orphan links
236 * @param layerTags layer tags
237 * @return a JSON representation of the data
238 */
239 ObjectNode orphans(Set<UiDevice> oDevices, Set<UiHost> oHosts,
240 Set<UiLink> oLinks, List<String> layerTags) {
241
242 ObjectNode payload = objectNode();
243
244 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, oDevices);
245 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, oHosts);
246
247 payload.set("devices", jsonGrouped(splitDevices));
248 payload.set("hosts", jsonGrouped(splitHosts));
249 payload.set("links", jsonLinks(oLinks));
250 payload.set("layerOrder", jsonStrings(layerTags));
251
252 return payload;
253 }
254
255 private ObjectNode json(UiNode node) {
256 if (node instanceof UiRegion) {
257 return jsonClosedRegion((UiRegion) node);
258 }
259 if (node instanceof UiDevice) {
260 return json((UiDevice) node);
261 }
262 if (node instanceof UiHost) {
263 return json((UiHost) node);
264 }
265 throw new IllegalStateException(E_UNKNOWN_UI_NODE + node.getClass());
266 }
267
Simon Huntd5b96732016-07-08 13:22:27 -0700268 private ObjectNode json(UiDevice device) {
269 ObjectNode node = objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700270 .put("id", device.idAsString())
Simon Huntd5b96732016-07-08 13:22:27 -0700271 .put("type", device.type())
272 .put("online", device.isOnline())
273 .put("master", device.master().toString())
274 .put("layer", device.layer());
275
276 // TODO: complete device details
277// addLabels(node, device);
278// addProps(node, device);
279// addGeoLocation(node, device);
280// addMetaUi(node, device);
281
282 return node;
283 }
284
285 private void addLabels(ObjectNode node, UiDevice device) {
286
287 }
288
289 private ObjectNode json(UiHost host) {
290 return objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700291 .put("id", host.idAsString())
Simon Huntd5b96732016-07-08 13:22:27 -0700292 .put("layer", host.layer());
293 // TODO: complete host details
294 }
295
296
297 private ObjectNode json(UiLink link) {
298 return objectNode()
Simon Hunt977aa052016-07-20 17:08:29 -0700299 .put("id", link.idAsString());
Simon Huntd5b96732016-07-08 13:22:27 -0700300 // TODO: complete link details
301 }
302
303
Simon Hunt977aa052016-07-20 17:08:29 -0700304 private ObjectNode jsonClosedRegion(UiRegion region) {
305 return objectNode()
306 .put("id", region.idAsString());
307 // TODO: complete closed-region details
308 }
309
310
311 /**
312 * Returns a JSON array representation of a list of regions. Note that the
313 * information about each region is limited to what needs to be used to
314 * show the regions as nodes on the view.
315 *
316 * @param regions the regions
317 * @return a JSON representation of the minimal region information
318 */
319 public ArrayNode closedRegions(Set<UiRegion> regions) {
320 ArrayNode array = arrayNode();
321 for (UiRegion r : regions) {
322 array.add(jsonClosedRegion(r));
323 }
324 return array;
325 }
326
327 /**
328 * Returns a JSON array representation of a list of devices.
329 *
330 * @param devices the devices
331 * @return a JSON representation of the devices
332 */
333 public ArrayNode devices(Set<UiDevice> devices) {
334 ArrayNode array = arrayNode();
335 for (UiDevice device : devices) {
336 array.add(json(device));
337 }
338 return array;
339 }
340
341 /**
342 * Returns a JSON array representation of a list of hosts.
343 *
344 * @param hosts the hosts
345 * @return a JSON representation of the hosts
346 */
347 public ArrayNode hosts(Set<UiHost> hosts) {
348 ArrayNode array = arrayNode();
349 for (UiHost host : hosts) {
350 array.add(json(host));
351 }
352 return array;
353 }
354
355 /**
356 * Returns a JSON array representation of a list of links.
357 *
358 * @param links the links
359 * @return a JSON representation of the links
360 */
361 public ArrayNode links(Set<UiLink> links) {
362 ArrayNode array = arrayNode();
363 for (UiLink link : links) {
364 array.add(json(link));
365 }
366 return array;
367 }
368
369 // package-private for unit testing
370 List<Set<UiNode>> splitByLayer(List<String> layerTags,
371 Set<? extends UiNode> nodes) {
372 final int nLayers = layerTags.size();
373 if (!layerTags.get(nLayers - 1).equals(LAYER_DEFAULT)) {
374 throw new IllegalArgumentException(E_DEF_NOT_LAST);
375 }
376
377 List<Set<UiNode>> splitList = new ArrayList<>(layerTags.size());
378 Map<String, Set<UiNode>> byLayer = new HashMap<>(layerTags.size());
379
380 for (String tag : layerTags) {
381 Set<UiNode> set = new HashSet<>();
382 byLayer.put(tag, set);
383 splitList.add(set);
384 }
385
386 for (UiNode n : nodes) {
387 String which = n.layer();
388 if (!layerTags.contains(which)) {
389 which = LAYER_DEFAULT;
390 }
391 byLayer.get(which).add(n);
392 }
393
394 return splitList;
395 }
Simon Huntd5b96732016-07-08 13:22:27 -0700396}