/* * 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.topology.northbound; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; 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.ext.ContextResolver; import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; import org.codehaus.enunciate.jaxrs.TypeHint; import org.opendaylight.controller.northbound.commons.RestMessages; import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException; import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; 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.Edge; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.utils.ServiceHelper; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.topologymanager.ITopologyManager; import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig; /** * Topology Northbound REST API * *
*
* Authentication scheme : HTTP Basic
* Authentication realm : opendaylight
* Transport : HTTP and HTTPS
*
* HTTPS Authentication is disabled by default. */ @Path("/") public class TopologyNorthboundJAXRS { 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; } /** * * Retrieve the Topology * * @param containerName * The container for which we want to retrieve the topology (Eg. * 'default') * * @return A List of EdgeProps each EdgeProp represent an Edge of the graph * with the corresponding properties attached to it. * *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/topology/default
     *
     * Response body in XML:
     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     * <topology>
     *     <edgeProperties>
     *         <edge>
     *             <tailNodeConnector>
     *                 <node>
     *                     <id>00:00:00:00:00:00:00:02</id>
     *                     <type>OF</type>
     *                 </node>
     *                 <id>2</id>
     *                 <type>OF</type>
     *             </tailNodeConnector>
     *             <headNodeConnector>
     *                 <node>
     *                     <id>00:00:00:00:00:00:00:51</id>
     *                     <type>OF</type>
     *                 </node>
     *                 <id>2</id>
     *                 <type>OF</type>
     *             </headNodeConnector>
     *         </edge>
     *         <properties>
     *             <state>
     *                 <value>1</value>
     *             </state>
     *             <config>
     *                 <value>1</value>
     *             </config>
     *             <name>
     *                 <value>C1_2-L2_2</value>
     *             </name>
     *             <timeStamp>
     *                 <value>1377279422032</value>
     *                 <name>creation</name>
     *             </timeStamp>
     *         </properties>
     *     </edgeProperties>
     *     <edgeProperties>
     *         <edge>
     *             <tailNodeConnector>
     *                 <node>
     *                     <id>00:00:00:00:00:00:00:51</id>
     *                     <type>OF</type>
     *                 </node>
     *                 <id>2</id>
     *                 <type>OF</type>
     *             </tailNodeConnector>
     *             <headNodeConnector>
     *                 <node>
     *                     <id>00:00:00:00:00:00:00:02</id>
     *                     <type>OF</type>
     *                 </node>
     *                 <id>2</id>
     *                 <type>OF</type>
     *             </headNodeConnector>
     *         </edge>
     *         <properties>
     *             <state>
     *                 <value>1</value>
     *             </state>
     *             <name>
     *                 <value>L2_2-C1_2</value>
     *             </name>
     *             <config>
     *                 <value>1</value>
     *             </config>
     *             <timeStamp>
     *                 <value>1377279423564</value>
     *                 <name>creation</name>
     *             </timeStamp>
     *         </properties>
     *     </edgeProperties>
     * </topology>
     *
     * Response body in JSON:
     * {
     *    "edgeProperties":[
     *       {
     *          "edge":{
     *             "tailNodeConnector":{
     *                "node":{
     *                   "id":"00:00:00:00:00:00:00:02",
     *                   "type":"OF"
     *                },
     *                "id":"2",
     *                "type":"OF"
     *             },
     *             "headNodeConnector":{
     *                "node":{
     *                   "id":"00:00:00:00:00:00:00:51",
     *                   "type":"OF"
     *                },
     *                "id":"2",
     *                "type":"OF"
     *             }
     *          },
     *          "properties":{
     *             "timeStamp": {
     *                "value": 1379527162648,
     *                "name": "creation",
     *             },
     *             "name": {
     *                "value": "s2-eth3"
     *             },
     *             "state": {
     *                "value": 1
     *             },
     *             "config": {
     *                "value": 1
     *             },
     *             "bandwidth": {
     *                "value": 10000000000
     *             }
     *          }
     *       },
     *       {
     *          "edge":{
     *             "tailNodeConnector":{
     *                "node":{
     *                   "id":"00:00:00:00:00:00:00:51",
     *                   "type":"OF"
     *                },
     *                "id":"2",
     *                "type":"OF"
     *             },
     *             "headNodeConnector":{
     *                "node":{
     *                   "id":"00:00:00:00:00:00:00:02",
     *                   "type":"OF"
     *                },
     *                "id":"2",
     *                "type":"OF"
     *             }
     *           },
     *           "properties":{
     *             "timeStamp": {
     *                "value": 1379527162648,
     *                "name": "creation",
     *             }
     *          }
     *        }
     *     ]
     *  }
     * 
*/ @Path("/{containerName}") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(Topology.class) @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") }) public Topology getTopology(@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); } ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class, containerName, this); if (topologyManager == null) { throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString()); } Map> topo = topologyManager.getEdges(); if (topo == null) { return null; } List res = new ArrayList(); for (Map.Entry> entry : topo.entrySet()) { EdgeProperties el = new EdgeProperties(entry.getKey(), entry.getValue()); res.add(el); } Topology result = new Topology(res); if (queryString != null) { queryContext.createQuery(queryString, Topology.class) .filter(result, EdgeProperties.class); } return result; } /** * Retrieve the user configured links * * @param containerName * The container for which we want to retrieve the user links * (Eg. 'default') * * @return A List of user configured links * *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/topology/default/userLinks
     *
     * Response body in XML:
     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     * <list>
     * <userLinks>
     * <status>Success</status>
     * <name>link1</name>
     * <srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector>
     * <dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector>
     * </userLinks>
     * </list>
     *
     * Response body in JSON:
    * {
     *   "userLinks": [
     *    {
     *      "status": "Success",
     *      "name": "link1",
     *      "srcNodeConnector": "OF|2@OF|00:00:00:00:00:00:00:02",
     *      "dstNodeConnector": "OF|5@OF|00:00:00:00:00:00:00:05"
     *    }
     *  ]
     * }
     *
     * 
*/ @Path("/{containerName}/userLinks") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @TypeHint(TopologyUserLinks.class) @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") }) public TopologyUserLinks getUserLinks(@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); } ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class, containerName, this); if (topologyManager == null) { throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString()); } ConcurrentMap userLinks = topologyManager.getUserLinks(); if ((userLinks == null) || (userLinks.values() == null)) { return null; } TopologyUserLinks result = new TopologyUserLinks( new ArrayList(userLinks.values())); if (queryString != null) { queryContext.createQuery(queryString, TopologyUserLinks.class) .filter(result, TopologyUserLinkConfig.class); } return result; } /** * Add an User Link * * @param containerName * Name of the Container (Eg. 'default') * @param name * Name of the user link * @param TopologyUserLinkConfig * in JSON or XML format * @return Response as dictated by the HTTP Response Status code * *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/topology/default/userLink/link1
     *
     * Request body in XML:
     * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     * <topologyUserLinkConfig>
     * <status>Success</status>
     * <name>link1</name>
     * <srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector>
     * <dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector>
     * </topologyUserLinkConfig>
     *
     * Request body in JSON:
     * {
     *    "status":"Success",
     *    "name":"link1",
     *    "srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02",
     *    "dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"
     * }
     *
     * 
*/ @Path("/{containerName}/userLink/{name}") @PUT @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 201, condition = "User Link added successfully"), @ResponseCode(code = 404, condition = "The Container Name was not found"), @ResponseCode(code = 409, condition = "Failed to add User Link due to Conflicting Name"), @ResponseCode(code = 500, condition = "Failed to add User Link. Failure Reason included in HTTP Error response"), @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") }) public Response addUserLink(@PathParam(value = "containerName") String containerName, @PathParam(value = "name") String name, @TypeHint(TopologyUserLinkConfig.class) TopologyUserLinkConfig userLinkConfig) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) { throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName); } ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class, containerName, this); if (topologyManager == null) { throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString()); } Status status = topologyManager.addUserLink(userLinkConfig); if (status.isSuccess()) { NorthboundUtils .auditlog( "User Link",username,"added", userLinkConfig.getName() + " from " + NorthboundUtils.getPortName( NodeConnector.fromString(userLinkConfig.getSrcNodeConnector()), containerName, this) + " to " + NorthboundUtils.getPortName(NodeConnector.fromString (userLinkConfig.getDstNodeConnector()),containerName, this), containerName); return Response.status(Response.Status.CREATED).build(); } throw new InternalServerErrorException(status.getDescription()); } /** * Delete an User Link * * @param containerName * Name of the Container (Eg. 'default') * @param name * Name of the Link Configuration (Eg. 'config1') * @return Response as dictated by the HTTP Response Status code * *
     *
     * Example:
     *
     * Request URL:
     * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1
     *
     * 
*/ @Path("/{containerName}/userLink/{name}") @DELETE @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @StatusCodes({ @ResponseCode(code = 204, condition = "User link removed successfully"), @ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"), @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") }) public Response deleteUserLink(@PathParam("containerName") String containerName, @PathParam("name") String name) { if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) { throw new UnauthorizedException("User is not authorized to perform this operation on container " + containerName); } ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class, containerName, this); if (topologyManager == null) { throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString()); } Status ret = topologyManager.deleteUserLink(name); if (ret.isSuccess()) { NorthboundUtils.auditlog("User Link", username, "removed", name, containerName); return Response.noContent().build(); } return NorthboundUtils.getResponse(ret); } }