Bug #65 - Fix inconsistencies in the NB REST APIs
[controller.git] / opendaylight / northbound / switchmanager / src / main / java / org / opendaylight / controller / switchmanager / northbound / SwitchNorthbound.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.switchmanager.northbound;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
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.POST;
22 import javax.ws.rs.PUT;
23 import javax.ws.rs.Path;
24 import javax.ws.rs.PathParam;
25 import javax.ws.rs.Produces;
26 import javax.ws.rs.core.Context;
27 import javax.ws.rs.core.MediaType;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.SecurityContext;
30 import javax.ws.rs.core.UriInfo;
31
32 import org.codehaus.enunciate.jaxrs.ResponseCode;
33 import org.codehaus.enunciate.jaxrs.StatusCodes;
34 import org.codehaus.enunciate.jaxrs.TypeHint;
35 import org.opendaylight.controller.containermanager.IContainerManager;
36 import org.opendaylight.controller.northbound.commons.RestMessages;
37 import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
38 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
39 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
40 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
41 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
42 import org.opendaylight.controller.sal.authorization.Privilege;
43 import org.opendaylight.controller.sal.core.Node;
44 import org.opendaylight.controller.sal.core.NodeConnector;
45 import org.opendaylight.controller.sal.core.Property;
46 import org.opendaylight.controller.sal.utils.GlobalConstants;
47 import org.opendaylight.controller.sal.utils.ServiceHelper;
48 import org.opendaylight.controller.sal.utils.Status;
49 import org.opendaylight.controller.sal.utils.StatusCode;
50 import org.opendaylight.controller.switchmanager.ISwitchManager;
51 import org.opendaylight.controller.switchmanager.SwitchConfig;
52
53 /**
54  * The class provides Northbound REST APIs to access the nodes, node connectors
55  * and their properties.
56  *
57  */
58
59 @Path("/")
60 public class SwitchNorthbound {
61
62     private String username;
63
64     @Context
65     public void setSecurityContext(SecurityContext context) {
66         if (context != null && context.getUserPrincipal() != null) username = context.getUserPrincipal().getName();
67     }
68
69     protected String getUserName() {
70         return username;
71     }
72
73     private ISwitchManager getIfSwitchManagerService(String containerName) {
74         IContainerManager containerManager = (IContainerManager) ServiceHelper
75                 .getGlobalInstance(IContainerManager.class, this);
76         if (containerManager == null) {
77             throw new ServiceUnavailableException("Container "
78                     + RestMessages.SERVICEUNAVAILABLE.toString());
79         }
80
81         boolean found = false;
82         List<String> containerNames = containerManager.getContainerNames();
83         for (String cName : containerNames) {
84             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
85                 found = true;
86                 break;
87             }
88         }
89
90         if (found == false) {
91             throw new ResourceNotFoundException(containerName + " "
92                     + RestMessages.NOCONTAINER.toString());
93         }
94
95         ISwitchManager switchManager = (ISwitchManager) ServiceHelper
96                 .getInstance(ISwitchManager.class, containerName, this);
97
98         if (switchManager == null) {
99             throw new ServiceUnavailableException("Switch Manager "
100                     + RestMessages.SERVICEUNAVAILABLE.toString());
101         }
102
103         return switchManager;
104     }
105
106     /**
107      *
108      * Retrieve a list of all the nodes and their properties in the network
109      *
110      * @param containerName
111      *            Name of the Container (Eg. 'default')
112      * @return A list of Pair each pair represents a
113      *         {@link org.opendaylight.controller.sal.core.Node} and Set of
114      *         {@link org.opendaylight.controller.sal.core.Property} attached to
115      *         it.
116      *
117      * <pre>
118      *
119      * Example:
120      *
121      * RequestURL:
122      * http://localhost:8080/controller/nb/v2/switchmanager/default/nodes
123      *
124      * Response in XML:
125      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
126      * &lt;list&gt;
127      *     &#x20;&#x20;&#x20;&lt;nodeProperties&gt;
128      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;node&gt;
129      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;id&gt;00:00:00:00:00:00:00:02&lt;/id&gt;
130      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;type&gt;OF&lt;/type&gt;
131      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/node&gt;
132      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;properties&gt;
133      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;tables&gt;
134      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;-1&lt;/value&gt;
135      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/tables&gt;
136      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;description&gt;
137      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;Switch2&lt;/value&gt;
138      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/description&gt;
139      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;actions&gt;
140      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;4095&lt;/value&gt;
141      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/actions&gt;
142      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;macAddress&gt;
143      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;00:00:00:00:00:02&lt;/value&gt;
144      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/macAddress&gt;
145      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;capabilities&gt;
146      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;199&lt;/value&gt;
147      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/capabilities&gt;
148      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;timeStamp&gt;
149      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1377291227877&lt;/value&gt;
150      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;connectedSince&lt;/name&gt;
151      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/timeStamp&gt;
152      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;buffers&gt;
153      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;256&lt;/value&gt;
154      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/buffers&gt;
155      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/properties&gt;
156      *     &#x20;&#x20;&#x20;&lt;/nodeProperties&gt;
157      * &lt;/list&gt;
158      *
159      * Response in JSON:
160      * {"nodeProperties":[{"node":{"id":"00:00:00:00:00:00:00:02","type":"OF"},"properties":{"tables":{"value":"-1"},
161      * "description":{"value":"None"},"actions":{"value":"4095"},"macAddress":{"value":"00:00:00:00:00:02"},"capabilities"
162      * :{"value":"199"},"timeStamp":{"value":"1377291227877","name":"connectedSince"},"buffers":{"value":"256"}}}]}
163      *
164      * </pre>
165      */
166     @Path("/{containerName}/nodes")
167     @GET
168     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
169     @TypeHint(Nodes.class)
170     @StatusCodes({
171             @ResponseCode(code = 200, condition = "Operation successful"),
172             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
173             @ResponseCode(code = 404, condition = "The containerName is not found"),
174             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
175     public Nodes getNodes(@PathParam("containerName") String containerName) {
176
177         if (!isValidContainer(containerName)) {
178             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
179         }
180
181         if (!NorthboundUtils.isAuthorized(
182                 getUserName(), containerName, Privilege.READ, this)) {
183             throw new UnauthorizedException(
184                     "User is not authorized to perform this operation on container "
185                             + containerName);
186         }
187
188         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
189         if (switchManager == null) {
190             throw new ServiceUnavailableException("Switch Manager "
191                     + RestMessages.SERVICEUNAVAILABLE.toString());
192         }
193
194         List<NodeProperties> res = new ArrayList<NodeProperties>();
195         Set<Node> nodes = switchManager.getNodes();
196         if (nodes == null) {
197             return new Nodes(res);
198         }
199
200         for (Node node : nodes) {
201             Map<String, Property> propMap = switchManager.getNodeProps(node);
202             if (propMap == null) {
203                 continue;
204             }
205             Set<Property> props = new HashSet<Property>(propMap.values());
206
207             NodeProperties nodeProps = new NodeProperties(node, props);
208             res.add(nodeProps);
209         }
210
211         return new Nodes(res);
212     }
213
214     /**
215      * Add a Description, Tier and Forwarding mode property to a node. This
216      * method returns a non-successful response if a node by that name already
217      * exists.
218      *
219      * @param containerName
220      *            Name of the Container (Eg. 'default')
221      * @param nodeType
222      *            Type of the node being programmed (Eg. 'OF')
223      * @param nodeId
224      *            Node Identifier as specified by
225      *            {@link org.opendaylight.controller.sal.core.Node}
226      *            (Eg. '00:00:00:00:00:00:00:03')
227      * @param propertyName
228      *            Name of the Property. Properties that can be
229      *            configured are: description, forwarding(only for default
230      *            container) and tier
231      * @param propertyValue
232      *            Value of the Property. Description can be any string (Eg. 'Node1'),
233      *            valid values for tier are 0, 1 and 2, and valid values for forwarding are 0 for
234      *            reactive and 1 for proactive forwarding.
235      * @return Response as dictated by the HTTP Response Status code
236      *
237      * <pre>
238      *
239      * Example:
240      *
241      * RequestURL:
242      * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
243      *
244      * </pre>
245      */
246
247     @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}/{propertyValue}")
248     @PUT
249     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
250     @TypeHint(Response.class)
251     @StatusCodes({
252             @ResponseCode(code = 201, condition = "Operation successful"),
253             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
254             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
255             @ResponseCode(code = 404, condition = "The Container Name or node or configuration name is not found"),
256             @ResponseCode(code = 406, condition = "The property cannot be configured in non-default container"),
257             @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict or conflicting description property"),
258             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
259     public Response addNodeProperty(
260             @Context UriInfo uriInfo,
261             @PathParam("containerName") String containerName,
262             @PathParam("nodeType") String nodeType,
263             @PathParam("nodeId") String nodeId,
264             @PathParam("propertyName") String propertyName,
265             @PathParam("propertyValue") String propertyValue) {
266
267         if (!isValidContainer(containerName)) {
268             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
269         }
270         if (!NorthboundUtils.isAuthorized(
271                 getUserName(), containerName, Privilege.WRITE, this)) {
272             throw new UnauthorizedException(
273                     "User is not authorized to perform this operation on container "
274                             + containerName);
275         }
276         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
277         if (switchManager == null) {
278             throw new ServiceUnavailableException("Switch Manager "
279                     + RestMessages.SERVICEUNAVAILABLE.toString());
280         }
281
282         handleNodeAvailability(containerName, nodeType, nodeId);
283         Node node = Node.fromString(nodeType, nodeId);
284         Property prop = switchManager.createProperty(propertyName, propertyValue);
285         if (prop == null) {
286             throw new ResourceNotFoundException("Property with name " + propertyName + " does not exist.");
287         }
288         SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
289         Map<String, Property> nodeProperties = (switchConfig == null) ? new HashMap<String, Property>()
290                 : new HashMap<String, Property>(switchConfig.getNodeProperties());
291         nodeProperties.put(prop.getName(), prop);
292         SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
293         Status status = switchManager.updateNodeConfig(newSwitchConfig);
294         if (status.isSuccess()) {
295             return Response.created(uriInfo.getRequestUri()).build();
296         }
297         return NorthboundUtils.getResponse(status);
298     }
299
300     /**
301      * Delete a property of a node
302      *
303      * @param containerName
304      *            Name of the Container (Eg. 'SliceRed')
305      * @param nodeType
306      *            Type of the node being programmed (Eg. 'OF')
307      * @param nodeId
308      *            Node Identifier as specified by
309      *            {@link org.opendaylight.controller.sal.core.Node}
310      *            (Eg. '00:00:00:00:00:03:01:02')
311      * @param propertyName
312      *            Name of the Property. Properties that can be deleted are
313      *            description, forwarding(only in default container) and tier.
314      * @return Response as dictated by the HTTP Response Status code
315      *
316      * <pre>
317      *
318      * Example:
319      *
320      * RequestURL:
321      * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
322      *
323      * </pre>
324      */
325
326     @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}")
327     @DELETE
328     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
329     @StatusCodes({
330             @ResponseCode(code = 204, condition = "Property removed successfully"),
331             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
332             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
333             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
334             @ResponseCode(code = 409, condition = "Unable to delete property due to cluster conflict"),
335             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
336     public Response deleteNodeProperty(
337             @PathParam("containerName") String containerName,
338             @PathParam("nodeType") String nodeType,
339             @PathParam("nodeId") String nodeId,
340             @PathParam("propertyName") String propertyName) {
341
342         if (!isValidContainer(containerName)) {
343             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
344         }
345         if (!NorthboundUtils.isAuthorized(
346                 getUserName(), containerName, Privilege.WRITE, this)) {
347             throw new UnauthorizedException(
348                     "User is not authorized to perform this operation on container "
349                             + containerName);
350         }
351         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
352         if (switchManager == null) {
353             throw new ServiceUnavailableException("Switch Manager "
354                     + RestMessages.SERVICEUNAVAILABLE.toString());
355         }
356
357         handleNodeAvailability(containerName, nodeType, nodeId);
358         Node node = Node.fromString(nodeType, nodeId);
359
360         SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
361         Status status;
362         if (switchConfig == null) {
363             status = new Status(StatusCode.NOTFOUND, "Switch Configuration does not exist");
364         } else {
365             Map<String, Property> nodeProperties = new HashMap<String, Property>(switchConfig.getNodeProperties());
366             if (!nodeProperties.containsKey(propertyName.toLowerCase())) {
367                 String msg = "Property " + propertyName + " does not exist or not configured for switch " + nodeId;
368                 status = new Status(StatusCode.NOTFOUND, msg);
369             } else {
370                 nodeProperties.remove(propertyName.toLowerCase());
371                 SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
372                 status = switchManager.updateNodeConfig(newSwitchConfig);
373                 if(status.isSuccess()){
374                     NorthboundUtils.auditlog("Node Property", username, "removed", propertyName  + " from " + nodeId, containerName);
375                     return Response.noContent().build();
376                 }
377             }
378         }
379         return NorthboundUtils.getResponse(status);
380     }
381
382     /**
383      *
384      * Retrieve a list of all the nodeconnectors and their properties in a given
385      * node
386      *
387      * @param containerName
388      *            The container for which we want to retrieve the list (Eg.
389      *            'default')
390      * @param nodeType
391      *            Type of the node being programmed (Eg. 'OF')
392      * @param nodeId
393      *            Node Identifier as specified by
394      *            {@link org.opendaylight.controller.sal.core.Node} (Eg.
395      *            '00:00:00:00:00:00:00:03')
396      * @return A List of Pair each pair represents a
397      *         {@link org.opendaylight.controller.sal.core.NodeConnector} and
398      *         its corresponding
399      *         {@link org.opendaylight.controller.sal.core.Property} attached to
400      *         it.
401      *
402      * <pre>
403      *
404      * Example:
405      *
406      * RequestURL:
407      * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:01
408      *
409      * Response in XML:
410      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
411      * &lt;list&gt;
412      *     &#x20;&#x20;&#x20;&lt;nodeConnectorProperties&gt;
413      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeconnector&gt;
414      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;node&gt;
415      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;id&gt;00:00:00:00:00:00:00:01&lt;/id&gt;
416      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;type&gt;OF&lt;/type&gt;
417      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/node&gt;
418      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;id&gt;2&lt;/id&gt;
419      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;type&gt;OF&lt;/type&gt;
420      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/nodeconnector&gt;
421      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;properties&gt;
422      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;state&gt;
423      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1&lt;/value&gt;
424      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/state&gt;
425      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;config&gt;
426      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1&lt;/value&gt;
427      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/config&gt;
428      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;
429      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;L1_2-C2_1&lt;/value&gt;
430      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/name&gt;
431      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/properties&gt;
432      *     &#x20;&#x20;&#x20;&lt;/nodeConnectorProperties&gt;
433      * &lt;/list&gt;
434      *
435      * Response in JSON:
436      * {"nodeConnectorProperties":[{"nodeconnector":{"node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},"id":"2","type":"OF"},
437      * "properties":{"state":{"value":"1"},"config":{"value":"1"},"name":{"value":"L1_2-C2_1"}}}]}
438      *
439      * </pre>
440      */
441     @Path("/{containerName}/node/{nodeType}/{nodeId}")
442     @GET
443     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
444     @TypeHint(NodeConnectors.class)
445     @StatusCodes({
446             @ResponseCode(code = 200, condition = "Operation successful"),
447             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
448             @ResponseCode(code = 404, condition = "The containerName is not found"),
449             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
450     public NodeConnectors getNodeConnectors(
451             @PathParam("containerName") String containerName,
452             @PathParam("nodeType") String nodeType,
453             @PathParam("nodeId") String nodeId) {
454
455         if (!isValidContainer(containerName)) {
456             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
457         }
458         if (!NorthboundUtils.isAuthorized(
459                 getUserName(), containerName, Privilege.READ, this)) {
460             throw new UnauthorizedException(
461                     "User is not authorized to perform this operation on container "
462                             + containerName);
463         }
464
465         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
466         if (switchManager == null) {
467             throw new ServiceUnavailableException("Switch Manager "
468                     + RestMessages.SERVICEUNAVAILABLE.toString());
469         }
470
471         handleNodeAvailability(containerName, nodeType, nodeId);
472         Node node = Node.fromString(nodeType, nodeId);
473         List<NodeConnectorProperties> res = new ArrayList<NodeConnectorProperties>();
474         Set<NodeConnector> ncs = switchManager.getNodeConnectors(node);
475         if (ncs == null) {
476             return null;
477         }
478
479         for (NodeConnector nc : ncs) {
480             Map<String, Property> propMap = switchManager
481                     .getNodeConnectorProps(nc);
482             if (propMap == null) {
483                 continue;
484             }
485             Set<Property> props = new HashSet<Property>(propMap.values());
486             NodeConnectorProperties ncProps = new NodeConnectorProperties(nc,
487                     props);
488             res.add(ncProps);
489         }
490
491         return new NodeConnectors(res);
492     }
493
494     /**
495      * Add node-connector property to a node connector. This method returns a
496      * non-successful response if a node connector by the given name already
497      * exists.
498      *
499      * @param containerName
500      *            Name of the Container (Eg. 'default')
501      * @param nodeType
502      *            Type of the node being programmed (Eg. 'OF')
503      * @param nodeId
504      *            Node Identifier as specified by
505      *            {@link org.opendaylight.controller.sal.core.Node}
506      *            (Eg. '00:00:00:00:00:00:00:03')
507      * @param nodeConnectorType
508      *            Type of the node connector being programmed (Eg. 'OF')
509      * @param nodeConnectorId
510      *            NodeConnector Identifier as specified by
511      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
512      *            (Eg. '2')
513      * @param propertyName
514      *            Name of the Property specified by
515      *            {@link org.opendaylight.controller.sal.core.Property} and its
516      *            extended classes
517      *            Property that can be configured is bandwidth
518      * @param propertyValue
519      *            Value of the Property specified by
520      *            {@link org.opendaylight.controller.sal.core.Property} and its
521      *            extended classes
522      * @return Response as dictated by the HTTP Response Status code
523      *
524      * <pre>
525      *
526      * Example:
527      *
528      * RequestURL:
529      * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
530      *
531      * </pre>
532      */
533
534     @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}/{propertyValue}")
535     @PUT
536     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
537     @StatusCodes({
538             @ResponseCode(code = 201, condition = "Operation successful"),
539             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
540             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
541             @ResponseCode(code = 409, condition = "Unable to add property due to cluster conflict"),
542             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
543     public Response addNodeConnectorProperty(
544             @Context UriInfo uriInfo,
545             @PathParam("containerName") String containerName,
546             @PathParam("nodeType") String nodeType,
547             @PathParam("nodeId") String nodeId,
548             @PathParam("nodeConnectorType") String nodeConnectorType,
549             @PathParam("nodeConnectorId") String nodeConnectorId,
550             @PathParam("propertyName") String propertyName,
551             @PathParam("propertyValue") String propertyValue) {
552
553         if (!isValidContainer(containerName)) {
554             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
555         }
556         if (!NorthboundUtils.isAuthorized(
557                 getUserName(), containerName, Privilege.WRITE, this)) {
558             throw new UnauthorizedException(
559                     "User is not authorized to perform this operation on container "
560                             + containerName);
561         }
562
563         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
564         if (switchManager == null) {
565             throw new ServiceUnavailableException("Switch Manager "
566                     + RestMessages.SERVICEUNAVAILABLE.toString());
567         }
568
569         handleNodeAvailability(containerName, nodeType, nodeId);
570         Node node = Node.fromString(nodeType, nodeId);
571
572         handleNodeConnectorAvailability(containerName, node, nodeConnectorType,
573                 nodeConnectorId);
574         NodeConnector nc = NodeConnector
575                 .fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
576
577         Property prop = switchManager.createProperty(propertyName, propertyValue);
578         if (prop == null) {
579             throw new ResourceNotFoundException(
580                     RestMessages.INVALIDDATA.toString());
581         }
582
583         Status ret = switchManager.addNodeConnectorProp(nc, prop);
584         if (ret.isSuccess()) {
585             return Response.created(uriInfo.getRequestUri()).build();
586         }
587         throw new InternalServerErrorException(ret.getDescription());
588     }
589
590     /**
591      * Delete a property of a node connector
592      *
593      * @param containerName
594      *            Name of the Container (Eg. 'default')
595      * @param nodeType
596      *            Type of the node being programmed (Eg. 'OF')
597      * @param nodeId
598      *            Node Identifier as specified by
599      *            {@link org.opendaylight.controller.sal.core.Node}
600      *            (Eg. '00:00:00:00:00:00:00:01')
601      * @param nodeConnectorType
602      *            Type of the node connector being programmed (Eg. 'OF')
603      * @param nodeConnectorId
604      *            NodeConnector Identifier as specified by
605      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
606      *            (Eg. '1')
607      * @param propertyName
608      *            Name of the Property specified by
609      *            {@link org.opendaylight.controller.sal.core.Property} and its
610      *            extended classes. Property that can be deleted is bandwidth
611      * @return Response as dictated by the HTTP Response Status code
612      *
613      * <pre>
614      *
615      * Example:
616      *
617      * RequestURL:
618      * http://localhost:8080/controller/nb/v2/switchmanager/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
619      *
620      * </pre>
621      */
622
623     @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}")
624     @DELETE
625     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
626     @StatusCodes({
627             @ResponseCode(code = 204, condition = "Property removed successfully"),
628             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
629             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
630             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
631     public Response deleteNodeConnectorProperty(
632             @PathParam("containerName") String containerName,
633             @PathParam("nodeType") String nodeType,
634             @PathParam("nodeId") String nodeId,
635             @PathParam("nodeConnectorType") String nodeConnectorType,
636             @PathParam("nodeConnectorId") String nodeConnectorId,
637             @PathParam("propertyName") String propertyName) {
638
639         if (!isValidContainer(containerName)) {
640             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
641         }
642         if (!NorthboundUtils.isAuthorized(
643                 getUserName(), containerName, Privilege.WRITE, this)) {
644             throw new UnauthorizedException(
645                     "User is not authorized to perform this operation on container "
646                             + containerName);
647         }
648
649         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
650         if (switchManager == null) {
651             throw new ServiceUnavailableException("Switch Manager "
652                     + RestMessages.SERVICEUNAVAILABLE.toString());
653         }
654
655         handleNodeAvailability(containerName, nodeType, nodeId);
656         Node node = Node.fromString(nodeType, nodeId);
657
658         handleNodeConnectorAvailability(containerName, node, nodeConnectorType,
659                 nodeConnectorId);
660         NodeConnector nc = NodeConnector
661                 .fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
662         Status ret = switchManager.removeNodeConnectorProp(nc, propertyName);
663         if (ret.isSuccess()) {
664             NorthboundUtils.auditlog("Node Connector Property", username, "removed", nc + " from " + nodeConnectorId, containerName);
665             return Response.noContent().build();
666         }
667         throw new ResourceNotFoundException(ret.getDescription());
668     }
669
670     /**
671      * Save the current switch configurations
672      *
673      * @param containerName
674      *            Name of the Container (Eg. 'default')
675      * @return Response as dictated by the HTTP Response Status code
676      *
677      * <pre>
678      *
679      * Example:
680      *
681      * RequestURL:
682      * http://localhost:8080/controller/nb/v2/switchmanager/default/save
683      *
684      * </pre>
685      */
686     @Path("/{containerName}/save")
687     @POST
688     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
689     @StatusCodes({
690             @ResponseCode(code = 200, condition = "Operation successful"),
691             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
692             @ResponseCode(code = 404, condition = "The containerName is not found"),
693             @ResponseCode(code = 500, condition = "Failed to save switch configuration. Failure Reason included in HTTP Error response"),
694             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
695     public Response saveSwitchConfig(
696             @PathParam("containerName") String containerName) {
697
698         if (!isValidContainer(containerName)) {
699             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
700         }
701         if (!NorthboundUtils.isAuthorized(
702                 getUserName(), containerName, Privilege.WRITE, this)) {
703             throw new UnauthorizedException(
704                     "User is not authorized to perform this operation on container "
705                             + containerName);
706         }
707         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
708         if (switchManager == null) {
709             throw new ServiceUnavailableException("Switch Manager "
710                     + RestMessages.SERVICEUNAVAILABLE.toString());
711         }
712
713         Status ret = switchManager.saveSwitchConfig();
714         if (ret.isSuccess()) {
715             return Response.ok().build();
716         }
717         throw new InternalServerErrorException(ret.getDescription());
718     }
719
720     private Node handleNodeAvailability(String containerName, String nodeType,
721             String nodeId) {
722
723         Node node = Node.fromString(nodeType, nodeId);
724         if (node == null) {
725             throw new ResourceNotFoundException(nodeId + " : "
726                     + RestMessages.NONODE.toString());
727         }
728
729         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
730                 ISwitchManager.class, containerName, this);
731
732         if (sm == null) {
733             throw new ServiceUnavailableException("Switch Manager "
734                     + RestMessages.SERVICEUNAVAILABLE.toString());
735         }
736
737         if (!sm.getNodes().contains(node)) {
738             throw new ResourceNotFoundException(node.toString() + " : "
739                     + RestMessages.NONODE.toString());
740         }
741         return node;
742     }
743
744     private void handleNodeConnectorAvailability(String containerName,
745             Node node, String nodeConnectorType, String nodeConnectorId) {
746
747         NodeConnector nc = NodeConnector.fromStringNoNode(nodeConnectorType,
748                 nodeConnectorId, node);
749         if (nc == null) {
750             throw new ResourceNotFoundException(nc + " : "
751                     + RestMessages.NORESOURCE.toString());
752         }
753
754         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
755                 ISwitchManager.class, containerName, this);
756
757         if (sm == null) {
758             throw new ServiceUnavailableException("Switch Manager "
759                     + RestMessages.SERVICEUNAVAILABLE.toString());
760         }
761
762         if (!sm.getNodeConnectors(node).contains(nc)) {
763             throw new ResourceNotFoundException(nc.toString() + " : "
764                     + RestMessages.NORESOURCE.toString());
765         }
766     }
767
768     private boolean isValidContainer(String containerName) {
769         if (containerName.equals(GlobalConstants.DEFAULT.toString())) {
770             return true;
771         }
772         IContainerManager containerManager = (IContainerManager) ServiceHelper
773                 .getGlobalInstance(IContainerManager.class, this);
774         if (containerManager == null) {
775             throw new InternalServerErrorException(
776                     RestMessages.INTERNALERROR.toString());
777         }
778         if (containerManager.getContainerNames().contains(containerName)) {
779             return true;
780        }
781         return false;
782     }
783
784 }