Issue 908: list, listInDetail, show, getAsStream

This commit is contained in:
Adrian Cole 2012-05-18 15:56:27 -07:00
parent 4c26a0c456
commit bab3852140
15 changed files with 1361 additions and 1 deletions

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.openstack.glance.v1_0.config;
import java.util.Map;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
@ -26,11 +28,15 @@ import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import org.jclouds.openstack.glance.v1_0.GlanceAsyncClient;
import org.jclouds.openstack.glance.v1_0.GlanceClient;
import org.jclouds.openstack.glance.v1_0.features.ImageAsyncClient;
import org.jclouds.openstack.glance.v1_0.features.ImageClient;
import org.jclouds.openstack.glance.v1_0.handlers.GlanceErrorHandler;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule;
import com.google.common.collect.ImmutableMap;
/**
* Configures the Glance connection.
*
@ -39,6 +45,14 @@ import org.jclouds.rest.config.RestClientModule;
@ConfiguresRestClient
public class GlanceRestClientModule extends RestClientModule<GlanceClient, GlanceAsyncClient> {
public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
.put(ImageClient.class, ImageAsyncClient.class)
.build();
public GlanceRestClientModule() {
super(DELEGATE_MAP);
}
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);

View File

@ -0,0 +1,83 @@
/**
* 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.glance.v1_0.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The container format refers to whether the virtual machine image is in a file format that also
* contains metadata about the actual virtual machine.
*
* <h3>Note</h3>
*
* Note that the container format string is not currently used by Glance or other OpenStack
* components, so it is safe to simply specify {@link #BARE} as the container format if you are
* unsure.
*
* @author Adrian Cole
* @see <a href= "http://glance.openstack.org/formats.html" />
*/
public enum ContainerFormat {
/**
* This indicates there is no container or metadata envelope for the image
*/
BARE,
/**
* This is the OVF container format
*/
OVF,
/**
* This indicates what is stored in Glance is an Amazon kernel image
*/
AKI,
/**
* This indicates what is stored in Glance is an Amazon ramdisk image
*/
ARI,
/**
* This indicates what is stored in Glance is an Amazon machine image
*/
AMI,
/**
* Type unknown to jclouds
*/
UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static ContainerFormat fromValue(String containerFormat) {
try {
return valueOf(checkNotNull(containerFormat, "containerFormat").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -0,0 +1,95 @@
/**
* 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.glance.v1_0.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The disk format of a virtual machine image is the format of the underlying disk image. Virtual
* appliance vendors have different formats for laying out the information contained in a virtual
* machine disk image.
*
* @author Adrian Cole
* @see <a href= "http://glance.openstack.org/formats.html" />
*/
public enum DiskFormat {
/**
* This is an unstructured disk image format
*/
RAW,
/**
* This is the VHD disk format, a common disk format used by virtual machine monitors from
* VMWare, Xen, Microsoft, VirtualBox, and others
*/
VHD,
/**
* Another common disk format supported by many common virtual machine monitors
*/
VMDK,
/**
* A disk format supported by VirtualBox virtual machine monitor and the QEMU emulator
*/
VDI,
/**
* An archive format for the data contents of an optical disc (e.g. CDROM).
*/
ISO,
/**
* A disk format supported by the QEMU emulator that can expand dynamically and supports Copy on
* Write
*/
QCOW2,
/**
* This indicates what is stored in Glance is an Amazon kernel image
*/
AKI,
/**
* This indicates what is stored in Glance is an Amazon ramdisk image
*/
ARI,
/**
* This indicates what is stored in Glance is an Amazon machine image
*/
AMI,
/**
* Type unknown to jclouds
*/
UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static DiskFormat fromValue(String diskFormat) {
try {
return valueOf(checkNotNull(diskFormat, "diskFormat").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -0,0 +1,191 @@
/**
* 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.glance.v1_0.domain;
import org.jclouds.openstack.domain.Resource;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.gson.annotations.SerializedName;
/**
* An image the Glance server knows about
*
* @author Adrian Cole
* @see <a href= "http://glance.openstack.org/glanceapi.html" />
* @see <a href= "https://github.com/openstack/glance/blob/master/glance/api/v1/images.py" />
*/
public class Image extends Resource {
public static enum Status {
UNRECOGNIZED, ACTIVE, SAVING, QUEUED, KILLED, PENDING_DELETE, DELETED;
public String value() {
return name();
}
public static Status fromValue(String v) {
try {
return valueOf(v.toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromImage(this);
}
public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T> {
private Optional<ContainerFormat> containerFormat = Optional.absent();
private Optional<DiskFormat> diskFormat = Optional.absent();
private Optional<Long> size = Optional.absent();
private Optional<String> checksum = Optional.absent();
/**
* @see Image#getContainerFormat()
*/
public T containerFormat(Optional<ContainerFormat> containerFormat) {
this.containerFormat = containerFormat;
return self();
}
/**
* @see Image#getDiskFormat()
*/
public T diskFormat(Optional<DiskFormat> diskFormat) {
this.diskFormat = diskFormat;
return self();
}
/**
* @see Image#getSize()
*/
public T size(Optional<Long> size) {
this.size = size;
return self();
}
/**
* @see Image#getSize()
*/
public T checksum(Optional<String> checksum) {
this.checksum = checksum;
return self();
}
/**
* @see Image#getContainerFormat()
*/
public T containerFormat(ContainerFormat containerFormat) {
return containerFormat(Optional.of(containerFormat));
}
/**
* @see Image#getDiskFormat()
*/
public T diskFormat(DiskFormat diskFormat) {
return diskFormat(Optional.of(diskFormat));
}
/**
* @see Image#getSize()
*/
public T size(long size) {
return size(Optional.of(size));
}
/**
* @see Image#getSize()
*/
public T checksum(String checksum) {
return checksum(Optional.of(checksum));
}
public Image build() {
return new Image(this);
}
public T fromImage(Image in) {
return super.fromResource(in).containerFormat(in.getContainerFormat()).diskFormat(in.getDiskFormat()).size(
in.getSize()).checksum(in.getChecksum());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
protected Image() {
// we want serializers like Gson to work w/o using sun.misc.Unsafe,
// prohibited in GAE. This also implies fields are not final.
// see http://code.google.com/p/jclouds/issues/detail?id=925
}
// | container_format | varchar(20) | YES | | NULL | |
@SerializedName("container_format")
private Optional<ContainerFormat> containerFormat = Optional.absent();
// | disk_format | varchar(20) | YES | | NULL | |
@SerializedName("disk_format")
private Optional<DiskFormat> diskFormat = Optional.absent();
// | size | bigint(20) | YES | | NULL | |
private Optional<Long> size = Optional.absent();
// | checksum | varchar(32) | YES | | NULL | |
private Optional<String> checksum = Optional.absent();
protected Image(Builder<?> builder) {
super(builder);
this.containerFormat = builder.containerFormat;
this.diskFormat = builder.diskFormat;
this.size = builder.size;
this.checksum = builder.checksum;
}
public Optional<ContainerFormat> getContainerFormat() {
return this.containerFormat;
}
public Optional<DiskFormat> getDiskFormat() {
return this.diskFormat;
}
public Optional<Long> getSize() {
return this.size;
}
public Optional<String> getChecksum() {
return checksum;
}
@Override
protected Objects.ToStringHelper string() {
return super.string().add("containerFormat", containerFormat).add("diskFormat", diskFormat).add("size", size)
.add("checksum", checksum);
}
}

View File

@ -0,0 +1,279 @@
/**
* 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.glance.v1_0.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
/**
* Detailed listing of an Image
*
* @author Adrian Cole
* @see <a href= "http://glance.openstack.org/glanceapi.html" />
* @see <a href= "https://github.com/openstack/glance/blob/master/glance/api/v1/images.py" />
* @see <a href= "https://github.com/openstack/glance/blob/master/glance/registry/db/api.py" />
*/
public class ImageDetails extends Image {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromImageDetails(this);
}
public static abstract class Builder<T extends Builder<T>> extends Image.Builder<T> {
private long minDisk;
private long minRam;
private Optional<String> location = Optional.absent();
private Optional<String> owner = Optional.absent();
private Date updatedAt;
private Date createdAt;
private Optional<Date> deletedAt = Optional.absent();
private Status status = Status.UNRECOGNIZED;
private boolean isPublic;
private Map<String, String> properties = ImmutableMap.of();
/**
* @see ImageDetails#getMinDisk()
*/
public T minDisk(long minDisk) {
this.minDisk = minDisk;
return self();
}
/**
* @see ImageDetails#getMinRam()
*/
public T minRam(long minRam) {
this.minRam = minRam;
return self();
}
/**
* @see ImageDetails#getLocation()
*/
public T location(Optional<String> location) {
this.location = location;
return self();
}
/**
* @see ImageDetails#getLocation()
*/
public T location(String location) {
return location(Optional.of(location));
}
/**
* @see ImageDetails#getOwner()
*/
public T owner(Optional<String> owner) {
this.owner = owner;
return self();
}
/**
* @see ImageDetails#getOwner()
*/
public T owner(String owner) {
return owner(Optional.of(owner));
}
/**
* @see ImageDetails#getUpdatedAt()
*/
public T updatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
return self();
}
/**
* @see ImageDetails#getCreatedAt()
*/
public T createdAt(Date createdAt) {
this.createdAt = createdAt;
return self();
}
/**
* @see ImageDetails#getDeletedAt()
*/
public T deletedAt(Optional<Date> deletedAt) {
this.deletedAt = deletedAt;
return self();
}
/**
* @see ImageDetails#getDeletedAt()
*/
public T deletedAt(Date deletedAt) {
return deletedAt(Optional.of(deletedAt));
}
/**
* @see ImageDetails#getStatus()
*/
public T status(Status status) {
this.status = status;
return self();
}
/**
* @see ImageDetails#isPublic()
*/
public T isPublic(boolean isPublic) {
this.isPublic = isPublic;
return self();
}
/**
* @see ImageDetails#getProperties()
*/
public T properties(Map<String, String> properties) {
this.properties = properties;
return self();
}
public ImageDetails build() {
return new ImageDetails(this);
}
public T fromImageDetails(ImageDetails in) {
return super.fromImage(in).minDisk(in.getMinDisk()).minRam(in.getMinRam()).location(in.getLocation())
.updatedAt(in.getUpdatedAt()).createdAt(in.getCreatedAt()).deletedAt(in.getDeletedAt()).status(
in.getStatus()).isPublic(in.isPublic()).properties(in.getProperties());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
protected ImageDetails() {
// we want serializers like Gson to work w/o using sun.misc.Unsafe,
// prohibited in GAE. This also implies fields are not final.
// see http://code.google.com/p/jclouds/issues/detail?id=925
}
// | min_disk | int(11) | YES | | NULL | |
@SerializedName("min_disk")
private long minDisk;
// | min_ram | int(11) | YES | | NULL | |
@SerializedName("min_ram")
private long minRam;
// | location | text | YES | | NULL | |
private Optional<String> location = Optional.absent();
// | owner | varchar(255) | YES | | NULL | |
private Optional<String> owner = Optional.absent();
// | updated_at | datetime | YES | | NULL | |
@SerializedName("updated_at")
private Date updatedAt;
// | created_at | datetime | NO | | NULL | |
@SerializedName("created_at")
private Date createdAt;
@SerializedName("deleted_at")
private Optional<Date> deletedAt = Optional.absent();
// | status | varchar(30) | NO | | NULL | |
private Status status = Status.UNRECOGNIZED;
// | is_public | tinyint(1) | NO | | NULL | |
@SerializedName("is_public")
private boolean isPublic;
private Map<String, String> properties = ImmutableMap.of();
protected ImageDetails(Builder<?> builder) {
super(builder);
this.minDisk = builder.minDisk;
this.minRam = checkNotNull(builder.minRam, "minRam");
this.location = checkNotNull(builder.location, "location");
this.owner = checkNotNull(builder.owner, "owner");
this.updatedAt = checkNotNull(builder.updatedAt, "updatedAt");
this.createdAt = checkNotNull(builder.createdAt, "createdAt");
this.deletedAt = checkNotNull(builder.deletedAt, "deletedAt");
this.status = checkNotNull(builder.status, "status");
this.isPublic = checkNotNull(builder.isPublic, "isPublic");
this.properties = ImmutableMap.copyOf(builder.properties);
}
/**
* Note this could be zero if unset
*/
public long getMinDisk() {
return this.minDisk;
}
/**
* Note this could be zero if unset
*/
public long getMinRam() {
return this.minRam;
}
public Status getStatus() {
return this.status;
}
public Optional<String> getLocation() {
return this.location;
}
public Date getUpdatedAt() {
return this.updatedAt;
}
public Date getCreatedAt() {
return this.createdAt;
}
public Optional<Date> getDeletedAt() {
return this.deletedAt;
}
public boolean isPublic() {
return this.isPublic;
}
public Map<String, String> getProperties() {
// in case this was assigned in gson
return ImmutableMap.copyOf(Maps.filterValues(this.properties, Predicates.notNull()));
}
// hashCode/equals from super is ok
@Override
protected ToStringHelper string() {
return super.string().add("minDisk", minDisk).add("minRam", minRam).add("location", location).add("deletedAt",
getDeletedAt()).add("updatedAt", updatedAt).add("createdAt", createdAt).add("status", status).add(
"location", location).add("owner", owner).add("isPublic", isPublic).add("properties", properties);
}
}

View File

@ -18,9 +18,29 @@
*/
package org.jclouds.openstack.glance.v1_0.features;
import java.io.InputStream;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeaders;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Image Services
@ -28,9 +48,52 @@ import org.jclouds.rest.annotations.SkipEncoding;
* @see ImageClient
* @author Adrian Cole
* @see <a href="http://glance.openstack.org/glanceapi.html">api doc</a>
* @see <a href="https://github.com/openstack/glance/blob/master/glance/api/v1/images.py">api src</a>
*/
@SkipEncoding( { '/', '=' })
@RequestFilters(AuthenticateRequest.class)
public interface ImageAsyncClient {
/**
* @see ImageClient#list
*/
@GET
@SelectJson("images")
@Consumes(MediaType.APPLICATION_JSON)
@Path("/images")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<Image>> list();
/**
* @see ImageClient#listInDetail
*/
@GET
@SelectJson("images")
@Consumes(MediaType.APPLICATION_JSON)
@Path("/images/detail")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<ImageDetails>> listInDetail();
/**
* @see ImageClient#show
*/
@HEAD
@Path("/images/{id}")
@ResponseParser(ParseImageDetailsFromHeaders.class)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<ImageDetails> show(@PathParam("id") String id);
/**
* @see ImageClient#getAsStream
*/
@GET
@Path("/images/{id}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<InputStream> getAsStream(@PathParam("id") String id);
// POST /images -- Store image data and return metadata about the
// newly-stored image
// PUT /images/<ID> -- Update image metadata and/or upload image
// data for a previously-reserved image
// DELETE /images/<ID> -- Delete the image with id <ID>
}

View File

@ -18,9 +18,14 @@
*/
package org.jclouds.openstack.glance.v1_0.features;
import java.io.InputStream;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
/**
* Image Services
@ -31,5 +36,31 @@ import org.jclouds.concurrent.Timeout;
*/
@Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
public interface ImageClient {
/**
* Returns a set of brief metadata about images
*/
Set<Image> list();
/**
* Returns a set of detailed metadata about images
*/
Set<ImageDetails> listInDetail();
/**
* Return metadata about an image with id
*/
@Nullable
ImageDetails show(String id);
/**
* Return image data for image with id
*/
@Nullable
InputStream getAsStream(String id);
// POST /images -- Store image data and return metadata about the
// newly-stored image
// PUT /images/<ID> -- Update image metadata and/or upload image
// data for a previously-reserved image
// DELETE /images/<ID> -- Delete the image with id <ID>
}

View File

@ -0,0 +1,67 @@
/**
* 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.glance.v1_0.functions;
import javax.inject.Inject;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import com.google.common.base.Function;
/**
* This parses {@link ImageDetails} from HTTP headers.
*
* @author Adrian Cole
*/
public class ParseImageDetailsFromHeaders implements Function<HttpResponse, ImageDetails> {
private final DateService dateService;
@Inject
public ParseImageDetailsFromHeaders(DateService dateService) {
this.dateService = dateService;
}
public ImageDetails apply(HttpResponse from) {
ImageDetails.Builder<?> builder = ImageDetails.builder()
.id(from.getFirstHeaderOrNull("X-Image-Meta-Id"))
.name(from.getFirstHeaderOrNull("X-Image-Meta-Name"))
.checksum(from.getFirstHeaderOrNull("X-Image-Meta-Checksum"))
.containerFormat(ContainerFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Container_format")))
.diskFormat(DiskFormat.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Disk_format")))
.size(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Size")))
.minDisk(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_disk")))
.minRam(Long.parseLong(from.getFirstHeaderOrNull("X-Image-Meta-Min_ram")))
.isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull("X-Image-Meta-Is_public")))
.createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Created_at")))
.updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull("X-Image-Meta-Updated_at")))
.owner(from.getFirstHeaderOrNull("X-Image-Meta-Owner"))
.status(Image.Status.fromValue(from.getFirstHeaderOrNull("X-Image-Meta-Status")));
String deletedAt = from.getFirstHeaderOrNull("X-Image-Meta-Deleted_at");
if (deletedAt != null)
builder.deletedAt(dateService.iso8601SecondsDateParse(deletedAt));
return builder.build();
}
}

View File

@ -18,13 +18,185 @@
*/
package org.jclouds.openstack.glance.v1_0.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.glance.v1_0.GlanceClient;
import org.jclouds.openstack.glance.v1_0.functions.ParseImageDetailsFromHeadersTest;
import org.jclouds.openstack.glance.v1_0.internal.BaseGlanceClientExpectTest;
import org.jclouds.openstack.glance.v1_0.parse.ParseImagesInDetailTest;
import org.jclouds.openstack.glance.v1_0.parse.ParseImagesTest;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ImageClientExpectTest")
public class ImageClientExpectTest extends BaseGlanceClientExpectTest {
public void testListWhenResponseIs2xx() throws Exception {
HttpRequest list = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/images.json")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, list, listResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").list().toString(),
new ParseImagesTest().expected().toString());
}
public void testListWhenReponseIs404IsEmpty() throws Exception {
HttpRequest list = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenNoExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, list, listResponse);
assertTrue(clientWhenNoExist.getImageClientForRegion("az-1.region-a.geo-1").list().isEmpty());
}
public void testListInDetailWhenResponseIs2xx() throws Exception {
HttpRequest listInDetail = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/detail"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse listInDetailResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResource("/images_detail.json")).build();
GlanceClient clientWhenExistInDetail = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, listInDetail, listInDetailResponse);
assertEquals(clientWhenExistInDetail.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExistInDetail.getImageClientForRegion("az-1.region-a.geo-1").listInDetail().toString(),
new ParseImagesInDetailTest().expected().toString());
}
public void testListInDetailWhenReponseIs404IsEmpty() throws Exception {
HttpRequest listInDetail = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/detail"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("Accept", "application/json")
.put("X-Auth-Token", authToken).build()).build();
HttpResponse listInDetailResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenNoExistInDetail = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, listInDetail, listInDetailResponse);
assertTrue(clientWhenNoExistInDetail.getImageClientForRegion("az-1.region-a.geo-1").listInDetail().isEmpty());
}
public void testShowWhenResponseIs2xx() throws Exception {
HttpRequest show = HttpRequest
.builder()
.method("HEAD")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("X-Auth-Token", authToken).build()).build();
HttpResponse showResponse = new ParseImageDetailsFromHeadersTest().response;
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, show, showResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").show("fcc451d0-f6e4-4824-ad8f-70ec12326d07").toString(),
new ParseImageDetailsFromHeadersTest().expected().toString());
}
public void testShowWhenReponseIs404IsNull() throws Exception {
HttpRequest show = HttpRequest
.builder()
.method("HEAD")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("X-Auth-Token", authToken).build()).build();
HttpResponse showResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenNoExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, show, showResponse);
assertNull(clientWhenNoExist.getImageClientForRegion("az-1.region-a.geo-1").show("fcc451d0-f6e4-4824-ad8f-70ec12326d07"));
}
public void testGetAsStreamWhenResponseIs2xx() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("X-Auth-Token", authToken).build()).build();
HttpResponse getResponse = HttpResponse.builder().statusCode(200).payload(Payloads.newStringPayload("foo")).build();
GlanceClient clientWhenExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, getResponse);
assertEquals(clientWhenExist.getConfiguredRegions(), ImmutableSet.of("az-1.region-a.geo-1"));
assertEquals(Strings2.toStringAndClose(clientWhenExist.getImageClientForRegion("az-1.region-a.geo-1").getAsStream("fcc451d0-f6e4-4824-ad8f-70ec12326d07")),
"foo");
}
public void testGetAsStreamWhenReponseIs404IsNull() throws Exception {
HttpRequest get = HttpRequest
.builder()
.method("GET")
.endpoint(URI.create("https://glance.jclouds.org:9292/v1.0/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07"))
.headers(
ImmutableMultimap.<String, String> builder()
.put("X-Auth-Token", authToken).build()).build();
HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
GlanceClient clientWhenNoExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
responseWithKeystoneAccess, get, getResponse);
assertNull(clientWhenNoExist.getImageClientForRegion("az-1.region-a.geo-1").getAsStream("fcc451d0-f6e4-4824-ad8f-70ec12326d07"));
}
}

View File

@ -18,6 +18,12 @@
*/
package org.jclouds.openstack.glance.v1_0.features;
import static org.testng.Assert.assertEquals;
import java.util.Set;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.openstack.glance.v1_0.internal.BaseGlanceClientLiveTest;
import org.testng.annotations.Test;
@ -27,4 +33,49 @@ import org.testng.annotations.Test;
@Test(groups = "live", testName = "ImageClientLiveTest")
public class ImageClientLiveTest extends BaseGlanceClientLiveTest {
@Test
public void testList() throws Exception {
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
Set<Image> response = client.list();
assert null != response;
for (Image image : response) {
checkImage(image);
}
}
}
private void checkImage(Image image) {
assert image.getId() != null : image;
assert image.getSize().isPresent() : image;
assert image.getChecksum().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
assert image.getContainerFormat().isPresent() : image;
}
@Test
public void testListInDetail() throws Exception {
for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
ImageClient client = glanceContext.getApi().getImageClientForRegion(zoneId);
Set<ImageDetails> response = client.listInDetail();
assert null != response;
for (ImageDetails image : response) {
checkImage(image);
ImageDetails newDetails = client.show(image.getId());
checkImageDetails(newDetails);
checkImageDetailsEqual(image, newDetails);
}
}
}
private void checkImageDetails(ImageDetails image) {
//TODO
}
private void checkImageDetailsEqual(ImageDetails image, ImageDetails newDetails) {
assertEquals(newDetails.getId(), image.getId());
assertEquals(newDetails.getName(), image.getName());
assertEquals(newDetails.getLinks(), image.getLinks());
}
}

View File

@ -0,0 +1,85 @@
/**
* 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.glance.v1_0.functions;
import static org.testng.Assert.assertEquals;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ParseImageDetailsFromHeadersTest")
public class ParseImageDetailsFromHeadersTest {
ParseImageDetailsFromHeaders fn = new ParseImageDetailsFromHeaders(new SimpleDateFormatDateService());
public HttpResponse response = HttpResponse.builder()
.message("HTTP/1.1 200 OK")
.statusCode(200)
.headers(ImmutableMultimap.<String,String>builder()
.put("X-Image-Meta-Id", "fcc451d0-f6e4-4824-ad8f-70ec12326d07")
.put("X-Image-Meta-Deleted", "False")
.put("X-Image-Meta-Container_format", "bare")
.put("X-Image-Meta-Checksum", "233afa7b8809d840679b5f0d36d7350a")
.put("X-Image-Meta-Protected", "False")
.put("X-Image-Meta-Min_disk", "0")
.put("X-Image-Meta-Created_at", "2012-05-18T18:06:44")
.put("X-Image-Meta-Size", "65645798")
.put("X-Image-Meta-Status", "active")
.put("X-Image-Meta-Is_public", "True")
.put("X-Image-Meta-Min_ram", "0")
.put("X-Image-Meta-Owner", "5821675")
.put("X-Image-Meta-Updated_at", "2012-05-18T18:42:58")
.put("X-Image-Meta-Disk_format", "raw")
.put("X-Image-Meta-Name", "debian")
.put("Location", "http://HOST/v1/images/fcc451d0-f6e4-4824-ad8f-70ec12326d07")
.put("Etag", "233afa7b8809d840679b5f0d36d7350a")
.build())
.build();
public void test() {
assertEquals(fn.apply(response).toString(), expected().toString());
}
public ImageDetails expected() {
return ImageDetails.builder()
.id("fcc451d0-f6e4-4824-ad8f-70ec12326d07")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.status(Image.Status.ACTIVE)
.owner("5821675")
.isPublic(true)
.createdAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-18T18:06:44"))
.updatedAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-18T18:42:58"))
.build();
}
}

View File

@ -0,0 +1,85 @@
/**
* 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.glance.v1_0.parse;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ParseImageDetailsListTest")
public class ParseImagesInDetailTest extends BaseSetParserTest<ImageDetails> {
@Override
public String resource() {
return "/images_detail.json";
}
@Override
@SelectJson("images")
@Consumes(MediaType.APPLICATION_JSON)
public Set<ImageDetails> expected() {
return ImmutableSet.<ImageDetails>builder()
.add(ImageDetails
.builder()
.id("fcc451d0-f6e4-4824-ad8f-70ec12326d07")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.status(Image.Status.ACTIVE)
.owner("5821675")
.isPublic(true)
.createdAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-18T18:06:44"))
.updatedAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-18T18:06:45"))
.build())
.add(ImageDetails
.builder()
.id("f9fcb127-071d-4670-883e-eedb7efac183")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.status(Image.Status.ACTIVE)
.owner("5821675")
.isPublic(true)
.createdAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T15:04:47"))
.updatedAt(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T15:04:48"))
.build())
.build();
}
}

View File

@ -0,0 +1,81 @@
/**
* 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.glance.v1_0.parse;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.json.BaseSetParserTest;
import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
import org.jclouds.openstack.glance.v1_0.domain.Image;
import org.jclouds.rest.annotations.SelectJson;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ParseImageListTest")
public class ParseImagesTest extends BaseSetParserTest<Image> {
@Override
public String resource() {
return "/images.json";
}
@Override
@SelectJson("images")
@Consumes(MediaType.APPLICATION_JSON)
public Set<Image> expected() {
return ImmutableSet.<Image>builder()
.add(Image
.builder()
.id("f0209a30-25b8-4d9a-8e2f-dbc028e20b2b")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.build())
.add(Image
.builder()
.id("fcc451d0-f6e4-4824-ad8f-70ec12326d07")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.build())
.add(Image
.builder()
.id("f9fcb127-071d-4670-883e-eedb7efac183")
.name("debian")
.containerFormat(ContainerFormat.BARE)
.diskFormat(DiskFormat.RAW)
.checksum("233afa7b8809d840679b5f0d36d7350a")
.size(65645798l)
.build())
.build();
}
}

View File

@ -0,0 +1,24 @@
{
"images": [{
"name": "debian",
"container_format": "bare",
"disk_format": "raw",
"checksum": "233afa7b8809d840679b5f0d36d7350a",
"id": "f0209a30-25b8-4d9a-8e2f-dbc028e20b2b",
"size": 65645798
}, {
"name": "debian",
"container_format": "bare",
"disk_format": "raw",
"checksum": "233afa7b8809d840679b5f0d36d7350a",
"id": "fcc451d0-f6e4-4824-ad8f-70ec12326d07",
"size": 65645798
}, {
"name": "debian",
"container_format": "bare",
"disk_format": "raw",
"checksum": "233afa7b8809d840679b5f0d36d7350a",
"id": "f9fcb127-071d-4670-883e-eedb7efac183",
"size": 65645798
}]
}

View File

@ -0,0 +1,39 @@
{
"images": [{
"status": "active",
"name": "debian",
"deleted": false,
"container_format": "bare",
"created_at": "2012-05-18T18:06:44",
"disk_format": "raw",
"updated_at": "2012-05-18T18:06:45",
"properties": {},
"min_disk": 0,
"protected": false,
"id": "fcc451d0-f6e4-4824-ad8f-70ec12326d07",
"checksum": "233afa7b8809d840679b5f0d36d7350a",
"owner": "5821675",
"is_public": true,
"deleted_at": null,
"min_ram": 0,
"size": 65645798
}, {
"status": "active",
"name": "debian",
"deleted": false,
"container_format": "bare",
"created_at": "2012-05-11T15:04:47",
"disk_format": "raw",
"updated_at": "2012-05-11T15:04:48",
"properties": {},
"min_disk": 0,
"protected": false,
"id": "f9fcb127-071d-4670-883e-eedb7efac183",
"checksum": "233afa7b8809d840679b5f0d36d7350a",
"owner": "5821675",
"is_public": true,
"deleted_at": null,
"min_ram": 0,
"size": 65645798
}]
}