Merge pull request #1065 from i11/master

Add flavor create and delete API calls for openstack-nova
This commit is contained in:
Adrian Cole 2013-03-01 15:22:26 -08:00
commit 6a6214d1c5
6 changed files with 172 additions and 6 deletions

View File

@ -18,6 +18,9 @@
*/
package org.jclouds.openstack.nova.v2_0.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import javax.inject.Named;
@ -34,7 +37,7 @@ import com.google.common.base.Optional;
* A flavor is an available hardware configuration for a server. Each flavor has
* a unique combination of disk space and memory capacity.
*
* @author Jeremy Daggett
* @author Jeremy Daggett, Ilja Bobkevic
* @see <a href=
"http://docs.openstack.org/api/openstack-compute/2/content/List_Flavors-d1e4188.html"
/>
@ -139,9 +142,11 @@ public class Flavor extends Resource {
@ConstructorProperties({
"id", "name", "links", "ram", "disk", "vcpus", "swap", "rxtx_factor", "OS-FLV-EXT-DATA:ephemeral"
})
protected Flavor(String id, @Nullable String name, java.util.Set<Link> links, int ram, int disk, int vcpus,
protected Flavor(String id, String name, java.util.Set<Link> links, int ram, int disk, int vcpus,
@Nullable String swap, @Nullable Double rxtxFactor, @Nullable Integer ephemeral) {
super(id, name, links);
super(id, checkNotNull(name, "name"), links);
checkArgument(ram > 0, "Value of ram has to greater than 0");
checkArgument(vcpus > 0, "Value of vcpus has to greater than 0");
this.ram = ram;
this.disk = disk;
this.vcpus = vcpus;
@ -149,7 +154,7 @@ public class Flavor extends Resource {
this.rxtxFactor = Optional.fromNullable(rxtxFactor);
this.ephemeral = Optional.fromNullable(ephemeral);
}
public int getRam() {
return this.ram;
}

View File

@ -32,7 +32,7 @@ import org.jclouds.openstack.v2_0.options.PaginationOptions;
* @see <a href=
* "http://docs.openstack.org/api/openstack-compute/2/content/List_Flavors-d1e4188.html"
* />
* @author Jeremy Daggett
* @author Jeremy Daggett, Ilja Bobkevic
*/
public interface FlavorApi {
@ -63,4 +63,18 @@ public interface FlavorApi {
*/
Flavor get(String id);
/**
* Create flavor according to the provided object
*
* @param flavor - flavor object
* @return newly created flavor
*/
Flavor create(Flavor flavor);
/**
* Delete flavor with a given id
*
* @param id - flavor id
*/
void delete(String id);
}

View File

@ -20,13 +20,17 @@ package org.jclouds.openstack.nova.v2_0.features;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.collect.PagedIterable;
import org.jclouds.openstack.keystone.v2_0.KeystoneFallbacks.EmptyPaginatedCollectionOnNotFoundOr404;
import org.jclouds.openstack.keystone.v2_0.domain.PaginatedCollection;
@ -41,6 +45,8 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.annotations.WrapWith;
import com.google.common.util.concurrent.ListenableFuture;
@ -54,6 +60,7 @@ import com.google.common.util.concurrent.ListenableFuture;
* >docs</a>
* @author Jeremy Daggett TODO: Need a ListFlavorOptions class minDisk=minDiskInGB&
* minRam=minRamInMB& marker=markerID&limit=int
* @author Ilja Bobkevic
*/
@RequestFilters(AuthenticateRequest.class)
public interface FlavorAsyncApi {
@ -115,4 +122,25 @@ public interface FlavorAsyncApi {
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture<? extends Flavor> get(@PathParam("id") String id);
/**
* @see FlavorApi#create
*/
@Named("flavor:create")
@POST
@Unwrap
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/flavors")
ListenableFuture<? extends Flavor> create(@WrapWith("flavor") Flavor flavor);
/**
* @see FlavorApi#delete
*/
@Named("flavor:delete")
@DELETE
@Consumes
@Path("/flavors/{id}")
@Fallback(VoidOnNotFoundOr404.class)
ListenableFuture<Void> delete(@PathParam("id") String id);
}

View File

@ -22,10 +22,16 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.domain.Flavor;
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
import org.jclouds.openstack.nova.v2_0.parse.ParseCreateFlavorTest;
import org.jclouds.openstack.nova.v2_0.parse.ParseFlavorListTest;
import org.jclouds.openstack.nova.v2_0.parse.ParseFlavorTest;
import org.testng.annotations.Test;
@ -35,7 +41,7 @@ import com.google.common.collect.ImmutableSet;
/**
* Tests annotation parsing of {@code FlavorAsyncApi}
*
* @author Jeremy Daggett
* @author Jeremy Daggett, Ilja Bobkevic
*/
@Test(groups = "unit", testName = "FlavorApiExpectTest")
public class FlavorApiExpectTest extends BaseNovaApiExpectTest {
@ -114,5 +120,47 @@ public class FlavorApiExpectTest extends BaseNovaApiExpectTest {
assertNull(apiWhenNoFlavorsExist.getFlavorApiForZone("az-1.region-a.geo-1").get("123"));
}
public void testCreateFlavor200() throws Exception {
ParseCreateFlavorTest parser = new ParseCreateFlavorTest();
HttpRequest listFlavors = HttpRequest
.builder()
.method(HttpMethod.POST)
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors")
.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
.addHeader("X-Auth-Token", authToken)
.payload(payloadFromResource(parser.resource())).build();
HttpResponse listFlavorsResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource(parser.resource())).build();
NovaApi api = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, listFlavors, listFlavorsResponse);
assertEquals(
api.getFlavorApiForZone("az-1.region-a.geo-1").create(Flavor.builder()
.id("1cb47a44-9b84-4da4-bf81-c1976e8414ab")
.name("128 MB Server").ram(128).vcpus(1)
.disk(10).build())
.toString(), parser.expected().toString());
}
public void testDeleteFlavor202() throws Exception {
String flavorId = "1cb47a44-9b84-4da4-bf81-c1976e8414ab";
HttpRequest updateMetadata = HttpRequest
.builder()
.method(HttpMethod.DELETE)
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/flavors/" + flavorId)
.addHeader(HttpHeaders.ACCEPT, MediaType.WILDCARD)
.addHeader("X-Auth-Token", authToken)
.build();
HttpResponse updateMetadataResponse = HttpResponse.builder().statusCode(204).build();
NovaApi api = requestsSendResponses(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess, updateMetadata, updateMetadataResponse);
api.getFlavorApiForZone("az-1.region-a.geo-1").delete(flavorId);
}
}

View File

@ -0,0 +1,61 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.nova.v2_0.parse;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
import org.jclouds.openstack.nova.v2_0.domain.Flavor;
import org.jclouds.openstack.nova.v2_0.features.FlavorApiExpectTest;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* @see FlavorApiExpectTest
* @author Ilja Bobkevic
*/
@Test(groups = "unit", testName = "ParseCreateFlavorTest")
public class ParseCreateFlavorTest extends BaseItemParserTest<Flavor> {
@Override
public String resource() {
return "/flavor_new.json";
}
@Override
@SelectJson("flavor")
@Consumes(MediaType.APPLICATION_JSON)
public Flavor expected() {
return Flavor.builder()
.id("1cb47a44-9b84-4da4-bf81-c1976e8414ab")
.name("128 MB Server").ram(128).vcpus(1)
.disk(10).build();
}
@Override
protected Injector injector() {
return Guice.createInjector(new NovaParserModule(), new GsonModule());
}
}

View File

@ -0,0 +1,10 @@
{
"flavor" : {
"id" : "1cb47a44-9b84-4da4-bf81-c1976e8414ab",
"name" : "128 MB Server",
"ram" : 128,
"disk" : 10,
"vcpus" : 1,
"links" : []
}
}