2 * Copyright (c) 2013 Plexxi, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.affinity.analytics.northbound;
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.List;
17 import javax.ws.rs.GET;
18 import javax.ws.rs.Path;
19 import javax.ws.rs.PathParam;
20 import javax.ws.rs.Produces;
21 import javax.ws.rs.core.Context;
22 import javax.ws.rs.core.MediaType;
23 import javax.ws.rs.core.SecurityContext;
25 import org.codehaus.enunciate.jaxrs.ResponseCode;
26 import org.codehaus.enunciate.jaxrs.StatusCodes;
27 import org.codehaus.enunciate.jaxrs.TypeHint;
29 import org.opendaylight.affinity.affinity.AffinityLink;
30 import org.opendaylight.affinity.affinity.IAffinityManager;
31 import org.opendaylight.affinity.analytics.IAnalyticsManager;
32 import org.opendaylight.controller.containermanager.IContainerManager;
33 import org.opendaylight.controller.hosttracker.IfIptoHost;
34 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
35 import org.opendaylight.controller.northbound.commons.RestMessages;
36 import org.opendaylight.controller.northbound.commons.exception.*;
37 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
38 import org.opendaylight.controller.sal.authorization.Privilege;
39 import org.opendaylight.controller.sal.core.Host;
40 import org.opendaylight.controller.sal.utils.GlobalConstants;
41 import org.opendaylight.controller.sal.utils.IPProtocols;
42 import org.opendaylight.controller.sal.utils.ServiceHelper;
43 import org.opendaylight.controller.switchmanager.ISwitchManager;
46 * Northbound APIs that returns various Analytics exposed by the Southbound
47 * plugins such as Openflow.
51 * Authentication scheme : <b>HTTP Basic</b><br>
52 * Authentication realm : <b>opendaylight</b><br>
53 * Transport : <b>HTTP and HTTPS</b><br>
55 * HTTPS Authentication is disabled by default. Administrator can enable it in
56 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
57 * trusted authority.<br>
59 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
63 public class AnalyticsNorthbound {
65 private String username;
68 public void setSecurityContext(SecurityContext context) {
69 username = context.getUserPrincipal().getName();
72 protected String getUserName() {
76 private IAnalyticsManager getAnalyticsService(String containerName) {
77 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(IContainerManager.class, this);
78 if (containerManager == null)
79 throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString());
81 boolean found = false;
82 List<String> containerNames = containerManager.getContainerNames();
83 for (String cName : containerNames)
84 if (cName.trim().equalsIgnoreCase(containerName.trim()))
87 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
89 IAnalyticsManager analyticsManager = (IAnalyticsManager) ServiceHelper.getInstance(IAnalyticsManager.class, containerName, this);
90 if (analyticsManager == null)
91 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
92 return analyticsManager;
96 * @param containerName: Name of the Container
97 * @param dataLayerAddr: DataLayerAddress for the host
98 * @param networkAddr: NetworkAddress for the host
99 * @return Statistics for a (src, dst) pair.
101 @Path("/{containerName}/hoststats/{srcNetworkAddr}/{dstNetworkAddr}")
103 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
104 @TypeHint(Statistics.class)
106 @ResponseCode(code = 200, condition = "Operation successful"),
107 @ResponseCode(code = 404, condition = "The containerName is not found"),
108 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
109 public Statistics getHostStatistics(
110 @PathParam("containerName") String containerName,
111 @PathParam("srcNetworkAddr") String srcNetworkAddr,
112 @PathParam("dstNetworkAddr") String dstNetworkAddr) {
113 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
114 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
115 handleDefaultDisabled(containerName);
117 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
118 if (analyticsManager == null)
119 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
121 Host srcHost = handleHostAvailability(containerName, srcNetworkAddr);
122 Host dstHost = handleHostAvailability(containerName, dstNetworkAddr);
123 long byteCount = analyticsManager.getByteCount(srcHost, dstHost);
124 double bitRate = analyticsManager.getBitRate(srcHost, dstHost);
126 return new Statistics(byteCount, bitRate);
130 * @param containerName: Name of the Container
131 * @param srcIP: Source IP
132 * @param dstIP: Destination IP
133 * @param protocol: IP protocol
134 * @return Statistics for a (src, dst) pair and a particular protocol
136 @Path("/{containerName}/hoststats/{srcIP}/{dstIP}/{protocol}")
138 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
139 @TypeHint(Statistics.class)
141 @ResponseCode(code = 200, condition = "Operation successful"),
142 @ResponseCode(code = 404, condition = "The containerName is not found"),
143 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
144 public Statistics getHostStatistics(
145 @PathParam("containerName") String containerName,
146 @PathParam("srcIP") String srcIP,
147 @PathParam("dstIP") String dstIP,
148 @PathParam("protocol") String protocol) {
149 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
150 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
151 handleDefaultDisabled(containerName);
153 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
154 if (analyticsManager == null)
155 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
157 Host srcHost = handleHostAvailability(containerName, srcIP);
158 Host dstHost = handleHostAvailability(containerName, dstIP);
159 long byteCount = analyticsManager.getByteCount(srcHost, dstHost, IPProtocols.getProtocolNumberByte(protocol));
160 double bitRate = analyticsManager.getBitRate(srcHost, dstHost, IPProtocols.getProtocolNumberByte(protocol));
162 return new Statistics(byteCount, bitRate);
166 * @param containerName: Name of the Container
167 * @param srcIP: Source IP
168 * @param dstIP: Destination IP
169 * @return All statistics for a (src, dst) pair
171 @Path("/{containerName}/hoststats/{srcIP}/{dstIP}/all")
173 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
174 @TypeHint(AllStatistics.class)
176 @ResponseCode(code = 200, condition = "Operation successful"),
177 @ResponseCode(code = 404, condition = "The containerName is not found"),
178 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
179 public AllStatistics getAllHostStatistics(
180 @PathParam("containerName") String containerName,
181 @PathParam("srcIP") String srcIP,
182 @PathParam("dstIP") String dstIP) {
183 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
184 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
185 handleDefaultDisabled(containerName);
187 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
188 if (analyticsManager == null)
189 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
191 Host srcHost = handleHostAvailability(containerName, srcIP);
192 Host dstHost = handleHostAvailability(containerName, dstIP);
193 Map<Byte, Long> byteCounts = analyticsManager.getAllByteCounts(srcHost, dstHost);
194 Map<Byte, Double> bitRates = analyticsManager.getAllBitRates(srcHost, dstHost);
195 return new AllStatistics(byteCounts, bitRates);
199 * @param containerName: Name of the Container
200 * @param linkName: AffinityLink name
201 * @return Statistics for an affinity link
203 @Path("/{containerName}/affinitylinkstats/{linkName}")
205 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
206 @TypeHint(Statistics.class)
208 @ResponseCode(code = 200, condition = "Operation successful"),
209 @ResponseCode(code = 404, condition = "The containerName is not found"),
210 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
211 public Statistics getAffinityLinkStatistics(
212 @PathParam("containerName") String containerName,
213 @PathParam("linkName") String affinityLinkName) {
214 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
215 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
216 handleDefaultDisabled(containerName);
218 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
219 if (analyticsManager == null)
220 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
222 AffinityLink al = handleAffinityLinkAvailability(containerName, affinityLinkName);
223 long byteCount = analyticsManager.getByteCount(al);
224 double bitRate = analyticsManager.getBitRate(al);
226 return new Statistics(byteCount, bitRate);
230 * @param containerName: Name of the Container
231 * @param linkName: AffinityLink name
232 * @param protocol: IP Protocol
233 * @return Statistics for an affinity link and a particular protocol
235 @Path("/{containerName}/affinitylinkstats/{linkName}/{protocol}")
237 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
238 @TypeHint(Statistics.class)
240 @ResponseCode(code = 200, condition = "Operation successful"),
241 @ResponseCode(code = 404, condition = "The containerName is not found"),
242 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
243 public Statistics getAffinityLinkStatistics(
244 @PathParam("containerName") String containerName,
245 @PathParam("linkName") String affinityLinkName,
246 @PathParam("protocol") String protocol) {
247 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
248 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
249 handleDefaultDisabled(containerName);
251 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
252 if (analyticsManager == null)
253 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
255 AffinityLink al = handleAffinityLinkAvailability(containerName, affinityLinkName);
256 long byteCount = analyticsManager.getByteCount(al, IPProtocols.getProtocolNumberByte(protocol));
257 double bitRate = analyticsManager.getBitRate(al, IPProtocols.getProtocolNumberByte(protocol));
259 return new Statistics(byteCount, bitRate);
263 * @param containerName: Name of the Container
264 * @param linkName: AffinityLink name
265 * @return All statistics for an affinity link
267 @Path("/{containerName}/affinitylinkstats/{linkName}/all")
269 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
270 @TypeHint(AllStatistics.class)
272 @ResponseCode(code = 200, condition = "Operation successful"),
273 @ResponseCode(code = 404, condition = "The containerName is not found"),
274 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
275 public AllStatistics getAllAffinityLinkStatistics(
276 @PathParam("containerName") String containerName,
277 @PathParam("linkName") String affinityLinkName) {
278 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
279 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
280 handleDefaultDisabled(containerName);
282 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
283 if (analyticsManager == null)
284 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
286 AffinityLink al = handleAffinityLinkAvailability(containerName, affinityLinkName);
287 Map<Byte, Long> byteCounts = analyticsManager.getAllByteCounts(al);
288 Map<Byte, Double> bitRates = analyticsManager.getAllBitRates(al);
289 return new AllStatistics(byteCounts, bitRates);
293 * @param containerName: Name of the Container
294 * @param srcIP: Source IP prefix
295 * @param srcMask: Source mask
296 * @param dstIP: Destination IP prefix
297 * @param dstMask: Destination mask
298 * @return Statistics between subnets
300 @Path("/{containerName}/subnetstats/{srcIP}/{srcMask}/{dstIP}/{dstMask}")
302 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
303 @TypeHint(Statistics.class)
305 @ResponseCode(code = 200, condition = "Operation successful"),
306 @ResponseCode(code = 404, condition = "The containerName is not found"),
307 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
308 public Statistics getSubnetStatistics(
309 @PathParam("containerName") String containerName,
310 @PathParam("srcIP") String srcIP,
311 @PathParam("srcMask") String srcMask,
312 @PathParam("dstIP") String dstIP,
313 @PathParam("dstMask") String dstMask) {
314 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
315 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
316 handleDefaultDisabled(containerName);
318 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
319 if (analyticsManager == null)
320 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
322 String srcString = srcIP + "/" + srcMask;
323 String dstString = dstIP + "/" + dstMask;
324 // TODO: This is hardly the most elegant way to handle null prefixes
325 if (srcString.equals("null/null"))
327 if (dstString.equals("null/null"))
329 long byteCount = analyticsManager.getByteCount(srcString, dstString);
330 double bitRate = analyticsManager.getBitRate(srcString, dstString);
332 return new Statistics(byteCount, bitRate);
336 * @param containerName: Name of the Container
337 * @param srcIP: Source IP prefix
338 * @param srcMask: Source mask
339 * @param dstIP: Destination IP prefix
340 * @param dstMask: Destination mask
341 * @param protocol: IP protocol
342 * @return Statistics between subnets for a particular protocol
344 @Path("/{containerName}/subnetstats/{srcIP}/{srcMask}/{dstIP}/{dstMask}/{protocol}")
346 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
347 @TypeHint(Statistics.class)
349 @ResponseCode(code = 200, condition = "Operation successful"),
350 @ResponseCode(code = 404, condition = "The containerName is not found"),
351 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
352 public Statistics getSubnetStatistics(
353 @PathParam("containerName") String containerName,
354 @PathParam("srcIP") String srcIP,
355 @PathParam("srcMask") String srcMask,
356 @PathParam("dstIP") String dstIP,
357 @PathParam("dstMask") String dstMask,
358 @PathParam("protocol") String protocol) {
359 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
360 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
361 handleDefaultDisabled(containerName);
363 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
364 if (analyticsManager == null)
365 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
367 String srcString = srcIP + "/" + srcMask;
368 String dstString = dstIP + "/" + dstMask;
369 // TODO: This is hardly the most elegant way to handle null prefixes
370 if (srcString.equals("null/null"))
372 if (dstString.equals("null/null"))
374 long byteCount = analyticsManager.getByteCount(srcString, dstString, IPProtocols.getProtocolNumberByte(protocol));
375 double bitRate = analyticsManager.getBitRate(srcString, dstString, IPProtocols.getProtocolNumberByte(protocol));
377 return new Statistics(byteCount, bitRate);
380 * @param containerName: Name of the Container
381 * @param srcIP: Source IP prefix
382 * @param srcMask: Source mask
383 * @param dstIP: Destination IP prefix
384 * @param dstMask: Destination mask
385 * @return Statistics between subnets for a particular protocol
387 @Path("/{containerName}/subnetstats/{srcIP}/{srcMask}/{dstIP}/{dstMask}/all")
389 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
390 @TypeHint(AllStatistics.class)
392 @ResponseCode(code = 200, condition = "Operation successful"),
393 @ResponseCode(code = 404, condition = "The containerName is not found"),
394 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
395 public AllStatistics getAllSubnetStatistics(
396 @PathParam("containerName") String containerName,
397 @PathParam("srcIP") String srcIP,
398 @PathParam("srcMask") String srcMask,
399 @PathParam("dstIP") String dstIP,
400 @PathParam("dstMask") String dstMask) {
401 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
402 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
403 handleDefaultDisabled(containerName);
405 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
406 if (analyticsManager == null)
407 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
409 String srcString = srcIP + "/" + srcMask;
410 String dstString = dstIP + "/" + dstMask;
411 // TODO: This is hardly the most elegant way to handle null prefixes
412 if (srcString.equals("null/null"))
414 if (dstString.equals("null/null"))
417 Map<Byte, Long> byteCounts = analyticsManager.getAllByteCounts(srcString, dstString);
418 Map<Byte, Double> bitRates = analyticsManager.getAllBitRates(srcString, dstString);
419 return new AllStatistics(byteCounts, bitRates);
423 * @param containerName: Name of the Container
424 * @param ip: IP prefix
426 * @return Hosts that sent data into this subnet
428 @Path("/{containerName}/subnetstats/incoming/{ip}/{mask}/")
430 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
431 @TypeHint(IncomingHostData.class)
433 @ResponseCode(code = 200, condition = "Operation successful"),
434 @ResponseCode(code = 404, condition = "The containerName is not found"),
435 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
436 public IncomingHostData getIncomingHostByteCounts(
437 @PathParam("containerName") String containerName,
438 @PathParam("ip") String ip,
439 @PathParam("mask") String mask) {
440 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
441 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
442 handleDefaultDisabled(containerName);
444 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
445 if (analyticsManager == null)
446 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
448 Map<Host, Long> hosts = analyticsManager.getIncomingHostByteCounts(ip + "/" + mask);
449 return new IncomingHostData(hosts);
453 * @param containerName: Name of the Container
454 * @param ip: IP prefix
456 * @param protocol: IP protocol
457 * @return Hosts that sent data into this subnet using this protocol
459 @Path("/{containerName}/subnetstats/incoming/{ip}/{mask}/{protocol}")
461 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
462 @TypeHint(IncomingHostData.class)
464 @ResponseCode(code = 200, condition = "Operation successful"),
465 @ResponseCode(code = 404, condition = "The containerName is not found"),
466 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
467 public IncomingHostData getIncomingHostByteCounts(
468 @PathParam("containerName") String containerName,
469 @PathParam("ip") String ip,
470 @PathParam("mask") String mask,
471 @PathParam("protocol") String protocol) {
472 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this))
473 throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName);
474 handleDefaultDisabled(containerName);
476 IAnalyticsManager analyticsManager = getAnalyticsService(containerName);
477 if (analyticsManager == null)
478 throw new ServiceUnavailableException("Analytics " + RestMessages.SERVICEUNAVAILABLE.toString());
480 Map<Host, Long> hosts = analyticsManager.getIncomingHostByteCounts(ip + "/" + mask, IPProtocols.getProtocolNumberByte(protocol));
481 return new IncomingHostData(hosts);
484 private void handleDefaultDisabled(String containerName) {
485 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(IContainerManager.class, this);
486 if (containerManager == null)
487 throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
488 if (containerName.equals(GlobalConstants.DEFAULT.toString()) && containerManager.hasNonDefaultContainer())
489 throw new ResourceConflictException(RestMessages.DEFAULTDISABLED.toString());
492 private AffinityLink handleAffinityLinkAvailability(String containerName, String linkName) {
493 IAffinityManager affinityManager = (IAffinityManager) ServiceHelper.getInstance(IAffinityManager.class, containerName, this);
494 if (affinityManager == null)
495 throw new ServiceUnavailableException("Affinity manager " + RestMessages.SERVICEUNAVAILABLE.toString());
496 AffinityLink al = affinityManager.getAffinityLink(linkName);
498 throw new ResourceNotFoundException(linkName + " : AffinityLink does not exist");
503 private Host handleHostAvailability(String containerName, String networkAddr) {
504 IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(IfIptoHost.class, containerName, this);
505 if (hostTracker == null)
506 throw new ServiceUnavailableException("Host tracker " + RestMessages.SERVICEUNAVAILABLE.toString());
508 Set<HostNodeConnector> allHosts = hostTracker.getAllHosts();
509 if (allHosts == null)
510 throw new ResourceNotFoundException(networkAddr + " : " + RestMessages.NOHOST.toString());
514 InetAddress networkAddress = InetAddress.getByName(networkAddr);
515 for (Host h : allHosts) {
516 if (h.getNetworkAddress().equals(networkAddress)) {
521 } catch (UnknownHostException e) {
525 throw new ResourceNotFoundException(networkAddr + " : " + RestMessages.NOHOST.toString());