Bug #65 - Fix inconsistencies in the NB REST APIs
[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 import javax.xml.bind.JAXBElement;
29
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.northbound.commons.RestMessages;
34 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
36 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
37 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
38 import org.opendaylight.controller.sal.authorization.Privilege;
39 import org.opendaylight.controller.sal.core.Edge;
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) username = context.getUserPrincipal().getName();
66     }
67
68     protected String getUserName() {
69         return username;
70     }
71
72     /**
73      *
74      * Retrieve the Topology
75      *
76      * @param containerName
77      *            The container for which we want to retrieve the topology (Eg.
78      *            'default')
79      *
80      * @return A List of EdgeProps each EdgeProp represent an Edge of the graph
81      *         with the corresponding properties attached to it.
82      *
83      *         <pre>
84      *
85      * Example:
86      *
87      * RequestURL:
88      * http://localhost:8080/controller/nb/v2/topology/default
89      *
90      * Response in XML:
91      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
92      * &lt;topology&gt;
93      *     &lt;edgeProperties&gt;
94      *         &lt;edge&gt;
95      *             &lt;tailNodeConnector&gt;
96      *                 &lt;node&gt;
97      *                     &lt;id&gt;00:00:00:00:00:00:00:02&lt;/id&gt;
98      *                     &lt;type&gt;OF&lt;/type&gt;
99      *                 &lt;/node&gt;
100      *                 &lt;id&gt;2&lt;/id&gt;
101      *                 &lt;type&gt;OF&lt;/type&gt;
102      *             &lt;/tailNodeConnector&gt;
103      *             &lt;headNodeConnector&gt;
104      *                 &lt;node&gt;
105      *                     &lt;id&gt;00:00:00:00:00:00:00:51&lt;/id&gt;
106      *                     &lt;type&gt;OF&lt;/type&gt;
107      *                 &lt;/node&gt;
108      *                 &lt;id&gt;2&lt;/id&gt;
109      *                 &lt;type&gt;OF&lt;/type&gt;
110      *             &lt;/headNodeConnector&gt;
111      *         &lt;/edge&gt;
112      *         &lt;properties&gt;
113      *             &lt;state&gt;
114      *                 &lt;value&gt;1&lt;/value&gt;
115      *             &lt;/state&gt;
116      *             &lt;config&gt;
117      *                 &lt;value&gt;1&lt;/value&gt;
118      *             &lt;/config&gt;
119      *             &lt;name&gt;
120      *                 &lt;value&gt;C1_2-L2_2&lt;/value&gt;
121      *             &lt;/name&gt;
122      *             &lt;timeStamp&gt;
123      *                 &lt;value&gt;1377279422032&lt;/value&gt;
124      *                 &lt;name&gt;creation&lt;/name&gt;
125      *             &lt;/timeStamp&gt;
126      *         &lt;/properties&gt;
127      *     &lt;/edgeProperties&gt;
128      *     &lt;edgeProperties&gt;
129      *         &lt;edge&gt;
130      *             &lt;tailNodeConnector&gt;
131      *                 &lt;node&gt;
132      *                     &lt;id&gt;00:00:00:00:00:00:00:51&lt;/id&gt;
133      *                     &lt;type&gt;OF&lt;/type&gt;
134      *                 &lt;/node&gt;
135      *                 &lt;id&gt;2&lt;/id&gt;
136      *                 &lt;type&gt;OF&lt;/type&gt;
137      *             &lt;/tailNodeConnector&gt;
138      *             &lt;headNodeConnector&gt;
139      *                 &lt;node&gt;
140      *                     &lt;id&gt;00:00:00:00:00:00:00:02&lt;/id&gt;
141      *                     &lt;type&gt;OF&lt;/type&gt;
142      *                 &lt;/node&gt;
143      *                 &lt;id&gt;2&lt;/id&gt;
144      *                 &lt;type&gt;OF&lt;/type&gt;
145      *             &lt;/headNodeConnector&gt;
146      *         &lt;/edge&gt;
147      *         &lt;properties&gt;
148      *             &lt;state&gt;
149      *                 &lt;value&gt;1&lt;/value&gt;
150      *             &lt;/state&gt;
151      *             &lt;name&gt;
152      *                 &lt;value&gt;L2_2-C1_2&lt;/value&gt;
153      *             &lt;/name&gt;
154      *             &lt;config&gt;
155      *                 &lt;value&gt;1&lt;/value&gt;
156      *             &lt;/config&gt;
157      *             &lt;timeStamp&gt;
158      *                 &lt;value&gt;1377279423564&lt;/value&gt;
159      *                 &lt;name&gt;creation&lt;/name&gt;
160      *             &lt;/timeStamp&gt;
161      *         &lt;/properties&gt;
162      *     &lt;/edgeProperties&gt;
163      * &lt;/topology&gt;
164      *
165      * Response in JSON:
166      * {"edgeProperties":[{"edge":{"tailNodeConnector":{"node":{"id":"00:00:00:00:00:00:00:02","type":"OF"},
167      * "id":"2","type":"OF"},"headNodeConnector":{"node":{"id":"00:00:00:00:00:00:00:51","type":"OF"},"id":
168      * "2","type":"OF"}},"properties":{"timeStamp":{"value":"1377278961017","name":"creation"}}},
169      * {"edge":{"tailNodeConnector":{"node":{"id":"00:00:00:00:00:00:00:51","type":"OF"},"id":
170      * "2","type":"OF"}},"headNodeConnector":{"node":{"id":"00:00:00:00:00:00:00:02","type":"OF"},
171      * "id":"2","type":"OF"}},"properties":{"timeStamp":{"value":"1377278961018","name":"creation"}}}]}
172      *
173      * </pre>
174      */
175     @Path("/{containerName}")
176     @GET
177     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
178     @TypeHint(Topology.class)
179     @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
180     public Topology getTopology(@PathParam("containerName") String containerName) {
181
182         if (!NorthboundUtils.isAuthorized(
183                 getUserName(), containerName, Privilege.READ, this)) {
184             throw new UnauthorizedException(
185                     "User is not authorized to perform this operation on container "
186                             + containerName);
187         }
188         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
189                 .getInstance(ITopologyManager.class, containerName, this);
190         if (topologyManager == null) {
191             throw new ResourceNotFoundException(
192                     RestMessages.NOCONTAINER.toString());
193         }
194
195         Map<Edge, Set<Property>> topo = topologyManager.getEdges();
196         if (topo != null) {
197             List<EdgeProperties> res = new ArrayList<EdgeProperties>();
198             for (Map.Entry<Edge, Set<Property>> entry : topo.entrySet()) {
199                 EdgeProperties el = new EdgeProperties(entry.getKey(), entry.getValue());
200                 res.add(el);
201             }
202             return new Topology(res);
203         }
204
205         return null;
206     }
207
208     /**
209      * Retrieve the user configured links
210      *
211      * @param containerName
212      *            The container for which we want to retrieve the user links (Eg. 'default')
213      *
214      * @return A List of user configured links
215      *
216      * <pre>
217      *
218      * Example:
219      *
220      * RequestURL:
221      * http://localhost:8080/controller/nb/v2/topology/default/userLinks
222      *
223      * Response in XML:
224      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
225      * &lt;topologyUserLinks&gt;
226      * &lt;userLinks&gt;
227      * &lt;status&gt;Success&lt;/status&gt;
228      * &lt;name&gt;link1&lt;/name&gt;
229      * &lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;
230      * &lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;
231      * &lt;/userLinks&gt;
232      * &lt;/topologyUserLinks&gt;
233      *
234      * Response in JSON:
235      * {"userLinks":{"status":"Success","name":"link1","srcNodeConnector":
236      * "OF|2@OF|00:00:00:00:00:00:00:02","dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"}}
237      *
238      * </pre>
239      */
240     @Path("/{containerName}/userLinks")
241     @GET
242     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
243     @TypeHint(TopologyUserLinks.class)
244     @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
245     public TopologyUserLinks getUserLinks(
246             @PathParam("containerName") String containerName) {
247
248         if (!NorthboundUtils.isAuthorized(
249                 getUserName(), containerName, Privilege.READ, this)) {
250             throw new UnauthorizedException(
251                     "User is not authorized to perform this operation on container "
252                             + containerName);
253         }
254         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
255                 .getInstance(ITopologyManager.class, containerName, this);
256         if (topologyManager == null) {
257             throw new ResourceNotFoundException(
258                     RestMessages.NOCONTAINER.toString());
259         }
260
261         ConcurrentMap<String, TopologyUserLinkConfig> userLinks = topologyManager
262                 .getUserLinks();
263         if ((userLinks != null) && (userLinks.values() != null)) {
264             List<TopologyUserLinkConfig> res = new ArrayList<TopologyUserLinkConfig>(
265                     userLinks.values());
266             return new TopologyUserLinks(res);
267         }
268
269         return null;
270     }
271
272     /**
273      * Add an User Link
274      *
275      * @param containerName
276      *            Name of the Container (Eg. 'default')
277      * @param name
278      *            Name of the user link
279      * @param TopologyUserLinkConfig
280      *            in JSON or XML format
281      * @return Response as dictated by the HTTP Response Status code
282      *
283      * <pre>
284      *
285      * Example:
286      *
287      * RequestURL:
288      * http://localhost:8080/controller/nb/v2/topology/default/userLink/link1
289      *
290      * Request in XML:
291      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
292      * &lt;topologyUserLinks&gt;
293      * &lt;status&gt;Success&lt;/status&gt;
294      * &lt;name&gt;link1&lt;/name&gt;
295      * &lt;srcNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:02&lt;/srcNodeConnector&gt;
296      * &lt;dstNodeConnector&gt;OF|2@OF|00:00:00:00:00:00:00:51&lt;/dstNodeConnector&gt;
297      * &lt;/topologyUserLinks&gt;
298      *
299      * Request in JSON:
300      * {"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"}
301      *
302      * </pre>
303      */
304     @Path("/{containerName}/userLink/{name}")
305     @PUT
306     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
307     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
308     @StatusCodes({
309             @ResponseCode(code = 201, condition = "User Link added successfully"),
310             @ResponseCode(code = 404, condition = "The Container Name was not found"),
311             @ResponseCode(code = 409, condition = "Failed to add User Link due to Conflicting Name"),
312             @ResponseCode(code = 500, condition = "Failed to add User Link. Failure Reason included in HTTP Error response"),
313             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
314     public Response addUserLink(
315             @PathParam(value = "containerName") String containerName,
316             @PathParam(value = "name") String name,
317             @TypeHint(TopologyUserLinkConfig.class) TopologyUserLinkConfig userLinkConfig) {
318
319         if (!NorthboundUtils.isAuthorized(
320                 getUserName(), containerName, Privilege.WRITE, this)) {
321             throw new UnauthorizedException(
322                     "User is not authorized to perform this operation on container "
323                             + containerName);
324         }
325         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
326                 .getInstance(ITopologyManager.class, containerName, this);
327         if (topologyManager == null) {
328             throw new ResourceNotFoundException(
329                     RestMessages.NOCONTAINER.toString());
330         }
331
332         Status status = topologyManager.addUserLink(userLinkConfig);
333         if (status.isSuccess()) {
334             NorthboundUtils.auditlog("User Link", username, "added", userLinkConfig.getName(), containerName);
335             return Response.status(Response.Status.CREATED).build();
336         }
337         throw new InternalServerErrorException(status.getDescription());
338     }
339
340     /**
341      * Delete an User Link
342      *
343      * @param containerName
344      *            Name of the Container (Eg. 'default')
345      * @param name
346      *            Name of the Link Configuration (Eg. 'config1')
347      * @return Response as dictated by the HTTP Response Status code
348      *
349      * <pre>
350      *
351      * Example:
352      *
353      * RequestURL:
354      * http://localhost:8080/controller/nb/v2/topology/default/userLink/config1
355      *
356      * </pre>
357      */
358     @Path("/{containerName}/userLink/{name}")
359     @DELETE
360     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
361     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
362     @StatusCodes({
363             @ResponseCode(code = 204, condition = "User link removed successfully"),
364             @ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"),
365             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
366     public Response deleteUserLink(
367             @PathParam("containerName") String containerName,
368             @PathParam("name") String name) {
369
370         if (!NorthboundUtils.isAuthorized(
371                 getUserName(), containerName, Privilege.WRITE, this)) {
372             throw new UnauthorizedException(
373                     "User is not authorized to perform this operation on container "
374                             + containerName);
375         }
376         ITopologyManager topologyManager = (ITopologyManager) ServiceHelper
377                 .getInstance(ITopologyManager.class, containerName, this);
378         if (topologyManager == null) {
379             throw new ResourceNotFoundException(
380                     RestMessages.NOCONTAINER.toString());
381         }
382
383         Status ret = topologyManager.deleteUserLink(name);
384         if (ret.isSuccess()) {
385             NorthboundUtils.auditlog("User Link", username, "removed", name, containerName);
386             return Response.noContent().build();
387         }
388         return NorthboundUtils.getResponse(ret);
389     }
390 }