Merge "Add Pagination to Neutron Networks Northbound API"
[controller.git] / opendaylight / northbound / networkconfiguration / neutron / src / main / java / org / opendaylight / controller / networkconfig / neutron / northbound / NeutronNetworksNorthbound.java
1 /*
2  * Copyright IBM Corporation, 2013.  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.networkconfig.neutron.northbound;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17
18 import javax.ws.rs.Consumes;
19 import javax.ws.rs.DELETE;
20 import javax.ws.rs.DefaultValue;
21 import javax.ws.rs.GET;
22 import javax.ws.rs.POST;
23 import javax.ws.rs.PUT;
24 import javax.ws.rs.Path;
25 import javax.ws.rs.PathParam;
26 import javax.ws.rs.Produces;
27 import javax.ws.rs.QueryParam;
28 import javax.ws.rs.core.Context;
29 import javax.ws.rs.core.MediaType;
30 import javax.ws.rs.core.UriInfo;
31 import javax.ws.rs.core.Response;
32
33 import org.codehaus.enunciate.jaxrs.ResponseCode;
34 import org.codehaus.enunciate.jaxrs.StatusCodes;
35 import org.codehaus.enunciate.jaxrs.TypeHint;
36 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkAware;
37 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
38 import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
39 import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
40 import org.opendaylight.controller.northbound.commons.RestMessages;
41 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
42 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
43 import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
44 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
45 import org.opendaylight.controller.sal.utils.ServiceHelper;
46
47 /**
48  * Neutron Northbound REST APIs for Network.<br>
49  * This class provides REST APIs for managing neutron Networks
50  *
51  * <br>
52  * <br>
53  * Authentication scheme : <b>HTTP Basic</b><br>
54  * Authentication realm : <b>opendaylight</b><br>
55  * Transport : <b>HTTP and HTTPS</b><br>
56  * <br>
57  * HTTPS Authentication is disabled by default. Administrator can enable it in
58  * tomcat-server.xml after adding a proper keystore / SSL certificate from a
59  * trusted authority.<br>
60  * More info :
61  * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
62  *
63  */
64
65 @Path("/networks")
66 public class NeutronNetworksNorthbound {
67
68     @Context
69     UriInfo uriInfo;
70
71     private NeutronNetwork extractFields(NeutronNetwork o, List<String> fields) {
72         return o.extractFields(fields);
73     }
74
75     /**
76      * Returns a list of all Networks */
77
78     @GET
79     @Produces({ MediaType.APPLICATION_JSON })
80     //@TypeHint(OpenStackNetworks.class)
81     @StatusCodes({
82         @ResponseCode(code = 200, condition = "Operation successful"),
83         @ResponseCode(code = 401, condition = "Unauthorized")})
84     public Response listNetworks(
85             // return fields
86             @QueryParam("fields") List<String> fields,
87             // note: openstack isn't clear about filtering on lists, so we aren't handling them
88             @QueryParam("id") String queryID,
89             @QueryParam("name") String queryName,
90             @QueryParam("admin_state_up") String queryAdminStateUp,
91             @QueryParam("status") String queryStatus,
92             @QueryParam("shared") String queryShared,
93             @QueryParam("tenant_id") String queryTenantID,
94             @QueryParam("router_external") String queryRouterExternal,
95             @QueryParam("provider_network_type") String queryProviderNetworkType,
96             @QueryParam("provider_physical_network") String queryProviderPhysicalNetwork,
97             @QueryParam("provider_segmentation_id") String queryProviderSegmentationID,
98             // pagination
99             @QueryParam("limit") Integer limit,
100             @QueryParam("marker") String marker,
101             @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
102             // sorting not supported
103             ) {
104         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
105         if (networkInterface == null) {
106             throw new ServiceUnavailableException("Network CRUD Interface "
107                     + RestMessages.SERVICEUNAVAILABLE.toString());
108         }
109         List<NeutronNetwork> allNetworks = networkInterface.getAllNetworks();
110         List<NeutronNetwork> ans = new ArrayList<NeutronNetwork>();
111         Iterator<NeutronNetwork> i = allNetworks.iterator();
112         while (i.hasNext()) {
113             NeutronNetwork oSN = i.next();
114             //match filters: TODO provider extension
115             Boolean bAdminStateUp = null;
116             Boolean bShared = null;
117             Boolean bRouterExternal = null;
118             if (queryAdminStateUp != null) {
119                 bAdminStateUp = Boolean.valueOf(queryAdminStateUp);
120             }
121             if (queryShared != null) {
122                 bShared = Boolean.valueOf(queryShared);
123             }
124             if (queryRouterExternal != null) {
125                 bRouterExternal = Boolean.valueOf(queryRouterExternal);
126             }
127             if ((queryID == null || queryID.equals(oSN.getID())) &&
128                     (queryName == null || queryName.equals(oSN.getNetworkName())) &&
129                     (bAdminStateUp == null || bAdminStateUp.booleanValue() == oSN.isAdminStateUp()) &&
130                     (queryStatus == null || queryStatus.equals(oSN.getStatus())) &&
131                     (bShared == null || bShared.booleanValue() == oSN.isShared()) &&
132                     (bRouterExternal == null || bRouterExternal.booleanValue() == oSN.isRouterExternal()) &&
133                     (queryTenantID == null || queryTenantID.equals(oSN.getTenantID()))) {
134                 if (fields.size() > 0) {
135                     ans.add(extractFields(oSN,fields));
136                 } else {
137                     ans.add(oSN);
138                 }
139             }
140         }
141
142         Comparator<NeutronNetwork> neutronNetworkComparator = new Comparator<NeutronNetwork>() {
143             @Override
144             public int compare(NeutronNetwork o1, NeutronNetwork o2) {
145                 return o1.getID().compareTo(o2.getID());
146             }
147         };
148
149         Collections.sort(ans, neutronNetworkComparator);
150
151         if (limit != null && ans.size() > 1) {
152             List<NeutronPageLink> links = new ArrayList<>();
153             Integer startPos = null;
154             String startMarker;
155             String endMarker;
156             Boolean firstPage = false;
157             Boolean lastPage = false;
158
159             if (marker == null) {
160                 startPos = 0;
161             }
162
163             else {
164
165                 NeutronNetwork markerNetwork = new NeutronNetwork();
166                 markerNetwork.setNetworkUUID(marker);
167
168                 startPos = Collections.binarySearch(ans, markerNetwork, neutronNetworkComparator);
169
170                 if (!pageReverse){
171                     startPos = startPos + 1;
172                 }
173                 else {
174                     startPos = startPos - limit;
175                 }
176
177             }
178
179             if (startPos == null) {
180                 throw new ResourceNotFoundException("UUID for marker:" + marker + " could not be found");
181             }
182
183             if (startPos == 0){
184                 firstPage = true;
185             }
186
187             if (startPos + limit >= ans.size()) {
188                 ans = ans.subList(startPos, ans.size());
189                 startMarker = ans.get(0).getID();
190                 endMarker = ans.get(ans.size() - 1).getID();
191                 lastPage = true;
192             }
193             else if (startPos < 0) {
194                 if (startPos + limit > 0) {
195                     ans = ans.subList(0, startPos + limit);
196                     startMarker = ans.get(0).getID();
197                     endMarker = ans.get(ans.size() - 1).getID();
198                     firstPage = true;
199                 }
200                 else {
201                     throw new BadRequestException("Requested page is out of bounds. Please check the supplied limit and marker");
202                 }
203             }
204             else {
205                 ans = ans.subList(startPos, startPos + limit);
206                 startMarker = ans.get(0).getID();
207                 endMarker = ans.get(limit-1).getID();
208             }
209
210             if (!lastPage) {
211                 NeutronPageLink next = new NeutronPageLink();
212                 next.setRef("next");
213                 next.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + endMarker);
214                 links.add(next);
215             }
216
217             if (!firstPage) {
218                 NeutronPageLink previous = new NeutronPageLink();
219                 previous.setRef("previous");
220                 previous.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + startMarker + "&page_reverse=True");
221                 links.add(previous);
222             }
223
224             return Response.status(200).entity(new PaginatedNeutronNetworkRequest(ans, links)).build();
225         }
226
227     return Response.status(200).entity(new NeutronNetworkRequest(ans)).build();
228
229     }
230
231     /**
232      * Returns a specific Network */
233
234     @Path("{netUUID}")
235     @GET
236     @Produces({ MediaType.APPLICATION_JSON })
237     //@TypeHint(OpenStackNetworks.class)
238     @StatusCodes({
239         @ResponseCode(code = 200, condition = "Operation successful"),
240         @ResponseCode(code = 401, condition = "Unauthorized"),
241         @ResponseCode(code = 404, condition = "Not Found") })
242     public Response showNetwork(
243             @PathParam("netUUID") String netUUID,
244             // return fields
245             @QueryParam("fields") List<String> fields
246             ) {
247         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
248         if (networkInterface == null) {
249             throw new ServiceUnavailableException("Network CRUD Interface "
250                     + RestMessages.SERVICEUNAVAILABLE.toString());
251         }
252         if (!networkInterface.networkExists(netUUID)) {
253             throw new ResourceNotFoundException("network UUID does not exist.");
254         }
255         if (fields.size() > 0) {
256             NeutronNetwork ans = networkInterface.getNetwork(netUUID);
257             return Response.status(200).entity(
258                     new NeutronNetworkRequest(extractFields(ans, fields))).build();
259         } else {
260             return Response.status(200).entity(
261                     new NeutronNetworkRequest(networkInterface.getNetwork(netUUID))).build();
262         }
263     }
264
265     /**
266      * Creates new Networks */
267     @POST
268     @Produces({ MediaType.APPLICATION_JSON })
269     @Consumes({ MediaType.APPLICATION_JSON })
270     @TypeHint(NeutronNetwork.class)
271     @StatusCodes({
272         @ResponseCode(code = 201, condition = "Created"),
273         @ResponseCode(code = 400, condition = "Bad Request"),
274         @ResponseCode(code = 401, condition = "Unauthorized") })
275     public Response createNetworks(final NeutronNetworkRequest input) {
276         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
277         if (networkInterface == null) {
278             throw new ServiceUnavailableException("Network CRUD Interface "
279                     + RestMessages.SERVICEUNAVAILABLE.toString());
280         }
281         if (input.isSingleton()) {
282             NeutronNetwork singleton = input.getSingleton();
283
284             /*
285              * network ID can't already exist
286              */
287             if (networkInterface.networkExists(singleton.getID())) {
288                 throw new BadRequestException("network UUID already exists");
289             }
290
291             Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
292             if (instances != null) {
293                 for (Object instance : instances) {
294                     INeutronNetworkAware service = (INeutronNetworkAware) instance;
295                     int status = service.canCreateNetwork(singleton);
296                     if (status < 200 || status > 299) {
297                         return Response.status(status).build();
298                     }
299                 }
300             }
301
302             // add network to cache
303             singleton.initDefaults();
304             networkInterface.addNetwork(singleton);
305             if (instances != null) {
306                 for (Object instance : instances) {
307                     INeutronNetworkAware service = (INeutronNetworkAware) instance;
308                     service.neutronNetworkCreated(singleton);
309                 }
310             }
311
312         } else {
313             List<NeutronNetwork> bulk = input.getBulk();
314             Iterator<NeutronNetwork> i = bulk.iterator();
315             HashMap<String, NeutronNetwork> testMap = new HashMap<String, NeutronNetwork>();
316             Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
317             while (i.hasNext()) {
318                 NeutronNetwork test = i.next();
319
320                 /*
321                  * network ID can't already exist, nor can there be an entry for this UUID
322                  * already in this bulk request
323                  */
324                 if (networkInterface.networkExists(test.getID())) {
325                     throw new BadRequestException("network UUID already exists");
326                 }
327                 if (testMap.containsKey(test.getID())) {
328                     throw new BadRequestException("network UUID already exists");
329                 }
330                 if (instances != null) {
331                     for (Object instance: instances) {
332                         INeutronNetworkAware service = (INeutronNetworkAware) instance;
333                         int status = service.canCreateNetwork(test);
334                         if (status < 200 || status > 299) {
335                             return Response.status(status).build();
336                         }
337                     }
338                 }
339                 testMap.put(test.getID(),test);
340             }
341
342             // now that everything passed, add items to the cache
343             i = bulk.iterator();
344             while (i.hasNext()) {
345                 NeutronNetwork test = i.next();
346                 test.initDefaults();
347                 networkInterface.addNetwork(test);
348                 if (instances != null) {
349                     for (Object instance: instances) {
350                         INeutronNetworkAware service = (INeutronNetworkAware) instance;
351                         service.neutronNetworkCreated(test);
352                     }
353                 }
354             }
355         }
356         return Response.status(201).entity(input).build();
357     }
358
359     /**
360      * Updates a Network */
361     @Path("{netUUID}")
362     @PUT
363     @Produces({ MediaType.APPLICATION_JSON })
364     @Consumes({ MediaType.APPLICATION_JSON })
365     //@TypeHint(OpenStackNetworks.class)
366     @StatusCodes({
367         @ResponseCode(code = 200, condition = "Operation successful"),
368         @ResponseCode(code = 400, condition = "Bad Request"),
369         @ResponseCode(code = 403, condition = "Forbidden"),
370         @ResponseCode(code = 404, condition = "Not Found"), })
371     public Response updateNetwork(
372             @PathParam("netUUID") String netUUID, final NeutronNetworkRequest input
373             ) {
374         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
375         if (networkInterface == null) {
376             throw new ServiceUnavailableException("Network CRUD Interface "
377                     + RestMessages.SERVICEUNAVAILABLE.toString());
378         }
379
380         /*
381          * network has to exist and only a single delta is supported
382          */
383         if (!networkInterface.networkExists(netUUID)) {
384             throw new ResourceNotFoundException("network UUID does not exist.");
385         }
386         if (!input.isSingleton()) {
387             throw new BadRequestException("only singleton edits supported");
388         }
389         NeutronNetwork delta = input.getSingleton();
390
391         /*
392          * transitions forbidden by Neutron
393          */
394         if (delta.getID() != null || delta.getTenantID() != null ||
395                 delta.getStatus() != null) {
396             throw new BadRequestException("attribute edit blocked by Neutron");
397         }
398
399         Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
400         if (instances != null) {
401             for (Object instance : instances) {
402                 INeutronNetworkAware service = (INeutronNetworkAware) instance;
403                 NeutronNetwork original = networkInterface.getNetwork(netUUID);
404                 int status = service.canUpdateNetwork(delta, original);
405                 if (status < 200 || status > 299) {
406                     return Response.status(status).build();
407                 }
408             }
409         }
410
411         // update network object and return the modified object
412                 networkInterface.updateNetwork(netUUID, delta);
413                 NeutronNetwork updatedSingleton = networkInterface.getNetwork(netUUID);
414                 if (instances != null) {
415                     for (Object instance : instances) {
416                         INeutronNetworkAware service = (INeutronNetworkAware) instance;
417                         service.neutronNetworkUpdated(updatedSingleton);
418                     }
419                 }
420                 return Response.status(200).entity(
421                         new NeutronNetworkRequest(networkInterface.getNetwork(netUUID))).build();
422     }
423
424     /**
425      * Deletes a Network */
426
427     @Path("{netUUID}")
428     @DELETE
429     @StatusCodes({
430         @ResponseCode(code = 204, condition = "No Content"),
431         @ResponseCode(code = 401, condition = "Unauthorized"),
432         @ResponseCode(code = 404, condition = "Not Found"),
433         @ResponseCode(code = 409, condition = "Network In Use") })
434     public Response deleteNetwork(
435             @PathParam("netUUID") String netUUID) {
436         INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
437         if (networkInterface == null) {
438             throw new ServiceUnavailableException("Network CRUD Interface "
439                     + RestMessages.SERVICEUNAVAILABLE.toString());
440         }
441
442         /*
443          * network has to exist and not be in use before it can be removed
444          */
445         if (!networkInterface.networkExists(netUUID)) {
446             throw new ResourceNotFoundException("network UUID does not exist.");
447         }
448         if (networkInterface.networkInUse(netUUID)) {
449             throw new ResourceConflictException("Network ID in use");
450         }
451
452         NeutronNetwork singleton = networkInterface.getNetwork(netUUID);
453         Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
454         if (instances != null) {
455             for (Object instance : instances) {
456                 INeutronNetworkAware service = (INeutronNetworkAware) instance;
457                 int status = service.canDeleteNetwork(singleton);
458                 if (status < 200 || status > 299) {
459                     return Response.status(status).build();
460                 }
461             }
462         }
463         networkInterface.removeNetwork(netUUID);
464         if (instances != null) {
465             for (Object instance : instances) {
466                 INeutronNetworkAware service = (INeutronNetworkAware) instance;
467                 service.neutronNetworkDeleted(singleton);
468             }
469         }
470         return Response.status(204).build();
471     }
472 }