/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.ceph; import java.util.ArrayList; import java.util.List; import com.ceph.rados.IoCTX; import com.ceph.rados.Rados; import com.ceph.rados.exceptions.ErrorCode; import com.ceph.rados.exceptions.RadosException; import com.ceph.rados.exceptions.RadosInvalidArgumentException; import com.ceph.rados.exceptions.RadosPermissionException; import com.ceph.rados.jna.RadosClusterInfo; import com.ceph.rbd.jna.RbdSnapInfo; import com.ceph.rbd.Rbd; import com.ceph.rbd.RbdException; import com.ceph.rbd.RbdImage; import com.emc.storageos.ceph.model.ClusterInfo; import com.emc.storageos.ceph.model.PoolInfo; import com.emc.storageos.ceph.model.SnapInfo; public class CephNativeClient implements CephClient { private static final long LAYERING = 1; private Rados rados; public CephNativeClient(final String monitorHost, final String userName, final String userKey) { Rados rados_tmp = new Rados(userName); try { rados_tmp.confSet("mon_host", monitorHost); rados_tmp.confSet("key", userKey); rados_tmp.connect(); rados = rados_tmp; } catch (RadosPermissionException | RadosInvalidArgumentException e) { throw CephException.exceptions.invalidCredentialsError(e); } catch (RadosException e) { throw CephException.exceptions.connectionError(e); } finally { if (rados == null) { rados_tmp.shutDown(); } } } private interface RadosOperationT<T> { public abstract T call() throws RadosException, RbdException; } private interface RbdOperation { public abstract void call(Rbd rbd) throws RadosException, RbdException; } private interface RbdOperationT<T> { public abstract T call(Rbd rbd) throws RadosException, RbdException; } private interface RbdImageOperationT<T> { public abstract T call(RbdImage image) throws RadosException, RbdException; } private interface RbdImageOperation { public abstract void call(RbdImage image) throws RadosException, RbdException; } private static String convertErrorMessage(RbdException e, String errorMsg, final Object... errorMsgArgs) { int errorCode = e.getReturnValue(); String errorName = ErrorCode.getErrorName(errorCode); String errorMessage = ErrorCode.getErrorMessage(errorCode); return String.format("%s; %s: %s", String.format(errorMsg, errorMsgArgs), errorName, errorMessage); } private <T> T doCall(final RadosOperationT<T> op, final String errorMsg, final Object... errorMsgArgs) { try { return op.call(); } catch (RbdException e) { throw CephException.exceptions.operationException(convertErrorMessage(e, errorMsg, errorMsgArgs)); } catch (RadosException e) { throw CephException.exceptions.operationException(e); } } private <T> T doCall(final String pool, final RbdOperationT<T> rbdOp, final String errorMsg, final Object... errorMsgArgs) { RadosOperationT<T> op = () -> { IoCTX ioCtx = rados.ioCtxCreate(pool); try { Rbd rbd = new Rbd(ioCtx); return rbdOp.call(rbd); } finally { rados.ioCtxDestroy(ioCtx); } }; return doCall(op, errorMsg, errorMsgArgs); } private void doCall(final String pool, final RbdOperation rbdOp, final String errorMsg, final Object... errorMsgArgs) { RbdOperationT<Object> op = (Rbd rbd) -> { rbdOp.call(rbd); return null; }; doCall(pool, op, errorMsg, errorMsgArgs); } private <T> T doCall(final String pool, final String imageName, final RbdImageOperationT<T> rbdImageOp, final String errorMsg, final Object... errorMsgArgs) { RbdOperationT<T> op = (Rbd rbd) -> { RbdImage image = rbd.open(imageName); try { return rbdImageOp.call(image); } finally { rbd.close(image); } }; return doCall(pool, op, errorMsg, errorMsgArgs); } private void doCall(final String pool, final String imageName, final RbdImageOperation rbdImageOp, final String errorMsg, final Object... errorMsgArgs) { RbdImageOperationT<Object> op = (RbdImage image) -> { rbdImageOp.call(image); return null; }; doCall(pool, imageName, op, errorMsg, errorMsgArgs); } @Override public ClusterInfo getClusterInfo() { RadosOperationT<ClusterInfo> op = () -> { ClusterInfo info = new ClusterInfo(); info.setFsid(rados.clusterFsid()); RadosClusterInfo stat = rados.clusterStat(); info.setKb(stat.kb); info.setKbAvail(stat.kb_avail); return info; }; return doCall(op, "Failed to get Ceph cluster info"); } @Override public List<PoolInfo> getPools() { RadosOperationT<List<PoolInfo>> op = () -> { List<PoolInfo> pools = new ArrayList<>(); String[] poolNames = rados.poolList(); for (String poolName : poolNames) { PoolInfo poolInfo = new PoolInfo(); poolInfo.setName(poolName); poolInfo.setId(rados.poolLookup(poolName)); pools.add(poolInfo); } return pools; }; return doCall(op, "Failed to get Ceph pools"); } @Override public void createImage(String pool, final String name, final long size) { doCall(pool, (Rbd rbd) -> rbd.create(name, size, LAYERING), "Failed to create Ceph image %s/%s with size %s", pool, name, size); } @Override public void deleteImage(String pool, final String name) { doCall(pool, (Rbd rbd) -> rbd.remove(name), "Failed to delete Ceph image %s/%s", pool, name); } @Override public void resizeImage(String pool, String name, final long size) { doCall(pool, name, (RbdImage image) -> image.resize(size), "Failed to resize Ceph image %s/%s to size %s", pool, name, size); } @Override public void createSnap(String pool, String imageName, final String snapName) { doCall(pool, imageName, (RbdImage image) -> image.snapCreate(snapName), "Failed to create snapshot %s for Ceph image %s/%s", snapName, pool, imageName); } @Override public void deleteSnap(String pool, String imageName, final String snapName) { doCall(pool, imageName, (RbdImage image) -> image.snapRemove(snapName), "Failed to remove Ceph snapshot %s/%s@%s", pool, imageName, snapName); } @Override public void cloneSnap(final String pool, final String parentImage, final String parentSnap, final String childName) { RbdOperation op = (Rbd rbd) -> { IoCTX childIOCtx = rados.ioCtxCreate(pool); try { long features = LAYERING; int order = 0; rbd.clone(parentImage, parentSnap, childIOCtx, childName, features, order); } finally { rados.ioCtxDestroy(childIOCtx); } }; doCall(pool, op, "Failed to clone Ceph snapshot %s/%s@%s with name %s", pool, parentImage, parentSnap, childName); } @Override public void protectSnap(String pool, String parentImage, final String snapName) { doCall(pool, parentImage, (RbdImage image) -> image.snapProtect(snapName), "Failed to protect Ceph snapshot %s/%s@%s", pool, parentImage, snapName); } @Override public void unprotectSnap(String pool, String parentImage, final String snapName) { doCall(pool, parentImage, (RbdImage image) -> image.snapUnprotect(snapName), "Failed to unprotect Ceph snapshot %s/%s@%s", pool, parentImage, snapName); } @Override public boolean snapIsProtected(String pool, String parentImage, final String snapName) { return doCall(pool, parentImage, (RbdImage image) -> image.snapIsProtected(snapName), "Failed to get is_protected status for Ceph snapshot %s/%s@%s", pool, parentImage, snapName); } @Override public void flattenImage(String pool, String imageName) { doCall(pool, imageName, (RbdImage image) -> image.flatten(), "Failed to flatten Ceph image %s/%s", pool, imageName); } @Override public List<SnapInfo> getSnapshots(String pool, String imageName) { RbdImageOperationT<List<SnapInfo>> op = (RbdImage image) -> { List<SnapInfo> result = new ArrayList<>(); List<RbdSnapInfo> snapList = image.snapList(); for (RbdSnapInfo snap : snapList) { SnapInfo snapInfo = new SnapInfo(); snapInfo.setId(snap.id); snapInfo.setName(snap.name); result.add(snapInfo); } return result; }; return doCall(pool, imageName, op, "Failed to list snapshots for Ceph image %s/%s", pool, imageName); } @Override public List<String> getChildren(String pool, String parentImage, final String snapName) { return doCall(pool, parentImage, (RbdImage image) -> image.listChildren(snapName), "Failed to list children for Ceph snapshot %s/%s@%s", pool, parentImage, snapName); } @Override public void close() throws Exception { if (rados != null) { rados.shutDown(); } } }