/*************************************************************************
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* 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/.
*
* 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.
*******************************************************************************/
/*
*
* Author: Neil Soman neil@eucalyptus.com
*/
package com.eucalyptus.blockstorage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityTransaction;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import com.eucalyptus.blockstorage.StorageManagers.StorageManagerProperty;
import com.eucalyptus.blockstorage.entities.DASInfo;
import com.eucalyptus.blockstorage.entities.DirectStorageInfo;
import com.eucalyptus.blockstorage.entities.ISCSIVolumeInfo;
import com.eucalyptus.blockstorage.entities.LVMVolumeInfo;
import com.eucalyptus.blockstorage.entities.StorageInfo;
import com.eucalyptus.blockstorage.entities.VolumeInfo;
import com.eucalyptus.blockstorage.util.StorageProperties;
import com.eucalyptus.component.Partitions;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.ServiceConfigurations;
import com.eucalyptus.cluster.common.ClusterController;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableProperty;
import com.eucalyptus.configurable.ConfigurablePropertyException;
import com.eucalyptus.configurable.PropertyDirectory;
import com.eucalyptus.crypto.Ciphers;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.objectstorage.util.ObjectStorageProperties;
import com.eucalyptus.storage.common.CheckerTask;
import com.eucalyptus.system.BaseDirectory;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.util.Exceptions;
import com.google.common.base.Function;
import edu.ucsb.eucalyptus.msgs.ComponentProperty;
import edu.ucsb.eucalyptus.util.SystemUtil;
import edu.ucsb.eucalyptus.util.SystemUtil.CommandOutput;
@StorageManagerProperty("das")
public class DASManager implements LogicalStorageManager {
private static Logger LOG = Logger.getLogger(DASManager.class);
public static final String lvmRootDirectory = "/dev";
protected static final long LVM_HEADER_LENGTH = 4 * StorageProperties.MB;
public static final String PATH_SEPARATOR = "/";
public static boolean initialized = false;
public static final int MAX_LOOP_DEVICES = 256;
public static final String EUCA_ROOT_WRAPPER = BaseDirectory.LIBEXEC.toString() + "/euca_rootwrap";
public static final String EUCA_VAR_RUN_PATH = System.getProperty("euca.run.dir");
public static final StorageExportManager exportManager = new ISCSIManager();
public static final StorageExportManager threadedExportManager = new ThreadPoolDispatchingStorageExportManager(new ISCSIManager());
private static String volumeGroup;
protected ConcurrentHashMap<String, VolumeOpMonitor> volumeOps;
@Override
public void checkPreconditions() throws EucalyptusCloudException {
// check if binaries exist, commands can be executed, etc.
if (!new File(EUCA_ROOT_WRAPPER).exists()) {
throw new EucalyptusCloudException("root wrapper (euca_rootwrap) does not exist in " + EUCA_ROOT_WRAPPER);
}
File varDir = new File(EUCA_VAR_RUN_PATH);
if (!varDir.exists()) {
varDir.mkdirs();
}
try {
String returnValue = LVMWrapper.getLvmVersion();
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Is lvm installed?");
} else {
LOG.debug("lvm version: " + returnValue);
}
// exportManager = new ISCSIManager();
if (exportManager != null) {
exportManager.checkPreconditions();
}
} catch (EucalyptusCloudException ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(error);
}
}
private void updateVolumeGroup() throws EucalyptusCloudException {
if (volumeGroup == null) {
String dasDevice = DASInfo.getStorageInfo().getDASDevice();
if (dasDevice != null) {
try {
boolean volumeGroupFound = false;
String returnValue = null;
try {
returnValue = LVMWrapper.getVolumeGroup(dasDevice);
if (returnValue.length() > 0) {
volumeGroupFound = true;
}
} catch (EucalyptusCloudException e) {
LOG.warn(e);
}
if (volumeGroupFound) {
Pattern volumeGroupPattern = Pattern.compile("(?s:.*VG Name)(.*)\n.*");
Matcher m = volumeGroupPattern.matcher(returnValue);
if (m.find())
volumeGroup = m.group(1).trim();
else
throw new EucalyptusCloudException("Not a volume group: " + dasDevice);
} else {
boolean physicalVolumeGroupFound = false;
try {
returnValue = LVMWrapper.getPhysicalVolume(dasDevice);
if (returnValue.matches("(?s:.*)PV Name.*" + dasDevice + "(?s:.*)")) {
physicalVolumeGroupFound = true;
}
} catch (EucalyptusCloudException e) {
LOG.warn(e);
}
if (!physicalVolumeGroupFound) {
returnValue = LVMWrapper.createPhysicalVolume(dasDevice);
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to create physical volume on device: " + dasDevice);
}
}
// PV should be initialized at this point.
returnValue = LVMWrapper.getPhysicalVolumeVerbose(dasDevice);
if (returnValue.matches("(?s:.*)PV Name.*" + dasDevice + "(?s:.*)")) {
Pattern volumeGroupPattern = Pattern.compile("(?s:.*VG Name)(.*)\n.*");
Matcher m = volumeGroupPattern.matcher(returnValue);
if (m.find()) {
volumeGroup = m.group(1).trim();
}
if ((volumeGroup == null) || (volumeGroup.length() == 0)) {
volumeGroup = generateVGName(Crypto.getRandom(10));
returnValue = LVMWrapper.createVolumeGroup(dasDevice, volumeGroup);
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to create volume group: " + volumeGroup + " physical volume: " + dasDevice);
}
}
} else {
Pattern volumeGroupPattern = Pattern.compile("(?s:.*VG Name)(.*)\n.*");
Matcher m = volumeGroupPattern.matcher(returnValue);
if (m.find())
volumeGroup = m.group(1).trim();
else
throw new EucalyptusCloudException("Unable to get volume group for physical volume: " + dasDevice);
}
}
} catch (EucalyptusCloudException e) {
LOG.error(e);
throw new EucalyptusCloudException(e);
}
} else {
throw new EucalyptusCloudException("DAS partition not yet configured. Please specify partition.");
}
}
}
protected String duplicateLogicalVolume(String oldLvName, String newLvName) throws EucalyptusCloudException {
return SystemUtil.run(new String[] {EUCA_ROOT_WRAPPER, "dd", "if=" + oldLvName, "of=" + newLvName, "bs=" + StorageProperties.blockSize});
}
protected String createFile(String fileName, long size) throws EucalyptusCloudException {
if (!DirectStorageInfo.getStorageInfo().getZeroFillVolumes())
return SystemUtil.run(new String[] {EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=1", "bs=" + StorageProperties.blockSize,
"seek=" + (size - 1)});
else
return SystemUtil
.run(new String[] {EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=" + size, "bs=" + StorageProperties.blockSize});
}
protected String createEmptyFile(String fileName, int size) throws EucalyptusCloudException {
long fileSize = size * 1024;
return createFile(fileName, fileSize);
}
public String createAbsoluteEmptyFile(String fileName, long size) throws EucalyptusCloudException {
size = size / ObjectStorageProperties.M;
return createFile(fileName, size);
}
@Override
public void initialize() throws EucalyptusCloudException {
if (!initialized) {
// DO NOT WANT!
// System.loadLibrary("dascontrol");
// registerSignals();
initialized = true;
}
}
@Override
public void configure() throws EucalyptusCloudException {
exportManager.configure();
// Initialize StorageInfo, DirectStorageInfo and DASInfo entities
StorageInfo.getStorageInfo();
DirectStorageInfo.getStorageInfo();
DASInfo.getStorageInfo();
}
public void startupChecks() {
reload();
}
public void cleanVolume(String volumeId) {
try {
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo lvmVolInfo = volumeManager.getVolumeInfo(volumeId);
if (lvmVolInfo != null) {
volumeManager.unexportVolume(lvmVolInfo);
String lvName = lvmVolInfo.getLvName();
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + lvName;
try {
String returnValue = LVMWrapper.removeLogicalVolume(absoluteLVName);
} catch (EucalyptusCloudException ex) {
// volumeManager.abort();
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
}
// Always delete the metadata regardless of the actual clean up
volumeManager.remove(lvmVolInfo);
volumeManager.finish();
}
}
} catch (EucalyptusCloudException e) {
LOG.debug("Failed to clean volume: " + volumeId, e);
return;
}
}
@Override
public void cleanSnapshot(String snapshotId, String snapshotPointId) {
try {
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo lvmVolInfo = volumeManager.getVolumeInfo(snapshotId);
if (lvmVolInfo != null) {
volumeManager.remove(lvmVolInfo);
}
volumeManager.finish();
}
} catch (EucalyptusCloudException e) {
LOG.debug("Failed to clean snapshotId: " + snapshotId, e);
return;
}
}
public native void registerSignals();
public void dupFile(String oldFileName, String newFileName) {
FileOutputStream fileOutputStream = null;
FileChannel out = null;
FileInputStream fileInputStream = null;
FileChannel in = null;
try {
fileOutputStream = new FileOutputStream(new File(newFileName));
out = fileOutputStream.getChannel();
fileInputStream = new FileInputStream(new File(oldFileName));
in = fileInputStream.getChannel();
in.transferTo(0, in.size(), out);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
out.close();
fileOutputStream.close();
} catch (IOException e) {
LOG.error(e);
}
}
if (fileInputStream != null) {
try {
in.close();
fileInputStream.close();
} catch (IOException e) {
LOG.error(e);
}
}
}
}
// creates a logical volume (and a new physical volume and volume group)
public void createLogicalVolume(String volumeId, String lvName, long size) throws EucalyptusCloudException {
if (volumeGroup != null) {
String returnValue = LVMWrapper.createLogicalVolume(volumeId, volumeGroup, lvName, size);
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to create logical volume " + lvName + " in volume group " + volumeGroup);
}
} else {
throw new EucalyptusCloudException("Volume group is null! This should never happen");
}
}
@Override
public void createVolume(String volumeId, int size) throws EucalyptusCloudException {
updateVolumeGroup();
File volumeDir = new File(DirectStorageInfo.getStorageInfo().getVolumesDir());
volumeDir.mkdirs();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
String lvName = generateLVName(volumeId);// "lv-" + Hashes.getRandom(4);
LVMVolumeInfo lvmVolumeInfo = null;
lvmVolumeInfo = new ISCSIVolumeInfo();
volumeManager.finish();
// create file and attach to loopback device
try {
// create logical volume
createLogicalVolume(volumeId, lvName, (size * StorageProperties.KB));
lvmVolumeInfo.setVolumeId(volumeId);
lvmVolumeInfo.setVgName(volumeGroup);
lvmVolumeInfo.setLvName(lvName);
lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString());
lvmVolumeInfo.setSize(size);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
nestedVolumeManager.add(lvmVolumeInfo);
nestedVolumeManager.finish();
}
} catch (EucalyptusCloudException ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(error);
}
}
}
@Override
public int createVolume(String volumeId, String snapshotId, int size) throws EucalyptusCloudException {
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundSnapshotInfo = volumeManager.getVolumeInfo(snapshotId);
if (foundSnapshotInfo != null) {
String status = foundSnapshotInfo.getStatus();
if (StorageProperties.Status.available.toString().equals(status)) {
String lvName = generateLVName(volumeId); // "lv-" + Hashes.getRandom(4);
LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo();
String snapId = foundSnapshotInfo.getVolumeId();
String loFileName = foundSnapshotInfo.getLoFileName();
volumeManager.finish();
try {
File snapshotFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + snapId);
assert (snapshotFile.exists());
long absoluteSize;
if (size <= 0 || size == foundSnapshotInfo.getSize()) {
// size = (int)(snapshotFile.length() / StorageProperties.GB);
absoluteSize = snapshotFile.length() / StorageProperties.MB;
size = (int) (absoluteSize / StorageProperties.KB);
} else {
absoluteSize = size * StorageProperties.KB;
}
// create physical volume, volume group and logical volume
createLogicalVolume(volumeId, lvName, absoluteSize);
// duplicate snapshot volume
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + lvName;
duplicateLogicalVolume(loFileName, absoluteLVName);
lvmVolumeInfo.setVolumeId(volumeId);
lvmVolumeInfo.setVgName(volumeGroup);
lvmVolumeInfo.setLvName(lvName);
lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString());
lvmVolumeInfo.setSize(size);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
nestedVolumeManager.add(lvmVolumeInfo);
nestedVolumeManager.finish();
}
} catch (EucalyptusCloudException ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(error);
}
} else {
throw new EucalyptusCloudException(
"Cannot create volume " + volumeId + " from snapshot " + snapshotId + " since snapshot status is " + status);
}
} else {
throw new EucalyptusCloudException("Unable to find snapshot: " + snapshotId);
}
return size;
}
}
@Override
public void cloneVolume(String volumeId, String parentVolumeId) throws EucalyptusCloudException {
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundVolumeInfo = volumeManager.getVolumeInfo(parentVolumeId);
if (foundVolumeInfo != null) {
String status = foundVolumeInfo.getStatus();
String lvName = generateLVName(volumeId); // "lv-" + Hashes.getRandom(4);
LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo();
String parentLvName = foundVolumeInfo.getLvName();
int size = foundVolumeInfo.getSize();
volumeManager.finish();
try {
File parentVolumeFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + parentVolumeId);
assert (parentVolumeFile.exists());
long absouluteSize = (parentVolumeFile.length() / StorageProperties.MB);
// create physical volume, volume group and logical volume
createLogicalVolume(volumeId, lvName, absouluteSize);
// duplicate snapshot volume
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + lvName;
String absoluteParentLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + parentLvName;
duplicateLogicalVolume(absoluteParentLVName, absoluteLVName);
// export logical volume
try {
volumeManager.exportVolume(lvmVolumeInfo, volumeGroup, lvName);
} catch (EucalyptusCloudException ex) {
String returnValue = LVMWrapper.removeLogicalVolume(absoluteLVName);
throw ex;
}
lvmVolumeInfo.setVolumeId(volumeId);
lvmVolumeInfo.setVgName(volumeGroup);
lvmVolumeInfo.setLvName(lvName);
lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString());
lvmVolumeInfo.setSize(size);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
nestedVolumeManager.add(lvmVolumeInfo);
nestedVolumeManager.finish();
}
} catch (EucalyptusCloudException ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(error);
}
} else {
throw new EucalyptusCloudException("Unable to find volume: " + parentVolumeId);
}
}
}
@Override
public void addSnapshot(String snapshotId) throws EucalyptusCloudException {
String snapshotRawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + snapshotId;
File snapshotFile = new File(snapshotRawFileName);
if (snapshotFile.exists()) {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo();
lvmVolumeInfo.setVolumeId(snapshotId);
lvmVolumeInfo.setLoFileName(snapshotRawFileName);
lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString());
lvmVolumeInfo.setSize((int) (snapshotFile.length() / StorageProperties.GB));
volumeManager.add(lvmVolumeInfo);
volumeManager.finish();
}
} else {
throw new EucalyptusCloudException("Snapshot backing file does not exist for: " + snapshotId);
}
}
@Override
public void deleteVolume(String volumeId) throws EucalyptusCloudException {
updateVolumeGroup();
LVMVolumeInfo foundLVMVolumeInfo = null;
{
try (final VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
volumeManager.finish();
}
}
if (foundLVMVolumeInfo != null) {
boolean isReadyForDelete = false;
int retryCount = 0;
// Obtain a lock on the volume
VolumeOpMonitor monitor = getMonitor(foundLVMVolumeInfo.getVolumeId());
do {
LOG.debug("Trying to lock volume for export detection and deletion " + volumeId);
synchronized (monitor) {
try (final VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
if (exportManager.isExported(foundLVMVolumeInfo)) {
LOG.error("Cannot delete volume " + volumeId + " because it is currently exported");
volumeManager.finish();
} else {
LOG.debug("Volume " + volumeId + " is prepped for deletion");
isReadyForDelete = true;
LOG.info("Deleting volume " + volumeId);
String lvName = foundLVMVolumeInfo.getLvName();
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + lvName;
String returnValue = "";
for (int i = 0; i < 5; ++i) {
returnValue = LVMWrapper.removeLogicalVolume(absoluteLVName);
if (returnValue.length() != 0) {
if (returnValue.contains("successfully removed")) {
break;
}
}
// retry lv deletion (can take a while).
try {
Thread.sleep(500);
} catch (InterruptedException e) {
LOG.error(e);
break;
}
}
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteLVName);
}
volumeManager.remove(foundLVMVolumeInfo);
volumeManager.finish();
break;
}
} catch (Exception e) {
LOG.warn("Error cleaning up volume " + volumeId, e);
}
LOG.debug("Lap: " + retryCount++);
} // Release the lock for retry.
if (!isReadyForDelete) {
try {
Thread.sleep(10000); // sleep before the retry
} catch (InterruptedException e) {
throw new EucalyptusCloudException("Thread interrupted. Failing volume delete for volume " + volumeId);
}
}
} while (!isReadyForDelete && retryCount < 20);
// Remove the monitor
removeMonitor(volumeId);
if (!isReadyForDelete) {
LOG.error("All attempts to cleanup volume " + volumeId + " failed");
throw new EucalyptusCloudException("Unable to delete volume: " + volumeId + ". All attempts to cleanup the volume failed");
}
} else {
throw new EucalyptusCloudException("Unable to find volume: " + volumeId);
}
}
protected static String generateLVName(String baseName) {
return "euca-" + baseName;
}
protected static String generateVGName(String baseName) {
return "euca-ebs-storage-vg-" + baseName;
}
@Override
public void createSnapshot(String volumeId, String snapshotId, String snapshotPointId) throws EucalyptusCloudException {
if (snapshotPointId != null) {
throw new EucalyptusCloudException("Synchronous snapshot points not supported in DAS storage manager");
}
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
// StorageResource snapInfo = null;
if (foundLVMVolumeInfo != null) {
LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo();
snapshotInfo.setVolumeId(snapshotId);
File snapshotDir = new File(DirectStorageInfo.getStorageInfo().getVolumesDir());
snapshotDir.mkdirs();
String lvName = generateLVName(snapshotId);// "lv-snap-" + Hashes.getRandom(4);
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + foundLVMVolumeInfo.getLvName();
int size = foundLVMVolumeInfo.getSize();
volumeManager.finish();
// volumeManager = null;
try {
long absoluteSize;
CommandOutput result = SystemUtil.runWithRawOutput(new String[] {EUCA_ROOT_WRAPPER, "blockdev", "--getsize64", absoluteLVName});
if (null != result && result.returnValue == 0 && StringUtils.isNotBlank(StringUtils.trim(result.output))) {
try {
absoluteSize = (Long.parseLong(StringUtils.trim(result.output)) / StorageProperties.MB);
} catch (NumberFormatException e) {
LOG.debug("Failed to parse size of volume " + volumeId, e);
absoluteSize = size * StorageProperties.KB;
}
} else {
absoluteSize = size * StorageProperties.KB;
}
// create physical volume, volume group and logical volume
// String returnValue = createSnapshotLogicalVolume(absoluteLVName, lvName, size);
String returnValue = LVMWrapper.createSnapshotLogicalVolume(absoluteLVName, lvName, absoluteSize);
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to create snapshot logical volume " + lvName + " for volume " + lvName);
}
String snapRawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + snapshotId;
String absoluteSnapLVName = lvmRootDirectory + PATH_SEPARATOR + volumeGroup + PATH_SEPARATOR + lvName;
duplicateLogicalVolume(absoluteSnapLVName, snapRawFileName);
returnValue = LVMWrapper.removeLogicalVolume(absoluteSnapLVName);
if (returnValue.length() == 0) {
throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteSnapLVName);
}
snapshotInfo.setLoFileName(snapRawFileName);
// snapshotInfo.setStatus(StorageProperties.Status.available.toString());
snapshotInfo.setSize(size);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
nestedVolumeManager.add(snapshotInfo);
nestedVolumeManager.finish();
}
// snapInfo = new FileResource(snapshotId, snapRawFileName);
} catch (Exception ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(error);
}
}
}
}
public List<String> prepareForTransfer(String snapshotId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(snapshotId);
ArrayList<String> returnValues = new ArrayList<String>();
if (foundLVMVolumeInfo != null) {
returnValues.add(DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + foundLVMVolumeInfo.getVolumeId());
volumeManager.finish();
} else {
volumeManager.abort();
throw new EucalyptusCloudException("Unable to find snapshot: " + snapshotId);
}
return returnValues;
}
}
@Override
public void deleteSnapshot(String snapshotId, String snapshotPointId) throws EucalyptusCloudException {
updateVolumeGroup();
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(snapshotId);
if (foundLVMVolumeInfo != null) {
volumeManager.remove(foundLVMVolumeInfo);
File snapFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + foundLVMVolumeInfo.getVolumeId());
volumeManager.finish();
if (snapFile.exists()) {
if (!snapFile.delete()) {
throw new EucalyptusCloudException("Unable to delete: " + snapFile.getAbsolutePath());
}
}
} else {
throw new EucalyptusCloudException("Unable to find snapshot: " + snapshotId);
}
}
}
public String getVolumeConnectionString(String volumeId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
String returnValue = volumeManager.getConnectionString(volumeId);
volumeManager.finish();
return returnValue;
}
}
public void reload() {
LOG.info("Reload: starting reload process to re-export volumes if necessary");
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
List<LVMVolumeInfo> volumeInfos = volumeManager.getAllVolumeInfos();
// now enable them
for (LVMVolumeInfo foundVolumeInfo : volumeInfos) {
try {
LOG.info("Reload: Checking volume " + foundVolumeInfo.getVolumeId() + " for export");
if (foundVolumeInfo.getVgName() != null && volumeManager.shouldExportOnReload(foundVolumeInfo)) {
LOG.info("Reload: Volume " + foundVolumeInfo.getVolumeId()
+ " was exported at shutdown. Not found to be already exported. Re-exporting volume.");
volumeManager.exportVolume(foundVolumeInfo);
} else {
LOG.info(
"Reload: volume " + foundVolumeInfo.getVolumeId() + " not previously exported or already exported, no action required. Skipping");
}
} catch (EucalyptusCloudException ex) {
LOG.error("Unable to reload volume: " + foundVolumeInfo.getVolumeId() + " due to: " + ex);
}
}
volumeManager.finish();
}
}
public int getSnapshotSize(String snapshotId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo(snapshotId);
if (lvmVolumeInfo != null) {
int snapSize = lvmVolumeInfo.getSize();
volumeManager.finish();
return snapSize;
} else {
volumeManager.abort();
return 0;
}
}
}
protected static class VolumeMetadataManager implements AutoCloseable {
private EntityTransaction transaction;
protected VolumeMetadataManager() {
transaction = Entities.get(VolumeInfo.class);
}
@Override
public void close() {
if (isActive()) {
transaction.rollback();
}
}
/**
* Returns if the volume should be re-exported on reload based on DB state. Masks the implementation details from DASManager so it can be changed
* based on DB or iscsi implementation
*
* @param lvmVolumeInfo
* @return
*/
public boolean shouldExportOnReload(LVMVolumeInfo lvmVolumeInfo) {
if (lvmVolumeInfo instanceof ISCSIVolumeInfo) {
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo;
try {
return iscsiVolumeInfo.getTid() >= 0 && !exportManager.isExported(lvmVolumeInfo);
} catch (EucalyptusCloudException e) {
LOG.error("Failed to determine if volume " + lvmVolumeInfo.getVolumeId() + " is already exported. Returning false", e);
}
}
return false;
}
public List<String> getSnapshotValues(String snapshotId) {
ArrayList<String> returnValues = new ArrayList<String>();
LVMVolumeInfo lvmVolumeInfo = getVolumeInfo(snapshotId);
return returnValues;
}
public void exportVolume(LVMVolumeInfo lvmVolumeInfo) throws EucalyptusCloudException {
if (lvmVolumeInfo instanceof ISCSIVolumeInfo) {
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo;
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + iscsiVolumeInfo.getVgName() + PATH_SEPARATOR + iscsiVolumeInfo.getLvName();
if (!LVMWrapper.logicalVolumeExists(absoluteLVName)) {
LOG.error("Backing volume not found: " + absoluteLVName);
throw new EucalyptusCloudException("Logical volume not found: " + absoluteLVName);
}
try {
LVMWrapper.enableLogicalVolume(absoluteLVName);
} catch (EucalyptusCloudException ex) {
String error = "Unable to run command: " + ex.getMessage();
LOG.error(error);
throw new EucalyptusCloudException(ex);
}
((ISCSIManager) exportManager).exportTarget(iscsiVolumeInfo.getVolumeId(), iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getStoreName(),
iscsiVolumeInfo.getLun(), absoluteLVName, iscsiVolumeInfo.getStoreUser());
}
}
public String getConnectionString(String volumeId) {
LVMVolumeInfo lvmVolumeInfo = getVolumeInfo(volumeId);
if (lvmVolumeInfo != null) {
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo;
String storeName = iscsiVolumeInfo.getStoreName();
String encryptedPassword;
try {
encryptedPassword = ((ISCSIManager) exportManager).getEncryptedPassword();
} catch (EucalyptusCloudException e) {
LOG.error(e);
return null;
}
return "iscsi,tgt,,,," + encryptedPassword + ",," + StorageProperties.STORAGE_HOST + "," + storeName;
}
return null;
}
public void unexportVolume(LVMVolumeInfo volumeInfo) {
StorageExportManager manager = exportManager;
if (!(exportManager instanceof ISCSIManager)) {
manager = new ISCSIManager();
}
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) volumeInfo;
// Use the absolute name to verify that the target is correct before unexport
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + iscsiVolumeInfo.getVgName() + PATH_SEPARATOR + iscsiVolumeInfo.getLvName();
if (LVMWrapper.logicalVolumeExists(absoluteLVName)) {
try {
((ISCSIManager) manager).unexportTarget(volumeInfo.getVolumeId(), iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getLun(), absoluteLVName);
} catch (EucalyptusCloudException e) {
LOG.error("Error unexporting target for volume " + volumeInfo.getVolumeId(), e);
return;
}
}
iscsiVolumeInfo.setTid(-1);
}
protected void finish() {
try {
transaction.commit();
} catch (Exception ex) {
LOG.error(ex, ex);
transaction.rollback();
}
}
protected void abort() {
transaction.rollback();
}
protected boolean isActive() {
return transaction != null && transaction.isActive();
}
protected LVMVolumeInfo getVolumeInfo(String volumeId) {
ISCSIVolumeInfo ISCSIVolumeInfo = new ISCSIVolumeInfo(volumeId);
List<ISCSIVolumeInfo> ISCSIVolumeInfos = Entities.query(ISCSIVolumeInfo);
if (ISCSIVolumeInfos.size() > 0) {
return ISCSIVolumeInfos.get(0);
}
return null;
}
protected boolean areSnapshotsPending(String volumeId) {
ISCSIVolumeInfo ISCSIVolumeInfo = new ISCSIVolumeInfo();
ISCSIVolumeInfo.setSnapshotOf(volumeId);
ISCSIVolumeInfo.setStatus(StorageProperties.Status.pending.toString());
List<ISCSIVolumeInfo> ISCSIVolumeInfos = Entities.query(ISCSIVolumeInfo);
if (ISCSIVolumeInfos.size() > 0) {
return true;
}
return false;
}
protected LVMVolumeInfo getVolumeInfo() {
return new ISCSIVolumeInfo();
}
protected List<LVMVolumeInfo> getAllVolumeInfos() {
List<LVMVolumeInfo> volumeInfos = new ArrayList<LVMVolumeInfo>();
volumeInfos.addAll(Entities.query(new ISCSIVolumeInfo()));
return volumeInfos;
}
protected void add(LVMVolumeInfo volumeInfo) {
Entities.persist(volumeInfo);
}
protected void remove(LVMVolumeInfo volumeInfo) {
Entities.delete(volumeInfo);
}
protected String encryptTargetPassword(String password) throws EucalyptusCloudException {
try {
List<ServiceConfiguration> partitionConfigs = ServiceConfigurations.listPartition(ClusterController.class, StorageProperties.NAME);
ServiceConfiguration clusterConfig = partitionConfigs.get(0);
PublicKey ncPublicKey = Partitions.lookup(clusterConfig).getNodeCertificate().getPublicKey();
Cipher cipher = Ciphers.RSA_PKCS1.get();
cipher.init(Cipher.ENCRYPT_MODE, ncPublicKey, Crypto.getSecureRandomSupplier().get());
return new String(Base64.encode(cipher.doFinal(password.getBytes())));
} catch (Exception e) {
LOG.error("Unable to encrypt storage target password");
throw new EucalyptusCloudException(e.getMessage(), e);
}
}
protected void exportVolume(LVMVolumeInfo lvmVolumeInfo, String vgName, String lvName) throws EucalyptusCloudException {
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo;
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName;
EucalyptusCloudException ex = null;
// 1st attempt's retry timeout is 10 ms, 2nd is 40ms etc., up to the configurable total timeout.
final int timeoutMultiplier = 4;
int attempt = 1;
Long retryTimeout = 10l; // ms
Long totalTimeoutSoFar = 0l;
Long totalTimeout = DirectStorageInfo.getStorageInfo().getTimeoutInMillis();
try {
threadedExportManager.allocateTarget(iscsiVolumeInfo);
} catch (EucalyptusCloudException ece) {
LOG.error("Failed to allocate target for volume " + iscsiVolumeInfo.getVolumeId(), ece);
throw ece;
}
do {
try {
((ISCSIManager) exportManager).exportTarget(iscsiVolumeInfo.getVolumeId(), iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getStoreName(),
iscsiVolumeInfo.getLun(), absoluteLVName, iscsiVolumeInfo.getStoreUser());
ex = null;
if (attempt > 1) {
LOG.info("Exporting volume " + iscsiVolumeInfo.getVolumeId() + " as target " + iscsiVolumeInfo.getTid() +
" succeeded on retry attempt " + attempt);
}
break;
} catch (EucalyptusCloudException ece) {
ex = ece;
LOG.warn("Failed to export volume " + iscsiVolumeInfo.getVolumeId() + " on attempt " + attempt);
totalTimeoutSoFar += retryTimeout;
retryTimeout *= timeoutMultiplier;
attempt++;
}
} while (totalTimeoutSoFar < totalTimeout);
// EUCA-3597 After all retries, check if the process actually completed
if (null != ex) {
LOG.error("Failed to export volume " + iscsiVolumeInfo.getVolumeId() + ", all retries exhausted.");
throw ex;
}
}
}
@Override
public void loadSnapshots(List<String> snapshotSet, List<String> snapshotFileNames) throws EucalyptusCloudException {
// TODO Auto-generated method stub
}
@Override
public StorageResourceWithCallback prepSnapshotForDownload(String snapshotId, int sizeExpected, long actualSizeInMB)
throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
String deviceName = null;
LVMVolumeInfo foundSnapshotInfo = volumeManager.getVolumeInfo(snapshotId);
if (null == foundSnapshotInfo) {
LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo();
snapshotInfo.setVolumeId(snapshotId);
snapshotInfo.setSize(sizeExpected);
snapshotInfo.setLoFileName(DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + snapshotId);
deviceName = snapshotInfo.getLoFileName();
volumeManager.add(snapshotInfo);
}
volumeManager.finish();
return new StorageResourceWithCallback(new FileResource(snapshotId, deviceName), new Function<StorageResource, String>() {
@Override
public String apply(StorageResource arg0) {
try {
LOG.debug("Executing callback after prepping for download of " + snapshotId);
finishVolume(snapshotId);
} catch (Exception e) {
LOG.warn("Failed to execute callback after prepping for download of " + snapshotId, e);
Exceptions.toUndeclared("Failed to execute callback for prepSnapshotForDownload() " + snapshotId, e);
}
return null;
}
});
// return DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + snapshotId;
}
}
@Override
public ArrayList<ComponentProperty> getStorageProps() {
ArrayList<ComponentProperty> componentProperties = null;
ConfigurableClass configurableClass = StorageInfo.class.getAnnotation(ConfigurableClass.class);
if (configurableClass != null) {
String root = configurableClass.root();
String alias = configurableClass.alias();
componentProperties = (ArrayList<ComponentProperty>) PropertyDirectory.getComponentPropertySet(StorageProperties.NAME + "." + root, alias);
}
configurableClass = DirectStorageInfo.class.getAnnotation(ConfigurableClass.class);
if (configurableClass != null) {
String root = configurableClass.root();
String alias = configurableClass.alias();
if (componentProperties == null)
componentProperties = (ArrayList<ComponentProperty>) PropertyDirectory.getComponentPropertySet(StorageProperties.NAME + "." + root, alias);
else
componentProperties.addAll(PropertyDirectory.getComponentPropertySet(StorageProperties.NAME + "." + root, alias));
}
configurableClass = DASInfo.class.getAnnotation(ConfigurableClass.class);
if (configurableClass != null) {
String root = configurableClass.root();
String alias = configurableClass.alias();
if (componentProperties == null)
componentProperties = (ArrayList<ComponentProperty>) PropertyDirectory.getComponentPropertySet(StorageProperties.NAME + "." + root, alias);
else
componentProperties.addAll(PropertyDirectory.getComponentPropertySet(StorageProperties.NAME + "." + root, alias));
}
return componentProperties;
}
@Override
public void setStorageProps(ArrayList<ComponentProperty> storageProps) {
for (ComponentProperty prop : storageProps) {
try {
ConfigurableProperty entry = PropertyDirectory.getPropertyEntry(prop.getQualifiedName());
// type parser will correctly covert the value
entry.setValue(prop.getValue());
} catch (IllegalAccessException | ConfigurablePropertyException e) {
LOG.error(e, e);
}
}
}
@Override
public String getStorageRootDirectory() {
return DirectStorageInfo.getStorageInfo().getVolumesDir();
}
@Override
public void finishVolume(String snapshotId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo foundSnapshotInfo = volumeManager.getVolumeInfo(snapshotId);
if (null != foundSnapshotInfo) {
foundSnapshotInfo.setStatus(StorageProperties.Status.available.toString());
}
volumeManager.finish();
}
}
@Override
public String getSnapshotPath(String snapshotId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo volInfo = volumeManager.getVolumeInfo(snapshotId);
if (volInfo != null) {
String snapPath = volInfo.getLoFileName();
volumeManager.finish();
return snapPath;
} else {
volumeManager.abort();
throw new EntityNotFoundException("Unable to find snapshot with id: " + snapshotId);
}
}
}
@Override
public String getVolumePath(String volumeId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo volInfo = volumeManager.getVolumeInfo(volumeId);
if (volInfo != null) {
String volumePath = lvmRootDirectory + File.separator + volInfo.getVgName() + File.separator + volInfo.getLvName();
volumeManager.finish();
return volumePath;
} else {
volumeManager.abort();
throw new EntityNotFoundException("Unable to find volume with id: " + volumeId);
}
}
}
@Override
public void importSnapshot(String snapshotId, String snapPath, String volumeId, int size) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo snapInfo = volumeManager.getVolumeInfo(snapshotId);
if (snapInfo != null) {
volumeManager.finish();
throw new EucalyptusCloudException("Snapshot " + snapshotId + " already exists. Import failed.");
}
volumeManager.finish();
String snapFileName = getStorageRootDirectory() + File.separator + snapshotId;
SystemUtil
.run(new String[] {StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + snapPath, "of=" + snapFileName, "bs=" + StorageProperties.blockSize});
// volumeManager = new VolumeMetadataManager();
LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo();
snapshotInfo.setVolumeId(snapshotId);
snapshotInfo.setLoFileName(snapFileName);
snapshotInfo.setSize(size);
snapshotInfo.setSnapshotOf(volumeId);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
nestedVolumeManager.add(snapshotInfo);
nestedVolumeManager.finish();
}
}
}
@Override
public void importVolume(String volumeId, String volumePath, int size) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo volInfo = volumeManager.getVolumeInfo(volumeId);
if (volInfo != null) {
volumeManager.finish();
throw new EucalyptusCloudException("Volume " + volumeId + " already exists. Import failed.");
}
volumeManager.finish();
createVolume(volumeId, size);
try (VolumeMetadataManager nestedVolumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo volumeInfo = nestedVolumeManager.getVolumeInfo(volumeId);
if (volumeInfo != null) {
SystemUtil.run(new String[] {StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + volumePath,
"of=" + lvmRootDirectory + File.separator + volumeInfo.getVgName() + File.separator + volumeInfo.getLvName(),
"bs=" + StorageProperties.blockSize});
nestedVolumeManager.finish();
} else {
nestedVolumeManager.abort();
throw new EucalyptusCloudException("Unable to find volume with id: " + volumeId);
}
}
}
}
/**
* NOTE: once exported to one host, it is exported to all in this implementation
*/
@Override
public String exportVolume(String volumeId, String nodeIqn) throws EucalyptusCloudException {
try {
updateVolumeGroup();
} catch (EucalyptusCloudException e) {
LOG.error(e);
throw e;
}
LVMVolumeInfo lvmVolumeInfo = null;
{
try (final VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
lvmVolumeInfo = volumeManager.getVolumeInfo(volumeId);
volumeManager.finish();
}
}
if (lvmVolumeInfo != null) {
VolumeOpMonitor monitor = getMonitor(volumeId);
synchronized (monitor) {
try (final VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
try {
lvmVolumeInfo = volumeManager.getVolumeInfo(volumeId);
String lvName = lvmVolumeInfo.getLvName();
if (lvmVolumeInfo.getVgName() == null) {
lvmVolumeInfo.setVgName(volumeGroup);
}
try {
// export logical volume
volumeManager.exportVolume(lvmVolumeInfo, volumeGroup, lvName);
} catch (EucalyptusCloudException ex) {
LOG.error("Unable to export volume " + volumeId, ex);
throw ex;
}
} catch (Exception ex) {
LOG.error("Failed to attach volume " + volumeId, ex);
throw new EucalyptusCloudException("Failed to attach volume " + volumeId, ex);
} finally {
try {
volumeManager.finish();
} catch (Exception e) {
LOG.error("Unable to commit the database transaction after an attempt to attach volume " + volumeId, e);
}
}
}
} // synchronized
}
// Return the connection string.
return getVolumeConnectionString(volumeId);
}
/**
* This implementation does not do export/unexport from single hosts, only all or none. Caller should be keeping track of exports and
* call @unexportVolumeFromAll when appropriate
*/
@Override
public void unexportVolume(String volumeId, String nodeIqn) throws EucalyptusCloudException, UnsupportedOperationException {
throw new UnsupportedOperationException("DASManager does not support node-specific export/unexport");
}
@Override
public void unexportVolumeFromAll(String volumeId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
try {
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
if (foundLVMVolumeInfo != null) {
// LOG.info("Marking volume: " + volumeId + " for cleanup");
// foundLVMVolumeInfo.setCleanup(true);
LOG.info("Unexporting volume " + volumeId + " from all clients");
VolumeOpMonitor monitor = getMonitor(volumeId);
synchronized (monitor) {
try {
LOG.info("Unexporting volume " + foundLVMVolumeInfo.getVolumeId());
try {
String lvPath = foundLVMVolumeInfo.getAbsoluteLVPath();
if (lvPath != null && LVMWrapper.logicalVolumeExists(lvPath)) {
// guard this. tgt is not happy when you ask it to
// get rid of a non existent tid
exportManager.cleanup(foundLVMVolumeInfo);
}
foundLVMVolumeInfo.setStatus("available");
LOG.info("Done cleaning up: " + foundLVMVolumeInfo.getVolumeId());
} catch (EucalyptusCloudException ee) {
LOG.error("Failed to unexport from all hosts for " + volumeId, ee);
throw ee;
}
} finally {
monitor.notifyAll();
}
} // synchronized
} else {
volumeManager.abort();
throw new EucalyptusCloudException("Unable to find volume: " + volumeId);
}
} catch (Exception e) {
LOG.error("Failed to unexport volume " + volumeId);
throw new EucalyptusCloudException("Failed to unexport volume " + volumeId);
} finally {
volumeManager.finish();
}
}
}
@Override
public void checkReady() throws EucalyptusCloudException {
// check if binaries exist, commands can be executed, etc.
if (!new File(StorageProperties.EUCA_ROOT_WRAPPER).exists()) {
throw new EucalyptusCloudException("root wrapper (euca_rootwrap) does not exist in " + StorageProperties.EUCA_ROOT_WRAPPER);
}
File varDir = new File(EUCA_VAR_RUN_PATH);
if (!varDir.exists()) {
varDir.mkdirs();
}
exportManager.check();
}
@Override
public void stop() throws EucalyptusCloudException {
exportManager.stop();
}
@Override
public void disable() throws EucalyptusCloudException {
volumeOps.clear();
volumeOps = null;
}
@Override
public void enable() throws EucalyptusCloudException {
volumeOps = new ConcurrentHashMap<String, VolumeOpMonitor>();
}
@Override
public boolean getFromBackend(String snapshotId, int size) throws EucalyptusCloudException {
return false;
}
@Override
public void checkVolume(String volumeId) throws EucalyptusCloudException {}
@Override
public List<CheckerTask> getCheckers() {
List<CheckerTask> checkers = new ArrayList<CheckerTask>();
// Volume cleanup is now synchronous, no need for background tasks
return checkers;
}
@Override
public String createSnapshotPoint(String volumeId, String snapshotId) throws EucalyptusCloudException {
return null;
}
@Override
public void deleteSnapshotPoint(String volumeId, String snapshotId, String snapshotPointId) throws EucalyptusCloudException {
throw new EucalyptusCloudException("Synchronous snapshot points not supported in DAS storage manager");
}
protected class VolumeOpMonitor {
public VolumeOpMonitor() {};
}
protected VolumeOpMonitor getMonitor(String key) {
VolumeOpMonitor monitor = volumeOps.putIfAbsent(key, new VolumeOpMonitor());
if (monitor == null) {
monitor = volumeOps.get(key);
}
return monitor;
}
public void removeMonitor(String key) {
volumeOps.remove(key);
}
@Override
public boolean supportsIncrementalSnapshots() throws EucalyptusCloudException {
return false;
}
@Override
public StorageResourceWithCallback prepIncrementalSnapshotForUpload(String volumeId, String snapshotId, String snapPointId, String prevSnapshotId,
String prevSnapPointId) throws EucalyptusCloudException {
// TODO may be throw unsupported exception?
return null;
}
@Override
public StorageResource prepSnapshotForUpload(String volumeId, String snapshotId, String snapPointId) throws EucalyptusCloudException {
try (VolumeMetadataManager volumeManager = new VolumeMetadataManager()) {
LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo(snapshotId);
volumeManager.finish();
return new FileResource(snapshotId, snapshotInfo.getLoFileName());
} catch (Exception e) {
LOG.warn("Failed to lookup lo file name for snapshot " + snapshotId, e);
throw new EucalyptusCloudException("Failed to lookup lo file name for snapshot " + snapshotId, e);
}
}
@Override
public StorageResourceWithCallback prepSnapshotBaseForRestore(String snapshotId, int size, String snapshotPointId) throws EucalyptusCloudException {
// TODO Auto-generated method stub
return null;
}
@Override
public <F, T> T executeCallback(Function<F, T> callback, F input) throws EucalyptusCloudException {
try {
return callback.apply(input);
} catch (Throwable t) {
throw new EucalyptusCloudException("Unable to execute callback for due to", t);
}
}
@Override
public void restoreSnapshotDelta(String currentSnapId, String prevSnapId, String baseId, StorageResource sr) throws EucalyptusCloudException {
// TODO Auto-generated method stub
}
@Override
public void completeSnapshotRestorationFromDeltas(String snapshotId) throws EucalyptusCloudException {
// TODO Auto-generated method stub
}
}