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.util.HashSet;
14 import java.util.List;
17 import javax.ws.rs.Consumes;
18 import javax.ws.rs.DELETE;
19 import javax.ws.rs.GET;
20 import javax.ws.rs.PUT;
21 import javax.ws.rs.Path;
22 import javax.ws.rs.PathParam;
23 import javax.ws.rs.Produces;
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;
28 import javax.ws.rs.core.UriInfo;
29 import javax.xml.bind.JAXBElement;
31 import org.codehaus.enunciate.jaxrs.ResponseCode;
32 import org.codehaus.enunciate.jaxrs.StatusCodes;
33 import org.codehaus.enunciate.jaxrs.TypeHint;
34 import org.opendaylight.controller.containermanager.IContainerManager;
35 import org.opendaylight.controller.hosttracker.IfIptoHost;
36 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
37 import org.opendaylight.controller.northbound.commons.RestMessages;
38 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
39 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
40 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
41 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
42 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
43 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
44 import org.opendaylight.controller.sal.authorization.Privilege;
45 import org.opendaylight.controller.sal.core.Node;
46 import org.opendaylight.controller.sal.core.NodeConnector;
47 import org.opendaylight.controller.sal.utils.GlobalConstants;
48 import org.opendaylight.controller.sal.utils.ServiceHelper;
49 import org.opendaylight.controller.sal.utils.Status;
50 import org.opendaylight.controller.switchmanager.ISwitchManager;
53 * Host Tracker Northbound REST APIs.<br>
54 * This class provides REST APIs to track host location in a network. Host
55 * Location is represented by Host node connector which is essentially a logical
56 * entity that represents a Switch/Port. A host is represented by it's
57 * IP-address and mac-address.
61 * Authentication scheme : <b>HTTP Basic</b><br>
62 * Authentication realm : <b>opendaylight</b><br>
63 * Transport : <b>HTTP and HTTPS</b><br>
65 * HTTPS Authentication is disabled by default. Administrator can enable it in
66 * tomcat-server.xml after adding a proper keystore / SSL certificate from a
67 * trusted authority.<br>
69 * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
74 public class HostTrackerNorthbound {
76 private String username;
79 public void setSecurityContext(SecurityContext context) {
80 if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
83 protected String getUserName() {
87 private IfIptoHost getIfIpToHostService(String containerName) {
88 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
89 IContainerManager.class, this);
90 if (containerManager == null) {
91 throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString());
94 boolean found = false;
95 List<String> containerNames = containerManager.getContainerNames();
96 for (String cName : containerNames) {
97 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
104 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
107 IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(IfIptoHost.class, containerName, this);
108 if (hostTracker == null) {
109 throw new ServiceUnavailableException("Host Tracker " + RestMessages.SERVICEUNAVAILABLE.toString());
115 private Hosts convertHosts(Set<HostNodeConnector> hostNodeConnectors) {
116 if(hostNodeConnectors == null) {
119 Set<HostConfig> hosts = new HashSet<HostConfig>();
120 for(HostNodeConnector hnc : hostNodeConnectors) {
121 hosts.add(HostConfig.convert(hnc));
123 return new Hosts(hosts);
127 * Returns a list of all Hosts : both configured via PUT API and dynamically
128 * learnt on the network.
130 * @param containerName
131 * Name of the Container. The Container name for the base
132 * controller is "default".
133 * @return List of Active Hosts.
140 * http://localhost:8080/controller/nb/v2/host/default
145 *  <hostConfig>
146 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
147 *   <networkAddress>1.1.1.1</networkAddress>
148 *   <nodeType>OF</nodeType>
149 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
150 *   <nodeConnectorType>OF</nodeConnectorType>
151 *   <nodeConnectorId>9</nodeConnectorId>
152 *   <vlan>0</vlan>
153 *   <staticHost>false</staticHost>
154 *  </hostConfig>
155 *  <hostConfig>
156 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
157 *   <networkAddress>2.2.2.2</networkAddress>
158 *   <nodeType>OF</nodeType>
159 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
160 *   <nodeConnectorType>OF</nodeConnectorType>
161 *   <nodeConnectorId>5</nodeConnectorId>
162 *   <vlan>0</vlan>
163 *   <staticHost>false</staticHost>
164 *  </hostConfig>
170 *  "hostConfig":[
172 *    "dataLayerAddress":"00:00:00:00:01:01",
173 *    "nodeType":"OF",
174 *    "nodeId":"00:00:00:00:00:00:00:01",
175 *    "nodeConnectorType":"OF",
176 *    "nodeConnectorId":"9",
177 *    "vlan":"0",
178 *    "staticHost":"false",
179 *    "networkAddress":"1.1.1.1"
182 *    "dataLayerAddress":"00:00:00:00:02:02",
183 *    "nodeType":"OF",
184 *    "nodeId":"00:00:00:00:00:00:00:02",
185 *    "nodeConnectorType":"OF",
186 *    "nodeConnectorId":"5",
187 *    "vlan":"0",
188 *    "staticHost":"false",
189 *    "networkAddress":"2.2.2.2"
195 @Path("/{containerName}")
197 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
198 @TypeHint(Hosts.class)
200 @ResponseCode(code = 200, condition = "Operation successful"),
201 @ResponseCode(code = 404, condition = "The containerName is not found"),
202 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
203 public Hosts getActiveHosts(@PathParam("containerName") String containerName) {
205 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
206 throw new UnauthorizedException("User is not authorized to perform this operation on container "
209 IfIptoHost hostTracker = getIfIpToHostService(containerName);
210 return convertHosts(hostTracker.getAllHosts());
214 * Returns a list of Hosts that are statically configured and are connected
215 * to a NodeConnector that is down.
217 * @param containerName
218 * Name of the Container. The Container name for the base
219 * controller is "default".
220 * @return List of inactive Hosts.
227 * http://localhost:8080/controller/nb/v2/host/default/inactive
232 *  <hostConfig>
233 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
234 *   <networkAddress>1.1.1.1</networkAddress>
235 *   <nodeType>OF</nodeType>
236 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
237 *   <nodeConnectorType>OF</nodeConnectorType>
238 *   <nodeConnectorId>9</nodeConnectorId>
239 *   <vlan>0</vlan>
240 *   <staticHost>false</staticHost>
241 *  </hostConfig>
242 *  <hostConfig>
243 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
244 *   <networkAddress>2.2.2.2</networkAddress>
245 *   <nodeType>OF</nodeType>
246 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
247 *   <nodeConnectorType>OF</nodeConnectorType>
248 *   <nodeConnectorId>5</nodeConnectorId>
249 *   <vlan>0</vlan>
250 *   <staticHost>false</staticHost>
251 *  </hostConfig>
257 *  "hostConfig":[
259 *    "dataLayerAddress":"00:00:00:00:01:01",
260 *    "nodeType":"OF",
261 *    "nodeId":"00:00:00:00:00:00:00:01",
262 *    "nodeConnectorType":"OF",
263 *    "nodeConnectorId":"9",
264 *    "vlan":"0",
265 *    "staticHost":"false",
266 *    "networkAddress":"1.1.1.1"
269 *    "dataLayerAddress":"00:00:00:00:02:02",
270 *    "nodeType":"OF",
271 *    "nodeId":"00:00:00:00:00:00:00:02",
272 *    "nodeConnectorType":"OF",
273 *    "nodeConnectorId":"5",
274 *    "vlan":"0",
275 *    "staticHost":"false",
276 *    "networkAddress":"2.2.2.2"
282 @Path("/{containerName}/inactive")
284 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
285 @TypeHint(Hosts.class)
287 @ResponseCode(code = 200, condition = "Operation successful"),
288 @ResponseCode(code = 404, condition = "The containerName is not found"),
289 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
290 public Hosts getInactiveHosts(
291 @PathParam("containerName") String containerName) {
292 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
293 throw new UnauthorizedException("User is not authorized to perform this operation on container "
296 IfIptoHost hostTracker = getIfIpToHostService(containerName);
297 return convertHosts(hostTracker.getInactiveStaticHosts());
301 * Returns a host that matches the IP Address value passed as parameter.
303 * @param containerName
304 * Name of the Container. The Container name for the base
305 * controller is "default".
306 * @param networkAddress
307 * IP Address being looked up
308 * @return host that matches the IP Address
315 * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
320 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
321 *  <networkAddress>1.1.1.1</networkAddress>
322 *  <nodeType>OF</nodeType>
323 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
324 *  <nodeConnectorType>OF</nodeConnectorType>
325 *  <nodeConnectorId>9</nodeConnectorId>
326 *  <vlan>0</vlan>
327 *  <staticHost>false</staticHost>
328 * </hostConfig>
333 *  "dataLayerAddress":"00:00:00:00:01:01",
334 *  "nodeType":"OF",
335 *  "nodeId":"00:00:00:00:00:00:00:01",
336 *  "nodeConnectorType":"OF",
337 *  "nodeConnectorId":"9",
339 *  "staticHost":"false",
340 *  "networkAddress":"1.1.1.1"
344 @Path("/{containerName}/{networkAddress}")
346 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
347 @TypeHint(HostConfig.class)
349 @ResponseCode(code = 200, condition = "Operation successful"),
350 @ResponseCode(code = 400, condition = "Invalid IP specified in networkAddress parameter"),
351 @ResponseCode(code = 404, condition = "The containerName is not found"),
352 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
353 public HostConfig getHostDetails(
354 @PathParam("containerName") String containerName,
355 @PathParam("networkAddress") String networkAddress) {
356 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
357 throw new UnauthorizedException("User is not authorized to perform this operation on container "
360 IfIptoHost hostTracker = getIfIpToHostService(containerName);
364 ip = InetAddress.getByName(networkAddress);
365 } catch (UnknownHostException e) {
366 throw new BadRequestException(RestMessages.INVALIDADDRESS.toString() + " " + networkAddress);
368 for (HostNodeConnector host : hostTracker.getAllHosts()) {
369 if (host.getNetworkAddress().equals(ip)) {
370 return HostConfig.convert(host);
373 throw new ResourceNotFoundException(RestMessages.NOHOST.toString());
377 * Add a Static Host configuration
379 * @param containerName
380 * Name of the Container. The Container name for the base
381 * controller is "default".
382 * @param networkAddress
385 * Host Config Details
386 * @return Response as dictated by the HTTP Response Status code
394 * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
399 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
400 *  <networkAddress>1.1.1.1</networkAddress>
401 *  <nodeType>OF</nodeType>
402 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
403 *  <nodeConnectorType>OF</nodeConnectorType>
404 *  <nodeConnectorId>9</nodeConnectorId>
405 *  <vlan>0</vlan>
406 *  <staticHost>false</staticHost>
407 * </hostConfig>
412 *  "dataLayerAddress":"00:00:00:00:01:01",
413 *  "nodeType":"OF",
414 *  "nodeId":"00:00:00:00:00:00:00:01",
415 *  "nodeConnectorType":"OF",
416 *  "nodeConnectorId":"9",
418 *  "staticHost":"false",
419 *  "networkAddress":"1.1.1.1"
424 @Path("/{containerName}/{networkAddress}")
426 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
428 @ResponseCode(code = 201, condition = "Static host created successfully"),
429 @ResponseCode(code = 400, condition = "Invalid parameters specified, see response body for details"),
430 @ResponseCode(code = 404, condition = "The container or resource is not found"),
431 @ResponseCode(code = 409, condition = "Resource conflict, see response body for details"),
432 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
433 public Response addHost(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
434 @PathParam("networkAddress") String networkAddress,
435 @TypeHint(HostConfig.class) HostConfig hostConfig) {
437 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
438 return Response.status(Response.Status.UNAUTHORIZED)
439 .entity("User is not authorized to perform this operation on container " + containerName)
442 handleDefaultDisabled(containerName);
444 IfIptoHost hostTracker = getIfIpToHostService(containerName);
446 HostConfig hc = hostConfig;
447 if (!networkAddress.equals(hc.getNetworkAddress())) {
448 return Response.status(Response.Status.CONFLICT)
449 .entity("Resource name in config object doesn't match URI")
452 if (!hc.isStaticHost()) {
453 return Response.status(Response.Status.BAD_REQUEST)
454 .entity("Can only add static host.")
457 Node node = handleNodeAvailability(containerName, hc.getNodeType(), hc.getNodeId());
458 NodeConnector nc = NodeConnector.fromStringNoNode(hc.getNodeConnectorType(), hc.getNodeConnectorId(), node);
460 Status status = hostTracker.addStaticHost(networkAddress, hc.getDataLayerAddress(), nc, hc.getVlan());
461 if (status.isSuccess()) {
462 NorthboundUtils.auditlog("Static Host", username, "added", networkAddress, containerName);
463 return Response.created(uriInfo.getRequestUri()).build();
466 return NorthboundUtils.getResponse(status);
470 * Delete a Static Host configuration
472 * @param containerName
473 * Name of the Container. The Container name for the base
474 * controller is "default".
475 * @param networkAddress
477 * @return Response as dictated by the HTTP Response code.
480 @Path("/{containerName}/{networkAddress}")
482 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
484 @ResponseCode(code = 204, condition = "Static host deleted successfully"),
485 @ResponseCode(code = 404, condition = "The container or a specified resource was not found"),
486 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
487 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
488 public Response deleteHost(
489 @PathParam(value = "containerName") String containerName,
490 @PathParam(value = "networkAddress") String networkAddress) {
492 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
493 return Response.status(Response.Status.UNAUTHORIZED)
494 .entity("User is not authorized to perform this operation on container " + containerName)
497 handleDefaultDisabled(containerName);
498 IfIptoHost hostTracker = getIfIpToHostService(containerName);
500 Status status = hostTracker.removeStaticHost(networkAddress);
501 if (status.isSuccess()) {
502 NorthboundUtils.auditlog("Static Host", username, "removed", networkAddress, containerName);
503 return Response.noContent().build();
505 return NorthboundUtils.getResponse(status);
509 private void handleDefaultDisabled(String containerName) {
510 IContainerManager containerManager = (IContainerManager) ServiceHelper
511 .getGlobalInstance(IContainerManager.class, this);
512 if (containerManager == null) {
513 throw new ServiceUnavailableException(
514 RestMessages.SERVICEUNAVAILABLE.toString());
516 if (containerName.equals(GlobalConstants.DEFAULT.toString())
517 && containerManager.hasNonDefaultContainer()) {
518 throw new ResourceConflictException(
519 RestMessages.DEFAULTDISABLED.toString());
523 private Node handleNodeAvailability(String containerName, String nodeType, String nodeId) {
525 Node node = Node.fromString(nodeType, nodeId);
527 throw new ResourceNotFoundException(nodeId + " : "
528 + RestMessages.NONODE.toString());
531 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
532 ISwitchManager.class, containerName, this);
535 throw new ServiceUnavailableException("Switch Manager "
536 + RestMessages.SERVICEUNAVAILABLE.toString());
539 if (!sm.getNodes().contains(node)) {
540 throw new ResourceNotFoundException(node.toString() + " : "
541 + RestMessages.NONODE.toString());