SwitchManager NB - Added URL info in response on successful creation of Node property...
[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/switch/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 null;
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.
216      *
217      * @param containerName
218      *            Name of the Container (Eg. 'default')
219      * @param nodeType
220      *            Type of the node being programmed (Eg. 'OF')
221      * @param nodeId
222      *            Node Identifier as specified by
223      *            {@link org.opendaylight.controller.sal.core.Node}
224      *            (Eg. '00:00:00:00:00:00:00:03')
225      * @param propertyName
226      *            Name of the Property. Properties that can be
227      *            configured are: description, forwarding(only for default
228      *            container) and tier
229      * @param propertyValue
230      *            Value of the Property. Description can be any string (Eg. 'Node1'),
231      *            valid values for tier are 0, 1 and 2, and valid values for forwarding are 0 for
232      *            reactive and 1 for proactive forwarding.
233      * @return Response as dictated by the HTTP Response Status code
234      *
235      * <pre>
236      *
237      * Example:
238      *
239      * RequestURL:
240      * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
241      *
242      * </pre>
243      */
244
245     @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}/{propertyValue}")
246     @PUT
247     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
248     @TypeHint(Response.class)
249     @StatusCodes({
250             @ResponseCode(code = 201, condition = "Operation successful"),
251             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
252             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
253             @ResponseCode(code = 404, condition = "The Container Name or node or configuration name is not found"),
254             @ResponseCode(code = 406, condition = "The property cannot be configured in non-default container"),
255             @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict or conflicting description property"),
256             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
257     public Response addNodeProperty(
258             @Context UriInfo uriInfo,
259             @PathParam("containerName") String containerName,
260             @PathParam("nodeType") String nodeType,
261             @PathParam("nodeId") String nodeId,
262             @PathParam("propertyName") String propertyName,
263             @PathParam("propertyValue") String propertyValue) {
264
265         if (!isValidContainer(containerName)) {
266             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
267         }
268         if (!NorthboundUtils.isAuthorized(
269                 getUserName(), containerName, Privilege.WRITE, this)) {
270             throw new UnauthorizedException(
271                     "User is not authorized to perform this operation on container "
272                             + containerName);
273         }
274         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
275         if (switchManager == null) {
276             throw new ServiceUnavailableException("Switch Manager "
277                     + RestMessages.SERVICEUNAVAILABLE.toString());
278         }
279
280         handleNodeAvailability(containerName, nodeType, nodeId);
281         Node node = Node.fromString(nodeType, nodeId);
282         Property prop = switchManager.createProperty(propertyName, propertyValue);
283         if (prop == null) {
284             throw new ResourceNotFoundException("Property with name " + propertyName + " does not exist.");
285         }
286         SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
287         Map<String, Property> nodeProperties = (switchConfig == null) ? new HashMap<String, Property>()
288                 : new HashMap<String, Property>(switchConfig.getNodeProperties());
289         nodeProperties.put(prop.getName(), prop);
290         SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
291         Status status = switchManager.updateNodeConfig(newSwitchConfig);
292         if (status.isSuccess()) {
293             return Response.created(uriInfo.getRequestUri()).build();
294         }
295         return NorthboundUtils.getResponse(status);
296     }
297
298     /**
299      * Delete a property of a node
300      *
301      * @param containerName
302      *            Name of the Container (Eg. 'SliceRed')
303      * @param nodeType
304      *            Type of the node being programmed (Eg. 'OF')
305      * @param nodeId
306      *            Node Identifier as specified by
307      *            {@link org.opendaylight.controller.sal.core.Node}
308      *            (Eg. '00:00:00:00:00:03:01:02')
309      * @param propertyName
310      *            Name of the Property. Properties that can be deleted are
311      *            description, forwarding(only in default container) and tier.
312      * @return Response as dictated by the HTTP Response Status code
313      *
314      * <pre>
315      *
316      * Example:
317      *
318      * RequestURL:
319      * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
320      *
321      * </pre>
322      */
323
324     @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}")
325     @DELETE
326     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
327     @StatusCodes({
328             @ResponseCode(code = 200, condition = "Operation successful"),
329             @ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
330             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
331             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
332             @ResponseCode(code = 409, condition = "Unable to delete property due to cluster conflict"),
333             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
334     public Response deleteNodeProperty(
335             @PathParam("containerName") String containerName,
336             @PathParam("nodeType") String nodeType,
337             @PathParam("nodeId") String nodeId,
338             @PathParam("propertyName") String propertyName) {
339
340         if (!isValidContainer(containerName)) {
341             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
342         }
343         if (!NorthboundUtils.isAuthorized(
344                 getUserName(), containerName, Privilege.WRITE, this)) {
345             throw new UnauthorizedException(
346                     "User is not authorized to perform this operation on container "
347                             + containerName);
348         }
349         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
350         if (switchManager == null) {
351             throw new ServiceUnavailableException("Switch Manager "
352                     + RestMessages.SERVICEUNAVAILABLE.toString());
353         }
354
355         handleNodeAvailability(containerName, nodeType, nodeId);
356         Node node = Node.fromString(nodeType, nodeId);
357
358         SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
359         Status status;
360         if (switchConfig == null) {
361             status = new Status(StatusCode.NOTFOUND, "Switch Configuration does not exist");
362         } else {
363             Map<String, Property> nodeProperties = new HashMap<String, Property>(switchConfig.getNodeProperties());
364             if (!nodeProperties.containsKey(propertyName.toLowerCase())) {
365                 String msg = "Property " + propertyName + " does not exist or not configured for switch " + nodeId;
366                 status = new Status(StatusCode.NOTFOUND, msg);
367             } else {
368                 nodeProperties.remove(propertyName.toLowerCase());
369                 SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
370                 status = switchManager.updateNodeConfig(newSwitchConfig);
371                 if(status.isSuccess()){
372                     NorthboundUtils.auditlog("Static Route", username, "updated", nodeId, containerName);
373                 }
374             }
375         }
376         return NorthboundUtils.getResponse(status);
377     }
378
379     /**
380      *
381      * Retrieve a list of all the nodeconnectors and their properties in a given
382      * node
383      *
384      * @param containerName
385      *            The container for which we want to retrieve the list (Eg.
386      *            'default')
387      * @param nodeType
388      *            Type of the node being programmed (Eg. 'OF')
389      * @param nodeId
390      *            Node Identifier as specified by
391      *            {@link org.opendaylight.controller.sal.core.Node} (Eg.
392      *            '00:00:00:00:00:00:00:03')
393      * @return A List of Pair each pair represents a
394      *         {@link org.opendaylight.controller.sal.core.NodeConnector} and
395      *         its corresponding
396      *         {@link org.opendaylight.controller.sal.core.Property} attached to
397      *         it.
398      *
399      * <pre>
400      *
401      * Example:
402      *
403      * RequestURL:
404      * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:00:00:01
405      *
406      * Response in XML:
407      * &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
408      * &lt;list&gt;
409      *     &#x20;&#x20;&#x20;&lt;nodeConnectorProperties&gt;
410      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;nodeconnector&gt;
411      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;node&gt;
412      *                 &#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;
413      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;type&gt;OF&lt;/type&gt;
414      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/node&gt;
415      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;id&gt;2&lt;/id&gt;
416      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;type&gt;OF&lt;/type&gt;
417      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/nodeconnector&gt;
418      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;properties&gt;
419      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;state&gt;
420      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;1&lt;/value&gt;
421      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/state&gt;
422      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;config&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;/config&gt;
425      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;name&gt;
426      *                 &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;value&gt;L1_2-C2_1&lt;/value&gt;
427      *             &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/name&gt;
428      *         &#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/properties&gt;
429      *     &#x20;&#x20;&#x20;&lt;/nodeConnectorProperties&gt;
430      * &lt;/list&gt;
431      *
432      * Response in JSON:
433      * {"nodeConnectorProperties":[{"nodeconnector":{"node":{"id":"00:00:00:00:00:00:00:01","type":"OF"},"id":"2","type":"OF"},
434      * "properties":{"state":{"value":"1"},"config":{"value":"1"},"name":{"value":"L1_2-C2_1"}}}]}
435      *
436      * </pre>
437      */
438     @Path("/{containerName}/node/{nodeType}/{nodeId}")
439     @GET
440     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
441     @TypeHint(NodeConnectors.class)
442     @StatusCodes({
443             @ResponseCode(code = 200, condition = "Operation successful"),
444             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
445             @ResponseCode(code = 404, condition = "The containerName is not found"),
446             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
447     public NodeConnectors getNodeConnectors(
448             @PathParam("containerName") String containerName,
449             @PathParam("nodeType") String nodeType,
450             @PathParam("nodeId") String nodeId) {
451
452         if (!isValidContainer(containerName)) {
453             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
454         }
455         if (!NorthboundUtils.isAuthorized(
456                 getUserName(), containerName, Privilege.READ, this)) {
457             throw new UnauthorizedException(
458                     "User is not authorized to perform this operation on container "
459                             + containerName);
460         }
461
462         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
463         if (switchManager == null) {
464             throw new ServiceUnavailableException("Switch Manager "
465                     + RestMessages.SERVICEUNAVAILABLE.toString());
466         }
467
468         handleNodeAvailability(containerName, nodeType, nodeId);
469         Node node = Node.fromString(nodeType, nodeId);
470         List<NodeConnectorProperties> res = new ArrayList<NodeConnectorProperties>();
471         Set<NodeConnector> ncs = switchManager.getNodeConnectors(node);
472         if (ncs == null) {
473             return null;
474         }
475
476         for (NodeConnector nc : ncs) {
477             Map<String, Property> propMap = switchManager
478                     .getNodeConnectorProps(nc);
479             if (propMap == null) {
480                 continue;
481             }
482             Set<Property> props = new HashSet<Property>(propMap.values());
483             NodeConnectorProperties ncProps = new NodeConnectorProperties(nc,
484                     props);
485             res.add(ncProps);
486         }
487
488         return new NodeConnectors(res);
489     }
490
491     /**
492      * Add Bandwidth property to a node connector
493      *
494      * @param containerName
495      *            Name of the Container (Eg. 'default')
496      * @param nodeType
497      *            Type of the node being programmed (Eg. 'OF')
498      * @param nodeId
499      *            Node Identifier as specified by
500      *            {@link org.opendaylight.controller.sal.core.Node}
501      *            (Eg. '00:00:00:00:00:00:00:03')
502      * @param nodeConnectorType
503      *            Type of the node connector being programmed (Eg. 'OF')
504      * @param nodeConnectorId
505      *            NodeConnector Identifier as specified by
506      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
507      *            (Eg. '2')
508      * @param propertyName
509      *            Name of the Property specified by
510      *            {@link org.opendaylight.controller.sal.core.Property} and its
511      *            extended classes
512      *            Property that can be configured is bandwidth
513      * @param propertyValue
514      *            Value of the Property specified by
515      *            {@link org.opendaylight.controller.sal.core.Property} and its
516      *            extended classes
517      * @return Response as dictated by the HTTP Response Status code
518      *
519      * <pre>
520      *
521      * Example:
522      *
523      * RequestURL:
524      * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
525      *
526      * </pre>
527      */
528
529     @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}/{propertyValue}")
530     @PUT
531     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
532     @StatusCodes({
533             @ResponseCode(code = 201, condition = "Operation successful"),
534             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
535             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
536             @ResponseCode(code = 409, condition = "Unable to add property due to cluster conflict"),
537             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
538     public Response addNodeConnectorProperty(
539             @Context UriInfo uriInfo,
540             @PathParam("containerName") String containerName,
541             @PathParam("nodeType") String nodeType,
542             @PathParam("nodeId") String nodeId,
543             @PathParam("nodeConnectorType") String nodeConnectorType,
544             @PathParam("nodeConnectorId") String nodeConnectorId,
545             @PathParam("propertyName") String propertyName,
546             @PathParam("propertyValue") String propertyValue) {
547
548         if (!isValidContainer(containerName)) {
549             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
550         }
551         if (!NorthboundUtils.isAuthorized(
552                 getUserName(), containerName, Privilege.WRITE, this)) {
553             throw new UnauthorizedException(
554                     "User is not authorized to perform this operation on container "
555                             + containerName);
556         }
557
558         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
559         if (switchManager == null) {
560             throw new ServiceUnavailableException("Switch Manager "
561                     + RestMessages.SERVICEUNAVAILABLE.toString());
562         }
563
564         handleNodeAvailability(containerName, nodeType, nodeId);
565         Node node = Node.fromString(nodeType, nodeId);
566
567         handleNodeConnectorAvailability(containerName, node, nodeConnectorType,
568                 nodeConnectorId);
569         NodeConnector nc = NodeConnector
570                 .fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
571
572         Property prop = switchManager.createProperty(propertyName, propertyValue);
573         if (prop == null) {
574             throw new ResourceNotFoundException(
575                     RestMessages.INVALIDDATA.toString());
576         }
577
578         Status ret = switchManager.addNodeConnectorProp(nc, prop);
579         if (ret.isSuccess()) {
580             return Response.created(uriInfo.getRequestUri()).build();
581         }
582         throw new InternalServerErrorException(ret.getDescription());
583     }
584
585     /**
586      * Delete a property of a node connector
587      *
588      * @param containerName
589      *            Name of the Container (Eg. 'default')
590      * @param nodeType
591      *            Type of the node being programmed (Eg. 'OF')
592      * @param nodeId
593      *            Node Identifier as specified by
594      *            {@link org.opendaylight.controller.sal.core.Node}
595      *            (Eg. '00:00:00:00:00:00:00:01')
596      * @param nodeConnectorType
597      *            Type of the node connector being programmed (Eg. 'OF')
598      * @param nodeConnectorId
599      *            NodeConnector Identifier as specified by
600      *            {@link org.opendaylight.controller.sal.core.NodeConnector}
601      *            (Eg. '1')
602      * @param propertyName
603      *            Name of the Property specified by
604      *            {@link org.opendaylight.controller.sal.core.Property} and its
605      *            extended classes. Property that can be deleted is bandwidth
606      * @return Response as dictated by the HTTP Response Status code
607      *
608      * <pre>
609      *
610      * Example:
611      *
612      * RequestURL:
613      * http://localhost:8080/controller/nb/v2/switch/default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
614      *
615      * </pre>
616      */
617
618     @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}")
619     @DELETE
620     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
621     @StatusCodes({
622             @ResponseCode(code = 200, condition = "Operation successful"),
623             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
624             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
625             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
626     public Response deleteNodeConnectorProperty(
627             @PathParam("containerName") String containerName,
628             @PathParam("nodeType") String nodeType,
629             @PathParam("nodeId") String nodeId,
630             @PathParam("nodeConnectorType") String nodeConnectorType,
631             @PathParam("nodeConnectorId") String nodeConnectorId,
632             @PathParam("propertyName") String propertyName) {
633
634         if (!isValidContainer(containerName)) {
635             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
636         }
637         if (!NorthboundUtils.isAuthorized(
638                 getUserName(), containerName, Privilege.WRITE, this)) {
639             throw new UnauthorizedException(
640                     "User is not authorized to perform this operation on container "
641                             + containerName);
642         }
643
644         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
645         if (switchManager == null) {
646             throw new ServiceUnavailableException("Switch Manager "
647                     + RestMessages.SERVICEUNAVAILABLE.toString());
648         }
649
650         handleNodeAvailability(containerName, nodeType, nodeId);
651         Node node = Node.fromString(nodeType, nodeId);
652
653         handleNodeConnectorAvailability(containerName, node, nodeConnectorType,
654                 nodeConnectorId);
655         NodeConnector nc = NodeConnector
656                 .fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
657         Status ret = switchManager.removeNodeConnectorProp(nc, propertyName);
658         if (ret.isSuccess()) {
659             NorthboundUtils.auditlog("Node Connector Property", username, "removed", nc + " from " + nodeConnectorId, containerName);
660             return Response.ok().build();
661         }
662         throw new ResourceNotFoundException(ret.getDescription());
663     }
664
665     /**
666      * Save the current switch configurations
667      *
668      * @param containerName
669      *            Name of the Container (Eg. 'default')
670      * @return Response as dictated by the HTTP Response Status code
671      *
672      * <pre>
673      *
674      * Example:
675      *
676      * RequestURL:
677      * http://localhost:8080/controller/nb/v2/switch/default/switch-config
678      *
679      * </pre>
680      */
681     @Path("/{containerName}/switch-config")
682     @POST
683     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
684     @StatusCodes({
685             @ResponseCode(code = 200, condition = "Operation successful"),
686             @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
687             @ResponseCode(code = 404, condition = "The containerName is not found"),
688             @ResponseCode(code = 500, condition = "Failed to save switch configuration. Failure Reason included in HTTP Error response"),
689             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
690     public Response saveSwitchConfig(
691             @PathParam("containerName") String containerName) {
692
693         if (!isValidContainer(containerName)) {
694             throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
695         }
696         if (!NorthboundUtils.isAuthorized(
697                 getUserName(), containerName, Privilege.WRITE, this)) {
698             throw new UnauthorizedException(
699                     "User is not authorized to perform this operation on container "
700                             + containerName);
701         }
702         ISwitchManager switchManager = getIfSwitchManagerService(containerName);
703         if (switchManager == null) {
704             throw new ServiceUnavailableException("Switch Manager "
705                     + RestMessages.SERVICEUNAVAILABLE.toString());
706         }
707
708         Status ret = switchManager.saveSwitchConfig();
709         if (ret.isSuccess()) {
710             return Response.ok().build();
711         }
712         throw new InternalServerErrorException(ret.getDescription());
713     }
714
715     private Node handleNodeAvailability(String containerName, String nodeType,
716             String nodeId) {
717
718         Node node = Node.fromString(nodeType, nodeId);
719         if (node == null) {
720             throw new ResourceNotFoundException(nodeId + " : "
721                     + RestMessages.NONODE.toString());
722         }
723
724         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
725                 ISwitchManager.class, containerName, this);
726
727         if (sm == null) {
728             throw new ServiceUnavailableException("Switch Manager "
729                     + RestMessages.SERVICEUNAVAILABLE.toString());
730         }
731
732         if (!sm.getNodes().contains(node)) {
733             throw new ResourceNotFoundException(node.toString() + " : "
734                     + RestMessages.NONODE.toString());
735         }
736         return node;
737     }
738
739     private void handleNodeConnectorAvailability(String containerName,
740             Node node, String nodeConnectorType, String nodeConnectorId) {
741
742         NodeConnector nc = NodeConnector.fromStringNoNode(nodeConnectorType,
743                 nodeConnectorId, node);
744         if (nc == null) {
745             throw new ResourceNotFoundException(nc + " : "
746                     + RestMessages.NORESOURCE.toString());
747         }
748
749         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
750                 ISwitchManager.class, containerName, this);
751
752         if (sm == null) {
753             throw new ServiceUnavailableException("Switch Manager "
754                     + RestMessages.SERVICEUNAVAILABLE.toString());
755         }
756
757         if (!sm.getNodeConnectors(node).contains(nc)) {
758             throw new ResourceNotFoundException(nc.toString() + " : "
759                     + RestMessages.NORESOURCE.toString());
760         }
761     }
762
763     private boolean isValidContainer(String containerName) {
764         if (containerName.equals(GlobalConstants.DEFAULT.toString())) {
765             return true;
766         }
767         IContainerManager containerManager = (IContainerManager) ServiceHelper
768                 .getGlobalInstance(IContainerManager.class, this);
769         if (containerManager == null) {
770             throw new InternalServerErrorException(
771                     RestMessages.INTERNALERROR.toString());
772         }
773         if (containerManager.getContainerNames().contains(containerName)) {
774             return true;
775        }
776         return false;
777     }
778
779 }