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