diff --git a/core/src/main/resources/rest.properties b/core/src/main/resources/rest.properties
index 621be07b02..c50d2b83dd 100644
--- a/core/src/main/resources/rest.properties
+++ b/core/src/main/resources/rest.properties
@@ -47,6 +47,9 @@ ec2.propertiesbuilder=org.jclouds.aws.ec2.EC2PropertiesBuilder
rimuhosting.contextbuilder=org.jclouds.rimuhosting.miro.RimuHostingContextBuilder
rimuhosting.propertiesbuilder=org.jclouds.rimuhosting.miro.RimuHostingPropertiesBuilder
+slicehost.contextbuilder=org.jclouds.slicehost.SlicehostContextBuilder
+slicehost.propertiesbuilder=org.jclouds.slicehost.SlicehostPropertiesBuilder
+
trmk-vcloudexpress.contextbuilder=org.jclouds.vcloud.terremark.TerremarkVCloudExpressContextBuilder
trmk-vcloudexpress.propertiesbuilder=org.jclouds.vcloud.terremark.TerremarkVCloudExpressPropertiesBuilder
diff --git a/slicehost/pom.xml b/slicehost/pom.xml
new file mode 100644
index 0000000000..d5a82a24e2
--- /dev/null
+++ b/slicehost/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+
+ 4.0.0
+
+ org.jclouds
+ jclouds-project
+ 1.0-SNAPSHOT
+ ../project/pom.xml
+
+ jclouds-slicehost
+ jclouds slicehost components
+
+ ${jclouds.slicehost.apikey}
+ ${jclouds.slicehost.apikey}
+
+
+
+ ${project.groupId}
+ jclouds-core
+ ${project.version}
+ test-jar
+ test
+
+
+ ${project.groupId}
+ jclouds-compute
+ ${project.version}
+
+
+ ${project.groupId}
+ jclouds-compute
+ ${project.version}
+ test-jar
+ test
+
+
+ log4j
+ log4j
+ 1.2.14
+ test
+
+
+ ${project.groupId}
+ jclouds-log4j
+ ${project.version}
+ test
+
+
+ ${project.groupId}
+ jclouds-jsch
+ ${project.version}
+ test
+
+
+
+
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/SlicehostAsyncClient.java b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostAsyncClient.java
new file mode 100644
index 0000000000..f25f683dea
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostAsyncClient.java
@@ -0,0 +1,204 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.MapPayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SkipEncoding;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+import org.jclouds.slicehost.binders.BindCreateSliceToXmlPayload;
+import org.jclouds.slicehost.domain.Backup;
+import org.jclouds.slicehost.domain.Flavor;
+import org.jclouds.slicehost.domain.Image;
+import org.jclouds.slicehost.domain.Slice;
+import org.jclouds.slicehost.filters.SlicehostBasic;
+import org.jclouds.slicehost.xml.BackupHandler;
+import org.jclouds.slicehost.xml.BackupsHandler;
+import org.jclouds.slicehost.xml.FlavorHandler;
+import org.jclouds.slicehost.xml.FlavorsHandler;
+import org.jclouds.slicehost.xml.ImageHandler;
+import org.jclouds.slicehost.xml.ImagesHandler;
+import org.jclouds.slicehost.xml.SliceHandler;
+import org.jclouds.slicehost.xml.SlicesHandler;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides asynchronous access to Slicehost via their REST API.
+ *
+ * All commands return a ListenableFuture of the result from Slicehost. Any
+ * exceptions incurred during processing will be wrapped in an
+ * {@link ExecutionException} as documented in {@link ListenableFuture#get()}.
+ *
+ * @see SlicehostClient
+ * @see
+ * @author Adrian Cole
+ */
+@SkipEncoding( { '/', '=' })
+@RequestFilters(SlicehostBasic.class)
+public interface SlicehostAsyncClient {
+
+ /**
+ * @see SlicehostClient#listSlices
+ */
+ @GET
+ @Path("/slices.xml")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ @XMLResponseParser(SlicesHandler.class)
+ ListenableFuture extends Set> listSlices();
+
+ /**
+ * @see SlicehostClient#getSlice
+ */
+ @GET
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @Path("/slices/{id}.xml")
+ @XMLResponseParser(SliceHandler.class)
+ ListenableFuture getSlice(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#destroySlice
+ */
+ @DELETE
+ @Path("/slices/{id}/destroy.xml")
+ ListenableFuture destroySlice(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#rebootSlice
+ */
+ @PUT
+ @Path("/slices/{id}/reboot.xml")
+ ListenableFuture rebootSlice(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#hardRebootSlice
+ */
+ @PUT
+ @Path("/slices/{id}/hardReboot.xml")
+ ListenableFuture hardRebootSlice(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#createSlice
+ */
+ @POST
+ @Path("/slices.xml")
+ @MapBinder(BindCreateSliceToXmlPayload.class)
+ @XMLResponseParser(SliceHandler.class)
+ ListenableFuture createSlice(@MapPayloadParam("name") String name, @MapPayloadParam("image_id") int imageId,
+ @MapPayloadParam("flavor_id") int flavorId);
+
+ /**
+ * @see SlicehostClient#rebuildSliceFromImage
+ */
+ @PUT
+ @Path("/slices/{id}/rebuild.xml")
+ ListenableFuture rebuildSliceFromImage(@PathParam("id") int id, @QueryParam("image_id") int imageId);
+
+ /**
+ * @see SlicehostClient#rebuildSliceFromBackup
+ */
+ @PUT
+ @Path("/slices/{id}/rebuild.xml")
+ ListenableFuture rebuildSliceFromBackup(@PathParam("id") int id, @QueryParam("backup_id") int imageId);
+
+ /**
+ * @see SlicehostClient#listFlavors
+ */
+ @GET
+ @Path("/flavors.xml")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ @XMLResponseParser(FlavorsHandler.class)
+ ListenableFuture extends Set> listFlavors();
+
+ /**
+ * @see SlicehostClient#getFlavor
+ */
+ @GET
+ @Path("/flavors/{id}.xml")
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @XMLResponseParser(FlavorHandler.class)
+ ListenableFuture getFlavor(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#listImages
+ */
+ @GET
+ @Path("/images.xml")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ @XMLResponseParser(ImagesHandler.class)
+ ListenableFuture extends Set> listImages();
+
+ /**
+ * @see SlicehostClient#getImage
+ */
+ @GET
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @Path("/images/{id}.xml")
+ @XMLResponseParser(ImageHandler.class)
+ ListenableFuture getImage(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#destroyBackup
+ */
+ @DELETE
+ @Path("/backups/{id}/destroy.xml")
+ ListenableFuture destroyBackup(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#listBackups
+ */
+ @GET
+ @Path("/backups.xml")
+ @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+ @XMLResponseParser(BackupsHandler.class)
+ ListenableFuture extends Set> listBackups();
+
+ /**
+ * @see SlicehostClient#getBackup
+ */
+ @GET
+ @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+ @Path("/backups/{id}.xml")
+ @XMLResponseParser(BackupHandler.class)
+ ListenableFuture getBackup(@PathParam("id") int id);
+
+ /**
+ * @see SlicehostClient#createBackupFromSlice
+ */
+ @PUT
+ @Path("/backups.xml")
+ @XMLResponseParser(BackupHandler.class)
+ ListenableFuture createBackupFromSlice(@QueryParam("slice_id") int sliceId);
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/SlicehostClient.java b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostClient.java
new file mode 100644
index 0000000000..e91452fcdc
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostClient.java
@@ -0,0 +1,78 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.slicehost.domain.Backup;
+import org.jclouds.slicehost.domain.Flavor;
+import org.jclouds.slicehost.domain.Image;
+import org.jclouds.slicehost.domain.Slice;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to Slicehost via their REST API.
+ *
+ * All commands return a ListenableFuture of the result from Slicehost. Any
+ * exceptions incurred during processing will be wrapped in an
+ * {@link ExecutionException} as documented in {@link ListenableFuture#get()}.
+ *
+ * @see SlicehostAsyncClient
+ * @see
+ * @author Adrian Cole
+ */
+@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
+public interface SlicehostClient {
+ Set listSlices();
+
+ Slice getSlice(int id);
+
+ Void destroySlice(int id);
+
+ Void rebootSlice(int id);
+
+ Void hardRebootSlice(int id);
+
+ Slice createSlice(String name, int imageId, int flavorId);
+
+ Void rebuildSliceFromImage(int id, int imageId);
+
+ Void rebuildSliceFromBackup(int id, int imageId);
+
+ Set listFlavors();
+
+ Flavor getFlavor(int id);
+
+ Set listImages();
+
+ Image getImage(int id);
+
+ Void destroyBackup(int id);
+
+ Set listBackups();
+
+ Backup getBackup(int id);
+
+ Backup createBackupFromSlice(int sliceId);
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/SlicehostContextBuilder.java b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostContextBuilder.java
new file mode 100644
index 0000000000..6a77d0df3a
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostContextBuilder.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.jclouds.compute.ComputeServiceContextBuilder;
+import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.jclouds.logging.jdk.config.JDKLoggingModule;
+import org.jclouds.slicehost.compute.config.SlicehostComputeServiceContextModule;
+import org.jclouds.slicehost.config.SlicehostRestClientModule;
+
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Creates {@link SlicehostComputeServiceContext} or {@link Injector} instances based on the most
+ * commonly requested arguments.
+ *
+ * Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
+ *
+ *
+ * If no Module
s are specified, the default {@link JDKLoggingModule logging} and
+ * {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed.
+ *
+ * @author Adrian Cole
+ * @see SlicehostComputeServiceContext
+ */
+public class SlicehostContextBuilder extends
+ ComputeServiceContextBuilder {
+
+ public SlicehostContextBuilder(Properties props) {
+ super(SlicehostClient.class, SlicehostAsyncClient.class, props);
+ }
+
+ @Override
+ protected void addContextModule(List modules) {
+ modules.add(new SlicehostComputeServiceContextModule());
+ }
+
+ @Override
+ protected void addClientModule(List modules) {
+ modules.add(new SlicehostRestClientModule());
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/SlicehostPropertiesBuilder.java b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostPropertiesBuilder.java
new file mode 100644
index 0000000000..3cf79eedb0
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/SlicehostPropertiesBuilder.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_ENDPOINT;
+
+import java.util.Properties;
+
+import org.jclouds.PropertiesBuilder;
+
+/**
+ * Builds properties used in Slicehost Connections
+ *
+ * @author Adrian Cole
+ */
+public class SlicehostPropertiesBuilder extends PropertiesBuilder {
+ @Override
+ protected Properties defaultProperties() {
+ Properties properties = super.defaultProperties();
+ properties.setProperty(PROPERTY_ENDPOINT, "https://api.slicehost.com");
+ properties.setProperty(PROPERTY_API_VERSION, "1.0");
+ return properties;
+ }
+
+ public SlicehostPropertiesBuilder(Properties properties) {
+ super(properties);
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java b/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java
new file mode 100644
index 0000000000..83ad8e93a2
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/binders/BindCreateSliceToXmlPayload.java
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.binders;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToStringPayload;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+@Singleton
+public class BindCreateSliceToXmlPayload implements MapBinder {
+ private final BindToStringPayload binder;
+
+ @Inject
+ BindCreateSliceToXmlPayload(BindToStringPayload binder) {
+ this.binder = binder;
+ }
+
+ public void bindToRequest(HttpRequest request, Map postParams) {
+ String flavorId = checkNotNull(postParams.get("flavor_id"), "flavor_id");
+ String imageId = checkNotNull(postParams.get("image_id"), "image_id");
+ String name = checkNotNull(postParams.get("name"), "name");
+ StringBuilder builder = new StringBuilder();
+ builder.append("");
+ builder.append("").append(flavorId).append("");
+ builder.append("").append(imageId).append("");
+ builder.append("").append(name).append("");
+ builder.append("");
+ binder.bindToRequest(request, builder.toString());
+ request.getPayload().setContentType(MediaType.APPLICATION_XML);
+ }
+
+ @Override
+ public void bindToRequest(HttpRequest request, Object input) {
+ throw new UnsupportedOperationException("should use map params");
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/compute/config/SlicehostComputeServiceContextModule.java b/slicehost/src/main/java/org/jclouds/slicehost/compute/config/SlicehostComputeServiceContextModule.java
new file mode 100644
index 0000000000..398403e2bb
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/compute/config/SlicehostComputeServiceContextModule.java
@@ -0,0 +1,333 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.compute.config;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.domain.OsFamily.UBUNTU;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.LoadBalancerService;
+import org.jclouds.compute.config.ComputeServiceTimeoutsModule;
+import org.jclouds.compute.domain.Architecture;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Size;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.domain.internal.ImageImpl;
+import org.jclouds.compute.domain.internal.NodeMetadataImpl;
+import org.jclouds.compute.domain.internal.SizeImpl;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.ComputeServiceContextImpl;
+import org.jclouds.compute.predicates.ImagePredicates;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.AddNodeWithTagStrategy;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.domain.internal.LocationImpl;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.RestContext;
+import org.jclouds.rest.annotations.Provider;
+import org.jclouds.rest.internal.RestContextImpl;
+import org.jclouds.slicehost.SlicehostAsyncClient;
+import org.jclouds.slicehost.SlicehostClient;
+import org.jclouds.slicehost.compute.functions.SliceToNodeMetadata;
+import org.jclouds.slicehost.domain.Flavor;
+import org.jclouds.slicehost.domain.Slice;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.util.Providers;
+
+/**
+ * Configures the {@link SlicehostComputeServiceContext}; requires
+ * {@link BaseComputeService} bound.
+ *
+ * @author Adrian Cole
+ */
+public class SlicehostComputeServiceContextModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ install(new ComputeServiceTimeoutsModule());
+ bind(new TypeLiteral>() {
+ }).to(SliceToNodeMetadata.class);
+ bind(LoadBalancerService.class).toProvider(Providers. of(null));
+ bind(new TypeLiteral() {
+ }).to(new TypeLiteral>() {
+ }).in(Scopes.SINGLETON);
+ bind(new TypeLiteral>() {
+ }).to(new TypeLiteral>() {
+ }).in(Scopes.SINGLETON);
+ bind(AddNodeWithTagStrategy.class).to(SlicehostAddNodeWithTagStrategy.class);
+ bind(ListNodesStrategy.class).to(SlicehostListNodesStrategy.class);
+ bind(GetNodeMetadataStrategy.class).to(SlicehostGetNodeMetadataStrategy.class);
+ bind(RebootNodeStrategy.class).to(SlicehostRebootNodeStrategy.class);
+ bind(DestroyNodeStrategy.class).to(SlicehostDestroyNodeStrategy.class);
+ }
+
+ @Provides
+ @Named("DEFAULT")
+ protected TemplateBuilder provideTemplate(TemplateBuilder template) {
+ return template.osFamily(UBUNTU).imageNameMatches(".*10\\.?04.*");
+ }
+
+ @Provides
+ @Named("NAMING_CONVENTION")
+ @Singleton
+ String provideNamingConvention(@Named(Constants.PROPERTY_IDENTITY) String identity) {
+ return identity + "-%s-%s";
+ }
+
+ @Singleton
+ public static class SlicehostRebootNodeStrategy implements RebootNodeStrategy {
+ private final SlicehostClient client;
+ private final GetNodeMetadataStrategy getNode;
+
+ @Inject
+ protected SlicehostRebootNodeStrategy(SlicehostClient client, GetNodeMetadataStrategy getNode) {
+ this.client = client;
+ this.getNode = getNode;
+ }
+
+ @Override
+ public NodeMetadata execute(String id) {
+ int sliceId = Integer.parseInt(id);
+ client.hardRebootSlice(sliceId);
+ return getNode.execute(id);
+ }
+
+ }
+
+ @Singleton
+ public static class SlicehostDestroyNodeStrategy implements DestroyNodeStrategy {
+ private final SlicehostClient client;
+ private final GetNodeMetadataStrategy getNode;
+
+ @Inject
+ protected SlicehostDestroyNodeStrategy(SlicehostClient client, GetNodeMetadataStrategy getNode) {
+ this.client = client;
+ this.getNode = getNode;
+ }
+
+ @Override
+ public NodeMetadata execute(String id) {
+ int sliceId = Integer.parseInt(id);
+ // if false slice wasn't around in the first place
+ client.destroySlice(sliceId);
+ return getNode.execute(id);
+ }
+
+ }
+
+ @Singleton
+ public static class SlicehostAddNodeWithTagStrategy implements AddNodeWithTagStrategy {
+ private final SlicehostClient client;
+
+ @Inject
+ protected SlicehostAddNodeWithTagStrategy(SlicehostClient client) {
+ this.client = checkNotNull(client, "client");
+ }
+
+ @Override
+ public NodeMetadata execute(String tag, String name, Template template) {
+ Slice slice = client.createSlice(name, Integer.parseInt(template.getImage().getProviderId()), Integer
+ .parseInt(template.getSize().getProviderId()));
+ return new NodeMetadataImpl(slice.getId() + "", name, slice.getId() + "", template.getLocation(), null,
+ ImmutableMap. of(), tag, template.getImage(), NodeState.PENDING, Iterables.filter(slice
+ .getAddresses(), new Predicate() {
+
+ @Override
+ public boolean apply(String input) {
+ return !input.startsWith("10.");
+ }
+
+ }), Iterables.filter(slice.getAddresses(), new Predicate() {
+
+ @Override
+ public boolean apply(String input) {
+ return input.startsWith("10.");
+ }
+
+ }), ImmutableMap. of(), new Credentials("root", slice.getRootPassword()));
+ }
+
+ }
+
+ @Singleton
+ public static class SlicehostListNodesStrategy implements ListNodesStrategy {
+ private final SlicehostClient client;
+ private final Function sliceToNodeMetadata;
+
+ @Inject
+ protected SlicehostListNodesStrategy(SlicehostClient client, Function sliceToNodeMetadata) {
+ this.client = client;
+ this.sliceToNodeMetadata = sliceToNodeMetadata;
+ }
+
+ @Override
+ public Iterable extends ComputeMetadata> list() {
+ return listDetailsOnNodesMatching(NodePredicates.all());
+ }
+
+ @Override
+ public Iterable extends NodeMetadata> listDetailsOnNodesMatching(Predicate filter) {
+ return Iterables.filter(Iterables.transform(client.listSlices(), sliceToNodeMetadata), filter);
+ }
+ }
+
+ @Singleton
+ public static class SlicehostGetNodeMetadataStrategy implements GetNodeMetadataStrategy {
+
+ private final SlicehostClient client;
+ private final Function sliceToNodeMetadata;
+
+ @Inject
+ protected SlicehostGetNodeMetadataStrategy(SlicehostClient client,
+ Function sliceToNodeMetadata) {
+ this.client = client;
+ this.sliceToNodeMetadata = sliceToNodeMetadata;
+ }
+
+ @Override
+ public NodeMetadata execute(String id) {
+ int sliceId = Integer.parseInt(id);
+ Slice slice = client.getSlice(sliceId);
+ return slice == null ? null : sliceToNodeMetadata.apply(slice);
+ }
+ }
+
+ @Singleton
+ @Provides
+ Map provideSliceToNodeState() {
+ return ImmutableMap. builder().put(Slice.Status.ACTIVE, NodeState.RUNNING)//
+ .put(Slice.Status.BUILD, NodeState.PENDING)//
+ .put(Slice.Status.REBOOT, NodeState.PENDING)//
+ .put(Slice.Status.HARD_REBOOT, NodeState.PENDING)//
+ .put(Slice.Status.TERMINATED, NodeState.TERMINATED)//
+ .build();
+ }
+
+ @Provides
+ @Singleton
+ protected Function indexer() {
+ return new Function() {
+ @Override
+ public String apply(ComputeMetadata from) {
+ return from.getProviderId();
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ protected Set extends Size> provideSizes(SlicehostClient sync, Set extends Image> images, Location location,
+ LogHolder holder, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
+ Function indexer) throws InterruptedException, TimeoutException, ExecutionException {
+ final Set sizes = Sets.newHashSet();
+ holder.logger.debug(">> providing sizes");
+ for (final Flavor from : sync.listFlavors()) {
+ sizes.add(new SizeImpl(from.getId() + "", from.getName(), from.getId() + "", location, null, ImmutableMap
+ . of(), from.getRam() / 1024.0, from.getRam(), (from.getRam() * 4) / 1024, ImagePredicates
+ .any()));
+ }
+ holder.logger.debug("<< sizes(%d)", sizes.size());
+ return sizes;
+ }
+
+ private static class LogHolder {
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+ }
+
+ public static final Pattern SLICEHOST_PATTERN = Pattern.compile("(([^ ]*) .*)");
+
+ @Provides
+ @Singleton
+ Location getLocation(@Provider String name) {
+ return new LocationImpl(LocationScope.PROVIDER, name, name, null);
+ }
+
+ @Provides
+ @Singleton
+ Set extends Location> provideLocations(Location location) {
+ return ImmutableSet.of(location);
+ }
+
+ @Provides
+ @Singleton
+ protected Set extends Image> provideImages(final SlicehostClient sync, Location location, LogHolder holder,
+ Function indexer) throws InterruptedException, ExecutionException, TimeoutException {
+ final Set images = Sets.newHashSet();
+ holder.logger.debug(">> providing images");
+ for (final org.jclouds.slicehost.domain.Image from : sync.listImages()) {
+ OsFamily os = null;
+ Architecture arch = Architecture.X86_64;
+ String osDescription = "";
+ String version = "";
+ Matcher matcher = SLICEHOST_PATTERN.matcher(from.getName());
+ osDescription = from.getName();
+ if (matcher.find()) {
+ try {
+ os = OsFamily.fromValue(matcher.group(2).toLowerCase());
+ } catch (IllegalArgumentException e) {
+ holder.logger.debug("<< didn't match os(%s)", matcher.group(2));
+ }
+ }
+ images
+ .add(new ImageImpl(from.getId() + "", from.getName(), from.getId() + "", location, null, ImmutableMap
+ . of(), from.getName(), version, os, osDescription, arch, new Credentials("root",
+ null)));
+ }
+ holder.logger.debug("<< images(%d)", images.size());
+ return images;
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/compute/functions/SliceToNodeMetadata.java b/slicehost/src/main/java/org/jclouds/slicehost/compute/functions/SliceToNodeMetadata.java
new file mode 100644
index 0000000000..0094283fc1
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/compute/functions/SliceToNodeMetadata.java
@@ -0,0 +1,111 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.internal.NodeMetadataImpl;
+import org.jclouds.domain.Location;
+import org.jclouds.logging.Logger;
+import org.jclouds.slicehost.domain.Slice;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+public class SliceToNodeMetadata implements Function {
+ public static final Pattern SECOND_FIELD_DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX = Pattern
+ .compile("[^-]+-([^-]+)-[0-9a-f]+");
+ private final Location location;
+ private final Map sliceToNodeState;
+ private final Set extends Image> images;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ private static class FindImageForSlice implements Predicate {
+ private final Location location;
+ private final Slice instance;
+
+ private FindImageForSlice(Location location, Slice instance) {
+ this.location = location;
+ this.instance = instance;
+ }
+
+ @Override
+ public boolean apply(Image input) {
+ return input.getProviderId().equals(instance.getImageId() + "")
+ && (input.getLocation() == null || input.getLocation().equals(location.getParent()));
+ }
+ }
+
+ @Inject
+ SliceToNodeMetadata(Map sliceStateToNodeState, Set extends Image> images,
+ Location location) {
+ this.sliceToNodeState = checkNotNull(sliceStateToNodeState, "sliceStateToNodeState");
+ this.images = checkNotNull(images, "images");
+ this.location = checkNotNull(location, "location");
+ }
+
+ @Override
+ public NodeMetadata apply(Slice from) {
+ Matcher matcher = SECOND_FIELD_DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX.matcher(from.getName());
+ final String tag = matcher.find() ? matcher.group(1) : null;
+ Image image = null;
+ try {
+ image = Iterables.find(images, new FindImageForSlice(location, from));
+ } catch (NoSuchElementException e) {
+ logger.warn("could not find a matching image for slice %s in location %s", from, location);
+ }
+
+ return new NodeMetadataImpl(from.getId() + "", from.getName(), from.getId() + "", location, null, ImmutableMap
+ . of(), tag, image, sliceToNodeState.get(from.getStatus()), Iterables.filter(from
+ .getAddresses(), new Predicate() {
+
+ @Override
+ public boolean apply(String input) {
+ return !input.startsWith("10.");
+ }
+
+ }), Iterables.filter(from.getAddresses(), new Predicate() {
+
+ @Override
+ public boolean apply(String input) {
+ return input.startsWith("10.");
+ }
+
+ }), ImmutableMap. of(), null);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/config/SlicehostRestClientModule.java b/slicehost/src/main/java/org/jclouds/slicehost/config/SlicehostRestClientModule.java
new file mode 100644
index 0000000000..29f707ce6c
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/config/SlicehostRestClientModule.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.config;
+
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.config.RestClientModule;
+import org.jclouds.slicehost.SlicehostAsyncClient;
+import org.jclouds.slicehost.SlicehostClient;
+import org.jclouds.slicehost.handlers.ParseSlicehostErrorFromHttpResponse;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@ConfiguresRestClient
+@RequiresHttp
+public class SlicehostRestClientModule extends RestClientModule {
+
+ public SlicehostRestClientModule() {
+ super(SlicehostClient.class, SlicehostAsyncClient.class);
+ }
+
+ @Override
+ protected void bindErrorHandlers() {
+ bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseSlicehostErrorFromHttpResponse.class);
+ bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseSlicehostErrorFromHttpResponse.class);
+ bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseSlicehostErrorFromHttpResponse.class);
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/domain/Backup.java b/slicehost/src/main/java/org/jclouds/slicehost/domain/Backup.java
new file mode 100644
index 0000000000..1274c63d20
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/domain/Backup.java
@@ -0,0 +1,115 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.domain;
+
+import java.util.Date;
+
+/**
+ *
+ *
+ * @author Adrian Cole
+ */
+public class Backup {
+
+ private final int id;
+ private final String name;
+ private final String sliceId;
+ private final Date date;
+
+ public Backup(int id, String name, String sliceId, Date date) {
+ this.id = id;
+ this.name = name;
+ this.sliceId = sliceId;
+ this.date = date;
+ }
+
+ /**
+ * @return id of the backup
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @return Examples: weekly, my snapshot
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return The Slice this backup was made from
+ */
+ public String getSliceId() {
+ return sliceId;
+ }
+
+ /**
+ * @return The time the backup was taken
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((date == null) ? 0 : date.hashCode());
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((sliceId == null) ? 0 : sliceId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Backup other = (Backup) obj;
+ if (date == null) {
+ if (other.date != null)
+ return false;
+ } else if (!date.equals(other.date))
+ return false;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (sliceId == null) {
+ if (other.sliceId != null)
+ return false;
+ } else if (!sliceId.equals(other.sliceId))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[date=" + date + ", id=" + id + ", name=" + name + ", sliceId=" + sliceId + "]";
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/domain/Flavor.java b/slicehost/src/main/java/org/jclouds/slicehost/domain/Flavor.java
new file mode 100644
index 0000000000..d7575aa6bc
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/domain/Flavor.java
@@ -0,0 +1,109 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.domain;
+
+/**
+ *
+ * A flavor is an available hardware configuration for a slice.
+ *
+ * @author Adrian Cole
+ */
+public class Flavor {
+
+ private final int id;
+ private final String name;
+ private final int price;
+ private final int ram;
+
+ public Flavor(int id, String name, int price, int ram) {
+ this.id = id;
+ this.name = name;
+ this.price = price;
+ this.ram = ram;
+ }
+
+ /**
+ * @return id of the flavor
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @return Verbose name for the flavor, e.g. Ò256 sliceÓ
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return The price as an integer of cents. For example: 2000 equals $20.00.
+ * Note that all prices are in USD
+ */
+ public int getPrice() {
+ return price;
+ }
+
+ /**
+ * @return The amount of RAM (in Megabytes) included with the plan
+ */
+ public int getRam() {
+ return ram;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + Float.floatToIntBits(price);
+ result = prime * result + ram;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Flavor other = (Flavor) obj;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (Float.floatToIntBits(price) != Float.floatToIntBits(other.price))
+ return false;
+ if (ram != other.ram)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[id=" + id + ", name=" + name + ", price=" + price + ", ram=" + ram + "]";
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/domain/Image.java b/slicehost/src/main/java/org/jclouds/slicehost/domain/Image.java
new file mode 100644
index 0000000000..5c1785b6b3
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/domain/Image.java
@@ -0,0 +1,85 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.domain;
+
+/**
+ *
+ *
+ * @author Adrian Cole
+ */
+public class Image {
+
+ private final int id;
+ private final String name;
+
+ public Image(int id, String name) {
+ this.id = id;
+ this.name = name;
+
+ }
+
+ /**
+ * @return id of the image
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @return Example: Ubuntu 8.04 LTS (hardy)
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Image other = (Image) obj;
+ if (id != other.id)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[id=" + id + ", name=" + name + "]";
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/domain/Slice.java b/slicehost/src/main/java/org/jclouds/slicehost/domain/Slice.java
new file mode 100644
index 0000000000..481dfbf6c6
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/domain/Slice.java
@@ -0,0 +1,249 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.CaseFormat;
+
+/**
+ * A slice is a virtual machine instance in the Slicehost system. Flavor and
+ * image are requisite elements when creating a slice.
+ *
+ * @author Adrian Cole
+ */
+public class Slice {
+ /**
+ * The current status of the slice
+ *
+ */
+ public enum Status {
+
+ ACTIVE, BUILD, REBOOT, HARD_REBOOT, TERMINATED;
+
+ public String value() {
+ return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_UNDERSCORE, name()));
+ }
+
+ @Override
+ public String toString() {
+ return value();
+ }
+
+ public static Status fromValue(String state) {
+ return valueOf(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
+ }
+ }
+
+ private final int id;
+ private final String name;
+ private final int flavorId;
+ @Nullable
+ private final Integer imageId;
+ @Nullable
+ private final Integer backupId;
+ private final Status status;
+ @Nullable
+ private final Integer progress;
+ private final float bandwidthIn;
+ private final float bandwidthOut;
+ private final Set addresses;
+ @Nullable
+ private final String rootPassword;
+
+ public Slice(int id, String name, int flavorId, @Nullable Integer imageId, @Nullable Integer backupId,
+ Status status, @Nullable Integer progress, float bandwidthIn, float bandwidthOut, Set addresses,
+ @Nullable String rootPassword) {
+ this.id = id;
+ this.name = name;
+ this.flavorId = flavorId;
+ this.imageId = imageId;
+ this.backupId = backupId;
+ this.status = status;
+ this.progress = progress;
+ this.bandwidthIn = bandwidthIn;
+ this.bandwidthOut = bandwidthOut;
+ this.addresses = addresses;
+ this.rootPassword = rootPassword;
+ }
+
+ /**
+ * @return unique id within slicehost
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @return A string to identify the slice
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the flavor of a slice
+ */
+ public int getFlavorId() {
+ return flavorId;
+ }
+
+ /**
+ * @return the image used to create the slice or null
+ */
+ public Integer getImageId() {
+ return imageId;
+ }
+
+ /**
+ * @return The current status of the slice
+ */
+ public Status getStatus() {
+ return status;
+ }
+
+ /**
+ * @return The percentage of current action in percentage
+ */
+ public Integer getProgress() {
+ return progress;
+ }
+
+ /**
+ * @return The incoming bandwidth total for this billing cycle, in Gigabytes
+ */
+ public float getBandwidthIn() {
+ return bandwidthIn;
+ }
+
+ /**
+ * @return The outgoing bandwidth total for this billing cycle, in Gigabytes
+ */
+ public float getBandwidthOut() {
+ return bandwidthOut;
+ }
+
+ /**
+ * @return an array of strings representing the Slice's IPs, including
+ * private IPs
+ */
+ public Set getAddresses() {
+ return addresses;
+ }
+
+ /**
+ * @return root password, if just created
+ */
+ public String getRootPassword() {
+ return rootPassword;
+ }
+
+ /**
+ * @return backup used to create this instance or null
+ */
+ public Integer getBackupId() {
+ return backupId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
+ result = prime * result + ((backupId == null) ? 0 : backupId.hashCode());
+ result = prime * result + Float.floatToIntBits(bandwidthIn);
+ result = prime * result + Float.floatToIntBits(bandwidthOut);
+ result = prime * result + flavorId;
+ result = prime * result + id;
+ result = prime * result + ((imageId == null) ? 0 : imageId.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((progress == null) ? 0 : progress.hashCode());
+ result = prime * result + ((rootPassword == null) ? 0 : rootPassword.hashCode());
+ result = prime * result + ((status == null) ? 0 : status.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Slice other = (Slice) obj;
+ if (addresses == null) {
+ if (other.addresses != null)
+ return false;
+ } else if (!addresses.equals(other.addresses))
+ return false;
+ if (backupId == null) {
+ if (other.backupId != null)
+ return false;
+ } else if (!backupId.equals(other.backupId))
+ return false;
+ if (Float.floatToIntBits(bandwidthIn) != Float.floatToIntBits(other.bandwidthIn))
+ return false;
+ if (Float.floatToIntBits(bandwidthOut) != Float.floatToIntBits(other.bandwidthOut))
+ return false;
+ if (flavorId != other.flavorId)
+ return false;
+ if (id != other.id)
+ return false;
+ if (imageId == null) {
+ if (other.imageId != null)
+ return false;
+ } else if (!imageId.equals(other.imageId))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (progress == null) {
+ if (other.progress != null)
+ return false;
+ } else if (!progress.equals(other.progress))
+ return false;
+ if (rootPassword == null) {
+ if (other.rootPassword != null)
+ return false;
+ } else if (!rootPassword.equals(other.rootPassword))
+ return false;
+ if (status == null) {
+ if (other.status != null)
+ return false;
+ } else if (!status.equals(other.status))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "[id=" + id + ", name=" + name + ", flavorId=" + flavorId + ", imageId=" + imageId + ", backupId="
+ + backupId + ", status=" + status + ", progress=" + progress + ", bandwidthIn=" + bandwidthIn
+ + ", bandwidthOut=" + bandwidthOut + ", addresses=" + addresses + ", rootPassword="
+ + (rootPassword != null) + "]";
+ }
+
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/filters/SlicehostBasic.java b/slicehost/src/main/java/org/jclouds/slicehost/filters/SlicehostBasic.java
new file mode 100644
index 0000000000..b3a658dd52
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/filters/SlicehostBasic.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.filters;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.encryption.EncryptionService;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class SlicehostBasic implements HttpRequestFilter {
+ private final String apikey;
+ private final EncryptionService encryptionService;
+
+ @Inject
+ public SlicehostBasic(@Named(Constants.PROPERTY_IDENTITY) String apikey, EncryptionService encryptionService) {
+ this.apikey = checkNotNull(apikey, "apikey");
+ this.encryptionService = checkNotNull(encryptionService, "encryptionService");
+ }
+
+ @Override
+ public void filter(HttpRequest request) throws HttpException {
+ request.getHeaders().replaceValues("Authorization",
+ Collections.singleton(String.format("Basic %s", encryptionService.base64(apikey.getBytes()))));
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/handlers/ParseSlicehostErrorFromHttpResponse.java b/slicehost/src/main/java/org/jclouds/slicehost/handlers/ParseSlicehostErrorFromHttpResponse.java
new file mode 100644
index 0000000000..88bd0650b7
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/handlers/ParseSlicehostErrorFromHttpResponse.java
@@ -0,0 +1,108 @@
+package org.jclouds.slicehost.handlers;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.http.functions.ParseSax.Factory;
+import org.jclouds.io.Payload;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.jclouds.slicehost.xml.ErrorHandler;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ *
+ * @author Adrian Cole
+ *
+ */
+@Singleton
+public class ParseSlicehostErrorFromHttpResponse implements HttpErrorHandler {
+ @Resource
+ protected Logger logger = Logger.NULL;
+ public static final Pattern RESOURCE_PATTERN = Pattern.compile("^/v1[^/]*/[0-9]+/([^/]+)/([0-9]+)");
+
+ private final ErrorParser errorParser;
+
+ @Inject
+ ParseSlicehostErrorFromHttpResponse(ErrorParser errorParser) {
+ this.errorParser = errorParser;
+ }
+
+ public void handleError(HttpCommand command, HttpResponse response) {
+ Exception exception = new HttpResponseException(command, response);
+ try {
+ String content = response.getStatusCode() != 401 ? parseErrorFromContentOrNull(command, response) : null;
+ switch (response.getStatusCode()) {
+ case 401:
+ exception = new AuthorizationException(command.getRequest(), content);
+ break;
+ case 403:
+ case 404:
+ if (!command.getRequest().getMethod().equals("DELETE")) {
+ String path = command.getRequest().getEndpoint().getPath();
+ Matcher matcher = RESOURCE_PATTERN.matcher(path);
+ String message;
+ if (matcher.find()) {
+ message = String.format("%s %s not found", matcher.group(1), matcher.group(2));
+ } else {
+ message = path;
+ }
+ exception = new ResourceNotFoundException(message);
+ }
+ break;
+ default:
+ exception = new HttpResponseException(command, response, content);
+ }
+ } finally {
+ releasePayload(response);
+ command.setException(exception);
+ }
+ }
+
+ @Singleton
+ static class ErrorParser {
+ final ParseSax.Factory factory;
+ final Provider errorHandlerProvider;
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ ErrorParser(Factory factory, Provider errorHandlerProvider) {
+ this.factory = factory;
+ this.errorHandlerProvider = errorHandlerProvider;
+ }
+
+ String parse(Payload in) {
+ try {
+ return factory.create(errorHandlerProvider.get()).parse(in.getInput());
+ } catch (HttpException e) {
+ logger.warn(e, "error parsing error");
+ return null;
+ } finally {
+ in.release();
+ }
+ }
+
+ }
+
+ String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) {
+ if (response.getPayload() != null) {
+ return errorParser.parse(response.getPayload());
+ }
+ return null;
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceActive.java b/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceActive.java
new file mode 100644
index 0000000000..5306dcd6ec
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceActive.java
@@ -0,0 +1,46 @@
+package org.jclouds.slicehost.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.slicehost.SlicehostClient;
+import org.jclouds.slicehost.domain.Slice;
+
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+
+/**
+ *
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class SliceActive implements Predicate {
+
+ private final SlicehostClient client;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ public SliceActive(SlicehostClient client) {
+ this.client = client;
+ }
+
+ public boolean apply(Slice slice) {
+ logger.trace("looking for state on slice %s", checkNotNull(slice, "slice"));
+ slice = refresh(slice);
+ if (slice == null)
+ return false;
+ logger.trace("%s: looking for slice state %s: currently: %s", slice.getId(), Slice.Status.ACTIVE, slice
+ .getStatus());
+ return slice.getStatus() == Slice.Status.ACTIVE;
+ }
+
+ private Slice refresh(Slice slice) {
+ return client.getSlice(slice.getId());
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceTerminated.java b/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceTerminated.java
new file mode 100644
index 0000000000..f476827e4e
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/predicates/SliceTerminated.java
@@ -0,0 +1,45 @@
+package org.jclouds.slicehost.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.slicehost.SlicehostClient;
+import org.jclouds.slicehost.domain.Slice;
+
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class SliceTerminated implements Predicate {
+
+ private final SlicehostClient client;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ public SliceTerminated(SlicehostClient client) {
+ this.client = client;
+ }
+
+ public boolean apply(Slice slice) {
+ logger.trace("looking for state on slice %s", checkNotNull(slice, "slice"));
+ slice = refresh(slice);
+ if (slice == null)
+ return true;
+ logger.trace("%s: looking for slice state %s: currently: %s", slice.getId(), Slice.Status.TERMINATED, slice
+ .getStatus());
+ return slice.getStatus() == Slice.Status.TERMINATED;
+ }
+
+ private Slice refresh(Slice slice) {
+ return client.getSlice(slice.getId());
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupHandler.java
new file mode 100644
index 0000000000..1f6faefe9f
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupHandler.java
@@ -0,0 +1,78 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Date;
+
+import javax.annotation.Resource;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.logging.Logger;
+import org.jclouds.slicehost.domain.Backup;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Adrian Cole
+ */
+public class BackupHandler extends ParseSax.HandlerWithResult {
+ private StringBuilder currentText = new StringBuilder();
+
+ private int id;
+ private String name;
+ private String sliceId;
+ private Date date;
+
+ private Backup backup;
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ public Backup getResult() {
+ return backup;
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (qName.equalsIgnoreCase("id")) {
+ id = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("name")) {
+ this.name = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("slice_id")) {
+ sliceId = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("date")) {
+ try {
+ date = DateFormat.getInstance().parse(currentText.toString().trim());
+ } catch (ParseException e) {
+ logger.warn(e, "error parsing: %s", currentText.toString().trim());
+ }
+ } else if (qName.equalsIgnoreCase("backup")) {
+ this.backup = new Backup(id, name, sliceId, date);
+ this.id = -1;
+ this.name = null;
+ this.sliceId = null;
+ this.date = null;
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupsHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupsHandler.java
new file mode 100644
index 0000000000..097db20de2
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/BackupsHandler.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Backup;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+
+/**
+ * @author Adrian Cole
+ */
+public class BackupsHandler extends ParseSax.HandlerWithResult> {
+ private StringBuilder currentText = new StringBuilder();
+
+ private Set backups = Sets.newLinkedHashSet();
+ private final BackupHandler locationHandler;
+
+ @Inject
+ public BackupsHandler(BackupHandler locationHandler) {
+ this.locationHandler = locationHandler;
+ }
+
+ public Set extends Backup> getResult() {
+ return backups;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ locationHandler.startElement(uri, localName, qName, attributes);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ locationHandler.endElement(uri, localName, qName);
+ if (qName.equals("backup") && currentText.toString().trim().equals("")) {
+ this.backups.add(locationHandler.getResult());
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ locationHandler.characters(ch, start, length);
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/ErrorHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/ErrorHandler.java
new file mode 100644
index 0000000000..eb24351e05
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/ErrorHandler.java
@@ -0,0 +1,47 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Adrian Cole
+ */
+public class ErrorHandler extends ParseSax.HandlerWithResult {
+ private StringBuilder currentText = new StringBuilder();
+
+ private String error;
+
+ public String getResult() {
+ return error;
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (qName.equalsIgnoreCase("error")) {
+ this.error = currentText.toString().trim();
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorHandler.java
new file mode 100644
index 0000000000..9c20e0f150
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorHandler.java
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Flavor;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Adrian Cole
+ */
+public class FlavorHandler extends ParseSax.HandlerWithResult {
+ private StringBuilder currentText = new StringBuilder();
+
+ private int id;
+ private String name;
+ private int price;
+ private int ram;
+
+ private Flavor flavor;
+
+ public Flavor getResult() {
+ return flavor;
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (qName.equalsIgnoreCase("id")) {
+ id = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("name")) {
+ this.name = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("ram")) {
+ ram = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("price")) {
+ price = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("flavor")) {
+ this.flavor = new Flavor(id, name, price, ram);
+ this.id = -1;
+ this.name = null;
+ this.ram = -1;
+ this.price = 0;
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorsHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorsHandler.java
new file mode 100644
index 0000000000..1ff2e7983c
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/FlavorsHandler.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Flavor;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+
+/**
+ * @author Adrian Cole
+ */
+public class FlavorsHandler extends ParseSax.HandlerWithResult> {
+ private StringBuilder currentText = new StringBuilder();
+
+ private Set slices = Sets.newLinkedHashSet();
+ private final FlavorHandler locationHandler;
+
+ @Inject
+ public FlavorsHandler(FlavorHandler locationHandler) {
+ this.locationHandler = locationHandler;
+ }
+
+ public Set extends Flavor> getResult() {
+ return slices;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ locationHandler.startElement(uri, localName, qName, attributes);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ locationHandler.endElement(uri, localName, qName);
+ if (qName.equals("flavor") && currentText.toString().trim().equals("")) {
+ this.slices.add(locationHandler.getResult());
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ locationHandler.characters(ch, start, length);
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/ImageHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/ImageHandler.java
new file mode 100644
index 0000000000..62756d8506
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/ImageHandler.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import javax.annotation.Resource;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.logging.Logger;
+import org.jclouds.slicehost.domain.Image;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Adrian Cole
+ */
+public class ImageHandler extends ParseSax.HandlerWithResult {
+ private StringBuilder currentText = new StringBuilder();
+
+ private int id;
+ private String name;
+
+ private Image image;
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ public Image getResult() {
+ return image;
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (qName.equalsIgnoreCase("id")) {
+ id = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("name")) {
+ this.name = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("image")) {
+ this.image = new Image(id, name);
+ this.id = -1;
+ this.name = null;
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/ImagesHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/ImagesHandler.java
new file mode 100644
index 0000000000..92041449f7
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/ImagesHandler.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Image;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+
+/**
+ * @author Adrian Cole
+ */
+public class ImagesHandler extends ParseSax.HandlerWithResult> {
+ private StringBuilder currentText = new StringBuilder();
+
+ private Set images = Sets.newLinkedHashSet();
+ private final ImageHandler locationHandler;
+
+ @Inject
+ public ImagesHandler(ImageHandler locationHandler) {
+ this.locationHandler = locationHandler;
+ }
+
+ public Set extends Image> getResult() {
+ return images;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ locationHandler.startElement(uri, localName, qName, attributes);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ locationHandler.endElement(uri, localName, qName);
+ if (qName.equals("image") && currentText.toString().trim().equals("")) {
+ this.images.add(locationHandler.getResult());
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ locationHandler.characters(ch, start, length);
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/SliceHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/SliceHandler.java
new file mode 100644
index 0000000000..6cb7927056
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/SliceHandler.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Slice;
+import org.jclouds.slicehost.domain.Slice.Status;
+import org.xml.sax.SAXException;
+
+import com.google.inject.internal.Sets;
+
+/**
+ * @author Adrian Cole
+ */
+public class SliceHandler extends ParseSax.HandlerWithResult {
+ private StringBuilder currentText = new StringBuilder();
+
+ private int id;
+ private String name;
+ private int flavorId;
+ @Nullable
+ private Integer imageId;
+ @Nullable
+ private Integer backupId;
+ private Status status;
+ @Nullable
+ private Integer progress;
+ private float bandwidthIn;
+ private float bandwidthOut;
+ private Set addresses = Sets.newLinkedHashSet();
+ @Nullable
+ private String rootPassword;
+
+ private Slice slice;
+
+ public Slice getResult() {
+ return slice;
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (qName.equalsIgnoreCase("id")) {
+ id = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("name")) {
+ this.name = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("flavor-id")) {
+ flavorId = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("image-id")) {
+ imageId = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("backup-id")) {
+ backupId = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("status")) {
+ this.status = Slice.Status.fromValue(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("progress")) {
+ progress = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("bw-in")) {
+ bandwidthIn = Float.parseFloat(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("bw-out")) {
+ bandwidthOut = Float.parseFloat(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("address")) {
+ this.addresses.add(currentText.toString().trim());
+ } else if (qName.equalsIgnoreCase("root-password")) {
+ this.rootPassword = currentText.toString().trim();
+ } else if (qName.equalsIgnoreCase("slice")) {
+ this.slice = new Slice(id, name, flavorId, imageId, backupId, status, progress, bandwidthIn, bandwidthOut,
+ addresses, rootPassword);
+ this.id = -1;
+ this.name = null;
+ this.flavorId = -1;
+ this.imageId = null;
+ this.backupId = null;
+ this.status = null;
+ this.progress = null;
+ this.bandwidthIn = 0;
+ this.bandwidthOut = 0;
+ this.addresses = Sets.newLinkedHashSet();
+ this.rootPassword = null;
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/main/java/org/jclouds/slicehost/xml/SlicesHandler.java b/slicehost/src/main/java/org/jclouds/slicehost/xml/SlicesHandler.java
new file mode 100644
index 0000000000..b485143cce
--- /dev/null
+++ b/slicehost/src/main/java/org/jclouds/slicehost/xml/SlicesHandler.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Slice;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+
+/**
+ * @author Adrian Cole
+ */
+public class SlicesHandler extends ParseSax.HandlerWithResult> {
+ private StringBuilder currentText = new StringBuilder();
+
+ private Set slices = Sets.newLinkedHashSet();
+ private final SliceHandler locationHandler;
+
+ @Inject
+ public SlicesHandler(SliceHandler locationHandler) {
+ this.locationHandler = locationHandler;
+ }
+
+ public Set extends Slice> getResult() {
+ return slices;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ locationHandler.startElement(uri, localName, qName, attributes);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ locationHandler.endElement(uri, localName, qName);
+ if (qName.equals("slice") && currentText.toString().trim().equals("")) {
+ this.slices.add(locationHandler.getResult());
+ }
+ currentText = new StringBuilder();
+ }
+
+ public void characters(char ch[], int start, int length) {
+ locationHandler.characters(ch, start, length);
+ currentText.append(ch, start, length);
+ }
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/ProvidersInPropertiesTest.java b/slicehost/src/test/java/org/jclouds/slicehost/ProvidersInPropertiesTest.java
new file mode 100644
index 0000000000..f59aac7099
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/ProvidersInPropertiesTest.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.util.Utils;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+/**
+ *
+ * @author Adrian Cole
+ *
+ */
+@Test(groups = "unit")
+public class ProvidersInPropertiesTest {
+
+ @Test
+ public void testSupportedComputeServiceProviders() {
+ Iterable providers = ComputeServiceUtils.getSupportedProviders();
+ assert Iterables.contains(providers, "slicehost") : providers;
+ }
+
+ @Test
+ public void testSupportedProviders() {
+ Iterable providers = Utils.getSupportedProviders();
+ assert Iterables.contains(providers, "slicehost") : providers;
+ }
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/SlicehostAsyncClientTest.java b/slicehost/src/test/java/org/jclouds/slicehost/SlicehostAsyncClientTest.java
new file mode 100644
index 0000000000..6df242ebb7
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/SlicehostAsyncClientTest.java
@@ -0,0 +1,272 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.http.functions.ReleasePayloadAndReturn;
+import org.jclouds.rest.RestClientTest;
+import org.jclouds.rest.RestContextFactory;
+import org.jclouds.rest.RestContextFactory.ContextSpec;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.jclouds.slicehost.filters.SlicehostBasic;
+import org.jclouds.slicehost.xml.FlavorHandler;
+import org.jclouds.slicehost.xml.FlavorsHandler;
+import org.jclouds.slicehost.xml.ImageHandler;
+import org.jclouds.slicehost.xml.ImagesHandler;
+import org.jclouds.slicehost.xml.SliceHandler;
+import org.jclouds.slicehost.xml.SlicesHandler;
+import org.testng.annotations.Test;
+
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code SlicehostClient}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "slicehost.SlicehostClientTest")
+public class SlicehostAsyncClientTest extends RestClientTest {
+
+ public void testCreateSlice() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("createSlice", String.class, int.class, int.class);
+ HttpRequest request = processor.createRequest(method, "ralphie", 2, 1);
+
+ assertRequestLineEquals(request, "POST https://api.slicehost.com/slices.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(
+ request,
+ "12ralphie",
+ "application/xml", false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, SliceHandler.class);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+
+ }
+
+ public void testDestroySlice() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("destroySlice", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE https://api.slicehost.com/slices/2/destroy.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testListSlices() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("listSlices");
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/slices.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, SlicesHandler.class);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetSlice() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("getSlice", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/slices/2.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, SliceHandler.class);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListFlavors() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("listFlavors");
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/flavors.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, FlavorsHandler.class);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetFlavor() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("getFlavor", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/flavors/2.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, FlavorHandler.class);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testListImages() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("listImages");
+ HttpRequest request = processor.createRequest(method);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/images.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, ImagesHandler.class);
+ assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testGetImage() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("getImage", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "GET https://api.slicehost.com/images/2.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ParseSax.class);
+ assertSaxResponseParserClassEquals(method, ImageHandler.class);
+ assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+ checkFilters(request);
+ }
+
+ public void testDestroyBackup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("destroyBackup", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "DELETE https://api.slicehost.com/backups/2/destroy.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testRebuildSliceFromImage() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("rebuildSliceFromImage", int.class, int.class);
+ HttpRequest request = processor.createRequest(method, 3, 1);
+
+ assertRequestLineEquals(request, "PUT https://api.slicehost.com/slices/3/rebuild.xml?image_id=1 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testRebuildSliceFromBackup() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("rebuildSliceFromBackup", int.class, int.class);
+ HttpRequest request = processor.createRequest(method, 3, 1);
+
+ assertRequestLineEquals(request, "PUT https://api.slicehost.com/slices/3/rebuild.xml?backup_id=1 HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testReboot() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("rebootSlice", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "PUT https://api.slicehost.com/slices/2/reboot.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ public void testHardReboot() throws IOException, SecurityException, NoSuchMethodException {
+ Method method = SlicehostAsyncClient.class.getMethod("hardRebootSlice", int.class);
+ HttpRequest request = processor.createRequest(method, 2);
+
+ assertRequestLineEquals(request, "PUT https://api.slicehost.com/slices/2/hardReboot.xml HTTP/1.1");
+ assertNonPayloadHeadersEqual(request, "");
+ assertPayloadEquals(request, null, null, false);
+
+ assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
+ assertSaxResponseParserClassEquals(method, null);
+ assertExceptionParserClassEquals(method, null);
+
+ checkFilters(request);
+ }
+
+ @Override
+ protected TypeLiteral> createTypeLiteral() {
+ return new TypeLiteral>() {
+ };
+ }
+
+ @Override
+ protected void checkFilters(HttpRequest request) {
+ assertEquals(request.getFilters().size(), 1);
+ assertEquals(request.getFilters().get(0).getClass(), SlicehostBasic.class);
+
+ }
+
+ @Override
+ public ContextSpec createContextSpec() {
+ return new RestContextFactory().createContextSpec("slicehost", "apikey", null, new Properties());
+ }
+
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java b/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java
new file mode 100644
index 0000000000..021d5ea4d6
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java
@@ -0,0 +1,342 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.security.SecureRandom;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.io.Payloads;
+import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.net.IPSocket;
+import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.predicates.SocketOpen;
+import org.jclouds.rest.RestContextFactory;
+import org.jclouds.slicehost.domain.Backup;
+import org.jclouds.slicehost.domain.Flavor;
+import org.jclouds.slicehost.domain.Image;
+import org.jclouds.slicehost.domain.Slice;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.ssh.SshException;
+import org.jclouds.ssh.jsch.config.JschSshClientModule;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Tests behavior of {@code SlicehostClient}
+ *
+ * @author Adrian Cole
+ */
+// disabled [Web Hosting #129069
+@Test(groups = "live", sequential = true, testName = "slicehost.SlicehostClientLiveTest")
+public class SlicehostClientLiveTest {
+
+ protected SlicehostClient client;
+ protected SshClient.Factory sshFactory;
+ private Predicate socketTester;
+
+ @BeforeGroups(groups = { "live" })
+ public void setupClient() {
+ String identity = checkNotNull(System.getProperty("jclouds.test.identity"), "jclouds.test.identity");
+
+ Injector injector = new RestContextFactory().createContextBuilder("slicehost", identity, null,
+ ImmutableSet. of(new Log4JLoggingModule(), new JschSshClientModule()), new Properties())
+ .buildInjector();
+
+ client = injector.getInstance(SlicehostClient.class);
+ sshFactory = injector.getInstance(SshClient.Factory.class);
+ SocketOpen socketOpen = injector.getInstance(SocketOpen.class);
+ socketTester = new RetryablePredicate(socketOpen, 120, 1, TimeUnit.SECONDS);
+ injector.injectMembers(socketOpen); // add logger
+ }
+
+ public void testListSlices() throws Exception {
+
+ Set response = client.listSlices();
+ assert null != response;
+ long initialContainerCount = response.size();
+ assertTrue(initialContainerCount >= 0);
+
+ }
+
+ public void testListSlicesDetail() throws Exception {
+ Set response = client.listSlices();
+ assert null != response;
+ long initialContainerCount = response.size();
+ assertTrue(initialContainerCount >= 0);
+ }
+
+ public void testListImages() throws Exception {
+ Set response = client.listImages();
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 1);
+ for (Image image : response) {
+ assertTrue(image.getId() >= 0);
+ assert null != image.getName() : image;
+ }
+
+ }
+
+ public void testListImagesDetail() throws Exception {
+ Set response = client.listImages();
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 0);
+ for (Image image : response) {
+ assertTrue(image.getId() >= 1);
+ assert null != image.getName() : image;
+ }
+ }
+
+ public void testGetImagesDetail() throws Exception {
+ Set response = client.listImages();
+ assert null != response;
+ long imageCount = response.size();
+ assertTrue(imageCount >= 0);
+ for (Image image : response) {
+ try {
+ Image newDetails = client.getImage(image.getId());
+ assertEquals(image, newDetails);
+ } catch (HttpResponseException e) {// Ticket #9867
+ if (e.getResponse().getStatusCode() != 400)
+ throw e;
+ }
+ }
+ }
+
+ @Test
+ public void testGetImageDetailsNotFound() throws Exception {
+ assert client.getImage(12312987) == null;
+ }
+
+ @Test
+ public void testGetSliceDetailsNotFound() throws Exception {
+ assert client.getSlice(12312987) == null;
+ }
+
+ public void testGetSlicesDetail() throws Exception {
+ Set response = client.listSlices();
+ assert null != response;
+ long sliceCount = response.size();
+ assertTrue(sliceCount >= 0);
+ for (Slice slice : response) {
+ Slice newDetails = client.getSlice(slice.getId());
+ assertEquals(slice, newDetails);
+ }
+ }
+
+ public void testListFlavors() throws Exception {
+ Set response = client.listFlavors();
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 1);
+ for (Flavor flavor : response) {
+ assertTrue(flavor.getId() >= 0);
+ assert null != flavor.getName() : flavor;
+ }
+
+ }
+
+ public void testListFlavorsDetail() throws Exception {
+ Set response = client.listFlavors();
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 0);
+ for (Flavor flavor : response) {
+ assertTrue(flavor.getId() >= 1);
+ assert null != flavor.getName() : flavor;
+ assert -1 != flavor.getRam() : flavor;
+ }
+ }
+
+ public void testGetFlavorsDetail() throws Exception {
+ Set response = client.listFlavors();
+ assert null != response;
+ long flavorCount = response.size();
+ assertTrue(flavorCount >= 0);
+ for (Flavor flavor : response) {
+ Flavor newDetails = client.getFlavor(flavor.getId());
+ assertEquals(flavor, newDetails);
+ }
+ }
+
+ @Test
+ public void testGetFlavorDetailsNotFound() throws Exception {
+ assert client.getFlavor(12312987) == null;
+ }
+
+ private String slicePrefix = System.getProperty("user.name") + ".sh";
+ private int sliceId;
+ private String rootPassword;
+ private int backupId;
+
+ @Test(enabled = false)
+ public void testCreateSlice() throws Exception {
+ int imageId = 14362;
+ int flavorId = 1;
+ Slice slice = null;
+ while (slice == null) {
+ String sliceName = slicePrefix + "createslice" + new SecureRandom().nextInt();
+ try {
+ slice = client.createSlice(sliceName, imageId, flavorId);
+ } catch (UndeclaredThrowableException e) {
+ HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ if (htpe.getResponse().getStatusCode() == 400)
+ continue;
+ throw e;
+ }
+ }
+ assertNotNull(slice.getRootPassword());
+ sliceId = slice.getId();
+ rootPassword = slice.getRootPassword();
+ assertEquals(slice.getStatus(), Slice.Status.BUILD);
+ blockUntilSliceActive(sliceId);
+ }
+
+ private void blockUntilSliceActive(int sliceId) throws InterruptedException {
+ Slice currentDetails = null;
+ for (currentDetails = client.getSlice(sliceId); currentDetails.getStatus() != Slice.Status.ACTIVE; currentDetails = client
+ .getSlice(sliceId)) {
+ System.out.printf("blocking on status active%n%s%n", currentDetails);
+ Thread.sleep(5 * 1000);
+ }
+ }
+
+ @Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateSlice")
+ public void testSliceDetails() throws Exception {
+ Slice slice = client.getSlice(sliceId);
+ assertEquals(slice.getStatus(), Slice.Status.ACTIVE);
+ assert slice.getProgress() >= 0 : "newDetails.getProgress()" + slice.getProgress();
+ assertEquals(new Integer(14362), slice.getImageId());
+ assertEquals(1, slice.getFlavorId());
+ assertNotNull(slice.getAddresses());
+ checkPassOk(slice, rootPassword);
+ }
+
+ private void checkPassOk(Slice newDetails, String pass) throws IOException {
+ try {
+ doCreateMarkerFile(newDetails, pass);
+ } catch (SshException e) {// try twice in case there is a network timeout
+ try {
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException e1) {
+ }
+ doCreateMarkerFile(newDetails, pass);
+ }
+ }
+
+ private void doCreateMarkerFile(Slice newDetails, String pass) throws IOException {
+ String ip = getIp(newDetails);
+ IPSocket socket = new IPSocket(ip, 22);
+ socketTester.apply(socket);
+
+ SshClient client = sshFactory.create(socket, "root", pass);
+ try {
+ client.connect();
+ client.put("/etc/jclouds.txt", Payloads.newStringPayload("slicehost"));
+ } finally {
+ if (client != null)
+ client.disconnect();
+ }
+ }
+
+ private String getIp(Slice newDetails) {
+ String ip = Iterables.find(newDetails.getAddresses(), new Predicate() {
+
+ @Override
+ public boolean apply(String input) {
+ return !input.startsWith("10.");
+ }
+
+ });
+ return ip;
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testSliceDetails")
+ public void testCreateBackup() throws Exception {
+ Backup backup = client.createBackupFromSlice(sliceId);
+ // TODO validate our request, as the above returns
+ assertEquals("hoofie", backup.getName());
+ assertEquals(new Integer(sliceId), backup.getSliceId());
+ backupId = backup.getId();
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testCreateBackup")
+ public void testRebuildSlice() throws Exception {
+ client.rebuildSliceFromBackup(sliceId, backupId);
+ blockUntilSliceActive(sliceId);
+ // issue Web Hosting #119580 imageId comes back incorrect after rebuild
+ // assertEquals(new Integer(imageId),
+ // client.getSlice(sliceId).getImageId());
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildSlice")
+ public void testRebootHard() throws Exception {
+ client.hardRebootSlice(sliceId);
+ blockUntilSliceActive(sliceId);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootHard")
+ public void testRebootSoft() throws Exception {
+ client.rebootSlice(sliceId);
+ blockUntilSliceActive(sliceId);
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootSoft")
+ void testDeleteBackup() {
+ if (backupId > 0) {
+ client.destroyBackup(backupId);
+ assert client.getBackup(backupId) == null;
+ }
+ }
+
+ @Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testDeleteBackup")
+ void destroySlice1() {
+ if (sliceId > 0) {
+ client.destroySlice(sliceId);
+ assert client.getSlice(sliceId) == null;
+ }
+ }
+
+ @AfterTest
+ void destroySlicesOnEnd() {
+ if (sliceId > 0) {
+ client.destroySlice(sliceId);
+ }
+
+ }
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java b/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java
new file mode 100644
index 0000000000..cde6d51a15
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/compute/SlicehostComputeServiceLiveTest.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.compute;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.jclouds.compute.BaseComputeServiceLiveTest;
+import org.jclouds.compute.ComputeServiceContextFactory;
+import org.jclouds.compute.domain.Architecture;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.slicehost.SlicehostAsyncClient;
+import org.jclouds.slicehost.SlicehostClient;
+import org.jclouds.rest.RestContext;
+import org.jclouds.ssh.jsch.config.JschSshClientModule;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * Generally disabled, as it incurs higher fees.
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "live", enabled = true, sequential = true, testName = "slicehost.SlicehostComputeServiceLiveTest")
+public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+ @BeforeClass
+ @Override
+ public void setServiceDefaults() {
+ provider = "slicehost";
+ }
+
+ @Test
+ public void testTemplateBuilder() {
+ Template defaultTemplate = client.templateBuilder().build();
+ assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_64);
+ assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.UBUNTU);
+ assertEquals(defaultTemplate.getLocation().getId(), "slicehost");
+ assertEquals(defaultTemplate.getSize().getCores(), 0.25d);
+ }
+
+ @Override
+ protected JschSshClientModule getSshModule() {
+ return new JschSshClientModule();
+ }
+
+ public void testAssignability() throws Exception {
+ @SuppressWarnings("unused")
+ RestContext tmContext = new ComputeServiceContextFactory()
+ .createContext(provider, identity, credential).getProviderSpecificContext();
+ }
+
+ @Override
+ protected void checkNodes(Iterable extends NodeMetadata> nodes, String tag) throws IOException {
+ super.checkNodes(nodes, tag);
+ for (NodeMetadata node : nodes) {
+ assertEquals(node.getLocation().getScope(), LocationScope.HOST);
+ }
+ }
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorHandlerTest.java b/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorHandlerTest.java
new file mode 100644
index 0000000000..15b9c9b505
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorHandlerTest.java
@@ -0,0 +1,50 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Flavor;
+import org.testng.annotations.Test;
+
+/**
+ * Tests behavior of {@code FlavorHandler}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "slicehost.FlavorHandler")
+public class FlavorHandlerTest extends BaseHandlerTest {
+
+ ParseSax createParser() {
+ ParseSax parser = (ParseSax) factory.create(injector.getInstance(FlavorHandler.class));
+ return parser;
+ }
+
+ public void test() {
+ InputStream is = getClass().getResourceAsStream("/test_get_flavor.xml");
+ Flavor expects = new Flavor(1, "256 slice", 2000, 256);
+
+ assertEquals(createParser().parse(is), expects);
+ }
+
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorsHandlerTest.java b/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorsHandlerTest.java
new file mode 100644
index 0000000000..8afd674d1e
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/xml/FlavorsHandlerTest.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Flavor;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code FlavorsHandler}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "slicehost.FlavorsHandler")
+public class FlavorsHandlerTest extends BaseHandlerTest {
+
+ ParseSax> createParser() {
+ ParseSax> parser = (ParseSax>) factory.create(injector
+ .getInstance(FlavorsHandler.class));
+ return parser;
+ }
+
+ public void test() {
+ InputStream is = getClass().getResourceAsStream("/test_list_flavors.xml");
+ Set extends Flavor> expects = ImmutableSet.of(new Flavor(1, "256 slice", 2000, 256), new Flavor(2, "512 slice",
+ 3800, 512), new Flavor(3, "1GB slice", 7000, 1024), new Flavor(4, "2GB slice", 13000, 2048));
+ assertEquals(createParser().parse(is), expects);
+ }
+
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/xml/SliceHandlerTest.java b/slicehost/src/test/java/org/jclouds/slicehost/xml/SliceHandlerTest.java
new file mode 100644
index 0000000000..4350e10fe2
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/xml/SliceHandlerTest.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Slice;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code SliceHandler}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "slicehost.SliceHandler")
+public class SliceHandlerTest extends BaseHandlerTest {
+
+ ParseSax createParser() {
+ ParseSax parser = (ParseSax) factory.create(injector.getInstance(SliceHandler.class));
+ return parser;
+ }
+
+ public void test() {
+ InputStream is = getClass().getResourceAsStream("/test_get_slice.xml");
+ Slice expects = new Slice(1, "jclouds-foo", 1, 10, null, Slice.Status.BUILD, 0, 0, 0, ImmutableSet. of(
+ "174.143.212.229", "10.176.164.199"), null);
+
+ assertEquals(createParser().parse(is), expects);
+ }
+
+ public void testNew() {
+ InputStream is = getClass().getResourceAsStream("/test_new_slice.xml");
+ Slice expects = new Slice(71907, "slicetest", 1, 11, null, Slice.Status.BUILD, 0, 0, 0, ImmutableSet. of(
+ "10.176.168.15", "67.23.20.114"), "fooadfa1231");
+
+ assertEquals(createParser().parse(is), expects);
+
+ }
+
+}
diff --git a/slicehost/src/test/java/org/jclouds/slicehost/xml/SlicesHandlerTest.java b/slicehost/src/test/java/org/jclouds/slicehost/xml/SlicesHandlerTest.java
new file mode 100644
index 0000000000..76769134d7
--- /dev/null
+++ b/slicehost/src/test/java/org/jclouds/slicehost/xml/SlicesHandlerTest.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright (C) 2010 Cloud Conscious, LLC.
+ *
+ * ====================================================================
+ * Licensed 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.slicehost.xml;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.slicehost.domain.Slice;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code SlicesHandler}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "slicehost.SlicesHandler")
+public class SlicesHandlerTest extends BaseHandlerTest {
+
+ ParseSax> createParser() {
+ ParseSax> parser = (ParseSax>) factory.create(injector
+ .getInstance(SlicesHandler.class));
+ return parser;
+ }
+
+ public void test() {
+ InputStream is = getClass().getResourceAsStream("/test_get_slice.xml");
+ Set extends Slice> expects = ImmutableSet.of(new Slice(1, "jclouds-foo", 1, 10, null, Slice.Status.BUILD, 0, 0,
+ 0, ImmutableSet. of("174.143.212.229", "10.176.164.199"), null));
+
+ assertEquals(createParser().parse(is), expects);
+ }
+
+}
diff --git a/slicehost/src/test/resources/log4j.xml b/slicehost/src/test/resources/log4j.xml
new file mode 100755
index 0000000000..f7337defb9
--- /dev/null
+++ b/slicehost/src/test/resources/log4j.xml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/slicehost/src/test/resources/test_errors.xml b/slicehost/src/test/resources/test_errors.xml
new file mode 100644
index 0000000000..6555aa11d5
--- /dev/null
+++ b/slicehost/src/test/resources/test_errors.xml
@@ -0,0 +1,4 @@
+
+
+ Slice parameters are not properly nested
+
diff --git a/slicehost/src/test/resources/test_get_flavor.xml b/slicehost/src/test/resources/test_get_flavor.xml
new file mode 100644
index 0000000000..d15457e55a
--- /dev/null
+++ b/slicehost/src/test/resources/test_get_flavor.xml
@@ -0,0 +1,7 @@
+
+
+ 1
+ 256 slice
+ 2000
+ 256
+
\ No newline at end of file
diff --git a/slicehost/src/test/resources/test_get_slice.xml b/slicehost/src/test/resources/test_get_slice.xml
new file mode 100644
index 0000000000..dbe8d32fd1
--- /dev/null
+++ b/slicehost/src/test/resources/test_get_slice.xml
@@ -0,0 +1,17 @@
+
+
+ jclouds-foo
+ 10
+
+ 174.143.212.229
+ 10.176.164.199
+
+
+ 1
+ 0.0
+ 0.0
+ 1
+ build
+ 174.143.212.229
+
+
diff --git a/slicehost/src/test/resources/test_list_flavors.xml b/slicehost/src/test/resources/test_list_flavors.xml
new file mode 100644
index 0000000000..fc1651c386
--- /dev/null
+++ b/slicehost/src/test/resources/test_list_flavors.xml
@@ -0,0 +1,27 @@
+
+
+
+ 1
+ 256 slice
+ 2000
+ 256
+
+
+ 2
+ 512 slice
+ 3800
+ 512
+
+
+ 3
+ 1GB slice
+ 7000
+ 1024
+
+
+ 4
+ 2GB slice
+ 13000
+ 2048
+
+
diff --git a/slicehost/src/test/resources/test_list_images.xml b/slicehost/src/test/resources/test_list_images.xml
new file mode 100644
index 0000000000..f87b4e7301
--- /dev/null
+++ b/slicehost/src/test/resources/test_list_images.xml
@@ -0,0 +1,47 @@
+
+
+
+ CentOS 5.2
+ 2
+
+
+ Gentoo 2008.0
+ 3
+
+
+ Debian 5.0 (lenny)
+ 4
+
+
+ Fedora 10 (Cambridge)
+ 5
+
+
+ CentOS 5.3
+ 7
+
+
+ Ubuntu 9.04 (jaunty)
+ 8
+
+
+ Arch 2009.02
+ 9
+
+
+ Ubuntu 8.04.2 LTS (hardy)
+ 10
+
+
+ Ubuntu 8.10 (intrepid)
+ 11
+
+
+ Red Hat EL 5.3
+ 12
+
+
+ Fedora 11 (Leonidas)
+ 13
+
+
diff --git a/slicehost/src/test/resources/test_new_slice.xml b/slicehost/src/test/resources/test_new_slice.xml
new file mode 100644
index 0000000000..2257f2660d
--- /dev/null
+++ b/slicehost/src/test/resources/test_new_slice.xml
@@ -0,0 +1,16 @@
+
+ slicetest
+ 11
+
+ 10.176.168.15
+ 67.23.20.114
+
+ fooadfa1231
+
+ 71907
+ 0.0
+ 0.0
+ 1
+ build
+ 10.176.168.15
+