deltacloud: added state handling

This commit is contained in:
Adrian Cole 2010-12-29 11:12:36 +01:00
parent e8b2303ba1
commit fced69c15f
14 changed files with 182 additions and 55 deletions

View File

@ -32,13 +32,16 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.deltacloud.collections.HardwareProfiles;
import org.jclouds.deltacloud.collections.Images;
import org.jclouds.deltacloud.collections.InstanceStates;
import org.jclouds.deltacloud.collections.Instances;
import org.jclouds.deltacloud.collections.Realms;
import org.jclouds.deltacloud.domain.DeltacloudCollection;
import org.jclouds.deltacloud.domain.HardwareProfile;
import org.jclouds.deltacloud.domain.Image;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.domain.Realm;
import org.jclouds.deltacloud.domain.Transition;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.deltacloud.xml.DeltacloudCollectionsHandler;
import org.jclouds.deltacloud.xml.HardwareProfileHandler;
@ -46,6 +49,7 @@ import org.jclouds.deltacloud.xml.HardwareProfilesHandler;
import org.jclouds.deltacloud.xml.ImageHandler;
import org.jclouds.deltacloud.xml.ImagesHandler;
import org.jclouds.deltacloud.xml.InstanceHandler;
import org.jclouds.deltacloud.xml.InstanceStatesHandler;
import org.jclouds.deltacloud.xml.InstancesHandler;
import org.jclouds.deltacloud.xml.RealmHandler;
import org.jclouds.deltacloud.xml.RealmsHandler;
@ -55,10 +59,12 @@ import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnEmptyMultimapOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
/**
@ -78,9 +84,20 @@ public interface DeltacloudAsyncClient {
*/
@GET
@Path("")
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
@XMLResponseParser(DeltacloudCollectionsHandler.class)
ListenableFuture<? extends Set<? extends DeltacloudCollection>> getCollections();
/**
* @see DeltacloudClient#getInstanceStates
*/
@GET
@Endpoint(InstanceStates.class)
@Path("")
@ExceptionParser(ReturnEmptyMultimapOnNotFoundOr404.class)
@XMLResponseParser(InstanceStatesHandler.class)
ListenableFuture<? extends Multimap<InstanceState, ? extends Transition>> getInstanceStates();
/**
* @see DeltacloudClient#listRealms
*/

View File

@ -28,10 +28,14 @@ import org.jclouds.deltacloud.domain.DeltacloudCollection;
import org.jclouds.deltacloud.domain.HardwareProfile;
import org.jclouds.deltacloud.domain.Image;
import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.domain.Realm;
import org.jclouds.deltacloud.domain.Transition;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.common.collect.Multimap;
/**
* Provides synchronous access to deltacloud.
* <p/>
@ -50,6 +54,12 @@ public interface DeltacloudClient {
*/
Set<? extends DeltacloudCollection> getCollections();
/**
*
* @return The possible states of an instance, and how to traverse between them
*/
Multimap<InstanceState, ? extends Transition> getInstanceStates();
/**
* The realms collection will return a set of all realms available to the current user.
*

View File

@ -33,6 +33,7 @@ import org.jclouds.deltacloud.DeltacloudAsyncClient;
import org.jclouds.deltacloud.DeltacloudClient;
import org.jclouds.deltacloud.collections.HardwareProfiles;
import org.jclouds.deltacloud.collections.Images;
import org.jclouds.deltacloud.collections.InstanceStates;
import org.jclouds.deltacloud.collections.Instances;
import org.jclouds.deltacloud.collections.Realms;
import org.jclouds.deltacloud.domain.DeltacloudCollection;
@ -135,4 +136,10 @@ public class DeltacloudRestClientModule extends RestClientModule<DeltacloudClien
protected URI provideRealmCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
return findCollectionWithRel(collectionSupplier.get(), "realms").getHref();
}
@Provides
@InstanceStates
protected URI provideInstanceStateCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
return findCollectionWithRel(collectionSupplier.get(), "instance_states").getHref();
}
}

View File

@ -79,46 +79,4 @@ public class DeltacloudCollection {
public String toString() {
return "[href=" + href + ", rel=" + rel + ", features=" + features + "]";
}
public static class Feature {
private final String name;
public Feature(String name) {
this.name = checkNotNull(name, "name");
}
public String getName() {
return name;
}
@Override
public String toString() {
return "[name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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;
Feature other = (Feature) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
}

View File

@ -30,12 +30,16 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public enum InstanceAction {
CREATE,
REBOOT,
START,
STOP,
DESTROY,
UNRECOGNIZED;
public String value() {

View File

@ -29,21 +29,26 @@ import static com.google.common.base.Preconditions.checkNotNull;
*
*/
public enum InstanceState {
/**
* initial state, before instance is created.
*/
START,
/**
* the instance is in the process of being launched
*/
PENDING,
/**
* the instance launched (although the boot process might not be completed)
*/
RUNNING,
/**
* the instance is stopped
*/
STOPPED,
/**
* the instance launched (although the boot process might not be completed)
* the instance is terminated
*/
RUNNING,
FINISH,
/**
* state returned as something besides the above.
*/

View File

@ -26,10 +26,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
* @author Adrian Cole
*/
public class TransitionOnAction implements Transition {
private final String action;
private final InstanceAction action;
private final InstanceState to;
public TransitionOnAction(String action, InstanceState to) {
public TransitionOnAction(InstanceAction action, InstanceState to) {
this.to = checkNotNull(to, "to");
this.action = checkNotNull(action, "action");
}
@ -38,7 +38,7 @@ public class TransitionOnAction implements Transition {
return to;
}
public String getAction() {
public InstanceAction getAction() {
return action;
}

View File

@ -24,7 +24,7 @@ import java.util.Map;
import java.util.Set;
import org.jclouds.deltacloud.domain.DeltacloudCollection;
import org.jclouds.deltacloud.domain.DeltacloudCollection.Feature;
import org.jclouds.deltacloud.domain.Feature;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.Utils;
import org.xml.sax.Attributes;

View File

@ -21,6 +21,7 @@ package org.jclouds.deltacloud.xml;
import java.util.Map;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.domain.Transition;
import org.jclouds.deltacloud.domain.TransitionAutomatically;
@ -56,8 +57,8 @@ public class InstanceStatesHandler extends ParseSax.HandlerWithResult<Multimap<I
else
states.put(
state,
new TransitionOnAction(attributes.get("action"), InstanceState.valueOf(attributes.get("to")
.toUpperCase())));
new TransitionOnAction(InstanceAction.fromValue(attributes.get("action")), InstanceState
.valueOf(attributes.get("to").toUpperCase())));
}
}

View File

@ -36,6 +36,7 @@ import org.jclouds.deltacloud.xml.HardwareProfilesHandler;
import org.jclouds.deltacloud.xml.ImageHandler;
import org.jclouds.deltacloud.xml.ImagesHandler;
import org.jclouds.deltacloud.xml.InstanceHandler;
import org.jclouds.deltacloud.xml.InstanceStatesHandler;
import org.jclouds.deltacloud.xml.InstancesHandler;
import org.jclouds.deltacloud.xml.RealmHandler;
import org.jclouds.deltacloud.xml.RealmsHandler;
@ -49,6 +50,7 @@ import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextSpec;
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
import org.jclouds.rest.functions.ReturnEmptyMultimapOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
@ -89,12 +91,27 @@ public class DeltacloudAsyncClientTest extends RestClientTest<DeltacloudAsyncCli
assertResponseParserClassEquals(method, httpRequest, ParseSax.class);
assertSaxResponseParserClassEquals(method, DeltacloudCollectionsHandler.class);
assertExceptionParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testGetInstanceStates() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("getInstanceStates");
HttpRequest request = processor.createRequest(method);
assertRequestLineEquals(request, "GET http://localhost:3001/api/instance_states HTTP/1.1");
assertNonPayloadHeadersEqual(request, "Accept: application/xml\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ParseSax.class);
assertSaxResponseParserClassEquals(method, InstanceStatesHandler.class);
assertExceptionParserClassEquals(method, ReturnEmptyMultimapOnNotFoundOr404.class);
checkFilters(request);
}
public void testListRealms() throws IOException, SecurityException, NoSuchMethodException {
Method method = DeltacloudAsyncClient.class.getMethod("listRealms");
HttpRequest request = processor.createRequest(method);
@ -326,6 +343,10 @@ public class DeltacloudAsyncClientTest extends RestClientTest<DeltacloudAsyncCli
return URI.create("http://localhost:3001/api/realms");
}
@Override
protected URI provideInstanceStateCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
return URI.create("http://localhost:3001/api/instance_states");
}
}
@Override

View File

@ -38,6 +38,7 @@ import org.jclouds.deltacloud.domain.Instance;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.domain.Realm;
import org.jclouds.deltacloud.domain.Transition;
import org.jclouds.deltacloud.options.CreateInstanceOptions;
import org.jclouds.domain.Credentials;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
@ -56,6 +57,7 @@ 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.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.inject.Guice;
import com.google.inject.Module;
@ -116,6 +118,28 @@ public class DeltacloudClientLiveTest {
assertNotNull(links);
}
@Test
public void testGetInstanceStatesCanGoFromStartToFinish() throws Exception {
Multimap<InstanceState, ? extends Transition> states = client.getInstanceStates();
assertNotNull(states);
Iterable<Transition> fromStart = findChainToFinish(InstanceState.START, states);
assert Iterables.size(fromStart) > 0 : fromStart;
}
Iterable<Transition> findChainToFinish(InstanceState currentState,
Multimap<InstanceState, ? extends Transition> states) {
for (Transition transition : states.get(currentState)) {
if (currentState.ordinal() >= transition.getTo().ordinal())
continue;
if (transition.getTo() == InstanceState.FINISH)
return ImmutableSet.<Transition> of(transition);
Iterable<Transition> transitions = findChainToFinish(transition.getTo(), states);
if (Iterables.size(transitions) > 0)
return Iterables.concat(ImmutableSet.of(transition), transitions);
}
return ImmutableSet.<Transition> of();
}
public void testListAndGetRealms() throws Exception {
Set<? extends Realm> response = client.listRealms();
assert null != response;

View File

@ -26,7 +26,7 @@ import java.net.URI;
import java.util.Set;
import org.jclouds.deltacloud.domain.DeltacloudCollection;
import org.jclouds.deltacloud.domain.DeltacloudCollection.Feature;
import org.jclouds.deltacloud.domain.Feature;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;

View File

@ -0,0 +1,62 @@
/**
*
* 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.deltacloud.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.deltacloud.domain.InstanceAction;
import org.jclouds.deltacloud.domain.InstanceState;
import org.jclouds.deltacloud.domain.Transition;
import org.jclouds.deltacloud.domain.TransitionAutomatically;
import org.jclouds.deltacloud.domain.TransitionOnAction;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
/**
* Tests behavior of {@code InstanceStatesHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class InstanceStatesHandlerTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/test_get_states.xml");
Multimap<InstanceState, ? extends Transition> expects = ImmutableMultimap
.<InstanceState, Transition> builder()
.put(InstanceState.START, new TransitionOnAction(InstanceAction.CREATE, InstanceState.PENDING))
.put(InstanceState.PENDING, new TransitionAutomatically(InstanceState.RUNNING))
.putAll(InstanceState.RUNNING, new TransitionOnAction(InstanceAction.REBOOT, InstanceState.RUNNING),
new TransitionOnAction(InstanceAction.STOP, InstanceState.STOPPED))
.putAll(InstanceState.STOPPED, new TransitionOnAction(InstanceAction.START, InstanceState.RUNNING),
new TransitionOnAction(InstanceAction.DESTROY, InstanceState.FINISH)).build();
// not sure why this isn"t always automatically called from surefire.
setUpInjector();
assertEquals(factory.create(injector.getInstance(InstanceStatesHandler.class)).parse(is).entries(),
expects.entries());
}
}

View File

@ -0,0 +1,18 @@
<states>
<state name='start'>
<transition action='create' to='pending'></transition>
</state>
<state name='pending'>
<transition auto='true' to='running'></transition>
</state>
<state name='running'>
<transition action='reboot' to='running'></transition>
<transition action='stop' to='stopped'></transition>
</state>
<state name='stopped'>
<transition action='start' to='running'></transition>
<transition action='destroy' to='finish'></transition>
</state>
<state name='finish'>
</state>
</states>