mirror of https://github.com/apache/cloudstack.git
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cloudstack
This commit is contained in:
commit
b037f2749d
|
|
@ -30,12 +30,15 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DateFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -154,6 +157,7 @@ import com.cloud.agent.resource.computing.KVMHABase.NfsStoragePool;
|
|||
import com.cloud.agent.resource.computing.LibvirtVMDef.ConsoleDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.DevicesDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.DiskDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.DiskDef.diskProtocol;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.FeaturesDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.GraphicDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtVMDef.GuestDef;
|
||||
|
|
@ -1298,6 +1302,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
|
||||
KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool(cmd
|
||||
.getPool().getUuid());
|
||||
|
||||
if (primaryPool.getType() == StoragePoolType.RBD) {
|
||||
s_logger.debug("Snapshots are not supported on RBD volumes");
|
||||
return new ManageSnapshotAnswer(cmd, false,
|
||||
"Snapshots are not supported on RBD volumes");
|
||||
}
|
||||
|
||||
KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(cmd
|
||||
.getVolumePath());
|
||||
if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING
|
||||
|
|
@ -1644,16 +1655,43 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
+ templateInstallFolder;
|
||||
_storage.mkdirs(tmpltPath);
|
||||
|
||||
Script command = new Script(_createTmplPath, _cmdsTimeout, s_logger);
|
||||
command.add("-f", disk.getPath());
|
||||
command.add("-t", tmpltPath);
|
||||
command.add("-n", cmd.getUniqueName() + ".qcow2");
|
||||
if (primary.getType() != StoragePoolType.RBD) {
|
||||
Script command = new Script(_createTmplPath, _cmdsTimeout, s_logger);
|
||||
command.add("-f", disk.getPath());
|
||||
command.add("-t", tmpltPath);
|
||||
command.add("-n", cmd.getUniqueName() + ".qcow2");
|
||||
|
||||
String result = command.execute();
|
||||
String result = command.execute();
|
||||
|
||||
if (result != null) {
|
||||
s_logger.debug("failed to create template: " + result);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, result);
|
||||
if (result != null) {
|
||||
s_logger.debug("failed to create template: " + result);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, result);
|
||||
}
|
||||
} else {
|
||||
s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName());
|
||||
Script.runSimpleBashScript("qemu-img convert"
|
||||
+ " -f raw -O qcow2 "
|
||||
+ KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(),
|
||||
primary.getSourcePort(),
|
||||
primary.getAuthUserName(),
|
||||
primary.getAuthSecret(),
|
||||
disk.getPath())
|
||||
+ " " + tmpltPath + "/" + cmd.getUniqueName() + ".qcow2");
|
||||
File templateProp = new File(tmpltPath + "/template.properties");
|
||||
if (!templateProp.exists()) {
|
||||
templateProp.createNewFile();
|
||||
}
|
||||
|
||||
String templateContent = "filename=" + cmd.getUniqueName() + ".qcow2" + System.getProperty("line.separator");
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("MM_dd_yyyy");
|
||||
Date date = new Date();
|
||||
templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator");
|
||||
|
||||
FileOutputStream templFo = new FileOutputStream(templateProp);
|
||||
templFo.write(templateContent.getBytes());
|
||||
templFo.flush();
|
||||
templFo.close();
|
||||
}
|
||||
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
|
|
@ -1756,8 +1794,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
|
||||
protected Answer execute(ModifyStoragePoolCommand cmd) {
|
||||
KVMStoragePool storagepool = _storagePoolMgr.createStoragePool(cmd
|
||||
.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool()
|
||||
.getPath(), cmd.getPool().getType());
|
||||
.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(),
|
||||
cmd.getPool().getPath(), cmd.getPool().getUserInfo(), cmd.getPool().getType());
|
||||
if (storagepool == null) {
|
||||
return new Answer(cmd, false, " Failed to create storage pool");
|
||||
}
|
||||
|
|
@ -2624,10 +2662,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
} else {
|
||||
int devId = (int) volume.getDeviceId();
|
||||
|
||||
if (volume.getType() == Volume.Type.DATADISK) {
|
||||
disk.defFileBasedDisk(physicalDisk.getPath(), devId,
|
||||
DiskDef.diskBus.VIRTIO,
|
||||
DiskDef.diskFmtType.QCOW2);
|
||||
if (pool.getType() == StoragePoolType.RBD) {
|
||||
/*
|
||||
For RBD pools we use the secret mechanism in libvirt.
|
||||
We store the secret under the UUID of the pool, that's why
|
||||
we pass the pool's UUID as the authSecret
|
||||
*/
|
||||
disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(),
|
||||
pool.getAuthUserName(), pool.getUuid(),
|
||||
devId, diskBusType, diskProtocol.RBD);
|
||||
} else if (volume.getType() == Volume.Type.DATADISK) {
|
||||
disk.defFileBasedDisk(physicalDisk.getPath(), devId,
|
||||
DiskDef.diskBus.VIRTIO,
|
||||
DiskDef.diskFmtType.QCOW2);
|
||||
} else {
|
||||
disk.defFileBasedDisk(physicalDisk.getPath(), devId,
|
||||
diskBusType, DiskDef.diskFmtType.QCOW2);
|
||||
|
|
@ -2982,8 +3029,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
try {
|
||||
|
||||
KVMStoragePool localStoragePool = _storagePoolMgr
|
||||
.createStoragePool(_localStorageUUID, "localhost",
|
||||
_localStoragePath, StoragePoolType.Filesystem);
|
||||
.createStoragePool(_localStorageUUID, "localhost", -1,
|
||||
_localStoragePath, "", StoragePoolType.Filesystem);
|
||||
com.cloud.agent.api.StoragePoolInfo pi = new com.cloud.agent.api.StoragePoolInfo(
|
||||
localStoragePool.getUuid(), cmd.getPrivateIpAddress(),
|
||||
_localStoragePath, _localStoragePath,
|
||||
|
|
@ -4083,5 +4130,4 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
|||
|
||||
return new Answer(cmd, success, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
// 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.agent.resource.computing;
|
||||
|
||||
public class LibvirtSecretDef {
|
||||
|
||||
public enum usage {
|
||||
VOLUME("volume"), CEPH("ceph");
|
||||
String _usage;
|
||||
|
||||
usage(String usage) {
|
||||
_usage = usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return _usage;
|
||||
}
|
||||
}
|
||||
|
||||
private usage _usage;
|
||||
private boolean _ephemeral;
|
||||
private boolean _private;
|
||||
private String _uuid;
|
||||
private String _description;
|
||||
private String _cephName;
|
||||
private String _volumeVolume;
|
||||
|
||||
public LibvirtSecretDef (usage usage, String uuid) {
|
||||
_usage = usage;
|
||||
_uuid = uuid;
|
||||
}
|
||||
|
||||
public LibvirtSecretDef (usage usage, String uuid, String description) {
|
||||
_usage = usage;
|
||||
_uuid = uuid;
|
||||
_description = description;
|
||||
}
|
||||
|
||||
public boolean getEphemeral() {
|
||||
return _ephemeral;
|
||||
}
|
||||
|
||||
public boolean getPrivate() {
|
||||
return _private;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return _uuid;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return _description;
|
||||
}
|
||||
|
||||
public String getVolumeVolume() {
|
||||
return _volumeVolume;
|
||||
}
|
||||
|
||||
public String getCephName() {
|
||||
return _cephName;
|
||||
}
|
||||
|
||||
public void setVolumeVolume(String volume) {
|
||||
_volumeVolume = volume;
|
||||
}
|
||||
|
||||
public void setCephName(String name) {
|
||||
_cephName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder secretBuilder = new StringBuilder();
|
||||
secretBuilder.append("<secret ephemeral='" + (_ephemeral ? "yes" : "no") + "' private='" + (_private ? "yes" : "no") + "'>\n");
|
||||
secretBuilder.append("<uuid>" + _uuid + "</uuid>\n");
|
||||
if (_description != null) {
|
||||
secretBuilder.append("<description>" + _description + "</description>\n");
|
||||
}
|
||||
secretBuilder.append("<usage type='" + _usage + "'>\n");
|
||||
if (_usage == _usage.VOLUME) {
|
||||
secretBuilder.append("<volume>" + _volumeVolume + "</volume>\n");
|
||||
}
|
||||
if (_usage == _usage.CEPH) {
|
||||
secretBuilder.append("<name>" + _cephName + "</name>\n");
|
||||
}
|
||||
secretBuilder.append("</usage>\n");
|
||||
secretBuilder.append("</secret>\n");
|
||||
return secretBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ package com.cloud.agent.resource.computing;
|
|||
|
||||
public class LibvirtStoragePoolDef {
|
||||
public enum poolType {
|
||||
ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir");
|
||||
ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir"), RBD("rbd");
|
||||
String _poolType;
|
||||
|
||||
poolType(String poolType) {
|
||||
|
|
@ -31,12 +31,41 @@ public class LibvirtStoragePoolDef {
|
|||
}
|
||||
}
|
||||
|
||||
public enum authType {
|
||||
CHAP("chap"), CEPH("ceph");
|
||||
String _authType;
|
||||
|
||||
authType(String authType) {
|
||||
_authType = authType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return _authType;
|
||||
}
|
||||
}
|
||||
|
||||
private poolType _poolType;
|
||||
private String _poolName;
|
||||
private String _uuid;
|
||||
private String _sourceHost;
|
||||
private int _sourcePort;
|
||||
private String _sourceDir;
|
||||
private String _targetPath;
|
||||
private String _authUsername;
|
||||
private authType _authType;
|
||||
private String _secretUuid;
|
||||
|
||||
public LibvirtStoragePoolDef(poolType type, String poolName, String uuid,
|
||||
String host, int port, String dir, String targetPath) {
|
||||
_poolType = type;
|
||||
_poolName = poolName;
|
||||
_uuid = uuid;
|
||||
_sourceHost = host;
|
||||
_sourcePort = port;
|
||||
_sourceDir = dir;
|
||||
_targetPath = targetPath;
|
||||
}
|
||||
|
||||
public LibvirtStoragePoolDef(poolType type, String poolName, String uuid,
|
||||
String host, String dir, String targetPath) {
|
||||
|
|
@ -48,6 +77,20 @@ public class LibvirtStoragePoolDef {
|
|||
_targetPath = targetPath;
|
||||
}
|
||||
|
||||
public LibvirtStoragePoolDef(poolType type, String poolName, String uuid,
|
||||
String sourceHost, int sourcePort, String dir, String authUsername,
|
||||
authType authType, String secretUuid) {
|
||||
_poolType = type;
|
||||
_poolName = poolName;
|
||||
_uuid = uuid;
|
||||
_sourceHost = sourceHost;
|
||||
_sourcePort = sourcePort;
|
||||
_sourceDir = dir;
|
||||
_authUsername = authUsername;
|
||||
_authType = authType;
|
||||
_secretUuid = secretUuid;
|
||||
}
|
||||
|
||||
public String getPoolName() {
|
||||
return _poolName;
|
||||
}
|
||||
|
|
@ -60,6 +103,10 @@ public class LibvirtStoragePoolDef {
|
|||
return _sourceHost;
|
||||
}
|
||||
|
||||
public int getSourcePort() {
|
||||
return _sourcePort;
|
||||
}
|
||||
|
||||
public String getSourceDir() {
|
||||
return _sourceDir;
|
||||
}
|
||||
|
|
@ -68,6 +115,18 @@ public class LibvirtStoragePoolDef {
|
|||
return _targetPath;
|
||||
}
|
||||
|
||||
public String getAuthUserName() {
|
||||
return _authUsername;
|
||||
}
|
||||
|
||||
public String getSecretUUID() {
|
||||
return _secretUuid;
|
||||
}
|
||||
|
||||
public authType getAuthType() {
|
||||
return _authType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder storagePoolBuilder = new StringBuilder();
|
||||
|
|
@ -81,9 +140,22 @@ public class LibvirtStoragePoolDef {
|
|||
storagePoolBuilder.append("<dir path='" + _sourceDir + "'/>\n");
|
||||
storagePoolBuilder.append("</source>\n");
|
||||
}
|
||||
storagePoolBuilder.append("<target>\n");
|
||||
storagePoolBuilder.append("<path>" + _targetPath + "</path>\n");
|
||||
storagePoolBuilder.append("</target>\n");
|
||||
if (_poolType == poolType.RBD) {
|
||||
storagePoolBuilder.append("<source>\n");
|
||||
storagePoolBuilder.append("<host name='" + _sourceHost + "' port='" + _sourcePort + "'/>\n");
|
||||
storagePoolBuilder.append("<name>" + _sourceDir + "</name>\n");
|
||||
if (_authUsername != null) {
|
||||
storagePoolBuilder.append("<auth username='" + _authUsername + "' type='" + _authType + "'>\n");
|
||||
storagePoolBuilder.append("<secret uuid='" + _secretUuid + "'/>\n");
|
||||
storagePoolBuilder.append("</auth>\n");
|
||||
}
|
||||
storagePoolBuilder.append("</source>\n");
|
||||
}
|
||||
if (_poolType != poolType.RBD) {
|
||||
storagePoolBuilder.append("<target>\n");
|
||||
storagePoolBuilder.append("<path>" + _targetPath + "</path>\n");
|
||||
storagePoolBuilder.append("</target>\n");
|
||||
}
|
||||
storagePoolBuilder.append("</pool>\n");
|
||||
return storagePoolBuilder.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,15 +51,34 @@ public class LibvirtStoragePoolXMLParser {
|
|||
Element source = (Element) rootElement.getElementsByTagName(
|
||||
"source").item(0);
|
||||
String host = getAttrValue("host", "name", source);
|
||||
String path = getAttrValue("dir", "path", source);
|
||||
|
||||
Element target = (Element) rootElement.getElementsByTagName(
|
||||
"target").item(0);
|
||||
String targetPath = getTagValue("path", target);
|
||||
if (type.equalsIgnoreCase("rbd")) {
|
||||
int port = Integer.parseInt(getAttrValue("host", "port", source));
|
||||
String pool = getTagValue("name", source);
|
||||
|
||||
return new LibvirtStoragePoolDef(
|
||||
LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()),
|
||||
poolName, uuid, host, path, targetPath);
|
||||
Element auth = (Element) source.getElementsByTagName(
|
||||
"auth").item(0);
|
||||
|
||||
if (auth != null) {
|
||||
String authUsername = auth.getAttribute("username");
|
||||
String authType = auth.getAttribute("type");
|
||||
return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()),
|
||||
poolName, uuid, host, port, pool, authUsername, LibvirtStoragePoolDef.authType.valueOf(authType.toUpperCase()), uuid);
|
||||
} else {
|
||||
return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()),
|
||||
poolName, uuid, host, port, pool, "");
|
||||
}
|
||||
} else {
|
||||
String path = getAttrValue("dir", "path", source);
|
||||
|
||||
Element target = (Element) rootElement.getElementsByTagName(
|
||||
"target").item(0);
|
||||
String targetPath = getTagValue("path", target);
|
||||
|
||||
return new LibvirtStoragePoolDef(
|
||||
LibvirtStoragePoolDef.poolType.valueOf(type.toUpperCase()),
|
||||
poolName, uuid, host, path, targetPath);
|
||||
}
|
||||
} catch (ParserConfigurationException e) {
|
||||
s_logger.debug(e.toString());
|
||||
} catch (SAXException e) {
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ public class LibvirtVMDef {
|
|||
}
|
||||
|
||||
enum diskType {
|
||||
FILE("file"), BLOCK("block"), DIRECTROY("dir");
|
||||
FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network");
|
||||
String _diskType;
|
||||
|
||||
diskType(String type) {
|
||||
|
|
@ -351,6 +351,20 @@ public class LibvirtVMDef {
|
|||
}
|
||||
}
|
||||
|
||||
enum diskProtocol {
|
||||
RBD("rbd"), SHEEPDOG("sheepdog");
|
||||
String _diskProtocol;
|
||||
|
||||
diskProtocol(String protocol) {
|
||||
_diskProtocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return _diskProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
enum diskBus {
|
||||
IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML(
|
||||
"uml"), FDC("fdc");
|
||||
|
|
@ -382,7 +396,12 @@ public class LibvirtVMDef {
|
|||
|
||||
private deviceType _deviceType; /* floppy, disk, cdrom */
|
||||
private diskType _diskType;
|
||||
private diskProtocol _diskProtocol;
|
||||
private String _sourcePath;
|
||||
private String _sourceHost;
|
||||
private int _sourcePort;
|
||||
private String _authUserName;
|
||||
private String _authSecretUUID;
|
||||
private String _diskLabel;
|
||||
private diskBus _bus;
|
||||
private diskFmtType _diskFmtType; /* qcow2, raw etc. */
|
||||
|
|
@ -461,6 +480,38 @@ public class LibvirtVMDef {
|
|||
_bus = bus;
|
||||
}
|
||||
|
||||
public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort,
|
||||
String authUserName, String authSecretUUID,
|
||||
int devId, diskBus bus, diskProtocol protocol) {
|
||||
_diskType = diskType.NETWORK;
|
||||
_deviceType = deviceType.DISK;
|
||||
_diskFmtType = diskFmtType.RAW;
|
||||
_sourcePath = diskName;
|
||||
_sourceHost = sourceHost;
|
||||
_sourcePort = sourcePort;
|
||||
_authUserName = authUserName;
|
||||
_authSecretUUID = authSecretUUID;
|
||||
_diskLabel = getDevLabel(devId, bus);
|
||||
_bus = bus;
|
||||
_diskProtocol = protocol;
|
||||
}
|
||||
|
||||
public void defNetworkBasedDisk(String diskName, String sourceHost, int sourcePort,
|
||||
String authUserName, String authSecretUUID,
|
||||
String diskLabel, diskBus bus, diskProtocol protocol) {
|
||||
_diskType = diskType.NETWORK;
|
||||
_deviceType = deviceType.DISK;
|
||||
_diskFmtType = diskFmtType.RAW;
|
||||
_sourcePath = diskName;
|
||||
_sourceHost = sourceHost;
|
||||
_sourcePort = sourcePort;
|
||||
_authUserName = authUserName;
|
||||
_authSecretUUID = authSecretUUID;
|
||||
_diskLabel = diskLabel;
|
||||
_bus = bus;
|
||||
_diskProtocol = protocol;
|
||||
}
|
||||
|
||||
public void setReadonly() {
|
||||
_readonly = true;
|
||||
}
|
||||
|
|
@ -527,6 +578,18 @@ public class LibvirtVMDef {
|
|||
diskBuilder.append(" dev='" + _sourcePath + "'");
|
||||
}
|
||||
diskBuilder.append("/>\n");
|
||||
} else if (_diskType == diskType.NETWORK) {
|
||||
diskBuilder.append("<source ");
|
||||
diskBuilder.append(" protocol='" + _diskProtocol + "'");
|
||||
diskBuilder.append(" name='" + _sourcePath + "'");
|
||||
diskBuilder.append(">\n");
|
||||
diskBuilder.append("<host name='" + _sourceHost + "' port='" + _sourcePort + "'/>\n");
|
||||
diskBuilder.append("</source>\n");
|
||||
if (_authUserName != null) {
|
||||
diskBuilder.append("<auth username='" + _authUserName + "'>\n");
|
||||
diskBuilder.append("<secret type='ceph' uuid='" + _authSecretUUID + "'/>\n");
|
||||
diskBuilder.append("</auth>\n");
|
||||
}
|
||||
}
|
||||
diskBuilder.append("<target dev='" + _diskLabel + "'");
|
||||
if (_bus != null) {
|
||||
|
|
@ -898,5 +961,4 @@ public class LibvirtVMDef {
|
|||
|
||||
System.out.println(vm.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,22 @@ public class KVMPhysicalDisk {
|
|||
}
|
||||
}
|
||||
|
||||
public static String RBDStringBuilder(String monHost, int monPort,
|
||||
String authUserName, String authSecret, String image) {
|
||||
String rbdOpts;
|
||||
|
||||
rbdOpts = "rbd:" + image;
|
||||
rbdOpts += ":mon_host=" + monHost + "\\\\:" + monPort;
|
||||
if (authUserName == null) {
|
||||
rbdOpts += ":auth_supported=none";
|
||||
} else {
|
||||
rbdOpts += ":auth_supported=cephx";
|
||||
rbdOpts += ":id=" + authUserName;
|
||||
rbdOpts += ":key=" + authSecret;
|
||||
}
|
||||
return rbdOpts;
|
||||
}
|
||||
|
||||
private PhysicalDiskFormat format;
|
||||
private long size;
|
||||
private long virtualSize;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,16 @@ public interface KVMStoragePool {
|
|||
|
||||
public String getLocalPath();
|
||||
|
||||
public String getSourceHost();
|
||||
|
||||
public String getSourceDir();
|
||||
|
||||
public int getSourcePort();
|
||||
|
||||
public String getAuthUserName();
|
||||
|
||||
public String getAuthSecret();
|
||||
|
||||
public StoragePoolType getType();
|
||||
|
||||
public boolean delete();
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ public class KVMStoragePoolManager {
|
|||
return this._storageAdaptor.getStoragePoolByUri(uri);
|
||||
}
|
||||
|
||||
public KVMStoragePool createStoragePool(String name, String host,
|
||||
String path, StoragePoolType type) {
|
||||
public KVMStoragePool createStoragePool(String name, String host, int port, String path,
|
||||
String userInfo, StoragePoolType type) {
|
||||
KVMStoragePool pool = this._storageAdaptor.createStoragePool(name,
|
||||
host, path, type);
|
||||
host, port, path, userInfo, type);
|
||||
if (type == StoragePoolType.NetworkFilesystem) {
|
||||
KVMHABase.NfsStoragePool nfspool = new KVMHABase.NfsStoragePool(
|
||||
pool.getUuid(), host, path, pool.getLocalPath(),
|
||||
|
|
@ -73,11 +73,16 @@ public class KVMStoragePoolManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template,
|
||||
String name, KVMStoragePool destPool) {
|
||||
return this._storageAdaptor.createDiskFromTemplate(template, name,
|
||||
KVMPhysicalDisk.PhysicalDiskFormat.QCOW2,
|
||||
template.getSize(), destPool);
|
||||
public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name,
|
||||
KVMStoragePool destPool) {
|
||||
if (destPool.getType() == StoragePoolType.RBD) {
|
||||
return this._storageAdaptor.createDiskFromTemplate(template, name,
|
||||
KVMPhysicalDisk.PhysicalDiskFormat.RAW, template.getSize(), destPool);
|
||||
} else {
|
||||
return this._storageAdaptor.createDiskFromTemplate(template, name,
|
||||
KVMPhysicalDisk.PhysicalDiskFormat.QCOW2,
|
||||
template.getSize(), destPool);
|
||||
}
|
||||
}
|
||||
|
||||
public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.LibvirtException;
|
||||
import org.libvirt.Secret;
|
||||
import org.libvirt.StoragePool;
|
||||
import org.libvirt.StoragePoolInfo;
|
||||
import org.libvirt.StorageVol;
|
||||
|
|
@ -32,10 +34,13 @@ import org.libvirt.StoragePoolInfo.StoragePoolState;
|
|||
|
||||
import com.cloud.agent.api.ManageSnapshotCommand;
|
||||
import com.cloud.agent.resource.computing.LibvirtConnection;
|
||||
import com.cloud.agent.resource.computing.LibvirtSecretDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtSecretDef.usage;
|
||||
import com.cloud.agent.resource.computing.LibvirtStoragePoolDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtStoragePoolXMLParser;
|
||||
import com.cloud.agent.resource.computing.LibvirtStorageVolumeDef;
|
||||
import com.cloud.agent.resource.computing.LibvirtStoragePoolDef.poolType;
|
||||
import com.cloud.agent.resource.computing.LibvirtStoragePoolDef.authType;
|
||||
import com.cloud.agent.resource.computing.LibvirtStorageVolumeDef.volFormat;
|
||||
import com.cloud.agent.resource.computing.LibvirtStorageVolumeXMLParser;
|
||||
import com.cloud.agent.storage.KVMPhysicalDisk.PhysicalDiskFormat;
|
||||
|
|
@ -143,7 +148,6 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
|
||||
synchronized (getStoragePool(uuid)) {
|
||||
sp = conn.storagePoolDefineXML(spd.toString(), 0);
|
||||
|
||||
if (sp == null) {
|
||||
s_logger.debug("Failed to define storage pool");
|
||||
return null;
|
||||
|
|
@ -270,6 +274,60 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
|
||||
}
|
||||
|
||||
private StoragePool createRBDStoragePool(Connect conn, String uuid,
|
||||
String host, int port, String userInfo, String path) {
|
||||
|
||||
LibvirtStoragePoolDef spd;
|
||||
StoragePool sp = null;
|
||||
|
||||
String[] userInfoTemp = userInfo.split(":");
|
||||
if (userInfoTemp.length == 2) {
|
||||
s_logger.debug("libvirt secret information found. id: " + userInfoTemp[0] + " secret: " + userInfoTemp[1]);
|
||||
LibvirtSecretDef sd = new LibvirtSecretDef(usage.CEPH, uuid);
|
||||
|
||||
Secret s = null;
|
||||
|
||||
sd.setCephName(userInfoTemp[0]);
|
||||
|
||||
try {
|
||||
s_logger.debug(sd.toString());
|
||||
s = conn.secretDefineXML(sd.toString());
|
||||
s.setValue(Base64.decodeBase64(userInfoTemp[1]));
|
||||
} catch (LibvirtException e) {
|
||||
s_logger.debug(e.toString());
|
||||
if (s != null) {
|
||||
try {
|
||||
s.undefine();
|
||||
s.free();
|
||||
} catch (LibvirtException l) {
|
||||
s_logger.debug("Failed to define secret with: " + l.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
spd = new LibvirtStoragePoolDef(poolType.RBD, uuid, uuid, host, port, path, userInfoTemp[0], authType.CEPH, uuid);
|
||||
} else {
|
||||
spd = new LibvirtStoragePoolDef(poolType.RBD, uuid, uuid, host, port, path, "");
|
||||
}
|
||||
|
||||
try {
|
||||
s_logger.debug(spd.toString());
|
||||
sp = conn.storagePoolDefineXML(spd.toString(), 0);
|
||||
sp.create(0);
|
||||
return sp;
|
||||
} catch (LibvirtException e) {
|
||||
s_logger.debug(e.toString());
|
||||
if (sp != null) {
|
||||
try {
|
||||
sp.undefine();
|
||||
sp.free();
|
||||
} catch (LibvirtException l) {
|
||||
s_logger.debug("Failed to define RBD storage pool with: " + l.toString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public StorageVol copyVolume(StoragePool destPool,
|
||||
LibvirtStorageVolumeDef destVol, StorageVol srcVol, int timeout)
|
||||
throws LibvirtException {
|
||||
|
|
@ -422,11 +480,36 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
if (spd.getPoolType() == LibvirtStoragePoolDef.poolType.NETFS
|
||||
|| spd.getPoolType() == LibvirtStoragePoolDef.poolType.DIR) {
|
||||
type = StoragePoolType.Filesystem;
|
||||
} else if (spd.getPoolType() == LibvirtStoragePoolDef.poolType.RBD) {
|
||||
type = StoragePoolType.RBD;
|
||||
}
|
||||
LibvirtStoragePool pool = new LibvirtStoragePool(uuid,
|
||||
storage.getName(), type, this, storage);
|
||||
pool.setLocalPath(spd.getTargetPath());
|
||||
getStats(pool);
|
||||
|
||||
LibvirtStoragePool pool = new LibvirtStoragePool(uuid, storage.getName(),
|
||||
type, this, storage);
|
||||
|
||||
if (pool.getType() != StoragePoolType.RBD) {
|
||||
pool.setLocalPath(spd.getTargetPath());
|
||||
} else {
|
||||
pool.setLocalPath("");
|
||||
pool.setSourceHost(spd.getSourceHost());
|
||||
pool.setSourcePort(spd.getSourcePort());
|
||||
pool.setSourceDir(spd.getSourceDir());
|
||||
String authUsername = spd.getAuthUserName();
|
||||
if (authUsername != null) {
|
||||
Secret secret = conn.secretLookupByUUIDString(spd.getSecretUUID());
|
||||
String secretValue = new String(Base64.encodeBase64(secret.getByteValue()));
|
||||
pool.setAuthUsername(authUsername);
|
||||
pool.setAuthSecret(secretValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (pool.getType() == StoragePoolType.RBD) {
|
||||
pool.setCapacity(storage.getInfo().capacity);
|
||||
pool.setUsed(storage.getInfo().allocation);
|
||||
} else {
|
||||
getStats(pool);
|
||||
}
|
||||
|
||||
return pool;
|
||||
} catch (LibvirtException e) {
|
||||
throw new CloudRuntimeException(e.toString());
|
||||
|
|
@ -448,6 +531,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
disk.setVirtualSize(vol.getInfo().capacity);
|
||||
if (voldef.getFormat() == null) {
|
||||
disk.setFormat(pool.getDefaultFormat());
|
||||
} else if (pool.getType() == StoragePoolType.RBD) {
|
||||
disk.setFormat(KVMPhysicalDisk.PhysicalDiskFormat.RAW);
|
||||
} else if (voldef.getFormat() == LibvirtStorageVolumeDef.volFormat.QCOW2) {
|
||||
disk.setFormat(KVMPhysicalDisk.PhysicalDiskFormat.QCOW2);
|
||||
} else if (voldef.getFormat() == LibvirtStorageVolumeDef.volFormat.RAW) {
|
||||
|
|
@ -461,8 +546,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public KVMStoragePool createStoragePool(String name, String host,
|
||||
String path, StoragePoolType type) {
|
||||
public KVMStoragePool createStoragePool(String name, String host, int port,
|
||||
String path, String userInfo, StoragePoolType type) {
|
||||
StoragePool sp = null;
|
||||
Connect conn = null;
|
||||
try {
|
||||
|
|
@ -487,6 +572,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
} else if (type == StoragePoolType.SharedMountPoint
|
||||
|| type == StoragePoolType.Filesystem) {
|
||||
sp = CreateSharedStoragePool(conn, name, host, path);
|
||||
} else if (type == StoragePoolType.RBD) {
|
||||
sp = createRBDStoragePool(conn, name, host, port, userInfo, path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -499,15 +586,23 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
LibvirtStoragePoolDef spd = getStoragePoolDef(conn, sp);
|
||||
LibvirtStoragePool pool = new LibvirtStoragePool(name,
|
||||
sp.getName(), type, this, sp);
|
||||
pool.setLocalPath(spd.getTargetPath());
|
||||
|
||||
getStats(pool);
|
||||
|
||||
if (pool.getType() != StoragePoolType.RBD) {
|
||||
pool.setLocalPath(spd.getTargetPath());
|
||||
} else {
|
||||
pool.setLocalPath("");
|
||||
}
|
||||
|
||||
if (pool.getType() == StoragePoolType.RBD) {
|
||||
pool.setCapacity(sp.getInfo().capacity);
|
||||
pool.setUsed(sp.getInfo().allocation);
|
||||
} else {
|
||||
getStats(pool);
|
||||
}
|
||||
return pool;
|
||||
} catch (LibvirtException e) {
|
||||
throw new CloudRuntimeException(e.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -520,6 +615,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
}
|
||||
|
||||
StoragePool sp = null;
|
||||
Secret s = null;
|
||||
|
||||
try {
|
||||
sp = conn.storagePoolLookupByUUIDString(uuid);
|
||||
|
|
@ -527,10 +623,23 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some storage pools, like RBD also have 'secret' information stored in libvirt
|
||||
* Destroy them if they exist
|
||||
*/
|
||||
try {
|
||||
s = conn.secretLookupByUUIDString(uuid);
|
||||
} catch (LibvirtException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
sp.destroy();
|
||||
sp.undefine();
|
||||
sp.free();
|
||||
if (s != null) {
|
||||
s.undefine();
|
||||
s.free();
|
||||
}
|
||||
return true;
|
||||
} catch (LibvirtException e) {
|
||||
throw new CloudRuntimeException(e.toString());
|
||||
|
|
@ -543,6 +652,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
LibvirtStoragePool libvirtPool = (LibvirtStoragePool) pool;
|
||||
StoragePool virtPool = libvirtPool.getPool();
|
||||
LibvirtStorageVolumeDef.volFormat libvirtformat = null;
|
||||
|
||||
if (pool.getType() == StoragePoolType.RBD) {
|
||||
format = PhysicalDiskFormat.RAW;
|
||||
}
|
||||
|
||||
if (format == PhysicalDiskFormat.QCOW2) {
|
||||
libvirtformat = LibvirtStorageVolumeDef.volFormat.QCOW2;
|
||||
} else if (format == PhysicalDiskFormat.RAW) {
|
||||
|
|
@ -580,19 +694,58 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
|
||||
@Override
|
||||
public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template,
|
||||
String name, PhysicalDiskFormat format, long size,
|
||||
KVMStoragePool destPool) {
|
||||
KVMPhysicalDisk disk = destPool.createPhysicalDisk(UUID.randomUUID()
|
||||
.toString(), format, template.getVirtualSize());
|
||||
String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) {
|
||||
|
||||
String newUuid = UUID.randomUUID().toString();
|
||||
KVMStoragePool srcPool = template.getPool();
|
||||
KVMPhysicalDisk disk = null;
|
||||
|
||||
/*
|
||||
With RBD you can't run qemu-img convert with an existing RBD image as destination
|
||||
qemu-img will exit with the error that the destination already exists.
|
||||
So for RBD we don't create the image, but let qemu-img do that for us.
|
||||
|
||||
We then create a KVMPhysicalDisk object that we can return
|
||||
*/
|
||||
|
||||
if (destPool.getType() != StoragePoolType.RBD) {
|
||||
disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize());
|
||||
|
||||
if (format == PhysicalDiskFormat.QCOW2) {
|
||||
Script.runSimpleBashScript("qemu-img create -f "
|
||||
+ template.getFormat() + " -b " + template.getPath() + " "
|
||||
+ disk.getPath());
|
||||
} else if (format == PhysicalDiskFormat.RAW) {
|
||||
Script.runSimpleBashScript("qemu-img convert -f "
|
||||
+ template.getFormat() + " -O raw " + template.getPath()
|
||||
+ " " + disk.getPath());
|
||||
} else {
|
||||
disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool);
|
||||
disk.setFormat(format);
|
||||
disk.setSize(template.getVirtualSize());
|
||||
disk.setVirtualSize(disk.getSize());
|
||||
|
||||
if (srcPool.getType() != StoragePoolType.RBD) {
|
||||
Script.runSimpleBashScript("qemu-img convert"
|
||||
+ " -f " + template.getFormat()
|
||||
+ " -O " + format
|
||||
+ " " + template.getPath()
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
||||
destPool.getSourcePort(),
|
||||
destPool.getAuthUserName(),
|
||||
destPool.getAuthSecret(),
|
||||
disk.getPath()));
|
||||
} else {
|
||||
template.setFormat(PhysicalDiskFormat.RAW);
|
||||
Script.runSimpleBashScript("qemu-img convert"
|
||||
+ " -f " + template.getFormat()
|
||||
+ " -O " + format
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(),
|
||||
srcPool.getSourcePort(),
|
||||
srcPool.getAuthUserName(),
|
||||
srcPool.getAuthSecret(),
|
||||
template.getPath())
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
||||
destPool.getSourcePort(),
|
||||
destPool.getAuthUserName(),
|
||||
destPool.getAuthSecret(),
|
||||
disk.getPath()));
|
||||
}
|
||||
}
|
||||
return disk;
|
||||
}
|
||||
|
|
@ -625,14 +778,60 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
@Override
|
||||
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name,
|
||||
KVMStoragePool destPool) {
|
||||
KVMPhysicalDisk newDisk = destPool.createPhysicalDisk(name,
|
||||
disk.getVirtualSize());
|
||||
String sourcePath = disk.getPath();
|
||||
String destPath = newDisk.getPath();
|
||||
|
||||
Script.runSimpleBashScript("qemu-img convert -f " + disk.getFormat()
|
||||
+ " -O " + newDisk.getFormat() + " " + sourcePath + " "
|
||||
+ destPath);
|
||||
/*
|
||||
With RBD you can't run qemu-img convert with an existing RBD image as destination
|
||||
qemu-img will exit with the error that the destination already exists.
|
||||
So for RBD we don't create the image, but let qemu-img do that for us.
|
||||
|
||||
We then create a KVMPhysicalDisk object that we can return
|
||||
*/
|
||||
|
||||
KVMPhysicalDisk newDisk;
|
||||
if (destPool.getType() != StoragePoolType.RBD) {
|
||||
newDisk = destPool.createPhysicalDisk(name, disk.getVirtualSize());
|
||||
} else {
|
||||
newDisk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + name, name, destPool);
|
||||
newDisk.setFormat(PhysicalDiskFormat.RAW);
|
||||
newDisk.setSize(disk.getVirtualSize());
|
||||
newDisk.setVirtualSize(disk.getSize());
|
||||
}
|
||||
|
||||
KVMStoragePool srcPool = disk.getPool();
|
||||
String destPath = newDisk.getPath();
|
||||
String sourcePath = disk.getPath();
|
||||
PhysicalDiskFormat sourceFormat = disk.getFormat();
|
||||
PhysicalDiskFormat destFormat = newDisk.getFormat();
|
||||
|
||||
if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) {
|
||||
Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat
|
||||
+ " -O " + destFormat
|
||||
+ " " + sourcePath
|
||||
+ " " + destPath);
|
||||
} else if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() == StoragePoolType.RBD)) {
|
||||
Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat
|
||||
+ " -O " + destFormat
|
||||
+ " " + sourcePath
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
||||
destPool.getSourcePort(),
|
||||
destPool.getAuthUserName(),
|
||||
destPool.getAuthSecret(),
|
||||
destPath));
|
||||
} else {
|
||||
Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat
|
||||
+ " -O " + destFormat
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(),
|
||||
srcPool.getSourcePort(),
|
||||
srcPool.getAuthUserName(),
|
||||
srcPool.getAuthSecret(),
|
||||
sourcePath)
|
||||
+ " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
|
||||
destPool.getSourcePort(),
|
||||
destPool.getAuthUserName(),
|
||||
destPool.getAuthSecret(),
|
||||
destPath));
|
||||
}
|
||||
|
||||
return newDisk;
|
||||
}
|
||||
|
||||
|
|
@ -658,7 +857,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
protocal = StoragePoolType.NetworkFilesystem;
|
||||
}
|
||||
|
||||
return createStoragePool(uuid, sourceHost, sourcePath, protocal);
|
||||
return createStoragePool(uuid, sourceHost, 0, sourcePath, "", protocal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -699,5 +898,4 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
|||
protected StoragePoolType type;
|
||||
protected StorageAdaptor _storageAdaptor;
|
||||
protected StoragePool _pool;
|
||||
protected String authUsername;
|
||||
protected String authSecret;
|
||||
protected String sourceHost;
|
||||
protected int sourcePort;
|
||||
protected String sourceDir;
|
||||
|
||||
public LibvirtStoragePool(String uuid, String name, StoragePoolType type,
|
||||
StorageAdaptor adaptor, StoragePool pool) {
|
||||
|
|
@ -137,6 +142,51 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
|||
this.localPath = localPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthUserName() {
|
||||
return this.authUsername;
|
||||
}
|
||||
|
||||
public void setAuthUsername(String authUsername) {
|
||||
this.authUsername = authUsername;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthSecret() {
|
||||
return this.authSecret;
|
||||
}
|
||||
|
||||
public void setAuthSecret(String authSecret) {
|
||||
this.authSecret = authSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceHost() {
|
||||
return this.sourceHost;
|
||||
}
|
||||
|
||||
public void setSourceHost(String host) {
|
||||
this.sourceHost = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourcePort() {
|
||||
return this.sourcePort;
|
||||
}
|
||||
|
||||
public void setSourcePort(int port) {
|
||||
this.sourcePort = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceDir() {
|
||||
return this.sourceDir;
|
||||
}
|
||||
|
||||
public void setSourceDir(String dir) {
|
||||
this.sourceDir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoragePoolType getType() {
|
||||
return this.type;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ public interface StorageAdaptor {
|
|||
public KVMPhysicalDisk getPhysicalDisk(String volumeUuid,
|
||||
KVMStoragePool pool);
|
||||
|
||||
public KVMStoragePool createStoragePool(String name, String host,
|
||||
String path, StoragePoolType type);
|
||||
public KVMStoragePool createStoragePool(String name, String host, int port,
|
||||
String path, String userInfo, StoragePoolType type);
|
||||
|
||||
public boolean deleteStoragePool(String uuid);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public class StorageFilerTO {
|
|||
String uuid;
|
||||
String host;
|
||||
String path;
|
||||
String userInfo;
|
||||
int port;
|
||||
StoragePoolType type;
|
||||
|
||||
|
|
@ -34,6 +35,7 @@ public class StorageFilerTO {
|
|||
this.path = pool.getPath();
|
||||
this.type = pool.getPoolType();
|
||||
this.uuid = pool.getUuid();
|
||||
this.userInfo = pool.getUserInfo();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
|
|
@ -52,6 +54,10 @@ public class StorageFilerTO {
|
|||
return path;
|
||||
}
|
||||
|
||||
public String getUserInfo() {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
|
|||
// ///////////////////////////////////////////////////
|
||||
|
||||
@IdentityMapper(entityTableName="user_ip_address")
|
||||
@Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.LONG, description = "the IP address id of the port forwarding rule")
|
||||
@Parameter(name = ApiConstants.IP_ADDRESS_ID, type = CommandType.LONG, required=true, description = "the IP address id of the port forwarding rule")
|
||||
private Long ipAddressId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.")
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ public class Storage {
|
|||
Iscsi(true), // for e.g., ZFS Comstar
|
||||
ISO(false), // for iso image
|
||||
LVM(false), // XenServer local LVM SR
|
||||
RBD(true),
|
||||
SharedMountPoint(true),
|
||||
VMFS(true), // VMware VMFS storage
|
||||
PreSetup(true), // for XenServer, Storage Pool is set up by customers.
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ public interface StoragePool {
|
|||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* @return the user information / credentials for the storage host
|
||||
*/
|
||||
String getUserInfo();
|
||||
|
||||
/**
|
||||
* @return the storage pool represents a shared storage resource
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ public class StoragePoolVO implements StoragePool, Identity {
|
|||
@Column(name="port")
|
||||
private int port;
|
||||
|
||||
@Column(name="user_info")
|
||||
private String userInfo;
|
||||
|
||||
@Column(name="cluster_id")
|
||||
private Long clusterId;
|
||||
|
||||
|
|
@ -179,6 +182,11 @@ public class StoragePoolVO implements StoragePool, Identity {
|
|||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserInfo() {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public StoragePoolVO(long poolId, String name, String uuid, StoragePoolType type,
|
||||
long dataCenterId, Long podId, long availableBytes, long capacityBytes, String hostAddress, int port, String hostPath) {
|
||||
|
|
@ -208,6 +216,16 @@ public class StoragePoolVO implements StoragePool, Identity {
|
|||
this.setStatus(StoragePoolStatus.Up);
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public StoragePoolVO(StoragePoolType type, String hostAddress, int port, String path, String userInfo) {
|
||||
this.poolType = type;
|
||||
this.hostAddress = hostAddress;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
this.userInfo = userInfo;
|
||||
this.setStatus(StoragePoolStatus.Up);
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public void setStatus(StoragePoolStatus status)
|
||||
{
|
||||
|
|
@ -233,6 +251,10 @@ public class StoragePoolVO implements StoragePool, Identity {
|
|||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setUserInfo(String userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
|
|
|
|||
|
|
@ -2786,8 +2786,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||
if (s_logger.isInfoEnabled()) {
|
||||
s_logger.info("Executing resource PingTestCommand: " + _gson.toJson(cmd));
|
||||
}
|
||||
|
||||
return new Answer(cmd);
|
||||
String controlIp = cmd.getRouterIp();
|
||||
String args = " -c 1 -n -q " + cmd.getPrivateIp();
|
||||
try {
|
||||
VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, "/bin/ping" + args);
|
||||
if(result.first())
|
||||
return new Answer(cmd);
|
||||
} catch (Exception e) {
|
||||
s_logger.error("Unable to execute ping command on DomR (" + controlIp + "), domR may not be ready yet. failure due to "
|
||||
+ VmwareHelper.getExceptionMessage(e), e);
|
||||
}
|
||||
return new Answer(cmd,false,"PingTestCommand failed");
|
||||
}
|
||||
|
||||
protected Answer execute(CheckOnHostCommand cmd) {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@
|
|||
package com.cloud.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
|
@ -55,6 +57,7 @@ import com.cloud.org.Cluster;
|
|||
import com.cloud.org.Grouping;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolHostVO;
|
||||
import com.cloud.storage.StoragePoolVO;
|
||||
|
|
@ -98,6 +101,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner {
|
|||
@Inject protected StoragePoolDao _storagePoolDao;
|
||||
@Inject protected CapacityDao _capacityDao;
|
||||
@Inject protected AccountManager _accountMgr;
|
||||
@Inject protected StorageManager _storageMgr;
|
||||
|
||||
@Inject(adapter=StoragePoolAllocator.class)
|
||||
protected Adapters<StoragePoolAllocator> _storagePoolAllocators;
|
||||
|
|
@ -638,25 +642,56 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner {
|
|||
s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM");
|
||||
|
||||
boolean hostCanAccessPool = false;
|
||||
boolean haveEnoughSpace = false;
|
||||
Map<Volume, StoragePool> storage = new HashMap<Volume, StoragePool>();
|
||||
TreeSet<Volume> volumesOrderBySizeDesc = new TreeSet<Volume>(new Comparator<Volume>() {
|
||||
@Override
|
||||
public int compare(Volume v1, Volume v2) {
|
||||
if(v1.getSize() < v2.getSize())
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
volumesOrderBySizeDesc.addAll(suitableVolumeStoragePools.keySet());
|
||||
boolean multipleVolume = volumesOrderBySizeDesc.size() > 1;
|
||||
for(Host potentialHost : suitableHosts){
|
||||
for(Volume vol : suitableVolumeStoragePools.keySet()){
|
||||
Map<StoragePool,List<Volume>> volumeAllocationMap = new HashMap<StoragePool,List<Volume>>();
|
||||
for(Volume vol : volumesOrderBySizeDesc){
|
||||
haveEnoughSpace = false;
|
||||
s_logger.debug("Checking if host: "+potentialHost.getId() +" can access any suitable storage pool for volume: "+ vol.getVolumeType());
|
||||
List<StoragePool> volumePoolList = suitableVolumeStoragePools.get(vol);
|
||||
hostCanAccessPool = false;
|
||||
for(StoragePool potentialSPool : volumePoolList){
|
||||
if(hostCanAccessSPool(potentialHost, potentialSPool)){
|
||||
storage.put(vol, potentialSPool);
|
||||
hostCanAccessPool = true;
|
||||
if(multipleVolume){
|
||||
List<Volume> requestVolumes = null;
|
||||
if(volumeAllocationMap.containsKey(potentialSPool))
|
||||
requestVolumes = volumeAllocationMap.get(potentialSPool);
|
||||
else
|
||||
requestVolumes = new ArrayList<Volume>();
|
||||
requestVolumes.add(vol);
|
||||
|
||||
if(!_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool))
|
||||
continue;
|
||||
volumeAllocationMap.put(potentialSPool,requestVolumes);
|
||||
}
|
||||
storage.put(vol, potentialSPool);
|
||||
haveEnoughSpace = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hostCanAccessPool){
|
||||
break;
|
||||
}
|
||||
if(!haveEnoughSpace) {
|
||||
s_logger.warn("insufficient capacity to allocate all volumes");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hostCanAccessPool){
|
||||
s_logger.debug("Found a potential host " + "id: "+potentialHost.getId() + " name: " +potentialHost.getName()+ " and associated storage pools for this VM");
|
||||
if(hostCanAccessPool && haveEnoughSpace){
|
||||
s_logger.debug("Found a potential host " + "id: "+potentialHost.getId() + " name: " +potentialHost.getName() + " and associated storage pools for this VM");
|
||||
return new Pair<Host, Map<Volume, StoragePool>>(potentialHost, storage);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -331,9 +331,11 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager {
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_ENABLE_STATIC_NAT, eventDescription = "enabling static nat")
|
||||
public boolean enableStaticNat(long ipId, long vmId, boolean isSystemVm) throws NetworkRuleConflictException, ResourceUnavailableException {
|
||||
UserContext ctx = UserContext.current();
|
||||
Account caller = ctx.getCaller();
|
||||
UserContext.current().setEventDetails("Ip Id: " + ipId);
|
||||
|
||||
IPAddressVO ipAddress = _ipAddressDao.findById(ipId);
|
||||
if (ipAddress == null) {
|
||||
|
|
@ -1052,6 +1054,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager {
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DISABLE_STATIC_NAT, eventDescription = "disabling static nat", async=true)
|
||||
public boolean disableStaticNat(long ipId) throws ResourceUnavailableException, NetworkRuleConflictException, InsufficientAddressCapacityException {
|
||||
UserContext ctx = UserContext.current();
|
||||
Account caller = ctx.getCaller();
|
||||
|
|
|
|||
|
|
@ -235,4 +235,6 @@ public interface StorageManager extends StorageService, Manager {
|
|||
String getSupportedImageFormatForCluster(Long clusterId);
|
||||
|
||||
HypervisorType getHypervisorTypeFromFormat(ImageFormat format);
|
||||
}
|
||||
|
||||
boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ import com.cloud.api.commands.UpdateStoragePoolCmd;
|
|||
import com.cloud.api.commands.UploadVolumeCmd;
|
||||
import com.cloud.async.AsyncJobManager;
|
||||
import com.cloud.capacity.Capacity;
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.capacity.CapacityState;
|
||||
import com.cloud.capacity.CapacityVO;
|
||||
import com.cloud.capacity.dao.CapacityDao;
|
||||
|
|
@ -133,6 +134,7 @@ import com.cloud.resource.ResourceManager;
|
|||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.server.ManagementServer;
|
||||
import com.cloud.server.ResourceTag.TaggedResourceType;
|
||||
import com.cloud.server.StatsCollector;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
|
|
@ -274,6 +276,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
@Inject
|
||||
protected CapacityDao _capacityDao;
|
||||
@Inject
|
||||
protected CapacityManager _capacityMgr;
|
||||
@Inject
|
||||
protected DiskOfferingDao _diskOfferingDao;
|
||||
@Inject
|
||||
protected AccountDao _accountDao;
|
||||
|
|
@ -352,6 +356,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
private StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
|
||||
private int _customDiskOfferingMinSize = 1;
|
||||
private int _customDiskOfferingMaxSize = 1024;
|
||||
private double _storageUsedThreshold = 1.0d;
|
||||
private double _storageAllocatedThreshold = 1.0d;
|
||||
protected BigDecimal _storageOverprovisioningFactor = new BigDecimal(1);
|
||||
|
||||
public boolean share(VMInstanceVO vm, List<VolumeVO> vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException {
|
||||
|
||||
|
|
@ -955,6 +962,19 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
String time = configs.get("storage.cleanup.interval");
|
||||
_storageCleanupInterval = NumbersUtil.parseInt(time, 86400);
|
||||
|
||||
String storageUsedThreshold = configDao.getValue(Config.StorageCapacityDisableThreshold.key());
|
||||
if (storageUsedThreshold != null) {
|
||||
_storageUsedThreshold = Double.parseDouble(storageUsedThreshold);
|
||||
}
|
||||
|
||||
String storageAllocatedThreshold = configDao.getValue(Config.StorageAllocatedCapacityDisableThreshold.key());
|
||||
if (storageAllocatedThreshold != null) {
|
||||
_storageAllocatedThreshold = Double.parseDouble(storageAllocatedThreshold);
|
||||
}
|
||||
|
||||
String globalStorageOverprovisioningFactor = configs.get("storage.overprovisioning.factor");
|
||||
_storageOverprovisioningFactor = new BigDecimal(NumbersUtil.parseFloat(globalStorageOverprovisioningFactor, 2.0f));
|
||||
|
||||
s_logger.info("Storage cleanup enabled: " + _storageCleanupEnabled + ", interval: " + _storageCleanupInterval + ", template cleanup enabled: " + _templateCleanupEnabled);
|
||||
|
||||
String workers = configs.get("expunge.workers");
|
||||
|
|
@ -1257,10 +1277,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
if (uriPath == null) {
|
||||
throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path");
|
||||
}
|
||||
} else if (uri.getScheme().equalsIgnoreCase("clvm")) {
|
||||
} else if (uri.getScheme().equalsIgnoreCase("rbd")) {
|
||||
String uriPath = uri.getPath();
|
||||
if (uriPath == null) {
|
||||
throw new InvalidParameterValueException("host or path is null, should be clvm://localhost/path");
|
||||
throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool");
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
|
|
@ -1283,6 +1303,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
String scheme = uri.getScheme();
|
||||
String storageHost = uri.getHost();
|
||||
String hostPath = uri.getPath();
|
||||
String userInfo = uri.getUserInfo();
|
||||
int port = uri.getPort();
|
||||
StoragePoolVO pool = null;
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
|
|
@ -1303,6 +1324,11 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
pool = new StoragePoolVO(StoragePoolType.Filesystem, "localhost", 0, hostPath);
|
||||
} else if (scheme.equalsIgnoreCase("sharedMountPoint")) {
|
||||
pool = new StoragePoolVO(StoragePoolType.SharedMountPoint, storageHost, 0, hostPath);
|
||||
} else if (scheme.equalsIgnoreCase("rbd")) {
|
||||
if (port == -1) {
|
||||
port = 6789;
|
||||
}
|
||||
pool = new StoragePoolVO(StoragePoolType.RBD, storageHost, port, hostPath.replaceFirst("/", ""), userInfo);
|
||||
} else if (scheme.equalsIgnoreCase("PreSetup")) {
|
||||
pool = new StoragePoolVO(StoragePoolType.PreSetup, storageHost, 0, hostPath);
|
||||
} else if (scheme.equalsIgnoreCase("iscsi")) {
|
||||
|
|
@ -1601,7 +1627,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
s_logger.debug("creating pool " + pool.getName() + " on host " + hostId);
|
||||
if (pool.getPoolType() != StoragePoolType.NetworkFilesystem && pool.getPoolType() != StoragePoolType.Filesystem && pool.getPoolType() != StoragePoolType.IscsiLUN
|
||||
&& pool.getPoolType() != StoragePoolType.Iscsi && pool.getPoolType() != StoragePoolType.VMFS && pool.getPoolType() != StoragePoolType.SharedMountPoint
|
||||
&& pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2) {
|
||||
&& pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2 && pool.getPoolType() != StoragePoolType.RBD) {
|
||||
s_logger.warn(" Doesn't support storage pool type " + pool.getPoolType());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3906,5 +3932,81 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
|||
return HypervisorType.None;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkUsagedSpace(StoragePool pool){
|
||||
StatsCollector sc = StatsCollector.getInstance();
|
||||
if (sc != null) {
|
||||
long totalSize = pool.getCapacityBytes();
|
||||
StorageStats stats = sc.getStoragePoolStats(pool.getId());
|
||||
if(stats == null){
|
||||
stats = sc.getStorageStats(pool.getId());
|
||||
}
|
||||
if (stats != null) {
|
||||
double usedPercentage = ((double)stats.getByteUsed() / (double)totalSize);
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Checking pool " + pool.getId() + " for storage, totalSize: " + pool.getCapacityBytes() + ", usedBytes: " + stats.getByteUsed() + ", usedPct: " + usedPercentage + ", disable threshold: " + _storageUsedThreshold);
|
||||
}
|
||||
if (usedPercentage >= _storageUsedThreshold) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Insufficient space on pool: " + pool.getId() + " since its usage percentage: " +usedPercentage + " has crossed the pool.storage.capacity.disablethreshold: " + _storageUsedThreshold);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean storagePoolHasEnoughSpace(List<Volume> volumes, StoragePool pool) {
|
||||
if(volumes == null || volumes.isEmpty())
|
||||
return false;
|
||||
|
||||
if(!checkUsagedSpace(pool))
|
||||
return false;
|
||||
|
||||
// allocated space includes template of specified volume
|
||||
StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId());
|
||||
long allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null);
|
||||
long totalAskingSize = 0;
|
||||
for (Volume volume : volumes) {
|
||||
if(volume.getTemplateId()!=null){
|
||||
VMTemplateVO tmpl = _templateDao.findById(volume.getTemplateId());
|
||||
if (tmpl.getFormat() != ImageFormat.ISO){
|
||||
allocatedSizeWithtemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, tmpl);
|
||||
}
|
||||
}
|
||||
if(volume.getState() != Volume.State.Ready)
|
||||
totalAskingSize = totalAskingSize + volume.getSize();
|
||||
}
|
||||
|
||||
long totalOverProvCapacity;
|
||||
if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) {
|
||||
totalOverProvCapacity = _storageOverprovisioningFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue();// All this for the inaccuracy of floats for big number multiplication.
|
||||
}else {
|
||||
totalOverProvCapacity = pool.getCapacityBytes();
|
||||
}
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Checking pool: " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + _storageAllocatedThreshold);
|
||||
}
|
||||
|
||||
double usedPercentage = (allocatedSizeWithtemplate + totalAskingSize) / (double)(totalOverProvCapacity);
|
||||
if (usedPercentage > _storageAllocatedThreshold){
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + " since its allocated percentage: " +usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + _storageAllocatedThreshold + ", skipping this pool");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (totalOverProvCapacity < (allocatedSizeWithtemplate + totalAskingSize)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + ", not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithtemplate + ", askingSize : " + totalAskingSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package com.cloud.storage.allocator;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
|
@ -27,7 +28,6 @@ import javax.naming.ConfigurationException;
|
|||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.configuration.dao.ConfigurationDao;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
|
|
@ -41,13 +41,11 @@ import com.cloud.storage.StorageManager;
|
|||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolStatus;
|
||||
import com.cloud.storage.StoragePoolVO;
|
||||
import com.cloud.storage.StorageStats;
|
||||
import com.cloud.storage.VMTemplateHostVO;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateSwiftVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.Volume.Type;
|
||||
import com.cloud.storage.dao.StoragePoolDao;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
|
|
@ -58,7 +56,6 @@ import com.cloud.storage.dao.VolumeDao;
|
|||
import com.cloud.storage.swift.SwiftManager;
|
||||
import com.cloud.template.TemplateManager;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.component.Inject;
|
||||
import com.cloud.vm.DiskProfile;
|
||||
|
|
@ -83,8 +80,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
|
|||
long _extraBytesPerVolume = 0;
|
||||
Random _rand;
|
||||
boolean _dontMatter;
|
||||
double _storageUsedThreshold = 1.0d;
|
||||
double _storageAllocatedThreshold = 1.0d;
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
|
|
@ -97,17 +92,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
|
|||
|
||||
_extraBytesPerVolume = 0;
|
||||
|
||||
|
||||
String storageUsedThreshold = _configDao.getValue(Config.StorageCapacityDisableThreshold.key());
|
||||
if (storageUsedThreshold != null) {
|
||||
_storageUsedThreshold = Double.parseDouble(storageUsedThreshold);
|
||||
}
|
||||
|
||||
String storageAllocatedThreshold = _configDao.getValue(Config.StorageAllocatedCapacityDisableThreshold.key());
|
||||
if (storageAllocatedThreshold != null) {
|
||||
_storageAllocatedThreshold = Double.parseDouble(storageAllocatedThreshold);
|
||||
}
|
||||
|
||||
_rand = new Random(System.currentTimeMillis());
|
||||
|
||||
_dontMatter = Boolean.parseBoolean(configs.get("storage.overwrite.provisioning"));
|
||||
|
|
@ -192,60 +176,15 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
|
|||
return false;
|
||||
}
|
||||
|
||||
// check the used size against the total size, skip this host if it's greater than the configured
|
||||
// capacity check "storage.capacity.threshold"
|
||||
if (sc != null) {
|
||||
long totalSize = pool.getCapacityBytes();
|
||||
StorageStats stats = sc.getStoragePoolStats(pool.getId());
|
||||
if(stats == null){
|
||||
stats = sc.getStorageStats(pool.getId());
|
||||
}
|
||||
if (stats != null) {
|
||||
double usedPercentage = ((double)stats.getByteUsed() / (double)totalSize);
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Attempting to look for pool " + pool.getId() + " for storage, totalSize: " + pool.getCapacityBytes() + ", usedBytes: " + stats.getByteUsed() + ", usedPct: " + usedPercentage + ", disable threshold: " + _storageUsedThreshold);
|
||||
}
|
||||
if (usedPercentage >= _storageUsedThreshold) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage since its usage percentage: " +usedPercentage + " has crossed the pool.storage.capacity.disablethreshold: " + _storageUsedThreshold + ", skipping this pool");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long totalAllocatedSize = _capacityMgr.getAllocatedPoolCapacity(pool, null);
|
||||
long askingSize = dskCh.getSize();
|
||||
|
||||
long totalOverProvCapacity;
|
||||
if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) {
|
||||
totalOverProvCapacity = _storageOverprovisioningFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue();// All this for the inaccuracy of floats for big number multiplication.
|
||||
}else {
|
||||
totalOverProvCapacity = pool.getCapacityBytes();
|
||||
}
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Attempting to look for pool " + pool.getId() + " for storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + totalAllocatedSize + ", askingSize : " + askingSize + ", allocated disable threshold: " + _storageAllocatedThreshold);
|
||||
}
|
||||
|
||||
double usedPercentage = (totalAllocatedSize + askingSize) / (double)(totalOverProvCapacity);
|
||||
if (usedPercentage > _storageAllocatedThreshold){
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage since its allocated percentage: " +usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + _storageAllocatedThreshold + ", skipping this pool");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (totalOverProvCapacity < (totalAllocatedSize + askingSize)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Cannot allocate this pool " + pool.getId() + " for storage, not enough storage, maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + totalAllocatedSize + ", askingSize : " + askingSize);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// check capacity
|
||||
Volume volume = _volumeDao.findById(dskCh.getVolumeId());
|
||||
List<Volume> requestVolumes = new ArrayList<Volume>();
|
||||
requestVolumes.add(volume);
|
||||
return _storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String chooseStorageIp(VirtualMachine vm, Host host, Host storage) {
|
||||
|
|
|
|||
|
|
@ -31,18 +31,25 @@ import org.apache.log4j.Logger;
|
|||
import com.cloud.deploy.DeploymentPlan;
|
||||
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||
import com.cloud.server.StatsCollector;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.vm.DiskProfile;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.cloud.utils.component.Inject;
|
||||
|
||||
@Local(value=StoragePoolAllocator.class)
|
||||
public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator {
|
||||
private static final Logger s_logger = Logger.getLogger(FirstFitStoragePoolAllocator.class);
|
||||
protected String _allocationAlgorithm = "random";
|
||||
|
||||
@Inject
|
||||
DiskOfferingDao _diskOfferingDao;
|
||||
|
||||
@Override
|
||||
public boolean allocatorIsCorrectType(DiskProfile dskCh) {
|
||||
|
|
@ -97,10 +104,16 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator {
|
|||
s_logger.debug("FirstFitStoragePoolAllocator has " + pools.size() + " pools to check for allocation");
|
||||
}
|
||||
|
||||
DiskOfferingVO diskOffering = _diskOfferingDao.findById(dskCh.getDiskOfferingId());
|
||||
for (StoragePoolVO pool: pools) {
|
||||
if(suitablePools.size() == returnUpTo){
|
||||
break;
|
||||
}
|
||||
if (diskOffering.getSystemUse() && pool.getPoolType() == StoragePoolType.RBD) {
|
||||
s_logger.debug("Skipping RBD pool " + pool.getName() + " as a suitable pool. RBD is not supported for System VM's");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkPool(avoid, pool, dskCh, template, null, sc, plan)) {
|
||||
suitablePools.add(pool);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ import com.cloud.storage.SnapshotPolicyVO;
|
|||
import com.cloud.storage.SnapshotScheduleVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolVO;
|
||||
|
|
@ -298,6 +299,13 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
|||
}
|
||||
}
|
||||
StoragePoolVO srcPool = _storagePoolDao.findById(volume.getPoolId());
|
||||
|
||||
// RBD volumes do not support snapshotting in the way CloudStack does it.
|
||||
// For now we leave the snapshot feature disabled for RBD volumes
|
||||
if (srcPool.getPoolType() == StoragePoolType.RBD) {
|
||||
throw new CloudRuntimeException("RBD volumes do not support snapshotting");
|
||||
}
|
||||
|
||||
ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshotId, volume.getPath(), srcPool, preSnapshotPath, snapshot.getName(), vmName);
|
||||
|
||||
ManageSnapshotAnswer answer = (ManageSnapshotAnswer) sendToPool(volume, cmd);
|
||||
|
|
|
|||
|
|
@ -1477,6 +1477,7 @@ CREATE TABLE `cloud`.`storage_pool` (
|
|||
`available_bytes` bigint unsigned,
|
||||
`capacity_bytes` bigint unsigned,
|
||||
`host_address` varchar(255) NOT NULL COMMENT 'FQDN or IP of storage server',
|
||||
`user_info` varchar(255) NULL COMMENT 'Authorization information for the storage pool. Used by network filesystems',
|
||||
`path` varchar(255) NOT NULL COMMENT 'Filesystem path that is shared',
|
||||
`created` datetime COMMENT 'date the pool created',
|
||||
`removed` datetime COMMENT 'date removed if not null',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
-- 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.
|
||||
|
||||
--;
|
||||
-- Schema upgrade from 3.0.3 to 4.0.0;
|
||||
--;
|
||||
|
||||
ALTER TABLE `storage_pool` ADD `user_info` VARCHAR( 255 ) NULL COMMENT 'Authorization information for the storage pool. Used by network filesystems' AFTER `host_address`;
|
||||
|
|
@ -2046,6 +2046,7 @@ dictionary = {
|
|||
'label.SR.name ': '<fmt:message key="label.SR.name " />',
|
||||
'label.SharedMountPoint': '<fmt:message key="label.SharedMountPoint" />',
|
||||
'label.clvm': '<fmt:message key="label.clvm" />',
|
||||
'label.rbd': '<fmt:message key="label.rbd" />',
|
||||
'label.volgroup': '<fmt:message key="label.volgroup" />',
|
||||
'label.VMFS.datastore': '<fmt:message key="label.VMFS.datastore" />',
|
||||
'label.network.device': '<fmt:message key="label.network.device" />',
|
||||
|
|
|
|||
|
|
@ -475,6 +475,31 @@ function SharedMountPointURL(server, path) {
|
|||
return url;
|
||||
}
|
||||
|
||||
function rbdURL(monitor, pool, id, secret) {
|
||||
var url;
|
||||
|
||||
/*
|
||||
Replace the + and / symbols by - and _ to have URL-safe base64 going to the API
|
||||
It's hacky, but otherwise we'll confuse java.net.URI which splits the incoming URI
|
||||
*/
|
||||
secret = str.replace("+", "-");
|
||||
secret = str.replace("/", "_");
|
||||
|
||||
if (id != null && secret != null) {
|
||||
monitor = id + ":" + secret + "@" + monitor;
|
||||
}
|
||||
|
||||
if(pool.substring(0,1) != "/")
|
||||
pool = "/" + pool;
|
||||
|
||||
if(monitor.indexOf("://")==-1)
|
||||
url = "rbd://" + monitor + pool;
|
||||
else
|
||||
url = monitor + pool;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
function clvmURL(vgname) {
|
||||
var url;
|
||||
if(vgname.indexOf("://")==-1)
|
||||
|
|
|
|||
|
|
@ -7494,6 +7494,7 @@
|
|||
var items = [];
|
||||
items.push({id: "nfs", description: "nfs"});
|
||||
items.push({id: "SharedMountPoint", description: "SharedMountPoint"});
|
||||
items.push({id: "rbd", description: "RBD"});
|
||||
args.response.success({data: items});
|
||||
}
|
||||
else if(selectedClusterObj.hypervisortype == "XenServer") {
|
||||
|
|
@ -7677,6 +7678,27 @@
|
|||
$form.find('.form-item[rel=vCenterDataCenter]').hide();
|
||||
$form.find('.form-item[rel=vCenterDataStore]').hide();
|
||||
}
|
||||
else if(protocol == "rbd") {
|
||||
$form.find('.form-item[rel=rbdmonitor]').css('display', 'inline-block');
|
||||
$form.find('.form-item[rel=rbdmonitor]').find(".name").find("label").text("RADOS Monitor:");
|
||||
|
||||
$form.find('.form-item[rel=rbdpool]').css('display', 'inline-block');
|
||||
$form.find('.form-item[rel=rbdpool]').find(".name").find("label").text("RADOS Pool:");
|
||||
|
||||
$form.find('.form-item[rel=rbdid]').css('display', 'inline-block');
|
||||
$form.find('.form-item[rel=rbdid]').find(".name").find("label").text("RADOS User:");
|
||||
|
||||
$form.find('.form-item[rel=rbdsecret]').css('display', 'inline-block');
|
||||
$form.find('.form-item[rel=rbdsecret]').find(".name").find("label").text("RADOS Secret:");
|
||||
|
||||
$form.find('.form-item[rel=server]').hide();
|
||||
$form.find('.form-item[rel=iqn]').hide();
|
||||
$form.find('.form-item[rel=lun]').hide();
|
||||
$form.find('.form-item[rel=volumegroup]').hide();
|
||||
$form.find('.form-item[rel=path]').hide();
|
||||
$form.find('.form-item[rel=vCenterDataCenter]').hide();
|
||||
$form.find('.form-item[rel=vCenterDataStore]').hide();
|
||||
}
|
||||
else {
|
||||
//$dialogAddPool.find("#add_pool_server_container").show();
|
||||
$form.find('.form-item[rel=server]').css('display', 'inline-block');
|
||||
|
|
@ -7744,6 +7766,28 @@
|
|||
validation: { required: true },
|
||||
isHidden: true
|
||||
},
|
||||
|
||||
// RBD
|
||||
rbdmonitor: {
|
||||
label: 'label.rbd.monitor',
|
||||
validation: { required: true },
|
||||
isHidden: true
|
||||
},
|
||||
rbdpool: {
|
||||
label: 'label.rbd.pool',
|
||||
validation: { required: true },
|
||||
isHidden: true
|
||||
},
|
||||
rbdid: {
|
||||
label: 'label.rbd.id',
|
||||
validation: { required: false },
|
||||
isHidden: true
|
||||
},
|
||||
rbdsecret: {
|
||||
label: 'label.rbd.secret',
|
||||
validation: { required: false },
|
||||
isHidden: true
|
||||
},
|
||||
|
||||
//always appear (begin)
|
||||
storageTags: {
|
||||
|
|
@ -7803,6 +7847,14 @@
|
|||
vg = "/" + vg;
|
||||
url = clvmURL(vg);
|
||||
}
|
||||
else if (args.data.protocol == "rbd") {
|
||||
var rbdmonitor = args.data.rbdmonitor;
|
||||
var rbdpool = args.data.rbdpool;
|
||||
var rbdid = args.data.rbdid;
|
||||
var rbdsecret = args.data.rbdsecret;
|
||||
|
||||
url = rbdURL(rbdmonitor, rbdpool, rbdid, rbdsecret);
|
||||
}
|
||||
else if (args.data.protocol == "vmfs") {
|
||||
//var path = trim($thisDialog.find("#add_pool_vmfs_dc").val());
|
||||
var path = args.data.vCenterDataCenter;
|
||||
|
|
|
|||
Loading…
Reference in New Issue