blob: b63e3e0540dbe9402270d5de46a99da8f33cc425 [file] [log] [blame]
Thomas Vachuska598924e2014-10-23 22:26:07 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska598924e2014-10-23 22:26:07 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska598924e2014-10-23 22:26:07 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska598924e2014-10-23 22:26:07 -070015 */
16package org.onlab.onos.gui;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuskae972ea52014-10-30 00:14:16 -070021import org.onlab.onos.net.Annotations;
Thomas Vachuska598924e2014-10-23 22:26:07 -070022import org.onlab.onos.net.ConnectPoint;
23import org.onlab.onos.net.Device;
Thomas Vachuskae972ea52014-10-30 00:14:16 -070024import org.onlab.onos.net.DeviceId;
Thomas Vachuska598924e2014-10-23 22:26:07 -070025import org.onlab.onos.net.Host;
Thomas Vachuskae972ea52014-10-30 00:14:16 -070026import org.onlab.onos.net.HostId;
Thomas Vachuska598924e2014-10-23 22:26:07 -070027import org.onlab.onos.net.HostLocation;
28import org.onlab.onos.net.Link;
29import org.onlab.onos.net.device.DeviceService;
30import org.onlab.onos.net.host.HostService;
31import org.onlab.onos.net.link.LinkService;
32import org.onlab.onos.net.topology.Topology;
33import org.onlab.onos.net.topology.TopologyGraph;
34import org.onlab.onos.net.topology.TopologyService;
35import org.onlab.onos.net.topology.TopologyVertex;
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -070036import org.onlab.packet.IpAddress;
Thomas Vachuska598924e2014-10-23 22:26:07 -070037import org.onlab.packet.MacAddress;
38import org.onlab.rest.BaseResource;
39
40import javax.ws.rs.GET;
Thomas Vachuskae972ea52014-10-30 00:14:16 -070041import javax.ws.rs.PathParam;
Thomas Vachuska598924e2014-10-23 22:26:07 -070042import javax.ws.rs.Produces;
43import javax.ws.rs.core.Response;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.Iterator;
47import java.util.Map;
48import java.util.Set;
49
Thomas Vachuskae972ea52014-10-30 00:14:16 -070050import static org.onlab.onos.net.DeviceId.deviceId;
51import static org.onlab.onos.net.HostId.hostId;
52
Thomas Vachuska598924e2014-10-23 22:26:07 -070053/**
54 * Topology viewer resource.
55 */
56@javax.ws.rs.Path("topology")
57public class TopologyResource extends BaseResource {
58
Thomas Vachuskae972ea52014-10-30 00:14:16 -070059 private static final String HOST_SEP = "/";
60
Thomas Vachuska598924e2014-10-23 22:26:07 -070061 @javax.ws.rs.Path("/graph")
62 @GET
63 @Produces("application/json")
64 public Response graph() {
65 // Fetch the services we'll be using.
66 DeviceService deviceService = get(DeviceService.class);
67 HostService hostService = get(HostService.class);
68 TopologyService topologyService = get(TopologyService.class);
69
70 // Fetch the current topology and its graph that we'll use to render.
71 Topology topo = topologyService.currentTopology();
72 TopologyGraph graph = topologyService.getGraph(topo);
73
74 ObjectMapper mapper = new ObjectMapper();
75 ObjectNode rootNode = mapper.createObjectNode();
76 rootNode.set("devices", getDevices(mapper, deviceService, graph));
77 rootNode.set("links", getLinks(mapper, topo, graph));
78 rootNode.set("hosts", getHosts(mapper, hostService));
79 return Response.ok(rootNode.toString()).build();
80 }
81
Thomas Vachuskae972ea52014-10-30 00:14:16 -070082 @javax.ws.rs.Path("/graph/{id}")
83 @GET
84 @Produces("application/json")
85 public Response details(@PathParam("id") String id) {
86 if (id.contains(HOST_SEP)) {
87 return hostDetails(hostId(id));
88 }
89 return deviceDetails(deviceId(id));
90 }
91
92 // Returns device details response.
93 private Response deviceDetails(DeviceId deviceId) {
94 DeviceService deviceService = get(DeviceService.class);
95 Device device = deviceService.getDevice(deviceId);
96 Annotations annot = device.annotations();
Thomas Vachuska1de66012014-10-30 03:03:30 -070097 int portCount = deviceService.getPorts(deviceId).size();
Thomas Vachuskae972ea52014-10-30 00:14:16 -070098 ObjectNode r = json(deviceId.toString(),
Thomas Vachuska1de66012014-10-30 03:03:30 -070099 device.type().toString().toLowerCase(),
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700100 new Prop("Name", annot.value("name")),
101 new Prop("Vendor", device.manufacturer()),
102 new Prop("H/W Version", device.hwVersion()),
103 new Prop("S/W Version", device.swVersion()),
Simon Hunt1bd9ccf2014-11-03 13:59:19 -0800104 new Prop("Serial Number", device.serialNumber()),
Thomas Vachuska1de66012014-10-30 03:03:30 -0700105 new Separator(),
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700106 new Prop("Latitude", annot.value("latitude")),
Thomas Vachuska1de66012014-10-30 03:03:30 -0700107 new Prop("Longitude", annot.value("longitude")),
108 new Prop("Ports", Integer.toString(portCount)));
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700109 return Response.ok(r.toString()).build();
110 }
111
112 // Returns host details response.
113 private Response hostDetails(HostId hostId) {
114 HostService hostService = get(HostService.class);
115 Host host = hostService.getHost(hostId);
116 Annotations annot = host.annotations();
Thomas Vachuska1de66012014-10-30 03:03:30 -0700117 ObjectNode r = json(hostId.toString(), "host",
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700118 new Prop("MAC", host.mac().toString()),
119 new Prop("IP", host.ipAddresses().toString()),
Thomas Vachuska1de66012014-10-30 03:03:30 -0700120 new Separator(),
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700121 new Prop("Latitude", annot.value("latitude")),
122 new Prop("Longitude", annot.value("longitude")));
123 return Response.ok(r.toString()).build();
124 }
125
126 // Produces JSON property details.
Thomas Vachuska1de66012014-10-30 03:03:30 -0700127 private ObjectNode json(String id, String type, Prop... props) {
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700128 ObjectMapper mapper = new ObjectMapper();
Thomas Vachuska1de66012014-10-30 03:03:30 -0700129 ObjectNode result = mapper.createObjectNode()
130 .put("id", id).put("type", type);
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700131 ObjectNode pnode = mapper.createObjectNode();
132 ArrayNode porder = mapper.createArrayNode();
133 for (Prop p : props) {
134 porder.add(p.key);
135 pnode.put(p.key, p.value);
136 }
137 result.set("propOrder", porder);
138 result.set("props", pnode);
139 return result;
140 }
141
Thomas Vachuska598924e2014-10-23 22:26:07 -0700142 // Encodes all infrastructure devices.
143 private ArrayNode getDevices(ObjectMapper mapper, DeviceService deviceService,
144 TopologyGraph graph) {
145 ArrayNode devices = mapper.createArrayNode();
146 for (TopologyVertex vertex : graph.getVertexes()) {
147 devices.add(json(mapper, deviceService.getDevice(vertex.deviceId()),
148 deviceService.isAvailable(vertex.deviceId())));
149 }
150 return devices;
151 }
152
153 // Encodes all infrastructure links.
154 private ArrayNode getLinks(ObjectMapper mapper, Topology topo, TopologyGraph graph) {
155 // Now scan all links and count number of them between the same devices
156 // using a normalized link key.
157 Map<String, AggLink> linkRecords = aggregateLinks();
158
159 // Now build all interior edges using the aggregated links.
160 ArrayNode links = mapper.createArrayNode();
161 for (AggLink lr : linkRecords.values()) {
162 links.add(json(mapper, lr));
163 }
164 return links;
165 }
166
167 // Encodes all end-station hosts.
168 private ArrayNode getHosts(ObjectMapper mapper, HostService hostService) {
169 ArrayNode hosts = mapper.createArrayNode();
170 for (Host host : hostService.getHosts()) {
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700171 Set<IpAddress> ipAddresses = host.ipAddresses();
172 IpAddress ipAddress = ipAddresses.isEmpty() ? null : ipAddresses.iterator().next();
Thomas Vachuska598924e2014-10-23 22:26:07 -0700173 String label = ipAddress != null ? ipAddress.toString() : host.mac().toString();
174 hosts.add(json(mapper, host));
175 }
176 return hosts;
177 }
178
179 // Scan all links and counts number of them between the same devices
180 // using a normalized link key.
181 private Map<String, AggLink> aggregateLinks() {
182 Map<String, AggLink> aggLinks = new HashMap<>();
183 LinkService linkService = get(LinkService.class);
184 for (Link link : linkService.getLinks()) {
185 String key = key(link);
186 AggLink lr = aggLinks.get(key);
187 if (lr == null) {
188 lr = new AggLink(key);
189 aggLinks.put(key, lr);
190 }
191 lr.addLink(link);
192 }
193 return aggLinks;
194 }
195
196 // Produces JSON for a device.
197 private ObjectNode json(ObjectMapper mapper, Device device, boolean isOnline) {
198 ObjectNode node = mapper.createObjectNode()
199 .put("id", device.id().toString())
200 .put("type", device.type().toString().toLowerCase())
201 .put("online", isOnline);
202 node.set("labels", labels(mapper,
203 device.id().uri().getSchemeSpecificPart(),
204 MacAddress.valueOf(device.chassisId().value()).toString(),
205 device.serialNumber()));
206 return node;
207 }
208
209 // Produces JSON for a link.
210 private ObjectNode json(ObjectMapper mapper, AggLink aggLink) {
211 Link link = aggLink.link;
Thomas Vachuskac3d86ae2014-10-30 09:15:34 -0700212 ConnectPoint src = link.src();
213 ConnectPoint dst = link.dst();
Thomas Vachuska598924e2014-10-23 22:26:07 -0700214 return mapper.createObjectNode()
Thomas Vachuskac3d86ae2014-10-30 09:15:34 -0700215 .put("src", src.deviceId().toString())
216 .put("srcPort", src.port().toString())
217 .put("dst", dst.deviceId().toString())
218 .put("dstPort", dst.port().toString())
Thomas Vachuska598924e2014-10-23 22:26:07 -0700219 .put("type", link.type().toString().toLowerCase())
220 .put("linkWidth", aggLink.links.size());
221 }
222
223 // Produces JSON for a device.
224 private ObjectNode json(ObjectMapper mapper, Host host) {
225 ObjectNode json = mapper.createObjectNode()
226 .put("id", host.id().toString());
227 json.set("cp", location(mapper, host.location()));
228 json.set("labels", labels(mapper, ip(host.ipAddresses()),
229 host.mac().toString()));
230 return json;
231 }
232
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700233 private String ip(Set<IpAddress> ipAddresses) {
234 Iterator<IpAddress> it = ipAddresses.iterator();
Thomas Vachuska598924e2014-10-23 22:26:07 -0700235 return it.hasNext() ? it.next().toString() : "unknown";
236 }
237
238 private ObjectNode location(ObjectMapper mapper, HostLocation location) {
239 return mapper.createObjectNode()
240 .put("device", location.deviceId().toString())
241 .put("port", location.port().toLong());
242 }
243
244 private ArrayNode labels(ObjectMapper mapper, String... labels) {
245 ArrayNode json = mapper.createArrayNode();
246 for (String label : labels) {
247 json.add(label);
248 }
249 return json;
250 }
251
252 // Aggregate link of all links between the same devices regardless of
253 // their direction.
254 private class AggLink {
255 Link link; // representative links
256
257 final String key;
258 final Set<Link> links = new HashSet<>();
259
260 AggLink(String key) {
261 this.key = key;
262 }
263
264 void addLink(Link link) {
265 links.add(link);
266 if (this.link == null) {
267 this.link = link;
268 }
269 }
270 }
271
272 // Returns a canonical key for the specified link.
273 static String key(Link link) {
274 String s = id(link.src());
275 String d = id(link.dst());
276 return s.compareTo(d) > 0 ? d + s : s + d;
277 }
278
279 // Returns a formatted string for the element associated with the given
280 // connection point.
281 private static String id(ConnectPoint cp) {
282 return cp.elementId().toString();
283 }
284
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700285 // Auxiliary key/value carrier.
Thomas Vachuska1de66012014-10-30 03:03:30 -0700286 private class Prop {
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700287 private final String key;
288 private final String value;
289
Thomas Vachuska1de66012014-10-30 03:03:30 -0700290 protected Prop(String key, String value) {
Thomas Vachuskae972ea52014-10-30 00:14:16 -0700291 this.key = key;
292 this.value = value;
293 }
294 }
Thomas Vachuska1de66012014-10-30 03:03:30 -0700295
296 private class Separator extends Prop {
297 protected Separator() {
298 super("-", "");
299 }
300 }
Thomas Vachuska598924e2014-10-23 22:26:07 -0700301}