mirror of https://github.com/apache/jclouds.git
Merge pull request #986 from rackspace/vol-snap-status-predicate
Added Predicates for handling Volume and Snapshot Status.
This commit is contained in:
commit
560666d412
|
@ -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);
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue