/******************************************************************************* *Copyright (c) 2009 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, only version 3 of the License. * * * This file 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., 130 Castilian * Dr., Goleta, CA 93101 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. *******************************************************************************/ /* * * Author: Neil Soman neil@eucalyptus.com */ package com.eucalyptus.storage; 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 javax.crypto.Cipher; import javax.persistence.EntityNotFoundException; import org.apache.log4j.Logger; import org.bouncycastle.util.encoders.Base64; import com.eucalyptus.auth.ClusterCredentials; import com.eucalyptus.auth.Authentication; import com.eucalyptus.auth.SystemCredentialProvider; import com.eucalyptus.auth.X509Cert; import com.eucalyptus.auth.util.Hashes; import com.eucalyptus.config.StorageControllerBuilder; import com.eucalyptus.configurable.ConfigurableClass; import com.eucalyptus.configurable.ConfigurableProperty; import com.eucalyptus.configurable.PropertyDirectory; import com.eucalyptus.entities.EntityWrapper; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.ExecutionException; import com.eucalyptus.util.StorageProperties; import com.eucalyptus.util.WalrusProperties; import edu.ucsb.eucalyptus.cloud.entities.AOEVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.DirectStorageInfo; import edu.ucsb.eucalyptus.cloud.entities.ISCSIVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.LVMVolumeInfo; import edu.ucsb.eucalyptus.cloud.entities.StorageInfo; import edu.ucsb.eucalyptus.cloud.entities.VolumeInfo; import edu.ucsb.eucalyptus.msgs.ComponentProperty; import edu.ucsb.eucalyptus.util.StreamConsumer; import edu.ucsb.eucalyptus.util.SystemUtil; public class OverlayManager implements LogicalStorageManager { public static final String lvmRootDirectory = "/dev"; public static final String PATH_SEPARATOR = File.separator; public static boolean initialized = false; public static final int MAX_LOOP_DEVICES = 256; public static final String EUCA_VAR_RUN_PATH = "/var/run/eucalyptus"; private static Logger LOG = Logger.getLogger(OverlayManager.class); public static String eucaHome = System.getProperty("euca.home"); private static final long LVM_HEADER_LENGTH = 4 * StorageProperties.MB; public static StorageExportManager exportManager; public static String iface = "eth0"; public static boolean zeroFillVolumes = false; public void checkPreconditions() throws EucalyptusCloudException { //check if binaries exist, commands can be executed, etc. String eucaHomeDir = System.getProperty("euca.home"); if(eucaHomeDir == null) { throw new EucalyptusCloudException("euca.home not set"); } eucaHome = eucaHomeDir; if(!new File(eucaHome + StorageProperties.EUCA_ROOT_WRAPPER).exists()) { throw new EucalyptusCloudException("root wrapper (euca_rootwrap) does not exist in " + eucaHome + StorageProperties.EUCA_ROOT_WRAPPER); } File varDir = new File(eucaHome + EUCA_VAR_RUN_PATH); if(!varDir.exists()) { varDir.mkdirs(); } try { String returnValue = getLvmVersion(); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Is lvm installed?"); } else { LOG.info(returnValue); } if(System.getProperty("euca.disable.iscsi") != null) { exportManager = new AOEManager(); } else { exportManager = new ISCSIManager(); } exportManager.checkPreconditions(); } catch(ExecutionException ex) { String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); throw new EucalyptusCloudException(error); } } private String getLvmVersion() throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvm", "version"}); } private String findFreeLoopback() throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", "-f"}).replaceAll("\n", ""); } private String getLoopback(String loDevName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", loDevName}); } private String createPhysicalVolume(String loDevName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "pvcreate", loDevName}); } private String createVolumeGroup(String pvName, String vgName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgcreate", vgName, pvName}); } private String extendVolumeGroup(String pvName, String vgName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgextend", vgName, pvName}); } private String scanVolumeGroups() throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgscan"}); } private String createLogicalVolume(String vgName, String lvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvcreate", "-n", lvName, "-l", "100%FREE", vgName}); } private String createSnapshotLogicalVolume(String lvName, String snapLvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvcreate", "-n", snapLvName, "-s", "-l", "100%FREE", lvName}); } private String removeLogicalVolume(String lvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvremove", "-f", lvName}); } private String removeVolumeGroup(String vgName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgremove", vgName}); } private String removePhysicalVolume(String loDevName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "pvremove", loDevName}); } private String removeLoopback(String loDevName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", "-d", loDevName}); } private String reduceVolumeGroup(String vgName, String pvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgreduce", vgName, pvName}); } private String enableLogicalVolume(String lvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvchange", "-ay", lvName}); } private int losetup(String absoluteFileName, String loDevName) { try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", loDevName, absoluteFileName}); StreamConsumer error = new StreamConsumer(proc.getErrorStream()); StreamConsumer output = new StreamConsumer(proc.getInputStream()); error.start(); output.start(); int errorCode = proc.waitFor(); output.join(); LOG.info("losetup " + loDevName + " " + absoluteFileName); LOG.info(output.getReturnValue()); LOG.info(error.getReturnValue()); return errorCode; } catch (Throwable t) { LOG.error(t); } return -1; } private String duplicateLogicalVolume(String oldLvName, String newLvName) throws ExecutionException { return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + oldLvName, "of=" + newLvName, "bs=" + StorageProperties.blockSize}); } private String createFile(String fileName, long size) throws ExecutionException { if(!DirectStorageInfo.getStorageInfo().getZeroFillVolumes()) return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=1", "bs=" + StorageProperties.blockSize, "seek=" + (size -1)}); else return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=" + size, "bs=" + StorageProperties.blockSize}); } private String createEmptyFile(String fileName, int size) throws ExecutionException { long fileSize = size * 1024; return createFile(fileName, fileSize); } public String createAbsoluteEmptyFile(String fileName, long size) throws ExecutionException { size = size / WalrusProperties.M; return createFile(fileName, size); } public void initialize() { if(!initialized) { System.loadLibrary("lvm2control"); registerSignals(); File storageRootDir = new File(getStorageRootDirectory()); if(!storageRootDir.exists()) { if(!storageRootDir.mkdirs()) { LOG.fatal("Unable to make volume root directory: " + getStorageRootDirectory()); } } initialized = true; } } public void configure() { exportManager.configure(); //First call to StorageInfo.getStorageInfo will add entity if it does not exist LOG.info(StorageInfo.getStorageInfo().getName()); checkVolumesDir(); } public void startupChecks() { reload(); } private void checkVolumesDir() { File volumeDir = new File(DirectStorageInfo.getStorageInfo().getVolumesDir()); if(!volumeDir.exists()) { if(!volumeDir.mkdirs()) { LOG.fatal("Unable to make volume root directory: " + DirectStorageInfo.getStorageInfo().getVolumesDir()); } } else if(!volumeDir.canWrite()) { LOG.fatal("Cannot write to volume root directory: " + DirectStorageInfo.getStorageInfo().getVolumesDir()); } } public void cleanVolume(String volumeId) { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo lvmVolInfo = volumeManager.getVolumeInfo(volumeId); if(lvmVolInfo != null) { String loDevName = lvmVolInfo.getLoDevName(); volumeManager.unexportVolume(lvmVolInfo); String vgName = lvmVolInfo.getVgName(); String lvName = lvmVolInfo.getLvName(); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; try { String returnValue = removeLogicalVolume(absoluteLVName); returnValue = removeVolumeGroup(vgName); returnValue = removePhysicalVolume(loDevName); removeLoopback(loDevName); } catch(ExecutionException ex) { volumeManager.abort(); String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); } volumeManager.remove(lvmVolInfo); File volFile = new File (DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + lvmVolInfo.getVolumeId()); if (volFile.exists()) { if(!volFile.delete()) { LOG.error("Unable to delete: " + volFile.getAbsolutePath() + " for failed volume"); } } } volumeManager.finish(); } public void cleanSnapshot(String snapshotId) { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo lvmVolInfo = volumeManager.getVolumeInfo(snapshotId); if(lvmVolInfo != null) { volumeManager.remove(lvmVolInfo); File volFile = new File (DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + lvmVolInfo.getVolumeId()); if (volFile.exists()) { if(!volFile.delete()) { LOG.error("Unable to delete: " + volFile.getAbsolutePath() + " for failed snapshot"); } } } volumeManager.finish(); } 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); } } } } public String createDuplicateLoopback(String oldRawFileName, String rawFileName) throws EucalyptusCloudException, ExecutionException { dupFile(oldRawFileName, rawFileName); return createLoopback(rawFileName); } public String createLoopback(String fileName, int size) throws EucalyptusCloudException, ExecutionException { createEmptyFile(fileName, size); if(!(new File(fileName).exists())) throw new EucalyptusCloudException("Unable to create file " + fileName); return createLoopback(fileName); } public synchronized String createLoopback(String fileName) throws EucalyptusCloudException, ExecutionException { int number_of_retries = 0; int status = -1; String loDevName; do { loDevName = findFreeLoopback(); if(loDevName.length() > 0) { status = losetup(fileName, loDevName); } if(number_of_retries++ >= MAX_LOOP_DEVICES) break; } while(status != 0); if(status != 0) { throw new EucalyptusCloudException("Could not create loopback device for " + fileName + ". Please check the max loop value and permissions"); } return loDevName; } public int createLoopback(String absoluteFileName, String loDevName) { return losetup(absoluteFileName, loDevName); } public String createLoopback(String fileName, long size) throws EucalyptusCloudException, ExecutionException { createAbsoluteEmptyFile(fileName, size); if(!(new File(fileName).exists())) throw new EucalyptusCloudException("Unable to create file " + fileName); return createLoopback(fileName); } //creates a logical volume (and a new physical volume and volume group) public void createLogicalVolume(String loDevName, String vgName, String lvName) throws EucalyptusCloudException, ExecutionException { String returnValue = createPhysicalVolume(loDevName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to create physical volume for " + loDevName); } returnValue = createVolumeGroup(loDevName, vgName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to create volume group " + vgName + " for " + loDevName); } returnValue = createLogicalVolume(vgName, lvName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to create logical volume " + lvName + " in volume group " + vgName); } } public void createSnapshotLogicalVolume(String loDevName, String vgName, String lvName, String snapLvName) throws EucalyptusCloudException, ExecutionException { String returnValue = createPhysicalVolume(loDevName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to create physical volume for " + loDevName); } returnValue = extendVolumeGroup(loDevName, vgName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to extend volume group " + vgName + " for " + loDevName); } returnValue = createSnapshotLogicalVolume(lvName, snapLvName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to create snapshot logical volume " + snapLvName + " for volume " + lvName); } } public void createVolume(String volumeId, int size) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); String vgName = "vg-" + Hashes.getRandom(4); String lvName = "lv-" + Hashes.getRandom(4); LVMVolumeInfo lvmVolumeInfo = null; if(exportManager instanceof AOEManager) { lvmVolumeInfo = new AOEVolumeInfo(); } else { lvmVolumeInfo = new ISCSIVolumeInfo(); } volumeManager.finish(); String rawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId; //create file and attach to loopback device long absoluteSize = size * StorageProperties.GB + LVM_HEADER_LENGTH; try { String loDevName = createLoopback(rawFileName, absoluteSize); //create physical volume, volume group and logical volume createLogicalVolume(loDevName, vgName, lvName); //export logical volume try { volumeManager.exportVolume(lvmVolumeInfo, vgName, lvName); } catch(EucalyptusCloudException ex) { LOG.error(ex); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; String returnValue = removeLogicalVolume(absoluteLVName); returnValue = removeVolumeGroup(vgName); returnValue = removePhysicalVolume(loDevName); removeLoopback(loDevName); throw ex; } lvmVolumeInfo.setVolumeId(volumeId); lvmVolumeInfo.setLoDevName(loDevName); lvmVolumeInfo.setPvName(loDevName); lvmVolumeInfo.setVgName(vgName); lvmVolumeInfo.setLvName(lvName); lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString()); lvmVolumeInfo.setSize(size); volumeManager = new VolumeEntityWrapperManager(); volumeManager.add(lvmVolumeInfo); volumeManager.finish(); } catch(ExecutionException ex) { String error = "Unable to run command: " + ex.getMessage(); volumeManager.abort(); LOG.error(error); throw new EucalyptusCloudException(error); } } public int createVolume(String volumeId, String snapshotId, int size) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo foundSnapshotInfo = volumeManager.getVolumeInfo(snapshotId); if(foundSnapshotInfo != null) { String status = foundSnapshotInfo.getStatus(); if(status.equals(StorageProperties.Status.available.toString())) { String vgName = "vg-" + Hashes.getRandom(4); String lvName = "lv-" + Hashes.getRandom(4); String loFileName = foundSnapshotInfo.getLoFileName(); String snapId = foundSnapshotInfo.getVolumeId(); LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo(); volumeManager.finish(); try { String rawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId; //create file and attach to loopback device File snapshotFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + snapId); assert(snapshotFile.exists()); long absoluteSize; if(size > 0) { absoluteSize = size * StorageProperties.GB + LVM_HEADER_LENGTH; } else { size = (int)(snapshotFile.length() / StorageProperties.GB); absoluteSize = snapshotFile.length() + LVM_HEADER_LENGTH; } String loDevName = createLoopback(rawFileName, absoluteSize); //create physical volume, volume group and logical volume createLogicalVolume(loDevName, vgName, lvName); //duplicate snapshot volume String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; duplicateLogicalVolume(loFileName, absoluteLVName); //export logical volume try { volumeManager.exportVolume(lvmVolumeInfo, vgName, lvName); } catch(EucalyptusCloudException ex) { String returnValue = removeLogicalVolume(absoluteLVName); returnValue = removeVolumeGroup(vgName); returnValue = removePhysicalVolume(loDevName); removeLoopback(loDevName); throw ex; } lvmVolumeInfo.setVolumeId(volumeId); lvmVolumeInfo.setLoDevName(loDevName); lvmVolumeInfo.setPvName(loDevName); lvmVolumeInfo.setVgName(vgName); lvmVolumeInfo.setLvName(lvName); lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString()); lvmVolumeInfo.setSize(size); volumeManager = new VolumeEntityWrapperManager(); volumeManager.add(lvmVolumeInfo); volumeManager.finish(); } catch(ExecutionException ex) { volumeManager.abort(); String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); throw new EucalyptusCloudException(error); } } } else { volumeManager.abort(); throw new EucalyptusCloudException("Unable to find snapshot: " + snapshotId); } return size; } public void addSnapshot(String snapshotId) throws EucalyptusCloudException { String snapshotRawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + snapshotId; File snapshotFile = new File(snapshotRawFileName); if(snapshotFile.exists()) { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); 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); } } public void deleteVolume(String volumeId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId); if(foundLVMVolumeInfo != null) { //remove aoe export String loDevName = foundLVMVolumeInfo.getLoDevName(); String vgName = foundLVMVolumeInfo.getVgName(); String lvName = foundLVMVolumeInfo.getLvName(); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; volumeManager.unexportVolume(foundLVMVolumeInfo); try { String returnValue = removeLogicalVolume(absoluteLVName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteLVName); } returnValue = removeVolumeGroup(vgName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to remove volume group " + vgName); } returnValue = removePhysicalVolume(loDevName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to remove physical volume " + loDevName); } returnValue = removeLoopback(loDevName); File rawFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId); if (rawFile.exists()) { if(!rawFile.delete()) { throw new EucalyptusCloudException("Unable to delete: " + rawFile.getAbsolutePath()); } } volumeManager.remove(foundLVMVolumeInfo); volumeManager.finish(); } catch(ExecutionException ex) { volumeManager.abort(); String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); throw new EucalyptusCloudException(error); } } else { volumeManager.abort(); throw new EucalyptusCloudException("Unable to find volume: " + volumeId); } } public List<String> createSnapshot(String volumeId, String snapshotId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId); ArrayList<String> returnValues = new ArrayList<String>(); if(foundLVMVolumeInfo != null) { LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo(); snapshotInfo.setVolumeId(snapshotId); String vgName = foundLVMVolumeInfo.getVgName(); String lvName = "lv-snap-" + Hashes.getRandom(4); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + foundLVMVolumeInfo.getLvName(); int size = foundLVMVolumeInfo.getSize(); long snapshotSize = (size * StorageProperties.GB) / 2; String rawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId + Hashes.getRandom(6); //create file and attach to loopback device volumeManager.finish(); volumeManager = null; try { String loDevName = createLoopback(rawFileName, snapshotSize); //create physical volume, volume group and logical volume createSnapshotLogicalVolume(loDevName, vgName, absoluteLVName, lvName); String snapRawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + snapshotId; String absoluteSnapLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; duplicateLogicalVolume(absoluteSnapLVName, snapRawFileName); String returnValue = removeLogicalVolume(absoluteSnapLVName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteSnapLVName); } returnValue = reduceVolumeGroup(vgName, loDevName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to reduce volume group " + vgName + " logical volume: " + loDevName); } returnValue = removePhysicalVolume(loDevName); if(returnValue.length() == 0) { throw new EucalyptusCloudException("Unable to remove physical volume " + loDevName); } returnValue = removeLoopback(loDevName); if(!(new File(rawFileName)).delete()) { LOG.error("Unable to remove temporary snapshot file: " + rawFileName); } snapshotInfo.setLoFileName(snapRawFileName); snapshotInfo.setStatus(StorageProperties.Status.available.toString()); snapshotInfo.setSize(size); returnValues.add(snapRawFileName); returnValues.add(String.valueOf(size * WalrusProperties.G)); volumeManager = new VolumeEntityWrapperManager(); volumeManager.add(snapshotInfo); volumeManager.finish(); } catch(ExecutionException ex) { if(volumeManager != null) volumeManager.abort(); String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); throw new EucalyptusCloudException(error); } } return returnValues; } public List<String> prepareForTransfer(String snapshotId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); 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; } public void deleteSnapshot(String snapshotId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(snapshotId); if(foundLVMVolumeInfo != null) { volumeManager.remove(foundLVMVolumeInfo); File snapFile = new File (DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + foundLVMVolumeInfo.getVolumeId()); if (snapFile.exists()) { if(!snapFile.delete()) { throw new EucalyptusCloudException("Unable to delete: " + snapFile.getAbsolutePath()); } } } else { volumeManager.abort(); throw new EucalyptusCloudException("Unable to find snapshot: " + snapshotId); } volumeManager.finish(); } public String getVolumeProperty(String volumeId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); String returnValue = volumeManager.getVolumeProperty(volumeId); volumeManager.finish(); return returnValue; } public void loadSnapshots(List<String> snapshotSet, List<String> snapshotFileNames) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); assert(snapshotSet.size() == snapshotFileNames.size()); int i = 0; for(String snapshotFileName: snapshotFileNames) { try { String loDevName = createLoopback(snapshotFileName); LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo(); lvmVolumeInfo.setVolumeId(snapshotSet.get(i++)); lvmVolumeInfo.setLoDevName(loDevName); lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString()); volumeManager.add(lvmVolumeInfo); } catch(ExecutionException ex) { volumeManager.abort(); String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); throw new EucalyptusCloudException(error); } } volumeManager.finish(); } public void reload() { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); List<LVMVolumeInfo> volumeInfos = volumeManager.getAllVolumeInfos(); for(LVMVolumeInfo foundVolumeInfo : volumeInfos) { String loDevName = foundVolumeInfo.getLoDevName(); if(loDevName != null) { String loFileName = foundVolumeInfo.getVolumeId(); String absoluteLoFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + loFileName; if(!new File(absoluteLoFileName).exists()) { LOG.error("Backing volume: " + absoluteLoFileName + " not found. Invalidating volume."); foundVolumeInfo.setStatus(StorageProperties.Status.failed.toString()); continue; } try { String returnValue = getLoopback(loDevName); if(returnValue.length() <= 0) { createLoopback(absoluteLoFileName, loDevName); } } catch(ExecutionException ex) { String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); } } } //now enable them try { scanVolumeGroups(); } catch (ExecutionException e) { LOG.error(e); } for(LVMVolumeInfo foundVolumeInfo : volumeInfos) { try { LOG.info("Scanning volume groups. This might take a little while..."); volumeManager.exportVolume(foundVolumeInfo); } catch(EucalyptusCloudException ex) { LOG.error("Unable to reload volume: " + foundVolumeInfo.getVolumeId() + ex); } } volumeManager.finish(); } public int getSnapshotSize(String snapshotId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo(snapshotId); if(lvmVolumeInfo != null) { int snapSize = lvmVolumeInfo.getSize(); volumeManager.finish(); return snapSize; } else { volumeManager.abort(); return 0; } } private String aoeStatus(int pid) { File file = new File("/proc/" + pid + "/cmdline"); String returnString = ""; if(file.exists()) { FileInputStream fileIn = null; try { fileIn = new FileInputStream(file); byte[] bytes = new byte[512]; int bytesRead; while((bytesRead = fileIn.read(bytes)) > 0) { returnString += new String(bytes, 0, bytesRead); } } catch (Exception ex) { LOG.warn("could not find " + file.getAbsolutePath()); } finally { if(fileIn != null) try { fileIn.close(); } catch (IOException e) { LOG.error(e); } } } return returnString; } private class VolumeEntityWrapperManager { private EntityWrapper entityWrapper; private VolumeEntityWrapperManager() { entityWrapper = StorageProperties.getEntityWrapper(); } public List<String> getSnapshotValues(String snapshotId) { ArrayList<String> returnValues = new ArrayList<String>(); LVMVolumeInfo lvmVolumeInfo = getVolumeInfo(snapshotId); if(lvmVolumeInfo instanceof AOEVolumeInfo) { returnValues.add(lvmVolumeInfo.getVgName()); returnValues.add(lvmVolumeInfo.getLvName()); } return returnValues; } public void exportVolume(LVMVolumeInfo lvmVolumeInfo) throws EucalyptusCloudException { if(exportManager instanceof AOEManager) { if(lvmVolumeInfo instanceof AOEVolumeInfo) { AOEVolumeInfo aoeVolumeInfo = (AOEVolumeInfo) lvmVolumeInfo; int pid = aoeVolumeInfo.getVbladePid(); if(pid > 0) { //enable logical volumes String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + aoeVolumeInfo.getVgName() + PATH_SEPARATOR + aoeVolumeInfo.getLvName(); try { enableLogicalVolume(absoluteLVName); } catch(ExecutionException ex) { String error = "Unable to run command: " + ex.getMessage(); LOG.error(error); return; } String returnValue = aoeStatus(pid); if(returnValue.length() == 0) { int majorNumber = aoeVolumeInfo.getMajorNumber(); int minorNumber = aoeVolumeInfo.getMinorNumber(); pid = exportManager.exportVolume(DirectStorageInfo.getStorageInfo().getStorageInterface(), absoluteLVName, majorNumber, minorNumber); aoeVolumeInfo.setVbladePid(pid); File vbladePidFile = new File(eucaHome + EUCA_VAR_RUN_PATH + "/vblade-" + majorNumber + minorNumber + ".pid"); FileOutputStream fileOutStream = null; try { fileOutStream = new FileOutputStream(vbladePidFile); String pidString = String.valueOf(pid); fileOutStream.write(pidString.getBytes()); fileOutStream.close(); } catch (Exception ex) { if(fileOutStream != null) try { fileOutStream.close(); } catch (IOException e) { LOG.error(e); } LOG.error("Could not write pid file vblade-" + majorNumber + minorNumber + ".pid"); } } } } else { //convert it AOEVolumeInfo volumeInfo = new AOEVolumeInfo(); convertVolumeInfo(lvmVolumeInfo, volumeInfo); try { unexportVolume(lvmVolumeInfo); exportVolume(volumeInfo, volumeInfo.getVgName(), volumeInfo.getLvName()); add(volumeInfo); remove(lvmVolumeInfo); } catch(EucalyptusCloudException ex) { LOG.error(ex); } } } else if(exportManager instanceof ISCSIManager) { if(lvmVolumeInfo instanceof ISCSIVolumeInfo) { ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo; String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + iscsiVolumeInfo.getVgName() + PATH_SEPARATOR + iscsiVolumeInfo.getLvName(); ((ISCSIManager)exportManager).exportTarget(iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getStoreName(), iscsiVolumeInfo.getLun(), absoluteLVName, iscsiVolumeInfo.getStoreUser()); } else { ISCSIVolumeInfo volumeInfo = new ISCSIVolumeInfo(); convertVolumeInfo(lvmVolumeInfo, volumeInfo); try { unexportVolume(lvmVolumeInfo); exportVolume(volumeInfo, volumeInfo.getVgName(), volumeInfo.getLvName()); add(volumeInfo); remove(lvmVolumeInfo); } catch(EucalyptusCloudException ex) { LOG.error(ex); } } } } private void convertVolumeInfo(LVMVolumeInfo lvmVolumeSource, LVMVolumeInfo lvmVolumeDestination) { lvmVolumeDestination.setScName(lvmVolumeSource.getScName()); lvmVolumeDestination.setLoFileName(lvmVolumeSource.getLoFileName()); lvmVolumeDestination.setLoDevName(lvmVolumeSource.getLoDevName()); lvmVolumeDestination.setLvName(lvmVolumeSource.getLvName()); lvmVolumeDestination.setVgName(lvmVolumeSource.getVgName()); lvmVolumeDestination.setPvName(lvmVolumeSource.getPvName()); lvmVolumeDestination.setSize(lvmVolumeSource.getSize()); lvmVolumeDestination.setSnapshotOf(lvmVolumeSource.getSnapshotOf()); lvmVolumeDestination.setStatus(lvmVolumeSource.getStatus()); lvmVolumeDestination.setVolumeId(lvmVolumeSource.getVolumeId()); } public String getVolumeProperty(String volumeId) { LVMVolumeInfo lvmVolumeInfo = getVolumeInfo(volumeId); if(lvmVolumeInfo != null) { if(exportManager instanceof AOEManager) { AOEVolumeInfo aoeVolumeInfo = (AOEVolumeInfo) lvmVolumeInfo; return StorageProperties.ETHERD_PREFIX + aoeVolumeInfo.getMajorNumber() + "." + aoeVolumeInfo.getMinorNumber(); } else if(exportManager instanceof ISCSIManager) { ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo; String storeName = iscsiVolumeInfo.getStoreName(); String encryptedPassword; try { encryptedPassword = ((ISCSIManager)exportManager).getEncryptedPassword(); } catch (EucalyptusCloudException e) { LOG.error(e); return null; } return System.getProperty("euca.home") + "," + StorageProperties.STORAGE_HOST + "," + storeName + "," + encryptedPassword; } } return null; } public void unexportVolume(LVMVolumeInfo volumeInfo) { StorageExportManager manager = exportManager; if(volumeInfo instanceof AOEVolumeInfo) { AOEVolumeInfo aoeVolumeInfo = (AOEVolumeInfo) volumeInfo; if(!(exportManager instanceof AOEManager)) { manager = new AOEManager(); } int pid = aoeVolumeInfo.getVbladePid(); if(pid > 0) { String returnValue = aoeStatus(pid); if(returnValue.length() > 0) { manager.unexportVolume(pid); int majorNumber = aoeVolumeInfo.getMajorNumber(); int minorNumber = aoeVolumeInfo.getMinorNumber(); File vbladePidFile = new File(eucaHome + EUCA_VAR_RUN_PATH + "/vblade-" + majorNumber + minorNumber + ".pid"); if(vbladePidFile.exists()) { vbladePidFile.delete(); } } } } else if(volumeInfo instanceof ISCSIVolumeInfo) { if(!(exportManager instanceof ISCSIManager)) { manager = new ISCSIManager(); } ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) volumeInfo; ((ISCSIManager)manager).unexportTarget(iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getLun()); } } private void finish() { entityWrapper.commit(); } private void abort() { entityWrapper.rollback(); } private LVMVolumeInfo getVolumeInfo(String volumeId) { if(exportManager instanceof AOEManager) { AOEVolumeInfo AOEVolumeInfo = new AOEVolumeInfo(volumeId); List<AOEVolumeInfo> AOEVolumeInfos = entityWrapper.query(AOEVolumeInfo); if(AOEVolumeInfos.size() > 0) { return AOEVolumeInfos.get(0); } } else if(exportManager instanceof ISCSIManager) { ISCSIVolumeInfo ISCSIVolumeInfo = new ISCSIVolumeInfo(volumeId); List<ISCSIVolumeInfo> ISCSIVolumeInfos = entityWrapper.query(ISCSIVolumeInfo); if(ISCSIVolumeInfos.size() > 0) { return ISCSIVolumeInfos.get(0); } } return null; } private LVMVolumeInfo getVolumeInfo() { if(exportManager instanceof AOEManager) { AOEVolumeInfo aoeVolumeInfo = new AOEVolumeInfo(); aoeVolumeInfo.setVbladePid(-1); aoeVolumeInfo.setMajorNumber(-1); aoeVolumeInfo.setMinorNumber(-1); return aoeVolumeInfo; } else if(exportManager instanceof ISCSIManager) { return new ISCSIVolumeInfo(); } return null; } private List<LVMVolumeInfo> getAllVolumeInfos() { List<LVMVolumeInfo> volumeInfos = new ArrayList<LVMVolumeInfo>(); volumeInfos.addAll(entityWrapper.query(new AOEVolumeInfo())); volumeInfos.addAll(entityWrapper.query(new ISCSIVolumeInfo())); return volumeInfos; } private void add(LVMVolumeInfo volumeInfo) { entityWrapper.add(volumeInfo); } private void remove(LVMVolumeInfo volumeInfo) { entityWrapper.delete(volumeInfo); } private String encryptTargetPassword(String password) throws EucalyptusCloudException { EntityWrapper<ClusterCredentials> credDb = Authentication.getEntityWrapper( ); try { ClusterCredentials credentials = credDb.getUnique( new ClusterCredentials( StorageProperties.NAME ) ); PublicKey ncPublicKey = X509Cert.toCertificate(credentials.getNodeCertificate()).getPublicKey(); credDb.commit(); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, ncPublicKey); return new String(Base64.encode(cipher.doFinal(password.getBytes()))); } catch ( Exception e ) { LOG.error( "Unable to encrypt storage target password" ); credDb.rollback( ); throw new EucalyptusCloudException(e.getMessage(), e); } } private int exportVolume(LVMVolumeInfo lvmVolumeInfo, String vgName, String lvName) throws EucalyptusCloudException { if(exportManager instanceof AOEManager) { AOEVolumeInfo aoeVolumeInfo = (AOEVolumeInfo) lvmVolumeInfo; exportManager.allocateTarget(aoeVolumeInfo); int majorNumber = aoeVolumeInfo.getMajorNumber(); int minorNumber = aoeVolumeInfo.getMinorNumber(); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; int pid = exportManager.exportVolume(DirectStorageInfo.getStorageInfo().getStorageInterface(), absoluteLVName, majorNumber, minorNumber); boolean success = false; String returnValue = ""; int timeout = 300; if(pid > 0) { for(int i=0; i < 3; ++i) { returnValue = aoeStatus(pid); if(returnValue.length() == 0) { success = false; } else { success = true; } try { Thread.sleep(timeout); } catch(InterruptedException ie) { LOG.error(ie); } } } if(!success) { throw new EucalyptusCloudException("Could not export AoE device " + absoluteLVName + " StorageInfo.getStorageInfo().getStorageInterface(): " + DirectStorageInfo.getStorageInfo().getStorageInterface() + " pid: " + pid + " returnValue: " + returnValue); } File vbladePidFile = new File(eucaHome + EUCA_VAR_RUN_PATH + "/vblade-" + majorNumber + minorNumber + ".pid"); FileOutputStream fileOutStream = null; try { fileOutStream = new FileOutputStream(vbladePidFile); String pidString = String.valueOf(pid); fileOutStream.write(pidString.getBytes()); } catch (Exception ex) { LOG.error("Could not write pid file vblade-" + majorNumber + minorNumber + ".pid"); } finally { if(fileOutStream != null) try { fileOutStream.close(); } catch (IOException e) { LOG.error(e); } } if(pid < 0) throw new EucalyptusCloudException("invalid vblade pid: " + pid); aoeVolumeInfo.setVbladePid(pid); return pid; } else if(exportManager instanceof ISCSIManager) { ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo; exportManager.allocateTarget(iscsiVolumeInfo); String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName; ((ISCSIManager)exportManager).exportTarget(iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getStoreName(), iscsiVolumeInfo.getLun(), absoluteLVName, iscsiVolumeInfo.getStoreUser()); } return 0; } } @Override public void finishVolume(String snapshotId) throws EucalyptusCloudException{ //Nothing to do here } @Override public String prepareSnapshot(String snapshotId, int sizeExpected) throws EucalyptusCloudException { 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)); } 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 e) { LOG.error(e, e); } } checkVolumesDir(); } @Override public String getStorageRootDirectory() { return DirectStorageInfo.getStorageInfo().getVolumesDir(); } @Override public String getVolumePath(String volumeId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); 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 importVolume(String volumeId, String volumePath, int size) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo volInfo = volumeManager.getVolumeInfo(volumeId); if(volInfo != null) { volumeManager.finish(); throw new EucalyptusCloudException("Volume " + volumeId + " already exists. Import failed."); } volumeManager.finish(); createVolume(volumeId, size); volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo volumeInfo = volumeManager.getVolumeInfo(volumeId); if(volumeInfo != null) { try { SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + volumePath, "of=" + lvmRootDirectory + File.separator + volumeInfo.getVgName() + File.separator + volumeInfo.getLvName(), "bs=" + StorageProperties.blockSize}); } catch (ExecutionException e) { LOG.error(e); throw new EucalyptusCloudException(e); } } else { volumeManager.abort(); throw new EucalyptusCloudException("Unable to find volume with id: " + volumeId); } } @Override public String getSnapshotPath(String snapshotId) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); 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 void importSnapshot(String snapshotId, String volumeId, String snapPath, int size) throws EucalyptusCloudException { VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager(); 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; try { SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + snapPath, "of=" + snapFileName, "bs=" + StorageProperties.blockSize}); } catch (ExecutionException e) { LOG.error(e); throw new EucalyptusCloudException(e); } volumeManager = new VolumeEntityWrapperManager(); LVMVolumeInfo snapshotInfo = volumeManager.getVolumeInfo(); snapshotInfo.setVolumeId(snapshotId); snapshotInfo.setLoFileName(snapFileName); snapshotInfo.setSize(size); snapshotInfo.setSnapshotOf(volumeId); volumeManager.add(snapshotInfo); volumeManager.finish(); } }