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