mirror of https://github.com/apache/cloudstack.git
Add support to RBD erasure code pools (#9808)
* Readd filename string on qemuimg create * Remove empty object on the data pool details of storage pools with no data pool * Only use the method createPhysicalDiskByLibVirt with RBD when the pool is of erasure code type. Also added javadoc for createPhysicalDisk method * Change literal '/' string to File.separator * Add support for erasure code pools * Fix null on putAll
This commit is contained in:
parent
2dfe6a6333
commit
cb4848bc1a
|
|
@ -149,6 +149,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations {
|
||||||
@Param(description = "whether this pool is managed or not")
|
@Param(description = "whether this pool is managed or not")
|
||||||
private Boolean managed;
|
private Boolean managed;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DETAILS)
|
||||||
|
@Param(description = "the storage pool details")
|
||||||
|
private Map<String, String> details;
|
||||||
|
|
||||||
public Map<String, String> getCaps() {
|
public Map<String, String> getCaps() {
|
||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
@ -407,4 +411,12 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations {
|
||||||
public void setManaged(Boolean managed) {
|
public void setManaged(Boolean managed) {
|
||||||
this.managed = managed;
|
this.managed = managed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetails(Map<String, String> details) {
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ import com.cloud.storage.StoragePool;
|
||||||
import com.cloud.storage.StoragePoolHostVO;
|
import com.cloud.storage.StoragePoolHostVO;
|
||||||
import com.cloud.storage.StorageService;
|
import com.cloud.storage.StorageService;
|
||||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||||
import com.cloud.utils.Pair;
|
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||||
|
|
@ -60,6 +59,7 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class DefaultHostListener implements HypervisorHostListener {
|
public class DefaultHostListener implements HypervisorHostListener {
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
@ -133,9 +133,11 @@ public class DefaultHostListener implements HypervisorHostListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean hostConnect(long hostId, long poolId) throws StorageConflictException {
|
public boolean hostConnect(long hostId, long poolId) throws StorageConflictException {
|
||||||
StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
|
StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
|
||||||
Pair<Map<String, String>, Boolean> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null);
|
Map<String, String> detailsMap = storagePoolDetailsDao.listDetailsKeyPairs(poolId);
|
||||||
|
Map<String, String> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null).first();
|
||||||
|
|
||||||
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, nfsMountOpts.first());
|
Optional.ofNullable(nfsMountOpts).ifPresent(detailsMap::putAll);
|
||||||
|
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, detailsMap);
|
||||||
cmd.setWait(modifyStoragePoolCommandWait);
|
cmd.setWait(modifyStoragePoolCommandWait);
|
||||||
HostVO host = hostDao.findById(hostId);
|
HostVO host = hostDao.findById(hostId);
|
||||||
logger.debug("Sending modify storage pool command to agent: {} for storage pool: {} with timeout {} seconds", host, pool, cmd.getWait());
|
logger.debug("Sending modify storage pool command to agent: {} for storage pool: {} with timeout {} seconds", host, pool, cmd.getWait());
|
||||||
|
|
|
||||||
|
|
@ -108,9 +108,7 @@ public final class LibvirtCreatePrivateTemplateFromVolumeCommandWrapper extends
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Converting RBD disk " + disk.getPath() + " into template " + command.getUniqueName());
|
logger.debug("Converting RBD disk " + disk.getPath() + " into template " + command.getUniqueName());
|
||||||
|
|
||||||
final QemuImgFile srcFile =
|
final QemuImgFile srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary, disk.getPath()));
|
||||||
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(),
|
|
||||||
primary.getAuthSecret(), disk.getPath()));
|
|
||||||
srcFile.setFormat(PhysicalDiskFormat.RAW);
|
srcFile.setFormat(PhysicalDiskFormat.RAW);
|
||||||
|
|
||||||
final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + command.getUniqueName() + ".qcow2");
|
final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + command.getUniqueName() + ".qcow2");
|
||||||
|
|
|
||||||
|
|
@ -161,11 +161,7 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
|
||||||
QemuImg qemu = new QemuImg(0);
|
QemuImg qemu = new QemuImg(0);
|
||||||
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
||||||
if (StoragePoolType.RBD.equals(pool.getType())) {
|
if (StoragePoolType.RBD.equals(pool.getType())) {
|
||||||
String rbdDestFile = KVMPhysicalDisk.RBDStringBuilder(pool.getSourceHost(),
|
String rbdDestFile = KVMPhysicalDisk.RBDStringBuilder(pool, disk.getPath());
|
||||||
pool.getSourcePort(),
|
|
||||||
pool.getAuthUserName(),
|
|
||||||
pool.getAuthSecret(),
|
|
||||||
disk.getPath());
|
|
||||||
qemuFile = new QemuImgFile(rbdDestFile, disk.getFormat());
|
qemuFile = new QemuImgFile(rbdDestFile, disk.getFormat());
|
||||||
}
|
}
|
||||||
return qemu.info(qemuFile, secure);
|
return qemu.info(qemuFile, secure);
|
||||||
|
|
|
||||||
|
|
@ -410,9 +410,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
|
||||||
KVMStoragePool srcPool = srcDisk.getPool();
|
KVMStoragePool srcPool = srcDisk.getPool();
|
||||||
|
|
||||||
if (srcPool.getType() == StoragePoolType.RBD) {
|
if (srcPool.getType() == StoragePoolType.RBD) {
|
||||||
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), srcPool.getSourcePort(),
|
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool, srcDisk.getPath()), srcDisk.getFormat());
|
||||||
srcPool.getAuthUserName(), srcPool.getAuthSecret(),
|
|
||||||
srcDisk.getPath()),srcDisk.getFormat());
|
|
||||||
} else {
|
} else {
|
||||||
srcFile = new QemuImgFile(srcDisk.getPath(), srcDisk.getFormat());
|
srcFile = new QemuImgFile(srcDisk.getPath(), srcDisk.getFormat());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class KVMPhysicalDisk {
|
public class KVMPhysicalDisk {
|
||||||
private String path;
|
private String path;
|
||||||
|
|
@ -32,10 +33,17 @@ public class KVMPhysicalDisk {
|
||||||
private String vmName;
|
private String vmName;
|
||||||
private boolean useAsTemplate;
|
private boolean useAsTemplate;
|
||||||
|
|
||||||
public static String RBDStringBuilder(String monHost, int monPort, String authUserName, String authSecret, String image) {
|
public static final String RBD_DEFAULT_DATA_POOL = "rbd_default_data_pool";
|
||||||
String rbdOpts;
|
|
||||||
|
|
||||||
rbdOpts = "rbd:" + image;
|
public static String RBDStringBuilder(KVMStoragePool storagePool, String image) {
|
||||||
|
String monHost = storagePool.getSourceHost();
|
||||||
|
int monPort = storagePool.getSourcePort();
|
||||||
|
String authUserName = storagePool.getAuthUserName();
|
||||||
|
String authSecret = storagePool.getAuthSecret();
|
||||||
|
Map<String, String> details = storagePool.getDetails();
|
||||||
|
String dataPool = (details == null) ? null : details.get(RBD_DEFAULT_DATA_POOL);
|
||||||
|
|
||||||
|
String rbdOpts = "rbd:" + image;
|
||||||
rbdOpts += ":mon_host=" + composeOptionForMonHosts(monHost, monPort);
|
rbdOpts += ":mon_host=" + composeOptionForMonHosts(monHost, monPort);
|
||||||
|
|
||||||
if (authUserName == null) {
|
if (authUserName == null) {
|
||||||
|
|
@ -46,6 +54,10 @@ public class KVMPhysicalDisk {
|
||||||
rbdOpts += ":key=" + authSecret;
|
rbdOpts += ":key=" + authSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dataPool != null) {
|
||||||
|
rbdOpts += String.format(":rbd_default_data_pool=%s", dataPool);
|
||||||
|
}
|
||||||
|
|
||||||
rbdOpts += ":rbd_default_format=2";
|
rbdOpts += ":rbd_default_format=2";
|
||||||
rbdOpts += ":client_mount_timeout=30";
|
rbdOpts += ":client_mount_timeout=30";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,28 +53,6 @@ import com.cloud.vm.VirtualMachine;
|
||||||
public class KVMStoragePoolManager {
|
public class KVMStoragePoolManager {
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
private class StoragePoolInformation {
|
|
||||||
String name;
|
|
||||||
String host;
|
|
||||||
int port;
|
|
||||||
String path;
|
|
||||||
String userInfo;
|
|
||||||
boolean type;
|
|
||||||
StoragePoolType poolType;
|
|
||||||
Map<String, String> details;
|
|
||||||
|
|
||||||
public StoragePoolInformation(String name, String host, int port, String path, String userInfo, StoragePoolType poolType, Map<String, String> details, boolean type) {
|
|
||||||
this.name = name;
|
|
||||||
this.host = host;
|
|
||||||
this.port = port;
|
|
||||||
this.path = path;
|
|
||||||
this.userInfo = userInfo;
|
|
||||||
this.type = type;
|
|
||||||
this.poolType = poolType;
|
|
||||||
this.details = details;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private KVMHAMonitor _haMonitor;
|
private KVMHAMonitor _haMonitor;
|
||||||
private final Map<String, StoragePoolInformation> _storagePools = new ConcurrentHashMap<String, StoragePoolInformation>();
|
private final Map<String, StoragePoolInformation> _storagePools = new ConcurrentHashMap<String, StoragePoolInformation>();
|
||||||
private final Map<String, StorageAdaptor> _storageMapper = new HashMap<String, StorageAdaptor>();
|
private final Map<String, StorageAdaptor> _storageMapper = new HashMap<String, StorageAdaptor>();
|
||||||
|
|
@ -303,14 +281,33 @@ public class KVMStoragePoolManager {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
StoragePoolInformation info = _storagePools.get(uuid);
|
StoragePoolInformation info = _storagePools.get(uuid);
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
pool = createStoragePool(info.name, info.host, info.port, info.path, info.userInfo, info.poolType, info.details, info.type);
|
pool = createStoragePool(info.getName(), info.getHost(), info.getPort(), info.getPath(), info.getUserInfo(), info.getPoolType(), info.getDetails(), info.isType());
|
||||||
} else {
|
} else {
|
||||||
throw new CloudRuntimeException("Could not fetch storage pool " + uuid + " from libvirt due to " + e.getMessage());
|
throw new CloudRuntimeException("Could not fetch storage pool " + uuid + " from libvirt due to " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pool instanceof LibvirtStoragePool) {
|
||||||
|
addPoolDetails(uuid, (LibvirtStoragePool) pool);
|
||||||
|
}
|
||||||
|
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As the class {@link LibvirtStoragePool} is constrained to the {@link org.libvirt.StoragePool} class, there is no way of saving a generic parameter such as the details, hence,
|
||||||
|
* this method was created to always make available the details of libvirt primary storages for when they are needed.
|
||||||
|
*/
|
||||||
|
private void addPoolDetails(String uuid, LibvirtStoragePool pool) {
|
||||||
|
StoragePoolInformation storagePoolInformation = _storagePools.get(uuid);
|
||||||
|
Map<String, String> details = storagePoolInformation.getDetails();
|
||||||
|
|
||||||
|
if (MapUtils.isNotEmpty(details)) {
|
||||||
|
logger.trace("Adding the details {} to the pool with UUID {}.", details, uuid);
|
||||||
|
pool.setDetails(details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public KVMStoragePool getStoragePoolByURI(String uri) {
|
public KVMStoragePool getStoragePoolByURI(String uri) {
|
||||||
URI storageUri = null;
|
URI storageUri = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -667,9 +667,7 @@ public class KVMStorageProcessor implements StorageProcessor {
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Converting RBD disk " + disk.getPath() + " into template " + templateName);
|
logger.debug("Converting RBD disk " + disk.getPath() + " into template " + templateName);
|
||||||
|
|
||||||
final QemuImgFile srcFile =
|
final QemuImgFile srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary, disk.getPath()));
|
||||||
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(),
|
|
||||||
primary.getAuthSecret(), disk.getPath()));
|
|
||||||
srcFile.setFormat(PhysicalDiskFormat.RAW);
|
srcFile.setFormat(PhysicalDiskFormat.RAW);
|
||||||
|
|
||||||
final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + templateName + ".qcow2");
|
final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + templateName + ".qcow2");
|
||||||
|
|
@ -1022,9 +1020,7 @@ public class KVMStorageProcessor implements StorageProcessor {
|
||||||
logger.debug("Attempting to create " + snapDir.getAbsolutePath() + " recursively for snapshot storage");
|
logger.debug("Attempting to create " + snapDir.getAbsolutePath() + " recursively for snapshot storage");
|
||||||
FileUtils.forceMkdir(snapDir);
|
FileUtils.forceMkdir(snapDir);
|
||||||
|
|
||||||
final QemuImgFile srcFile =
|
final QemuImgFile srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primaryPool, rbdSnapshot));
|
||||||
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primaryPool.getSourceHost(), primaryPool.getSourcePort(), primaryPool.getAuthUserName(),
|
|
||||||
primaryPool.getAuthSecret(), rbdSnapshot));
|
|
||||||
srcFile.setFormat(snapshotDisk.getFormat());
|
srcFile.setFormat(snapshotDisk.getFormat());
|
||||||
|
|
||||||
final QemuImgFile destFile = new QemuImgFile(snapshotFile);
|
final QemuImgFile destFile = new QemuImgFile(snapshotFile);
|
||||||
|
|
|
||||||
|
|
@ -960,17 +960,55 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a physical disk depending on the {@link StoragePoolType}:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* <b>{@link StoragePoolType#RBD}</b>
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* If it is an erasure code pool, utilizes QemuImg to create the physical disk through the method
|
||||||
|
* {@link LibvirtStorageAdaptor#createPhysicalDiskByQemuImg(String, KVMStoragePool, PhysicalDiskFormat, Storage.ProvisioningType, long, byte[])}
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* Otherwise, utilize Libvirt to create the physical disk through the method
|
||||||
|
* {@link LibvirtStorageAdaptor#createPhysicalDiskByLibVirt(String, KVMStoragePool, PhysicalDiskFormat, Storage.ProvisioningType, long)}
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* {@link StoragePoolType#NetworkFilesystem} and {@link StoragePoolType#Filesystem}
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* If the format is {@link PhysicalDiskFormat#QCOW2} or {@link PhysicalDiskFormat#RAW}, utilizes QemuImg to create the physical disk through the method
|
||||||
|
* {@link LibvirtStorageAdaptor#createPhysicalDiskByQemuImg(String, KVMStoragePool, PhysicalDiskFormat, Storage.ProvisioningType, long, byte[])}
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* If the format is {@link PhysicalDiskFormat#DIR} or {@link PhysicalDiskFormat#TAR}, utilize Libvirt to create the physical disk through the method
|
||||||
|
* {@link LibvirtStorageAdaptor#createPhysicalDiskByLibVirt(String, KVMStoragePool, PhysicalDiskFormat, Storage.ProvisioningType, long)}
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* For the rest of the {@link StoragePoolType} types, utilizes the Libvirt method
|
||||||
|
* {@link LibvirtStorageAdaptor#createPhysicalDiskByLibVirt(String, KVMStoragePool, PhysicalDiskFormat, Storage.ProvisioningType, long)}
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool,
|
public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool,
|
||||||
PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) {
|
PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) {
|
||||||
|
|
||||||
logger.info("Attempting to create volume " + name + " (" + pool.getType().toString() + ") in pool "
|
logger.info("Attempting to create volume {} ({}) in pool {} with size {}", name, pool.getType().toString(), pool.getUuid(), toHumanReadableSize(size));
|
||||||
+ pool.getUuid() + " with size " + toHumanReadableSize(size));
|
|
||||||
|
|
||||||
StoragePoolType poolType = pool.getType();
|
StoragePoolType poolType = pool.getType();
|
||||||
if (poolType.equals(StoragePoolType.RBD)) {
|
if (StoragePoolType.RBD.equals(poolType)) {
|
||||||
return createPhysicalDiskByLibVirt(name, pool, PhysicalDiskFormat.RAW, provisioningType, size);
|
Map<String, String> details = pool.getDetails();
|
||||||
} else if (poolType.equals(StoragePoolType.NetworkFilesystem) || poolType.equals(StoragePoolType.Filesystem)) {
|
String dataPool = (details == null) ? null : details.get(KVMPhysicalDisk.RBD_DEFAULT_DATA_POOL);
|
||||||
|
|
||||||
|
return (dataPool == null) ? createPhysicalDiskByLibVirt(name, pool, PhysicalDiskFormat.RAW, provisioningType, size) :
|
||||||
|
createPhysicalDiskByQemuImg(name, pool, PhysicalDiskFormat.RAW, provisioningType, size, passphrase);
|
||||||
|
} else if (StoragePoolType.NetworkFilesystem.equals(poolType) || StoragePoolType.Filesystem.equals(poolType)) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case QCOW2:
|
case QCOW2:
|
||||||
case RAW:
|
case RAW:
|
||||||
|
|
@ -1018,18 +1056,25 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private KVMPhysicalDisk createPhysicalDiskByQemuImg(String name, KVMStoragePool pool,
|
private KVMPhysicalDisk createPhysicalDiskByQemuImg(String name, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size,
|
||||||
PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) {
|
byte[] passphrase) {
|
||||||
String volPath = pool.getLocalPath() + "/" + name;
|
String volPath;
|
||||||
String volName = name;
|
String volName = name;
|
||||||
long virtualSize = 0;
|
long virtualSize = 0;
|
||||||
long actualSize = 0;
|
long actualSize = 0;
|
||||||
QemuObject.EncryptFormat encryptFormat = null;
|
QemuObject.EncryptFormat encryptFormat = null;
|
||||||
List<QemuObject> passphraseObjects = new ArrayList<>();
|
List<QemuObject> passphraseObjects = new ArrayList<>();
|
||||||
|
|
||||||
final int timeout = 0;
|
final int timeout = 0;
|
||||||
|
QemuImgFile destFile;
|
||||||
|
|
||||||
|
if (StoragePoolType.RBD.equals(pool.getType())) {
|
||||||
|
volPath = pool.getSourceDir() + File.separator + name;
|
||||||
|
destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(pool, volPath));
|
||||||
|
} else {
|
||||||
|
volPath = pool.getLocalPath() + File.separator + name;
|
||||||
|
destFile = new QemuImgFile(volPath);
|
||||||
|
}
|
||||||
|
|
||||||
QemuImgFile destFile = new QemuImgFile(volPath);
|
|
||||||
destFile.setFormat(format);
|
destFile.setFormat(format);
|
||||||
destFile.setSize(size);
|
destFile.setSize(size);
|
||||||
Map<String, String> options = new HashMap<String, String>();
|
Map<String, String> options = new HashMap<String, String>();
|
||||||
|
|
@ -1312,11 +1357,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||||
|
|
||||||
|
|
||||||
QemuImgFile srcFile;
|
QemuImgFile srcFile;
|
||||||
QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool, disk.getPath()));
|
||||||
destPool.getSourcePort(),
|
|
||||||
destPool.getAuthUserName(),
|
|
||||||
destPool.getAuthSecret(),
|
|
||||||
disk.getPath()));
|
|
||||||
destFile.setFormat(format);
|
destFile.setFormat(format);
|
||||||
|
|
||||||
if (srcPool.getType() != StoragePoolType.RBD) {
|
if (srcPool.getType() != StoragePoolType.RBD) {
|
||||||
|
|
@ -1591,11 +1632,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||||
try {
|
try {
|
||||||
srcFile = new QemuImgFile(sourcePath, sourceFormat);
|
srcFile = new QemuImgFile(sourcePath, sourceFormat);
|
||||||
String rbdDestPath = destPool.getSourceDir() + "/" + name;
|
String rbdDestPath = destPool.getSourceDir() + "/" + name;
|
||||||
String rbdDestFile = KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
String rbdDestFile = KVMPhysicalDisk.RBDStringBuilder(destPool, rbdDestPath);
|
||||||
destPool.getSourcePort(),
|
|
||||||
destPool.getAuthUserName(),
|
|
||||||
destPool.getAuthSecret(),
|
|
||||||
rbdDestPath);
|
|
||||||
destFile = new QemuImgFile(rbdDestFile, destFormat);
|
destFile = new QemuImgFile(rbdDestFile, destFormat);
|
||||||
|
|
||||||
logger.debug("Starting copy from source image " + srcFile.getFileName() + " to RBD image " + rbdDestPath);
|
logger.debug("Starting copy from source image " + srcFile.getFileName() + " to RBD image " + rbdDestPath);
|
||||||
|
|
@ -1638,9 +1675,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||||
We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning
|
We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning
|
||||||
it doesn't benefit us. It's better to keep the current code in place which works
|
it doesn't benefit us. It's better to keep the current code in place which works
|
||||||
*/
|
*/
|
||||||
srcFile =
|
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool, sourcePath));
|
||||||
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), srcPool.getSourcePort(), srcPool.getAuthUserName(), srcPool.getAuthSecret(),
|
|
||||||
sourcePath));
|
|
||||||
srcFile.setFormat(sourceFormat);
|
srcFile.setFormat(sourceFormat);
|
||||||
destFile = new QemuImgFile(destPath);
|
destFile = new QemuImgFile(destPath);
|
||||||
destFile.setFormat(destFormat);
|
destFile.setFormat(destFormat);
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
||||||
protected String authSecret;
|
protected String authSecret;
|
||||||
protected String sourceHost;
|
protected String sourceHost;
|
||||||
protected int sourcePort;
|
protected int sourcePort;
|
||||||
|
|
||||||
protected String sourceDir;
|
protected String sourceDir;
|
||||||
|
protected Map<String, String> details;
|
||||||
|
|
||||||
public LibvirtStoragePool(String uuid, String name, StoragePoolType type, StorageAdaptor adaptor, StoragePool pool) {
|
public LibvirtStoragePool(String uuid, String name, StoragePoolType type, StorageAdaptor adaptor, StoragePool pool) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
|
@ -311,7 +311,11 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getDetails() {
|
public Map<String, String> getDetails() {
|
||||||
return null;
|
return this.details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetails(Map<String, String> details) {
|
||||||
|
this.details = details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package com.cloud.hypervisor.kvm.storage;
|
||||||
|
|
||||||
|
import com.cloud.storage.Storage;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class StoragePoolInformation {
|
||||||
|
private String name;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private String path;
|
||||||
|
private String userInfo;
|
||||||
|
private boolean type;
|
||||||
|
private Storage.StoragePoolType poolType;
|
||||||
|
private Map<String, String> details;
|
||||||
|
|
||||||
|
public StoragePoolInformation(String name, String host, int port, String path, String userInfo, Storage.StoragePoolType poolType, Map<String, String> details, boolean type) {
|
||||||
|
this.name = name;
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
this.path = path;
|
||||||
|
this.userInfo = userInfo;
|
||||||
|
this.type = type;
|
||||||
|
this.poolType = poolType;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserInfo() {
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage.StoragePoolType getPoolType() {
|
||||||
|
return poolType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,43 +17,73 @@
|
||||||
package com.cloud.hypervisor.kvm.storage;
|
package com.cloud.hypervisor.kvm.storage;
|
||||||
|
|
||||||
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
|
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class KVMPhysicalDiskTest extends TestCase {
|
public class KVMPhysicalDiskTest {
|
||||||
|
@Mock
|
||||||
|
KVMStoragePool kvmStoragePoolMock;
|
||||||
|
|
||||||
|
private final String authUserName = "admin";
|
||||||
|
|
||||||
|
private final String authSecret = "supersecret";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRBDStringBuilder() {
|
public void testRBDStringBuilder() {
|
||||||
assertEquals(KVMPhysicalDisk.RBDStringBuilder("ceph-monitor", 8000, "admin", "supersecret", "volume1"),
|
String monHosts = "ceph-monitor";
|
||||||
"rbd:volume1:mon_host=ceph-monitor\\:8000:auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30");
|
int monPort = 8000;
|
||||||
|
|
||||||
|
Mockito.doReturn(monHosts).when(kvmStoragePoolMock).getSourceHost();
|
||||||
|
Mockito.doReturn(monPort).when(kvmStoragePoolMock).getSourcePort();
|
||||||
|
Mockito.doReturn(authUserName).when(kvmStoragePoolMock).getAuthUserName();
|
||||||
|
Mockito.doReturn(authSecret).when(kvmStoragePoolMock).getAuthSecret();
|
||||||
|
|
||||||
|
String expected = "rbd:volume1:mon_host=ceph-monitor\\:8000:auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30";
|
||||||
|
String result = KVMPhysicalDisk.RBDStringBuilder(kvmStoragePoolMock, "volume1");
|
||||||
|
|
||||||
|
Assert.assertEquals(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRBDStringBuilder2() {
|
public void testRBDStringBuilder2() {
|
||||||
String monHosts = "ceph-monitor1,ceph-monitor2,ceph-monitor3";
|
String monHosts = "ceph-monitor1,ceph-monitor2,ceph-monitor3";
|
||||||
int monPort = 3300;
|
int monPort = 3300;
|
||||||
|
|
||||||
|
Mockito.doReturn(monHosts).when(kvmStoragePoolMock).getSourceHost();
|
||||||
|
Mockito.doReturn(monPort).when(kvmStoragePoolMock).getSourcePort();
|
||||||
|
Mockito.doReturn(authUserName).when(kvmStoragePoolMock).getAuthUserName();
|
||||||
|
Mockito.doReturn(authSecret).when(kvmStoragePoolMock).getAuthSecret();
|
||||||
|
|
||||||
String expected = "rbd:volume1:" +
|
String expected = "rbd:volume1:" +
|
||||||
"mon_host=ceph-monitor1\\:3300\\;ceph-monitor2\\:3300\\;ceph-monitor3\\:3300:" +
|
"mon_host=ceph-monitor1\\:3300\\;ceph-monitor2\\:3300\\;ceph-monitor3\\:3300:" +
|
||||||
"auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30";
|
"auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30";
|
||||||
String actualResult = KVMPhysicalDisk.RBDStringBuilder(monHosts, monPort, "admin", "supersecret", "volume1");
|
String actualResult = KVMPhysicalDisk.RBDStringBuilder(kvmStoragePoolMock, "volume1");
|
||||||
assertEquals(expected, actualResult);
|
|
||||||
|
Assert.assertEquals(expected, actualResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRBDStringBuilder3() {
|
public void testRBDStringBuilder3() {
|
||||||
String monHosts = "[fc00:1234::1],[fc00:1234::2],[fc00:1234::3]";
|
String monHosts = "[fc00:1234::1],[fc00:1234::2],[fc00:1234::3]";
|
||||||
int monPort = 3300;
|
int monPort = 3300;
|
||||||
|
|
||||||
|
Mockito.doReturn(monHosts).when(kvmStoragePoolMock).getSourceHost();
|
||||||
|
Mockito.doReturn(monPort).when(kvmStoragePoolMock).getSourcePort();
|
||||||
|
Mockito.doReturn(authUserName).when(kvmStoragePoolMock).getAuthUserName();
|
||||||
|
Mockito.doReturn(authSecret).when(kvmStoragePoolMock).getAuthSecret();
|
||||||
|
|
||||||
String expected = "rbd:volume1:" +
|
String expected = "rbd:volume1:" +
|
||||||
"mon_host=[fc00\\:1234\\:\\:1]\\:3300\\;[fc00\\:1234\\:\\:2]\\:3300\\;[fc00\\:1234\\:\\:3]\\:3300:" +
|
"mon_host=[fc00\\:1234\\:\\:1]\\:3300\\;[fc00\\:1234\\:\\:2]\\:3300\\;[fc00\\:1234\\:\\:3]\\:3300:" +
|
||||||
"auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30";
|
"auth_supported=cephx:id=admin:key=supersecret:rbd_default_format=2:client_mount_timeout=30";
|
||||||
String actualResult = KVMPhysicalDisk.RBDStringBuilder(monHosts, monPort, "admin", "supersecret", "volume1");
|
String actualResult = KVMPhysicalDisk.RBDStringBuilder(kvmStoragePoolMock, "volume1");
|
||||||
assertEquals(expected, actualResult);
|
|
||||||
|
Assert.assertEquals(expected, actualResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -64,18 +94,18 @@ public class KVMPhysicalDiskTest extends TestCase {
|
||||||
LibvirtStoragePool pool = Mockito.mock(LibvirtStoragePool.class);
|
LibvirtStoragePool pool = Mockito.mock(LibvirtStoragePool.class);
|
||||||
|
|
||||||
KVMPhysicalDisk disk = new KVMPhysicalDisk(path, name, pool);
|
KVMPhysicalDisk disk = new KVMPhysicalDisk(path, name, pool);
|
||||||
assertEquals(disk.getName(), name);
|
Assert.assertEquals(disk.getName(), name);
|
||||||
assertEquals(disk.getPath(), path);
|
Assert.assertEquals(disk.getPath(), path);
|
||||||
assertEquals(disk.getPool(), pool);
|
Assert.assertEquals(disk.getPool(), pool);
|
||||||
assertEquals(disk.getSize(), 0);
|
Assert.assertEquals(disk.getSize(), 0);
|
||||||
assertEquals(disk.getVirtualSize(), 0);
|
Assert.assertEquals(disk.getVirtualSize(), 0);
|
||||||
|
|
||||||
disk.setSize(1024);
|
disk.setSize(1024);
|
||||||
disk.setVirtualSize(2048);
|
disk.setVirtualSize(2048);
|
||||||
assertEquals(disk.getSize(), 1024);
|
Assert.assertEquals(disk.getSize(), 1024);
|
||||||
assertEquals(disk.getVirtualSize(), 2048);
|
Assert.assertEquals(disk.getVirtualSize(), 2048);
|
||||||
|
|
||||||
disk.setFormat(PhysicalDiskFormat.RAW);
|
disk.setFormat(PhysicalDiskFormat.RAW);
|
||||||
assertEquals(disk.getFormat(), PhysicalDiskFormat.RAW);
|
Assert.assertEquals(disk.getFormat(), PhysicalDiskFormat.RAW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,11 +114,7 @@ public final class StorPoolDownloadVolumeCommandWrapper extends CommandWrapper<S
|
||||||
if (isRBDPool) {
|
if (isRBDPool) {
|
||||||
KVMStoragePool srcPool = srcDisk.getPool();
|
KVMStoragePool srcPool = srcDisk.getPool();
|
||||||
String rbdDestPath = srcPool.getSourceDir() + "/" + srcDisk.getName();
|
String rbdDestPath = srcPool.getSourceDir() + "/" + srcDisk.getName();
|
||||||
srcPath = KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(),
|
srcPath = KVMPhysicalDisk.RBDStringBuilder(srcPool, rbdDestPath);
|
||||||
srcPool.getSourcePort(),
|
|
||||||
srcPool.getAuthUserName(),
|
|
||||||
srcPool.getAuthSecret(),
|
|
||||||
rbdDestPath);
|
|
||||||
} else {
|
} else {
|
||||||
srcPath = srcDisk.getPath();
|
srcPath = srcDisk.getPath();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.EnumUtils;
|
import org.apache.commons.lang3.EnumUtils;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
@ -3158,28 +3159,41 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||||
List<StoragePoolResponse> poolResponses = ViewResponseHelper.createStoragePoolResponse(getCustomStats, storagePools.first().toArray(new StoragePoolJoinVO[storagePools.first().size()]));
|
List<StoragePoolResponse> poolResponses = ViewResponseHelper.createStoragePoolResponse(getCustomStats, storagePools.first().toArray(new StoragePoolJoinVO[storagePools.first().size()]));
|
||||||
Map<String, Long> poolUuidToIdMap = storagePools.first().stream().collect(Collectors.toMap(StoragePoolJoinVO::getUuid, StoragePoolJoinVO::getId, (a, b) -> a));
|
Map<String, Long> poolUuidToIdMap = storagePools.first().stream().collect(Collectors.toMap(StoragePoolJoinVO::getUuid, StoragePoolJoinVO::getId, (a, b) -> a));
|
||||||
for (StoragePoolResponse poolResponse : poolResponses) {
|
for (StoragePoolResponse poolResponse : poolResponses) {
|
||||||
|
Long poolId = poolUuidToIdMap.get(poolResponse.getId());
|
||||||
DataStore store = dataStoreManager.getPrimaryDataStore(poolResponse.getId());
|
DataStore store = dataStoreManager.getPrimaryDataStore(poolResponse.getId());
|
||||||
|
|
||||||
if (store != null) {
|
if (store != null) {
|
||||||
DataStoreDriver driver = store.getDriver();
|
addPoolDetailsAndCapabilities(poolResponse, store, poolId);
|
||||||
if (driver != null && driver.getCapabilities() != null) {
|
|
||||||
Map<String, String> caps = driver.getCapabilities();
|
|
||||||
if (Storage.StoragePoolType.NetworkFilesystem.toString().equals(poolResponse.getType()) &&
|
|
||||||
HypervisorType.VMware.toString().equals(poolResponse.getHypervisor())) {
|
|
||||||
StoragePoolDetailVO detail = _storagePoolDetailsDao.findDetail(poolUuidToIdMap.get(poolResponse.getId()), Storage.Capability.HARDWARE_ACCELERATION.toString());
|
|
||||||
if (detail != null) {
|
|
||||||
caps.put(Storage.Capability.HARDWARE_ACCELERATION.toString(), detail.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poolResponse.setCaps(caps);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setPoolResponseNFSMountOptions(poolResponse, poolUuidToIdMap.get(poolResponse.getId()));
|
|
||||||
|
setPoolResponseNFSMountOptions(poolResponse, poolId);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResponses(poolResponses, storagePools.second());
|
response.setResponses(poolResponses, storagePools.second());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addPoolDetailsAndCapabilities(StoragePoolResponse poolResponse, DataStore store, Long poolId) {
|
||||||
|
Map<String, String> details = _storagePoolDetailsDao.listDetailsKeyPairs(store.getId(), true);
|
||||||
|
poolResponse.setDetails(details);
|
||||||
|
|
||||||
|
DataStoreDriver driver = store.getDriver();
|
||||||
|
if (ObjectUtils.anyNull(driver, driver.getCapabilities())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> caps = driver.getCapabilities();
|
||||||
|
if (Storage.StoragePoolType.NetworkFilesystem.toString().equals(poolResponse.getType()) && HypervisorType.VMware.toString().equals(poolResponse.getHypervisor())) {
|
||||||
|
StoragePoolDetailVO detail = _storagePoolDetailsDao.findDetail(poolId, Storage.Capability.HARDWARE_ACCELERATION.toString());
|
||||||
|
if (detail != null) {
|
||||||
|
caps.put(Storage.Capability.HARDWARE_ACCELERATION.toString(), detail.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
poolResponse.setCaps(caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private Pair<List<StoragePoolJoinVO>, Integer> searchForStoragePoolsInternal(ListStoragePoolsCmd cmd) {
|
private Pair<List<StoragePoolJoinVO>, Integer> searchForStoragePoolsInternal(ListStoragePoolsCmd cmd) {
|
||||||
ScopeType scopeType = ScopeType.validateAndGetScopeType(cmd.getScope());
|
ScopeType scopeType = ScopeType.validateAndGetScopeType(cmd.getScope());
|
||||||
StoragePoolStatus status = StoragePoolStatus.validateAndGetStatus(cmd.getStatus());
|
StoragePoolStatus status = StoragePoolStatus.validateAndGetStatus(cmd.getStatus());
|
||||||
|
|
|
||||||
|
|
@ -665,6 +665,8 @@
|
||||||
"label.dark.mode": "Dark mode",
|
"label.dark.mode": "Dark mode",
|
||||||
"label.dashboard": "Dashboard",
|
"label.dashboard": "Dashboard",
|
||||||
"label.data.disk": "Data disk",
|
"label.data.disk": "Data disk",
|
||||||
|
"label.data.pool": "Data pool",
|
||||||
|
"label.data.pool.description": "Data pool is required when using a Ceph pool with erasure code",
|
||||||
"label.data.disk.offering": "Data disk offering",
|
"label.data.disk.offering": "Data disk offering",
|
||||||
"label.date": "Date",
|
"label.date": "Date",
|
||||||
"label.datetime.filter.period": "From <b>{startDate}</b> to <b>{endDate}</b>",
|
"label.datetime.filter.period": "From <b>{startDate}</b> to <b>{endDate}</b>",
|
||||||
|
|
|
||||||
|
|
@ -463,6 +463,8 @@
|
||||||
"label.dashboard": "Dashboard",
|
"label.dashboard": "Dashboard",
|
||||||
"label.data.disk": "Disco de dados",
|
"label.data.disk": "Disco de dados",
|
||||||
"label.data.disk.offering": "Oferta de disco adicional",
|
"label.data.disk.offering": "Oferta de disco adicional",
|
||||||
|
"label.data.pool": "Data pool",
|
||||||
|
"label.data.pool.description": "\u00c9 necess\u00e1rio informar um data pool ao utilizar um Ceph pool com erasure code",
|
||||||
"label.date": "Data",
|
"label.date": "Data",
|
||||||
"label.day": "Dia",
|
"label.day": "Dia",
|
||||||
"label.day.of.month": "Dia do m\u00eas",
|
"label.day.of.month": "Dia do m\u00eas",
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,13 @@
|
||||||
<div>{{ $toLocaleDate(dataResource[item]) }}</div>
|
<div>{{ $toLocaleDate(dataResource[item]) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
|
<a-list-item v-else-if="item === 'details' && $route.meta.name === 'storagepool' && dataResource[item].rbd_default_data_pool">
|
||||||
|
<div>
|
||||||
|
<strong>{{ $t('label.data.pool') }}</strong>
|
||||||
|
<br/>
|
||||||
|
<div>{{ dataResource[item].rbd_default_data_pool }}</div>
|
||||||
|
</div>
|
||||||
|
</a-list-item>
|
||||||
</template>
|
</template>
|
||||||
<HostInfo :resource="dataResource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
<HostInfo :resource="dataResource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
||||||
<DedicateData :resource="dataResource" v-if="dedicatedSectionActive" />
|
<DedicateData :resource="dataResource" v-if="dedicatedSectionActive" />
|
||||||
|
|
@ -207,7 +214,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
customDisplayItems () {
|
customDisplayItems () {
|
||||||
var items = ['ip4routes', 'ip6routes', 'privatemtu', 'publicmtu', 'provider']
|
var items = ['ip4routes', 'ip6routes', 'privatemtu', 'publicmtu', 'provider', 'details']
|
||||||
if (this.$route.meta.name === 'webhookdeliveries') {
|
if (this.$route.meta.name === 'webhookdeliveries') {
|
||||||
items.push('startdate')
|
items.push('startdate')
|
||||||
items.push('enddate')
|
items.push('enddate')
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export default {
|
||||||
fields.push('zonename')
|
fields.push('zonename')
|
||||||
return fields
|
return fields
|
||||||
},
|
},
|
||||||
details: ['name', 'id', 'ipaddress', 'type', 'nfsmountopts', 'scope', 'tags', 'path', 'provider', 'hypervisor', 'overprovisionfactor', 'disksizetotal', 'disksizeallocated', 'disksizeused', 'capacityiops', 'usediops', 'clustername', 'podname', 'zonename', 'created'],
|
details: ['name', 'id', 'ipaddress', 'type', 'details', 'nfsmountopts', 'scope', 'tags', 'path', 'provider', 'hypervisor', 'overprovisionfactor', 'disksizetotal', 'disksizeallocated', 'disksizeused', 'capacityiops', 'usediops', 'clustername', 'podname', 'zonename', 'created'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'volume',
|
name: 'volume',
|
||||||
title: 'label.volumes',
|
title: 'label.volumes',
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,12 @@
|
||||||
<a-form-item name="radospool" ref="radospool" :label="$t('label.rados.pool')">
|
<a-form-item name="radospool" ref="radospool" :label="$t('label.rados.pool')">
|
||||||
<a-input v-model:value="form.radospool" :placeholder="$t('label.rados.pool')"/>
|
<a-input v-model:value="form.radospool" :placeholder="$t('label.rados.pool')"/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item name="datapool" ref="datapool">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.data.pool')" :tooltip="$t('label.data.pool.description')"/>
|
||||||
|
</template>
|
||||||
|
<a-input v-model:value="form.datapool" :placeholder="$t('label.data.pool')"/>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item name="radosuser" ref="radosuser" :label="$t('label.rados.user')">
|
<a-form-item name="radosuser" ref="radosuser" :label="$t('label.rados.user')">
|
||||||
<a-input v-model:value="form.radosuser" :placeholder="$t('label.rados.user')" />
|
<a-input v-model:value="form.radosuser" :placeholder="$t('label.rados.user')" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -499,6 +505,10 @@ export default {
|
||||||
powerflexGatewayUsername: [{ required: true, message: this.$t('label.required') }],
|
powerflexGatewayUsername: [{ required: true, message: this.$t('label.required') }],
|
||||||
powerflexGatewayPassword: [{ required: true, message: this.$t('label.required') }],
|
powerflexGatewayPassword: [{ required: true, message: this.$t('label.required') }],
|
||||||
powerflexStoragePool: [{ required: true, message: this.$t('label.required') }],
|
powerflexStoragePool: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
radosmonitor: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
radospool: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
radosuser: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
radossecret: [{ required: true, message: this.$t('label.required') }],
|
||||||
username: [{ required: true, message: this.$t('label.required') }],
|
username: [{ required: true, message: this.$t('label.required') }],
|
||||||
password: [{ required: true, message: this.$t('label.required') }],
|
password: [{ required: true, message: this.$t('label.required') }],
|
||||||
primeraURL: [{ required: true, message: this.$t('label.url') }],
|
primeraURL: [{ required: true, message: this.$t('label.url') }],
|
||||||
|
|
@ -845,6 +855,9 @@ export default {
|
||||||
url = this.clvmURL(vg)
|
url = this.clvmURL(vg)
|
||||||
} else if (values.protocol === 'RBD') {
|
} else if (values.protocol === 'RBD') {
|
||||||
url = this.rbdURL(values.radosmonitor, values.radospool, values.radosuser, values.radossecret)
|
url = this.rbdURL(values.radosmonitor, values.radospool, values.radosuser, values.radossecret)
|
||||||
|
if (values.datapool) {
|
||||||
|
params['details[0].rbd_default_data_pool'] = values.datapool
|
||||||
|
}
|
||||||
} else if (values.protocol === 'vmfs') {
|
} else if (values.protocol === 'vmfs') {
|
||||||
path = values.vCenterDataCenter
|
path = values.vCenterDataCenter
|
||||||
if (path.substring(0, 1) !== '/') {
|
if (path.substring(0, 1) !== '/') {
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,7 @@ export default {
|
||||||
title: 'label.rados.monitor',
|
title: 'label.rados.monitor',
|
||||||
key: 'primaryStorageRADOSMonitor',
|
key: 'primaryStorageRADOSMonitor',
|
||||||
placeHolder: 'message.error.rados.monitor',
|
placeHolder: 'message.error.rados.monitor',
|
||||||
required: false,
|
required: true,
|
||||||
display: {
|
display: {
|
||||||
primaryStorageProtocol: ['rbd']
|
primaryStorageProtocol: ['rbd']
|
||||||
}
|
}
|
||||||
|
|
@ -478,6 +478,14 @@ export default {
|
||||||
title: 'label.rados.pool',
|
title: 'label.rados.pool',
|
||||||
key: 'primaryStorageRADOSPool',
|
key: 'primaryStorageRADOSPool',
|
||||||
placeHolder: 'message.error.rados.pool',
|
placeHolder: 'message.error.rados.pool',
|
||||||
|
required: true,
|
||||||
|
display: {
|
||||||
|
primaryStorageProtocol: ['rbd']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'label.data.pool',
|
||||||
|
key: 'primaryStorageDataPool',
|
||||||
required: false,
|
required: false,
|
||||||
display: {
|
display: {
|
||||||
primaryStorageProtocol: ['rbd']
|
primaryStorageProtocol: ['rbd']
|
||||||
|
|
@ -487,7 +495,7 @@ export default {
|
||||||
title: 'label.rados.user',
|
title: 'label.rados.user',
|
||||||
key: 'primaryStorageRADOSUser',
|
key: 'primaryStorageRADOSUser',
|
||||||
placeHolder: 'message.error.rados.user',
|
placeHolder: 'message.error.rados.user',
|
||||||
required: false,
|
required: true,
|
||||||
display: {
|
display: {
|
||||||
primaryStorageProtocol: ['rbd']
|
primaryStorageProtocol: ['rbd']
|
||||||
}
|
}
|
||||||
|
|
@ -496,7 +504,7 @@ export default {
|
||||||
title: 'label.rados.secret',
|
title: 'label.rados.secret',
|
||||||
key: 'primaryStorageRADOSSecret',
|
key: 'primaryStorageRADOSSecret',
|
||||||
placeHolder: 'message.error.rados.secret',
|
placeHolder: 'message.error.rados.secret',
|
||||||
required: false,
|
required: true,
|
||||||
display: {
|
display: {
|
||||||
primaryStorageProtocol: ['rbd']
|
primaryStorageProtocol: ['rbd']
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1486,6 +1486,10 @@ export default {
|
||||||
const rbdpool = this.prefillContent?.primaryStorageRADOSPool || ''
|
const rbdpool = this.prefillContent?.primaryStorageRADOSPool || ''
|
||||||
const rbdid = this.prefillContent?.primaryStorageRADOSUser || ''
|
const rbdid = this.prefillContent?.primaryStorageRADOSUser || ''
|
||||||
const rbdsecret = this.prefillContent?.primaryStorageRADOSSecret || ''
|
const rbdsecret = this.prefillContent?.primaryStorageRADOSSecret || ''
|
||||||
|
|
||||||
|
if (this.prefillContent?.primaryStorageDataPool) {
|
||||||
|
params['details[0].rbd_default_data_pool'] = this.prefillContent.primaryStorageDataPool
|
||||||
|
}
|
||||||
url = this.rbdURL(rbdmonitor, rbdpool, rbdid, rbdsecret)
|
url = this.rbdURL(rbdmonitor, rbdpool, rbdid, rbdsecret)
|
||||||
} else if (protocol === 'Linstor') {
|
} else if (protocol === 'Linstor') {
|
||||||
url = this.linstorURL(server)
|
url = this.linstorURL(server)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue