Issue 165: compute service now works in gogrid

This commit is contained in:
Adrian Cole 2010-07-26 12:50:50 -07:00
parent 0612e5e1ba
commit 9ff4f84783
11 changed files with 168 additions and 46 deletions

View File

@ -37,6 +37,7 @@ import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.slicehost.binders.BindCreateBackupToXmlPayload;
import org.jclouds.slicehost.binders.BindCreateSliceToXmlPayload; import org.jclouds.slicehost.binders.BindCreateSliceToXmlPayload;
import org.jclouds.slicehost.domain.Backup; import org.jclouds.slicehost.domain.Backup;
import org.jclouds.slicehost.domain.Flavor; import org.jclouds.slicehost.domain.Flavor;
@ -194,11 +195,12 @@ public interface SlicehostAsyncClient {
ListenableFuture<Backup> getBackup(@PathParam("id") int id); ListenableFuture<Backup> getBackup(@PathParam("id") int id);
/** /**
* @see SlicehostClient#createBackupFromSlice * @see BackuphostClient#createBackup
*/ */
@PUT @POST
@Path("/backups.xml") @Path("/slices.xml")
@MapBinder(BindCreateBackupToXmlPayload.class)
@XMLResponseParser(BackupHandler.class) @XMLResponseParser(BackupHandler.class)
ListenableFuture<Backup> createBackupFromSlice(@QueryParam("slice_id") int sliceId); ListenableFuture<Backup> createBackup(@MapPayloadParam("name") String name, @MapPayloadParam("slice_id") int sliceId);
} }

View File

@ -73,6 +73,6 @@ public interface SlicehostClient {
Backup getBackup(int id); Backup getBackup(int id);
Backup createBackupFromSlice(int sliceId); Backup createBackup(String name, int sliceId);
} }

View File

@ -35,7 +35,8 @@ public class SlicehostPropertiesBuilder extends PropertiesBuilder {
protected Properties defaultProperties() { protected Properties defaultProperties() {
Properties properties = super.defaultProperties(); Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_ENDPOINT, "https://api.slicehost.com"); properties.setProperty(PROPERTY_ENDPOINT, "https://api.slicehost.com");
properties.setProperty(PROPERTY_API_VERSION, "1.0"); properties.setProperty(PROPERTY_API_VERSION, "1.4.1.1");
properties.setProperty("jclouds.ssh.max_retries", "8");
return properties; return properties;
} }

View File

@ -0,0 +1,63 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 BindCreateBackupToXmlPayload implements MapBinder {
private final BindToStringPayload binder;
@Inject
BindCreateBackupToXmlPayload(BindToStringPayload binder) {
this.binder = binder;
}
public void bindToRequest(HttpRequest request, Map<String, String> postParams) {
String sliceId = checkNotNull(postParams.get("slice_id"), "slice_id");
String name = checkNotNull(postParams.get("name"), "name");
StringBuilder builder = new StringBuilder();
builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><backup>");
builder.append("<slice-id type=\"integer\">").append(sliceId).append("</slice-id>");
builder.append("<name>").append(name).append("</name>");
builder.append("</backup>");
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");
}
}

View File

@ -122,8 +122,8 @@ public class SlicehostComputeServiceContextModule extends AbstractModule {
@Provides @Provides
@Named("NAMING_CONVENTION") @Named("NAMING_CONVENTION")
@Singleton @Singleton
String provideNamingConvention(@Named(Constants.PROPERTY_IDENTITY) String identity) { String provideNamingConvention() {
return identity + "-%s-%s"; return "%s-%s";
} }
@Singleton @Singleton
@ -275,8 +275,8 @@ public class SlicehostComputeServiceContextModule extends AbstractModule {
holder.logger.debug(">> providing sizes"); holder.logger.debug(">> providing sizes");
for (final Flavor from : sync.listFlavors()) { for (final Flavor from : sync.listFlavors()) {
sizes.add(new SizeImpl(from.getId() + "", from.getName(), from.getId() + "", location, null, ImmutableMap sizes.add(new SizeImpl(from.getId() + "", from.getName(), from.getId() + "", location, null, ImmutableMap
.<String, String> of(), from.getRam() / 1024.0, from.getRam(), (from.getRam() * 4) / 1024, ImagePredicates .<String, String> of(), from.getRam() / 1024.0, from.getRam(), (from.getRam() * 4) / 1024,
.any())); ImagePredicates.any()));
} }
holder.logger.debug("<< sizes(%d)", sizes.size()); holder.logger.debug("<< sizes(%d)", sizes.size());
return sizes; return sizes;

View File

@ -46,8 +46,7 @@ import com.google.common.collect.Iterables;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> { public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> {
public static final Pattern SECOND_FIELD_DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX = Pattern public static final Pattern DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX = Pattern.compile("([^-]+)-[0-9a-f]+");
.compile("[^-]+-([^-]+)-[0-9a-f]+");
private final Location location; private final Location location;
private final Map<Slice.Status, NodeState> sliceToNodeState; private final Map<Slice.Status, NodeState> sliceToNodeState;
private final Set<? extends Image> images; private final Set<? extends Image> images;
@ -56,18 +55,15 @@ public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> {
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private static class FindImageForSlice implements Predicate<Image> { private static class FindImageForSlice implements Predicate<Image> {
private final Location location; private final Slice slice;
private final Slice instance;
private FindImageForSlice(Location location, Slice instance) { private FindImageForSlice(Slice slice) {
this.location = location; this.slice = slice;
this.instance = instance;
} }
@Override @Override
public boolean apply(Image input) { public boolean apply(Image input) {
return input.getProviderId().equals(instance.getImageId() + "") return input.getProviderId().equals(slice.getImageId() + "");
&& (input.getLocation() == null || input.getLocation().equals(location.getParent()));
} }
} }
@ -81,11 +77,11 @@ public class SliceToNodeMetadata implements Function<Slice, NodeMetadata> {
@Override @Override
public NodeMetadata apply(Slice from) { public NodeMetadata apply(Slice from) {
Matcher matcher = SECOND_FIELD_DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX.matcher(from.getName()); Matcher matcher = DELIMETED_BY_HYPHEN_ENDING_IN_HYPHEN_HEX.matcher(from.getName());
final String tag = matcher.find() ? matcher.group(1) : null; final String tag = matcher.find() ? matcher.group(1) : null;
Image image = null; Image image = null;
try { try {
image = Iterables.find(images, new FindImageForSlice(location, from)); image = Iterables.find(images, new FindImageForSlice(from));
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
logger.warn("could not find a matching image for slice %s in location %s", from, location); logger.warn("could not find a matching image for slice %s in location %s", from, location);
} }

View File

@ -64,6 +64,9 @@ public class ParseSlicehostErrorFromHttpResponse implements HttpErrorHandler {
exception = new ResourceNotFoundException(message); exception = new ResourceNotFoundException(message);
} }
break; break;
case 422:
exception = new IllegalStateException(content);
break;
default: default:
exception = new HttpResponseException(command, response, content); exception = new HttpResponseException(command, response, content);
} }
@ -100,7 +103,8 @@ public class ParseSlicehostErrorFromHttpResponse implements HttpErrorHandler {
} }
String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) { String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) {
if (response.getPayload() != null) { // slicehost returns " " which is unparsable
if (response.getPayload() != null && response.getPayload().getContentLength() != 1) {
return errorParser.parse(response.getPayload()); return errorParser.parse(response.getPayload());
} }
return null; return null;

View File

@ -34,6 +34,7 @@ import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.slicehost.filters.SlicehostBasic; import org.jclouds.slicehost.filters.SlicehostBasic;
import org.jclouds.slicehost.xml.BackupHandler;
import org.jclouds.slicehost.xml.FlavorHandler; import org.jclouds.slicehost.xml.FlavorHandler;
import org.jclouds.slicehost.xml.FlavorsHandler; import org.jclouds.slicehost.xml.FlavorsHandler;
import org.jclouds.slicehost.xml.ImageHandler; import org.jclouds.slicehost.xml.ImageHandler;
@ -251,6 +252,25 @@ public class SlicehostAsyncClientTest extends RestClientTest<SlicehostAsyncClien
checkFilters(request); checkFilters(request);
} }
public void testCreateBackup() throws IOException, SecurityException, NoSuchMethodException {
Method method = SlicehostAsyncClient.class.getMethod("createBackup", String.class, int.class);
HttpRequest request = processor.createRequest(method, "ralphie", 2);
assertRequestLineEquals(request, "POST https://api.slicehost.com/slices.xml HTTP/1.1");
assertNonPayloadHeadersEqual(request, "");
assertPayloadEquals(
request,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><backup><slice-id type=\"integer\">2</slice-id><name>ralphie</name></backup>",
"application/xml", false);
assertResponseParserClassEquals(method, request, ParseSax.class);
assertSaxResponseParserClassEquals(method, BackupHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(request);
}
@Override @Override
protected TypeLiteral<RestAnnotationProcessor<SlicehostAsyncClient>> createTypeLiteral() { protected TypeLiteral<RestAnnotationProcessor<SlicehostAsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<SlicehostAsyncClient>>() { return new TypeLiteral<RestAnnotationProcessor<SlicehostAsyncClient>>() {

View File

@ -203,7 +203,7 @@ public class SlicehostClientLiveTest {
private String rootPassword; private String rootPassword;
private int backupId; private int backupId;
@Test(enabled = false) @Test(enabled = true)
public void testCreateSlice() throws Exception { public void testCreateSlice() throws Exception {
int imageId = 14362; int imageId = 14362;
int flavorId = 1; int flavorId = 1;
@ -235,7 +235,7 @@ public class SlicehostClientLiveTest {
} }
} }
@Test(enabled = false, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateSlice") @Test(enabled = true, timeOut = 5 * 60 * 1000, dependsOnMethods = "testCreateSlice")
public void testSliceDetails() throws Exception { public void testSliceDetails() throws Exception {
Slice slice = client.getSlice(sliceId); Slice slice = client.getSlice(sliceId);
assertEquals(slice.getStatus(), Slice.Status.ACTIVE); assertEquals(slice.getStatus(), Slice.Status.ACTIVE);
@ -285,9 +285,9 @@ public class SlicehostClientLiveTest {
return ip; return ip;
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testSliceDetails") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testSliceDetails")
public void testCreateBackup() throws Exception { public void testCreateBackup() throws Exception {
Backup backup = client.createBackupFromSlice(sliceId); Backup backup = client.createBackup("hoofie", sliceId);
// TODO validate our request, as the above returns <nil-classes // TODO validate our request, as the above returns <nil-classes
// type="array"/> // type="array"/>
assertEquals("hoofie", backup.getName()); assertEquals("hoofie", backup.getName());
@ -295,7 +295,7 @@ public class SlicehostClientLiveTest {
backupId = backup.getId(); backupId = backup.getId();
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testCreateBackup") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testCreateBackup")
public void testRebuildSlice() throws Exception { public void testRebuildSlice() throws Exception {
client.rebuildSliceFromBackup(sliceId, backupId); client.rebuildSliceFromBackup(sliceId, backupId);
blockUntilSliceActive(sliceId); blockUntilSliceActive(sliceId);
@ -304,19 +304,19 @@ public class SlicehostClientLiveTest {
// client.getSlice(sliceId).getImageId()); // client.getSlice(sliceId).getImageId());
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildSlice") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebuildSlice")
public void testRebootHard() throws Exception { public void testRebootHard() throws Exception {
client.hardRebootSlice(sliceId); client.hardRebootSlice(sliceId);
blockUntilSliceActive(sliceId); blockUntilSliceActive(sliceId);
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootHard") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootHard")
public void testRebootSoft() throws Exception { public void testRebootSoft() throws Exception {
client.rebootSlice(sliceId); client.rebootSlice(sliceId);
blockUntilSliceActive(sliceId); blockUntilSliceActive(sliceId);
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootSoft") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testRebootSoft")
void testDeleteBackup() { void testDeleteBackup() {
if (backupId > 0) { if (backupId > 0) {
client.destroyBackup(backupId); client.destroyBackup(backupId);
@ -324,7 +324,7 @@ public class SlicehostClientLiveTest {
} }
} }
@Test(enabled = false, timeOut = 10 * 60 * 1000, dependsOnMethods = "testDeleteBackup") @Test(enabled = true, timeOut = 10 * 60 * 1000, dependsOnMethods = "testDeleteBackup")
void destroySlice1() { void destroySlice1() {
if (sliceId > 0) { if (sliceId > 0) {
client.destroySlice(sliceId); client.destroySlice(sliceId);

View File

@ -21,18 +21,14 @@ package org.jclouds.slicehost.compute;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.compute.BaseComputeServiceLiveTest; import org.jclouds.compute.BaseComputeServiceLiveTest;
import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.ComputeServiceContextFactory;
import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.Template;
import org.jclouds.domain.LocationScope; import org.jclouds.rest.RestContext;
import org.jclouds.slicehost.SlicehostAsyncClient; import org.jclouds.slicehost.SlicehostAsyncClient;
import org.jclouds.slicehost.SlicehostClient; import org.jclouds.slicehost.SlicehostClient;
import org.jclouds.rest.RestContext;
import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.jclouds.ssh.jsch.config.JschSshClientModule;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -68,15 +64,7 @@ public class SlicehostComputeServiceLiveTest extends BaseComputeServiceLiveTest
public void testAssignability() throws Exception { public void testAssignability() throws Exception {
@SuppressWarnings("unused") @SuppressWarnings("unused")
RestContext<SlicehostClient, SlicehostAsyncClient> tmContext = new ComputeServiceContextFactory() RestContext<SlicehostClient, SlicehostAsyncClient> tmContext = new ComputeServiceContextFactory().createContext(
.createContext(provider, identity, credential).getProviderSpecificContext(); 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);
}
} }
} }

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<slices type="array">
<slice>
<name>slicehost-1</name>
<image-id type="integer">50</image-id>
<addresses type="array">
<address>10.179.78.86</address>
<address>173.203.51.27</address>
</addresses>
<progress type="integer">100</progress>
<id type="integer">281343</id>
<bw-out type="float">0.0</bw-out>
<bw-in type="float">0.07</bw-in>
<flavor-id type="integer">1</flavor-id>
<status>active</status>
<ip-address>10.179.78.86</ip-address>
</slice>
<slice>
<name>slicehost-602</name>
<image-id type="integer">50</image-id>
<addresses type="array">
<address>10.179.78.89</address>
<address>173.203.51.32</address>
</addresses>
<progress type="integer">100</progress>
<id type="integer">281347</id>
<bw-out type="float">0.0</bw-out>
<bw-in type="float">0.07</bw-in>
<flavor-id type="integer">1</flavor-id>
<status>active</status>
<ip-address>10.179.78.89</ip-address>
</slice>
<slice>
<name>slicehostblock-a7</name>
<image-id type="integer">50</image-id>
<addresses type="array">
<address>10.179.78.91</address>
<address>173.203.51.33</address>
</addresses>
<progress type="integer">100</progress>
<id type="integer">281357</id>
<bw-out type="float">0.0</bw-out>
<bw-in type="float">0.0</bw-in>
<flavor-id type="integer">1</flavor-id>
<status>build</status>
<ip-address>10.179.78.91</ip-address>
</slice>
</slices>