427aa1c1deaaf98c86fa3ecd072757bc6331892c
[controller.git] / opendaylight / northbound / topology / src / main / java / org / opendaylight / controller / topology / northbound / TopologyNorthboundJAXRS.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.topology.northbound;
10
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.concurrent.ConcurrentMap;
16
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
29 import org.codehaus.enunciate.jaxrs.ResponseCode;
30 import org.codehaus.enunciate.jaxrs.StatusCodes;
31 import org.codehaus.enunciate.jaxrs.TypeHint;
32 import org.opendaylight.controller.northbound.commons.RestMessages;
33 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
34 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
35 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
36 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
37 import org.opendaylight.controller.sal.authorization.Privilege;
38 import org.opendaylight.controller.sal.core.Edge;
39 import org.opendaylight.controller.sal.core.NodeConnector;
40 import org.opendaylight.controller.sal.core.Property;
41 import org.opendaylight.controller.sal.utils.ServiceHelper;
42 import org.opendaylight.controller.sal.utils.Status;
43 import org.opendaylight.controller.topologymanager.ITopologyManager;
44 import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
45
46 /**
47  * Topology Northbound REST API
48  *
49  * <br>
50  * <br>
51  * Authentication scheme : <b>HTTP Basic</b><br>
52  * Authentication realm : <b>opendaylight</b><br>
53  * Transport : <b>HTTP and HTTPS</b><br>
54  * <br>
55  * HTTPS Authentication is disabled by default.
56  */
57
58 @Path("/")
59 public class TopologyNorthboundJAXRS {
60
61     private String username;
62
63     @Context
64     public void setSecurityContext(SecurityContext context) {
65         if (context != null && context.getUserPrincipal() != null) {
66             username = context.getUserPrincipal().getName();
67         }
68     }
69
70     protected String getUserName() {
71         return username;
72     }
73
74     /**
75      *
76      * Retrieve the Topology
77      *
78      * @param containerName
79      *            The container for which we want to retrieve the topology (Eg.
80      *            'default')
81      *
82      * @return A List of EdgeProps each EdgeProp represent an Edge of the graph
83      *         with the corresponding properties attached to it.
84      *
85      *         <pre>
86      *
87      * Example:
88      *
89      * Request URL:
90      * http://localhost:8080/controller/nb/v2/topology/default
91      *
92      * Response body in XML:
93      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
94      * &lt;topology&gt;
95      *     &lt;edgeProperties&gt;
96      *         &lt;edge&gt;
97      *             &lt;tailNodeConnector&gt;
98      *                 &lt;node&gt;
99      *                     &lt;id&gt;00:00:00:00:00:00:00:02&lt;/id&gt;
100      *                     &lt;type&gt;OF&lt;/type&gt;
101      *                 &lt;/node&gt;
102      *                 &lt;id&gt;2&lt;/id&gt;
103      *                 &lt;type&gt;OF&lt;/type&gt;
104      *             &lt;/tailNodeConnector&gt;
105      *             &lt;headNodeConnector&gt;
106      *                 &lt;node&gt;
107      *                     &lt;id&gt;00:00:00:00:00:00:00:51&lt;/id&gt;
108      *                     &lt;type&gt;OF&lt;/type&gt;
109      *                 &lt;/node&gt;
110      *                 &lt;id&gt;2&lt;/id&gt;
111      *                 &lt;type&gt;OF&lt;/type&gt;
112      *             &lt;/headNodeConnector&gt;
113      *         &lt;/edge&gt;
114      *         &lt;properties&gt;
115      *             &lt;state&gt;
116      *                 &lt;value&gt;1&lt;/value&gt;
117      *             &lt;/state&gt;
118      *             &lt;config&gt;
119      *                 &lt;value&gt;1&lt;/value&gt;
120      *             &lt;/config&gt;
121      *             &lt;name&gt;
122      *                 &lt;value&gt;C1_2-L2_2&lt;/value&gt;
123      *             &lt;/name&gt;
124      *             &lt;timeStamp&gt;
125      *                 &lt;value&gt;1377279422032&lt;/value&gt;
126      *                 &lt;name&gt;creation&lt;/name&gt;
127      *             &lt;/timeStamp&gt;
128      *         &lt;/properties&gt;
129      *     &lt;/edgeProperties&gt;
130      *     &lt;edgeProperties&gt;
131      *         &lt;edge&gt;
132      *             &lt;tailNodeConnector&gt;
133      *                 &lt;node&gt;
134      *                     &lt;id&gt;00:00:00:00:00:00:00:51&lt;/id&gt;
135      *                     &lt;type&gt;OF&lt;/type&gt;
136      *                 &lt;/node&gt;
137      *                 &lt;id&gt;2&lt;/id&gt;
138      *                 &lt;type&gt;OF&lt;/type&gt;
139      *             &lt;/tailNodeConnector&gt;
140      *             &lt;headNodeConnector&gt;
141      *                 &lt;node&gt;
142      *                     &lt;id&gt;00:00:00:00:00:00:00:02&lt;/id&gt;
143      *                     &lt;type&gt;OF&lt;/type&gt;
144      *                 &lt;/node&gt;
145      *                 &lt;id&gt;2&lt;/id&gt;
146      *                 &lt;type&gt;OF&lt;/type&gt;
147      *             &lt;/headNodeConnector&gt;
148      *         &lt;/edge&gt;
149      *         &lt;properties&gt;
150      *             &lt;state&gt;
151      *                 &lt;value&gt;1&lt;/value&gt;
152      *             &lt;/state&gt;
153      *             &lt;name&gt;
154      *                 &lt;value&gt;L2_2-C1_2&lt;/value&gt;
155      *             &lt;/name&gt;
156      *             &lt;config&gt;
157      *                 &lt;value&gt;1&lt;/value&gt;
158      *             &lt;/config&gt;
159      *             &lt;timeStamp&gt;
160      *                 &lt;value&gt;1377279423564&lt;/value&gt;
161      *                 &lt;name&gt;creation&lt;/name&gt;
162      *             &lt;/timeStamp&gt;
163      *         &lt;/properties&gt;
164      *     &lt;/edgeProperties&gt;
165      * &lt;/topology&gt;
166      *
167      * Response body in JSON:
168      * {
169      *    "edgeProperties":[
170      *       {
171      *          "edge":{
172      *             "tailNodeConnector":{
173      *                "node":{
174      *                   "id":"00:00:00:00:00:00:00:02",
175      *                   "type":"OF"
176      *                },
177      *                "id":"2",
178      *                "type":"OF"
179      *             },
180      *             "headNodeConnector":{
181      *                "node":{
182      *                   "id":"00:00:00:00:00:00:00:51",
183      *                   "type":"OF"
184      *                },
185      *                "id":"2",
186      *                "type":"OF"
187      *             }
188      *          },
189      *          "properties":{
190      *             "timeStamp": {
191      *                "value": 1379527162648,
192      *                "name": "creation",
193      *             },
194      *             "name": {
195      *                "value": "s2-eth3"
196      *             },
197      *             "state": {
198      *                "value": 1
199      *             },
200      *             "config": {
201      *                "value": 1
202      *             },
203      *             "bandwidth": {
204      *                "value": 10000000000
205      *             }
206      *          }
207      *       },
208      *       {
209      *          "edge":{
210      *             "tailNodeConnector":{
211      *                "node":{
212      *                   "id":"00:00:00:00:00:00:00:51",
213      *                   "type":"OF"
214      *                },
215      *                "id":"2",
216      *                "type":"OF"
217      *             },
218      *             "headNodeConnector":{
219      *                "node":{
220      *                   "id":"00:00:00:00:00:00:00:02",
221      *                   "type":"OF"
222      *                },
223      *                "id":"2",
224      *                "type":"OF"
225      *             }
226      *           },
227      *           "properties":{
228      *             "timeStamp": {
229      *                "value": 1379527162648,
230      *                "name": "creation",
231      *             }
232      *          }
233      *        }
234      *     ]
235      *  }
236      * </pre>
237      */
238     @Path("/{containerName}")
239     @GET
240     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
241     @TypeHint(Topology.class)
242     @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
243     public Topology getTopology(@PathParam("containerName") String containerName) {
244
245         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
246             throw new UnauthorizedException("User is not authorized to perform this operation on container "
247                     + containerName);
248         }
249         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class,
250                 containerName, this);
251         if (topologyManager == null) {
252             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
253         }
254
255         Map<Edge, Set<Property>> topo = topologyManager.getEdges();
256         if (topo != null) {
257             List<EdgeProperties> res = new ArrayList<EdgeProperties>();
258             for (Map.Entry<Edge, Set<Property>> entry : topo.entrySet()) {
259                 EdgeProperties el = new EdgeProperties(entry.getKey(), entry.getValue());
260                 res.add(el);
261             }
262             return new Topology(res);
263         }
264
265         return null;
266     }
267
268     /**
269      * Retrieve the user configured links
270      *
271      * @param containerName
272      *            The container for which we want to retrieve the user links
273      *            (Eg. 'default')
274      *
275      * @return A List of user configured links
276      *
277      *         <pre>
278      *
279      * Example:
280      *
281      * Request URL:
282      * http://localhost:8080/controller/nb/v2/topology/default/userLinks
283      *
284      * Response body in XML:
285      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
286      * &lt;list&gt;
287      * &lt;userLinks&gt;
288      * &lt;status&gt;Success&lt;/status&gt;
289      * &lt;name&gt;link1&lt;/name&gt;
290      * &lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;
291      * &lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;
292      * &lt;/userLinks&gt;
293      * &lt;/list&gt;
294      *
295      * Response body in JSON:
296     * {
297      *   "userLinks": [
298      *    {
299      *      "status": "Success",
300      *      "name": "link1",
301      *      "srcNodeConnector": "OF|2@OF|00:00:00:00:00:00:00:02",
302      *      "dstNodeConnector": "OF|5@OF|00:00:00:00:00:00:00:05"
303      *    }
304      *  ]
305      * }
306      *
307      * </pre>
308      */
309     @Path("/{containerName}/userLinks")
310     @GET
311     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
312     @TypeHint(TopologyUserLinks.class)
313     @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
314     public TopologyUserLinks getUserLinks(@PathParam("containerName") String containerName) {
315
316         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
317             throw new UnauthorizedException("User is not authorized to perform this operation on container "
318                     + containerName);
319         }
320         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class,
321                 containerName, this);
322         if (topologyManager == null) {
323             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
324         }
325
326         ConcurrentMap<String, TopologyUserLinkConfig> userLinks = topologyManager.getUserLinks();
327         if ((userLinks != null) && (userLinks.values() != null)) {
328             List<TopologyUserLinkConfig> res = new ArrayList<TopologyUserLinkConfig>(userLinks.values());
329             return new TopologyUserLinks(res);
330         }
331
332         return null;
333     }
334
335     /**
336      * Add an User Link
337      *
338      * @param containerName
339      *            Name of the Container (Eg. 'default')
340      * @param name
341      *            Name of the user link
342      * @param TopologyUserLinkConfig
343      *            in JSON or XML format
344      * @return Response as dictated by the HTTP Response Status code
345      *
346      *         <pre>
347      *
348      * Example:
349      *
350      * Request URL:
351      * http://localhost:8080/controller/nb/v2/topology/default/userLink/link1
352      *
353      * Request body in XML:
354      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
355      * &lt;topologyUserLinkConfig&gt;
356      * &lt;status&gt;Success&lt;/status&gt;
357      * &lt;name&gt;link1&lt;/name&gt;
358      * &lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;
359      * &lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;
360      * &lt;/topologyUserLinkConfig&gt;
361      *
362      * Request body in JSON:
363      * {
364      *    "status":"Success",
365      *    "name":"link1",
366      *    "srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02",
367      *    "dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"
368      * }
369      *
370      * </pre>
371      */
372     @Path("/{containerName}/userLink/{name}")
373     @PUT
374     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
375     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
376     @StatusCodes({
377         @ResponseCode(code = 201, condition = "User Link added successfully"),
378         @ResponseCode(code = 404, condition = "The Container Name was not found"),
379         @ResponseCode(code = 409, condition = "Failed to add User Link due to Conflicting Name"),
380         @ResponseCode(code = 500, condition = "Failed to add User Link. Failure Reason included in HTTP Error response"),
381         @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
382     public Response addUserLink(@PathParam(value = "containerName") String containerName,
383             @PathParam(value = "name") String name,
384             @TypeHint(TopologyUserLinkConfig.class) TopologyUserLinkConfig userLinkConfig) {
385
386         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
387             throw new UnauthorizedException("User is not authorized to perform this operation on container "
388                     + containerName);
389         }
390         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class,
391                 containerName, this);
392         if (topologyManager == null) {
393             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
394         }
395
396         Status status = topologyManager.addUserLink(userLinkConfig);
397         if (status.isSuccess()) {
398             NorthboundUtils
399             .auditlog(
400                     "User Link",username,"added", userLinkConfig.getName() + " from " + NorthboundUtils.getPortName(
401                             NodeConnector.fromString(userLinkConfig.getSrcNodeConnector()),
402                             containerName, this) + " to "
403                             + NorthboundUtils.getPortName(NodeConnector.fromString
404                                     (userLinkConfig.getDstNodeConnector()),containerName, this), containerName);
405             return Response.status(Response.Status.CREATED).build();
406         }
407         throw new InternalServerErrorException(status.getDescription());
408     }
409
410     /**
411      * Delete an User Link
412      *
413      * @param containerName
414      *            Name of the Container (Eg. 'default')
415      * @param name
416      *            Name of the Link Configuration (Eg. 'config1')
417      * @return Response as dictated by the HTTP Response Status code
418      *
419      *         <pre>
420      *
421      * Example:
422      *
423      * Request URL:
424      * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1
425      *
426      * </pre>
427      */
428     @Path("/{containerName}/userLink/{name}")
429     @DELETE
430     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
431     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
432     @StatusCodes({ @ResponseCode(code = 204, condition = "User link removed successfully"),
433         @ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"),
434         @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
435     public Response deleteUserLink(@PathParam("containerName") String containerName, @PathParam("name") String name) {
436
437         if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.WRITE, this)) {
438             throw new UnauthorizedException("User is not authorized to perform this operation on container "
439                     + containerName);
440         }
441         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper.getInstance(ITopologyManager.class,
442                 containerName, this);
443         if (topologyManager == null) {
444             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString());
445         }
446
447         Status ret = topologyManager.deleteUserLink(name);
448         if (ret.isSuccess()) {
449             NorthboundUtils.auditlog("User Link", username, "removed", name, containerName);
450             return Response.noContent().build();
451         }
452         return NorthboundUtils.getResponse(ret);
453     }
454 }