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