a9f210e0eceebdad88e783640ae2bc120b6b7b31
[controller.git] / opendaylight / northbound / hosttracker / src / main / java / org / opendaylight / controller / hosttracker / northbound / HostTrackerNorthbound.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.hosttracker.northbound;
10
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.security.Principal;
14 import java.util.List;
15 import javax.ws.rs.Consumes;
16 import javax.ws.rs.DELETE;
17 import javax.ws.rs.DefaultValue;
18 import javax.ws.rs.GET;
19 import javax.ws.rs.POST;
20 import javax.ws.rs.Path;
21 import javax.ws.rs.PathParam;
22 import javax.ws.rs.Produces;
23 import javax.ws.rs.QueryParam;
24 import javax.ws.rs.core.Context;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
27 import javax.ws.rs.core.SecurityContext;
28
29 import org.codehaus.enunciate.jaxrs.ResponseCode;
30 import org.codehaus.enunciate.jaxrs.StatusCodes;
31 import org.codehaus.enunciate.jaxrs.TypeHint;
32 import org.opendaylight.controller.containermanager.IContainerManager;
33 import org.opendaylight.controller.hosttracker.IfIptoHost;
34 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
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.ResourceConflictException;
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.exception.UnsupportedMediaTypeException;
42 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
43 import org.opendaylight.controller.sal.core.Node;
44 import org.opendaylight.controller.sal.core.NodeConnector;
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
51 import org.opendaylight.controller.sal.authorization.Privilege;
52
53 /**
54  * Host Tracker Northbound REST APIs.<br>
55  * This class provides REST APIs to track host location in a network. Host
56  * Location is represented by Host node connector which is essentially a logical
57  * entity that represents a Switch/Port. A host is represented by it's
58  * IP-address and mac-address.
59  * 
60  * <br>
61  * <br>
62  * Authentication scheme : <b>HTTP Basic</b><br>
63  * Authentication realm : <b>opendaylight</b><br>
64  * Transport : <b>HTTP and HTTPS</b><br>
65  * <br>
66  * HTTPS Authentication is disabled by default. Administrator can enable it in
67  * tomcat-server.xml after adding a proper keystore / SSL certificate from a
68  * trusted authority.<br>
69  * More info :
70  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
71  * 
72  */
73
74 @Path("/")
75 public class HostTrackerNorthbound {
76
77     private String username;
78
79     @Context
80     public void setSecurityContext(SecurityContext context) {
81         username = context.getUserPrincipal().getName();
82     }
83
84     protected String getUserName() {
85         return username;
86     }
87
88     private IfIptoHost getIfIpToHostService(String containerName) {
89         IContainerManager containerManager = (IContainerManager) ServiceHelper
90                 .getGlobalInstance(IContainerManager.class, this);
91         if (containerManager == null) {
92             throw new ServiceUnavailableException("Container "
93                     + RestMessages.SERVICEUNAVAILABLE.toString());
94         }
95
96         boolean found = false;
97         List<String> containerNames = containerManager.getContainerNames();
98         for (String cName : containerNames) {
99             if (cName.trim().equalsIgnoreCase(containerName.trim())) {
100                 found = true;
101             }
102         }
103
104         if (found == false) {
105             throw new ResourceNotFoundException(containerName + " "
106                     + RestMessages.NOCONTAINER.toString());
107         }
108
109         IfIptoHost hostTracker = (IfIptoHost) ServiceHelper.getInstance(
110                 IfIptoHost.class, containerName, this);
111
112         if (hostTracker == null) {
113             throw new ServiceUnavailableException("Host Tracker "
114                     + RestMessages.SERVICEUNAVAILABLE.toString());
115         }
116
117         return hostTracker;
118     }
119
120     /**
121      * Returns a list of all Hosts : both configured via PUT API and dynamically
122      * learnt on the network.
123      * 
124      * @param containerName
125      *            Name of the Container. The Container name for the base
126      *            controller is "default".
127      * @return List of Active Hosts.
128      */
129     @Path("/{containerName}")
130     @GET
131     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
132     @TypeHint(Hosts.class)
133     @StatusCodes({
134             @ResponseCode(code = 200, condition = "Operation successful"),
135             @ResponseCode(code = 404, condition = "The containerName is not found"),
136             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
137     public Hosts getActiveHosts(@PathParam("containerName") String containerName) {
138  
139         if (!NorthboundUtils.isAuthorized(
140                 getUserName(), containerName, Privilege.READ, this)) {
141             throw new UnauthorizedException(
142                     "User is not authorized to perform this operation on container "
143                             + containerName);
144         }
145         IfIptoHost hostTracker = getIfIpToHostService(containerName);
146         if (hostTracker == null) {
147             throw new ServiceUnavailableException("Host Tracker "
148                     + RestMessages.SERVICEUNAVAILABLE.toString());
149         }
150
151         return new Hosts(hostTracker.getAllHosts());
152     }
153
154     /**
155      * Returns a list of Hosts that are statically configured and are connected
156      * to a NodeConnector that is down.
157      * 
158      * @param containerName
159      *            Name of the Container. The Container name for the base
160      *            controller is "default".
161      * @return List of inactive Hosts.
162      */
163     @Path("/{containerName}/inactive")
164     @GET
165     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
166     @TypeHint(Hosts.class)
167     @StatusCodes({
168             @ResponseCode(code = 200, condition = "Operation successful"),
169             @ResponseCode(code = 404, condition = "The containerName is not found"),
170             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
171     public Hosts getInactiveHosts(
172             @PathParam("containerName") String containerName) {
173         if (!NorthboundUtils.isAuthorized(
174                 getUserName(), containerName, Privilege.READ, this)) {
175             throw new UnauthorizedException(
176                     "User is not authorized to perform this operation on container "
177                             + containerName);
178         }
179         IfIptoHost hostTracker = getIfIpToHostService(containerName);
180         if (hostTracker == null) {
181             throw new ServiceUnavailableException("Host Tracker "
182                     + RestMessages.SERVICEUNAVAILABLE.toString());
183         }
184
185         return new Hosts(hostTracker.getInactiveStaticHosts());
186     }
187
188     /**
189      * Returns a host that matches the IP Address value passed as parameter.
190      * 
191      * @param containerName
192      *            Name of the Container. The Container name for the base
193      *            controller is "default".
194      * @param networkAddress
195      *            IP Address being looked up
196      * @return host that matches the IP Address
197      */
198     @Path("/{containerName}/{networkAddress}")
199     @GET
200     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
201     @TypeHint(HostNodeConnector.class)
202     @StatusCodes({
203             @ResponseCode(code = 200, condition = "Operation successful"),
204             @ResponseCode(code = 404, condition = "The containerName is not found"),
205             @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
206             @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
207     public HostNodeConnector getHostDetails(
208             @PathParam("containerName") String containerName,
209             @PathParam("networkAddress") String networkAddress) {
210         if (!NorthboundUtils.isAuthorized(
211                 getUserName(), containerName, Privilege.READ, this)) {
212             throw new UnauthorizedException(
213                     "User is not authorized to perform this operation on container "
214                             + containerName);
215         }
216         IfIptoHost hostTracker = getIfIpToHostService(containerName);
217         if (hostTracker == null) {
218             throw new ServiceUnavailableException("Host Tracker "
219                     + RestMessages.SERVICEUNAVAILABLE.toString());
220         }
221
222         InetAddress ip;
223         try {
224             ip = InetAddress.getByName(networkAddress);
225         } catch (UnknownHostException e) {
226             throw new UnsupportedMediaTypeException(networkAddress + " "
227                     + RestMessages.INVALIDADDRESS.toString());
228         }
229         for (HostNodeConnector host : hostTracker.getAllHosts()) {
230             if (host.getNetworkAddress().equals(ip)) {
231                 return host;
232             }
233         }
234         throw new ResourceNotFoundException(RestMessages.NOHOST.toString());
235     }
236
237     /**
238      * Add a Static Host configuration
239      * 
240      * @param containerName
241      *            Name of the Container. The Container name for the base
242      *            controller is "default".
243      * @param networkAddress
244      *            Host IP Address
245      * @param dataLayerAddress
246      *            Host L2 data-layer address.
247      * @param nodeType
248      *            Node Type as specifid by Node class
249      * @param nodeId
250      *            Node Identifier as specifid by Node class
251      * @param nodeConnectorType
252      *            Port Type as specified by NodeConnector class
253      * @param nodeConnectorId
254      *            Port Identifier as specified by NodeConnector class
255      * @param vlan
256      *            Vlan number
257      * @return Response as dictated by the HTTP Response Status code
258      */
259
260     @Path("/{containerName}/{networkAddress}")
261     @POST
262     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
263     @StatusCodes({
264             @ResponseCode(code = 201, condition = "Flow Config processed successfully"),
265             @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
266             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
267             @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
268             @ResponseCode(code = 500, condition = "Failed to create Static Host entry. Failure Reason included in HTTP Error response"),
269             @ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
270     public Response addHost(@PathParam("containerName") String containerName,
271             @PathParam("networkAddress") String networkAddress,
272             @QueryParam("dataLayerAddress") String dataLayerAddress,
273             @QueryParam("nodeType") String nodeType,
274             @QueryParam("nodeId") String nodeId,
275             @QueryParam("nodeConnectorType") String nodeConnectorType,
276             @QueryParam("nodeConnectorId") String nodeConnectorId,
277             @DefaultValue("0") @QueryParam("vlan") String vlan) {
278
279         if (!NorthboundUtils.isAuthorized(
280                 getUserName(), containerName, Privilege.WRITE, this)) {
281             throw new UnauthorizedException(
282                     "User is not authorized to perform this operation on container "
283                             + containerName);
284         }
285         handleDefaultDisabled(containerName);
286
287         IfIptoHost hostTracker = getIfIpToHostService(containerName);
288         if (hostTracker == null) {
289             throw new ServiceUnavailableException("Host Tracker "
290                     + RestMessages.SERVICEUNAVAILABLE.toString());
291         }
292
293         Node node = handleNodeAvailability(containerName, nodeType, nodeId);
294         if (node == null) {
295             throw new InternalServerErrorException(
296                     RestMessages.NONODE.toString());
297         }
298
299         try {
300             InetAddress.getByName(networkAddress);
301         } catch (UnknownHostException e) {
302             throw new UnsupportedMediaTypeException(networkAddress + " "
303                     + RestMessages.INVALIDADDRESS.toString());
304         }
305         NodeConnector nc = NodeConnector.fromStringNoNode(nodeConnectorType,
306                 nodeConnectorId, node);
307         if (nc == null) {
308             throw new ResourceNotFoundException(nodeConnectorType + "|"
309                     + nodeConnectorId + " : " + RestMessages.NONODE.toString());
310         }
311         Status status = hostTracker.addStaticHost(networkAddress,
312                 dataLayerAddress, nc, vlan);
313         if (status.isSuccess()) {
314             return Response.status(Response.Status.CREATED).build();
315         } else if (status.getCode().equals(StatusCode.BADREQUEST)) {
316             throw new UnsupportedMediaTypeException(status.getDescription());
317         }
318         throw new InternalServerErrorException(status.getDescription());
319     }
320
321     /**
322      * Delete a Static Host configuration
323      * 
324      * @param containerName
325      *            Name of the Container. The Container name for the base
326      *            controller is "default".
327      * @param networkAddress
328      *            IP Address
329      * @return Response as dictated by the HTTP Response code.
330      */
331
332     @Path("/{containerName}/{networkAddress}")
333     @DELETE
334     @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
335     @StatusCodes({
336             @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
337             @ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
338             @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
339             @ResponseCode(code = 415, condition = "Invalid IP Address passed in networkAddress parameter"),
340             @ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
341             @ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
342     public Response deleteFlow(
343             @PathParam(value = "containerName") String containerName,
344             @PathParam(value = "networkAddress") String networkAddress) {
345  
346         if (!NorthboundUtils.isAuthorized(
347                 getUserName(), containerName, Privilege.WRITE, this)) {
348             throw new UnauthorizedException(
349                     "User is not authorized to perform this operation on container "
350                             + containerName);
351         }
352         handleDefaultDisabled(containerName);
353         IfIptoHost hostTracker = getIfIpToHostService(containerName);
354         if (hostTracker == null) {
355             throw new ServiceUnavailableException("Host Tracker "
356                     + RestMessages.SERVICEUNAVAILABLE.toString());
357         }
358
359         try {
360             InetAddress.getByName(networkAddress);
361         } catch (UnknownHostException e) {
362             throw new UnsupportedMediaTypeException(networkAddress + " "
363                     + RestMessages.INVALIDADDRESS.toString());
364         }
365
366         Status status = hostTracker.removeStaticHost(networkAddress);
367         if (status.isSuccess()) {
368             return Response.ok().build();
369         }
370         throw new InternalServerErrorException(status.getDescription());
371
372     }
373
374     private void handleDefaultDisabled(String containerName) {
375         IContainerManager containerManager = (IContainerManager) ServiceHelper
376                 .getGlobalInstance(IContainerManager.class, this);
377         if (containerManager == null) {
378             throw new InternalServerErrorException(
379                     RestMessages.INTERNALERROR.toString());
380         }
381         if (containerName.equals(GlobalConstants.DEFAULT.toString())
382                 && containerManager.hasNonDefaultContainer()) {
383             throw new ResourceConflictException(
384                     RestMessages.DEFAULTDISABLED.toString());
385         }
386     }
387
388     private Node handleNodeAvailability(String containerName, String nodeType,
389             String nodeId) {
390
391         Node node = Node.fromString(nodeType, nodeId);
392         if (node == null) {
393             throw new ResourceNotFoundException(nodeId + " : "
394                     + RestMessages.NONODE.toString());
395         }
396
397         ISwitchManager sm = (ISwitchManager) ServiceHelper.getInstance(
398                 ISwitchManager.class, containerName, this);
399
400         if (sm == null) {
401             throw new ServiceUnavailableException("Switch Manager "
402                     + RestMessages.SERVICEUNAVAILABLE.toString());
403         }
404
405         if (!sm.getNodes().contains(node)) {
406             throw new ResourceNotFoundException(node.toString() + " : "
407                     + RestMessages.NONODE.toString());
408         }
409         return node;
410     }
411
412 }