CLOUDSTACK-6170

This commit is contained in:
Mike Tutkowski 2014-02-27 13:20:56 -07:00
parent bbaec7bae8
commit b06e66c50a
21 changed files with 639 additions and 67 deletions

View File

@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
@ -117,6 +118,21 @@ public class CreateServiceOfferingCmd extends BaseCmd {
@Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering")
private Long iopsWriteRate;
@Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether compute offering iops is custom or not")
private Boolean customizedIops;
@Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the compute offering")
private Long minIops;
@Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the compute offering")
private Long maxIops;
@Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE,
type = CommandType.INTEGER,
required = false,
description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)")
private Integer hypervisorSnapshotReserve;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -215,6 +231,22 @@ public class CreateServiceOfferingCmd extends BaseCmd {
return iopsWriteRate;
}
public Boolean isCustomizedIops() {
return customizedIops;
}
public Long getMinIops() {
return minIops;
}
public Long getMaxIops() {
return maxIops;
}
public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.storage.to;
import java.util.Map;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import com.cloud.agent.api.to.DataStoreTO;
@ -23,6 +25,16 @@ import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage.StoragePoolType;
public class PrimaryDataStoreTO implements DataStoreTO {
public static final String MANAGED = PrimaryDataStore.MANAGED;
public static final String STORAGE_HOST = PrimaryDataStore.STORAGE_HOST;
public static final String MANAGED_STORE_TARGET = PrimaryDataStore.MANAGED_STORE_TARGET;
public static final String MANAGED_STORE_TARGET_ROOT_VOLUME = PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME;
public static final String CHAP_INITIATOR_USERNAME = PrimaryDataStore.CHAP_INITIATOR_USERNAME;
public static final String CHAP_INITIATOR_SECRET = PrimaryDataStore.CHAP_INITIATOR_SECRET;
public static final String CHAP_TARGET_USERNAME = PrimaryDataStore.CHAP_TARGET_USERNAME;
public static final String CHAP_TARGET_SECRET = PrimaryDataStore.CHAP_TARGET_SECRET;
public static final String VOLUME_SIZE = PrimaryDataStore.VOLUME_SIZE;
private final String uuid;
private final String name;
private String type;
@ -32,6 +44,7 @@ public class PrimaryDataStoreTO implements DataStoreTO {
private String path;
private int port;
private final String url;
private Map<String, String> details;
public PrimaryDataStoreTO(PrimaryDataStore dataStore) {
this.uuid = dataStore.getUuid();
@ -42,6 +55,7 @@ public class PrimaryDataStoreTO implements DataStoreTO {
this.setPath(dataStore.getPath());
this.setPort(dataStore.getPort());
this.url = dataStore.getUri();
this.details = dataStore.getDetails();
}
public long getId() {
@ -58,6 +72,10 @@ public class PrimaryDataStoreTO implements DataStoreTO {
return this.url;
}
public Map<String, String> getDetails() {
return this.details;
}
public String getName() {
return this.name;
}

View File

@ -26,6 +26,8 @@ import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
public interface DataMotionService {
void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);

View File

@ -30,6 +30,8 @@ public interface DataMotionStrategy {
StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost);
Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);

View File

@ -18,6 +18,8 @@
*/
package org.apache.cloudstack.engine.subsystem.api.storage;
import java.util.Map;
import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -25,6 +27,16 @@ import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StoragePool;
public interface PrimaryDataStoreInfo extends StoragePool {
static final String MANAGED = "managed";
static final String STORAGE_HOST= "storageHost";
static final String MANAGED_STORE_TARGET = "managedStoreTarget";
static final String MANAGED_STORE_TARGET_ROOT_VOLUME = "managedStoreTargetRootVolume";
static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";
static final String CHAP_TARGET_USERNAME = "chapTargetUsername";
static final String CHAP_TARGET_SECRET = "chapTargetSecret";
static final String VOLUME_SIZE = "volumeSize";
boolean isHypervisorSupported(HypervisorType hypervisor);
boolean isLocalStorageSupported();
@ -37,5 +49,11 @@ public interface PrimaryDataStoreInfo extends StoragePool {
@Override
StoragePoolType getPoolType();
boolean isManaged();
void setDetails(Map<String, String> details);
Map<String, String> getDetails();
PrimaryDataStoreLifeCycle getLifeCycle();
}

View File

@ -78,7 +78,11 @@ public interface VolumeService {
VolumeEntity getVolumeEntity(long volumeId);
AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template);
AsyncCallFuture<VolumeApiResult> createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId,
TemplateInfo srcTemplateInfo, long destHostId);
AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId,
TemplateInfo template);
AsyncCallFuture<VolumeApiResult> copyVolume(VolumeInfo srcVolume, DataStore destStore);

View File

@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService;
@ -1113,7 +1114,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
future = volService.createVolumeAsync(volume, destPool);
} else {
TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image);
future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ);
PrimaryDataStore primaryDataStore = (PrimaryDataStore)destPool;
if (primaryDataStore.isManaged()) {
DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId());
HypervisorType hyperType = vm.getVirtualMachine().getHypervisorType();
// update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage)
updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
long hostId = vm.getVirtualMachine().getHostId();
future = volService.createManagedStorageAndVolumeFromTemplateAsync(volume, destPool.getId(), templ, hostId);
}
else {
future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ);
}
}
VolumeApiResult result = null;
try {

View File

@ -46,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.RemoteHostEndPoint;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
@ -94,15 +95,17 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
protected boolean needCacheStorage(DataObject srcData, DataObject destData) {
DataTO srcTO = srcData.getTO();
DataTO destTO = destData.getTO();
DataStoreTO srcStoreTO = srcTO.getDataStore();
DataStoreTO destStoreTO = destTO.getDataStore();
if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache) {
//||
// (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) {
return false;
}
DataTO destTO = destData.getTO();
DataStoreTO destStoreTO = destTO.getDataStore();
if (destStoreTO instanceof NfsTO || destStoreTO.getRole() == DataStoreRole.ImageCache) {
return false;
}
@ -147,7 +150,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return selectedScope;
}
protected Answer copyObject(DataObject srcData, DataObject destData) {
protected Answer copyObject(DataObject srcData, DataObject destData, Host destHost) {
String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
Answer answer = null;
@ -160,7 +163,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
}
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), destData.getTO(), _primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
EndPoint ep = selector.select(srcForCopy, destData);
EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData);
if (ep == null) {
String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
s_logger.error(errMsg);
@ -193,6 +196,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
}
}
protected Answer copyObject(DataObject srcData, DataObject destData) {
return copyObject(srcData, destData, null);
}
protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) {
DataObject leafData = null;
DataStore store = cacheMgr.getCacheStorage(snapshot, scope);
@ -392,8 +399,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return answer;
}
// Note: destHost is currently only used if the copyObject method is invoked
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
Answer answer = null;
String errMsg = null;
try {
@ -416,7 +424,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
} else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) {
answer = copySnapshot(srcData, destData);
} else {
answer = copyObject(srcData, destData);
answer = copyObject(srcData, destData, destHost);
}
if (answer != null && !answer.getResult()) {
@ -432,6 +440,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return null;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
return copyAsync(srcData, destData, null, callback);
}
@DB
protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) {

View File

@ -46,7 +46,7 @@ public class DataMotionServiceImpl implements DataMotionService {
StorageStrategyFactory storageStrategyFactory;
@Override
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
if (srcData.getDataStore() == null || destData.getDataStore() == null) {
throw new CloudRuntimeException("can't find data store");
}
@ -65,7 +65,12 @@ public class DataMotionServiceImpl implements DataMotionService {
destData.getType().name() + " '" + destData.getUuid() + "'");
}
strategy.copyAsync(srcData, destData, callback);
strategy.copyAsync(srcData, destData, destHost, callback);
}
@Override
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
copyAsync(srcData, destData, null, callback);
}
@Override

View File

@ -30,6 +30,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
@ -299,6 +300,20 @@ public class TemplateObject implements TemplateInfo {
if (dataStore == null) {
return null;
}
// managed primary data stores should not have an install path
if (dataStore instanceof PrimaryDataStore) {
PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore;
Map<String, String> details = primaryDataStore.getDetails();
boolean managed = details != null && Boolean.parseBoolean(details.get(PrimaryDataStore.MANAGED));
if (managed) {
return null;
}
}
DataObjectInStore obj = objectInStoreMgr.findObject(this, dataStore);
return obj.getInstallPath();
}

View File

@ -57,6 +57,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy {
return StrategyPriority.HIGHEST;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
CopyCmdAnswer answer = null;

View File

@ -20,6 +20,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@ -67,6 +68,7 @@ import com.cloud.utils.storage.encoding.EncodingType;
public class PrimaryDataStoreImpl implements PrimaryDataStore {
private static final Logger s_logger = Logger.getLogger(PrimaryDataStoreImpl.class);
protected PrimaryDataStoreDriver driver;
protected StoragePoolVO pdsv;
@Inject
@ -86,6 +88,7 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
@Inject
private VolumeDao volumeDao;
private Map<String, String> _details;
public PrimaryDataStoreImpl() {
@ -135,6 +138,16 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
return pdsv.getId();
}
@Override
public void setDetails(Map<String, String> details) {
_details = details;
}
@Override
public Map<String, String> getDetails() {
return _details;
}
@Override
public String getUri() {
String path = pdsv.getPath().replaceFirst("/*", "");
@ -222,10 +235,15 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
return null;
}
@Override
public boolean isManaged() {
return pdsv.isManaged();
}
@Override
public DataObject create(DataObject obj) {
// create template on primary storage
if (obj.getType() == DataObjectType.TEMPLATE) {
if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) {
try {
String templateIdPoolIdString = "templateId:" + obj.getId() + "poolId:" + getId();
VMTemplateStoragePoolVO templateStoragePoolRef;

View File

@ -85,7 +85,7 @@ public class DefaultHostListener implements HypervisorHostListener {
poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes());
primaryStoreDao.update(pool.getId(), poolVO);
s_logger.info("Connection established between " + pool + " host + " + hostId);
s_logger.info("Connection established between storage pool " + pool + " and host " + hostId);
return true;
}

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.volume;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -40,6 +41,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
@ -60,6 +62,7 @@ import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.storage.ListVolumeAnswer;
@ -72,6 +75,7 @@ import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.dao.HostDao;
import com.cloud.host.Host;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.ScopeType;
@ -122,6 +126,8 @@ public class VolumeServiceImpl implements VolumeService {
VolumeDao _volumeDao;
@Inject
EndPointSelector _epSelector;
@Inject
HostDao _hostDao;
public VolumeServiceImpl() {
}
@ -349,6 +355,39 @@ public class VolumeServiceImpl implements VolumeService {
return null;
}
private class ManagedCreateBaseImageContext<T> extends AsyncRpcContext<T> {
private final VolumeInfo _volumeInfo;
private final PrimaryDataStore _primaryDataStore;
private final TemplateInfo _templateInfo;
private final AsyncCallFuture<VolumeApiResult> _future;
public ManagedCreateBaseImageContext(AsyncCompletionCallback<T> callback, VolumeInfo volumeInfo,
PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, AsyncCallFuture<VolumeApiResult> future) {
super(callback);
_volumeInfo = volumeInfo;
_primaryDataStore = primaryDatastore;
_templateInfo = templateInfo;
_future = future;
}
public VolumeInfo getVolumeInfo() {
return _volumeInfo;
}
public PrimaryDataStore getPrimaryDataStore() {
return _primaryDataStore;
}
public TemplateInfo getTemplateInfo() {
return _templateInfo;
}
public AsyncCallFuture<VolumeApiResult> getFuture() {
return _future;
}
}
class CreateBaseImageContext<T> extends AsyncRpcContext<T> {
private final VolumeInfo volume;
private final PrimaryDataStore dataStore;
@ -411,7 +450,6 @@ public class VolumeServiceImpl implements VolumeService {
@DB
protected void createBaseImageAsync(VolumeInfo volume, PrimaryDataStore dataStore, TemplateInfo template, AsyncCallFuture<VolumeApiResult> future) {
DataObject templateOnPrimaryStoreObj = dataStore.create(template);
VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(dataStore.getId(), template.getId());
@ -476,6 +514,37 @@ public class VolumeServiceImpl implements VolumeService {
return;
}
protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback,
ManagedCreateBaseImageContext<VolumeApiResult> context) {
CopyCommandResult result = callback.getResult();
VolumeInfo volumeInfo = context.getVolumeInfo();
VolumeApiResult res = new VolumeApiResult(volumeInfo);
if (result.isSuccess()) {
// volumeInfo.processEvent(Event.OperationSuccessed, result.getAnswer());
VolumeVO volume = volDao.findById(volumeInfo.getId());
CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer();
TemplateObjectTO templateObjectTo = (TemplateObjectTO)answer.getNewData();
volume.setPath(templateObjectTo.getPath());
volume.setFormat(templateObjectTo.getFormat());
volDao.update(volume.getId(), volume);
}
else {
volumeInfo.processEvent(Event.DestroyRequested);
res.setResult(result.getResult());
}
AsyncCallFuture<VolumeApiResult> future = context.getFuture();
future.complete(res);
return null;
}
@DB
protected Void copyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) {
CopyCommandResult result = callback.getResult();
@ -577,6 +646,91 @@ public class VolumeServiceImpl implements VolumeService {
return null;
}
@Override
public AsyncCallFuture<VolumeApiResult> createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId,
TemplateInfo srcTemplateInfo, long destHostId) {
PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId);
TemplateInfo destTemplateInfo = (TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo);
Host destHost = _hostDao.findById(destHostId);
if (destHost == null) {
throw new CloudRuntimeException("Destinatin host should not be null.");
}
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
try {
// must call driver to have a volume created
AsyncCallFuture<VolumeApiResult> createVolumeFuture = createVolumeAsync(volumeInfo, destPrimaryDataStore);
VolumeApiResult createVolumeResult = createVolumeFuture.get();
if (createVolumeResult.isFailed()) {
throw new CloudRuntimeException("Creation of a volume failed: " + createVolumeResult.getResult());
}
// refresh the volume from the DB
volumeInfo = volFactory.getVolume(volumeInfo.getId(), destPrimaryDataStore);
connectVolumeToHost(volumeInfo, destHost, destPrimaryDataStore);
ManagedCreateBaseImageContext<CreateCmdResult> context = new ManagedCreateBaseImageContext<CreateCmdResult>(null, volumeInfo,
destPrimaryDataStore, srcTemplateInfo, future);
AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, null)).setContext(context);
Map<String, String> details = new HashMap<String, String>();
details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString());
details.put(PrimaryDataStore.STORAGE_HOST, destPrimaryDataStore.getHostAddress());
// for managed storage, the storage repository (XenServer) or datastore (ESX) name is based off of the iScsiName property of a volume
details.put(PrimaryDataStore.MANAGED_STORE_TARGET, volumeInfo.get_iScsiName());
details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, volumeInfo.getName());
details.put(PrimaryDataStore.VOLUME_SIZE, String.valueOf(volumeInfo.getSize()));
ChapInfo chapInfo = getChapInfo(volumeInfo, destPrimaryDataStore);
if (chapInfo != null) {
details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername());
details.put(PrimaryDataStore.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret());
details.put(PrimaryDataStore.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername());
details.put(PrimaryDataStore.CHAP_TARGET_SECRET, chapInfo.getTargetSecret());
}
destPrimaryDataStore.setDetails(details);
motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller);
}
catch (Throwable t) {
String errMsg = t.toString();
volumeInfo.processEvent(Event.DestroyRequested);
disconnectVolumeFromHost(volumeInfo, destHost, destPrimaryDataStore);
try {
AsyncCallFuture<VolumeApiResult> expungeVolumeFuture = expungeVolumeAsync(volumeInfo);
VolumeApiResult expungeVolumeResult = expungeVolumeFuture.get();
if (expungeVolumeResult.isFailed()) {
errMsg += " : Failed to expunge a volume that was created";
}
}
catch (Exception ex) {
errMsg += " : " + ex.getMessage();
}
VolumeApiResult result = new VolumeApiResult(volumeInfo);
result.setResult(errMsg);
future.complete(result);
}
return future;
}
@DB
@Override
public AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template) {

View File

@ -42,6 +42,11 @@ public class SimulatorDataMotionStrategy implements DataMotionStrategy {
return StrategyPriority.HYPERVISOR;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
CopyCommandResult result = new CopyCommandResult("something", null);

View File

@ -85,6 +85,11 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
return StrategyPriority.CANT_HANDLE;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
CopyCommandResult result = new CopyCommandResult(null, null);

View File

@ -1869,18 +1869,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return prepareManagedStorage(conn, details, null, vdiNameLabel);
}
protected VDI prepareManagedStorage(Connection conn, Map<String, String> details, String path, String vdiNameLabel) throws Exception {
protected SR prepareManagedSr(Connection conn, Map<String, String> details) {
String iScsiName = details.get(DiskTO.IQN);
String storageHost = details.get(DiskTO.STORAGE_HOST);
String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME);
String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET);
Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE));
SR sr = getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true);
return getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true);
}
protected VDI prepareManagedStorage(Connection conn, Map<String, String> details, String path, String vdiNameLabel) throws Exception {
SR sr = prepareManagedSr(conn, details);
VDI vdi = getVDIbyUuid(conn, path, false);
if (vdi == null) {
Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE));
vdi = createVdi(sr, vdiNameLabel, volumeSize);
}

View File

@ -59,6 +59,7 @@ import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectAnswer;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@ -887,58 +888,121 @@ public class XenServerStorageProcessor implements StorageProcessor {
@Override
public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
DataTO srcData = cmd.getSrcTO();
DataTO destData = cmd.getDestTO();
DataTO srcDataTo = cmd.getSrcTO();
DataTO destDataTo = cmd.getDestTO();
int wait = cmd.getWait();
DataStoreTO srcStore = srcData.getDataStore();
try {
if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) {
NfsTO srcImageStore = (NfsTO)srcStore;
TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData;
String storeUrl = srcImageStore.getUrl();
DataStoreTO srcDataStoreTo = srcDataTo.getDataStore();
try {
if ((srcDataStoreTo instanceof NfsTO) && (srcDataTo.getObjectType() == DataObjectType.TEMPLATE)) {
NfsTO srcImageStore = (NfsTO)srcDataStoreTo;
TemplateObjectTO srcTemplateObjectTo = (TemplateObjectTO)srcDataTo;
String storeUrl = srcImageStore.getUrl();
URI uri = new URI(storeUrl);
String tmplpath = uri.getHost() + ":" + uri.getPath() + "/" + srcData.getPath();
String poolName = destData.getDataStore().getUuid();
String tmplPath = uri.getHost() + ":" + uri.getPath() + "/" + srcDataTo.getPath();
DataStoreTO destDataStoreTo = destDataTo.getDataStore();
boolean managed = false;
String storageHost = null;
String managedStoragePoolName = null;
String managedStoragePoolRootVolumeName = null;
String managedStoragePoolRootVolumeSize = null;
String chapInitiatorUsername = null;
String chapInitiatorSecret = null;
if (destDataStoreTo instanceof PrimaryDataStoreTO) {
PrimaryDataStoreTO destPrimaryDataStoreTo = (PrimaryDataStoreTO)destDataStoreTo;
Map<String, String> details = destPrimaryDataStoreTo.getDetails();
if (details != null) {
managed = Boolean.parseBoolean(details.get(PrimaryDataStoreTO.MANAGED));
if (managed) {
storageHost = details.get(PrimaryDataStoreTO.STORAGE_HOST);
managedStoragePoolName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET);
managedStoragePoolRootVolumeName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET_ROOT_VOLUME);
managedStoragePoolRootVolumeSize = details.get(PrimaryDataStoreTO.VOLUME_SIZE);
chapInitiatorUsername = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_USERNAME);
chapInitiatorSecret = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_SECRET);
}
}
}
Connection conn = hypervisorResource.getConnection();
SR poolsr = null;
Set<SR> srs = SR.getByNameLabel(conn, poolName);
if (srs.size() != 1) {
String msg = "There are " + srs.size() + " SRs with same name: " + poolName;
s_logger.warn(msg);
return new CopyCmdAnswer(msg);
} else {
poolsr = srs.iterator().next();
final SR sr;
if (managed) {
Map<String, String> details = new HashMap<String, String>();
details.put(DiskTO.STORAGE_HOST, storageHost);
details.put(DiskTO.IQN, managedStoragePoolName);
details.put(DiskTO.VOLUME_SIZE, managedStoragePoolRootVolumeSize);
details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInitiatorUsername);
details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInitiatorSecret);
sr = hypervisorResource.prepareManagedSr(conn, details);
}
String pUuid = poolsr.getUuid(conn);
boolean isISCSI = IsISCSI(poolsr.getType(conn));
String uuid = copy_vhd_from_secondarystorage(conn, tmplpath, pUuid, wait);
VDI tmpl = getVDIbyUuid(conn, uuid);
VDI snapshotvdi = tmpl.snapshot(conn, new HashMap<String, String>());
String snapshotUuid = snapshotvdi.getUuid(conn);
snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName());
String parentuuid = getVhdParent(conn, pUuid, snapshotUuid, isISCSI);
VDI parent = getVDIbyUuid(conn, parentuuid);
Long phySize = parent.getPhysicalUtilisation(conn);
tmpl.destroy(conn);
poolsr.scan(conn);
else {
String srName = destDataStoreTo.getUuid();
Set<SR> srs = SR.getByNameLabel(conn, srName);
if (srs.size() != 1) {
String msg = "There are " + srs.size() + " SRs with same name: " + srName;
s_logger.warn(msg);
return new CopyCmdAnswer(msg);
} else {
sr = srs.iterator().next();
}
}
String srUuid = sr.getUuid(conn);
String tmplUuid = copy_vhd_from_secondarystorage(conn, tmplPath, srUuid, wait);
VDI tmplVdi = getVDIbyUuid(conn, tmplUuid);
final String uuidToReturn;
if (managed) {
uuidToReturn = tmplUuid;
tmplVdi.setNameLabel(conn, managedStoragePoolRootVolumeName);
}
else {
VDI snapshotVdi = tmplVdi.snapshot(conn, new HashMap<String, String>());
uuidToReturn = snapshotVdi.getUuid(conn);
snapshotVdi.setNameLabel(conn, "Template " + srcTemplateObjectTo.getName());
tmplVdi.destroy(conn);
}
sr.scan(conn);
try {
Thread.sleep(5000);
} catch (Exception e) {
}
TemplateObjectTO newVol = new TemplateObjectTO();
newVol.setUuid(snapshotvdi.getUuid(conn));
newVol.setPath(newVol.getUuid());
newVol.setUuid(uuidToReturn);
newVol.setPath(uuidToReturn);
newVol.setFormat(ImageFormat.VHD);
return new CopyCmdAnswer(newVol);
}
} catch (Exception e) {
String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString();
s_logger.warn(msg, e);
return new CopyCmdAnswer(msg);
}
return new CopyCmdAnswer("not implemented yet");
}

View File

@ -90,6 +90,11 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
return StrategyPriority.CANT_HANDLE;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
throw new UnsupportedOperationException();
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
CopyCommandResult result = new CopyCommandResult(null, null);

View File

@ -2016,15 +2016,54 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired,
offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(),
cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate());
cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(),
cmd.getHypervisorSnapshotReserve());
}
protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed,
String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag,
Integer networkRate, String deploymentPlanner, Map<String, String> details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
Integer networkRate, String deploymentPlanner, Map<String, String> details, Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) {
tags = StringUtils.cleanupTags(tags);
ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired,
false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner);
if (isCustomizedIops != null) {
bytesReadRate = null;
bytesWriteRate = null;
iopsReadRate = null;
iopsWriteRate = null;
if (isCustomizedIops) {
minIops = null;
maxIops = null;
} else {
if (minIops == null && maxIops == null) {
minIops = 0L;
maxIops = 0L;
} else {
if (minIops == null || minIops <= 0) {
throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
}
if (maxIops == null) {
maxIops = 0L;
}
if (minIops > maxIops) {
throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
}
}
}
} else {
minIops = null;
maxIops = null;
}
offering.setCustomizedIops(isCustomizedIops);
offering.setMinIops(minIops);
offering.setMaxIops(maxIops);
if ((bytesReadRate != null) && (bytesReadRate > 0))
offering.setBytesReadRate(bytesReadRate);
if ((bytesWriteRate != null) && (bytesWriteRate > 0))
@ -2034,6 +2073,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if ((iopsWriteRate != null) && (iopsWriteRate > 0))
offering.setIopsWriteRate(iopsWriteRate);
if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) {
throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0.");
}
offering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
if ((offering = _serviceOfferingDao.persist(offering)) != null) {
if (details != null) {
List<ServiceOfferingDetailsVO> detailsVO = new ArrayList<ServiceOfferingDetailsVO>();

View File

@ -136,6 +136,113 @@
number: true
}
},
qosType: {
label: 'label.qos.type',
docID: 'helpDiskOfferingQoSType',
select: function(args) {
var items = [];
items.push({
id: '',
description: ''
});
items.push({
id: 'hypervisor',
description: 'hypervisor'
});
items.push({
id: 'storage',
description: 'storage'
});
args.response.success({
data: items
});
args.$select.change(function() {
var $form = $(this).closest('form');
var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]');
var $minIops = $form.find('.form-item[rel=minIops]');
var $maxIops = $form.find('.form-item[rel=maxIops]');
var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]');
var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]');
var qosId = $(this).val();
if (qosId == 'storage') { // Storage QoS
$diskBytesReadRate.hide();
$diskBytesWriteRate.hide();
$diskIopsReadRate.hide();
$diskIopsWriteRate.hide();
$isCustomizedIops.css('display', 'inline-block');
if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) {
$minIops.hide();
$maxIops.hide();
} else {
$minIops.css('display', 'inline-block');
$maxIops.css('display', 'inline-block');
}
$hypervisorSnapshotReserve.css('display', 'inline-block');
} else if (qosId == 'hypervisor') { // Hypervisor Qos
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
$hypervisorSnapshotReserve.hide();
$diskBytesReadRate.css('display', 'inline-block');
$diskBytesWriteRate.css('display', 'inline-block');
$diskIopsReadRate.css('display', 'inline-block');
$diskIopsWriteRate.css('display', 'inline-block');
} else { // No Qos
$diskBytesReadRate.hide();
$diskBytesWriteRate.hide();
$diskIopsReadRate.hide();
$diskIopsWriteRate.hide();
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
$hypervisorSnapshotReserve.hide();
}
});
}
},
isCustomizedIops: {
label: 'label.custom.disk.iops',
docID: 'helpDiskOfferingCustomDiskIops',
isBoolean: true,
isReverse: true,
isChecked: false
},
minIops: {
label: 'label.disk.iops.min',
docID: 'helpDiskOfferingDiskIopsMin',
dependsOn: 'isCustomizedIops',
validation: {
required: false,
number: true
}
},
maxIops: {
label: 'label.disk.iops.max',
docID: 'helpDiskOfferingDiskIopsMax',
dependsOn: 'isCustomizedIops',
validation: {
required: false,
number: true
}
},
hypervisorSnapshotReserve: {
label: 'label.hypervisor.snapshot.reserve',
docID: 'helpDiskOfferingHypervisorSnapshotReserve',
validation: {
required: false,
number: true
}
},
diskBytesReadRate: {
label: 'label.disk.bytes.read.rate',
docID: 'helpComputeOfferingDiskBytesReadRate',
@ -326,26 +433,59 @@
networkrate: args.data.networkRate
});
}
if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
if (args.data.qosType == 'storage') {
var customIops = args.data.isCustomizedIops == "on";
$.extend(data, {
bytesreadrate: args.data.diskBytesReadRate
});
}
if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
$.extend(data, {
byteswriterate: args.data.diskBytesWriteRate
});
}
if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
$.extend(data, {
iopsreadrate: args.data.diskIopsReadRate
});
}
if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
$.extend(data, {
iopswriterate: args.data.diskIopsWriteRate
customizediops: customIops
});
if (!customIops) {
if (args.data.minIops != null && args.data.minIops.length > 0) {
$.extend(data, {
miniops: args.data.minIops
});
}
if (args.data.maxIops != null && args.data.maxIops.length > 0) {
$.extend(data, {
maxiops: args.data.maxIops
});
}
}
if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) {
$.extend(data, {
hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve
});
}
} else if (args.data.qosType == 'hypervisor') {
if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
$.extend(data, {
bytesreadrate: args.data.diskBytesReadRate
});
}
if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
$.extend(data, {
byteswriterate: args.data.diskBytesWriteRate
});
}
if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
$.extend(data, {
iopsreadrate: args.data.diskIopsReadRate
});
}
if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
$.extend(data, {
iopswriterate: args.data.diskIopsWriteRate
});
}
}
$.extend(data, {
offerha: (args.data.offerHA == "on")
});