mirror of https://github.com/apache/jclouds.git
fix for JCLOUDS-655
This commit is contained in:
parent
bc48aad54d
commit
f708d20355
|
@ -18,17 +18,25 @@ package org.jclouds.openstack.nova.v2_0.config;
|
||||||
|
|
||||||
import java.beans.ConstructorProperties;
|
import java.beans.ConstructorProperties;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||||
import org.jclouds.openstack.nova.v2_0.domain.Address;
|
import org.jclouds.openstack.nova.v2_0.domain.Address;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.domain.BlockDeviceMapping;
|
||||||
import org.jclouds.openstack.nova.v2_0.domain.HostResourceUsage;
|
import org.jclouds.openstack.nova.v2_0.domain.HostResourceUsage;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.domain.Image;
|
||||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||||
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedAttributes;
|
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedAttributes;
|
||||||
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedStatus;
|
import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedStatus;
|
||||||
|
@ -56,9 +64,10 @@ public class NovaParserModule extends AbstractModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
public Map<Type, Object> provideCustomAdapterBindings() {
|
public Map<Type, Object> provideCustomAdapterBindings() {
|
||||||
return ImmutableMap.<Type, Object>of(
|
return ImmutableMap.<Type, Object>of(
|
||||||
HostResourceUsage.class, new HostResourceUsageAdapter(),
|
HostResourceUsage.class, new HostResourceUsageAdapter(),
|
||||||
ServerWithSecurityGroups.class, new ServerWithSecurityGroupsAdapter(),
|
ServerWithSecurityGroups.class, new ServerWithSecurityGroupsAdapter(),
|
||||||
Server.class, new ServerAdapter()
|
Server.class, new ServerAdapter(),
|
||||||
|
Image.class, new ImageAdapter()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +99,7 @@ public class NovaParserModule extends AbstractModule {
|
||||||
private static class HostResourceUsageInternal extends HostResourceUsage {
|
private static class HostResourceUsageInternal extends HostResourceUsage {
|
||||||
|
|
||||||
@ConstructorProperties({
|
@ConstructorProperties({
|
||||||
"host", "project", "memory_mb", "cpu", "disk_gb"
|
"host", "project", "memory_mb", "cpu", "disk_gb"
|
||||||
})
|
})
|
||||||
protected HostResourceUsageInternal(String host, @Nullable String project, int memoryMb, int cpu, int diskGb) {
|
protected HostResourceUsageInternal(String host, @Nullable String project, int memoryMb, int cpu, int diskGb) {
|
||||||
super(host, project, memoryMb, cpu, diskGb);
|
super(host, project, memoryMb, cpu, diskGb);
|
||||||
|
@ -102,7 +111,7 @@ public class NovaParserModule extends AbstractModule {
|
||||||
public static class ServerWithSecurityGroupsAdapter implements JsonDeserializer<ServerWithSecurityGroups> {
|
public static class ServerWithSecurityGroupsAdapter implements JsonDeserializer<ServerWithSecurityGroups> {
|
||||||
@Override
|
@Override
|
||||||
public ServerWithSecurityGroups deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
|
public ServerWithSecurityGroups deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
|
||||||
throws JsonParseException {
|
throws JsonParseException {
|
||||||
Server server = context.deserialize(jsonElement, Server.class);
|
Server server = context.deserialize(jsonElement, Server.class);
|
||||||
ServerWithSecurityGroups.Builder<?> result = ServerWithSecurityGroups.builder().fromServer(server);
|
ServerWithSecurityGroups.Builder<?> result = ServerWithSecurityGroups.builder().fromServer(server);
|
||||||
Set<String> names = Sets.newLinkedHashSet();
|
Set<String> names = Sets.newLinkedHashSet();
|
||||||
|
@ -121,7 +130,7 @@ public class NovaParserModule extends AbstractModule {
|
||||||
public static class ServerAdapter implements JsonDeserializer<Server> {
|
public static class ServerAdapter implements JsonDeserializer<Server> {
|
||||||
@Override
|
@Override
|
||||||
public Server deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
|
public Server deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
|
||||||
throws JsonParseException {
|
throws JsonParseException {
|
||||||
Server serverBase;
|
Server serverBase;
|
||||||
|
|
||||||
// Servers can be created without an image so test if an image object is returned
|
// Servers can be created without an image so test if an image object is returned
|
||||||
|
@ -149,7 +158,7 @@ public class NovaParserModule extends AbstractModule {
|
||||||
|
|
||||||
private static class ServerInternal extends Server {
|
private static class ServerInternal extends Server {
|
||||||
@ConstructorProperties({
|
@ConstructorProperties({
|
||||||
"id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "image", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
|
"id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "image", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
|
||||||
})
|
})
|
||||||
protected ServerInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
|
protected ServerInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
|
||||||
String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
|
String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
|
||||||
|
@ -162,15 +171,72 @@ public class NovaParserModule extends AbstractModule {
|
||||||
|
|
||||||
private static class ServerInternalWithoutImage extends Server {
|
private static class ServerInternalWithoutImage extends Server {
|
||||||
@ConstructorProperties({
|
@ConstructorProperties({
|
||||||
"id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
|
"id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
|
||||||
})
|
})
|
||||||
protected ServerInternalWithoutImage(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
|
protected ServerInternalWithoutImage(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
|
||||||
String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
|
String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
|
||||||
@Nullable String accessIPv6, Server.Status status, Resource flavor, @Nullable String keyName,
|
@Nullable String accessIPv6, Server.Status status, Resource flavor, @Nullable String keyName,
|
||||||
@Nullable String configDrive, Multimap<String, Address> addresses, Map<String, String> metadata,
|
@Nullable String configDrive, Multimap<String, Address> addresses, Map<String, String> metadata,
|
||||||
@Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes, @Nullable String diskConfig) {
|
@Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes, @Nullable String diskConfig) {
|
||||||
super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, null, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig);
|
super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, null, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public static class ImageAdapter implements JsonDeserializer<Image> {
|
||||||
|
public static final String METADATA = "metadata";
|
||||||
|
public static final String BLOCK_DEVICE_MAPPING = "block_device_mapping";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Image deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
JsonObject json = jsonElement.getAsJsonObject();
|
||||||
|
Map<String, String> metadata = null;
|
||||||
|
List<BlockDeviceMapping> blockDeviceMapping = null;
|
||||||
|
|
||||||
|
JsonElement meta = json.get(METADATA);
|
||||||
|
if (meta != null && meta.isJsonObject()) {
|
||||||
|
metadata = Maps.newTreeMap();
|
||||||
|
for (Map.Entry<String, JsonElement> e : meta.getAsJsonObject().entrySet()) {
|
||||||
|
Object value;
|
||||||
|
if (e.getValue().isJsonArray()) {
|
||||||
|
value = context.deserialize(e.getValue().getAsJsonArray(), ArrayList.class);
|
||||||
|
} else if (e.getValue().isJsonObject()) {
|
||||||
|
value = context.deserialize(e.getValue().getAsJsonObject(), TreeMap.class);
|
||||||
|
} else if (e.getValue().isJsonPrimitive()) {
|
||||||
|
value = e.getValue().getAsJsonPrimitive().getAsString();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//keep non-string members out of normal metadata
|
||||||
|
if (value instanceof String) {
|
||||||
|
metadata.put(e.getKey(), (String) value);
|
||||||
|
} else if (value instanceof List && BLOCK_DEVICE_MAPPING.equals(e.getKey())) {
|
||||||
|
blockDeviceMapping = context.deserialize(e.getValue(), new TypeToken<List<BlockDeviceMapping>>(){}.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.remove(METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply(context.<ImageInternal>deserialize(json, ImageInternal.class), metadata, blockDeviceMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image apply(ImageInternal in, Map<String, String> metadata, List<BlockDeviceMapping> blockDeviceMapping) {
|
||||||
|
return in.toBuilder().metadata(metadata).blockDeviceMapping(blockDeviceMapping).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ImageInternal extends Image {
|
||||||
|
@ConstructorProperties({
|
||||||
|
"id", "name", "links", "updated", "created", "tenant_id", "user_id", "status", "progress", "minDisk", "minRam", "blockDeviceMapping", "server", "metadata"
|
||||||
|
})
|
||||||
|
protected ImageInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable Date updated, @Nullable Date created,
|
||||||
|
String tenantId, @Nullable String userId, @Nullable Status status, int progress, int minDisk, int minRam,
|
||||||
|
@Nullable List<BlockDeviceMapping> blockDeviceMapping, @Nullable Resource server, @Nullable Map<String, String> metadata) {
|
||||||
|
super(id, name, links, updated, created, tenantId, userId, status, progress, minDisk, minRam, blockDeviceMapping, server, metadata);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.beans.ConstructorProperties;
|
import java.beans.ConstructorProperties;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.openstack.v2_0.domain.Link;
|
import org.jclouds.openstack.v2_0.domain.Link;
|
||||||
import org.jclouds.openstack.v2_0.domain.Resource;
|
import org.jclouds.openstack.v2_0.domain.Resource;
|
||||||
|
@ -85,6 +87,7 @@ public class Image extends Resource {
|
||||||
protected int minDisk;
|
protected int minDisk;
|
||||||
protected int minRam;
|
protected int minRam;
|
||||||
protected Resource server;
|
protected Resource server;
|
||||||
|
protected List<BlockDeviceMapping> blockDeviceMapping = ImmutableList.of();
|
||||||
protected Map<String, String> metadata = ImmutableMap.of();
|
protected Map<String, String> metadata = ImmutableMap.of();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,6 +162,11 @@ public class Image extends Resource {
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T blockDeviceMapping(List<BlockDeviceMapping> blockDeviceMapping){
|
||||||
|
this.blockDeviceMapping = blockDeviceMapping;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Image#getMetadata()
|
* @see Image#getMetadata()
|
||||||
*/
|
*/
|
||||||
|
@ -168,7 +176,7 @@ public class Image extends Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Image build() {
|
public Image build() {
|
||||||
return new Image(id, name, links, updated, created, tenantId, userId, status, progress, minDisk, minRam, server, metadata);
|
return new Image(id, name, links, updated, created, tenantId, userId, status, progress, minDisk, minRam, blockDeviceMapping, server, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T fromImage(Image in) {
|
public T fromImage(Image in) {
|
||||||
|
@ -203,15 +211,16 @@ public class Image extends Resource {
|
||||||
private final int progress;
|
private final int progress;
|
||||||
private final int minDisk;
|
private final int minDisk;
|
||||||
private final int minRam;
|
private final int minRam;
|
||||||
|
private final List<BlockDeviceMapping> blockDeviceMapping;
|
||||||
private final Resource server;
|
private final Resource server;
|
||||||
private final Map<String, String> metadata;
|
private final Map<String, String> metadata;
|
||||||
|
|
||||||
@ConstructorProperties({
|
@ConstructorProperties({
|
||||||
"id", "name", "links", "updated", "created", "tenant_id", "user_id", "status", "progress", "minDisk", "minRam", "server", "metadata"
|
"id", "name", "links", "updated", "created", "tenant_id", "user_id", "status", "progress", "minDisk", "minRam", "server", "blockDeviceMapping", "metadata"
|
||||||
})
|
})
|
||||||
protected Image(String id, @Nullable String name, java.util.Set<Link> links, @Nullable Date updated, @Nullable Date created,
|
protected Image(String id, @Nullable String name, java.util.Set<Link> links, @Nullable Date updated, @Nullable Date created,
|
||||||
String tenantId, @Nullable String userId, @Nullable Status status, int progress, int minDisk, int minRam,
|
String tenantId, @Nullable String userId, @Nullable Status status, int progress, int minDisk, int minRam,
|
||||||
@Nullable Resource server, @Nullable Map<String, String> metadata) {
|
@Nullable List<BlockDeviceMapping> blockDeviceMapping, @Nullable Resource server, @Nullable Map<String, String> metadata) {
|
||||||
super(id, name, links);
|
super(id, name, links);
|
||||||
this.updated = updated;
|
this.updated = updated;
|
||||||
this.created = created;
|
this.created = created;
|
||||||
|
@ -221,6 +230,7 @@ public class Image extends Resource {
|
||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
this.minDisk = minDisk;
|
this.minDisk = minDisk;
|
||||||
this.minRam = minRam;
|
this.minRam = minRam;
|
||||||
|
this.blockDeviceMapping = blockDeviceMapping == null ? ImmutableList.<BlockDeviceMapping>of() : blockDeviceMapping;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.metadata = metadata == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(metadata);
|
this.metadata = metadata == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(metadata);
|
||||||
}
|
}
|
||||||
|
@ -262,6 +272,11 @@ public class Image extends Resource {
|
||||||
return this.minRam;
|
return this.minRam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public List<BlockDeviceMapping> getBlockDeviceMapping(){
|
||||||
|
return this.blockDeviceMapping;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Resource getServer() {
|
public Resource getServer() {
|
||||||
return this.server;
|
return this.server;
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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.openstack.nova.v2_0.config;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jclouds.json.config.GsonModule;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.domain.Image;
|
||||||
|
import org.testng.annotations.BeforeTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
@Test(groups = "unit")
|
||||||
|
public class ImageAdapterTest {
|
||||||
|
|
||||||
|
private Gson gson;
|
||||||
|
|
||||||
|
@BeforeTest
|
||||||
|
public void setup() {
|
||||||
|
Injector injector = Guice.createInjector(new GsonModule(), new NovaParserModule());
|
||||||
|
gson = injector.getInstance(Gson.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeWithBlockDeviceMappingAndMetadata() throws Exception {
|
||||||
|
ImageContainer container = gson.fromJson(stringFromResource("image_details_with_block_device_mapping.json"), ImageContainer.class);
|
||||||
|
|
||||||
|
// Note that the block device mapping keys are removed from the metadata by the adapter.
|
||||||
|
assertNotNull(container.image.getMetadata());
|
||||||
|
assertEquals(container.image.getMetadata().size(), 2);
|
||||||
|
assertEquals("Gold", container.image.getMetadata().get("ImageType"));
|
||||||
|
assertEquals("1.5", container.image.getMetadata().get("ImageVersion"));
|
||||||
|
|
||||||
|
assertNotNull(container.image.getBlockDeviceMapping());
|
||||||
|
assertEquals(container.image.getBlockDeviceMapping().size(), 1);
|
||||||
|
assertEquals(new Integer(2), getOnlyElement(container.image.getBlockDeviceMapping()).getBootIndex());
|
||||||
|
assertEquals("snapshot", getOnlyElement(container.image.getBlockDeviceMapping()).getSourceType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeWithoutBlockDeviceMapping() throws Exception {
|
||||||
|
ImageContainer container = gson.fromJson(stringFromResource("image_details.json"), ImageContainer.class);
|
||||||
|
|
||||||
|
assertNotNull(container.image.getMetadata());
|
||||||
|
assertEquals(container.image.getMetadata().size(), 2);
|
||||||
|
assertEquals("Gold", container.image.getMetadata().get("ImageType"));
|
||||||
|
assertEquals("1.5", container.image.getMetadata().get("ImageVersion"));
|
||||||
|
|
||||||
|
assertNotNull(container.image.getBlockDeviceMapping());
|
||||||
|
assertEquals(0, container.image.getBlockDeviceMapping().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeWithoutBlockDeviceMappingOrMetadata() throws Exception {
|
||||||
|
ImageContainer container = gson.fromJson(stringFromResource("image_details_without_metadata.json"), ImageContainer.class);
|
||||||
|
|
||||||
|
assertNotNull(container.image.getMetadata());
|
||||||
|
assertEquals(container.image.getMetadata().size(), 0);
|
||||||
|
assertNotNull(container.image.getBlockDeviceMapping());
|
||||||
|
assertEquals(0, container.image.getBlockDeviceMapping().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stringFromResource(String resource) throws IOException {
|
||||||
|
return Resources.toString(Resources.getResource(resource), Charsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the ImageApi methods use the "@SelectJson" annotation to unwrap the object inside the "image" key
|
||||||
|
// We use this container to deserialize the Image object to simulate that behavior and use a *real* json
|
||||||
|
// in the tests.
|
||||||
|
public static class ImageContainer {
|
||||||
|
public Image image;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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.openstack.nova.v2_0.features;
|
||||||
|
|
||||||
|
import com.google.common.collect.FluentIterable;
|
||||||
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.domain.BlockDeviceMapping;
|
||||||
|
import org.jclouds.openstack.nova.v2_0.domain.Image;
|
||||||
|
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
|
@Test(groups = "unit")
|
||||||
|
public class ImageApiMockTest extends BaseOpenStackMockTest<NovaApi> {
|
||||||
|
public void testImageWithBlockDeviceMapping() throws Exception {
|
||||||
|
MockWebServer server = mockOpenStackServer();
|
||||||
|
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
|
||||||
|
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/image_list_with_block_device_mapping.json"))));
|
||||||
|
|
||||||
|
try {
|
||||||
|
NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
|
||||||
|
ImageApi imageApi = novaApi.getImageApiForZone("RegionOne");
|
||||||
|
|
||||||
|
FluentIterable<? extends Image> images = imageApi.listInDetail().concat();
|
||||||
|
|
||||||
|
Image img = images.get(0);
|
||||||
|
assertNotNull(img.getMetadata());
|
||||||
|
assertEquals(10, img.getMetadata().size());
|
||||||
|
assertNotNull(img.getBlockDeviceMapping());
|
||||||
|
assertEquals(1, img.getBlockDeviceMapping().size());
|
||||||
|
BlockDeviceMapping blockDeviceMapping = img.getBlockDeviceMapping().get(0);
|
||||||
|
assertEquals("snapshot", blockDeviceMapping.getSourceType());
|
||||||
|
assertEquals(new Integer(2), blockDeviceMapping.getBootIndex());
|
||||||
|
} finally {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"image": {
|
||||||
|
"id": "52415800-8b69-11e0-9b19-734f5736d2a2",
|
||||||
|
"name": "My Server Backup",
|
||||||
|
"updated": "2010-10-10T12:00:00Z",
|
||||||
|
"created": "2010-08-10T12:00:00Z",
|
||||||
|
"tenant_id": "12345",
|
||||||
|
"user_id": "joe",
|
||||||
|
"status": "SAVING",
|
||||||
|
"progress": 80,
|
||||||
|
"minDisk": 5,
|
||||||
|
"minRam": 256,
|
||||||
|
"metadata": {
|
||||||
|
"ImageType": "Gold",
|
||||||
|
"ImageVersion": 1.5,
|
||||||
|
"block_device_mapping": [
|
||||||
|
{
|
||||||
|
"guest_format": null,
|
||||||
|
"boot_index": 2,
|
||||||
|
"no_device": null,
|
||||||
|
"volume_id": null,
|
||||||
|
"volume_size": null,
|
||||||
|
"disk_bus": null,
|
||||||
|
"image_id": null,
|
||||||
|
"source_type": "snapshot",
|
||||||
|
"device_type": null,
|
||||||
|
"snapshot_id": "a900a56c-61b7-4438-9150-76312fa1aa10",
|
||||||
|
"destination_type": "volume",
|
||||||
|
"delete_on_termination": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"id": "52415800-8b69-11e0-9b19-734f335aa7b3",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"href": "http://servers.api.openstack.org/v2/1234/servers/52415800-8b69-11e0-9b19-734f335aa7b3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "bookmark",
|
||||||
|
"href": "http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734f335aa7b3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"href": "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "bookmark",
|
||||||
|
"href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"image": {
|
||||||
|
"id": "52415800-8b69-11e0-9b19-734f5736d2a2",
|
||||||
|
"name": "My Server Backup",
|
||||||
|
"updated": "2010-10-10T12:00:00Z",
|
||||||
|
"created": "2010-08-10T12:00:00Z",
|
||||||
|
"tenant_id": "12345",
|
||||||
|
"user_id": "joe",
|
||||||
|
"status": "SAVING",
|
||||||
|
"progress": 80,
|
||||||
|
"minDisk": 5,
|
||||||
|
"minRam": 256,
|
||||||
|
"metadata": {},
|
||||||
|
"server": {
|
||||||
|
"id": "52415800-8b69-11e0-9b19-734f335aa7b3",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"href": "http://servers.api.openstack.org/v2/1234/servers/52415800-8b69-11e0-9b19-734f335aa7b3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "bookmark",
|
||||||
|
"href": "http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734f335aa7b3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "self",
|
||||||
|
"href": "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "bookmark",
|
||||||
|
"href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f5736d2a2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"updated": "2014-08-08T04:43:36Z",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "http://192.168.24.16:8774/v2/d312a9d1acee46499e04fc2c0cd7e540/images/cd9d57a9-0978-45f3-9cbc-edb99347be6b",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "http://192.168.24.16:8774/d312a9d1acee46499e04fc2c0cd7e540/images/cd9d57a9-0978-45f3-9cbc-edb99347be6b",
|
||||||
|
"rel": "bookmark"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "http://192.168.24.16:9292/d312a9d1acee46499e04fc2c0cd7e540/images/cd9d57a9-0978-45f3-9cbc-edb99347be6b",
|
||||||
|
"type": "application/vnd.openstack.image",
|
||||||
|
"rel": "alternate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "cd9d57a9-0978-45f3-9cbc-edb99347be6b",
|
||||||
|
"OS-EXT-IMG-SIZE:size": 0,
|
||||||
|
"name": "t11",
|
||||||
|
"created": "2014-08-08T04:43:36Z",
|
||||||
|
"minDisk": 0,
|
||||||
|
"progress": 100,
|
||||||
|
"minRam": 0,
|
||||||
|
"metadata": {
|
||||||
|
"block_device_mapping": [
|
||||||
|
{
|
||||||
|
"guest_format": null,
|
||||||
|
"boot_index": 2,
|
||||||
|
"no_device": null,
|
||||||
|
"volume_id": null,
|
||||||
|
"volume_size": null,
|
||||||
|
"disk_bus": null,
|
||||||
|
"image_id": null,
|
||||||
|
"source_type": "snapshot",
|
||||||
|
"device_type": null,
|
||||||
|
"snapshot_id": "a900a56c-61b7-4438-9150-76312fa1aa10",
|
||||||
|
"destination_type": "volume",
|
||||||
|
"delete_on_termination": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checksum": "32c08d302f9206668030d47789b77858",
|
||||||
|
"min_ram": "1",
|
||||||
|
"disk_format": "qcow2",
|
||||||
|
"image_name": "Ubuntu LTS 14.04",
|
||||||
|
"bdm_v2": "True",
|
||||||
|
"image_id": "cfefefc1-eba2-4b1e-9b07-a8c74a872d65",
|
||||||
|
"root_device_name": "/dev/vda",
|
||||||
|
"container_format": "bare",
|
||||||
|
"min_disk": "8",
|
||||||
|
"size": "254149120"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue