Merge pull request #273 from alasdairhodge/CloudSigma-SSD-support

CloudSigma support for drive tags and SSD affinity
This commit is contained in:
Adrian Cole 2011-12-21 18:41:23 -08:00
commit cd9b140ad0
17 changed files with 297 additions and 42 deletions

View File

@ -219,6 +219,7 @@ public interface CloudSigmaClient {
* options to control size
* @return new drive
*/
@Timeout(duration = 300, timeUnit = TimeUnit.SECONDS)
DriveInfo cloneDrive(String sourceUuid, String newName, CloneDriveOptions... options);
/**

View File

@ -0,0 +1,51 @@
/**
* 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.cloudsigma.domain;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Option for the cloneDrive operation.
* 'HDD' to specifies a regular "spinning oxide" disk; 'SSD' specifies a solid-state drive.
*
* @author Alasdair Hodge
*/
public enum AffinityType {
HDD,
SSD,
UNRECOGNIZED;
public String value() {
return name().toLowerCase();
}
@Override
public String toString() {
return value();
}
public static AffinityType fromValue(String affinity) {
try {
return valueOf(checkNotNull(affinity, "affinity").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}

View File

@ -64,6 +64,14 @@ public class CreateDriveRequest extends Drive {
return Builder.class.cast(super.name(name));
}
/**
* {@inheritDoc}
*/
@Override
public Builder tags(Iterable<String> tags) {
return Builder.class.cast(super.tags(tags));
}
/**
* {@inheritDoc}
*/
@ -89,7 +97,7 @@ public class CreateDriveRequest extends Drive {
}
public CreateDriveRequest build() {
return new CreateDriveRequest(name, size, claimType, readers, use, encryptionCipher, avoid);
return new CreateDriveRequest(name, size, claimType, tags, readers, use, encryptionCipher, avoid);
}
}
@ -97,9 +105,9 @@ public class CreateDriveRequest extends Drive {
@Nullable
private final String encryptionCipher;
public CreateDriveRequest(String name, long size, @Nullable ClaimType claimType, Iterable<String> readers,
Iterable<String> use, @Nullable String encryptionCipher, Iterable<String> avoid) {
super(null, name, size, claimType, readers, use);
public CreateDriveRequest(String name, long size, @Nullable ClaimType claimType, Iterable<String> tags,
Iterable<String> readers, Iterable<String> use, @Nullable String encryptionCipher, Iterable<String> avoid) {
super(null, name, size, claimType, tags, readers, use);
this.encryptionCipher = encryptionCipher;
this.avoid = ImmutableSet.copyOf(checkNotNull(avoid, "avoid"));
}
@ -155,7 +163,8 @@ public class CreateDriveRequest extends Drive {
@Override
public String toString() {
return "[name=" + name + ", size=" + size + ", claimType=" + claimType + ", readers=" + readers + ", use=" + use
+ ", avoid=" + avoid + ", encryptionCipher=" + encryptionCipher + "]";
return "[name=" + name + ", size=" + size + ", claimType=" + claimType + ", tags=" + tags
+ ", readers=" + readers + ", use=" + use + ", avoid=" + avoid
+ ", encryptionCipher=" + encryptionCipher + "]";
}
}

View File

@ -24,6 +24,7 @@ import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
/**
@ -34,6 +35,7 @@ public class Drive extends Item {
public static class Builder extends Item.Builder {
protected long size;
protected ClaimType claimType = ClaimType.EXCLUSIVE;
protected Set<String> tags = ImmutableSet.of();
protected Set<String> readers = ImmutableSet.of();
public Builder claimType(ClaimType claimType) {
@ -41,6 +43,11 @@ public class Drive extends Item {
return this;
}
public Builder tags(Iterable<String> tags) {
this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
return this;
}
public Builder readers(Iterable<String> readers) {
this.readers = ImmutableSet.copyOf(checkNotNull(readers, "readers"));
return this;
@ -76,7 +83,7 @@ public class Drive extends Item {
}
public Drive build() {
return new Drive(uuid, name, size, claimType, readers, use);
return new Drive(uuid, name, size, claimType, tags, readers, use);
}
@Override
@ -84,6 +91,7 @@ public class Drive extends Item {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((claimType == null) ? 0 : claimType.hashCode());
result = prime * result + ((tags == null) ? 0 : tags.hashCode());
result = prime * result + ((readers == null) ? 0 : readers.hashCode());
result = prime * result + (int) (size ^ (size >>> 32));
return result;
@ -100,11 +108,10 @@ public class Drive extends Item {
Builder other = (Builder) obj;
if (claimType != other.claimType)
return false;
if (readers == null) {
if (other.readers != null)
return false;
} else if (!readers.equals(other.readers))
return false;
if (!Objects.equal(tags, other.tags))
return false;
if (!Objects.equal(readers, other.readers))
return false;
if (size != other.size)
return false;
return true;
@ -113,13 +120,15 @@ public class Drive extends Item {
protected final long size;
protected final ClaimType claimType;
protected final Set<String> tags;
protected final Set<String> readers;
public Drive(@Nullable String uuid, String name, long size, @Nullable ClaimType claimType, Iterable<String> readers,
Iterable<String> use) {
public Drive(@Nullable String uuid, String name, long size, @Nullable ClaimType claimType,
Iterable<String> tags, Iterable<String> readers, Iterable<String> use) {
super(uuid, name, use);
this.size = size;
this.claimType = checkNotNull(claimType, "set claimType to exclusive, not null");
this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
this.readers = ImmutableSet.copyOf(checkNotNull(readers, "readers"));
}
@ -133,6 +142,13 @@ public class Drive extends Item {
return claimType;
}
/**
* @return all tags associated with this drive, both user-specified and "system" tags (e.g. "affinity:ssd")
*/
public Set<String> getTags() {
return tags;
}
/**
*
* @return list of users allowed to read from a drive or 'ffffffff-ffff-ffff-ffff-ffffffffffff'
@ -155,6 +171,7 @@ public class Drive extends Item {
final int prime = 31;
int result = 1;
result = prime * result + ((claimType == null) ? 0 : claimType.hashCode());
result = prime * result + ((tags == null) ? 0 : tags.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((readers == null) ? 0 : readers.hashCode());
result = prime * result + (int) (size ^ (size >>> 32));
@ -173,16 +190,12 @@ public class Drive extends Item {
Drive other = (Drive) obj;
if (claimType != other.claimType)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (readers == null) {
if (other.readers != null)
return false;
} else if (!readers.equals(other.readers))
return false;
if (!Objects.equal(tags, other.tags))
return false;
if (!Objects.equal(name, other.name))
return false;
if (!Objects.equal(readers, other.readers))
return false;
if (size != other.size)
return false;
if (use == null) {
@ -196,7 +209,7 @@ public class Drive extends Item {
@Override
public String toString() {
return "[uuid=" + uuid + ", name=" + name + ", use=" + use + ", size=" + size + ", claimType=" + claimType
+ ", readers=" + readers + "]";
+ ", tags=" + tags + ", readers=" + readers + "]";
}
}

View File

@ -43,6 +43,14 @@ public class DriveData extends Drive {
return Builder.class.cast(super.name(name));
}
/**
* {@inheritDoc}
*/
@Override
public Builder tags(Iterable<String> tags) {
return Builder.class.cast(super.tags(tags));
}
/**
* {@inheritDoc}
*/
@ -68,12 +76,12 @@ public class DriveData extends Drive {
}
public DriveData build() {
return new DriveData(uuid, name, size, claimType, readers, use);
return new DriveData(uuid, name, size, claimType, tags, readers, use);
}
}
public DriveData(@Nullable String uuid, String name, long size, @Nullable ClaimType claimType,
Iterable<String> readers, Iterable<String> use) {
super(uuid, name, size, claimType, readers, use);
Iterable<String> tags, Iterable<String> readers, Iterable<String> use) {
super(uuid, name, size, claimType, tags, readers, use);
}
}

View File

@ -141,6 +141,14 @@ public class DriveInfo extends Drive {
return Builder.class.cast(super.claimType(claimType));
}
/**
* {@inheritDoc}
*/
@Override
public Builder tags(Iterable<String> tags) {
return Builder.class.cast(super.tags(tags));
}
/**
* {@inheritDoc}
*/
@ -195,7 +203,7 @@ public class DriveInfo extends Drive {
*/
@Override
public DriveInfo build() {
return new DriveInfo(uuid, name, size, claimType, readers, use, status, user, claimed, encryptionCipher,
return new DriveInfo(uuid, name, size, claimType, tags, readers, use, status, user, claimed, encryptionCipher,
imaging, metrics, autoexpanding, bits, description, driveType, encryptionKey, free, installNotes, os,
type, url);
}
@ -221,12 +229,12 @@ public class DriveInfo extends Drive {
private final DriveType type;
private final URI url;
public DriveInfo(String uuid, String name, long size, ClaimType claimType, Iterable<String> readers,
public DriveInfo(String uuid, String name, long size, ClaimType claimType, Iterable<String> tags, Iterable<String> readers,
Iterable<String> use, DriveStatus status, String user, Set<String> claimed, String encryptionCipher,
String imaging, DriveMetrics metrics, Boolean autoexpanding, Integer bits, String description,
Iterable<String> driveType, String encryptionKey, Boolean free, String installNotes, String os,
DriveType type, URI url) {
super(uuid, name, size, claimType, readers, use);
super(uuid, name, size, claimType, tags, readers, use);
this.status = status;
this.user = user;
this.claimed = ImmutableSet.copyOf(checkNotNull(claimed, "claimed"));
@ -455,9 +463,10 @@ public class DriveInfo extends Drive {
@Override
public String toString() {
return "[size=" + size + ", claimType=" + claimType + ", readers=" + readers + ", uuid=" + uuid + ", name="
+ name + ", use=" + use + ", status=" + status + ", user=" + user + ", claimed=" + claimed
+ ", encryptionCipher=" + encryptionCipher + ", imaging=" + imaging + ", metrics=" + metrics + "]";
return "[size=" + size + ", claimType=" + claimType + ", tags=" + tags + ", readers=" + readers
+ ", uuid=" + uuid + ", name=" + name + ", use=" + use + ", status=" + status
+ ", user=" + user + ", claimed=" + claimed + ", encryptionCipher=" + encryptionCipher
+ ", imaging=" + imaging + ", metrics=" + metrics + "]";
}
}

View File

@ -45,6 +45,8 @@ public class BaseDriveToMap implements Function<Drive, Map<String, String>> {
builder.put("size", from.getSize() + "");
if (from.getClaimType() != ClaimType.EXCLUSIVE)
builder.put("claim:type", from.getClaimType().toString());
if (from.getTags().size() != 0)
builder.put("tags", Joiner.on(' ').join(from.getTags()));
if (from.getReaders().size() != 0)
builder.put("readers", Joiner.on(' ').join(from.getReaders()));
if (from.getUse().size() != 0)

View File

@ -48,8 +48,6 @@ public class MapToDriveInfo implements Function<Map<String, String>, DriveInfo>
@Override
public DriveInfo apply(Map<String, String> from) {
if (from.size() == 0)
return null;
if (from.size() == 0)
return null;
DriveInfo.Builder builder = new DriveInfo.Builder();
@ -66,6 +64,8 @@ public class MapToDriveInfo implements Function<Map<String, String>, DriveInfo>
builder.claimType(ClaimType.fromValue(from.get("claim:type")));
if (from.containsKey("claimed"))
builder.claimed(Splitter.on(' ').split(from.get("claimed")));
if (from.containsKey("tags"))
builder.tags(Splitter.on(' ').split(from.get("tags")));
if (from.containsKey("readers"))
builder.readers(Splitter.on(' ').split(from.get("readers")));
if (from.containsKey("size"))

View File

@ -20,10 +20,18 @@ package org.jclouds.cloudsigma.options;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.jclouds.cloudsigma.domain.AffinityType;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Contains options supported for clone drive operations. <h2>
@ -42,6 +50,7 @@ import com.google.common.collect.Maps;
*
*/
public class CloneDriveOptions {
private final String SSD_AFFINITY_TAG = "affinity:ssd";
private final Map<String, String> options = Maps.newLinkedHashMap();
/**
@ -53,6 +62,51 @@ public class CloneDriveOptions {
return this;
}
public CloneDriveOptions tags(String... tags) {
// Affinity is conveyed using regular tags; make sure to preserve any already-set affinity tag.
String currentTagsString = options.remove("tags");
Set<String> currentTags = (currentTagsString == null) ? new HashSet<String>() :
Sets.newLinkedHashSet(Splitter.on(' ').split(currentTagsString));
Set<String> newTags = new LinkedHashSet<String>();
for (String tag : tags)
newTags.add(tag);
if (currentTags.contains(SSD_AFFINITY_TAG))
newTags.add(SSD_AFFINITY_TAG);
options.put("tags", Joiner.on(' ').join(newTags));
return this;
}
/**
* Specifies whether the new drive has 'HDD' affinity (the default) or 'SSD' (for solid-state drives).
* Affinity is conveyed via a special value among the drive's tags.
*/
public CloneDriveOptions affinity(AffinityType affinity) {
// Affinity is conveyed using regular tags; make sure to avoid multiple affinity tags in the options.
String currentTagsString = options.remove("tags");
Set<String> tags = (currentTagsString == null) ? new LinkedHashSet<String>() :
Sets.newLinkedHashSet(Splitter.on(' ').split(currentTagsString));
switch (affinity) {
// SSD affinity is conveyed as a special tag: "affinity:ssd".
case SSD:
tags.add(SSD_AFFINITY_TAG);
break;
// HDD affinity (the default) is conveyed by the *absence* of the "affinity:ssd" tag.
case HDD:
tags.remove(SSD_AFFINITY_TAG);
break;
}
if (!tags.isEmpty())
options.put("tags", Joiner.on(' ').join(tags));
return this;
}
public static class Builder {
/**
@ -63,6 +117,22 @@ public class CloneDriveOptions {
return options.size(size);
}
/**
* @see CloneDriveOptions#tags
*/
public static CloneDriveOptions tags(String... tags) {
CloneDriveOptions options = new CloneDriveOptions();
return options.tags(tags);
}
/**
* @see CloneDriveOptions#affinity
*/
public static CloneDriveOptions affinity(AffinityType affinity) {
CloneDriveOptions options = new CloneDriveOptions();
return options.affinity(affinity);
}
}
public Map<String, String> getOptions() {

View File

@ -460,9 +460,16 @@ public class CloudSigmaClientLiveTest {
protected void prepareDrive() {
client.destroyDrive(drive.getUuid());
drive = client.cloneDrive(bootDrive, drive.getName(),
new CloneDriveOptions().size(driveSize));
new CloneDriveOptions()
.size(driveSize)
.tags("cat:mouse", "monkey:banana")
);
// Block until the async clone operation has completed.
assert driveNotClaimed.apply(drive) : client.getDriveInfo(drive.getUuid());
System.err.println("after prepare" + client.getDriveInfo(drive.getUuid()));
DriveInfo clonedDrive = client.getDriveInfo(drive.getUuid());
System.err.println("after prepare" + clonedDrive);
assertEquals(clonedDrive.getTags(), ImmutableSet.of("cat:mouse", "monkey:banana"));
}
}

View File

@ -72,6 +72,7 @@ public class BindDriveDataToPlainTextStringTest {
//
.size(8589934592l)//
.claimType(ClaimType.SHARED)//
.tags(ImmutableSet.of("foo", "bar", "baz"))//
.readers(ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff"))//
.use(ImmutableSet.of("tag1", "tag2"))//
.build();

View File

@ -48,13 +48,16 @@ public class BaseDriveToMapTest {
//
.size(8589934592l)//
.claimType(ClaimType.SHARED)//
.tags(ImmutableSet.of("foo", "bar", "baz"))//
.readers(ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff"))//
.use(ImmutableSet.of("tag1", "tag2"))//
.build();
assertEquals(
BASEDRIVE_TO_MAP.apply(one),
ImmutableMap.builder().put("name", "Ubuntu 10.10 Server Edition Linux 64bit Preinstalled System")
.put("size", "8589934592").put("claim:type", "shared")
.put("size", "8589934592")
.put("claim:type", "shared")
.put("tags", "foo bar baz")
.put("readers", "ffffffff-ffff-ffff-ffff-ffffffffffff").put("use", "tag1 tag2").build()
);

View File

@ -49,14 +49,19 @@ public class DriveDataToMapTest {
//
.size(8589934592l)//
.claimType(ClaimType.SHARED)//
.tags(ImmutableSet.of("foo", "bar", "baz"))//
.readers(ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff"))//
.use(ImmutableSet.of("tag1", "tag2"))//
.build();
assertEquals(
BASEDRIVE_TO_MAP.apply(one),
ImmutableMap.builder().put("name", "Ubuntu 10.10 Server Edition Linux 64bit Preinstalled System")
.put("size", "8589934592").put("claim:type", "shared")
.put("readers", "ffffffff-ffff-ffff-ffff-ffffffffffff").put("use", "tag1 tag2").build()
ImmutableMap.builder()
.put("name", "Ubuntu 10.10 Server Edition Linux 64bit Preinstalled System")
.put("size", "8589934592")
.put("claim:type", "shared")
.put("tags", "foo bar baz")
.put("readers", "ffffffff-ffff-ffff-ffff-ffffffffffff")
.put("use", "tag1 tag2").build()
);

View File

@ -54,6 +54,7 @@ public class MapToDriveInfoTest {
.encryptionCipher("aes-xts-plain")
.encryptionKey("ba6c2a4897072e9f25920ed73bd522e9c10d89f30a215158cccf8d0f654ac643")
.description("The Ubuntu Linux distribution brings the spirit of Ubuntu to the software world.")
.tags(ImmutableSet.of("foo", "bar", "baz"))
.uuid("b8171d28-755a-4271-b891-7998871a160e")
.installNotes("first line\n\n")
.os("linux")

View File

@ -19,9 +19,12 @@
package org.jclouds.cloudsigma.options;
import static org.jclouds.cloudsigma.options.CloneDriveOptions.Builder.size;
import static org.jclouds.cloudsigma.options.CloneDriveOptions.Builder.tags;
import static org.jclouds.cloudsigma.options.CloneDriveOptions.Builder.affinity;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import org.jclouds.cloudsigma.domain.AffinityType;
import org.testng.annotations.Test;
/**
@ -55,4 +58,74 @@ public class CloneDriveOptionsTest {
size(-1);
}
@Test
public void testNullTags() {
CloneDriveOptions options = new CloneDriveOptions();
assertNull(options.getOptions().get("tags"));
}
@Test
public void testTags() {
CloneDriveOptions options = new CloneDriveOptions().tags("foo", "bar", "baz");
assertEquals(options.getOptions().get("tags"), "foo bar baz");
}
@Test
public void testTagsStatic() {
CloneDriveOptions options = tags("foo", "bar", "baz");
assertEquals(options.getOptions().get("tags"), "foo bar baz");
}
@Test
public void testHddAffinity() {
CloneDriveOptions options = new CloneDriveOptions().affinity(AffinityType.HDD);
assertNull(options.getOptions().get("tags"));
}
@Test
public void testHddAffinityStatic() {
CloneDriveOptions options = affinity(AffinityType.HDD);
assertNull(options.getOptions().get("tags"));
}
@Test
public void testSsdAffinity() {
CloneDriveOptions options = new CloneDriveOptions().affinity(AffinityType.SSD);
assertEquals(options.getOptions().get("tags"), "affinity:ssd");
}
@Test
public void testSsdAffinityStatic() {
CloneDriveOptions options = affinity(AffinityType.SSD);
assertEquals(options.getOptions().get("tags"), "affinity:ssd");
}
@Test
public void testHddAffinityBeforeTags() {
CloneDriveOptions options = new CloneDriveOptions().affinity(AffinityType.HDD);
options.tags("foo", "bar", "baz");
assertEquals(options.getOptions().get("tags"), "foo bar baz");
}
@Test
public void testSsdAffinityBeforeTags() {
CloneDriveOptions options = new CloneDriveOptions().affinity(AffinityType.SSD);
options.tags("foo", "bar", "baz");
assertEquals(options.getOptions().get("tags"), "foo bar baz affinity:ssd");
}
@Test
public void testHddAffinityAfterTags() {
CloneDriveOptions options = new CloneDriveOptions().tags("foo", "bar", "baz");
options.affinity(AffinityType.HDD);
assertEquals(options.getOptions().get("tags"), "foo bar baz");
}
@Test
public void testSsdAffinityAfterTags() {
CloneDriveOptions options = new CloneDriveOptions().tags("foo", "bar", "baz");
options.affinity(AffinityType.SSD);
assertEquals(options.getOptions().get("tags"), "foo bar baz affinity:ssd");
}
}

View File

@ -16,6 +16,7 @@ claim:type shared
claimed 00109617-2c6b-424b-9cfa-5b572c17bafe:guest:692cd1c7-a863-4a22-8170-fc6e6feb68af:ide:0:0 00031836-a624-4b22-bc7d-41ff8977087b:guest:a1414360-7c24-4730-8c97-180bf7775a71:ide:0:0 0002c6df-a1d2-4d1d-96f0-f95405a28183:guest:386f1cc7-affc-49c1-82a5-2f8e412170e4:ide:0:0 00031836-a624-4b22-bc7d-41ff8977087b:guest:17b076be-430d-4a76-9df3-b9896fec82a5:ide:0:0 000663ee-9fb6-4461-90f6-01327a4aff07:guest:f83b519f-feab-42cf-859c-f61495681ada:ide:0:1
drive_type installcd,livecd
autoexpanding false
tags foo bar baz
readers ffffffff-ffff-ffff-ffff-ffffffffffff
read:requests 1
free true

View File

@ -1,5 +1,6 @@
name Ubuntu 10.10 Server Edition Linux 64bit Preinstalled System
size 8589934592
claim:type shared
tags foo bar baz
readers ffffffff-ffff-ffff-ffff-ffffffffffff
use tag1 tag2