org.dasein.cloud.aws.compute.EBSSnapshot Maven / Gradle / Ivy
/**
* Copyright (C) 2009-2015 Dell, Inc.
* See annotations for authorship information
*
* ====================================================================
* 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.dasein.cloud.aws.compute;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.log4j.Logger;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.Requirement;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.Tag;
import org.dasein.cloud.aws.AWSCloud;
import org.dasein.cloud.compute.*;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.util.APITrace;
import org.dasein.util.Jiterator;
import org.dasein.util.JiteratorPopulator;
import org.dasein.util.PopulatorThread;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class EBSSnapshot extends AbstractSnapshotSupport {
static private final Logger logger = AWSCloud.getLogger(EBSSnapshot.class);
private EBSSnapshotCapabilities capabilities;
EBSSnapshot(@Nonnull AWSCloud provider) {
super(provider);
}
@Override
public void addSnapshotShare(@Nonnull String providerSnapshotId, @Nonnull String accountNumber) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.addSnapshotShare");
try {
setPrivateShare(providerSnapshotId, true, accountNumber);
}
finally {
APITrace.end();
}
}
@Override
public void addPublicShare(@Nonnull String providerSnapshotId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.addPublicShare");
try {
setPublicShare(providerSnapshotId, true);
}
finally {
APITrace.end();
}
}
@Override
public @Nonnull String createSnapshot(@Nonnull SnapshotCreateOptions options) throws InternalException, CloudException {
APITrace.begin(getProvider(), "Snapshot.createSnapshot");
try {
String volumeId = options.getVolumeId();
Map parameters;
EC2Method method;
NodeList blocks;
Document doc;
if( volumeId != null ) {
parameters = getProvider().getStandardParameters(getContext(), EC2Method.CREATE_SNAPSHOT);
parameters.put("VolumeId", volumeId);
}
else {
parameters = getProvider().getStandardParameters(getContext(), EC2Method.COPY_SNAPSHOT);
parameters.put("SourceSnapshotId", options.getSnapshotId());
parameters.put("SourceRegion", options.getRegionId());
}
parameters.put("Description", options.getDescription());
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("snapshotId");
if( blocks.getLength() > 0 ) {
String id = blocks.item(0).getFirstChild().getNodeValue().trim();
if( id == null ) {
throw new CloudException("No error occurred, but no snapshot was provided");
}
Map meta = options.getMetaData();
meta.put("Name", options.getName());
meta.put("Description", options.getDescription());
ArrayList tags = new ArrayList();
for( Map.Entry entry : meta.entrySet() ) {
String value = entry.getValue();
if( value != null ) {
tags.add(new Tag(entry.getKey(), value));
}
}
getProvider().createTags(EC2Method.SERVICE_ID, id, tags.toArray(new Tag[tags.size()]));
return id;
}
throw new CloudException("No error occurred, but no snapshot was provided");
}
finally {
APITrace.end();
}
}
@Nonnull
@Override
public SnapshotCapabilities getCapabilities() {
if( capabilities == null ) {
capabilities = new EBSSnapshotCapabilities(getProvider());
}
return capabilities;
}
@Override
public @Nonnull String getProviderTermForSnapshot(@Nonnull Locale locale) {
return getCapabilities().getProviderTermForSnapshot(locale);
}
@Override
public @Nullable Snapshot getSnapshot(@Nonnull String snapshotId) throws InternalException, CloudException {
APITrace.begin(getProvider(), "Snapshot.getSnapshot");
try {
ProviderContext ctx = getContext();
if( ctx == null ) {
throw new CloudException("No context exists for this request.");
}
if( getProvider().getEC2Provider().isAWS() ) {
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOTS);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId.1", snapshotId);
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && (code.startsWith("InvalidSnapshot.NotFound") || code.equals("InvalidParameterValue")) ) {
return null;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("snapshotSet");
for( int i=0; i parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOT_ATTRIBUTE);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId.1", snapshotId);
parameters.put("Attribute", "createVolumePermission");
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && code.startsWith("InvalidSnapshot.NotFound") ) {
return false;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("createVolumePermission");
for( int i=0; i listShares(@Nonnull String forSnapshotId) throws InternalException, CloudException {
APITrace.begin(getProvider(), "Snapshot.listShares");
try {
if( !getProvider().getEC2Provider().isAWS() ) {
return new ArrayList();
}
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOT_ATTRIBUTE);
ArrayList list = new ArrayList();
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId.1", forSnapshotId);
parameters.put("Attribute", "createVolumePermission");
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && (code.startsWith("InvalidSnapshotID") || code.equals("InvalidSnapshot.NotFound")) ) {
return list;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("createVolumePermission");
for( int i=0; i 0 ) {
list.add(userId);
}
}
}
}
}
}
}
return list;
}
finally {
APITrace.end();
}
}
@Override
public @Nonnull Iterable listSnapshotStatus() throws InternalException, CloudException {
getProvider().hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
try {
APITrace.begin(getProvider(), "Snapshot.listSnapshotStatus");
try {
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOTS);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("Owner.1", "self");
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("snapshotSet");
for( int i=0; i listSnapshots() throws InternalException, CloudException {
return listSnapshots( null );
}
@Override
public @Nonnull Iterable listSnapshots(final @Nullable SnapshotFilterOptions options) throws InternalException, CloudException {
getProvider().hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
try {
APITrace.begin(getProvider(), "Snapshot.listSnapshots");
try {
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOTS);
EC2Method method;
NodeList blocks;
Document doc;
// we want to use the more efficient tag search via AWS if possible
// it is only possible if a) tags is the only search criterion or b) the options is set ot match all criteria
if ( options != null && options.hasCriteria() && !options.isMatchesAny() ) {
Map tags = options.getTags();
if( tags != null && !tags.isEmpty() ) {
AWSCloud.addExtraParameters( parameters, getProvider().getTagFilterParams( options.getTags() ) );
}
}
if( options == null || options.getAccountNumber() == null || getContext().getAccountNumber().equals(options.getAccountNumber()) ) {
parameters.put("Owner.1", "self");
}
else {
parameters.put("Owner.1", options.getAccountNumber());
}
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("snapshotSet");
for( int i=0; i parameters = getProvider().getStandardParameters(getContext(), EC2Method.DELETE_SNAPSHOT);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId", snapshotId);
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null ) {
if( code.equals("InvalidSnapshot.NotFound") ) {
return;
}
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("return");
if( blocks.getLength() > 0 ) {
if( !blocks.item(0).getFirstChild().getNodeValue().equalsIgnoreCase("true") ) {
throw new CloudException("Deletion of snapshot denied.");
}
}
}
finally {
APITrace.end();
}
}
@Override
public void removeAllSnapshotShares(@Nonnull String providerSnapshotId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.removeAllSnapshotShares");
try {
List shares = (List)listShares(providerSnapshotId);
if( shares.isEmpty() ) {
return;
}
setPrivateShare(providerSnapshotId, false, shares.toArray(new String[shares.size()]));
setPublicShare(providerSnapshotId, false);
}
finally {
APITrace.end();
}
}
@Override
public void removeSnapshotShare(@Nonnull String providerSnapshotId, @Nonnull String accountNumber) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.removeSnapshotShare");
try {
setPrivateShare(providerSnapshotId, false, accountNumber);
}
finally {
APITrace.end();
}
}
@Override
public void removePublicShare(@Nonnull String providerSnapshotId) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.removePublicShare");
try {
setPublicShare(providerSnapshotId, false);
}
finally {
APITrace.end();
}
}
@Override
public void removeTags(@Nonnull String snapshotId, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.removeTags");
try {
((AWSCloud)getProvider()).removeTags(EC2Method.SERVICE_ID, snapshotId, tags);
}
finally {
APITrace.end();
}
}
@Override
public void removeTags(@Nonnull String[] snapshotIds, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Snapshot.removeTags");
try {
((AWSCloud)getProvider()).removeTags(EC2Method.SERVICE_ID, snapshotIds, tags);
}
finally {
APITrace.end();
}
}
@Override
public @Nonnull Iterable searchSnapshots(final @Nonnull SnapshotFilterOptions opts) throws InternalException, CloudException {
getProvider().hold();
PopulatorThread populator = new PopulatorThread(new JiteratorPopulator() {
@Override
public void populate(@Nonnull Jiterator iterator) throws Exception {
try {
APITrace.begin(getProvider(), "Snapshot.searchSnapshots");
try {
SnapshotFilterOptions options = opts;
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.DESCRIBE_SNAPSHOTS);
EC2Method method;
NodeList blocks;
Document doc;
// we want to use the more efficient tag search via AWS if possible
// it is only possible if a) tags is the only search criterion or b) the options is set ot match all criteria
if ( options != null && options.hasCriteria() && (!options.isMatchesAny() || (options.getRegex() == null && options.getAccountNumber() == null)) ) {
Map tags = options.getTags();
if( tags != null && !tags.isEmpty() ) {
AWSCloud.addExtraParameters( parameters, getProvider().getTagFilterParams( options.getTags() ) );
SnapshotFilterOptions sfo = SnapshotFilterOptions.getInstance();
if( options.getAccountNumber() != null ) {
sfo.withAccountNumber(options.getAccountNumber());
}
if( options.getRegex() != null ) {
sfo.matchingRegex(options.getRegex());
}
options = sfo;
}
}
if( options != null && options.getAccountNumber() != null && !options.isMatchesAny() ) {
parameters.put("Owner.1", options.getAccountNumber());
}
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("snapshotSet");
for( int i=0; i parameters = getProvider().getStandardParameters(getContext(), EC2Method.MODIFY_SNAPSHOT_ATTRIBUTE);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId", snapshotId);
parameters.put("UserGroup.1", "all");
parameters.put("Attribute", "createVolumePermission");
parameters.put("OperationType", affirmative ? "add" : "remove");
method = new EC2Method(getProvider(), parameters);
try {
doc = method.invoke();
}
catch( EC2Exception e ) {
String code = e.getCode();
if( code != null && code.startsWith("InvalidSnapshot.NotFound") ) {
return;
}
logger.error(e.getSummary());
throw new CloudException(e);
}
blocks = doc.getElementsByTagName("return");
if( blocks.getLength() > 0 ) {
if( !blocks.item(0).getFirstChild().getNodeValue().equalsIgnoreCase("true") ) {
throw new CloudException("Deletion of snapshot denied.");
}
}
}
private void setPrivateShare(@Nonnull String snapshotId, boolean affirmative, @Nonnull String ... accountIds) throws InternalException, CloudException {
Map parameters = getProvider().getStandardParameters(getContext(), EC2Method.MODIFY_SNAPSHOT_ATTRIBUTE);
EC2Method method;
NodeList blocks;
Document doc;
parameters.put("SnapshotId", snapshotId);
for(int i=0; i 0 ) {
if( !blocks.item(0).getFirstChild().getNodeValue().equalsIgnoreCase("true") ) {
throw new CloudException("Deletion of snapshot denied.");
}
}
}
private @Nullable Snapshot toSnapshot(@Nullable Node node) throws CloudException, InternalException {
if( node == null ) {
return null;
}
NodeList attrs = node.getChildNodes();
Snapshot snapshot = new Snapshot();
if( !getProvider().getEC2Provider().isAWS() ) {
snapshot.setOwner(getContext().getAccountNumber());
}
for( int i=0; i 0 ) {
String vol = children.item(0).getNodeValue();
if( vol != null ) {
vol = vol.trim();
if( vol.length() > 0 ) {
snapshot.setVolumeId(vol);
}
}
}
}
else if( name.equals("status") ) {
String s = attr.getFirstChild().getNodeValue().trim();
SnapshotState state;
if( s.equals("completed") ) {
state = SnapshotState.AVAILABLE;
}
else if( s.equals("deleting") || s.equals("deleted") ) {
state = SnapshotState.DELETED;
}
else {
state = SnapshotState.PENDING;
}
snapshot.setCurrentState(state);
}
else if( name.equals("startTime") ) {
NodeList children = attr.getChildNodes();
long ts = 0L;
if( children != null && children.getLength() > 0 ) {
String t = children.item(0).getNodeValue();
if( t != null ) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
t = t.trim();
if( t.length() > 0 ) {
try {
ts = fmt.parse(t).getTime();
}
catch( ParseException e ) {
logger.error(e);
e.printStackTrace();
throw new CloudException(e);
}
}
}
}
snapshot.setSnapshotTimestamp(ts);
}
else if( name.equals("progress") ) {
NodeList children = attr.getChildNodes();
String progress = "100%";
if( children != null && children.getLength() > 0 ) {
String p = children.item(0).getNodeValue();
if( p != null ) {
p = p.trim();
if( p.length() > 0 ) {
progress = p;
}
}
}
snapshot.setProgress(progress);
}
else if( name.equals("ownerId") ) {
snapshot.setOwner(attr.getFirstChild().getNodeValue().trim());
}
else if( name.equals("volumeSize") ) {
String val = attr.getFirstChild().getNodeValue().trim();
if( val == null || val.equals("n/a") ) {
snapshot.setSizeInGb(0);
}
else {
snapshot.setSizeInGb(Integer.parseInt(val));
}
}
else if( name.equals("description") ) {
NodeList children = attr.getChildNodes();
String description = null;
if( children != null && children.getLength() > 0 ) {
String d = children.item(0).getNodeValue();
if( d != null ) {
description = d.trim();
}
}
snapshot.setDescription(description);
}
else if( name.equals("tagSet") ) {
getProvider().setTags(attr, snapshot);
}
}
String name = snapshot.getName();
if( name == null ) {
name = snapshot.getTags().get("Name");
if( name == null ) {
name = snapshot.getProviderSnapshotId();
}
snapshot.setName(name);
}
String description = snapshot.getDescription();
if( description == null ) {
description = snapshot.getTags().get("Description");
if( description == null ) {
description = (name + " [" + snapshot.getSizeInGb() + " GB]");
}
snapshot.setDescription(description);
}
snapshot.setRegionId(getContext().getRegionId());
if( snapshot.getSizeInGb() < 1 ) {
EC2ComputeServices svc = getProvider().getComputeServices();
if( svc != null ) {
EBSVolume vs = svc.getVolumeSupport();
try {
Volume volume = vs.getVolume(snapshot.getProviderSnapshotId());
if( volume != null ) {
snapshot.setSizeInGb(volume.getSizeInGigabytes());
}
}
catch( InternalException ignore ) {
// ignore
}
}
}
return snapshot;
}
private @Nullable ResourceStatus toStatus(@Nullable Node node) throws CloudException {
if( node == null ) {
return null;
}
NodeList attrs = node.getChildNodes();
SnapshotState state = SnapshotState.PENDING;
String snapshotId = null;
for( int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy