/*************************************************************************
* Copyright 2009-2012 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.blockstorage.entities;
import static com.eucalyptus.upgrade.Upgrades.Version.v4_1_0;
import static com.eucalyptus.upgrade.Upgrades.Version.v4_2_0;
import static com.eucalyptus.upgrade.Upgrades.Version.v4_4_0;
import groovy.sql.GroovyRowResult;
import groovy.sql.Sql;
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PersistenceContext;
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.log4j.Logger;
import com.eucalyptus.blockstorage.Storage;
import com.eucalyptus.blockstorage.util.StorageProperties;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.configurable.ConfigurableFieldType;
import com.eucalyptus.configurable.ConfigurableIdentifier;
import com.eucalyptus.configurable.ConfigurableInit;
import com.eucalyptus.configurable.ConfigurableProperty;
import com.eucalyptus.configurable.ConfigurablePropertyException;
import com.eucalyptus.configurable.PropertyChangeListener;
import com.eucalyptus.entities.AbstractPersistent;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.entities.Transactions;
import com.eucalyptus.upgrade.Upgrades.DatabaseFilters;
import com.eucalyptus.upgrade.Upgrades.EntityUpgrade;
import com.eucalyptus.upgrade.Upgrades.PreUpgrade;
import com.eucalyptus.util.Exceptions;
import com.google.common.base.Predicate;
@Entity
@PersistenceContext(name = "eucalyptus_storage")
@Table(name = "storage_info")
@ConfigurableClass(root = "storage", alias = "basic", description = "Basic storage controller configuration.", singleton = false, deferred = true)
public class StorageInfo extends AbstractPersistent {
private static final String DEFAULT_SHOULD_TRANSFER_SNAPSHOTS_TXT = "true";
private static final Boolean DEFAULT_SHOULD_TRANSFER_SNAPSHOTS = Boolean.valueOf( DEFAULT_SHOULD_TRANSFER_SNAPSHOTS_TXT );
private static final Integer DEFAULT_SNAPSHOT_PART_SIZE_IN_MB = 100;
private static final Integer DEFAULT_MAX_SNAPSHOT_PARTS_QUEUE_SIZE = 5;
private static final Integer DEFAULT_MAX_SNAPSHOT_CONCURRENT_TRANSFERS = 3;
private static final Integer DEFAULT_SNAPSHOT_TRANSFER_TIMEOUT = 48;
private static final Integer DEFAULT_READ_BUFFER_SIZE_IN_MB = 1;
private static final Integer DEFAULT_WRITE_BUFFER_SIZE_IN_MB = 100;
private static final Integer DEFAULT_DELETED_VOL_EXPIRATION_TIME = 1440;// minutes
private static final Integer DEFAULT_DELETED_SNAP_EXPIRATION_TIME = 60;// minutes
private static final Integer MIN_RESOURCE_EXPIRATION_TIME = 10;// minutes
private static final String DEFAULT_MAX_CONCURRENT_VOLUMES = "10";
private static final String DEFAULT_MAX_CONCURRENT_SNAPSHOTS = "3";
private static final String DEFAULT_MAX_SNAP_DELTAS = "0";
@Transient
private static Logger LOG = Logger.getLogger(StorageInfo.class);
@ConfigurableIdentifier
@Column(name = "storage_name", unique = true)
private String name;
@ConfigurableField(description = "Total disk space reserved for volumes",
displayName = "Disk space reserved for volumes",
initialInt = StorageProperties.MAX_TOTAL_VOLUME_SIZE )
@Column(name = "system_storage_volume_size_gb")
private Integer maxTotalVolumeSizeInGb;
@ConfigurableField(description = "Max volume size", displayName = "Max volume size",
initialInt = StorageProperties.MAX_VOLUME_SIZE )
@Column(name = "system_storage_max_volume_size_gb")
private Integer maxVolumeSizeInGB;
@ConfigurableField(description = "Should transfer snapshots", displayName = "Transfer snapshots to ObjectStorage",
type = ConfigurableFieldType.BOOLEAN, initial = DEFAULT_SHOULD_TRANSFER_SNAPSHOTS_TXT )
@Column(name = "system_storage_transfer_snapshots")
private Boolean shouldTransferSnapshots;
@ConfigurableField(
description = "Time interval in minutes after which Storage Controller metadata for volumes that have been physically removed from the block storage backend will be deleted",
displayName = "Volume Metadata Expiration Time In Minutes", initial = "1440", changeListener = MinimumExpirationTimeChangeListener.class)
@Column(name = "vol_expiration")
private Integer volExpiration;
@ConfigurableField(
description = "Time interval in minutes after which Storage Controller metadata for snapshots that have been physically removed from the block storage backend will be deleted",
displayName = "Snapshot Metadata Expiration Time in Minutes", initial = "60", changeListener = MinimumExpirationTimeChangeListener.class)
@Column(name = "snap_expiration")
private Integer snapExpiration;
@ConfigurableField(description = "Snapshot part size in MB for snapshot transfers using multipart upload. Minimum part size is 5MB",
displayName = "Snapshot Part Size", initial = "100", changeListener = MinimumPartSizeChangeListener.class)
@Column(name = "snapshot_part_size_mb")
private Integer snapshotPartSizeInMB;
@ConfigurableField(description = "Maximum number of snapshot parts per snapshot that can be spooled on the disk",
displayName = "Maximum Queue Size", initial = "5", changeListener = PositiveIntegerChangeListener.class)
@Column(name = "max_snapshot_parts_queue_size")
private Integer maxSnapshotPartsQueueSize;
@ConfigurableField(description = "Maximum number of snapshots that can be uploaded to or downloaded from objectstorage gateway at a given time",
displayName = "Maximum Concurrent Snapshot Transfers", initial = "3", changeListener = PositiveIntegerChangeListener.class)
@Column(name = "max_concurrent_snapshot_transfers")
private Integer maxConcurrentSnapshotTransfers;
@ConfigurableField(description = "Snapshot upload wait time in hours after which the upload will be cancelled",
displayName = "Snapshot Upload Timeout", initial = "48", changeListener = PositiveIntegerChangeListener.class)
@Column(name = "snapshot_transfer_timeout_hours")
private Integer snapshotTransferTimeoutInHours;
@ConfigurableField(description = "Buffer size in MB for reading data from snapshot when uploading snapshot to objectstorage gateway",
displayName = "Read Buffer Size", initial = "1", changeListener = PositiveIntegerChangeListener.class)
@Column(name = "read_buffer_size_mb")
private Integer readBufferSizeInMB;
@ConfigurableField(description = "Buffer size in MB for writing data to snapshot when downloading snapshot from objectstorage gateway",
displayName = "Write Buffer Size", initial = "100", changeListener = PositiveIntegerChangeListener.class)
@Column(name = "write_buffer_size_mb")
private Integer writeBufferSizeInMB;
@ConfigurableField(description = "Maximum number of volumes processed on the block storage backend at a given time",
displayName = "Maximum Concurrent Volumes", initial = DEFAULT_MAX_CONCURRENT_VOLUMES, changeListener = PositiveIntegerChangeListener.class)
@Column(name = "max_concurrent_volumes_processed")
private Integer maxConcurrentVolumes;
@ConfigurableField(description = "Maximum number of snapshots processed on the block storage backend at a given time",
displayName = "Maximum Concurrent Snapshots", initial = DEFAULT_MAX_CONCURRENT_SNAPSHOTS, changeListener = PositiveIntegerChangeListener.class)
@Column(name = "max_concurrent_snapshots")
private Integer maxConcurrentSnapshots;
@ConfigurableField(
description = "Maximum number of snapshots deltas allowed before triggering a complete upload of the snapshot content for a given volume",
displayName = "Maximum Snapshot Deltas", initial = DEFAULT_MAX_SNAP_DELTAS, changeListener = NonNegativeIntegerChangeListener.class)
@Column(name = "max_snapshot_deltas")
private Integer maxSnapshotDeltas;
public StorageInfo() {
this.name = StorageProperties.NAME;
}
public StorageInfo(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMaxTotalVolumeSizeInGb() {
return maxTotalVolumeSizeInGb;
}
public void setMaxTotalVolumeSizeInGb(Integer maxTotalVolumeSizeInGb) {
this.maxTotalVolumeSizeInGb = maxTotalVolumeSizeInGb;
}
public Integer getMaxVolumeSizeInGB() {
return maxVolumeSizeInGB;
}
public void setMaxVolumeSizeInGB(Integer maxVolumeSizeInGB) {
this.maxVolumeSizeInGB = maxVolumeSizeInGB;
}
public Boolean getShouldTransferSnapshots() {
return shouldTransferSnapshots;
}
public void setShouldTransferSnapshots(Boolean shouldTransferSnapshots) {
this.shouldTransferSnapshots = shouldTransferSnapshots;
}
public Integer getVolExpiration() {
return volExpiration;
}
public void setVolExpiration(Integer volExpiration) {
this.volExpiration = volExpiration;
}
public Integer getSnapExpiration() {
return snapExpiration;
}
public void setSnapExpiration(Integer snapExpiration) {
this.snapExpiration = snapExpiration;
}
public Integer getSnapshotPartSizeInMB() {
return snapshotPartSizeInMB;
}
public void setSnapshotPartSizeInMB(Integer snapshotPartSizeInMB) {
this.snapshotPartSizeInMB = snapshotPartSizeInMB;
}
public Integer getMaxSnapshotPartsQueueSize() {
return maxSnapshotPartsQueueSize;
}
public void setMaxSnapshotPartsQueueSize(Integer maxSnapshotPartsQueueSize) {
this.maxSnapshotPartsQueueSize = maxSnapshotPartsQueueSize;
}
public Integer getMaxConcurrentSnapshotTransfers() {
return maxConcurrentSnapshotTransfers;
}
public void setMaxConcurrentSnapshotTransfers(Integer maxConcurrentSnapshotTransfers) {
this.maxConcurrentSnapshotTransfers = maxConcurrentSnapshotTransfers;
}
public Integer getSnapshotTransferTimeoutInHours() {
return snapshotTransferTimeoutInHours;
}
public void setSnapshotTransferTimeoutInHours(Integer snapshotTransferTimeoutInHours) {
this.snapshotTransferTimeoutInHours = snapshotTransferTimeoutInHours;
}
public Integer getReadBufferSizeInMB() {
return readBufferSizeInMB;
}
public void setReadBufferSizeInMB(Integer readBufferSizeInMB) {
this.readBufferSizeInMB = readBufferSizeInMB;
}
public Integer getWriteBufferSizeInMB() {
return writeBufferSizeInMB;
}
public void setWriteBufferSizeInMB(Integer writeBufferSizeInMB) {
this.writeBufferSizeInMB = writeBufferSizeInMB;
}
public Integer getMaxConcurrentVolumes() {
return maxConcurrentVolumes;
}
public void setMaxConcurrentVolumes(Integer maxConcurrentVolumesProcessed) {
this.maxConcurrentVolumes = maxConcurrentVolumesProcessed;
}
public Integer getMaxConcurrentSnapshots() {
return maxConcurrentSnapshots;
}
public void setMaxConcurrentSnapshots(Integer maxConcurrentSnapshotsProcessed) {
this.maxConcurrentSnapshots = maxConcurrentSnapshotsProcessed;
}
public Integer getMaxSnapshotDeltas() {
return maxSnapshotDeltas;
}
public void setMaxSnapshotDeltas(Integer maxSnapshotDeltas) {
this.maxSnapshotDeltas = maxSnapshotDeltas;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StorageInfo other = (StorageInfo) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public String toString() {
return this.name;
}
@PreUpdate
@PostLoad
public void setDefaults() {
if (maxTotalVolumeSizeInGb == null) {
maxTotalVolumeSizeInGb = StorageProperties.MAX_TOTAL_VOLUME_SIZE;
}
if (maxVolumeSizeInGB == null) {
maxVolumeSizeInGB = StorageProperties.MAX_VOLUME_SIZE;
}
if (shouldTransferSnapshots == null) {
shouldTransferSnapshots = DEFAULT_SHOULD_TRANSFER_SNAPSHOTS;
}
if (volExpiration == null) {
volExpiration = DEFAULT_DELETED_VOL_EXPIRATION_TIME;
}
if (snapExpiration == null) {
snapExpiration = DEFAULT_DELETED_SNAP_EXPIRATION_TIME;
}
if (snapshotPartSizeInMB == null) {
snapshotPartSizeInMB = DEFAULT_SNAPSHOT_PART_SIZE_IN_MB;
}
if (maxSnapshotPartsQueueSize == null) {
maxSnapshotPartsQueueSize = DEFAULT_MAX_SNAPSHOT_PARTS_QUEUE_SIZE;
}
if (maxConcurrentSnapshotTransfers == null) {
maxConcurrentSnapshotTransfers = DEFAULT_MAX_SNAPSHOT_CONCURRENT_TRANSFERS;
}
if (snapshotTransferTimeoutInHours == null) {
snapshotTransferTimeoutInHours = DEFAULT_SNAPSHOT_TRANSFER_TIMEOUT;
}
if (readBufferSizeInMB == null) {
readBufferSizeInMB = DEFAULT_READ_BUFFER_SIZE_IN_MB;
}
if (writeBufferSizeInMB == null) {
writeBufferSizeInMB = DEFAULT_WRITE_BUFFER_SIZE_IN_MB;
}
if (maxConcurrentVolumes == null) {
maxConcurrentVolumes = Integer.valueOf(DEFAULT_MAX_CONCURRENT_VOLUMES);
}
if (maxConcurrentSnapshots == null) {
maxConcurrentSnapshots = Integer.valueOf(DEFAULT_MAX_CONCURRENT_SNAPSHOTS);
}
if (maxSnapshotDeltas == null) {
maxSnapshotDeltas = Integer.valueOf(DEFAULT_MAX_SNAP_DELTAS);
}
}
@ConfigurableInit
public StorageInfo init( ) {
setDefaults( );
return this;
}
private static StorageInfo getDefaultInstance() {
return new StorageInfo( ).init( );
}
public static StorageInfo getStorageInfo() {
StorageInfo conf = null;
try {
conf = Transactions.find(new StorageInfo());
} catch (Exception e) {
LOG.warn("Storage controller properties for " + StorageProperties.NAME + " not found. Loading defaults.");
try {
conf = Transactions.saveDirect(getDefaultInstance());
} catch (Exception e1) {
try {
conf = Transactions.find(new StorageInfo());
} catch (Exception e2) {
LOG.warn("Failed to persist and retrieve StorageInfo entity");
}
}
}
if (conf == null) {
conf = getDefaultInstance();
}
return conf;
}
public static class MinimumPartSizeChangeListener implements PropertyChangeListener<Integer> {
@Override
public void fireChange(ConfigurableProperty t, Integer newValue) throws ConfigurablePropertyException {
try {
if (newValue == null) {
LOG.error("Invalid value for " + t.getFieldName());
throw new ConfigurablePropertyException("Invalid value for " + t.getFieldName());
} else if (newValue.intValue() < 5) {
LOG.error(t.getFieldName() + " cannot be modified to " + newValue + ". It must be greater than or equal to 5");
throw new ConfigurablePropertyException(
t.getFieldName() + " cannot be modified to " + newValue + ". It must be greater than or equal to 5");
}
} catch (IllegalArgumentException e) {
throw new ConfigurablePropertyException("Invalid paths: " + e, e);
}
}
}
public static class PositiveIntegerChangeListener implements PropertyChangeListener<Integer> {
@Override
public void fireChange(ConfigurableProperty t, Integer newValue) throws ConfigurablePropertyException {
if (newValue == null) {
LOG.error("Invalid value for " + t.getFieldName());
throw new ConfigurablePropertyException("Invalid value for " + t.getFieldName());
} else if (newValue.intValue() <= 0) {
LOG.error(t.getFieldName() + " cannot be modified to " + newValue + ". It must be an integer greater than 0");
throw new ConfigurablePropertyException(t.getFieldName() + " cannot be modified to " + newValue + ". It must be an integer greater than 0");
}
}
}
public static class NonNegativeIntegerChangeListener implements PropertyChangeListener<Integer> {
@Override
public void fireChange(ConfigurableProperty t, Integer newValue) throws ConfigurablePropertyException {
if (newValue == null) {
LOG.error("Invalid value for " + t.getFieldName());
throw new ConfigurablePropertyException("Invalid value for " + t.getFieldName());
} else if (newValue.intValue() < 0) {
LOG.error(t.getFieldName() + " cannot be modified to " + newValue + ". It must be an integer greater than or equal to 0");
throw new ConfigurablePropertyException(
t.getFieldName() + " cannot be modified to " + newValue + ". It must be an integer greater than or equal to 0");
}
}
}
public static class MinimumExpirationTimeChangeListener implements PropertyChangeListener<Integer> {
@Override
public void fireChange(ConfigurableProperty t, Integer newValue) throws ConfigurablePropertyException {
if (newValue == null) {
LOG.warn("Invalid value for " + t.getFieldName());
throw new ConfigurablePropertyException("Invalid value for " + t.getFieldName());
} else if (newValue.intValue() < MIN_RESOURCE_EXPIRATION_TIME) {
LOG.warn(t.getFieldName() + " cannot be modified to " + newValue + ". It must be an integer greater than or equal to "
+ MIN_RESOURCE_EXPIRATION_TIME);
throw new ConfigurablePropertyException(t.getFieldName() + " cannot be modified to " + newValue
+ ". It must be an integer greater than or equal to " + MIN_RESOURCE_EXPIRATION_TIME);
}
}
}
@PreUpgrade(since = v4_1_0, value = Storage.class)
public static class RenameColumns implements Callable<Boolean> {
private static final Logger LOG = Logger.getLogger(RenameColumns.class);
@Override
public Boolean call() throws Exception {
LOG.info("Renaming columns in table storage_info");
Sql sql = null;
try {
sql = DatabaseFilters.NEWVERSION.getConnection("eucalyptus_storage");
String table = "storage_info";
// check if the old column exists before renaming it
String oldName = "max_concurrent_snapshot_uploads";
String newName = "max_concurrent_snapshot_transfers";
List<GroovyRowResult> result =
sql.rows(String.format("select column_name from information_schema.columns where table_name='%s' and column_name='%s'", table, oldName));
if (result != null && !result.isEmpty()) {
// drop new column if it exists
LOG.info("Dropping column if it exists " + newName);
sql.execute(String.format("alter table %s drop column if exists %s", table, newName));
// rename the new column
LOG.info("Renaming column " + oldName + " to " + newName);
sql.execute(String.format("alter table %s rename column %s to %s", table, oldName, newName));
} else {
LOG.debug("Column " + oldName + " not found, nothing to rename");
}
// check if the old column exists before renaming it
oldName = "snapshot_upload_timeout_hours";
newName = "snapshot_transfer_timeout_hours";
result =
sql.rows(String.format("select column_name from information_schema.columns where table_name='%s' and column_name='%s'", table, oldName));
if (result != null && !result.isEmpty()) {
// drop new column if it exists
LOG.info("Dropping column if it exists " + newName);
sql.execute(String.format("alter table %s drop column if exists %s", table, newName));
// rename the new column
LOG.info("Renaming column " + oldName + " to " + newName);
sql.execute(String.format("alter table %s rename column %s to %s", table, oldName, newName));
} else {
LOG.debug("Column " + oldName + " not found, nothing to rename");
}
return Boolean.TRUE;
} catch (Exception e) {
LOG.warn("Failed to rename columns in table storage_info", e);
return Boolean.TRUE;
} finally {
if (sql != null) {
sql.close();
}
}
}
}
@PreUpgrade(since = v4_2_0, value = Storage.class)
public static class RenameColumnDeletedVolExpiration implements Callable<Boolean> {
private static final Logger LOG = Logger.getLogger(RenameColumns.class);
@Override
public Boolean call() throws Exception {
LOG.info("Renaming column deleted_vol_expiration to vol_expiration in table storage_info");
Sql sql = null;
try {
sql = DatabaseFilters.NEWVERSION.getConnection("eucalyptus_storage");
String table = "storage_info";
// check if the old column exists before renaming it
String oldName = "deleted_vol_expiration";
String newName = "vol_expiration";
List<GroovyRowResult> result =
sql.rows(String.format("select column_name from information_schema.columns where table_name='%s' and column_name='%s'", table, oldName));
if (result != null && !result.isEmpty()) {
// drop new column if it exists
LOG.info("Dropping column if it exists " + newName);
sql.execute(String.format("alter table %s drop column if exists %s", table, newName));
// rename the new column
LOG.info("Renaming column " + oldName + " to " + newName);
sql.execute(String.format("alter table %s rename column %s to %s", table, oldName, newName));
} else {
LOG.debug("Column " + oldName + " not found, nothing to rename");
}
return Boolean.TRUE;
} catch (Exception e) {
LOG.warn("Failed to rename column deleted_vol_expiration to vol_expiration in table storage_info", e);
return Boolean.TRUE;
} finally {
if (sql != null) {
sql.close();
}
}
}
}
@EntityUpgrade(entities = {StorageInfo.class}, since = v4_2_0, value = Storage.class)
public static enum ConvertExpirationTime implements Predicate<Class> {
INSTANCE;
private static final Logger LOG = Logger.getLogger(StorageInfo.ConvertExpirationTime.class);
@Override
public boolean apply(@Nullable Class arg0) {
LOG.info("Entity upgrade for StorageInfo entities - converting deletionTime column from hours to minutes");
try (TransactionResource tr = Entities.transactionFor(StorageInfo.class)) {
List<StorageInfo> storageInfoList = Entities.query(new StorageInfo(null));
if (storageInfoList != null && !storageInfoList.isEmpty()) {
for (StorageInfo storageInfo : storageInfoList) {
if (storageInfo.getVolExpiration() != null) {
storageInfo.setVolExpiration(storageInfo.getVolExpiration() * 1440);
}
}
}
tr.commit();
} catch (Exception e) {
LOG.warn("Failed to perform entity upgrade for StorageInfo entities", e);
Exceptions.toUndeclared("Failed to perform entity upgrade for StorageInfo entities", e);
}
return true;
}
}
@PreUpgrade(since = v4_4_0, value = Storage.class)
public static class RemoveMaxSnapTransferRetries implements Callable<Boolean> {
private static final Logger LOG = Logger.getLogger(RemoveMaxSnapTransferRetries.class);
@Override
public Boolean call() throws Exception {
Sql sql = null;
try {
sql = DatabaseFilters.NEWVERSION.getConnection("eucalyptus_storage");
String table = "storage_info";
// check if the old column exists before renaming it
String column = "max_snap_transfer_retries";
List<GroovyRowResult> result =
sql.rows(String.format("select column_name from information_schema.columns where table_name='%s' and column_name='%s'", table, column));
if (result != null && !result.isEmpty()) {
// drop column if it exists
LOG.info("Dropping column if it exists " + column);
sql.execute(String.format("alter table %s drop column if exists %s", table, column));
} else {
LOG.debug("Column " + column + " not found, nothing to drop");
}
return Boolean.TRUE;
} catch (Exception e) {
LOG.warn("Failed to drop columns in table storage_info", e);
return Boolean.TRUE;
} finally {
if (sql != null) {
sql.close();
}
}
}
}
}