blob: 086afa63337396f07e3b107f147d6887c3f12e78 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -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 Vachuska781d18b2014-10-27 10:31:25 -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 Vachuska781d18b2014-10-27 10:31:25 -070015 */
tom8bb16062014-09-12 14:47:46 -070016package org.onlab.onos.tvue;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import org.onlab.onos.net.ConnectPoint;
22import org.onlab.onos.net.ElementId;
tom025e09f2014-09-15 15:29:24 -070023import org.onlab.onos.net.Host;
tom8bb16062014-09-12 14:47:46 -070024import org.onlab.onos.net.Link;
toma33dd342014-09-12 19:07:40 -070025import org.onlab.onos.net.Path;
tom8bb16062014-09-12 14:47:46 -070026import org.onlab.onos.net.device.DeviceService;
27import org.onlab.onos.net.host.HostService;
28import org.onlab.onos.net.link.LinkService;
tom210f3f32014-09-16 17:33:40 -070029import org.onlab.onos.net.topology.PathService;
tom8bb16062014-09-12 14:47:46 -070030import org.onlab.onos.net.topology.Topology;
31import org.onlab.onos.net.topology.TopologyGraph;
32import org.onlab.onos.net.topology.TopologyService;
33import org.onlab.onos.net.topology.TopologyVertex;
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -070034import org.onlab.packet.IpAddress;
tom8bb16062014-09-12 14:47:46 -070035import org.onlab.rest.BaseResource;
36
37import javax.ws.rs.GET;
tom6a9986b2014-09-12 18:44:52 -070038import javax.ws.rs.PathParam;
tom8bb16062014-09-12 14:47:46 -070039import javax.ws.rs.Produces;
40import javax.ws.rs.core.Response;
41import java.util.HashMap;
42import java.util.HashSet;
43import java.util.Map;
44import java.util.Set;
45
tom6a9986b2014-09-12 18:44:52 -070046import static org.onlab.onos.net.DeviceId.deviceId;
tomcbefa232014-09-16 14:17:20 -070047import static org.onlab.onos.net.HostId.hostId;
tom025e09f2014-09-15 15:29:24 -070048import static org.onlab.onos.net.PortNumber.portNumber;
tom6a9986b2014-09-12 18:44:52 -070049
tom8bb16062014-09-12 14:47:46 -070050/**
51 * Topology viewer resource.
52 */
tom6a9986b2014-09-12 18:44:52 -070053@javax.ws.rs.Path("topology")
tom8bb16062014-09-12 14:47:46 -070054public class TopologyResource extends BaseResource {
55
tom6a9986b2014-09-12 18:44:52 -070056 @javax.ws.rs.Path("/graph")
tom8bb16062014-09-12 14:47:46 -070057 @GET
58 @Produces("application/json")
59 public Response graph() {
60 ObjectMapper mapper = new ObjectMapper();
61
62 // Fetch the services we'll be using.
63 DeviceService deviceService = get(DeviceService.class);
64 HostService hostService = get(HostService.class);
65 TopologyService topologyService = get(TopologyService.class);
66
67 // Fetch the current topology and its graph that we'll use to render.
68 Topology topo = topologyService.currentTopology();
69 TopologyGraph graph = topologyService.getGraph(topo);
70
71 // Build all interior vertexes, i.e. no end-station hosts yet
72 ArrayNode vertexesNode = mapper.createArrayNode();
73 for (TopologyVertex vertex : graph.getVertexes()) {
74 vertexesNode.add(json(mapper, vertex.deviceId(), 2,
tom4d0c6632014-09-15 23:27:01 -070075 vertex.deviceId().uri().getSchemeSpecificPart(),
tom8bb16062014-09-12 14:47:46 -070076 deviceService.isAvailable(vertex.deviceId())));
77 }
78
79 // Now scan all links and count number of them between the same devices
80 // using a normalized link key.
81 Map<String, AggLink> linkRecords = aggregateLinks();
82
83 // Now build all interior edges using the aggregated links.
84 ArrayNode edgesNode = mapper.createArrayNode();
85 for (AggLink lr : linkRecords.values()) {
86 edgesNode.add(json(mapper, lr.links.size(), lr.link.src(), lr.link.dst()));
87 }
88
89 // Merge the exterior and interior vertexes and inject host links as
90 // the exterior edges.
tom025e09f2014-09-15 15:29:24 -070091 for (Host host : hostService.getHosts()) {
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -070092 Set<IpAddress> ipAddresses = host.ipAddresses();
93 IpAddress ipAddress = ipAddresses.isEmpty() ? null : ipAddresses.iterator().next();
tom4d0c6632014-09-15 23:27:01 -070094 String label = ipAddress != null ? ipAddress.toString() : host.mac().toString();
95 vertexesNode.add(json(mapper, host.id(), 3, label, true));
tom025e09f2014-09-15 15:29:24 -070096 edgesNode.add(json(mapper, 1, host.location(), new ConnectPoint(host.id(), portNumber(-1))));
97 }
tom8bb16062014-09-12 14:47:46 -070098
99 // Now put the vertexes and edges into a root node and ship them off
100 ObjectNode rootNode = mapper.createObjectNode();
tom4d0c6632014-09-15 23:27:01 -0700101 rootNode.set("vertexes", vertexesNode);
102 rootNode.set("edges", edgesNode);
tom8bb16062014-09-12 14:47:46 -0700103 return Response.ok(rootNode.toString()).build();
104 }
105
tom6a9986b2014-09-12 18:44:52 -0700106
tom6a9986b2014-09-12 18:44:52 -0700107 /**
108 * Returns a JSON array of all paths between the specified hosts.
109 *
110 * @param src source host id
111 * @param dst target host id
112 * @return JSON array of paths
113 */
114 @javax.ws.rs.Path("/paths/{src}/{dst}")
115 @GET
116 @Produces("application/json")
117 public Response paths(@PathParam("src") String src, @PathParam("dst") String dst) {
118 ObjectMapper mapper = new ObjectMapper();
tomcbefa232014-09-16 14:17:20 -0700119 PathService pathService = get(PathService.class);
120 Set<Path> paths = pathService.getPaths(elementId(src), elementId(dst));
tom6a9986b2014-09-12 18:44:52 -0700121
122 ArrayNode pathsNode = mapper.createArrayNode();
tomcbefa232014-09-16 14:17:20 -0700123 for (Path path : paths) {
toma33dd342014-09-12 19:07:40 -0700124 pathsNode.add(json(mapper, path));
125 }
tom6a9986b2014-09-12 18:44:52 -0700126
127 // Now put the vertexes and edges into a root node and ship them off
128 ObjectNode rootNode = mapper.createObjectNode();
toma33dd342014-09-12 19:07:40 -0700129 rootNode.set("paths", pathsNode);
tom6a9986b2014-09-12 18:44:52 -0700130 return Response.ok(rootNode.toString()).build();
131 }
132
tomcbefa232014-09-16 14:17:20 -0700133 // Creates either device ID or host ID as appropriate.
134 private ElementId elementId(String id) {
135 return id.startsWith("nic:") ? hostId(id) : deviceId(id);
136 }
137
tom8bb16062014-09-12 14:47:46 -0700138 // Scan all links and counts number of them between the same devices
139 // using a normalized link key.
140 private Map<String, AggLink> aggregateLinks() {
141 Map<String, AggLink> aggLinks = new HashMap<>();
142 LinkService linkService = get(LinkService.class);
143 for (Link link : linkService.getLinks()) {
144 String key = key(link);
145 AggLink lr = aggLinks.get(key);
146 if (lr == null) {
147 lr = new AggLink(key);
148 aggLinks.put(key, lr);
149 }
150 lr.addLink(link);
151 }
152 return aggLinks;
153 }
154
155 // Produces JSON for a graph vertex.
156 private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
tom4d0c6632014-09-15 23:27:01 -0700157 String label, boolean isOnline) {
tom8bb16062014-09-12 14:47:46 -0700158 return mapper.createObjectNode()
tom545708e2014-10-09 17:10:02 -0700159 .put("name", id.toString())
tom4d0c6632014-09-15 23:27:01 -0700160 .put("label", label)
tom8bb16062014-09-12 14:47:46 -0700161 .put("group", group)
162 .put("online", isOnline);
163 }
164
165 // Produces JSON for a graph edge.
166 private ObjectNode json(ObjectMapper mapper, int count,
167 ConnectPoint src, ConnectPoint dst) {
168 return json(mapper, count, id(src), id(dst));
169 }
170
171 // Produces JSON for a graph edge.
172 private ObjectNode json(ObjectMapper mapper, int count, String src, String dst) {
173 return mapper.createObjectNode()
174 .put("source", src).put("target", dst).put("value", count);
175 }
176
toma33dd342014-09-12 19:07:40 -0700177 // Produces JSON representation of a network path.
178 private ArrayNode json(ObjectMapper mapper, Path path) {
179 ArrayNode pathNode = mapper.createArrayNode();
180 for (Link link : path.links()) {
181 ObjectNode linkNode = mapper.createObjectNode()
182 .put("src", id(link.src()))
183 .put("dst", id(link.dst()));
184 pathNode.add(linkNode);
185 }
186 return pathNode;
187 }
188
189
tom8bb16062014-09-12 14:47:46 -0700190 // Aggregate link of all links between the same devices regardless of
191 // their direction.
192 private class AggLink {
193 Link link; // representative links
194
195 final String key;
196 final Set<Link> links = new HashSet<>();
197
198 AggLink(String key) {
199 this.key = key;
200 }
201
202 void addLink(Link link) {
203 links.add(link);
204 if (this.link == null) {
205 this.link = link;
206 }
207 }
208 }
209
210 // Returns a canonical key for the specified link.
211 static String key(Link link) {
212 String s = id(link.src());
213 String d = id(link.dst());
214 return s.compareTo(d) > 0 ? d + s : s + d;
215 }
216
217 // Returns a formatted string for the element associated with the given
218 // connection point.
219 private static String id(ConnectPoint cp) {
tom545708e2014-10-09 17:10:02 -0700220 return cp.elementId().toString();
tom8bb16062014-09-12 14:47:46 -0700221 }
222
223}