JCLOUDS-160: Support tags in EC2 images

Based on the work made by Brock Noland
This commit is contained in:
Ignasi Barrera 2013-07-05 00:43:47 +02:00
parent 8db0218cf7
commit bbfec4a990
6 changed files with 99 additions and 13 deletions

View File

@ -59,6 +59,7 @@ public class Image implements Comparable<Image> {
@Nullable @Nullable
private final String rootDeviceName; private final String rootDeviceName;
private final Map<String, EbsBlockDevice> ebsBlockDevices = Maps.newHashMap(); private final Map<String, EbsBlockDevice> ebsBlockDevices = Maps.newHashMap();
private final Map<String, String> tags = Maps.newLinkedHashMap();
private final VirtualizationType virtualizationType; private final VirtualizationType virtualizationType;
public VirtualizationType getVirtualizationType() { public VirtualizationType getVirtualizationType() {
@ -76,7 +77,7 @@ public class Image implements Comparable<Image> {
ImageType imageType, boolean isPublic, Iterable<String> productCodes, @Nullable String kernelId, ImageType imageType, boolean isPublic, Iterable<String> productCodes, @Nullable String kernelId,
@Nullable String platform, @Nullable String ramdiskId, RootDeviceType rootDeviceType, @Nullable String platform, @Nullable String ramdiskId, RootDeviceType rootDeviceType,
@Nullable String rootDeviceName, Map<String, EbsBlockDevice> ebsBlockDevices, @Nullable String rootDeviceName, Map<String, EbsBlockDevice> ebsBlockDevices,
VirtualizationType virtualizationType, Hypervisor hypervisor) { Map<String, String> tags, VirtualizationType virtualizationType, Hypervisor hypervisor) {
this.region = checkNotNull(region, "region"); this.region = checkNotNull(region, "region");
this.architecture = architecture; this.architecture = architecture;
this.imageId = checkNotNull(imageId, "imageId"); this.imageId = checkNotNull(imageId, "imageId");
@ -95,6 +96,7 @@ public class Image implements Comparable<Image> {
this.ramdiskId = ramdiskId; this.ramdiskId = ramdiskId;
this.rootDeviceType = checkNotNull(rootDeviceType, "rootDeviceType"); this.rootDeviceType = checkNotNull(rootDeviceType, "rootDeviceType");
this.ebsBlockDevices.putAll(checkNotNull(ebsBlockDevices, "ebsBlockDevices")); this.ebsBlockDevices.putAll(checkNotNull(ebsBlockDevices, "ebsBlockDevices"));
this.tags.putAll(checkNotNull(tags, "tags"));
this.virtualizationType = checkNotNull(virtualizationType, "virtualizationType"); this.virtualizationType = checkNotNull(virtualizationType, "virtualizationType");
this.hypervisor = checkNotNull(hypervisor, "hypervisor"); this.hypervisor = checkNotNull(hypervisor, "hypervisor");
} }
@ -346,6 +348,10 @@ public class Image implements Comparable<Image> {
return ebsBlockDevices; return ebsBlockDevices;
} }
public Map<String, String> getTags() {
return tags;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
@ -353,6 +359,7 @@ public class Image implements Comparable<Image> {
result = prime * result + ((architecture == null) ? 0 : architecture.hashCode()); result = prime * result + ((architecture == null) ? 0 : architecture.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode()); result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((ebsBlockDevices == null) ? 0 : ebsBlockDevices.hashCode()); result = prime * result + ((ebsBlockDevices == null) ? 0 : ebsBlockDevices.hashCode());
result = prime * result + ((tags == null) ? 0 : tags.hashCode());
result = prime * result + ((imageId == null) ? 0 : imageId.hashCode()); result = prime * result + ((imageId == null) ? 0 : imageId.hashCode());
result = prime * result + ((imageLocation == null) ? 0 : imageLocation.hashCode()); result = prime * result + ((imageLocation == null) ? 0 : imageLocation.hashCode());
result = prime * result + ((imageOwnerId == null) ? 0 : imageOwnerId.hashCode()); result = prime * result + ((imageOwnerId == null) ? 0 : imageOwnerId.hashCode());
@ -395,6 +402,11 @@ public class Image implements Comparable<Image> {
return false; return false;
} else if (!ebsBlockDevices.equals(other.ebsBlockDevices)) } else if (!ebsBlockDevices.equals(other.ebsBlockDevices))
return false; return false;
if (tags == null) {
if (other.tags != null)
return false;
} else if (!tags.equals(other.tags))
return false;
if (imageId == null) { if (imageId == null) {
if (other.imageId != null) if (other.imageId != null)
return false; return false;
@ -478,7 +490,7 @@ public class Image implements Comparable<Image> {
+ ", kernelId=" + kernelId + ", name=" + name + ", platform=" + platform + ", productCodes=" + ", kernelId=" + kernelId + ", name=" + name + ", platform=" + platform + ", productCodes="
+ productCodes + ", ramdiskId=" + ramdiskId + ", region=" + region + ", rootDeviceName=" + productCodes + ", ramdiskId=" + ramdiskId + ", region=" + region + ", rootDeviceName="
+ rootDeviceName + ", rootDeviceType=" + rootDeviceType + ", virtualizationType=" + virtualizationType + rootDeviceName + ", rootDeviceType=" + rootDeviceType + ", virtualizationType=" + virtualizationType
+ ", hypervisor=" + hypervisor + "]"; + ", hypervisor=" + hypervisor + ", tags=" + tags + "]";
} }
} }

View File

@ -17,6 +17,7 @@
package org.jclouds.ec2.xml; package org.jclouds.ec2.xml;
import static org.jclouds.util.SaxUtils.currentOrNull; import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -54,8 +55,9 @@ import com.google.common.collect.Sets;
public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Set<Image>> { public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Set<Image>> {
@Inject @Inject
public DescribeImagesResponseHandler(@Region Supplier<String> defaultRegion) { public DescribeImagesResponseHandler(@Region Supplier<String> defaultRegion, TagSetHandler tagSetHandler) {
this.defaultRegion = defaultRegion; this.defaultRegion = defaultRegion;
this.tagSetHandler = tagSetHandler;
} }
@Resource @Resource
@ -64,6 +66,7 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR
protected Set<Image> contents = Sets.newLinkedHashSet(); protected Set<Image> contents = Sets.newLinkedHashSet();
private StringBuilder currentText = new StringBuilder(); private StringBuilder currentText = new StringBuilder();
private final Supplier<String> defaultRegion; private final Supplier<String> defaultRegion;
private final TagSetHandler tagSetHandler;
private Architecture architecture; private Architecture architecture;
private String name; private String name;
@ -81,8 +84,10 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR
private String ramdiskId; private String ramdiskId;
private boolean inProductCodes; private boolean inProductCodes;
private boolean inBlockDeviceMapping; private boolean inBlockDeviceMapping;
private boolean inTagSet;
private RootDeviceType rootDeviceType = RootDeviceType.INSTANCE_STORE; private RootDeviceType rootDeviceType = RootDeviceType.INSTANCE_STORE;
private Map<String, EbsBlockDevice> ebsBlockDevices = Maps.newHashMap(); private Map<String, EbsBlockDevice> ebsBlockDevices = Maps.newHashMap();
private Map<String, String> tags = Maps.newLinkedHashMap();
private String deviceName; private String deviceName;
private String snapshotId; private String snapshotId;
private VirtualizationType virtualizationType = VirtualizationType.PARAVIRTUAL; private VirtualizationType virtualizationType = VirtualizationType.PARAVIRTUAL;
@ -102,10 +107,21 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR
inProductCodes = true; inProductCodes = true;
} else if (qName.equals("blockDeviceMapping")) { } else if (qName.equals("blockDeviceMapping")) {
inBlockDeviceMapping = true; inBlockDeviceMapping = true;
} else if (equalsOrSuffix(qName, "tagSet")) {
inTagSet = true;
}
if (inTagSet) {
tagSetHandler.startElement(uri, name, qName, attrs);
} }
} }
public void endElement(String uri, String name, String qName) { public void endElement(String uri, String name, String qName) {
if (equalsOrSuffix(qName, "tagSet")) {
inTagSet = false;
tags = tagSetHandler.getResult();
} else if (inTagSet) {
tagSetHandler.endElement(uri, name, qName);
}
if (qName.equals("architecture")) { if (qName.equals("architecture")) {
architecture = Architecture.fromValue(currentText.toString().trim()); architecture = Architecture.fromValue(currentText.toString().trim());
// Nova Diablo uses the wrong name for this field // Nova Diablo uses the wrong name for this field
@ -161,14 +177,14 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR
this.snapshotId = null; this.snapshotId = null;
this.volumeSize = 0; this.volumeSize = 0;
this.deleteOnTermination = true; this.deleteOnTermination = true;
} else if (!inProductCodes) { } else if (!inTagSet && !inProductCodes) {
try { try {
String region = getRequest() != null ? AWSUtils.findRegionInArgsOrNull(getRequest()) : null; String region = getRequest() != null ? AWSUtils.findRegionInArgsOrNull(getRequest()) : null;
if (region == null) if (region == null)
region = defaultRegion.get(); region = defaultRegion.get();
contents.add(new Image(region, architecture, this.name, description, imageId, imageLocation, contents.add(new Image(region, architecture, this.name, description, imageId, imageLocation,
imageOwnerId, imageState, rawState, imageType, isPublic, productCodes, kernelId, platform, imageOwnerId, imageState, rawState, imageType, isPublic, productCodes, kernelId, platform,
ramdiskId, rootDeviceType, rootDeviceName, ebsBlockDevices, virtualizationType, hypervisor)); ramdiskId, rootDeviceType, rootDeviceName, ebsBlockDevices, tags, virtualizationType, hypervisor));
} catch (NullPointerException e) { } catch (NullPointerException e) {
logger.warn(e, "malformed image: %s", imageId); logger.warn(e, "malformed image: %s", imageId);
} }
@ -198,6 +214,10 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerForGeneratedR
} }
public void characters(char ch[], int start, int length) { public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length); if (inTagSet) {
tagSetHandler.characters(ch, start, length);
} else {
currentText.append(ch, start, length);
}
} }
} }

View File

@ -59,7 +59,7 @@ public class DescribeImagesResponseHandlerTest {
"ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml", "206029621532", ImageState.AVAILABLE, "available", "ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml", "206029621532", ImageState.AVAILABLE, "available",
ImageType.MACHINE, false, Sets.<String> newHashSet("9961934F"), "aki-4438dd2d", null, "ari-4538dd2c", ImageType.MACHINE, false, Sets.<String> newHashSet("9961934F"), "aki-4438dd2d", null, "ari-4538dd2c",
RootDeviceType.INSTANCE_STORE, null, ImmutableMap.<String, EbsBlockDevice> of(), RootDeviceType.INSTANCE_STORE, null, ImmutableMap.<String, EbsBlockDevice> of(),
VirtualizationType.PARAVIRTUAL, Hypervisor.XEN)); ImmutableMap.<String, String> of(), VirtualizationType.PARAVIRTUAL, Hypervisor.XEN));
Set<Image> result = parseImages("/describe_images.xml"); Set<Image> result = parseImages("/describe_images.xml");
@ -73,7 +73,7 @@ public class DescribeImagesResponseHandlerTest {
"aws-solutions-amis/SqlSvrStd2003r2-x86_64-Win_SFWBasic5.1-v1.0.manifest.xml", "771350841976", "aws-solutions-amis/SqlSvrStd2003r2-x86_64-Win_SFWBasic5.1-v1.0.manifest.xml", "771350841976",
ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets.<String> newHashSet("5771E9A6"), null, "windows", ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets.<String> newHashSet("5771E9A6"), null, "windows",
null, RootDeviceType.INSTANCE_STORE, null, ImmutableMap.<String, EbsBlockDevice> of(), null, RootDeviceType.INSTANCE_STORE, null, ImmutableMap.<String, EbsBlockDevice> of(),
VirtualizationType.PARAVIRTUAL, Hypervisor.XEN)); ImmutableMap.<String, String> of(), VirtualizationType.PARAVIRTUAL, Hypervisor.XEN));
Set<Image> result = parseImages("/describe_images_windows.xml"); Set<Image> result = parseImages("/describe_images_windows.xml");
@ -89,7 +89,7 @@ public class DescribeImagesResponseHandlerTest {
ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets.<String> newHashSet(), null, "windows", null, ImageState.AVAILABLE, "available", ImageType.MACHINE, true, Sets.<String> newHashSet(), null, "windows", null,
RootDeviceType.EBS, "/dev/sda1", ImmutableMap.<String, EbsBlockDevice> of("/dev/sda1", RootDeviceType.EBS, "/dev/sda1", ImmutableMap.<String, EbsBlockDevice> of("/dev/sda1",
new EbsBlockDevice("snap-d01272b9", 30, true), "xvdf", new EbsBlockDevice("snap-d31272ba", 250, new EbsBlockDevice("snap-d01272b9", 30, true), "xvdf", new EbsBlockDevice("snap-d31272ba", 250,
false)), VirtualizationType.HVM, Hypervisor.XEN)); false)), ImmutableMap.<String, String> of(), VirtualizationType.HVM, Hypervisor.XEN));
Set<Image> result = parseImages("/describe_images_ebs.xml"); Set<Image> result = parseImages("/describe_images_ebs.xml");
@ -98,12 +98,29 @@ public class DescribeImagesResponseHandlerTest {
assertEquals(get(result, 0).getRawState(), "available"); assertEquals(get(result, 0).getRawState(), "available");
} }
public void testTags() {
Set<Image> contents = ImmutableSet.of(new Image("us-east-1", Architecture.I386, null, null, "ami-be3adfd7",
"ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml", "206029621532", ImageState.AVAILABLE, "available",
ImageType.MACHINE, false, Sets.<String> newHashSet("9961934F"), "aki-4438dd2d", null, "ari-4538dd2c",
RootDeviceType.INSTANCE_STORE, null, ImmutableMap.<String, EbsBlockDevice> of(),
ImmutableMap.<String, String> of("Name", "Some machine name", "Second", "Second value"),
VirtualizationType.PARAVIRTUAL, Hypervisor.XEN));
Set<Image> result = parseImages("/describe_images_tags.xml");
assertEquals(result.toString(), contents.toString());
assertEquals(get(result, 0).getImageState(), ImageState.AVAILABLE);
assertEquals(get(result, 0).getRawState(), "available");
assertEquals(get(result, 0).getTags().get("Name"), "Some machine name");
assertEquals(get(result, 0).getTags().get("Second"), "Second value");
}
public void testDiabloWithIncorrectDisplayNameField() { public void testDiabloWithIncorrectDisplayNameField() {
Set<Image> contents = ImmutableSet.of(new Image("us-east-1", Architecture.X86_64, "CentOS 6.2 Server 64-bit 20120125", "", "ami-0000054e", Set<Image> contents = ImmutableSet.of(new Image("us-east-1", Architecture.X86_64, "CentOS 6.2 Server 64-bit 20120125", "", "ami-0000054e",
"local (CentOS 6.2 Server 64-bit 20120125)", "", ImageState.AVAILABLE, "available", "local (CentOS 6.2 Server 64-bit 20120125)", "", ImageState.AVAILABLE, "available",
ImageType.MACHINE, true, Sets.<String> newHashSet(), "aki-0000054c", null, "ari-0000054d", ImageType.MACHINE, true, Sets.<String> newHashSet(), "aki-0000054c", null, "ari-0000054d",
RootDeviceType.INSTANCE_STORE, "/dev/sda1", ImmutableMap.<String, EbsBlockDevice> of(), RootDeviceType.INSTANCE_STORE, "/dev/sda1", ImmutableMap.<String, EbsBlockDevice> of(),
VirtualizationType.PARAVIRTUAL, Hypervisor.XEN)); ImmutableMap.<String, String> of(), VirtualizationType.PARAVIRTUAL, Hypervisor.XEN));
Set<Image> result = parseImages("/describe_images_nova.xml"); Set<Image> result = parseImages("/describe_images_nova.xml");

View File

@ -0,0 +1,33 @@
<DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<imagesSet>
<item>
<imageId>ami-be3adfd7</imageId>
<imageLocation>ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml</imageLocation>
<imageState>available</imageState>
<imageOwnerId>206029621532</imageOwnerId>
<isPublic>false</isPublic>
<productCodes>
<item>
<productCode>9961934F</productCode>
</item>
</productCodes>
<architecture>i386</architecture>
<imageType>machine</imageType>
<kernelId>aki-4438dd2d</kernelId>
<ramdiskId>ari-4538dd2c</ramdiskId>
<rootDeviceType>instance-store</rootDeviceType>
<blockDeviceMapping />
<virtualizationType>paravirtual</virtualizationType>
<tagSet>
<item>
<key>Name</key>
<value>Some machine name</value>
</item>
<item>
<key>Second</key>
<value>Second value</value>
</item>
</tagSet>
</item>
</imagesSet>
</DescribeImagesResponse>

View File

@ -23,6 +23,7 @@ import javax.inject.Inject;
import org.jclouds.ec2.domain.Image; import org.jclouds.ec2.domain.Image;
import org.jclouds.ec2.domain.Image.ImageType; import org.jclouds.ec2.domain.Image.ImageType;
import org.jclouds.ec2.xml.DescribeImagesResponseHandler; import org.jclouds.ec2.xml.DescribeImagesResponseHandler;
import org.jclouds.ec2.xml.TagSetHandler;
import org.jclouds.location.Region; import org.jclouds.location.Region;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -37,8 +38,8 @@ import com.google.common.collect.Iterables;
*/ */
public class NovaDescribeImagesResponseHandler extends DescribeImagesResponseHandler { public class NovaDescribeImagesResponseHandler extends DescribeImagesResponseHandler {
@Inject @Inject
public NovaDescribeImagesResponseHandler(@Region Supplier<String> defaultRegion) { public NovaDescribeImagesResponseHandler(@Region Supplier<String> defaultRegion, TagSetHandler tagSetHandler) {
super(defaultRegion); super(defaultRegion, tagSetHandler);
} }
public Set<Image> getResult() { public Set<Image> getResult() {

View File

@ -100,9 +100,12 @@ public class AWSEC2ReviseParsedImageTest {
RootDeviceType rootDeviceType = RootDeviceType.EBS; RootDeviceType rootDeviceType = RootDeviceType.EBS;
String rootDeviceName = ""; String rootDeviceName = "";
Map<String, Image.EbsBlockDevice> ebsBlockDevices = ImmutableMap.of(); Map<String, Image.EbsBlockDevice> ebsBlockDevices = ImmutableMap.of();
Map<String, String> tags = ImmutableMap.of();
VirtualizationType virtualizationType = VirtualizationType.HVM; VirtualizationType virtualizationType = VirtualizationType.HVM;
Hypervisor hypervisor = Hypervisor.XEN; Hypervisor hypervisor = Hypervisor.XEN;
Image from = new Image(region, architecture, imageName, description, imageId, imageOwnerId + "/" + imageName, imageOwnerId, imageState, "available", imageType, isPublic, productCodes, kernelId, platform, ramdiskId, rootDeviceType, rootDeviceName, ebsBlockDevices, virtualizationType, hypervisor); Image from = new Image(region, architecture, imageName, description, imageId, imageOwnerId + "/" + imageName,
imageOwnerId, imageState, "available", imageType, isPublic, productCodes, kernelId, platform, ramdiskId,
rootDeviceType, rootDeviceName, ebsBlockDevices, tags, virtualizationType, hypervisor);
return from; return from;
} }
} }