/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.hosttracker.northbound; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.ContextResolver; import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; import org.codehaus.enunciate.jaxrs.TypeHint; import org.opendaylight.controller.containermanager.IContainerManager; import org.opendaylight.controller.hosttracker.IfIptoHost; import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; import org.opendaylight.controller.northbound.commons.RestMessages; import org.opendaylight.controller.northbound.commons.exception.BadRequestException; import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException; import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException; import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException; import org.opendaylight.controller.northbound.commons.query.QueryContext; import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils; import org.opendaylight.controller.sal.authorization.Privilege; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.utils.GlobalConstants; import org.opendaylight.controller.sal.utils.ServiceHelper; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.switchmanager.ISwitchManager; /** * Host Tracker Northbound REST APIs.
* This class provides REST APIs to track host location in a network. Host * Location is represented by Host node connector which is essentially a logical * entity that represents a Switch/Port. A host is represented by it's * IP-address and mac-address. * *
*
* Authentication scheme : HTTP Basic
* Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
* HTTPS Authentication is disabled by default. * */ @Path("/") public class HostTrackerNorthbound { private String username; private QueryContext queryContext; @Context public void setQueryContext(ContextResolver queryCtxResolver) { if (queryCtxResolver != null) { queryContext = queryCtxResolver.getContext(QueryContext.class); } } @Context public void setSecurityContext(SecurityContext context) { if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName(); } protected String getUserName() { return username; } private IfIptoHost getIfIpToHostService(String containerName) { IContainerManager containerManager = (IContainerManager) ServiceHelper.getGlobalInstance( IContainerManager.class, this); if (containerManager == null) { throw new ServiceUnavailableException("Container " + RestMessages.SERVICEUNAVAILABLE.toString()); } boolean found = false; List containerNames = containerManager.getContainerNames(); for (String cName : containerNames) { if (cName.trim().equalsIgnoreCase(containerName.trim())) { found = true; break; } } if (!found) { throw new ResourceNotFoundException(containerName + " " + RestMessages.NOCONTAINER.toString()); } IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(IfIptoHost.class, containerName, this); if (hostTracker == null) { throw new ServiceUnavailableException("Host Tracker " + RestMessages.SERVICEUNAVAILABLE.toString()); } return hostTracker; } private Set convertHosts(Set hostNodeConnectors) { if(hostNodeConnectors == null) { return null; } Set hosts = new HashSet(); for(HostNodeConnector hnc : hostNodeConnectors) { hosts.add(HostConfig.convert(hnc)); } return hosts; } /** * Returns a list of all Hosts : both configured via PUT API and dynamically * learnt on the network. * * @param containerName * Name of the Container. The Container name for the base * controller is "default". * @return List of Active Hosts. *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active
     *
     * Response body in XML
     *
     * <list>
     *  <hostConfig>
     *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
     *   <networkAddress>1.1.1.1</networkAddress>
     *   <nodeType>OF</nodeType>
     *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
     *   <nodeConnectorType>OF</nodeConnectorType>
     *   <nodeConnectorId>9</nodeConnectorId>
     *   <vlan>0</vlan>
     *   <staticHost>false</staticHost>
     *  </hostConfig>
     *  <hostConfig>
     *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
     *   <networkAddress>2.2.2.2</networkAddress>
     *   <nodeType>OF</nodeType>
     *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
     *   <nodeConnectorType>OF</nodeConnectorType>
     *   <nodeConnectorId>5</nodeConnectorId>
     *   <vlan>0</vlan>
     *   <staticHost>false</staticHost>
     *  </hostConfig>
     * </list>
     *
     * Response body in JSON:
     *
     * {
     *  "hostConfig":[
     *   {
     *    "dataLayerAddress":"00:00:00:00:01:01",
     *    "nodeType":"OF",
     *    "nodeId":"00:00:00:00:00:00:00:01",
     *    "nodeConnectorType":"OF",
     *    "nodeConnectorId":"9",
     *    "vlan":"0",
     *    "staticHost":"false",
     *    "networkAddress":"1.1.1.1"
     *   },
     *   {
     *    "dataLayerAddress":"00:00:00:00:02:02",
     *    "nodeType":"OF",
     *    "nodeId":"00:00:00:00:00:00:00:02",
     *    "nodeConnectorType":"OF",
     *    "nodeConnectorId":"5",
     *    "vlan":"0",
     *    "staticHost":"false",
     *    "networkAddress":"2.2.2.2"
     *   }
     *  ]
     * }
     * 
*/ @Path("/{containerName}/hosts/active") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Hosts.class) @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"), @ResponseCode(code = 404, condition = "The containerName is not found"), @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") }) public Hosts getActiveHosts(@PathParam("containerName") String containerName, @QueryParam("_q") String queryString) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) { throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName); } IfIptoHost hostTracker = getIfIpToHostService(containerName); Hosts hosts = new Hosts(convertHosts(hostTracker.getAllHosts())); if (queryString != null) { queryContext.createQuery(queryString, Hosts.class) .filter(hosts, HostConfig.class); } return hosts; } /** * Returns a list of Hosts that are statically configured and are connected * to a NodeConnector that is down. * * @param containerName * Name of the Container. The Container name for the base * controller is "default". * @return List of inactive Hosts. *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/inactive
     *
     * Response body in XML
     *
     * <list>
     *  <hostConfig>
     *   <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
     *   <networkAddress>1.1.1.1</networkAddress>
     *   <nodeType>OF</nodeType>
     *   <nodeId>00:00:00:00:00:00:00:01</nodeId>
     *   <nodeConnectorType>OF</nodeConnectorType>
     *   <nodeConnectorId>9</nodeConnectorId>
     *   <vlan>0</vlan>
     *   <staticHost>false</staticHost>
     *  </hostConfig>
     *  <hostConfig>
     *   <dataLayerAddress>00:00:00:00:02:02</dataLayerAddress>
     *   <networkAddress>2.2.2.2</networkAddress>
     *   <nodeType>OF</nodeType>
     *   <nodeId>00:00:00:00:00:00:00:02</nodeId>
     *   <nodeConnectorType>OF</nodeConnectorType>
     *   <nodeConnectorId>5</nodeConnectorId>
     *   <vlan>0</vlan>
     *   <staticHost>false</staticHost>
     *  </hostConfig>
     * </list>
     *
     * Response body in JSON:
     *
     * {
     *  "hostConfig":[
     *   {
     *    "dataLayerAddress":"00:00:00:00:01:01",
     *    "nodeType":"OF",
     *    "nodeId":"00:00:00:00:00:00:00:01",
     *    "nodeConnectorType":"OF",
     *    "nodeConnectorId":"9",
     *    "vlan":"0",
     *    "staticHost":"false",
     *    "networkAddress":"1.1.1.1"
     *   },
     *   {
     *    "dataLayerAddress":"00:00:00:00:02:02",
     *    "nodeType":"OF",
     *    "nodeId":"00:00:00:00:00:00:00:02",
     *    "nodeConnectorType":"OF",
     *    "nodeConnectorId":"5",
     *    "vlan":"0",
     *    "staticHost":"false",
     *    "networkAddress":"2.2.2.2"
     *   }
     *  ]
     * }
     * 
*/ @Path("/{containerName}/hosts/inactive") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Hosts.class) @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"), @ResponseCode(code = 404, condition = "The containerName is not found"), @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") }) public Hosts getInactiveHosts( @PathParam("containerName") String containerName, @QueryParam("_q") String queryString) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) { throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName); } IfIptoHost hostTracker = getIfIpToHostService(containerName); Hosts hosts = new Hosts(convertHosts(hostTracker.getInactiveStaticHosts())); if (queryString != null) { queryContext.createQuery(queryString, Hosts.class) .filter(hosts, HostConfig.class); } return hosts; } /** * Returns a host that matches the IP Address value passed as parameter. * * @param containerName * Name of the Container. The Container name for the base * controller is "default". * @param networkAddress * IP Address being looked up * @return host that matches the IP Address *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
     *
     * Response body in XML
     *
     * <hostConfig>
     *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
     *  <networkAddress>1.1.1.1</networkAddress>
     *  <nodeType>OF</nodeType>
     *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
     *  <nodeConnectorType>OF</nodeConnectorType>
     *  <nodeConnectorId>9</nodeConnectorId>
     *  <vlan>0</vlan>
     *  <staticHost>false</staticHost>
     * </hostConfig>
     *
     * Response body in JSON:
     *
     * {
     *  "dataLayerAddress":"00:00:00:00:01:01",
     *  "nodeType":"OF",
     *  "nodeId":"00:00:00:00:00:00:00:01",
     *  "nodeConnectorType":"OF",
     *  "nodeConnectorId":"9",
     *  "vlan":"0",
     *  "staticHost":"false",
     *  "networkAddress":"1.1.1.1"
     * }
     * 
*/ @Path("/{containerName}/address/{networkAddress}") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(HostConfig.class) @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"), @ResponseCode(code = 400, condition = "Invalid IP specified in networkAddress parameter"), @ResponseCode(code = 404, condition = "The containerName is not found"), @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") }) public HostConfig getHostDetails( @PathParam("containerName") String containerName, @PathParam("networkAddress") String networkAddress) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) { throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName); } IfIptoHost hostTracker = getIfIpToHostService(containerName); InetAddress ip; try { ip = InetAddress.getByName(networkAddress); } catch (UnknownHostException e) { throw new BadRequestException(RestMessages.INVALIDADDRESS.toString() + " " + networkAddress); } for (HostNodeConnector host : hostTracker.getAllHosts()) { if (host.getNetworkAddress().equals(ip)) { return HostConfig.convert(host); } } throw new ResourceNotFoundException(RestMessages.NOHOST.toString()); } /** * Add a Static Host configuration. If a host by the given address already * exists, this method will respond with a non-successful status response. * * @param containerName * Name of the Container. The Container name for the base * controller is "default". * @param networkAddress * Host IP Address * @param hostConfig * Host Config Details * @return Response as dictated by the HTTP Response Status code * *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1
     *
     * Request body in XML
     *
     * <hostConfig>
     *  <dataLayerAddress>00:00:00:00:01:01</dataLayerAddress>
     *  <networkAddress>1.1.1.1</networkAddress>
     *  <nodeType>OF</nodeType>
     *  <nodeId>00:00:00:00:00:00:00:01</nodeId>
     *  <nodeConnectorType>OF</nodeConnectorType>
     *  <nodeConnectorId>9</nodeConnectorId>
     *  <vlan>1</vlan>
     *  <staticHost>true</staticHost>
     * </hostConfig>
     *
     * Request body in JSON:
     *
     * {
     *  "dataLayerAddress":"00:00:00:00:01:01",
     *  "nodeType":"OF",
     *  "nodeId":"00:00:00:00:00:00:00:01",
     *  "nodeConnectorType":"OF",
     *  "nodeConnectorId":"9",
     *  "vlan":"1",
     *  "staticHost":"true",
     *  "networkAddress":"1.1.1.1"
     * }
     * 
*/ @Path("/{containerName}/address/{networkAddress}") @PUT @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 201, condition = "Static host created successfully"), @ResponseCode(code = 400, condition = "Invalid parameters specified, see response body for details"), @ResponseCode(code = 404, condition = "The container or resource is not found"), @ResponseCode(code = 409, condition = "Resource conflict, see response body for details"), @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") }) public Response addHost(@Context UriInfo uriInfo, @PathParam("containerName") String containerName, @PathParam("networkAddress") String networkAddress, @TypeHint(HostConfig.class) HostConfig hostConfig) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) { return Response.status(Response.Status.UNAUTHORIZED) .entity("User is not authorized to perform this operation on container " + containerName) .build(); } handleDefaultDisabled(containerName); IfIptoHost hostTracker = getIfIpToHostService(containerName); HostConfig hc = hostConfig; if (!networkAddress.equals(hc.getNetworkAddress())) { return Response.status(Response.Status.CONFLICT) .entity("Resource name in config object doesn't match URI") .build(); } if (!hc.isStaticHost()) { return Response.status(Response.Status.BAD_REQUEST) .entity("Can only add static host.") .build(); } Node node = handleNodeAvailability(containerName, hc.getNodeType(), hc.getNodeId()); NodeConnector nc = NodeConnector.fromStringNoNode(hc.getNodeConnectorType(), hc.getNodeConnectorId(), node); Status status = hostTracker.addStaticHost(networkAddress, hc.getDataLayerAddress(), nc, hc.getVlan()); if (status.isSuccess()) { NorthboundUtils.auditlog("Static Host", username, "added", networkAddress, containerName); return Response.created(uriInfo.getRequestUri()).build(); } return NorthboundUtils.getResponse(status); } /** * Delete a Static Host configuration * * @param containerName * Name of the Container. The Container name for the base * controller is "default". * @param networkAddress * IP Address * @return Response as dictated by the HTTP Response code. * * Example: * * Request URL: * http://localhost:8080/controller/nb/v2/hosttracker/default/address/1.1.1.1 * */ @Path("/{containerName}/address/{networkAddress}") @DELETE @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 204, condition = "Static host deleted successfully"), @ResponseCode(code = 404, condition = "The container or a specified resource was not found"), @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"), @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") }) public Response deleteHost( @PathParam(value = "containerName") String containerName, @PathParam(value = "networkAddress") String networkAddress) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) { return Response.status(Response.Status.UNAUTHORIZED) .entity("User is not authorized to perform this operation on container " + containerName) .build(); } handleDefaultDisabled(containerName); IfIptoHost hostTracker = getIfIpToHostService(containerName); Status status = hostTracker.removeStaticHost(networkAddress); if (status.isSuccess()) { NorthboundUtils.auditlog("Static Host", username, "removed", networkAddress, containerName); return Response.noContent().build(); } return NorthboundUtils.getResponse(status); } private void handleDefaultDisabled(String containerName) { IContainerManager containerManager = (IContainerManager) ServiceHelper .getGlobalInstance(IContainerManager.class, this); if (containerManager == null) { throw new ServiceUnavailableException( RestMessages.SERVICEUNAVAILABLE.toString()); } if (containerName.equals(GlobalConstants.DEFAULT.toString()) && containerManager.hasNonDefaultContainer()) { throw new ResourceConflictException( RestMessages.DEFAULTDISABLED.toString()); } } private Node handleNodeAvailability(String containerName, String nodeType, String nodeId) { Node node = Node.fromString(nodeType, nodeId); if (node == null) { throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString()); } ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance( ISwitchManager.class, containerName, this); if (sm == null) { throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString()); } if (!sm.getNodes().contains(node)) { throw new ResourceNotFoundException(node.toString() + " : " + RestMessages.NONODE.toString()); } return node; } }