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.xml.bind.JAXBElement;
30 import org.codehaus.enunciate.jaxrs.ResponseCode;
31 import org.codehaus.enunciate.jaxrs.StatusCodes;
32 import org.codehaus.enunciate.jaxrs.TypeHint;
33 import org.opendaylight.controller.containermanager.IContainerManager;
34 import org.opendaylight.controller.hosttracker.IfIptoHost;
35 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
36 import org.opendaylight.controller.northbound.commons.RestMessages;
37 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
38 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
39 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
40 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
41 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
42 import org.opendaylight.controller.northbound.commons.exception.UnsupportedMediaTypeException;
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.sal.utils.StatusCode;
51 import org.opendaylight.controller.switchmanager.ISwitchManager;
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 if (context != null && context.getUserPrincipal() != null) 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());
120 private Hosts convertHosts(Set<HostNodeConnector> hostNodeConnectors) {
121 if(hostNodeConnectors == null) {
124 Set<HostConfig> hosts = new HashSet<HostConfig>();
125 for(HostNodeConnector hnc : hostNodeConnectors) {
126 hosts.add(HostConfig.convert(hnc));
128 return new Hosts(hosts);
132 * Returns a list of all Hosts : both configured via PUT API and dynamically
133 * learnt on the network.
135 * @param containerName
136 * Name of the Container. The Container name for the base
137 * controller is "default".
138 * @return List of Active Hosts.
145 * http://localhost:8080/controller/nb/v2/host/default
150 *  <hostConfig>
151 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
152 *   <networkAddress>1.1.1.1</networkAddress>
153 *   <nodeType>OF</nodeType>
154 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
155 *   <nodeConnectorType>OF</nodeConnectorType>
156 *   <nodeConnectorId>9</nodeConnectorId>
157 *   <vlan>0</vlan>
158 *   <staticHost>false</staticHost>
159 *  </hostConfig>
160 *  <hostConfig>
161 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
162 *   <networkAddress>2.2.2.2</networkAddress>
163 *   <nodeType>OF</nodeType>
164 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
165 *   <nodeConnectorType>OF</nodeConnectorType>
166 *   <nodeConnectorId>5</nodeConnectorId>
167 *   <vlan>0</vlan>
168 *   <staticHost>false</staticHost>
169 *  </hostConfig>
175 *  "hostConfig":[
177 *    "dataLayerAddress":"00:00:00:00:01:01",
178 *    "nodeType":"OF",
179 *    "nodeId":"00:00:00:00:00:00:00:01",
180 *    "nodeConnectorType":"OF",
181 *    "nodeConnectorId":"9",
182 *    "vlan":"0",
183 *    "staticHost":"false",
184 *    "networkAddress":"1.1.1.1"
187 *    "dataLayerAddress":"00:00:00:00:02:02",
188 *    "nodeType":"OF",
189 *    "nodeId":"00:00:00:00:00:00:00:02",
190 *    "nodeConnectorType":"OF",
191 *    "nodeConnectorId":"5",
192 *    "vlan":"0",
193 *    "staticHost":"false",
194 *    "networkAddress":"2.2.2.2"
200 @Path("/{containerName}")
202 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
203 @TypeHint(Hosts.class)
205 @ResponseCode(code = 200, condition = "Operation successful"),
206 @ResponseCode(code = 404, condition = "The containerName is not found"),
207 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
208 public Hosts getActiveHosts(@PathParam("containerName") String containerName) {
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());
221 return convertHosts(hostTracker.getAllHosts());
225 * Returns a list of Hosts that are statically configured and are connected
226 * to a NodeConnector that is down.
228 * @param containerName
229 * Name of the Container. The Container name for the base
230 * controller is "default".
231 * @return List of inactive Hosts.
238 * http://localhost:8080/controller/nb/v2/host/default/inactive
243 *  <hostConfig>
244 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
245 *   <networkAddress>1.1.1.1</networkAddress>
246 *   <nodeType>OF</nodeType>
247 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
248 *   <nodeConnectorType>OF</nodeConnectorType>
249 *   <nodeConnectorId>9</nodeConnectorId>
250 *   <vlan>0</vlan>
251 *   <staticHost>false</staticHost>
252 *  </hostConfig>
253 *  <hostConfig>
254 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
255 *   <networkAddress>2.2.2.2</networkAddress>
256 *   <nodeType>OF</nodeType>
257 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
258 *   <nodeConnectorType>OF</nodeConnectorType>
259 *   <nodeConnectorId>5</nodeConnectorId>
260 *   <vlan>0</vlan>
261 *   <staticHost>false</staticHost>
262 *  </hostConfig>
268 *  "hostConfig":[
270 *    "dataLayerAddress":"00:00:00:00:01:01",
271 *    "nodeType":"OF",
272 *    "nodeId":"00:00:00:00:00:00:00:01",
273 *    "nodeConnectorType":"OF",
274 *    "nodeConnectorId":"9",
275 *    "vlan":"0",
276 *    "staticHost":"false",
277 *    "networkAddress":"1.1.1.1"
280 *    "dataLayerAddress":"00:00:00:00:02:02",
281 *    "nodeType":"OF",
282 *    "nodeId":"00:00:00:00:00:00:00:02",
283 *    "nodeConnectorType":"OF",
284 *    "nodeConnectorId":"5",
285 *    "vlan":"0",
286 *    "staticHost":"false",
287 *    "networkAddress":"2.2.2.2"
293 @Path("/{containerName}/inactive")
295 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
296 @TypeHint(Hosts.class)
298 @ResponseCode(code = 200, condition = "Operation successful"),
299 @ResponseCode(code = 404, condition = "The containerName is not found"),
300 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
301 public Hosts getInactiveHosts(
302 @PathParam("containerName") String containerName) {
303 if (!NorthboundUtils.isAuthorized(
304 getUserName(), containerName, Privilege.READ, this)) {
305 throw new UnauthorizedException(
306 "User is not authorized to perform this operation on container "
309 IfIptoHost hostTracker = getIfIpToHostService(containerName);
310 if (hostTracker == null) {
311 throw new ServiceUnavailableException("Host Tracker "
312 + RestMessages.SERVICEUNAVAILABLE.toString());
314 return convertHosts(hostTracker.getInactiveStaticHosts());
318 * Returns a host that matches the IP Address value passed as parameter.
320 * @param containerName
321 * Name of the Container. The Container name for the base
322 * controller is "default".
323 * @param networkAddress
324 * IP Address being looked up
325 * @return host that matches the IP Address
332 * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
337 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
338 *  <networkAddress>1.1.1.1</networkAddress>
339 *  <nodeType>OF</nodeType>
340 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
341 *  <nodeConnectorType>OF</nodeConnectorType>
342 *  <nodeConnectorId>9</nodeConnectorId>
343 *  <vlan>0</vlan>
344 *  <staticHost>false</staticHost>
345 * </hostConfig>
350 *  "dataLayerAddress":"00:00:00:00:01:01",
351 *  "nodeType":"OF",
352 *  "nodeId":"00:00:00:00:00:00:00:01",
353 *  "nodeConnectorType":"OF",
354 *  "nodeConnectorId":"9",
356 *  "staticHost":"false",
357 *  "networkAddress":"1.1.1.1"
361 @Path("/{containerName}/{networkAddress}")
363 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
364 @TypeHint(HostConfig.class)
366 @ResponseCode(code = 200, condition = "Operation successful"),
367 @ResponseCode(code = 404, condition = "The containerName is not found"),
368 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
369 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
370 public HostConfig getHostDetails(
371 @PathParam("containerName") String containerName,
372 @PathParam("networkAddress") String networkAddress) {
373 if (!NorthboundUtils.isAuthorized(
374 getUserName(), containerName, Privilege.READ, this)) {
375 throw new UnauthorizedException(
376 "User is not authorized to perform this operation on container "
379 IfIptoHost hostTracker = getIfIpToHostService(containerName);
380 if (hostTracker == null) {
381 throw new ServiceUnavailableException("Host Tracker "
382 + RestMessages.SERVICEUNAVAILABLE.toString());
387 ip = InetAddress.getByName(networkAddress);
388 } catch (UnknownHostException e) {
389 throw new UnsupportedMediaTypeException(networkAddress + " "
390 + RestMessages.INVALIDADDRESS.toString());
392 for (HostNodeConnector host : hostTracker.getAllHosts()) {
393 if (host.getNetworkAddress().equals(ip)) {
394 return HostConfig.convert(host);
397 throw new ResourceNotFoundException(RestMessages.NOHOST.toString());
401 * Add a Static Host configuration
403 * @param containerName
404 * Name of the Container. The Container name for the base
405 * controller is "default".
406 * @param networkAddress
409 * Host Config Details
410 * @return Response as dictated by the HTTP Response Status code
418 * http://localhost:8080/controller/nb/v2/host/default/1.1.1.1
423 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
424 *  <networkAddress>1.1.1.1</networkAddress>
425 *  <nodeType>OF</nodeType>
426 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
427 *  <nodeConnectorType>OF</nodeConnectorType>
428 *  <nodeConnectorId>9</nodeConnectorId>
429 *  <vlan>0</vlan>
430 *  <staticHost>false</staticHost>
431 * </hostConfig>
436 *  "dataLayerAddress":"00:00:00:00:01:01",
437 *  "nodeType":"OF",
438 *  "nodeId":"00:00:00:00:00:00:00:01",
439 *  "nodeConnectorType":"OF",
440 *  "nodeConnectorId":"9",
442 *  "staticHost":"false",
443 *  "networkAddress":"1.1.1.1"
448 @Path("/{containerName}/{networkAddress}")
450 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
452 @ResponseCode(code = 201, condition = "Static host created successfully"),
453 @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
454 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
455 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
456 @ResponseCode(code = 500, condition = "Failed to create Static Host entry. Failure Reason included in HTTP Error response"),
457 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
458 public Response addHost(@PathParam("containerName") String containerName,
459 @PathParam("networkAddress") String networkAddress,
460 @TypeHint(HostConfig.class) JAXBElement<HostConfig> hostConfig) {
462 if (!NorthboundUtils.isAuthorized(
463 getUserName(), containerName, Privilege.WRITE, this)) {
464 throw new UnauthorizedException(
465 "User is not authorized to perform this operation on container "
468 handleDefaultDisabled(containerName);
470 IfIptoHost hostTracker = getIfIpToHostService(containerName);
471 if (hostTracker == null) {
472 throw new ServiceUnavailableException("Host Tracker "
473 + RestMessages.SERVICEUNAVAILABLE.toString());
476 HostConfig hc = hostConfig.getValue();
477 Node node = handleNodeAvailability(containerName, hc.getNodeType(), hc.getNodeId());
479 throw new InternalServerErrorException(
480 RestMessages.NONODE.toString());
484 InetAddress.getByName(networkAddress);
485 } catch (UnknownHostException e) {
486 throw new UnsupportedMediaTypeException(networkAddress + " "
487 + RestMessages.INVALIDADDRESS.toString());
489 if(!networkAddress.equals(hc.getNetworkAddress())) {
490 throw new UnsupportedMediaTypeException(networkAddress + " is not the same as "
491 + hc.getNetworkAddress());
493 if(!hc.isStaticHost()) {
494 throw new UnsupportedMediaTypeException("StaticHost flag must be true");
496 NodeConnector nc = NodeConnector.fromStringNoNode(hc.getNodeConnectorType(), hc.getNodeConnectorId(), node);
498 throw new ResourceNotFoundException(hc.getNodeConnectorType() + "|"
499 + hc.getNodeConnectorId() + " : " + RestMessages.NONODE.toString());
501 Status status = hostTracker.addStaticHost(networkAddress,
502 hc.getDataLayerAddress(), nc, hc.getVlan());
503 if (status.isSuccess()) {
504 NorthboundUtils.auditlog("Static Host", username, "added", networkAddress, containerName);
505 return Response.status(Response.Status.CREATED).build();
506 } else if (status.getCode().equals(StatusCode.BADREQUEST)) {
507 throw new UnsupportedMediaTypeException(status.getDescription());
509 throw new InternalServerErrorException(status.getDescription());
513 * Delete a Static Host configuration
515 * @param containerName
516 * Name of the Container. The Container name for the base
517 * controller is "default".
518 * @param networkAddress
520 * @return Response as dictated by the HTTP Response code.
523 @Path("/{containerName}/{networkAddress}")
525 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
527 @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
528 @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
529 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
530 @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
531 @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
532 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
533 public Response deleteFlow(
534 @PathParam(value = "containerName") String containerName,
535 @PathParam(value = "networkAddress") String networkAddress) {
537 if (!NorthboundUtils.isAuthorized(
538 getUserName(), containerName, Privilege.WRITE, this)) {
539 throw new UnauthorizedException(
540 "User is not authorized to perform this operation on container "
543 handleDefaultDisabled(containerName);
544 IfIptoHost hostTracker = getIfIpToHostService(containerName);
545 if (hostTracker == null) {
546 throw new ServiceUnavailableException("Host Tracker "
547 + RestMessages.SERVICEUNAVAILABLE.toString());
551 InetAddress.getByName(networkAddress);
552 } catch (UnknownHostException e) {
553 throw new UnsupportedMediaTypeException(networkAddress + " "
554 + RestMessages.INVALIDADDRESS.toString());
557 Status status = hostTracker.removeStaticHost(networkAddress);
558 if (status.isSuccess()) {
559 NorthboundUtils.auditlog("Static Host", username, "removed", networkAddress, containerName);
560 return Response.ok().build();
562 throw new InternalServerErrorException(status.getDescription());
566 private void handleDefaultDisabled(String containerName) {
567 IContainerManager containerManager = (IContainerManager) ServiceHelper
568 .getGlobalInstance(IContainerManager.class, this);
569 if (containerManager == null) {
570 throw new InternalServerErrorException(
571 RestMessages.INTERNALERROR.toString());
573 if (containerName.equals(GlobalConstants.DEFAULT.toString())
574 && containerManager.hasNonDefaultContainer()) {
575 throw new ResourceConflictException(
576 RestMessages.DEFAULTDISABLED.toString());
580 private Node handleNodeAvailability(String containerName, String nodeType,
583 Node node = Node.fromString(nodeType, nodeId);
585 throw new ResourceNotFoundException(nodeId + " : "
586 + RestMessages.NONODE.toString());
589 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
590 ISwitchManager.class, containerName, this);
593 throw new ServiceUnavailableException("Switch Manager "
594 + RestMessages.SERVICEUNAVAILABLE.toString());
597 if (!sm.getNodes().contains(node)) {
598 throw new ResourceNotFoundException(node.toString() + " : "
599 + RestMessages.NONODE.toString());