Support Disk Configuration Extension

This commit is contained in:
Everett Toews 2013-07-31 21:11:30 -05:00
parent 675c649cb6
commit e6b6e4edb2
7 changed files with 232 additions and 10 deletions

View File

@ -25,6 +25,7 @@ import java.util.Map;
import javax.inject.Named;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
import org.jclouds.openstack.v2_0.domain.Link;
import org.jclouds.openstack.v2_0.domain.Resource;
@ -45,6 +46,8 @@ import com.google.common.collect.Multimap;
* />
*/
public class Server extends Resource {
public final static String DISK_CONFIG_MANUAL = "MANUAL";
public final static String DISK_CONFIG_AUTO = "AUTO";
/**
* Servers contain a status attribute that can be used as an indication of the current server
@ -441,12 +444,18 @@ public class Server extends Resource {
}
/**
* Disk config attribute from the Disk Config Extension (alias "OS-DCF")
* Disk config attribute from the Disk Config Extension (alias "OS-DCF").
* One of {@link Server#DISK_CONFIG_AUTO} or {@link Server#DISK_CONFIG_MANUAL}.
* This field is only present if the Disk Config extension is installed.
* <p/>
* NOTE: This field is only present if the Disk Config extension is installed
* NOTE: Typically a field like this would be implemented as an enum but this field was
* originally implmented as a String and {@link Server#DISK_CONFIG_AUTO} and
* {@link Server#DISK_CONFIG_MANUAL} were added later as Strings to preserve backwards
* compatibility.
*
* @see org.jclouds.openstack.nova.v2_0.features.ExtensionApi#getExtensionByAlias
* @see org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces#DISK_CONFIG
* @see CreateServerOptions#getDiskConfig()
*/
public Optional<String> getDiskConfig() {
return this.diskConfig;

View File

@ -20,6 +20,7 @@ import java.beans.ConstructorProperties;
import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
import org.jclouds.openstack.v2_0.domain.Link;
import org.jclouds.openstack.v2_0.domain.Resource;
@ -47,6 +48,7 @@ public class ServerCreated extends Resource {
public static final class Builder extends Resource.Builder<Builder> {
protected String adminPass;
protected String diskConfig;
/**
* @see ServerCreated#getAdminPass()
@ -55,13 +57,21 @@ public class ServerCreated extends Resource {
this.adminPass = adminPass;
return self();
}
/**
* @see ServerCreated#getDiskConfig()
*/
public Builder diskConfig(String diskConfig) {
this.diskConfig = diskConfig;
return self();
}
public ServerCreated build() {
return new ServerCreated(id, name, links, adminPass);
return new ServerCreated(id, name, links, adminPass, diskConfig);
}
public Builder fromServerCreated(ServerCreated in) {
return super.fromResource(in).adminPass(in.getAdminPass().orNull());
return super.fromResource(in).adminPass(in.getAdminPass().orNull()).diskConfig(in.getDiskConfig().orNull());
}
@Override
@ -71,13 +81,16 @@ public class ServerCreated extends Resource {
}
private final Optional<String> adminPass;
private final Optional<String> diskConfig;
@ConstructorProperties({
"id", "name", "links", "adminPass"
"id", "name", "links", "adminPass", "OS-DCF:diskConfig"
})
protected ServerCreated(String id, @Nullable String name, Set<Link> links, @Nullable String adminPass) {
protected ServerCreated(String id, @Nullable String name, Set<Link> links, @Nullable String adminPass,
@Nullable String diskConfig) {
super(id, name, links);
this.adminPass = Optional.fromNullable(adminPass);
this.diskConfig = Optional.fromNullable(diskConfig);
}
/**
@ -87,9 +100,16 @@ public class ServerCreated extends Resource {
return this.adminPass;
}
/**
* @see CreateServerOptions#getDiskConfig()
*/
public Optional<String> getDiskConfig() {
return this.diskConfig;
}
@Override
public int hashCode() {
return Objects.hashCode(adminPass);
return Objects.hashCode(adminPass, diskConfig);
}
@Override
@ -97,11 +117,12 @@ public class ServerCreated extends Resource {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
ServerCreated that = ServerCreated.class.cast(obj);
return super.equals(that) && Objects.equal(this.adminPass, that.adminPass);
return super.equals(that) && Objects.equal(this.adminPass, that.adminPass)
&& Objects.equal(this.diskConfig, that.diskConfig);
}
@Override
protected ToStringHelper string() {
return super.string().add("adminPass", adminPass.orNull());
return super.string().add("adminPass", adminPass.orNull()).add("diskConfig", diskConfig.orNull());
}
}

View File

@ -34,6 +34,7 @@ import javax.inject.Named;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.domain.Server;
import org.jclouds.rest.MapBinder;
import org.jclouds.rest.binders.BindToJsonPayload;
@ -109,6 +110,7 @@ public class CreateServerOptions implements MapBinder {
private Map<String, String> metadata = ImmutableMap.of();
private List<File> personality = Lists.newArrayList();
private byte[] userData;
private String diskConfig;
@Override
public boolean equals(Object object) {
@ -119,7 +121,7 @@ public class CreateServerOptions implements MapBinder {
final CreateServerOptions other = CreateServerOptions.class.cast(object);
return equal(keyName, other.keyName) && equal(securityGroupNames, other.securityGroupNames)
&& equal(metadata, other.metadata) && equal(personality, other.personality)
&& equal(adminPass, other.adminPass);
&& equal(adminPass, other.adminPass) && equal(diskConfig, other.diskConfig);
} else {
return false;
}
@ -141,6 +143,8 @@ public class CreateServerOptions implements MapBinder {
toString.add("personality", personality);
if (adminPass != null)
toString.add("adminPassPresent", true);
if (diskConfig != null)
toString.add("diskConfig", diskConfig);
toString.add("userData", userData == null ? null : new String(userData));
return toString;
}
@ -161,6 +165,8 @@ public class CreateServerOptions implements MapBinder {
@Named("security_groups")
Set<NamedThingy> securityGroupNames;
String user_data;
@Named("OS-DCF:diskConfig")
String diskConfig;
private ServerRequest(String name, String imageRef, String flavorRef) {
this.name = name;
@ -192,6 +198,9 @@ public class CreateServerOptions implements MapBinder {
if (adminPass != null) {
server.adminPass = adminPass;
}
if (diskConfig != null) {
server.diskConfig = diskConfig;
}
return bindToRequest(request, ImmutableMap.of("server", server));
}
@ -320,6 +329,29 @@ public class CreateServerOptions implements MapBinder {
this.securityGroupNames = ImmutableSet.copyOf(securityGroupNames);
return this;
}
/**
* When you create a server from an image with the diskConfig value set to
* {@link Server#DISK_CONFIG_AUTO}, the server is built with a single partition that is expanded to
* the disk size of the flavor selected. When you set the diskConfig attribute to
* {@link Server#DISK_CONFIG_MANUAL}, the server is built by using the partition scheme and file
* system that is in the source image.
* <p/>
* If the target flavor disk is larger, remaining disk space is left unpartitioned. A server inherits the diskConfig
* attribute from the image from which it is created. However, you can override the diskConfig value when you create
* a server. This field is only present if the Disk Config extension is installed in your OpenStack deployment.
*/
public String getDiskConfig() {
return diskConfig;
}
/**
* @see #getDiskConfig
*/
public CreateServerOptions diskConfig(String diskConfig) {
this.diskConfig = diskConfig;
return this;
}
public static class Builder {
@ -367,6 +399,14 @@ public class CreateServerOptions implements MapBinder {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.securityGroupNames(groupNames));
}
/**
* @see CreateServerOptions#getDiskConfig
*/
public static CreateServerOptions diskConfig(String diskConfig) {
CreateServerOptions options = new CreateServerOptions();
return CreateServerOptions.class.cast(options.diskConfig(diskConfig));
}
}
@Override

View File

@ -23,6 +23,7 @@ import static org.testng.Assert.fail;
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.Server;
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiExpectTest;
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
import org.jclouds.openstack.nova.v2_0.parse.ParseCreatedServerTest;
@ -127,6 +128,58 @@ public class ServerApiExpectTest extends BaseNovaApiExpectTest {
new ParseCreatedServerTest().expected().toString());
}
public void testCreateServerWithDiskConfigAuto() throws Exception {
HttpRequest createServer = HttpRequest
.builder()
.method("POST")
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")
.addHeader("Accept", "application/json")
.addHeader("X-Auth-Token", authToken)
.payload(payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"OS-DCF:diskConfig\":\"AUTO\"}}","application/json"))
.build();
HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
.payload(payloadFromResourceWithContentType("/new_server_disk_config_auto.json","application/json; charset=UTF-8")).build();
NovaApi apiWithNewServer = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
createServer, createServerResponse);
assertEquals(apiWithNewServer.getServerApiForZone("az-1.region-a.geo-1").create("test-e92", "1241",
"100", new CreateServerOptions().diskConfig(Server.DISK_CONFIG_AUTO)).toString(),
new ParseCreatedServerTest().expectedWithDiskConfig(Server.DISK_CONFIG_AUTO).toString());
}
public void testCreateServerWithDiskConfigManual() throws Exception {
HttpRequest createServer = HttpRequest
.builder()
.method("POST")
.endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers")
.addHeader("Accept", "application/json")
.addHeader("X-Auth-Token", authToken)
.payload(payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"OS-DCF:diskConfig\":\"MANUAL\"}}","application/json"))
.build();
HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
.payload(payloadFromResourceWithContentType("/new_server_disk_config_manual.json","application/json; charset=UTF-8")).build();
NovaApi apiWithNewServer = requestsSendResponses(
keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess,
createServer, createServerResponse);
assertEquals(apiWithNewServer.getServerApiForZone("az-1.region-a.geo-1").create("test-e92", "1241",
"100", new CreateServerOptions().diskConfig(Server.DISK_CONFIG_MANUAL)).toString(),
new ParseCreatedServerTest().expectedWithDiskConfig(Server.DISK_CONFIG_MANUAL).toString());
}
public void testCreateImageWhenResponseIs2xx() throws Exception {
String serverId = "123";
String imageId = "456";

View File

@ -59,6 +59,21 @@ public class ParseCreatedServerTest extends BaseItemParserTest<ServerCreated> {
}
@SelectJson("server")
@Consumes(MediaType.APPLICATION_JSON)
public ServerCreated expectedWithDiskConfig(String diskConfig) {
return ServerCreated
.builder()
.id("71752")
.name("test-e92")
.adminPass("ZWuHcmTMQ7eXoHeM")
.diskConfig(diskConfig)
.links(
Link.create(Relation.SELF, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752")),
Link.create(Relation.BOOKMARK, URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752"))).build();
}
protected Injector injector() {
return Guice.createInjector(new NovaParserModule(), new GsonModule());
}

View File

@ -0,0 +1,42 @@
{
"server": {
"status": "BUILD(scheduling)",
"updated": "2012-03-19T06:21:13Z",
"hostId": "",
"user_id": "54297837463082",
"name": "test-e92",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752",
"rel": "self"
}, {
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752",
"rel": "bookmark"
}],
"addresses": {},
"tenant_id": "37936628937291",
"image": {
"id": "1241",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241",
"rel": "bookmark"
}]
},
"created": "2012-03-19T06:21:13Z",
"uuid": "47491020-6a78-4f63-9475-23195ac4515c",
"accessIPv4": "",
"accessIPv6": "",
"key_name": null,
"adminPass": "ZWuHcmTMQ7eXoHeM",
"flavor": {
"id": "100",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100",
"rel": "bookmark"
}]
},
"config_drive": "",
"id": 71752,
"metadata": {},
"OS-DCF:diskConfig": "AUTO"
}
}

View File

@ -0,0 +1,42 @@
{
"server": {
"status": "BUILD(scheduling)",
"updated": "2012-03-19T06:21:13Z",
"hostId": "",
"user_id": "54297837463082",
"name": "test-e92",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752",
"rel": "self"
}, {
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752",
"rel": "bookmark"
}],
"addresses": {},
"tenant_id": "37936628937291",
"image": {
"id": "1241",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241",
"rel": "bookmark"
}]
},
"created": "2012-03-19T06:21:13Z",
"uuid": "47491020-6a78-4f63-9475-23195ac4515c",
"accessIPv4": "",
"accessIPv6": "",
"key_name": null,
"adminPass": "ZWuHcmTMQ7eXoHeM",
"flavor": {
"id": "100",
"links": [{
"href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100",
"rel": "bookmark"
}]
},
"config_drive": "",
"id": 71752,
"metadata": {},
"OS-DCF:diskConfig": "MANUAL"
}
}