2 * Copyright (c) 2013 Cisco Systems, 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.controller.hosttracker.northbound;
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.security.Principal;
14 import java.util.List;
15 import javax.ws.rs.Consumes;
16 import javax.ws.rs.DELETE;
17 import javax.ws.rs.DefaultValue;
18 import javax.ws.rs.GET;
19 import javax.ws.rs.POST;
20 import javax.ws.rs.Path;
21 import javax.ws.rs.PathParam;
22 import javax.ws.rs.Produces;
23 import javax.ws.rs.QueryParam;
24 import javax.ws.rs.core.Context;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
27 import javax.ws.rs.core.SecurityContext;
29 import org.codehaus.enunciate.jaxrs.ResponseCode;
30 import org.codehaus.enunciate.jaxrs.StatusCodes;
31 import org.codehaus.enunciate.jaxrs.TypeHint;
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.InternalServerErrorException;
37 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
38 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
39 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
40 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
41 import org.opendaylight.controller.northbound.commons.exception.UnsupportedMediaTypeException;
42 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
43 import org.opendaylight.controller.sal.core.Node;
44 import org.opendaylight.controller.sal.core.NodeConnector;
45 import org.opendaylight.controller.sal.utils.GlobalConstants;
46 import org.opendaylight.controller.sal.utils.ServiceHelper;
47 import org.opendaylight.controller.sal.utils.Status;
48 import org.opendaylight.controller.sal.utils.StatusCode;
49 import org.opendaylight.controller.switchmanager.ISwitchManager;
51 import org.opendaylight.controller.sal.authorization.Privilege;
54 * Host Tracker Northbound REST APIs.<br>
55 * This class provides REST APIs to track host location in a network. Host
56 * Location is represented by Host node connector which is essentially a logical
57 * entity that represents a Switch/Port. A host is represented by it's
58 * IP-address and mac-address.
62 * Authentication scheme : <b>HTTP Basic</b><br>
63 * Authentication realm : <b>opendaylight</b><br>
64 * Transport : <b>HTTP and HTTPS</b><br>
66 * HTTPS Authentication is disabled by default. Administrator can enable it in
67 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
68 * trusted authority.<br>
70 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
75 public class HostTrackerNorthbound {
77 private String username;
80 public void setSecurityContext(SecurityContext context) {
81 username = context.getUserPrincipal().getName();
84 protected String getUserName() {
88 private IfIptoHost getIfIpToHostService(String containerName) {
89 IContainerManager containerManager = (IContainerManager) ServiceHelper
90 .getGlobalInstance(IContainerManager.class, this);
91 if (containerManager == null) {
92 throw new ServiceUnavailableException("Container "
93 + RestMessages.SERVICEUNAVAILABLE.toString());
96 boolean found = false;
97 List<String> containerNames = containerManager.getContainerNames();
98 for (String cName : containerNames) {
99 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
104 if (found == false) {
105 throw new ResourceNotFoundException(containerName + " "
106 + RestMessages.NOCONTAINER.toString());
109 IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(
110 IfIptoHost.class, containerName, this);
112 if (hostTracker == null) {
113 throw new ServiceUnavailableException("Host Tracker "
114 + RestMessages.SERVICEUNAVAILABLE.toString());
121 * Returns a list of all Hosts : both configured via PUT API and dynamically
122 * learnt on the network.
124 * @param containerName
125 * Name of the Container. The Container name for the base
126 * controller is "default".
127 * @return List of Active Hosts.
129 @Path("/{containerName}")
131 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
132 @TypeHint(Hosts.class)
134 @ResponseCode(code = 200, condition = "Operation successful"),
135 @ResponseCode(code = 404, condition = "The containerName is not found"),
136 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
137 public Hosts getActiveHosts(@PathParam("containerName") String containerName) {
139 if (!NorthboundUtils.isAuthorized(
140 getUserName(), containerName, Privilege.READ, this)) {
141 throw new UnauthorizedException(
142 "User is not authorized to perform this operation on container "
145 IfIptoHost hostTracker = getIfIpToHostService(containerName);
146 if (hostTracker == null) {
147 throw new ServiceUnavailableException("Host Tracker "
148 + RestMessages.SERVICEUNAVAILABLE.toString());
151 return new Hosts(hostTracker.getAllHosts());
155 * Returns a list of Hosts that are statically configured and are connected
156 * to a NodeConnector that is down.
158 * @param containerName
159 * Name of the Container. The Container name for the base
160 * controller is "default".
161 * @return List of inactive Hosts.
163 @Path("/{containerName}/inactive")
165 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
166 @TypeHint(Hosts.class)
168 @ResponseCode(code = 200, condition = "Operation successful"),
169 @ResponseCode(code = 404, condition = "The containerName is not found"),
170 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
171 public Hosts getInactiveHosts(
172 @PathParam("containerName") String containerName) {
173 if (!NorthboundUtils.isAuthorized(
174 getUserName(), containerName, Privilege.READ, this)) {
175 throw new UnauthorizedException(
176 "User is not authorized to perform this operation on container "
179 IfIptoHost hostTracker = getIfIpToHostService(containerName);
180 if (hostTracker == null) {
181 throw new ServiceUnavailableException("Host Tracker "
182 + RestMessages.SERVICEUNAVAILABLE.toString());
185 return new Hosts(hostTracker.getInactiveStaticHosts());
189 * Returns a host that matches the IP Address value passed as parameter.
191 * @param containerName
192 * Name of the Container. The Container name for the base
193 * controller is "default".
194 * @param networkAddress
195 * IP Address being looked up
196 * @return host that matches the IP Address
198 @Path("/{containerName}/{networkAddress}")
200 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
201 @TypeHint(HostNodeConnector.class)
203 @ResponseCode(code = 200, condition = "Operation successful"),
204 @ResponseCode(code = 404, condition = "The containerName is not found"),
205 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
206 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
207 public HostNodeConnector getHostDetails(
208 @PathParam("containerName") String containerName,
209 @PathParam("networkAddress") String networkAddress) {
210 if (!NorthboundUtils.isAuthorized(
211 getUserName(), containerName, Privilege.READ, this)) {
212 throw new UnauthorizedException(
213 "User is not authorized to perform this operation on container "
216 IfIptoHost hostTracker = getIfIpToHostService(containerName);
217 if (hostTracker == null) {
218 throw new ServiceUnavailableException("Host Tracker "
219 + RestMessages.SERVICEUNAVAILABLE.toString());
224 ip = InetAddress.getByName(networkAddress);
225 } catch (UnknownHostException e) {
226 throw new UnsupportedMediaTypeException(networkAddress + " "
227 + RestMessages.INVALIDADDRESS.toString());
229 for (HostNodeConnector host : hostTracker.getAllHosts()) {
230 if (host.getNetworkAddress().equals(ip)) {
234 throw new ResourceNotFoundException(RestMessages.NOHOST.toString());
238 * Add a Static Host configuration
240 * @param containerName
241 * Name of the Container. The Container name for the base
242 * controller is "default".
243 * @param networkAddress
245 * @param dataLayerAddress
246 * Host L2 data-layer address.
248 * Node Type as specifid by Node class
250 * Node Identifier as specifid by Node class
251 * @param nodeConnectorType
252 * Port Type as specified by NodeConnector class
253 * @param nodeConnectorId
254 * Port Identifier as specified by NodeConnector class
257 * @return Response as dictated by the HTTP Response Status code
260 @Path("/{containerName}/{networkAddress}")
262 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
264 @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
265 @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
266 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
267 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
268 @ResponseCode(code = 500, condition = "Failed to create Static Host entry. Failure Reason included in HTTP Error response"),
269 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
270 public Response addHost(@PathParam("containerName") String containerName,
271 @PathParam("networkAddress") String networkAddress,
272 @QueryParam("dataLayerAddress") String dataLayerAddress,
273 @QueryParam("nodeType") String nodeType,
274 @QueryParam("nodeId") String nodeId,
275 @QueryParam("nodeConnectorType") String nodeConnectorType,
276 @QueryParam("nodeConnectorId") String nodeConnectorId,
277 @DefaultValue("0") @QueryParam("vlan") String vlan) {
279 if (!NorthboundUtils.isAuthorized(
280 getUserName(), containerName, Privilege.WRITE, this)) {
281 throw new UnauthorizedException(
282 "User is not authorized to perform this operation on container "
285 handleDefaultDisabled(containerName);
287 IfIptoHost hostTracker = getIfIpToHostService(containerName);
288 if (hostTracker == null) {
289 throw new ServiceUnavailableException("Host Tracker "
290 + RestMessages.SERVICEUNAVAILABLE.toString());
293 Node node = handleNodeAvailability(containerName, nodeType, nodeId);
295 throw new InternalServerErrorException(
296 RestMessages.NONODE.toString());
300 InetAddress.getByName(networkAddress);
301 } catch (UnknownHostException e) {
302 throw new UnsupportedMediaTypeException(networkAddress + " "
303 + RestMessages.INVALIDADDRESS.toString());
305 NodeConnector nc = NodeConnector.fromStringNoNode(nodeConnectorType,
306 nodeConnectorId, node);
308 throw new ResourceNotFoundException(nodeConnectorType + "|"
309 + nodeConnectorId + " : " + RestMessages.NONODE.toString());
311 Status status = hostTracker.addStaticHost(networkAddress,
312 dataLayerAddress, nc, vlan);
313 if (status.isSuccess()) {
314 return Response.status(Response.Status.CREATED).build();
315 } else if (status.getCode().equals(StatusCode.BADREQUEST)) {
316 throw new UnsupportedMediaTypeException(status.getDescription());
318 throw new InternalServerErrorException(status.getDescription());
322 * Delete a Static Host configuration
324 * @param containerName
325 * Name of the Container. The Container name for the base
326 * controller is "default".
327 * @param networkAddress
329 * @return Response as dictated by the HTTP Response code.
332 @Path("/{containerName}/{networkAddress}")
334 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
336 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
337 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
338 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
339 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
340 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
341 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
342 public Response deleteFlow(
343 @PathParam(value = "containerName") String containerName,
344 @PathParam(value = "networkAddress") String networkAddress) {
346 if (!NorthboundUtils.isAuthorized(
347 getUserName(), containerName, Privilege.WRITE, this)) {
348 throw new UnauthorizedException(
349 "User is not authorized to perform this operation on container "
352 handleDefaultDisabled(containerName);
353 IfIptoHost hostTracker = getIfIpToHostService(containerName);
354 if (hostTracker == null) {
355 throw new ServiceUnavailableException("Host Tracker "
356 + RestMessages.SERVICEUNAVAILABLE.toString());
360 InetAddress.getByName(networkAddress);
361 } catch (UnknownHostException e) {
362 throw new UnsupportedMediaTypeException(networkAddress + " "
363 + RestMessages.INVALIDADDRESS.toString());
366 Status status = hostTracker.removeStaticHost(networkAddress);
367 if (status.isSuccess()) {
368 return Response.ok().build();
370 throw new InternalServerErrorException(status.getDescription());
374 private void handleDefaultDisabled(String containerName) {
375 IContainerManager containerManager = (IContainerManager) ServiceHelper
376 .getGlobalInstance(IContainerManager.class, this);
377 if (containerManager == null) {
378 throw new InternalServerErrorException(
379 RestMessages.INTERNALERROR.toString());
381 if (containerName.equals(GlobalConstants.DEFAULT.toString())
382 && containerManager.hasNonDefaultContainer()) {
383 throw new ResourceConflictException(
384 RestMessages.DEFAULTDISABLED.toString());
388 private Node handleNodeAvailability(String containerName, String nodeType,
391 Node node = Node.fromString(nodeType, nodeId);
393 throw new ResourceNotFoundException(nodeId + " : "
394 + RestMessages.NONODE.toString());
397 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
398 ISwitchManager.class, containerName, this);
401 throw new ServiceUnavailableException("Switch Manager "
402 + RestMessages.SERVICEUNAVAILABLE.toString());
405 if (!sm.getNodes().contains(node)) {
406 throw new ResourceNotFoundException(node.toString() + " : "
407 + RestMessages.NONODE.toString());