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