Merge pull request #986 from rackspace/vol-snap-status-predicate

Added Predicates for handling Volume and Snapshot Status.
This commit is contained in:
Adrian Cole 2012-11-13 10:09:30 -08:00
commit 560666d412
7 changed files with 250 additions and 19 deletions

View File

@ -83,9 +83,11 @@ public interface VolumeAttachmentApi {
/** /**
* Detach a Volume from a server. * Detach a Volume from a server.
* *
* @param serverId The ID of the Server * Note: Make sure you've unmounted the volume first. Failure to do so could result in failure or data loss.
*
* @param volumeId The ID of the Volume * @param volumeId The ID of the Volume
* @param serverId The ID of the Server
* @return true if successful * @return true if successful
*/ */
boolean detachVolumeFromServer(String serverId, String volumeId); boolean detachVolumeFromServer(String volumeId, String serverId);
} }

View File

@ -205,6 +205,10 @@ public class Volume {
return this; return this;
} }
} }
public static Volume forId(String volumeId) {
return builder().id(volumeId).build();
}
private final String id; private final String id;
private final Volume.Status status; private final Volume.Status status;
@ -315,7 +319,7 @@ public class Volume {
public Map<String, String> getMetadata() { public Map<String, String> getMetadata() {
return this.metadata; return this.metadata;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata); return Objects.hashCode(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata);

View File

@ -40,7 +40,7 @@ public interface SnapshotApi {
Snapshot get(String snapshotId); Snapshot get(String snapshotId);
/** /**
* Creates a new Snapshot * Creates a new Snapshot. The Volume status must be Available.
* *
* @param volumeId The Volume Id from which to create the Snapshot * @param volumeId The Volume Id from which to create the Snapshot
* @param options See CreateSnapshotOptions * @param options See CreateSnapshotOptions

View File

@ -52,7 +52,7 @@ public interface VolumeApi {
Volume create(int sizeGB, CreateVolumeOptions... options); Volume create(int sizeGB, CreateVolumeOptions... options);
/** /**
* Delete a Volume. * Delete a Volume. The Volume status must be Available or Error.
* *
* @param volumeId Id of the Volume * @param volumeId Id of the Volume
* @return true if successful, false otherwise * @return true if successful, false otherwise

View File

@ -0,0 +1,111 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.cinder.v1.predicates;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.concurrent.TimeUnit;
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.domain.Volume.Status;
import org.jclouds.openstack.cinder.v1.features.SnapshotApi;
import org.jclouds.predicates.RetryablePredicate;
import com.google.common.base.Predicate;
/**
* Tests to see if snapshot has reached status. This class is most useful when paired with a RetryablePredicate as
* in the code below. This class can be used to block execution until the Snapshot status has reached a desired state.
* This is useful when your Snapshot needs to be 100% ready before you can continue with execution.
*
* <pre>
* {@code
* Snapshot snapshot = snapshotApi.create(volumeId);
* RetryablePredicate<String> awaitAvailable = new RetryablePredicate<String>(
* SnapshotPredicates.available(snapshotApi), 600, 10, 10, TimeUnit.SECONDS);
*
* if (!awaitAvailable.apply(snapshot.getId())) {
* throw new TimeoutException("Timeout on snapshot: " + snapshot);
* }
* }
* </pre>
*
* You can also use the static convenience methods as so.
*
* <pre>
* {@code
* Snapshot snapshot = snapshotApi.create(volumeId);
*
* if (!SnapshotPredicates.awaitAvailable(snapshotApi).apply(snapshot.getId())) {
* throw new TimeoutException("Timeout on snapshot: " + snapshot);
* }
* }
* </pre>
*
* @author Everett Toews
*/
public class SnapshotPredicates {
/**
* Wait until a Snapshot is Available.
*
* @param snapshotApi The SnapshotApi in the zone where your Snapshot resides.
* @return RetryablePredicate That will check the status every 5 seconds for a maxiumum of 20 minutes.
*/
public static RetryablePredicate<Snapshot> awaitAvailable(SnapshotApi snapshotApi) {
StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(snapshotApi, Volume.Status.AVAILABLE);
return new RetryablePredicate<Snapshot>(statusPredicate, 1200, 5, 5, TimeUnit.SECONDS);
}
public static RetryablePredicate<Snapshot> awaitStatus(
SnapshotApi snapshotApi, Volume.Status status, long maxWaitInSec, long periodInSec) {
StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(snapshotApi, status);
return new RetryablePredicate<Snapshot>(statusPredicate, maxWaitInSec, periodInSec, periodInSec, TimeUnit.SECONDS);
}
private static class StatusUpdatedPredicate implements Predicate<Snapshot> {
private SnapshotApi snapshotApi;
private Status status;
public StatusUpdatedPredicate(SnapshotApi snapshotApi, Volume.Status status) {
this.snapshotApi = checkNotNull(snapshotApi, "snapshotApi must be defined");
this.status = checkNotNull(status, "status must be defined");
}
/**
* @return boolean Return true when the snapshot reaches status, false otherwise
*/
@Override
public boolean apply(Snapshot snapshot) {
checkNotNull(snapshot, "snapshotId must be defined");
if (status.equals(snapshot.getStatus())) {
return true;
}
else {
Snapshot snapshotUpdated = snapshotApi.get(snapshot.getId());
checkNotNull(snapshotUpdated, "Snapshot %s not found.", snapshot.getId());
return status.equals(snapshotUpdated.getStatus());
}
}
}
}

View File

@ -0,0 +1,123 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.cinder.v1.predicates;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.concurrent.TimeUnit;
import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.domain.Volume.Status;
import org.jclouds.openstack.cinder.v1.features.VolumeApi;
import org.jclouds.predicates.RetryablePredicate;
import com.google.common.base.Predicate;
/**
* Tests to see if volume has reached status. This class is most useful when paired with a RetryablePredicate as
* in the code below. This class can be used to block execution until the Volume status has reached a desired state.
* This is useful when your Volume needs to be 100% ready before you can continue with execution.
*
* <pre>
* {@code
* Volume volume = volumeApi.create(100);
*
* RetryablePredicate<String> awaitAvailable = new RetryablePredicate<String>(
* VolumePredicates.available(volumeApi), 600, 10, 10, TimeUnit.SECONDS);
*
* if (!awaitAvailable.apply(volume.getId())) {
* throw new TimeoutException("Timeout on volume: " + volume);
* }
* }
* </pre>
*
* You can also use the static convenience methods as so.
*
* <pre>
* {@code
* Volume volume = volumeApi.create(100);
*
* if (!VolumePredicates.awaitAvailable(volumeApi).apply(volume.getId())) {
* throw new TimeoutException("Timeout on volume: " + volume);
* }
* }
* </pre>
*
* @author Everett Toews
*/
public class VolumePredicates {
/**
* Wait until a Volume is Available.
*
* @param volumeApi The VolumeApi in the zone where your Volume resides.
* @return RetryablePredicate That will check the status every 5 seconds for a maxiumum of 10 minutes.
*/
public static RetryablePredicate<Volume> awaitAvailable(VolumeApi volumeApi) {
StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(volumeApi, Volume.Status.AVAILABLE);
return new RetryablePredicate<Volume>(statusPredicate, 600, 5, 5, TimeUnit.SECONDS);
}
/**
* Wait until a Volume is In Use.
*
* @param volumeApi The VolumeApi in the zone where your Volume resides.
* @return RetryablePredicate That will check the status every 5 seconds for a maxiumum of 10 minutes.
*/
public static RetryablePredicate<Volume> awaitInUse(VolumeApi volumeApi) {
StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(volumeApi, Volume.Status.IN_USE);
return new RetryablePredicate<Volume>(statusPredicate, 600, 5, 5, TimeUnit.SECONDS);
}
public static RetryablePredicate<Volume> awaitStatus(
VolumeApi volumeApi, Volume.Status status, long maxWaitInSec, long periodInSec) {
StatusUpdatedPredicate statusPredicate = new StatusUpdatedPredicate(volumeApi, status);
return new RetryablePredicate<Volume>(statusPredicate, maxWaitInSec, periodInSec, periodInSec, TimeUnit.SECONDS);
}
private static class StatusUpdatedPredicate implements Predicate<Volume> {
private VolumeApi volumeApi;
private Status status;
public StatusUpdatedPredicate(VolumeApi volumeApi, Volume.Status status) {
this.volumeApi = checkNotNull(volumeApi, "volumeApi must be defined");
this.status = checkNotNull(status, "status must be defined");
}
/**
* @return boolean Return true when the volume reaches status, false otherwise
*/
@Override
public boolean apply(Volume volume) {
checkNotNull(volume, "volume must be defined");
if (status.equals(volume.getStatus())) {
return true;
}
else {
Volume volumeUpdated = volumeApi.get(volume.getId());
checkNotNull(volumeUpdated, "Volume %s not found.", volume.getId());
return status.equals(volumeUpdated.getStatus());
}
}
}
}

View File

@ -29,6 +29,8 @@ import org.jclouds.openstack.cinder.v1.domain.Volume;
import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiLiveTest; import org.jclouds.openstack.cinder.v1.internal.BaseCinderApiLiveTest;
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions; import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions; import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
import org.jclouds.openstack.cinder.v1.predicates.SnapshotPredicates;
import org.jclouds.openstack.cinder.v1.predicates.VolumePredicates;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
@ -97,12 +99,7 @@ public class VolumeAndSnapshotApiLiveTest extends BaseCinderApiLiveTest {
.availabilityZone(zone); .availabilityZone(zone);
testVolume = volumeApi.create(100, options); testVolume = volumeApi.create(100, options);
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() { assertTrue(VolumePredicates.awaitAvailable(volumeApi).apply(testVolume));
@Override
public boolean apply(VolumeApi volumeApi) {
return volumeApi.get(testVolume.getId()).getStatus() == Volume.Status.AVAILABLE;
}
}, 600 * 1000L).apply(volumeApi));
} }
@Test(dependsOnMethods = "testCreateVolume") @Test(dependsOnMethods = "testCreateVolume")
@ -152,17 +149,11 @@ public class VolumeAndSnapshotApiLiveTest extends BaseCinderApiLiveTest {
"jclouds live test snapshot").force()); "jclouds live test snapshot").force());
assertNotNull(testSnapshot); assertNotNull(testSnapshot);
assertNotNull(testSnapshot.getId()); assertNotNull(testSnapshot.getId());
final String snapshotId = testSnapshot.getId();
assertNotNull(testSnapshot.getStatus()); assertNotNull(testSnapshot.getStatus());
assertTrue(testSnapshot.getSize() > -1); assertTrue(testSnapshot.getSize() > -1);
assertNotNull(testSnapshot.getCreated()); assertNotNull(testSnapshot.getCreated());
assertTrue(new RetryablePredicate<VolumeApi>(new Predicate<VolumeApi>() { assertTrue(SnapshotPredicates.awaitAvailable(snapshotApi).apply(testSnapshot));
@Override
public boolean apply(VolumeApi volumeApi) {
return snapshotApi.get(snapshotId).getStatus() == Volume.Status.AVAILABLE;
}
}, 1200 * 1000L).apply(volumeApi));
} }
@Test(dependsOnMethods = "testCreateSnapshot") @Test(dependsOnMethods = "testCreateSnapshot")