blob: 686455bb1119a37f916bc4bbe0d405e22e2ba5f5 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
tom8bb16062014-09-12 14:47:46 -070019package org.onlab.onos.tvue;
20
21import com.fasterxml.jackson.databind.ObjectMapper;
22import com.fasterxml.jackson.databind.node.ArrayNode;
23import com.fasterxml.jackson.databind.node.ObjectNode;
24import org.onlab.onos.net.ConnectPoint;
25import org.onlab.onos.net.ElementId;
tom025e09f2014-09-15 15:29:24 -070026import org.onlab.onos.net.Host;
tom8bb16062014-09-12 14:47:46 -070027import org.onlab.onos.net.Link;
toma33dd342014-09-12 19:07:40 -070028import org.onlab.onos.net.Path;
tom8bb16062014-09-12 14:47:46 -070029import org.onlab.onos.net.device.DeviceService;
30import org.onlab.onos.net.host.HostService;
31import org.onlab.onos.net.link.LinkService;
tom210f3f32014-09-16 17:33:40 -070032import org.onlab.onos.net.topology.PathService;
tom8bb16062014-09-12 14:47:46 -070033import org.onlab.onos.net.topology.Topology;
34import org.onlab.onos.net.topology.TopologyGraph;
35import org.onlab.onos.net.topology.TopologyService;
36import org.onlab.onos.net.topology.TopologyVertex;
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070037import org.onlab.packet.IpPrefix;
tom8bb16062014-09-12 14:47:46 -070038import org.onlab.rest.BaseResource;
39
40import javax.ws.rs.GET;
tom6a9986b2014-09-12 18:44:52 -070041import javax.ws.rs.PathParam;
tom8bb16062014-09-12 14:47:46 -070042import javax.ws.rs.Produces;
43import javax.ws.rs.core.Response;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.Map;
47import java.util.Set;
48
tom6a9986b2014-09-12 18:44:52 -070049import static org.onlab.onos.net.DeviceId.deviceId;
tomcbefa232014-09-16 14:17:20 -070050import static org.onlab.onos.net.HostId.hostId;
tom025e09f2014-09-15 15:29:24 -070051import static org.onlab.onos.net.PortNumber.portNumber;
tom6a9986b2014-09-12 18:44:52 -070052
tom8bb16062014-09-12 14:47:46 -070053/**
54 * Topology viewer resource.
55 */
tom6a9986b2014-09-12 18:44:52 -070056@javax.ws.rs.Path("topology")
tom8bb16062014-09-12 14:47:46 -070057public class TopologyResource extends BaseResource {
58
tom6a9986b2014-09-12 18:44:52 -070059 @javax.ws.rs.Path("/graph")
tom8bb16062014-09-12 14:47:46 -070060 @GET
61 @Produces("application/json")
62 public Response graph() {
63 ObjectMapper mapper = new ObjectMapper();
64
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 // Build all interior vertexes, i.e. no end-station hosts yet
75 ArrayNode vertexesNode = mapper.createArrayNode();
76 for (TopologyVertex vertex : graph.getVertexes()) {
77 vertexesNode.add(json(mapper, vertex.deviceId(), 2,
tom4d0c6632014-09-15 23:27:01 -070078 vertex.deviceId().uri().getSchemeSpecificPart(),
tom8bb16062014-09-12 14:47:46 -070079 deviceService.isAvailable(vertex.deviceId())));
80 }
81
82 // Now scan all links and count number of them between the same devices
83 // using a normalized link key.
84 Map<String, AggLink> linkRecords = aggregateLinks();
85
86 // Now build all interior edges using the aggregated links.
87 ArrayNode edgesNode = mapper.createArrayNode();
88 for (AggLink lr : linkRecords.values()) {
89 edgesNode.add(json(mapper, lr.links.size(), lr.link.src(), lr.link.dst()));
90 }
91
92 // Merge the exterior and interior vertexes and inject host links as
93 // the exterior edges.
tom025e09f2014-09-15 15:29:24 -070094 for (Host host : hostService.getHosts()) {
Ayaka Koshibe1d56fe42014-09-19 16:51:58 -070095 Set<IpPrefix> ipAddresses = host.ipAddresses();
96 IpPrefix ipAddress = ipAddresses.isEmpty() ? null : ipAddresses.iterator().next();
tom4d0c6632014-09-15 23:27:01 -070097 String label = ipAddress != null ? ipAddress.toString() : host.mac().toString();
98 vertexesNode.add(json(mapper, host.id(), 3, label, true));
tom025e09f2014-09-15 15:29:24 -070099 edgesNode.add(json(mapper, 1, host.location(), new ConnectPoint(host.id(), portNumber(-1))));
100 }
tom8bb16062014-09-12 14:47:46 -0700101
102 // Now put the vertexes and edges into a root node and ship them off
103 ObjectNode rootNode = mapper.createObjectNode();
tom4d0c6632014-09-15 23:27:01 -0700104 rootNode.set("vertexes", vertexesNode);
105 rootNode.set("edges", edgesNode);
tom8bb16062014-09-12 14:47:46 -0700106 return Response.ok(rootNode.toString()).build();
107 }
108
tom6a9986b2014-09-12 18:44:52 -0700109
tom6a9986b2014-09-12 18:44:52 -0700110 /**
111 * Returns a JSON array of all paths between the specified hosts.
112 *
113 * @param src source host id
114 * @param dst target host id
115 * @return JSON array of paths
116 */
117 @javax.ws.rs.Path("/paths/{src}/{dst}")
118 @GET
119 @Produces("application/json")
120 public Response paths(@PathParam("src") String src, @PathParam("dst") String dst) {
121 ObjectMapper mapper = new ObjectMapper();
tomcbefa232014-09-16 14:17:20 -0700122 PathService pathService = get(PathService.class);
123 Set<Path> paths = pathService.getPaths(elementId(src), elementId(dst));
tom6a9986b2014-09-12 18:44:52 -0700124
125 ArrayNode pathsNode = mapper.createArrayNode();
tomcbefa232014-09-16 14:17:20 -0700126 for (Path path : paths) {
toma33dd342014-09-12 19:07:40 -0700127 pathsNode.add(json(mapper, path));
128 }
tom6a9986b2014-09-12 18:44:52 -0700129
130 // Now put the vertexes and edges into a root node and ship them off
131 ObjectNode rootNode = mapper.createObjectNode();
toma33dd342014-09-12 19:07:40 -0700132 rootNode.set("paths", pathsNode);
tom6a9986b2014-09-12 18:44:52 -0700133 return Response.ok(rootNode.toString()).build();
134 }
135
tomcbefa232014-09-16 14:17:20 -0700136 // Creates either device ID or host ID as appropriate.
137 private ElementId elementId(String id) {
138 return id.startsWith("nic:") ? hostId(id) : deviceId(id);
139 }
140
tom8bb16062014-09-12 14:47:46 -0700141 // Scan all links and counts number of them between the same devices
142 // using a normalized link key.
143 private Map<String, AggLink> aggregateLinks() {
144 Map<String, AggLink> aggLinks = new HashMap<>();
145 LinkService linkService = get(LinkService.class);
146 for (Link link : linkService.getLinks()) {
147 String key = key(link);
148 AggLink lr = aggLinks.get(key);
149 if (lr == null) {
150 lr = new AggLink(key);
151 aggLinks.put(key, lr);
152 }
153 lr.addLink(link);
154 }
155 return aggLinks;
156 }
157
158 // Produces JSON for a graph vertex.
159 private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
tom4d0c6632014-09-15 23:27:01 -0700160 String label, boolean isOnline) {
tom8bb16062014-09-12 14:47:46 -0700161 return mapper.createObjectNode()
tom545708e2014-10-09 17:10:02 -0700162 .put("name", id.toString())
tom4d0c6632014-09-15 23:27:01 -0700163 .put("label", label)
tom8bb16062014-09-12 14:47:46 -0700164 .put("group", group)
165 .put("online", isOnline);
166 }
167
168 // Produces JSON for a graph edge.
169 private ObjectNode json(ObjectMapper mapper, int count,
170 ConnectPoint src, ConnectPoint dst) {
171 return json(mapper, count, id(src), id(dst));
172 }
173
174 // Produces JSON for a graph edge.
175 private ObjectNode json(ObjectMapper mapper, int count, String src, String dst) {
176 return mapper.createObjectNode()
177 .put("source", src).put("target", dst).put("value", count);
178 }
179
toma33dd342014-09-12 19:07:40 -0700180 // Produces JSON representation of a network path.
181 private ArrayNode json(ObjectMapper mapper, Path path) {
182 ArrayNode pathNode = mapper.createArrayNode();
183 for (Link link : path.links()) {
184 ObjectNode linkNode = mapper.createObjectNode()
185 .put("src", id(link.src()))
186 .put("dst", id(link.dst()));
187 pathNode.add(linkNode);
188 }
189 return pathNode;
190 }
191
192
tom8bb16062014-09-12 14:47:46 -0700193 // Aggregate link of all links between the same devices regardless of
194 // their direction.
195 private class AggLink {
196 Link link; // representative links
197
198 final String key;
199 final Set<Link> links = new HashSet<>();
200
201 AggLink(String key) {
202 this.key = key;
203 }
204
205 void addLink(Link link) {
206 links.add(link);
207 if (this.link == null) {
208 this.link = link;
209 }
210 }
211 }
212
213 // Returns a canonical key for the specified link.
214 static String key(Link link) {
215 String s = id(link.src());
216 String d = id(link.dst());
217 return s.compareTo(d) > 0 ? d + s : s + d;
218 }
219
220 // Returns a formatted string for the element associated with the given
221 // connection point.
222 private static String id(ConnectPoint cp) {
tom545708e2014-10-09 17:10:02 -0700223 return cp.elementId().toString();
tom8bb16062014-09-12 14:47:46 -0700224 }
225
226}