Issue 77: added support for flavors

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1635 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-17 13:41:02 +00:00
parent b25f3fbed7
commit f5e52178f9
10 changed files with 380 additions and 40 deletions

View File

@ -30,7 +30,9 @@ import java.util.concurrent.Future;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import org.jclouds.rackspace.cloudservers.domain.Flavor;
import org.jclouds.rackspace.cloudservers.domain.Server; import org.jclouds.rackspace.cloudservers.domain.Server;
import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse;
import org.jclouds.rackspace.filters.AuthenticateRequest; import org.jclouds.rackspace.filters.AuthenticateRequest;
import org.jclouds.rest.Query; import org.jclouds.rest.Query;
@ -61,24 +63,48 @@ public interface CloudServersConnection {
@ResponseParser(ParseServerListFromGsonResponse.class) @ResponseParser(ParseServerListFromGsonResponse.class)
@Query(key = "format", value = "json") @Query(key = "format", value = "json")
@Path("/servers") @Path("/servers")
// TODO: Error Response Code(s): cloudServersFault (400, 500), serviceUnavailable (503),
// unauthorized (401), badRequest (400), overLimit (413)
List<Server> listServers(); List<Server> listServers();
/** /**
* This operation provides a list of servers associated with your account. Servers that have been * This operation provides a list of servers associated with your account. Servers that have been
* deleted are not included in this list. Servers contain a status attribute that can be used as * deleted are not included in this list.
* an indication of the current server state. Servers with an ACTIVE status are available for
* use. Other possible values for the status attribute include: BUILD, REBUILD, SUSPENDED,
* QUEUE_RESIZE, PREP_RESIZE, VERIFY_RESIZE, PASSWORD, RESCUE, REBOOT, HARD_REBOOT, SHARE_IP,
* SHARE_IP_NO_CONFIG, DELETE_IP, and UNKNOWN. The Cloud Servers provisioning algorithm has an
* anti-affinity property that attempts to spread out customer VMs across hosts. Under certain
* situations, VMs from the same customer may be placed on the same host. hostId represents the
* host your cloud server runs on and can be used to determine this scenario if it's relevant to
* your application. Note: hostId is unique PER ACCOUNT and is not globally unique.
*/ */
@GET @GET
@ResponseParser(ParseServerListFromGsonResponse.class) @ResponseParser(ParseServerListFromGsonResponse.class)
@Query(key = "format", value = "json") @Query(key = "format", value = "json")
@Path("/servers/detail") @Path("/servers/detail")
// TODO: Error Response Code(s): cloudServersFault (400, 500), serviceUnavailable (503),
// unauthorized (401), badRequest (400), overLimit (413)
List<Server> listServerDetails(); List<Server> listServerDetails();
/**
*
* List available flavors (IDs and names only)
*
* @see #listFlavorDetails()
*/
@GET
@ResponseParser(ParseFlavorListFromGsonResponse.class)
@Query(key = "format", value = "json")
@Path("/flavors")
// TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest
// (400)
List<Flavor> listFlavors();
/**
*
* List available flavors (all details)
*
* @see Flavor
*/
@GET
@ResponseParser(ParseFlavorListFromGsonResponse.class)
@Query(key = "format", value = "json")
@Path("/flavors/detail")
// TODO: cloudServersFault (400, 500), serviceUnavailable (503), unauthorized (401), badRequest
// (400)
List<Flavor> listFlavorDetails();
} }

View File

@ -24,19 +24,32 @@
package org.jclouds.rackspace.cloudservers.domain; package org.jclouds.rackspace.cloudservers.domain;
/**
*
* A flavor is an available hardware configuration for a server. Each flavor has a unique
* combination of disk space, memory capacity, and priority for CPU time.
*
* @author Adrian Cole
*/
public class Flavor { public class Flavor {
public Flavor() {
}
@Override
public String toString() {
return "Flavor [disk=" + disk + ", id=" + id + ", name=" + name + ", ram=" + ram + "]";
}
public Flavor(int id, String name) { public Flavor(int id, String name) {
super();
this.id = id; this.id = id;
this.name = name; this.name = name;
} }
protected Integer disk; private int id;
protected int id; private String name;
protected String name; private Integer disk;
protected Integer ram; private Integer ram;
public Integer getDisk() { public Integer getDisk() {
return disk; return disk;
@ -53,6 +66,7 @@ public class Flavor {
public void setId(int value) { public void setId(int value) {
this.id = value; this.id = value;
} }
public String getName() { public String getName() {
return name; return name;
} }
@ -69,4 +83,44 @@ public class Flavor {
this.ram = value; this.ram = value;
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((disk == null) ? 0 : disk.hashCode());
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((ram == null) ? 0 : ram.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Flavor other = (Flavor) obj;
if (disk == null) {
if (other.disk != null)
return false;
} else if (!disk.equals(other.disk))
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (ram == null) {
if (other.ram != null)
return false;
} else if (!ram.equals(other.ram))
return false;
return true;
}
} }

View File

@ -29,6 +29,13 @@ import java.util.Map;
import com.google.inject.internal.Lists; import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps; import com.google.inject.internal.Maps;
/**
*
* Server.
*
* @author Adrian Cole
* @since 4.0
*/
public class Server { public class Server {
private int id; private int id;
private String name; private String name;
@ -98,6 +105,14 @@ public class Server {
this.hostId = hostId; this.hostId = hostId;
} }
/**
* The Cloud Servers provisioning algorithm has an anti-affinity property that attempts to spread
* out customer VMs across hosts. Under certain situations, VMs from the same customer may be
* placed on the same host. hostId represents the host your cloud server runs on and can be used
* to determine this scenario if it's relevant to your application.
* <p/>
* Note: hostId is unique PER ACCOUNT and is not globally unique.
*/
public String getHostId() { public String getHostId() {
return hostId; return hostId;
} }
@ -138,6 +153,10 @@ public class Server {
this.status = status; this.status = status;
} }
/**
* Servers contain a status attribute that can be used as an indication of the current server
* state. Servers with an ACTIVE status are available for use.
*/
public ServerStatus getStatus() { public ServerStatus getStatus() {
return status; return status;
} }

View File

@ -23,6 +23,13 @@
*/ */
package org.jclouds.rackspace.cloudservers.domain; package org.jclouds.rackspace.cloudservers.domain;
/**
*
* Servers contain a status attribute that can be used as an indication of the current server state.
* Servers with an ACTIVE status are available for use.
*
* @author Adrian Cole
*/
public enum ServerStatus { public enum ServerStatus {
ACTIVE, SUSPENDED, DELETED, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, QUEUE_MOVE, PREP_MOVE, MOVE, VERIFY_MOVE, RESCUE, ERROR, BUILD, RESTORING, PASSWORD, REBUILD, DELETE_IP, SHARE_IP_NO_CONFIG, SHARE_IP, REBOOT, HARD_REBOOT, UNKNOWN; ACTIVE, SUSPENDED, DELETED, QUEUE_RESIZE, PREP_RESIZE, RESIZE, VERIFY_RESIZE, QUEUE_MOVE, PREP_MOVE, MOVE, VERIFY_MOVE, RESCUE, ERROR, BUILD, RESTORING, PASSWORD, REBUILD, DELETE_IP, SHARE_IP_NO_CONFIG, SHARE_IP, REBOOT, HARD_REBOOT, UNKNOWN;

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.rackspace.cloudservers.functions;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.rackspace.cloudservers.domain.Flavor;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.internal.Lists;
/**
* This parses {@link Flavor} from a gson string.
*
* @author Adrian Cole
*/
public class ParseFlavorListFromGsonResponse extends ParseJson<List<Flavor>> {
@Inject
public ParseFlavorListFromGsonResponse(Gson gson) {
super(gson);
}
private static class FlavorListResponse {
List<Flavor> flavors = Lists.newArrayList();
}
public List<Flavor> apply(InputStream stream) {
try {
return gson.fromJson(new InputStreamReader(stream, "UTF-8"), FlavorListResponse.class).flavors;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
}
}
}

View File

@ -30,6 +30,7 @@ import static org.testng.Assert.assertTrue;
import java.util.List; import java.util.List;
import org.jclouds.rackspace.cloudservers.domain.Flavor;
import org.jclouds.rackspace.cloudservers.domain.Server; import org.jclouds.rackspace.cloudservers.domain.Server;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -65,4 +66,34 @@ public class CloudServersConnectionLiveTest {
assertTrue(initialContainerCount >= 0); assertTrue(initialContainerCount >= 0);
} }
@Test
public void testListFlavors() throws Exception {
CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser,
sysRackspaceKey).withJsonDebug().buildContext().getConnection();
List<Flavor> response = connection.listFlavors();
assertNotNull(response);
long flavorCount = response.size();
assertTrue(flavorCount >= 1);
for (Flavor flavor : response) {
assertTrue(flavor.getId() >= 0);
assertNotNull(flavor.getName());
}
}
@Test
public void testListFlavorsDetail() throws Exception {
CloudServersConnection connection = CloudServersContextBuilder.newBuilder(sysRackspaceUser,
sysRackspaceKey).withJsonDebug().buildContext().getConnection();
List<Flavor> response = connection.listFlavorDetails();
assertNotNull(response);
long flavorCount = response.size();
assertTrue(flavorCount >= 0);
for (Flavor flavor : response) {
assertTrue(flavor.getId() >= 1);
assertNotNull(flavor.getName());
assertNotNull(flavor.getDisk());
assertNotNull(flavor.getRam());
}
}
} }

View File

@ -34,6 +34,7 @@ import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.rackspace.Authentication; import org.jclouds.rackspace.Authentication;
import org.jclouds.rackspace.cloudservers.functions.ParseFlavorListFromGsonResponse;
import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse; import org.jclouds.rackspace.cloudservers.functions.ParseServerListFromGsonResponse;
import org.jclouds.rest.JaxrsAnnotationProcessor; import org.jclouds.rest.JaxrsAnnotationProcessor;
import org.jclouds.rest.config.JaxrsModule; import org.jclouds.rest.config.JaxrsModule;
@ -86,6 +87,38 @@ public class CloudServersConnectionTest {
} }
public void testListFlavors() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("listFlavors");
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint,
method, new Object[] {});
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/flavors");
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseFlavorListFromGsonResponse.class);
}
public void testListFlavorsDetail() throws SecurityException, NoSuchMethodException {
Method method = CloudServersConnection.class.getMethod("listFlavorDetails");
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudServersConnection.class).createRequest(endpoint,
method, new Object[] {});
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/flavors/detail");
assertEquals(httpMethod.getEndpoint().getQuery(), "format=json");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
factory.create(CloudServersConnection.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseFlavorListFromGsonResponse.class);
}
@BeforeClass @BeforeClass
void setupFactory() { void setupFactory() {
factory = Guice.createInjector(new AbstractModule() { factory = Guice.createInjector(new AbstractModule() {

View File

@ -0,0 +1,80 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.rackspace.cloudservers.functions;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.util.List;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.rackspace.cloudservers.domain.Flavor;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code ParseFlavorListFromGsonResponseTest}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "cloudFlavors.ParseFlavorListFromGsonResponseTest")
public class ParseFlavorListFromGsonResponseTest {
Injector i = Guice.createInjector(new ParserModule());
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/test_list_flavors.json");
List<Flavor> expects = ImmutableList.of(new Flavor(1, "256 MB Server"), new Flavor(2,
"512 MB Server"));
ParseFlavorListFromGsonResponse parser = new ParseFlavorListFromGsonResponse(i
.getInstance(Gson.class));
assertEquals(parser.apply(is), expects);
}
public void testApplyInputStreamDetails() throws UnknownHostException {
InputStream is = getClass().getResourceAsStream("/test_list_flavors_detail.json");
ParseFlavorListFromGsonResponse parser = new ParseFlavorListFromGsonResponse(i
.getInstance(Gson.class));
List<Flavor> response = parser.apply(is);
assertEquals(response.get(0).getId(), 1);
assertEquals(response.get(0).getName(), "256 MB Server");
assertEquals(response.get(0).getDisk(), new Integer(10));
assertEquals(response.get(0).getRam(), new Integer(256));
assertEquals(response.get(1).getId(), 2);
assertEquals(response.get(1).getName(), "512 MB Server");
assertEquals(response.get(1).getDisk(), new Integer(20));
assertEquals(response.get(1).getRam(), new Integer(512));
}
}

View File

@ -0,0 +1,12 @@
{
"flavors" : [
{
"id" : 1,
"name" : "256 MB Server"
},
{
"id" : 2,
"name" : "512 MB Server"
}
]
}

View File

@ -0,0 +1,16 @@
{
"flavors" : [
{
"id" : 1,
"name" : "256 MB Server",
"ram" : 256,
"disk" : 10
},
{
"id" : 2,
"name" : "512 MB Server",
"ram" : 512,
"disk" : 20
}
]
}