Issue 158: Functions to convert to Image/Hardware, tests and adject the adapter

This commit is contained in:
Jason King 2011-09-27 13:05:29 +01:00
parent 72b7fb0b5f
commit 753bf9f066
7 changed files with 353 additions and 92 deletions

View File

@ -29,7 +29,7 @@ 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.ProductItemPricesToHardware;
import org.jclouds.softlayer.compute.functions.ProductItemsToHardware;
import org.jclouds.softlayer.compute.functions.ProductItemToImage;
import org.jclouds.softlayer.compute.functions.VirtualGuestToNodeMetadata;
import org.jclouds.softlayer.compute.strategy.SoftLayerComputeServiceAdapter;
@ -45,7 +45,7 @@ import java.util.Set;
* @author Adrian Cole
*/
public class SoftLayerComputeServiceContextModule extends
ComputeServiceAdapterContextModule<SoftLayerClient, SoftLayerAsyncClient, VirtualGuest, Set<ProductItemPrice>, ProductItemPrice, Datacenter> {
ComputeServiceAdapterContextModule<SoftLayerClient, SoftLayerAsyncClient, VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> {
public SoftLayerComputeServiceContextModule() {
super(SoftLayerClient.class, SoftLayerAsyncClient.class);
@ -54,14 +54,14 @@ public class SoftLayerComputeServiceContextModule extends
@Override
protected void configure() {
super.configure();
bind(new TypeLiteral<ComputeServiceAdapter<VirtualGuest, Set<ProductItemPrice>, ProductItemPrice, Datacenter>>() {})
bind(new TypeLiteral<ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter>>() {})
.to(SoftLayerComputeServiceAdapter.class);
bind(new TypeLiteral<Function<VirtualGuest, NodeMetadata>>() {})
.to(VirtualGuestToNodeMetadata.class);
bind(new TypeLiteral<Function<ProductItem, org.jclouds.compute.domain.Image>>() {})
.to(ProductItemToImage.class);
bind(new TypeLiteral<Function<Set<ProductItemPrice>, org.jclouds.compute.domain.Hardware>>() {})
.to(ProductItemPricesToHardware.class);
bind(new TypeLiteral<Function<Set<ProductItem>, org.jclouds.compute.domain.Hardware>>() {})
.to(ProductItemsToHardware.class);
bind(new TypeLiteral<Function<Datacenter, Location>>() {})
.to(DatacenterToLocation.class);
bind(new TypeLiteral<Supplier<Location>>() {})

View File

@ -1,48 +0,0 @@
/**
* 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.softlayer.compute.functions;
import java.util.Set;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.softlayer.domain.ProductItemPrice;
import com.google.common.base.Function;
/**
* @author Adrian Cole
*/
@Singleton
public class ProductItemPricesToHardware implements Function<Set<ProductItemPrice>, Hardware> {
@Override
public Hardware apply(Set<ProductItemPrice> from) {
HardwareBuilder builder = new HardwareBuilder();
// builder.ids(from.id + "");
// builder.name(from.name);
// builder.processors(ImmutableList.of(new Processor(from.cores, 1.0)));
// builder.ram(from.ram);
// builder.volumes(ImmutableList.<Volume> of(new VolumeImpl(from.disk, true, false)));
return builder.build();
}
}

View File

@ -18,10 +18,7 @@
*/
package org.jclouds.softlayer.compute.functions;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import com.google.common.base.Function;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
@ -29,13 +26,17 @@ import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemPrice;
import com.google.common.base.Function;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Jason King
*/
@ -57,21 +58,21 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
protected Logger logger = Logger.NULL;
@Override
public Image apply(ProductItem from) {
public Image apply(ProductItem productItem) {
checkNotNull(productItem,"productItem");
//Somehow this method gets called with the correct product item.
OsFamily family = osFamily().apply(from);
Integer bits = osBits().apply(from);
OsFamily family = osFamily().apply(productItem);
Integer bits = osBits().apply(productItem);
OperatingSystem os = OperatingSystem.builder()
.description(from.getDescription())
.description(productItem.getDescription())
.family(family)
.version(osVersion().apply(from))
.version(osVersion().apply(productItem))
.is64Bit(bits.equals(64))
.build();
return new ImageBuilder()
.id("" + from.getId())
.description(from.getDescription())
.id(imageId().apply(productItem))
.description(productItem.getDescription())
.operatingSystem(os)
.build();
}
@ -84,13 +85,15 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
return new Function<ProductItem,OsFamily>() {
@Override
public OsFamily apply(ProductItem productItem) {
checkNotNull(productItem,"productItem");
final String description = productItem.getDescription();
if ( description.startsWith(CENTOS)) return OsFamily.CENTOS;
else if( description.startsWith(DEBIAN) ) return OsFamily.DEBIAN;
else if( description.startsWith(FEDORA) ) return OsFamily.FEDORA;
else if( description.startsWith(RHEL) ) return OsFamily.RHEL;
else if( description.startsWith(UBUNTU) ) return OsFamily.UBUNTU;
else if( description.startsWith(WINDOWS) ) return OsFamily.WINDOWS;
if(description.startsWith(CENTOS)) return OsFamily.CENTOS;
else if(description.startsWith(DEBIAN)) return OsFamily.DEBIAN;
else if(description.startsWith(FEDORA)) return OsFamily.FEDORA;
else if(description.startsWith(RHEL)) return OsFamily.RHEL;
else if(description.startsWith(UBUNTU)) return OsFamily.UBUNTU;
else if(description.startsWith(WINDOWS)) return OsFamily.WINDOWS;
return OsFamily.UNRECOGNIZED;
}
};
@ -105,6 +108,8 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
return new Function<ProductItem,String>() {
@Override
public String apply(ProductItem productItem) {
checkNotNull(productItem,"productItem");
final String description = productItem.getDescription();
OsFamily family = osFamily().apply(productItem);
if (family.equals(OsFamily.CENTOS)) return parseVersion(description, CENTOS);
@ -113,7 +118,7 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
else if(family.equals(OsFamily.RHEL)) return parseVersion(description, RHEL);
else if(family.equals(OsFamily.UBUNTU)) return parseVersion(description, UBUNTU);
else if(family.equals(OsFamily.WINDOWS)) return parseVersion(description, WINDOWS);
else throw new NoSuchElementException("No os parseVersion for item:"+productItem);
else throw new NoSuchElementException("No os version for item:"+productItem);
}
};
}
@ -134,6 +139,8 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
return new Function<ProductItem,Integer>() {
@Override
public Integer apply(ProductItem productItem) {
checkNotNull(productItem,"productItem");
Matcher m = OS_BITS_PATTERN.matcher(productItem.getDescription());
if (m.matches()) {
return Integer.parseInt(m.group(1));
@ -141,7 +148,22 @@ public class ProductItemToImage implements Function<ProductItem, Image> {
throw new NoSuchElementException("Cannot determine os-bits for item:"+productItem);
}
}
};
};
}
/**
* Generates an id for an Image.
* @return the generated id
*/
public static Function<ProductItem,String> imageId() {
return new Function<ProductItem,String>() {
@Override
public String apply(ProductItem productItem) {
checkNotNull(productItem,"productItem");
ProductItemPrice price = ProductItems.price().apply(productItem);
return ""+price.getId();
}
};
}
}

View File

@ -0,0 +1,139 @@
/**
* 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.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.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;
/**
* Converts a set of ProductItems to Hardware.
* All cores have a speed of 2.0Ghz
* The Hardware Id will be a comma separated list containing the price ids:
* cpus,ram,volume
*
* @author Jason King
*/
@Singleton
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) {
ProductItem coresItem = getCoresItem(from);
ProductItem ramItem = getRamItem(from);
ProductItem volumeItem = bootVolume().apply(from);
final String hardwareId = hardwareId().apply(ImmutableList.of(coresItem, ramItem, volumeItem));
final double cores = getCores(coresItem);
final int ram = getRam(ramItem);
final float volumeSize = ProductItems.capacity().apply(volumeItem);
return new HardwareBuilder()
.id(hardwareId)
.processors(ImmutableList.of(new Processor(cores, CORE_SPEED)))
.ram(ram)
.volumes(ImmutableList.<Volume> of(new VolumeImpl(volumeSize, true, false)))
.build();
}
/**
* Generates a hardwareId based on the priceId's of the items in the list
* @return comma separated list of priceid's
*/
public static Function<List<ProductItem>,String> hardwareId() {
return new Function<List<ProductItem>,String>() {
@Override
public String apply(List<ProductItem> productItems) {
StringBuilder builder = new StringBuilder();
for(ProductItem item:productItems) {
ProductItemPrice price = ProductItems.price().apply(item);
builder.append(price.getId())
.append(",");
}
return builder.toString().substring(0,builder.lastIndexOf(","));
}
};
}
/**
* 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,29 +18,27 @@
*/
package org.jclouds.softlayer.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
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.domain.Datacenter;
import org.jclouds.softlayer.domain.ProductItemPrice;
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.reference.SoftLayerConstants;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* defines the connection between the {@link SoftLayerClient} implementation and the jclouds
@ -49,7 +47,7 @@ import com.google.common.collect.Iterables;
*/
@Singleton
public class SoftLayerComputeServiceAdapter implements
ComputeServiceAdapter<VirtualGuest, Set<ProductItemPrice>, ProductItemPrice, Datacenter> {
ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> {
private final SoftLayerClient client;
private final String virtualGuestPackageName;
@ -76,13 +74,13 @@ public class SoftLayerComputeServiceAdapter implements
}
@Override
public Iterable<Set<ProductItemPrice>> listHardwareProfiles() {
public Iterable<Set<ProductItem>> listHardwareProfiles() {
// TODO: get the set of product item prices corresponding to the hardware profiles
return ImmutableSet.of();
}
@Override
public Iterable<ProductItemPrice> listImages() {
public Iterable<ProductItem> listImages() {
// TODO: get the list of product item prices corresponding to images
return ImmutableSet.of();
}

View File

@ -1,9 +1,11 @@
package org.jclouds.softlayer.compute.functions;
import com.google.common.collect.ImmutableSet;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemPrice;
import org.testng.annotations.Test;
import java.util.Arrays;
@ -75,7 +77,10 @@ public class ProductItemToImageTest {
public void testConversion() {
for( String description : operatingSystems )
{
ProductItem item = ProductItem.builder().description(description).build();
ProductItem item = ProductItem.builder()
.description(description)
.price(ProductItemPrice.builder().id(1234).build())
.build();
Image i = new ProductItemToImage().apply(item);
OperatingSystem os = i.getOperatingSystem();
assertNotNull(os);
@ -85,6 +90,41 @@ public class ProductItemToImageTest {
}
}
@Test
public void testUbuntu() {
ProductItem item = ProductItem.builder()
.description("Ubuntu Linux 10.04 LTS Lucid Lynx - Minimal Install (64 bit)")
.price(ProductItemPrice.builder().id(1234).build())
.build();
Image i = new ProductItemToImage().apply(item);
OperatingSystem os = i.getOperatingSystem();
assertNotNull(os);
assertEquals(OsFamily.UBUNTU, os.getFamily());
assertEquals("10.04",os.getVersion());
assertTrue(os.is64Bit());
}
@Test
public void testId() {
ProductItemPrice price = ProductItemPrice.builder().id(1234).build();
ProductItem item = ProductItem.builder().price(price).build();
assertEquals("1234",imageId().apply(item));
}
@Test
public void testIdManyPrices() {
ProductItemPrice price1 = ProductItemPrice.builder().id(1234).build();
ProductItemPrice price2 = ProductItemPrice.builder().id(5678).build();
ProductItem item = ProductItem.builder().prices(ImmutableSet.of(price1,price2)).build();
assertEquals("1234",imageId().apply(item));
}
@Test(expectedExceptions = NoSuchElementException.class)
public void testIdMissingPrices() {
ProductItem item = ProductItem.builder().build();
imageId().apply(item);
}
@Test
public void testOsFamily() {
ProductItem item = ProductItem.builder().description("Ubuntu Linux os").build();
@ -123,7 +163,7 @@ public class ProductItemToImageTest {
@Test(expectedExceptions = NoSuchElementException.class)
public void testOsVersionMissing() {
ProductItem item = ProductItem.builder().description("asd Server ").build();
ProductItem item = ProductItem.builder().description("asd Server").build();
osVersion().apply(item);
}
}

View File

@ -0,0 +1,110 @@
/**
* 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.softlayer.compute.functions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Volume;
import org.jclouds.softlayer.domain.ProductItem;
import org.jclouds.softlayer.domain.ProductItemCategory;
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;
/**
* Tests {@code ProductItemsToHardware}
*
* @author Jason King
*/
@Test(groups = "unit")
public class ProductItemsToHardwareTest {
@Test
public void testHardwareId() {
ProductItem item1 = ProductItem.builder().price(ProductItemPrice.builder().id(123).build()).build();
ProductItem item2 = ProductItem.builder().price(ProductItemPrice.builder().id(456).build()).build();
ProductItem item3 = ProductItem.builder().price(ProductItemPrice.builder().id(789).build()).build();
String id = hardwareId().apply(ImmutableList.of(item1, item2, item3));
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()
.id(1)
.description("2 cores")
.units("PRIVATE_CORE")
.capacity(2F)
.price(ProductItemPrice.builder().id(123).build())
.build();
ProductItem ramItem = ProductItem.builder()
.id(2)
.description("2GB ram")
.capacity(2F)
.category(ProductItemCategory.builder().categoryCode("ram").build())
.price(ProductItemPrice.builder().id(456).build())
.build();
ProductItem volumeItem = ProductItem.builder()
.id(3)
.description("100 GB (SAN)")
.capacity(100F)
.price(ProductItemPrice.builder().id(789).build())
.build();
Hardware hardware = new ProductItemsToHardware().apply(ImmutableSet.of(cpuItem,ramItem,volumeItem));
assertEquals("123,456,789",hardware.getId());
List<? extends Processor> processors = hardware.getProcessors();
assertEquals(1,processors.size());
assertEquals(2.0,processors.get(0).getCores());
assertEquals(2, hardware.getRam());
List<? extends Volume> volumes = hardware.getVolumes();
assertEquals(1,volumes.size());
assertEquals(100F,volumes.get(0).getSize());
}
}