Issue 158: implemented listImages and listHardwareProfiles in SoftLayerComputeServiceAdapter. Tidied ProductItemsToHardware and tests

This commit is contained in:
Jason King 2011-09-27 15:15:59 +01:00
parent afda717d2f
commit 2f73dbc539
6 changed files with 109 additions and 89 deletions

View File

@ -20,22 +20,24 @@ package org.jclouds.softlayer.compute.config;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.domain.Location;
import org.jclouds.location.suppliers.OnlyLocationOrFirstZone;
import org.jclouds.softlayer.SoftLayerAsyncClient;
import org.jclouds.softlayer.SoftLayerClient;
import org.jclouds.softlayer.compute.functions.DatacenterToLocation;
import org.jclouds.softlayer.compute.functions.ProductItemsToHardware;
import org.jclouds.softlayer.compute.functions.ProductItemToImage;
import org.jclouds.softlayer.compute.functions.ProductItemsToHardware;
import org.jclouds.softlayer.compute.functions.VirtualGuestToNodeMetadata;
import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter;
import org.jclouds.softlayer.domain.Datacenter;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemPrice;
import org.jclouds.softlayer.domain.VirtualGuest;
import java.util.Set;
@ -67,4 +69,12 @@ public class SoftLayerComputeServiceContextModule extends
bind(new TypeLiteral<Supplier<Location>>() {})
.to(OnlyLocationOrFirstZone.class);
}
protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) {
return template.osFamily(OsFamily.UBUNTU)
.osVersionMatches("1[10].[10][04]")
.os64Bit(true)
.osDescriptionMatches(".*Minimal Install.*")
.minCores(2);
}
}

View File

@ -20,25 +20,22 @@ package org.jclouds.softlayer.compute.functions;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.domain.internal.VolumeImpl;
import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemPrice;
import javax.inject.Singleton;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.regex.Pattern;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.categoryCode;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.units;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.*;
/**
* Converts a set of ProductItems to Hardware.
@ -52,18 +49,17 @@ import static org.jclouds.softlayer.predicates.ProductItemPredicates.units;
public class ProductItemsToHardware implements Function<Set<ProductItem>, Hardware> {
private static final double CORE_SPEED = 2.0;
private static final Pattern SAN_REGEX = Pattern.compile(".*GB \\(SAN\\).*");
@Override
public Hardware apply(Set<ProductItem> from) {
public Hardware apply(Set<ProductItem> items) {
ProductItem coresItem = getCoresItem(from);
ProductItem ramItem = getRamItem(from);
ProductItem volumeItem = bootVolume().apply(from);
ProductItem coresItem = getOnlyElement(filter(items, units("PRIVATE_CORE")));
ProductItem ramItem = getOnlyElement(filter(items, categoryCode("ram")));
ProductItem volumeItem = getOnlyElement(filter(items, matches(SoftLayerComputeServiceAdapter.SAN_DESCRIPTION_REGEX)));
final String hardwareId = hardwareId().apply(ImmutableList.of(coresItem, ramItem, volumeItem));
final double cores = getCores(coresItem);
final int ram = getRam(ramItem);
final double cores = ProductItems.capacity().apply(coresItem).doubleValue();
final int ram = ProductItems.capacity().apply(ramItem).intValue();
final float volumeSize = ProductItems.capacity().apply(volumeItem);
return new HardwareBuilder()
@ -92,48 +88,4 @@ public class ProductItemsToHardware implements Function<Set<ProductItem>, Hardwa
}
};
}
/**
* Finds an item that is usable as the hardware volume (is a SAN)
* @return The product item
* @throws java.util.NoSuchElementException if the item cannot be found
*/
public static Function<Set<ProductItem>,ProductItem> bootVolume() {
return new Function<Set<ProductItem>,ProductItem>() {
@Override
public ProductItem apply(Set<ProductItem> productItems) {
for(ProductItem item: productItems) {
String description = item.getDescription();
if (SAN_REGEX.matcher(description).matches()) {
return item;
}
}
throw new NoSuchElementException("cannot find suitable boot volume item");
}
};
}
private ProductItem getCoresItem(Set<ProductItem> from) {
Iterable<ProductItem> cpuItems = Iterables.filter(from, units("PRIVATE_CORE"));
Map<Float, ProductItem> coresToProductItem = Maps.uniqueIndex(cpuItems, ProductItems.capacity());
assert coresToProductItem.size() == 1 : "Must have 1 cpu product item:"+coresToProductItem;
return coresToProductItem.entrySet().iterator().next().getValue();
}
private double getCores(ProductItem from) {
return ProductItems.capacity().apply(from).doubleValue();
}
private ProductItem getRamItem(Set<ProductItem> from) {
Iterable<ProductItem> ramItems = Iterables.filter(from,categoryCode("ram"));
Map<Float, ProductItem> ramToProductItem = Maps.uniqueIndex(ramItems, ProductItems.capacity());
assert ramToProductItem.size() == 1 : "Must have 1 ram product item:"+ramToProductItem;
return ramToProductItem.entrySet().iterator().next().getValue();
}
private int getRam(ProductItem from) {
return ProductItems.capacity().apply(from).intValue();
}
}

View File

@ -18,18 +18,23 @@
*/
package org.jclouds.softlayer.compute.strategy;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.domain.Credentials;
import org.jclouds.softlayer.SoftLayerClient;
import org.jclouds.softlayer.compute.functions.ProductItems;
import org.jclouds.softlayer.domain.Datacenter;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductPackage;
import org.jclouds.softlayer.domain.VirtualGuest;
import org.jclouds.softlayer.predicates.ProductPackagePredicates;
import org.jclouds.softlayer.features.AccountClient;
import org.jclouds.softlayer.features.ProductPackageClient;
import org.jclouds.softlayer.reference.SoftLayerConstants;
import javax.inject.Inject;
@ -39,6 +44,8 @@ import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.softlayer.predicates.ProductItemPredicates.*;
import static org.jclouds.softlayer.predicates.ProductPackagePredicates.named;
/**
* defines the connection between the {@link SoftLayerClient} implementation and the jclouds
@ -48,6 +55,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Singleton
public class SoftLayerComputeServiceAdapter implements
ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> {
public static final String SAN_DESCRIPTION_REGEX=".*GB \\(SAN\\).*";
//TODO: Better to pass this in as a property like virtualGuestPackageName?
private static final Float BOOT_VOLUME_CAPACITY = 100F;
private final SoftLayerClient client;
private final String virtualGuestPackageName;
@ -75,14 +87,36 @@ public class SoftLayerComputeServiceAdapter implements
@Override
public Iterable<Set<ProductItem>> listHardwareProfiles() {
// TODO: get the set of product item prices corresponding to the hardware profiles
return ImmutableSet.of();
ProductPackage productPackage = getProductPackage();
Set<ProductItem> items = productPackage.getItems();
Iterable<ProductItem> cpuItems = Iterables.filter(items, units("PRIVATE_CORE"));
Iterable<ProductItem> ramItems = Iterables.filter(items,categoryCode("ram"));
Iterable<ProductItem> sanItems = Iterables.filter(items, Predicates.and(matches(SAN_DESCRIPTION_REGEX),categoryCode("one_time_charge")));
Map<Float, ProductItem> cpuMap = Maps.uniqueIndex(cpuItems, ProductItems.capacity());
Map<Float, ProductItem> ramMap = Maps.uniqueIndex(ramItems, ProductItems.capacity());
Map<Float, ProductItem> sanMap = Maps.uniqueIndex(sanItems, ProductItems.capacity());
final ProductItem bootVolume = sanMap.get(BOOT_VOLUME_CAPACITY);
assert bootVolume!=null : "Boot volume capacity not found:"+BOOT_VOLUME_CAPACITY+", available:"+sanItems;
Set<Set<ProductItem>> result = Sets.newLinkedHashSet();
for(Map.Entry<Float, ProductItem> coresEntry : cpuMap.entrySet()) {
Float cores = coresEntry.getKey();
ProductItem ramItem = ramMap.get(cores);
//Amount of RAM and number of cores must match.
if(ramItem==null) continue;
result.add(ImmutableSet.of(coresEntry.getValue(),ramItem,bootVolume));
}
return result;
}
@Override
public Iterable<ProductItem> listImages() {
// TODO: get the list of product item prices corresponding to images
return ImmutableSet.of();
return Iterables.filter(getProductPackage().getItems(), categoryCode("os"));
}
@Override
@ -92,10 +126,15 @@ public class SoftLayerComputeServiceAdapter implements
@Override
public Iterable<Datacenter> listLocations() {
// TODO we should be able to specify a filter that gets the datacenters here.
ProductPackage virtualGuestPackage = Iterables.find(client.getAccountClient().getActivePackages(),
ProductPackagePredicates.named(virtualGuestPackageName));
return client.getProductPackageClient().getProductPackage(virtualGuestPackage.getId()).getDatacenters();
return getProductPackage().getDatacenters();
}
private ProductPackage getProductPackage() {
AccountClient accountClient = client.getAccountClient();
ProductPackageClient productPackageClient = client.getProductPackageClient();
ProductPackage p = Iterables.find(accountClient.getActivePackages(),named(virtualGuestPackageName));
return productPackageClient.getProductPackage(p.getId());
}
@Override

View File

@ -22,6 +22,8 @@ import com.google.common.base.Predicate;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemCategory;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
public class ProductItemPredicates {
@ -75,7 +77,6 @@ public class ProductItemPredicates {
/**
* Tests if the ProductItem has the required units.
* TODO Write test method
* @param units
* @return true if it does, otherwise false.
*/
@ -94,4 +95,28 @@ public class ProductItemPredicates {
}
};
}
/**
* Tests if the ProductItem's description matches the supplied regular expression.
* @param regex a regular expression to match against.
* @return true if it does, otherwise false.
* @throws java.util.regex.PatternSyntaxException if the regex is invalid
*/
public static Predicate<ProductItem> matches(final String regex) {
checkNotNull(regex, "regex cannot be null");
final Pattern PATTERN = Pattern.compile(regex);
return new Predicate<ProductItem>() {
@Override
public boolean apply(ProductItem productItem) {
checkNotNull(productItem, "productItem cannot ne null");
return PATTERN.matcher(productItem.getDescription()).matches();
}
@Override
public String toString() {
return "regex("+regex+")";
}
};
}
}

View File

@ -29,9 +29,7 @@ import org.jclouds.softlayer.domain.ProductItemPrice;
import org.testng.annotations.Test;
import java.util.List;
import java.util.NoSuchElementException;
import static org.jclouds.softlayer.compute.functions.ProductItemsToHardware.bootVolume;
import static org.jclouds.softlayer.compute.functions.ProductItemsToHardware.hardwareId;
import static org.testng.AssertJUnit.assertEquals;
@ -53,21 +51,6 @@ public class ProductItemsToHardwareTest {
assertEquals("123,456,789",id);
}
@Test
public void testBootVolume() {
ProductItem item1 = ProductItem.builder().id(1).description("Not a SAN").build();
ProductItem item2 = ProductItem.builder().id(2).description("100 GB (SAN)").build();
ProductItem found = bootVolume().apply(ImmutableSet.of(item1,item2));
assertEquals(item2,found);
}
@Test(expectedExceptions = NoSuchElementException.class)
public void testBootVolumeMissing() {
ProductItem item1 = ProductItem.builder().id(1).description("Not a SAN").build();
bootVolume().apply(ImmutableSet.of(item1));
}
@Test
public void testHardware() {
ProductItem cpuItem = ProductItem.builder()

View File

@ -38,6 +38,7 @@ public class ProductItemPredicatesTest {
ramCategory = ProductItemCategory.builder().id(1).categoryCode("ram").build();
item = ProductItem.builder().id(1)
.description("a test item")
.categories(ImmutableSet.of(ramCategory))
.capacity(2.0f)
.units("GB")
@ -88,4 +89,14 @@ public class ProductItemPredicatesTest {
public void testUnitsMissing() {
assertFalse(ProductItemPredicates.units("Kg").apply(item));
}
@Test
public void testMatchesRegex() {
assert ProductItemPredicates.matches(".*test.*").apply(item);
}
@Test
public void testNoMatchRegex() {
assertFalse(ProductItemPredicates.matches("no match").apply(item));
}
}