diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java index 35030a79fe..2be1a24562 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java @@ -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 @@ -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 links, int ram, int disk, int vcpus, + protected Flavor(String id, String name, java.util.Set 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; } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java index 2043de3d6c..f6f211697e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java @@ -32,7 +32,7 @@ import org.jclouds.openstack.v2_0.options.PaginationOptions; * @see - * @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); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java index ac87968d20..7bb02f5b36 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java @@ -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 * @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 get(@PathParam("id") String id); + + /** + * @see FlavorApi#create + */ + @Named("flavor:create") + @POST + @Unwrap + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("/flavors") + ListenableFuture create(@WrapWith("flavor") Flavor flavor); + + /** + * @see FlavorApi#delete + */ + @Named("flavor:delete") + @DELETE + @Consumes + @Path("/flavors/{id}") + @Fallback(VoidOnNotFoundOr404.class) + ListenableFuture delete(@PathParam("id") String id); } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiExpectTest.java index e1891e5349..e5474dc908 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiExpectTest.java @@ -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); + } } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseCreateFlavorTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseCreateFlavorTest.java new file mode 100644 index 0000000000..23f641f812 --- /dev/null +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseCreateFlavorTest.java @@ -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 { + + @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()); + } +} diff --git a/apis/openstack-nova/src/test/resources/flavor_new.json b/apis/openstack-nova/src/test/resources/flavor_new.json new file mode 100644 index 0000000000..5356f8c824 --- /dev/null +++ b/apis/openstack-nova/src/test/resources/flavor_new.json @@ -0,0 +1,10 @@ +{ + "flavor" : { + "id" : "1cb47a44-9b84-4da4-bf81-c1976e8414ab", + "name" : "128 MB Server", + "ram" : 128, + "disk" : 10, + "vcpus" : 1, + "links" : [] + } +}