CORS support for ContainerManager Northbound APIs
[controller.git] / opendaylight / northbound / containermanager / src / main / java / org / opendaylight / controller / containermanager / northbound / ContainerManagerNorthbound.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.containermanager.northbound;
11
12 import java.security.Principal;
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17
18 import javax.ws.rs.Consumes;
19 import javax.ws.rs.DELETE;
20 import javax.ws.rs.GET;
21 import javax.ws.rs.PUT;
22 import javax.ws.rs.Path;
23 import javax.ws.rs.PathParam;
24 import javax.ws.rs.Produces;
25 import javax.ws.rs.core.Context;
26 import javax.ws.rs.core.MediaType;
27 import javax.ws.rs.core.Response;
28 import javax.ws.rs.core.SecurityContext;
29 import javax.ws.rs.core.UriInfo;
30
31 import org.codehaus.enunciate.jaxrs.ResponseCode;
32 import org.codehaus.enunciate.jaxrs.StatusCodes;
33 import org.codehaus.enunciate.jaxrs.TypeHint;
34 import org.opendaylight.controller.containermanager.ContainerConfig;
35 import org.opendaylight.controller.containermanager.ContainerFlowConfig;
36 import org.opendaylight.controller.containermanager.IContainerAuthorization;
37 import org.opendaylight.controller.containermanager.IContainerManager;
38 import org.opendaylight.controller.northbound.commons.RestMessages;
39 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
40 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
41 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
42 import org.opendaylight.controller.northbound.commons.exception.ResourceForbiddenException;
43 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
44 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
45 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
46 import org.opendaylight.controller.sal.authorization.Privilege;
47 import org.opendaylight.controller.sal.authorization.UserLevel;
48 import org.opendaylight.controller.sal.utils.GlobalConstants;
49 import org.opendaylight.controller.sal.utils.ServiceHelper;
50 import org.opendaylight.controller.sal.utils.Status;
51 import org.opendaylight.controller.usermanager.IUserManager;
52
53 /**
54  * Container Manager Northbound API
55  *
56  * <br>
57  * <br>
58  * Authentication scheme : <b>HTTP Basic</b><br>
59  * Authentication realm : <b>opendaylight</b><br>
60  * Transport : <b>HTTP and HTTPS</b><br>
61  * <br>
62  * HTTPS Authentication is disabled by default. Administrator can enable it in
63  * tomcat-server.xml after adding a proper keystore / SSL certificate from a
64  * trusted authority.<br>
65  * More info :
66  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
67  *
68  */
69 @Path("/")
70 public class ContainerManagerNorthbound {
71     private String username;
72
73     @Context
74     public void setSecurityContext(SecurityContext context) {
75         if (context != null && context.getUserPrincipal() != null) {
76             username = context.getUserPrincipal().getName();
77         }
78     }
79
80     protected String getUserName() {
81         return username;
82     }
83
84     private IContainerManager getContainerManager() {
85         IContainerManager containerMgr = (IContainerManager) ServiceHelper.getGlobalInstance(IContainerManager.class, this);
86         if (containerMgr == null) {
87             throw new InternalServerErrorException(RestMessages.INTERNALERROR.toString());
88         }
89         return containerMgr;
90     }
91
92     private void handleNameMismatch(String name, String nameinURL) {
93         if (name == null || nameinURL == null) {
94             throw new BadRequestException(RestMessages.INVALIDJSON.toString());
95         }
96
97         if (name.equalsIgnoreCase(nameinURL)) {
98             return;
99         }
100         throw new BadRequestException(RestMessages.INVALIDJSON.toString());
101     }
102
103
104
105     /**
106      * Get all the containers configured in the system
107      *
108      * @return a List of all {@link org.opendaylight.controller.containermanager.ContainerConfig}
109      *
110      *         <pre>
111      *
112      * Example:
113      *
114      * Request URL:
115      * http://localhost:8080/controller/nb/v2/containermanager/containers
116      *
117      * Response body in XML:
118      * &lt;container-config-list&gt;
119      *    &#x20;&#x20;&#x20;&lt;container-config&gt;
120      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;container&gt;black&lt;/container&gt;
121      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
122      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
123      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
124      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;flowSpecs&gt;
125      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;tcp&lt;/name&gt;
126      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;TCP&lt;/protocol&gt;
127      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/flowSpecs&gt;
128      *    &#x20;&#x20;&#x20;&#x20;&lt;/container-config&gt;
129      *    &#x20;&#x20;&#x20;&#x20;&lt;container-config&gt;
130      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;container&gt;red&lt;/container&gt;
131      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;20&lt;/staticVlan&gt;
132      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
133      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
134      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;flowSpecs&gt;
135      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;udp&lt;/name&gt;
136      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;UDP&lt;/protocol&gt;
137      *    &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/flowSpecs&gt;
138      *  &#x20;&#x20;&#x20;&#x20;&lt;/container-config&gt;
139      * &lt;/container-config-list&gt;
140      *
141      * Response body in JSON:
142      * { "container-config" : [
143      *     { "container" : "black",
144      *       "nodeConnectors" : [
145      *          "OF|1@OF|00:00:00:00:00:00:00:01", "OF|23@OF|00:00:00:00:00:00:20:21"
146      *       ],
147      *       "staticVlan" : "10",
148      *       "flowSpecs : [
149      *          { "name": "udp",
150      *            "protocol": "UDP" }
151      *       ]
152      *     },
153      *     { "container" : "red",
154      *       "nodeConnectors" : [
155      *          "OF|1@OF|00:00:00:00:00:00:00:01",
156      *          "OF|23@OF|00:00:00:00:00:00:20:21"
157      *       ],
158      *       "staticVlan" : "20",
159      *       "flowSpecs": [
160      *          { "name": "tcp",
161      *            "protocol": "TCP"
162      *          }
163      *       ]
164      *     }
165      *   ]
166      * }
167      * </pre>
168      */
169     @Path("/containers")
170     @GET
171     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
172     @TypeHint(ContainerConfigs.class)
173     @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
174             @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
175             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
176     public ContainerConfigs viewAllContainers() {
177
178         handleNetworkAuthorization(getUserName());
179
180         IContainerManager containerManager = getContainerManager();
181
182         return new ContainerConfigs(containerManager.getContainerConfigList());
183     }
184
185     /**
186      * Get the container configuration for container name requested
187      *
188      * @param container
189      *            name of the Container (eg. blue)
190      * @return a List of {@link org.opendaylight.controller.containermanager.ContainerConfig}
191      *
192      *         <pre>
193      *
194      * Example:
195      *
196      * Request URL:
197      * http://localhost:8080/controller/nb/v2/containermanager/container/blue
198      *
199      * Response body in XML:
200      * &lt;container-config&gt;
201      *  &#x20;&#x20;&#x20;&#x20;&lt;container&gt;blue&lt;/container&gt;
202      *  &#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
203      *  &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
204      *  &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;OF|23@OF|00:00:00:00:00:00:20:21&lt;/nodeConnectors&gt;
205      * &lt;/container-config&gt;
206      *
207      * Response body in JSON:
208      * {
209      *    "container-config": [
210      *       {
211      *        "container": "yellow",
212      *        "staticVlan": "10",
213      *        "nodeConnectors": [
214      *           "OF|1@OF|00:00:00:00:00:00:00:01",
215      *           "OF|2@OF|00:00:00:00:00:00:00:02"
216      *        ],
217      *        "flowSpecs": []
218      *       }
219      *    ]
220      * }
221      * </pre>
222      */
223     @Path("/container/{container}")
224     @GET
225     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
226     @TypeHint(ContainerConfig.class)
227     @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
228             @ResponseCode(code = 401, condition = "User is not authorized to perform this operation"),
229             @ResponseCode(code = 403, condition = "Operation forbidden on default"),
230             @ResponseCode(code = 404, condition = "The container is not found") })
231     public ContainerConfigs viewContainer(@PathParam(value = "container") String container) {
232
233         handleContainerAuthorization(container, getUserName());
234         handleForbiddenOnDefault(container);
235
236         handleContainerNotExists(container);
237
238         IContainerManager containerManager = getContainerManager();
239         List<ContainerConfig> containerConfigs = new ArrayList<ContainerConfig>();
240         containerConfigs.add(containerManager.getContainerConfig(container));
241         return new ContainerConfigs(containerConfigs);
242     }
243
244     /**
245      * Create a container
246      *
247      * @param uriInfo
248      * @param container
249      *            name of the Container (eg. yellow)
250      * @param containerConfig
251      *            details of the container as specified by:
252      *            {@link org.opendaylight.controller.containermanager.ContainerConfig}
253      * @return Response as dictated by the HTTP Response Status code
254      *
255      * <pre>
256      *
257      * Example:
258      *
259      * Request URL:
260      * http://localhost:8080/controller/nb/v2/containermanager/container/yellow
261      *
262      * Request body in XML:
263      * &lt;container-config&gt;
264      *   &#x20;&#x20;&#x20;&#x20;&lt;container&gt;yellow&lt;/container&gt;
265      *   &#x20;&#x20;&#x20;&#x20;&lt;staticVlan&gt;10&lt;/staticVlan&gt;
266      *   &#x20;&#x20;&#x20;&#x20;&lt;nodeConnectors&gt;&lt;/nodeConnectors&gt;
267      * &lt;/container-config&gt;
268      *
269      * Request body in JSON:
270      * {
271      *    "container" : "yellow",
272      *    "nodeConnectors" : [
273      *       "OF|1@OF|00:00:00:00:00:00:00:01",
274      *       "OF|23@OF|00:00:00:00:00:00:20:21"
275      *    ],
276      *    "staticVlan" : "10"
277      * }
278      *
279      * </pre>
280      */
281     @Path("/container/{container}")
282     @PUT
283     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
284     @StatusCodes({ @ResponseCode(code = 201, condition = "Container created successfully"),
285             @ResponseCode(code = 400, condition = "Invalid Container configuration."),
286             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
287             @ResponseCode(code = 403, condition = "Operation forbidden on default"),
288             @ResponseCode(code = 404, condition = "Container Name is not found"),
289             @ResponseCode(code = 409, condition = "Failed to create Container due to Conflicting Name"),
290             @ResponseCode(code = 500, condition = "Failure Reason included in HTTP Error response") })
291     public Response createContainer(@Context UriInfo uriInfo,
292             @PathParam(value = "container") String container,
293             @TypeHint(ContainerConfig.class) ContainerConfig containerConfig) {
294
295         handleAdminAuthorization(getUserName());
296         handleContainerExists(container);
297
298         handleNameMismatch(containerConfig.getContainerName(), container);
299         handleForbiddenOnDefault(container);
300
301         IContainerManager containerManager = getContainerManager();
302         Status status = containerManager.addContainer(containerConfig);
303         if (status.isSuccess()) {
304             NorthboundUtils.auditlog("Container", username, "added", container);
305             return Response.created(uriInfo.getRequestUri()).build();
306         }
307         return NorthboundUtils.getResponse(status);
308     }
309
310     /**
311      * Delete a container
312      *
313      * @param container
314      *            name of the Container (eg. green)
315      * @return Response as dictated by the HTTP Response code
316      *
317      * <pre>
318      *
319      * Example:
320      *
321      * Request URL:
322      * http://localhost:8080/controller/nb/v2/containermanager/container/green
323      *
324      * </pre>
325      */
326     @Path("/container/{container}")
327     @DELETE
328     @StatusCodes({
329             @ResponseCode(code = 204, condition = "Container deleted successfully"),
330             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
331             @ResponseCode(code = 403, condition = "Operation forbidden on default"),
332             @ResponseCode(code = 404, condition = "The container is not found") })
333     public Response removeContainer(@PathParam(value = "container") String container) {
334
335         handleAdminAuthorization(getUserName());
336         handleForbiddenOnDefault(container);
337         handleContainerNotExists(container);
338         IContainerManager containerManager = getContainerManager();
339         Status status = containerManager.removeContainer(container);
340         if (status.isSuccess()) {
341             NorthboundUtils.auditlog("Container", username, "removed", container);
342             return Response.noContent().build();
343         }
344         return NorthboundUtils.getResponse(status);
345     }
346
347     /**
348      * Get flowspec within a given container
349      *
350      * @param container
351      *            name of the Container (eg. green)
352      * @param name
353      *            name of the flowspec (eg. ssh)
354      * @return flowspec detail as specified by:
355      *         {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
356      *
357      *         <pre>
358      *
359      * Example:
360      *
361      * Request URL:
362      * http://localhost:8080/controller/nb/v2/containermanager/container/green/flowspec/ssh
363      *
364      * Response body in XML:
365      * &lt;flow-spec-config&gt;
366      *  &#x20;&#x20;&#x20;&#x20;&lt;name&gt;ssh&lt;/name&gt;
367      *  &#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
368      *  &#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
369      *  &#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;IPv4&lt;/protocol&gt;
370      *  &#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
371      *  &#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
372      * &lt;/flow-spec-config&gt;
373      *
374      * Response body in JSON:
375      * {
376      *    "protocol" : "IPv4",
377      *    "nwDst" : "10.0.0.102",
378      *    "name" : "ssh",
379      *    "nwSrc" : "10.0.0.101",
380      *    "tpSrc" : "80",
381      *    "tpDst" : "100"
382      * }
383      *
384      * </pre>
385      */
386     @Path("/container/{container}/flowspec/{flowspec}")
387     @GET
388     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
389     @TypeHint(ContainerFlowConfig.class)
390     @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
391             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
392             @ResponseCode(code = 404, condition = "The container is not found"),
393             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
394     public ContainerFlowConfig viewContainerFlowSpec(@PathParam(value = "container") String container,
395             @PathParam(value = "flowspec") String flowspec) {
396
397         handleContainerAuthorization(container, getUserName());
398         handleForbiddenOnDefault(container);
399
400         handleContainerNotExists(container);
401         IContainerManager containerManager = getContainerManager();
402         List<ContainerFlowConfig> flowSpecs = containerManager.getContainerFlows(container);
403
404         for (ContainerFlowConfig containerFlowConfig : flowSpecs) {
405             if (containerFlowConfig.equalsByName(flowspec)) {
406                 return containerFlowConfig;
407             }
408         }
409         throw new ResourceNotFoundException("Flow Spec not found");
410     }
411
412     /**
413      * Get all the flowspec in a given container
414      *
415      * @param container
416      *            name of the Container (eg. red)
417      * @return list of all flowspec configured for a container. Flowspec as
418      *         specified by:
419      *         {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
420      *
421      *         <pre>
422      *
423      * Example:
424      *
425      * Request URL:
426      * http://localhost:8080/controller/nb/v2/containermanager/container/red/flowspec
427      *
428      * Response body in XML:
429      * &lt;flow-spec-configs&gt;
430      *   &#x20;&#x20;&#x20;&#x20;&lt;flow-spec-config&gt;
431      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;ssh&lt;/name&gt;
432      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
433      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
434      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;IPv4&lt;/protocol&gt;
435      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;23&lt;/tpSrc&gt;
436      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
437      *   &#x20;&#x20;&#x20;&#x20;&lt;/flow-spec-config&gt;
438      *   &#x20;&#x20;&#x20;&#x20;&lt;flow-spec-config&gt;
439      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;http2&lt;/name&gt;
440      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.201&lt;/nwSrc&gt;
441      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.202&lt;/nwDst&gt;
442      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;&lt;/protocol&gt;
443      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
444      *     &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
445      *   &#x20;&#x20;&#x20;&#x20;&lt;/flow-spec-config&gt;
446      * &lt;/flow-spec-configs&gt;
447      *
448       * Response body in JSON:
449      * {
450      *   "flow-spec-config": [
451      *     {
452      *       "name": "http",
453      *       "nwSrc": "10.0.0.201",
454      *       "nwDst": "10.0.0.202",
455      *       "protocol": "",
456      *       "tpSrc": "80",
457      *       "tpDst": "100"
458      *     },
459      *     {
460      *       "name": "ssh",
461      *       "nwSrc": "10.0.0.101",
462      *       "nwDst": "10.0.0.102",
463      *       "protocol": "IPv4",
464      *       "tpSrc": "23",
465      *       "tpDst": "100"
466      *     }
467      *   ]
468      * }
469      *
470      * </pre>
471      */
472     @Path("/container/{container}/flowspecs")
473     @GET
474     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
475     @TypeHint(FlowSpecConfigs.class)
476     @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
477             @ResponseCode(code = 404, condition = "The container is not found"),
478             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
479     public FlowSpecConfigs viewContainerFlowSpecs(@PathParam(value = "container") String container) {
480
481         handleContainerAuthorization(container, getUserName());
482         handleForbiddenOnDefault(container);
483
484         handleContainerNotExists(container);
485
486         IContainerManager containerManager = getContainerManager();
487
488         return new FlowSpecConfigs(containerManager.getContainerFlows(container));
489     }
490
491     /**
492      * Add flowspec to a container
493      *
494      * @param container
495      *            name of the container (eg. purple)
496      * @param name
497      *            name of the flowspec (eg. http)
498      * @param flowspec
499      *            configuration as specified by:
500      *            {@link org.opendaylight.controller.containermanager.ContainerFlowConfig}
501      *
502      * @return Response as dictated by the HTTP Response code
503      *
504      *         <pre>
505      *
506      * Example:
507      *
508      * Request URL:
509      * http://localhost:8080/controller/nb/v2/containermanager/container/purple/flowspec/http
510      *
511      * Request body in XML:
512      *   &lt;flow-spec-config&gt;
513      *     &#x20;&#x20;&#x20;&#x20;&lt;name&gt;http&lt;/name&gt;
514      *     &#x20;&#x20;&#x20;&#x20;&lt;nwSrc&gt;10.0.0.101&lt;/nwSrc&gt;
515      *     &#x20;&#x20;&#x20;&#x20;&lt;nwDst&gt;10.0.0.102&lt;/nwDst&gt;
516      *     &#x20;&#x20;&#x20;&#x20;&lt;protocol&gt;&lt;/protocol&gt;
517      *     &#x20;&#x20;&#x20;&#x20;&lt;tpSrc&gt;80&lt;/tpSrc&gt;
518      *     &#x20;&#x20;&#x20;&#x20;&lt;tpDst&gt;100&lt;/tpDst&gt;
519      *   &lt;/flow-spec-config&gt;
520      *
521      * Request body in JSON:
522      * {
523      *    "protocol" : "",
524      *    "nwDst" : "10.0.0.102",
525      *    "name" : "http",
526      *    "nwSrc" : "10.0.0.101",
527      *    "tpSrc" : "80",
528      *    "tpDst" : "100"
529      * }
530      *
531      * </pre>
532      */
533     @Path("/container/{container}/flowspec/{flowspec}")
534     @PUT
535     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
536     @StatusCodes({
537             @ResponseCode(code = 201, condition = "FlowSpec created successfully"),
538             @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
539             @ResponseCode(code = 404, condition = "The container is not found"),
540             @ResponseCode(code = 409, condition = "Container Entry already exists"),
541             @ResponseCode(code = 500, condition = "Failed to create Flow specifications. Failure Reason included in HTTP Error response") })
542     public Response createFlowSpec(@Context UriInfo uriInfo,
543             @PathParam(value = "container") String container,
544             @PathParam(value = "flowspec") String flowspec,
545             @TypeHint(ContainerFlowConfig.class) ContainerFlowConfig containerFlowConfig) {
546
547         handleAdminAuthorization(getUserName());
548         handleForbiddenOnDefault(container);
549
550         handleContainerNotExists(container);
551         handleNameMismatch(containerFlowConfig.getName(), flowspec);
552
553         IContainerManager containerManager = getContainerManager();
554         List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
555         list.add(containerFlowConfig);
556         Status status = containerManager.addContainerFlows(container, list);
557         if (status.isSuccess()) {
558             NorthboundUtils.auditlog("Flow Spec", username, "added", containerFlowConfig.getName());
559             return Response.created(uriInfo.getRequestUri()).build();
560         }
561         return NorthboundUtils.getResponse(status);
562     }
563
564     /**
565      * Remove flowspec from a container
566      *
567      * @param name
568      *            name of the flowspec (eg. telnet)
569      * @param container
570      *            name of the Container (eg. black)
571      * @return Response as dictated by the HTTP Response code
572      *
573      * <pre>
574      *
575      * Example:
576      *
577      * Request URL:
578      * http://localhost:8080/controller/nb/v2/containermanager/container/black/flowspec/telnet
579      *
580      * </pre>
581      */
582     @Path("/container/{container}/flowspec/{flowspec}")
583     @DELETE
584     @StatusCodes({
585             @ResponseCode(code = 204, condition = "Flow Spec deleted successfully"),
586             @ResponseCode(code = 400, condition = "Invalid flowspec configuration"),
587             @ResponseCode(code = 404, condition = "Container or Container Entry not found"),
588             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
589             @ResponseCode(code = 500, condition = "Failed to delete Flowspec. Failure Reason included in HTTP Error response"),
590             @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
591     public Response removeFlowSpec(@PathParam(value = "container") String container,
592             @PathParam(value = "flowspec") String flowspec) {
593
594         handleAdminAuthorization(getUserName());
595         handleForbiddenOnDefault(container);
596
597         handleContainerNotExists(container);
598
599         IContainerManager containerManager = getContainerManager();
600         Set<String> set = new HashSet<String>();
601         set.add(flowspec);
602         Status status = containerManager.removeContainerFlows(container, set);
603         if (status.isSuccess()) {
604             NorthboundUtils.auditlog("Flow Spec", username, "added", flowspec);
605             return Response.noContent().build();
606         }
607         return NorthboundUtils.getResponse(status);
608     }
609
610     /**
611      * Add node connectors to a container
612      *
613      * @param container
614      *            name of the container (eg. green)
615      * @param list
616      *            The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
617      * @return response as dictated by the HTTP Status code
618      *
619      * <pre>
620      *
621      * Example:
622      *
623      * Request URL:
624      * http://localhost:8080/controller/nb/v2/containermanager/container/green/nodeconnector
625      *
626      * Request body in XML:
627      * &lt;nodeConnectors&gt;
628      *     &lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
629      *     &lt;nodeConnectors&gt;OF|2@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
630      *     &lt;nodeConnectors&gt;OF|3@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
631      *     &lt;nodeConnectors&gt;OF|4@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
632      * &lt;/nodeConnectors&gt;
633      *
634      * Request body in JSON:
635      * {
636      *    "nodeConnectors" : [
637      *       "OF|1@OF|00:00:00:00:00:00:00:01",
638      *       "OF|2@OF|00:00:00:00:00:00:00:01",
639      *       "OF|3@OF|00:00:00:00:00:00:00:22",
640      *       "OF|4@OF|00:00:00:00:00:00:00:22"
641      *    ]
642      * }
643      *
644      * </pre>
645      */
646     @Path("/container/{container}/nodeconnector/")
647     @PUT
648     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
649     @TypeHint(Response.class)
650     @StatusCodes({
651             @ResponseCode(code = 200, condition = "NodeConnectors added successfully"),
652             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
653             @ResponseCode(code = 403, condition = "Operation forbidden on default"),
654             @ResponseCode(code = 404, condition = "The Container is not found"),
655             @ResponseCode(code = 409, condition = "Container Entry already exists"),
656             @ResponseCode(code = 500, condition = "Failed to create nodeconnectors. Failure Reason included in HTTP Error response") })
657     public Response addNodeConnectors(@PathParam(value = "container") String container,
658             @TypeHint(StringList.class) StringList list) {
659
660         handleAdminAuthorization(getUserName());
661         handleForbiddenOnDefault(container);
662         handleContainerNotExists(container);
663
664         IContainerManager containerManager = getContainerManager();
665         Status status = containerManager.addContainerEntry(container, list.getList());
666         if (status.isSuccess()) {
667             NorthboundUtils.auditlog("Node ", username, "added", " Ports:" + list.getList());
668         }
669         return NorthboundUtils.getResponse(status);
670     }
671
672     /**
673      * Remove node connectors from a container
674      *
675      * @param container
676      *            name of the container (eg. red)
677      * @param list
678      *            The list of strings each representing a node connector in the form "<Port Type>|<Port id>@<Node Type>|<Node id>", as "OF|1@OF|00:00:00:ab:00:00:00:01"
679      * @return response as dictated by the HTTP Status code
680      *
681      *         <pre>
682      *
683      * Example:
684      *
685      * Request URL:
686      * http://localhost:8080/controller/nb/v2/containermanager/container/red/nodeconnector
687      *
688      * Request body in XML:
689      * &lt;nodeConnectors&gt;
690      *     &lt;nodeConnectors&gt;OF|1@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
691      *     &lt;nodeConnectors&gt;OF|2@OF|00:00:00:00:00:00:00:01&lt;/nodeConnectors&gt;
692      *     &lt;nodeConnectors&gt;OF|3@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
693      *     &lt;nodeConnectors&gt;OF|4@OF|00:00:00:00:00:00:00:22&lt;/nodeConnectors&gt;
694      * &lt;/nodeConnectors&gt;
695      *
696      * Request body in JSON:
697      * {
698      *    "nodeConnectors" : [
699      *       "OF|1@OF|00:00:00:00:00:00:00:01",
700      *       "OF|2@OF|00:00:00:00:00:00:00:01",
701      *       "OF|3@OF|00:00:00:00:00:00:00:22",
702      *       "OF|4@OF|00:00:00:00:00:00:00:22"
703      *       ]
704      * }
705      *
706      * </pre>
707      */
708     @Path("/container/{container}/nodeconnector/")
709     @DELETE
710     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
711     @StatusCodes({
712             @ResponseCode(code = 204, condition = "Container Entry deleted successfully"),
713             @ResponseCode(code = 400, condition = "Invalid Container Entry configuration"),
714             @ResponseCode(code = 404, condition = "The Container is not found"),
715             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
716             @ResponseCode(code = 500, condition = "Failed to delete node connector. Failure Reason included in HTTP Error response") })
717     public Response removeNodeConnectors(@PathParam(value = "container") String container,
718             @TypeHint(StringList.class) StringList portList) {
719
720         handleAdminAuthorization(getUserName());
721         handleForbiddenOnDefault(container);
722         handleContainerNotExists(container);
723
724         IContainerManager containerManager = getContainerManager();
725         Status status = containerManager.removeContainerEntry(container, portList.getList());
726         if (status.isSuccess()) {
727             NorthboundUtils.auditlog("Node", username, "removed", " Ports:" + portList.getList());
728             return Response.noContent().build();
729         }
730         return NorthboundUtils.getResponse(status);
731     }
732
733     /*
734      * Check If the function is not allowed on default container, Throw a
735      * ResourceForbiddenException exception if forbidden
736      */
737     private void handleForbiddenOnDefault(String container) {
738         if (container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) {
739             throw new ResourceForbiddenException(RestMessages.NODEFAULT.toString() + ": " + container);
740         }
741     }
742
743     /*
744      * Check if container exists, Throw a ResourceNotFoundException exception if it
745      * does not exist
746      */
747     private void handleContainerNotExists(String container) {
748         IContainerManager containerManager = getContainerManager();
749         if (!containerManager.doesContainerExist(container)) {
750             throw new ResourceNotFoundException(RestMessages.NOCONTAINER.toString() + ": " + container);
751         }
752     }
753
754     private void handleContainerExists(String container) {
755         IContainerManager containerManager = getContainerManager();
756         if (containerManager.doesContainerExist(container)) {
757             throw new ResourceConflictException(RestMessages.RESOURCECONFLICT.toString() + ": " + container);
758         }
759     }
760
761     private void handleAdminAuthorization(String userName) {
762         IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
763
764         UserLevel level = usrMgr.getUserLevel(userName);
765         if (level.ordinal() <= UserLevel.NETWORKADMIN.ordinal()) {
766             return;
767         }
768
769         throw new UnauthorizedException("User is not authorized to perform this operation");
770     }
771
772     private void handleNetworkAuthorization(String userName) {
773         IUserManager usrMgr = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
774
775         UserLevel level = usrMgr.getUserLevel(userName);
776         if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
777             return;
778         }
779         throw new UnauthorizedException("User is not authorized to perform this operation");
780     }
781
782     private void handleContainerAuthorization(String container, String userName) {
783         IContainerAuthorization auth = (IContainerAuthorization) ServiceHelper.getGlobalInstance(
784                 IContainerAuthorization.class, this);
785
786         UserLevel level = auth.getUserLevel(userName);
787         if (level.ordinal() <= UserLevel.NETWORKOPERATOR.ordinal()) {
788             return;
789         }
790
791         Privilege current = (auth == null) ? Privilege.NONE : auth.getResourcePrivilege(userName, container);
792
793         if (current.ordinal() > Privilege.NONE.ordinal()) {
794             return;
795         }
796         throw new UnauthorizedException("User is not authorized to perform this operation");
797     }
798
799 }