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.
70 public class HostTrackerNorthbound {
72 private String username;
75 public void setSecurityContext(SecurityContext context) {
76 if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
79 protected String getUserName() {
83 private IfIptoHost getIfIpToHostService(String containerName) {
84 IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance(
85 IContainerManager.class, this);
86 if (containerManager == null) {
87 throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString());
90 boolean found = false;
91 List<String> containerNames = containerManager.getContainerNames();
92 for (String cName : containerNames) {
93 if (cName.trim().equalsIgnoreCase(containerName.trim())) {
100 throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString());
103 IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(IfIptoHost.class, containerName, this);
104 if (hostTracker == null) {
105 throw new ServiceUnavailableException("Host Tracker " + RestMessages.SERVICEUNAVAILABLE.toString());
111 private Hosts convertHosts(Set<HostNodeConnector> hostNodeConnectors) {
112 if(hostNodeConnectors == null) {
115 Set<HostConfig> hosts = new HashSet<HostConfig>();
116 for(HostNodeConnector hnc : hostNodeConnectors) {
117 hosts.add(HostConfig.convert(hnc));
119 return new Hosts(hosts);
123 * Returns a list of all Hosts : both configured via PUT API and dynamically
124 * learnt on the network.
126 * @param containerName
127 * Name of the Container. The Container name for the base
128 * controller is "default".
129 * @return List of Active Hosts.
136 * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active
141 *  <hostConfig>
142 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
143 *   <networkAddress>1.1.1.1</networkAddress>
144 *   <nodeType>OF</nodeType>
145 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
146 *   <nodeConnectorType>OF</nodeConnectorType>
147 *   <nodeConnectorId>9</nodeConnectorId>
148 *   <vlan>0</vlan>
149 *   <staticHost>false</staticHost>
150 *  </hostConfig>
151 *  <hostConfig>
152 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
153 *   <networkAddress>2.2.2.2</networkAddress>
154 *   <nodeType>OF</nodeType>
155 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
156 *   <nodeConnectorType>OF</nodeConnectorType>
157 *   <nodeConnectorId>5</nodeConnectorId>
158 *   <vlan>0</vlan>
159 *   <staticHost>false</staticHost>
160 *  </hostConfig>
166 *  "hostConfig":[
168 *    "dataLayerAddress":"00:00:00:00:01:01",
169 *    "nodeType":"OF",
170 *    "nodeId":"00:00:00:00:00:00:00:01",
171 *    "nodeConnectorType":"OF",
172 *    "nodeConnectorId":"9",
173 *    "vlan":"0",
174 *    "staticHost":"false",
175 *    "networkAddress":"1.1.1.1"
178 *    "dataLayerAddress":"00:00:00:00:02:02",
179 *    "nodeType":"OF",
180 *    "nodeId":"00:00:00:00:00:00:00:02",
181 *    "nodeConnectorType":"OF",
182 *    "nodeConnectorId":"5",
183 *    "vlan":"0",
184 *    "staticHost":"false",
185 *    "networkAddress":"2.2.2.2"
191 @Path("/{containerName}/hosts/active")
193 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
194 @TypeHint(Hosts.class)
196 @ResponseCode(code = 200, condition = "Operation successful"),
197 @ResponseCode(code = 404, condition = "The containerName is not found"),
198 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
199 public Hosts getActiveHosts(@PathParam("containerName") String containerName) {
201 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
202 throw new UnauthorizedException("User is not authorized to perform this operation on container "
205 IfIptoHost hostTracker = getIfIpToHostService(containerName);
206 return convertHosts(hostTracker.getAllHosts());
210 * Returns a list of Hosts that are statically configured and are connected
211 * to a NodeConnector that is down.
213 * @param containerName
214 * Name of the Container. The Container name for the base
215 * controller is "default".
216 * @return List of inactive Hosts.
223 * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/inactive
228 *  <hostConfig>
229 *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
230 *   <networkAddress>1.1.1.1</networkAddress>
231 *   <nodeType>OF</nodeType>
232 *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
233 *   <nodeConnectorType>OF</nodeConnectorType>
234 *   <nodeConnectorId>9</nodeConnectorId>
235 *   <vlan>0</vlan>
236 *   <staticHost>false</staticHost>
237 *  </hostConfig>
238 *  <hostConfig>
239 *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
240 *   <networkAddress>2.2.2.2</networkAddress>
241 *   <nodeType>OF</nodeType>
242 *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
243 *   <nodeConnectorType>OF</nodeConnectorType>
244 *   <nodeConnectorId>5</nodeConnectorId>
245 *   <vlan>0</vlan>
246 *   <staticHost>false</staticHost>
247 *  </hostConfig>
253 *  "hostConfig":[
255 *    "dataLayerAddress":"00:00:00:00:01:01",
256 *    "nodeType":"OF",
257 *    "nodeId":"00:00:00:00:00:00:00:01",
258 *    "nodeConnectorType":"OF",
259 *    "nodeConnectorId":"9",
260 *    "vlan":"0",
261 *    "staticHost":"false",
262 *    "networkAddress":"1.1.1.1"
265 *    "dataLayerAddress":"00:00:00:00:02:02",
266 *    "nodeType":"OF",
267 *    "nodeId":"00:00:00:00:00:00:00:02",
268 *    "nodeConnectorType":"OF",
269 *    "nodeConnectorId":"5",
270 *    "vlan":"0",
271 *    "staticHost":"false",
272 *    "networkAddress":"2.2.2.2"
278 @Path("/{containerName}/hosts/inactive")
280 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
281 @TypeHint(Hosts.class)
283 @ResponseCode(code = 200, condition = "Operation successful"),
284 @ResponseCode(code = 404, condition = "The containerName is not found"),
285 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
286 public Hosts getInactiveHosts(
287 @PathParam("containerName") String containerName) {
288 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
289 throw new UnauthorizedException("User is not authorized to perform this operation on container "
292 IfIptoHost hostTracker = getIfIpToHostService(containerName);
293 return convertHosts(hostTracker.getInactiveStaticHosts());
297 * Returns a host that matches the IP Address value passed as parameter.
299 * @param containerName
300 * Name of the Container. The Container name for the base
301 * controller is "default".
302 * @param networkAddress
303 * IP Address being looked up
304 * @return host that matches the IP Address
311 * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
316 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
317 *  <networkAddress>1.1.1.1</networkAddress>
318 *  <nodeType>OF</nodeType>
319 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
320 *  <nodeConnectorType>OF</nodeConnectorType>
321 *  <nodeConnectorId>9</nodeConnectorId>
322 *  <vlan>0</vlan>
323 *  <staticHost>false</staticHost>
324 * </hostConfig>
329 *  "dataLayerAddress":"00:00:00:00:01:01",
330 *  "nodeType":"OF",
331 *  "nodeId":"00:00:00:00:00:00:00:01",
332 *  "nodeConnectorType":"OF",
333 *  "nodeConnectorId":"9",
335 *  "staticHost":"false",
336 *  "networkAddress":"1.1.1.1"
340 @Path("/{containerName}/address/{networkAddress}")
342 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
343 @TypeHint(HostConfig.class)
345 @ResponseCode(code = 200, condition = "Operation successful"),
346 @ResponseCode(code = 400, condition = "Invalid IP specified in networkAddress parameter"),
347 @ResponseCode(code = 404, condition = "The containerName is not found"),
348 @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
349 public HostConfig getHostDetails(
350 @PathParam("containerName") String containerName,
351 @PathParam("networkAddress") String networkAddress) {
352 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
353 throw new UnauthorizedException("User is not authorized to perform this operation on container "
356 IfIptoHost hostTracker = getIfIpToHostService(containerName);
360 ip = InetAddress.getByName(networkAddress);
361 } catch (UnknownHostException e) {
362 throw new BadRequestException(RestMessages.INVALIDADDRESS.toString() + " " + networkAddress);
364 for (HostNodeConnector host : hostTracker.getAllHosts()) {
365 if (host.getNetworkAddress().equals(ip)) {
366 return HostConfig.convert(host);
369 throw new ResourceNotFoundException(RestMessages.NOHOST.toString());
373 * Add a Static Host configuration. If a host by the given address already
374 * exists, this method will respond with a non-successful status response.
376 * @param containerName
377 * Name of the Container. The Container name for the base
378 * controller is "default".
379 * @param networkAddress
382 * Host Config Details
383 * @return Response as dictated by the HTTP Response Status code
391 * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
396 *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
397 *  <networkAddress>1.1.1.1</networkAddress>
398 *  <nodeType>OF</nodeType>
399 *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
400 *  <nodeConnectorType>OF</nodeConnectorType>
401 *  <nodeConnectorId>9</nodeConnectorId>
402 *  <vlan>0</vlan>
403 *  <staticHost>false</staticHost>
404 * </hostConfig>
409 *  "dataLayerAddress":"00:00:00:00:01:01",
410 *  "nodeType":"OF",
411 *  "nodeId":"00:00:00:00:00:00:00:01",
412 *  "nodeConnectorType":"OF",
413 *  "nodeConnectorId":"9",
415 *  "staticHost":"false",
416 *  "networkAddress":"1.1.1.1"
421 @Path("/{containerName}/address/{networkAddress}")
423 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
425 @ResponseCode(code = 201, condition = "Static host created successfully"),
426 @ResponseCode(code = 400, condition = "Invalid parameters specified, see response body for details"),
427 @ResponseCode(code = 404, condition = "The container or resource is not found"),
428 @ResponseCode(code = 409, condition = "Resource conflict, see response body for details"),
429 @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
430 public Response addHost(@Context UriInfo uriInfo, @PathParam("containerName") String containerName,
431 @PathParam("networkAddress") String networkAddress,
432 @TypeHint(HostConfig.class) HostConfig hostConfig) {
434 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
435 return Response.status(Response.Status.UNAUTHORIZED)
436 .entity("User is not authorized to perform this operation on container " + containerName)
439 handleDefaultDisabled(containerName);
441 IfIptoHost hostTracker = getIfIpToHostService(containerName);
443 HostConfig hc = hostConfig;
444 if (!networkAddress.equals(hc.getNetworkAddress())) {
445 return Response.status(Response.Status.CONFLICT)
446 .entity("Resource name in config object doesn't match URI")
449 if (!hc.isStaticHost()) {
450 return Response.status(Response.Status.BAD_REQUEST)
451 .entity("Can only add static host.")
454 Node node = handleNodeAvailability(containerName, hc.getNodeType(), hc.getNodeId());
455 NodeConnector nc = NodeConnector.fromStringNoNode(hc.getNodeConnectorType(), hc.getNodeConnectorId(), node);
457 Status status = hostTracker.addStaticHost(networkAddress, hc.getDataLayerAddress(), nc, hc.getVlan());
458 if (status.isSuccess()) {
459 NorthboundUtils.auditlog("Static Host", username, "added", networkAddress, containerName);
460 return Response.created(uriInfo.getRequestUri()).build();
463 return NorthboundUtils.getResponse(status);
467 * Delete a Static Host configuration
469 * @param containerName
470 * Name of the Container. The Container name for the base
471 * controller is "default".
472 * @param networkAddress
474 * @return Response as dictated by the HTTP Response code.
477 @Path("/{containerName}/address/{networkAddress}")
479 @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
481 @ResponseCode(code = 204, condition = "Static host deleted successfully"),
482 @ResponseCode(code = 404, condition = "The container or a specified resource was not found"),
483 @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
484 @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
485 public Response deleteHost(
486 @PathParam(value = "containerName") String containerName,
487 @PathParam(value = "networkAddress") String networkAddress) {
489 if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
490 return Response.status(Response.Status.UNAUTHORIZED)
491 .entity("User is not authorized to perform this operation on container " + containerName)
494 handleDefaultDisabled(containerName);
495 IfIptoHost hostTracker = getIfIpToHostService(containerName);
497 Status status = hostTracker.removeStaticHost(networkAddress);
498 if (status.isSuccess()) {
499 NorthboundUtils.auditlog("Static Host", username, "removed", networkAddress, containerName);
500 return Response.noContent().build();
502 return NorthboundUtils.getResponse(status);
506 private void handleDefaultDisabled(String containerName) {
507 IContainerManager containerManager = (IContainerManager) ServiceHelper
508 .getGlobalInstance(IContainerManager.class, this);
509 if (containerManager == null) {
510 throw new ServiceUnavailableException(
511 RestMessages.SERVICEUNAVAILABLE.toString());
513 if (containerName.equals(GlobalConstants.DEFAULT.toString())
514 && containerManager.hasNonDefaultContainer()) {
515 throw new ResourceConflictException(
516 RestMessages.DEFAULTDISABLED.toString());
520 private Node handleNodeAvailability(String containerName, String nodeType, String nodeId) {
522 Node node = Node.fromString(nodeType, nodeId);
524 throw new ResourceNotFoundException(nodeId + " : "
525 + RestMessages.NONODE.toString());
528 ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
529 ISwitchManager.class, containerName, this);
532 throw new ServiceUnavailableException("Switch Manager "
533 + RestMessages.SERVICEUNAVAILABLE.toString());
536 if (!sm.getNodes().contains(node)) {
537 throw new ResourceNotFoundException(node.toString() + " : "
538 + RestMessages.NONODE.toString());