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