/*************************************************************************
* (c) Copyright 2017 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.
************************************************************************/
package com.eucalyptus.blockstorage.ceph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.annotation.Nonnull;
import org.apache.log4j.Logger;
import com.ceph.rbd.RbdException;
import com.ceph.rbd.RbdImage;
import com.ceph.rbd.jna.RbdSnapInfo;
import com.eucalyptus.blockstorage.ceph.entities.CephRbdInfo;
import com.eucalyptus.blockstorage.ceph.exceptions.CannotDeleteCephImageException;
import com.eucalyptus.blockstorage.ceph.exceptions.CephImageNotFoundException;
import com.eucalyptus.blockstorage.ceph.exceptions.EucalyptusCephException;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
/**
* Eucalyptus RBD adapter for managing Ceph RBD images of type Format 2
*/
public class CephRbdFormatTwoAdapter implements CephRbdAdapter {
private static final Logger LOG = Logger.getLogger(CephRbdFormatTwoAdapter.class);
private static Random randomGenerator = new Random();
// For EUCA-13254. Can remove and change messages to ERROR if EUCA-13243 gets resoloved.
private static final String dontPanic =
"This may be a known intermittent issue with connectivity to the Ceph " +
"cluster, and has no disruptive effects. " +
"Contact support only if this occurs repeatedly.";
private CephRbdInfo config;
public CephRbdFormatTwoAdapter(CephRbdInfo cephInfo) {
this.config = cephInfo;
}
// Added this method so that a running operation using an older configuration does not get impacted.
@Override
public void setCephConfig(CephRbdInfo cephInfo) {
this.config = cephInfo;
}
/**
* @return Returns Ceph representation of the image in the form: <b><code>pool/image</code></b>
*/
@Override
public String createImage(final String imageName, final long imageSize) {
LOG.info("Create ceph-rbd image imageName=" + imageName + ", imageSize=" + imageSize);
return executeRbdOpInRandomPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
// RbdImage image = null;
try {
LOG.trace("Creating format 2 image imageName=" + imageName + ", imageSize=" + imageSize + ", pool=" + arg0.getPool());
// We only want layering and format 2
int features = (1 << 0);
arg0.getRbd().create(imageName, imageSize, features, 0);
return arg0.getPool() + CephRbdInfo.POOL_IMAGE_DELIMITER + imageName;
} catch (RbdException e) {
LOG.warn("Caught error while creating image " + imageName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to create image " + imageName, e);
}
}
}, imageName);
}
@Override
public void deleteImage(final String imageName, final String poolName) {
LOG.debug("Delete ceph-rbd image imageName=" + imageName + ", poolName=" + poolName);
executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(CephRbdConnectionManager arg0) {
RbdImage image = null;
try {
LOG.trace("Opening image=" + imageName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(imageName);
boolean canBeDeleted = true;
List<String> snapChildren = null;
LOG.trace("Listing snapshots of image=" + imageName + ", pool=" + arg0.getPool());
List<RbdSnapInfo> snapList = image.snapList();
if (snapList != null && !snapList.isEmpty()) {
for (RbdSnapInfo snap : snapList) {
LOG.trace("Listing clones of snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
if ((snapChildren = image.listChildren(snap.name)) != null && !snapChildren.isEmpty()) {
LOG.trace("Found clones of snapshot=" + snap.name + ": " + snapChildren);
canBeDeleted = false;
break;
} else {
LOG.trace("No clones found for snapshot=" + snap.name);
if (image.snapIsProtected(snap.name)) {
LOG.trace("Unprotecting snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapUnprotect(snap.name);
}
LOG.info("Removing snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapRemove(snap.name);
continue;
}
}
} else {
// Nothing to do here
}
if (canBeDeleted) {
arg0.getRbd().close(image);
image = null;
LOG.info("Deleting image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().remove(imageName);
} else {
LOG.debug("Cannot delete image " + imageName + " in pool " + poolName
+ " as it may be associated with other images by a parent-child relationship");
throw new CannotDeleteCephImageException("Cannot delete image " + imageName + " in pool " + poolName
+ " as it may be associated with other images by a parent-child relationship");
}
return null;
} catch (RbdException e) {
LOG.warn("Caught error while checking and or deleting image " + imageName + " in pool " + poolName + ": " + e.getMessage());
throw new EucalyptusCephException("Caught error while checking and or deleting image " + imageName + " in pool " + poolName, e);
} finally {
if (image != null) {
try {
LOG.trace("Closing image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception closing image " + imageName, e);
}
}
}
}
}, poolName);
}
@Override
public List<String> cleanUpImages(final String poolName, final String imagePrefix, final List<String> toBeDeleted) {
LOG.trace("Cleanup RBD images poolName=" + poolName + ", prefix=" + imagePrefix);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, List<String>>() {
@Override
public List<String> apply(CephRbdConnectionManager arg0) {
List<String> imageNames = null;
List<String> deletedSnapshotNames = new ArrayList<String>();
try {
LOG.trace("Listing images in pool=" + poolName);
imageNames = Arrays.asList(arg0.getRbd().list());
} catch (RbdException e) {
LOG.info("Caught error while getting list of RBD images in pool " + poolName + ": " + e.getMessage() +
", return value = " + e.getReturnValue() + "\n" + dontPanic);
throw new EucalyptusCephException("Failed to get list of RBD images in pool " + poolName, e);
}
LOG.trace("Found " + imageNames.size() + " image(s) in pool=" + poolName);
for (String imageName : imageNames) {
if (imageName.startsWith(imagePrefix) || (toBeDeleted != null && toBeDeleted.contains(imageName))) {
LOG.debug("Image=" + imageName + ", pool=" + poolName + " marked for deletion, trying to clean it up");
RbdImage image = null;
try {
LOG.trace("Opening image=" + imageName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(imageName);
boolean canBeDeleted = true;
List<String> snapChildren = null;
LOG.trace("Listing snapshots of image=" + imageName + ", pool=" + arg0.getPool());
List<RbdSnapInfo> snapList = image.snapList();
if (snapList != null && !snapList.isEmpty()) {
for (RbdSnapInfo snap : snapList) {
LOG.trace("Listing clones of snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
if ((snapChildren = image.listChildren(snap.name)) != null && !snapChildren.isEmpty()) {
LOG.trace("Found clones of snapshot=" + snap.name + ": " + snapChildren);
canBeDeleted = false;
break;
} else {
LOG.trace("No clones found for snapshot=" + snap.name);
if (image.snapIsProtected(snap.name)) {
LOG.trace("Unprotecting snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapUnprotect(snap.name);
}
LOG.info("Removing snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapRemove(snap.name);
deletedSnapshotNames.add(snap.name);
continue;
}
}
} else {
// Nothing to do here
}
if (canBeDeleted) {
arg0.getRbd().close(image);
image = null;
LOG.info("Deleting image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().remove(imageName);
} else {
if (!imageName.startsWith(imagePrefix)) {
String newImageName = imagePrefix + imageName;
LOG.debug(
"Cannot delete image as it may be associated with other images by a parent-child relationship. Renaming for future delete attempts currentImageName="
+ imageName + ", newImageName=" + newImageName + ", poolName=" + poolName);
arg0.getRbd().rename(imageName, newImageName);
} else {
LOG.debug("Cannot delete image " + imageName + " in pool " + poolName
+ " as it may be associated with other images by a parent-child relationship. Will retry later");
}
}
} catch (RbdException e) {
LOG.debug("Caught RBD error while checking or deleting image " + imageName + " in pool " + poolName + ": "
+ e.getMessage() + ", return value = " + e.getReturnValue() + "\n" + dontPanic);
throw new EucalyptusCephException("Failed while checking or deleting image " + imageName + " in pool " + poolName, e);
} finally {
if (image != null) {
try {
LOG.trace("Closing image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception in image deletion while closing the image " + imageName, e);
}
}
}
} else {
// image does not start with prefix, don't delete
}
}
return deletedSnapshotNames;
}
}, poolName);
}
@Override
public SetMultimap<String, String> cleanUpSnapshots(final String poolName, final String imagePrefix, final SetMultimap<String, String> toBeDeleted) {
LOG.trace("Cleanup RBD snapshots poolName=" + poolName + ", prefix=" + imagePrefix);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, SetMultimap<String, String>>() {
@Override
public SetMultimap<String, String> apply(CephRbdConnectionManager arg0) {
try {
SetMultimap<String, String> cantBeDeleted = Multimaps.newSetMultimap(Maps.newHashMap(), new Supplier<Set<String>>() {
@Override
public Set<String> get() {
return Sets.newHashSet();
}
});
for (String imageName : toBeDeleted.keySet()) {
String originalImageName = imageName;
RbdImage image = null;
Set<String> snapNames = null;
if ((snapNames = toBeDeleted.get(imageName)) != null && !snapNames.isEmpty()) {
try {
LOG.trace("Opening image=" + imageName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(imageName);
} catch (RbdException e1) {
LOG.debug("Image " + imageName + " could not be opened. " +
"Trying again with to-be-deleted prefix " + imagePrefix);
String prefixedImageName = imagePrefix + imageName;
RbdImage prefixedImage = null;
try {
LOG.trace("Opening image=" + prefixedImageName + ", pool=" + arg0.getPool() + ", mode=read-write");
prefixedImage = arg0.getRbd().open(prefixedImageName);
LOG.debug("Image " + prefixedImageName + " opened successfully.");
originalImageName = imageName;
imageName = prefixedImageName;
image = prefixedImage;
} catch (RbdException e2) {
LOG.debug("Caught RBD error trying to open image " + prefixedImageName + " in pool " + poolName + ": " + e2.getMessage() +
", return value = " + e2.getReturnValue());
throw new EucalyptusCephException("Failed while opening image " + imageName + " or " + prefixedImageName +
" in pool " + poolName, e2);
}
}
try {
for (String snapName : snapNames) {
LOG.debug(
"RBD snapshot=" + snapName + ", image=" + imageName + ", pool=" + poolName + " marked for deletion, trying to clean it up");
List<String> snapChildren = null;
LOG.trace("Listing clones of RBD snapshot=" + snapName + ", image=" + imageName + ", pool=" + arg0.getPool());
if ((snapChildren = image.listChildren(snapName)) == null || snapChildren.isEmpty()) {
LOG.trace("No clones found for RBD snapshot=" + snapName);
if (image.snapIsProtected(snapName)) {
LOG.trace("Unprotecting RBD snapshot=" + snapName + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapUnprotect(snapName);
}
LOG.debug("Removing RBD snapshot=" + snapName + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapRemove(snapName);
} else {
LOG.debug("Cannot delete RBD snapshot=" + snapName + ", image=" + imageName + ", pool=" + poolName
+ " as it may be a parent to other images");
// Add the non-prefixed image name to the list we send back to the caller.
cantBeDeleted.put(originalImageName, snapName);
}
}
} catch (RbdException e) {
LOG.debug("Caught RBD error deleting snapshots on image " + imageName + " in pool " + poolName + ": " + e.getMessage() +
", return value = " + e.getReturnValue());
throw new EucalyptusCephException("Failed while deleting snapshots on image " + imageName + " in pool " + poolName, e);
} finally {
if (image != null) {
try {
LOG.debug("Closing image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception in RBD snapshot deletion while closing the image " + imageName, e);
}
}
}
} else {
// no snaps to be deleted for this image, nothing to see here, move on!
LOG.trace("No input RBD snapshots to be deleted on image=" + imageName);
}
}
return cantBeDeleted;
} catch (Exception e) {
LOG.debug("Caught error while deleting RBD snapshots in pool " + poolName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to delete RBD snapshots in pool " + poolName, e);
}
}
}, poolName);
}
@Override
public void renameImage(final String imageName, final String newImageName, final String poolName) {
LOG.info("Rename ceph-rbd image currentImageName=" + imageName + ", newImageName=" + newImageName + ", poolName=" + poolName);
executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
try {
LOG.debug("Renaming image=" + imageName + ", pool=" + arg0.getPool() + " to image=" + newImageName);
arg0.getRbd().rename(imageName, newImageName);
return null;
} catch (RbdException e) {
LOG.warn("Caught error while renaming image " + imageName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to rename image " + imageName, e);
}
}
}, poolName);
}
@Override
public String getImagePool(final String imageName) {
LOG.debug("Get ceph-rbd image pool imageName=" + imageName);
return findAndExecuteRbdOp(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
return arg0.getPool();
}
}, imageName);
}
/**
* @return Returns Ceph representation of the snapshot in the form: <b><code>pool/image@snapshot</code></b>
*/
@Override
public String createSnapshot(final String parentName, final String snapName, final String parentPoolName) {
LOG.info("Create ceph-rbd snapshot on image parentName=" + parentName + ", snapName=" + snapName + ", parentPoolName=" + parentPoolName);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
RbdImage image = null;
try {
LOG.trace("Opening image=" + parentName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(parentName);
LOG.debug("Creating snapshot=" + snapName + ", image=" + parentName + ", pool=" + arg0.getPool());
image.snapCreate(snapName);
LOG.debug("Protecting snapshot=" + snapName + ", image=" + parentName + ", pool=" + arg0.getPool());
image.snapProtect(snapName);
return arg0.getPool() + CephRbdInfo.POOL_IMAGE_DELIMITER + parentName + CephRbdInfo.IMAGE_SNAPSHOT_DELIMITER + snapName;
} catch (Exception e) {
LOG.warn("Caught error while creating snapshot " + snapName + " on parent " + parentName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to create snapshot " + snapName + " on parent " + parentName, e);
} finally {
if (image != null) {
try {
LOG.trace("Closing image=" + parentName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception closing image " + parentName, e);
}
}
}
}
}, parentPoolName);
}
@Override
public void deleteSnapshot(final String parentName, final String snapName, final String parentPoolName) {
LOG.info("Delete ceph-rbd snapshot on image parentName=" + parentName + ", snapName=" + snapName + ", parentPoolName=" + parentPoolName);
executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
RbdImage image = null;
try {
LOG.trace("Opening image=" + parentName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(parentName);
if (image.snapIsProtected(snapName)) {
LOG.debug("Unprotecting snapshot=" + snapName + ", image=" + parentName + ", pool=" + arg0.getPool());
image.snapUnprotect(snapName);
}
LOG.debug("Removing snapshot=" + snapName + ", image=" + parentName + ", pool=" + arg0.getPool());
image.snapRemove(snapName);
return null;
} catch (Exception e) {
LOG.warn("Caught error while deleting snapshot " + snapName + " on parent " + parentName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to delete snapshot " + snapName + " on parent " + parentName, e);
} finally {
if (image != null) {
try {
LOG.trace("Closing image=" + parentName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception closing image " + parentName, e);
}
}
}
}
}, parentPoolName);
}
@Override
public String deleteAllSnapshots(final String imageName, final String poolName, final String snapName) {
LOG.debug("Delete ceph-rbd snapshots on imageName=" + imageName + ", poolName=" + poolName);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(@Nonnull CephRbdConnectionManager arg0) {
RbdImage image = null;
try {
LOG.trace("Opening image=" + imageName + ", pool=" + arg0.getPool() + ", mode=read-write");
image = arg0.getRbd().open(imageName);
LOG.trace("Listing snapshots of image=" + imageName + ", pool=" + arg0.getPool());
List<RbdSnapInfo> snapList = image.snapList();
if (snapList != null && !snapList.isEmpty()) {
for (RbdSnapInfo snap : snapList) {
if (image.snapIsProtected(snap.name)) {
LOG.trace("Unprotecting snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapUnprotect(snap.name);
}
LOG.info("Removing snapshot=" + snap.name + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapRemove(snap.name);
}
}
if (snapName != null && !snapName.isEmpty()) {
LOG.debug("Creating snapshot=" + snapName + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapCreate(snapName);
LOG.debug("Protecting snapshot=" + snapName + ", image=" + imageName + ", pool=" + arg0.getPool());
image.snapProtect(snapName);
return arg0.getPool() + CephRbdInfo.POOL_IMAGE_DELIMITER + imageName + CephRbdInfo.IMAGE_SNAPSHOT_DELIMITER + snapName;
} else {
return null;
}
} catch (Exception e) {
LOG.warn("Caught error while deleting snapshots on image " + imageName + ": " + e.getMessage());
throw new EucalyptusCephException("Caught error while deleting snapshots on image " + imageName, e);
} finally {
if (image != null) {
try {
LOG.trace("Closing image=" + imageName + ", pool=" + arg0.getPool());
arg0.getRbd().close(image);
} catch (Exception e) {
LOG.debug("Caught exception closing image " + imageName, e);
}
}
}
}
}, poolName);
}
/**
* <p>
* In its current state, following is the order of steps. Please document if any of the steps change
* </p>
*
* <ol>
* <li>Protect snapshot if its not protected</li>
* <li>Clone parent using snapshot</li>
* <li>Resize clone if necessary</li>
* <li>If cloned image is an EBS snapshot, create snapshot on cloned image and protect it</li>
* </ol>
*
* @return Returns Ceph representation of the image in the form: <b><code>pool/image</code></b>
*/
@Override
public String cloneAndResizeImage(final String parentName, final String snapName, final String cloneName, final Long size,
final String parentPoolName) {
LOG.debug("Clone (and resize) ceph-rbd image parentName=" + parentName + ", snapName=" + snapName + ", cloneName=" + cloneName + ", size=" + size
+ ", parentPoolName=" + parentPoolName);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(final CephRbdConnectionManager parentConn) {
try {
return executeRbdOpInRandomPool(new Function<CephRbdConnectionManager, String>() {
@Override
public String apply(CephRbdConnectionManager cloneConn) {
RbdImage clone = null;
try {
// We only want layering and format 2
int features = (1 << 0);
LOG.info("Cloning snapshot=" + snapName + ", image=" + parentName + ", pool=" + parentConn.getPool() + " to image=" + cloneName
+ ", pool=" + cloneConn.getPool());
parentConn.getRbd().clone(parentName, snapName, cloneConn.getIoContext(), cloneName, features, 0);
if (size != null) {
// Open the cloned image only if it has to be resized
LOG.trace("Opening image=" + cloneName + ", pool=" + cloneConn.getPool() + ", mode=read-write");
clone = cloneConn.getRbd().open(cloneName);
LOG.info("Resizing image=" + cloneName + ", pool=" + cloneConn.getPool() + " to " + size + " bytes");
clone.resize(size.longValue());
} else {
// nothing to do here
}
return cloneConn.getPool() + CephRbdInfo.POOL_IMAGE_DELIMITER + cloneName;
} catch (RbdException e) {
LOG.warn("Caught error while cloning/resizing image " + cloneName + " from source image " + parentName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to clone/resize image " + cloneName + " from source image " + parentName, e);
} finally {
if (clone != null) {
try {
LOG.trace("Closing image=" + cloneName + ", pool=" + cloneConn.getPool());
cloneConn.getRbd().close(clone);
} catch (Exception e) {
LOG.debug("Caught exception closing image " + cloneName, e);
}
}
}
}
}, cloneName);
} catch (EucalyptusCephException e) {
throw e;
} catch (Exception e) {
LOG.warn("Caught error while cloning/resizing image " + cloneName + " from source image " + parentName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to clone/resize image " + cloneName + " from source image " + parentName, e);
}
}
}, parentPoolName);
}
@Override
public List<String> listPool(final String poolName) {
LOG.debug("List ceph-rbd pool poolName=" + poolName);
return executeRbdOpInPool(new Function<CephRbdConnectionManager, List<String>>() {
@Override
public List<String> apply(CephRbdConnectionManager arg0) {
try {
LOG.trace("Listing images in pool=" + poolName);
return Arrays.asList(arg0.getRbd().list());
} catch (RbdException e) {
LOG.warn("Caught error while listing images in pool " + poolName + ": " + e.getMessage());
throw new EucalyptusCephException("Failed to list images in pool " + poolName, e);
}
}
}, poolName);
}
private <T> T executeRbdOpInPool(Function<CephRbdConnectionManager, T> function, String poolName) {
try (CephRbdConnectionManager conn = CephRbdConnectionManager.getConnection(config, poolName)) {
return function.apply(conn);
} catch (EucalyptusCephException e) {
throw e;
} catch (Exception e) {
throw new EucalyptusCephException("Caught error during ceph operation" + e);
}
}
private <T> T executeRbdOpInRandomPool(Function<CephRbdConnectionManager, T> function, String imageName) {
try {
String[] allPools = null;
String poolName = null;
if (imageName.contains("vol-")) {
allPools = config.getAllVolumePools();
} else {
allPools = config.getAllSnapshotPools();
}
poolName = allPools[randomGenerator.nextInt(allPools.length)];
return executeRbdOpInPool(function, poolName);
} catch (EucalyptusCephException e) {
throw e;
} catch (Exception e) {
throw new EucalyptusCephException("Caught error during ceph operation" + e);
}
}
private <T> T findAndExecuteRbdOp(Function<CephRbdConnectionManager, T> function, String imageName) {
try {
String[] allPools = null;
if (imageName.contains("vol-")) {
allPools = config.getAllVolumePools();
} else {
allPools = config.getAllSnapshotPools();
}
for (int i = 0; i < allPools.length; i++) {
String poolName = allPools[i];
try (CephRbdConnectionManager conn = CephRbdConnectionManager.getConnection(config, poolName)) {
LOG.debug("Searching for image=" + imageName + ", pool=" + poolName);
RbdImage image = conn.getRbd().openReadOnly(imageName);
conn.getRbd().close(image);
return function.apply(conn);
} catch (Exception e) {
LOG.trace("Image=" + imageName + " not found in pool=" + allPools[i] + ". Reason: " + e.getMessage());
}
}
// Throw an error if the image was not found
throw new CephImageNotFoundException("Unable to find image " + imageName + " in configured pools: " + Arrays.toString(allPools));
} catch (EucalyptusCephException e) {
throw e;
} catch (Exception e) {
throw new EucalyptusCephException("Caught error during ceph operation" + e);
}
}
}