/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.compute.common.internal.blockstorage;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityTransaction;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.PersistenceContext;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.entities.Transactions;
import com.eucalyptus.records.Logs;
import com.eucalyptus.util.Callback;
import com.google.common.collect.Sets;
import org.apache.log4j.Logger;
import com.eucalyptus.compute.common.CloudMetadata.SnapshotMetadata;
import com.eucalyptus.component.ComponentIds;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.UserMetadata;
import com.eucalyptus.upgrade.Upgrades.EntityUpgrade;
import com.eucalyptus.upgrade.Upgrades.Version;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.auth.principal.FullName;
import com.eucalyptus.auth.principal.OwnerFullName;
import com.google.common.base.Predicate;
@Entity
@PersistenceContext( name = "eucalyptus_cloud" )
@Table( name = "metadata_snapshots", indexes = {
@Index( name = "metadata_snapshots_user_id_idx", columnList = "metadata_user_id" ),
@Index( name = "metadata_snapshots_account_id_idx", columnList = "metadata_account_id" ),
@Index( name = "metadata_snapshots_display_name_idx", columnList = "metadata_display_name" ),
} )
public class Snapshot extends UserMetadata<State> implements SnapshotMetadata {
@Transient
private static Logger LOG = Logger.getLogger( Snapshot.class );
public static final String ID_PREFIX = "snap";
@Column( name = "metadata_snapshot_vol_size" )
private Integer volumeSize;
@Column( name = "metadata_snapshot_parentvolume", updatable = false )
private String parentVolume;
/**
* @deprecated srsly. dont use it.
*/
@Deprecated
@Column( name = "metadata_snapshot_vol_sc", updatable = false )
private String volumeSc;
@Column( name = "metadata_snapshot_vol_partition", updatable = false )
private String volumePartition;
@Column( name = "metadata_snapshot_progress" )
private String progress;
@Column( name = "metadata_snapshot_description", updatable = false )
private String description;
@OneToMany( fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy = "snapshot" )
private Collection<SnapshotTag> tags;
@ElementCollection
@CollectionTable( name = "metadata_snapshot_permissions" )
private Set<String> permissions = new HashSet<String>( );
@ElementCollection
@CollectionTable( name = "metadata_snapshot_pcodes" )
private Set<String> productCodes = new HashSet<String>( );
@Column( name = "metadata_snapshot_is_public", columnDefinition = "boolean default false" )
private Boolean snapshotPublic;
protected Snapshot( ) {
super( );
}
Snapshot( final OwnerFullName ownerFullName, final String displayName ) {
super( ownerFullName, displayName );
}
public Snapshot(
final OwnerFullName ownerFullName,
final String displayName,
final String description,
final String parentVolume,
final Integer volumeSize,
final String volumeScName,
final String volumePartition
) {
this( ownerFullName, displayName );
this.description = description;
this.parentVolume = parentVolume;
this.volumeSc = volumeScName;
this.volumePartition = volumePartition;
this.volumeSize = volumeSize;
this.progress = "0%";
this.snapshotPublic = false;
super.setState( State.NIHIL );
}
public static Snapshot named( @Nullable final OwnerFullName ownerFullName,
@Nullable final String snapshotId ) {
return new Snapshot( ownerFullName, snapshotId );
}
public static Snapshot naturalId( final String naturalId ) {
final Snapshot snapshot = new Snapshot();
snapshot.setNaturalId( naturalId );
return snapshot;
}
public String mapState( ) {
switch ( this.getState( ) ) {
case GENERATING:
case NIHIL:
return "pending";
case EXTANT:
return "completed";
default:
return "failed";
}
}
public com.eucalyptus.compute.common.Snapshot morph( final com.eucalyptus.compute.common.Snapshot snap ) {
snap.setSnapshotId( this.getDisplayName( ) );
snap.setDescription( this.getDescription() );
snap.setStatus( this.mapState( ) );
snap.setStartTime( this.getCreationTimestamp( ) );
snap.setVolumeId( this.getParentVolume( ) );
snap.setVolumeSize( Integer.toString( this.getVolumeSize( ) ) );
if ( this.getProgress( ) != null ) {
snap.setProgress( this.getProgress( ) );
} else {
snap.setProgress( State.EXTANT.equals( this.getState( ) ) ? "100%" : "0%" );//GRZE: sigh @ this crap.
}
return snap;
}
public Boolean getSnapshotPublic( ) {
return this.snapshotPublic;
}
public void setSnapshotPublic( final Boolean aPublic ) {
this.snapshotPublic = aPublic;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getParentVolume( ) {
return parentVolume;
}
protected void setParentVolume( final String parentVolume ) {
this.parentVolume = parentVolume;
}
@Override
public String getPartition( ) {
return this.volumePartition;
}
@Override
public FullName getFullName( ) {
return FullName.create.vendor( "euca" )
.region( ComponentIds.lookup( Eucalyptus.class ).name( ) )
.namespace( this.getOwnerAccountNumber( ) )
.relativeId( "snapshot", this.getDisplayName( ) );
}
public Integer getVolumeSize( ) {
return this.volumeSize;
}
public void setVolumeSize( Integer integer ) {
this.volumeSize = integer;
}
public void setPartition( String partition ) {
this.volumePartition = partition;
}
public String getVolumeCluster( ) {
return this.volumeSc;
}
public void setVolumeCluster( String volumeCluster ) {
this.volumeSc = volumeCluster;
}
public String getVolumePartition( ) {
return this.volumePartition;
}
public void setVolumePartition( String volumePartition ) {
this.volumePartition = volumePartition;
}
public String getVolumeSc( ) {
return this.volumeSc;
}
public String getProgress( ) {
return this.progress;
}
public void setProgress( String progress ) {
this.progress = progress;
}
static Snapshot self( final Snapshot snap ) {
return new Snapshot(snap.getOwner(), snap.getDisplayName( ) );
}
public boolean addProductCode( final String prodCode ) {
EntityTransaction db = Entities.get( Snapshot.class );
try {
Snapshot entity = Entities.merge( this );
entity.getProductCodes( ).add( prodCode );
db.commit( );
return true;
} catch ( Exception ex ) {
Logs.exhaust( ).error( ex, ex );
db.rollback( );
return false;
}
}
public Snapshot resetPermission( ) {
try {
Transactions.one(new Snapshot(this.getOwner(), this.displayName), new Callback<Snapshot>() {
@Override
public void fire(final Snapshot t) {
t.getPermissions().clear();
t.getPermissions().add(t.getOwnerAccountNumber());
}
});
} catch ( final ExecutionException e ) {
LOG.debug( e, e );
}
return this;
}
public Snapshot resetProductCodes( ) {
try {
Transactions.one( Snapshot.self( this ), new Callback<Snapshot>( ) {
@Override
public void fire( final Snapshot t ) {
t.getProductCodes( ).clear( );
}
} );
} catch ( final ExecutionException e ) {
LOG.debug( e, e );
}
return this;
}
public Set<String> getProductCodes( ) {
return this.productCodes;
}
public Set<String> getPermissions() {
return this.permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
/**
* @param accountIds
* @return true if the accountId has an explicit launch permission.
*/
public boolean hasPermission( final String... accountIds ) {
try (TransactionResource db = Entities.transactionFor(Snapshot.class)) {
Snapshot entity = Entities.merge(this);
return !Sets.intersection(entity.getPermissions(), Sets.newHashSet(accountIds)).isEmpty();
}
}
/**
* Add createvolume permissions.
*
* @param accountIds
*/
public void addPermissions( final List<String> accountIds ) {
getPermissions( ).addAll( accountIds );
}
/**
* Remove createvolume permissions.
*
* @param accountIds
*/
public void removePermissions( final List<String> accountIds ) {
getPermissions( ).removeAll( accountIds );
}
@EntityUpgrade( entities = { Snapshot.class }, since = Version.v3_2_0, value = Compute.class )
public enum SnapshotUpgrade implements Predicate<Class> {
INSTANCE;
private static Logger LOG = Logger.getLogger( Snapshot.SnapshotUpgrade.class );
@Override
public boolean apply( Class arg0 ) {
EntityTransaction db = Entities.get( Snapshot.class );
try {
List<Snapshot> entities = Entities.query( new Snapshot( ) );
for ( Snapshot entry : entities ) {
LOG.debug( "Upgrading: " + entry.getDisplayName() );
entry.setDescription(null);
}
db.commit( );
return true;
} catch ( Exception ex ) {
db.rollback();
throw Exceptions.toUndeclared( ex );
}
}
}
}