add deletevolume and createdata disk

This commit is contained in:
Edison Su 2013-01-03 18:55:55 -08:00
parent ed17281f0d
commit 9410cd1f3c
19 changed files with 256 additions and 72 deletions

View File

@ -35,7 +35,8 @@ public interface Volume extends ControlledEntity, BasedOn, StateObject<Volume.St
Migrating("The volume is migrating to other storage pool"),
Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"),
Expunging("The volume is being expunging"),
Destroy("The volume is destroyed, and can't be recovered."),
Destroy("The volume is destroyed, and can't be recovered."),
Destroying("The volume is destroying, and can't be recovered."),
UploadOp ("The volume upload operation is in progress or in short the volume is on secondary storage");
String _description;

View File

@ -42,4 +42,5 @@ public interface VolumeInfo {
public Date getUpdatedDate();
public String getOwner();
public String getName();
public boolean isAttachedVM();
}

View File

@ -125,6 +125,7 @@ public class volumeServiceTest extends CloudStackTestNGBase {
Long podId;
HostVO host;
String primaryName = "my primary data store";
PrimaryDataStoreInfo primaryStore;
@Test(priority = -1)
public void setUp() {
@ -270,21 +271,29 @@ public class volumeServiceTest extends CloudStackTestNGBase {
}
}
private VolumeVO createVolume(long templateId, long dataStoreId) {
private VolumeVO createVolume(Long templateId, long dataStoreId) {
VolumeVO volume = new VolumeVO(1000, new RootDisk().toString(), UUID.randomUUID().toString(), templateId);
volume.setPoolId(dataStoreId);
volume = volumeDao.persist(volume);
return volume;
}
@Test(priority=2)
public void createVolumeFromTemplate() {
TemplateEntity te = createTemplate();
PrimaryDataStoreInfo dataStoreInfo = createPrimaryDataStore();
VolumeVO volume = createVolume(te.getId(), dataStoreInfo.getId());
primaryStore = createPrimaryDataStore();
VolumeVO volume = createVolume(te.getId(), primaryStore.getId());
VolumeEntity ve = volumeService.getVolumeEntity(volume.getId());
ve.createVolumeFromTemplate(dataStoreInfo.getId(), new VHD(), te);
ve.createVolumeFromTemplate(primaryStore.getId(), new VHD(), te);
ve.destroy();
}
@Test(priority=3)
public void createDataDisk() {
VolumeVO volume = createVolume(null, primaryStore.getId());
VolumeEntity ve = volumeService.getVolumeEntity(volume.getId());
ve.createVolume(primaryStore.getId(), new VHD());
ve.destroy();
}
//@Test(priority=3)

View File

@ -17,6 +17,7 @@
<context:component-scan base-package="com.cloud.utils.component" />
<context:component-scan base-package="com.cloud.host.dao" />
<context:component-scan base-package="com.cloud.dc.dao" />
<context:component-scan base-package="com.cloud.cluster.agentlb.dao" />
<context:component-scan base-package=" com.cloud.upgrade.dao" />
<tx:annotation-driven transaction-manager="transactionManager" />

View File

@ -23,7 +23,7 @@ public class CommandResult {
private String result;
public CommandResult() {
this.success = false;
this.success = true;
this.result = "";
}

View File

@ -32,6 +32,10 @@ public class CreateVolumeAnswer extends Answer {
super(cmd);
this.volumeUuid = volumeUuid;
}
public CreateVolumeAnswer(Command cmd, boolean status, String result) {
super(cmd, status, result);
}
public String getVolumeUuid() {
return this.volumeUuid;

View File

@ -23,11 +23,11 @@ import org.apache.cloudstack.storage.to.VolumeTO;
import com.cloud.agent.api.Command;
public class CreateVolumeCommand extends Command implements StorageSubSystemCommand {
protected VolumeTO volumeInfo;
protected VolumeTO volumeTO;
public CreateVolumeCommand(VolumeTO volumeInfo) {
public CreateVolumeCommand(VolumeTO volumeTO) {
super();
this.volumeInfo = volumeInfo;
this.volumeTO = volumeTO;
}
protected CreateVolumeCommand() {
@ -39,5 +39,9 @@ public class CreateVolumeCommand extends Command implements StorageSubSystemComm
// TODO Auto-generated method stub
return false;
}
public VolumeTO getVolume() {
return this.volumeTO;
}
}

View File

@ -18,17 +18,17 @@
*/
package org.apache.cloudstack.storage.command;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.storage.to.VolumeTO;
import com.cloud.agent.api.Command;
public class DeleteVolume extends Command {
public DeleteVolume(VolumeInfo volume) {
public class DeleteVolumeCommand extends Command implements StorageSubSystemCommand {
private VolumeTO volume;
public DeleteVolumeCommand(VolumeTO volume) {
this.volume = volume;
}
protected DeleteVolume() {
protected DeleteVolumeCommand() {
}
@Override
@ -36,5 +36,9 @@ public class DeleteVolume extends Command {
// TODO Auto-generated method stub
return false;
}
public VolumeTO getVolume() {
return this.volume;
}
}

View File

@ -36,7 +36,7 @@ public interface PrimaryDataStore extends PrimaryDataStoreInfo {
List<VolumeInfo> getVolumes();
boolean deleteVolume(VolumeInfo volume);
void deleteVolumeAsync(VolumeInfo volume, AsyncCompletionCallback<CommandResult> callback);
void createVolumeAsync(VolumeInfo vo, VolumeDiskType diskType, AsyncCompletionCallback<CommandResult> callback);

View File

@ -11,6 +11,7 @@ public class VolumeTO {
private final VolumeDiskType diskType;
private PrimaryDataStoreTO dataStore;
private final String name;
private final long size;
public VolumeTO(VolumeInfo volume) {
this.uuid = volume.getUuid();
this.path = volume.getPath();
@ -22,6 +23,7 @@ public class VolumeTO {
this.dataStore = null;
}
this.name = volume.getName();
this.size = volume.getSize();
}
public String getUuid() {
@ -51,4 +53,8 @@ public class VolumeTO {
public String getName() {
return this.name;
}
public long getSize() {
return this.size;
}
}

View File

@ -146,10 +146,6 @@ public class VolumeEntityImpl implements VolumeEntity {
}
@Override
public void destroy() {
//vs.deleteVolume(volumeInfo);
}
@Override
public long getSize() {
@ -190,7 +186,7 @@ public class VolumeEntityImpl implements VolumeEntity {
}
private Void createVolumeFromTemplateAsyncCallback(AsyncCallbackDispatcher<VolumeEntityImpl, VolumeInfo> callback, Object context) {
public Object createVolumeFromTemplateAsyncCallback(AsyncCallbackDispatcher<VolumeEntityImpl, VolumeInfo> callback, Object context) {
synchronized (volumeInfo) {
volumeInfo.notify();
}
@ -216,7 +212,33 @@ public class VolumeEntityImpl implements VolumeEntity {
}
}
private Void createVolumeCallback(AsyncCallbackDispatcher<VolumeApiResult, VolumeApiResult> callback, Object context) {
public Void createVolumeCallback(AsyncCallbackDispatcher<VolumeApiResult, VolumeApiResult> callback, Object context) {
synchronized (volumeInfo) {
this.result = callback.getResult();
volumeInfo.notify();
}
return null;
}
@Override
public void destroy() {
AsyncCallbackDispatcher<VolumeEntityImpl, VolumeApiResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().destroyCallback(null, null));
vs.deleteVolumeAsync(volumeInfo, caller);
try {
synchronized (volumeInfo) {
volumeInfo.wait();
}
if (!result.isSuccess()) {
throw new CloudRuntimeException("Failed to create volume:" + result.getResult());
}
} catch (InterruptedException e) {
throw new CloudRuntimeException("wait volume info failed", e);
}
}
public Void destroyCallback(AsyncCallbackDispatcher<VolumeApiResult, VolumeApiResult> callback, Object context) {
synchronized (volumeInfo) {
this.result = callback.getResult();
volumeInfo.notify();

View File

@ -111,8 +111,13 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore {
}
@Override
public boolean deleteVolume(VolumeInfo volume) {
return this.driver.deleteVolume((VolumeObject)volume);
public void deleteVolumeAsync(VolumeInfo volume, AsyncCompletionCallback<CommandResult> callback) {
CommandResult result = new CommandResult();
if (volume.isAttachedVM()) {
result.setResult("Can't delete volume: " + volume.getId() + ", if it's attached to a VM");
callback.complete(result);
}
this.driver.deleteVolumeAsync((VolumeObject)volume, callback);
}
@Override

View File

@ -12,7 +12,7 @@ import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.command.CreateVolumeAnswer;
import org.apache.cloudstack.storage.command.CreateVolumeCommand;
import org.apache.cloudstack.storage.command.CreateVolumeFromBaseImageCommand;
import org.apache.cloudstack.storage.command.DeleteVolume;
import org.apache.cloudstack.storage.command.DeleteVolumeCommand;
import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
import org.apache.cloudstack.storage.to.ImageOnPrimayDataStoreTO;
import org.apache.cloudstack.storage.to.VolumeTO;
@ -72,7 +72,7 @@ public class DefaultPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
ep.sendMessageAsync(createCmd, caller);
}
protected Void createVolumeAsyncCallback(AsyncCallbackDispatcher<DefaultPrimaryDataStoreDriverImpl, Answer> callback, CreateVolumeContext<CommandResult> context) {
public Void createVolumeAsyncCallback(AsyncCallbackDispatcher<DefaultPrimaryDataStoreDriverImpl, Answer> callback, CreateVolumeContext<CommandResult> context) {
CommandResult result = new CommandResult();
CreateVolumeAnswer volAnswer = (CreateVolumeAnswer) callback.getResult();
if (volAnswer.getResult()) {
@ -85,14 +85,27 @@ public class DefaultPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
context.getParentCallback().complete(result);
return null;
}
@Override
public boolean deleteVolume(VolumeObject vo) {
DeleteVolume cmd = new DeleteVolume((VolumeInfo)vo);
public void deleteVolumeAsync(VolumeObject vo, AsyncCompletionCallback<CommandResult> callback) {
DeleteVolumeCommand cmd = new DeleteVolumeCommand(this.dataStore.getVolumeTO(vo));
List<EndPoint> endPoints = vo.getDataStore().getEndPoints();
sendOutCommand(cmd, endPoints);
return true;
EndPoint ep = endPoints.get(0);
AsyncRpcConext<CommandResult> context = new AsyncRpcConext<CommandResult>(callback);
AsyncCallbackDispatcher<DefaultPrimaryDataStoreDriverImpl, Answer> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteVolumeCallback(null, null))
.setContext(context);
ep.sendMessageAsync(cmd, caller);
}
public Void deleteVolumeCallback(AsyncCallbackDispatcher<DefaultPrimaryDataStoreDriverImpl, Answer> callback, AsyncRpcConext<CommandResult> context) {
CommandResult result = new CommandResult();
Answer answer = callback.getResult();
if (!answer.getResult()) {
result.setResult(answer.getDetails());
}
context.getParentCallback().complete(result);
return null;
}
@Override
@ -106,33 +119,6 @@ public class DefaultPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
// TODO Auto-generated method stub
return true;
}
protected Answer sendOutCommand(Command cmd, List<EndPoint> endPoints) {
Answer answer = null;
int retries = 3;
int i = 0;
for (EndPoint ep : endPoints) {
answer = ep.sendMessage(cmd);
if (answer == null || answer.getDetails() != null) {
if (i < retries) {
s_logger.debug("create volume failed, retrying: " + i);
}
i++;
} else {
break;
}
}
if (answer == null || answer.getDetails() != null) {
if (answer == null) {
throw new CloudRuntimeException("Failed to created volume");
} else {
throw new CloudRuntimeException(answer.getDetails());
}
}
return answer;
}
private class CreateVolumeFromBaseImageContext<T> extends AsyncRpcConext<T> {
private final VolumeObject volume;

View File

@ -14,7 +14,7 @@ public interface PrimaryDataStoreDriver {
void createVolumeFromBaseImageAsync(VolumeObject volume, TemplateOnPrimaryDataStoreInfo template, AsyncCompletionCallback<CommandResult> callback);
boolean deleteVolume(VolumeObject vo);
void deleteVolumeAsync(VolumeObject vo, AsyncCompletionCallback<CommandResult> callback);
String grantAccess(VolumeObject vol, EndPoint ep);

View File

@ -54,18 +54,18 @@ public class VolumeManagerImpl implements VolumeManager {
private void initStateMachine() {
s_fsm.addTransition(Volume.State.Allocated, Event.CreateRequested, Volume.State.Creating);
s_fsm.addTransition(Volume.State.Allocated, Event.DestroyRequested, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.Allocated, Event.DestroyRequested, Volume.State.Destroying);
s_fsm.addTransition(Volume.State.Creating, Event.OperationRetry, Volume.State.Creating);
s_fsm.addTransition(Volume.State.Creating, Event.OperationFailed, Volume.State.Allocated);
s_fsm.addTransition(Volume.State.Creating, Event.OperationSucceeded, Volume.State.Ready);
s_fsm.addTransition(Volume.State.Creating, Event.DestroyRequested, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.Creating, Event.DestroyRequested, Volume.State.Destroying);
s_fsm.addTransition(Volume.State.Creating, Event.CreateRequested, Volume.State.Creating);
s_fsm.addTransition(Volume.State.Allocated, Event.UploadRequested, Volume.State.UploadOp);
s_fsm.addTransition(Volume.State.UploadOp, Event.CopyRequested, Volume.State.Creating);// CopyRequested for volume from sec to primary storage
s_fsm.addTransition(Volume.State.Creating, Event.CopySucceeded, Volume.State.Ready);
s_fsm.addTransition(Volume.State.Creating, Event.CopyFailed, Volume.State.UploadOp);// Copying volume from sec to primary failed.
s_fsm.addTransition(Volume.State.UploadOp, Event.DestroyRequested, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.Ready, Event.DestroyRequested, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.UploadOp, Event.DestroyRequested, Volume.State.Destroying);
s_fsm.addTransition(Volume.State.Ready, Event.DestroyRequested, Volume.State.Destroying);
s_fsm.addTransition(Volume.State.Destroy, Event.ExpungingRequested, Volume.State.Expunging);
s_fsm.addTransition(Volume.State.Ready, Event.SnapshotRequested, Volume.State.Snapshotting);
s_fsm.addTransition(Volume.State.Snapshotting, Event.OperationSucceeded, Volume.State.Ready);
@ -74,6 +74,9 @@ public class VolumeManagerImpl implements VolumeManager {
s_fsm.addTransition(Volume.State.Migrating, Event.OperationSucceeded, Volume.State.Ready);
s_fsm.addTransition(Volume.State.Migrating, Event.OperationFailed, Volume.State.Ready);
s_fsm.addTransition(Volume.State.Destroy, Event.OperationSucceeded, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.Destroying, Event.OperationSucceeded, Volume.State.Destroy);
s_fsm.addTransition(Volume.State.Destroying, Event.OperationFailed, Volume.State.Destroying);
s_fsm.addTransition(Volume.State.Destroying, Event.DestroyRequested, Volume.State.Destroying);
}
@Override

View File

@ -158,4 +158,9 @@ public class VolumeObject implements VolumeInfo {
public String getName() {
return this.volumeVO.getName();
}
@Override
public boolean isAttachedVM() {
return (this.volumeVO.getInstanceId() == null) ? false : true;
}
}

View File

@ -98,7 +98,7 @@ public class VolumeServiceImpl implements VolumeService {
dataStore.createVolumeAsync(vo, diskType, caller);
}
protected Void createVolumeCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> callback, CreateVolumeContext<VolumeApiResult> context) {
public Void createVolumeCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> callback, CreateVolumeContext<VolumeApiResult> context) {
CommandResult result = callback.getResult();
VolumeObject vo = context.getVolume();
VolumeApiResult volResult = new VolumeApiResult(vo);
@ -112,11 +112,55 @@ public class VolumeServiceImpl implements VolumeService {
context.getParentCallback().complete(volResult);
return null;
}
private class DeleteVolumeContext<T> extends AsyncRpcConext<T> {
private final VolumeObject volume;
/**
* @param callback
*/
public DeleteVolumeContext(AsyncCompletionCallback<T> callback, VolumeObject volume) {
super(callback);
this.volume = volume;
}
public VolumeObject getVolume() {
return this.volume;
}
}
@DB
@Override
public void deleteVolumeAsync(VolumeInfo volume, AsyncCompletionCallback<VolumeApiResult> callback) {
VolumeObject vo = (VolumeObject)volume;
PrimaryDataStore dataStore = vo.getDataStore();
vo.stateTransit(Volume.Event.DestroyRequested);
if (dataStore == null) {
vo.stateTransit(Volume.Event.OperationSucceeded);
volDao.remove(vo.getId());
return;
}
DeleteVolumeContext<VolumeApiResult> context = new DeleteVolumeContext<VolumeApiResult>(callback, vo);
AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteVolumeCallback(null, null))
.setContext(context);
dataStore.deleteVolumeAsync(volume, caller);
}
public Void deleteVolumeCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> callback, DeleteVolumeContext<VolumeApiResult> context) {
CommandResult result = callback.getResult();
VolumeObject vo = context.getVolume();
VolumeApiResult apiResult = new VolumeApiResult(vo);
if (result.isSuccess()) {
vo.stateTransit(Volume.Event.OperationSucceeded);
volDao.remove(vo.getId());
} else {
vo.stateTransit(Volume.Event.OperationFailed);
apiResult.setResult(result.getResult());
}
context.getParentCallback().complete(apiResult);
return null;
}
@Override

View File

@ -34,7 +34,9 @@ import org.apache.cloudstack.storage.command.CopyTemplateToPrimaryStorageCmd;
import org.apache.cloudstack.storage.command.CopyTemplateToPrimaryStorageAnswer;
import org.apache.cloudstack.storage.command.CreatePrimaryDataStoreCmd;
import org.apache.cloudstack.storage.command.CreateVolumeAnswer;
import org.apache.cloudstack.storage.command.CreateVolumeCommand;
import org.apache.cloudstack.storage.command.CreateVolumeFromBaseImageCommand;
import org.apache.cloudstack.storage.command.DeleteVolumeCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol;
import org.apache.cloudstack.storage.to.ImageDataStoreTO;
@ -72,6 +74,7 @@ import com.xensource.xenapi.PBD;
import com.xensource.xenapi.Pool;
import com.xensource.xenapi.SR;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.VBD;
import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.XenAPIException;
import com.xensource.xenapi.VDI;
@ -93,11 +96,96 @@ public class XenServerStorageResource {
return execute((CreatePrimaryDataStoreCmd) command);
} else if (command instanceof CreateVolumeFromBaseImageCommand) {
return execute((CreateVolumeFromBaseImageCommand)command);
} else if (command instanceof CreateVolumeCommand) {
return execute((CreateVolumeCommand) command);
} else if (command instanceof DeleteVolumeCommand) {
return execute((DeleteVolumeCommand)command);
}
return new Answer((Command)command, false, "not implemented yet");
}
public Answer execute(CreateVolumeFromBaseImageCommand cmd) {
protected SR getSRByNameLabel(Connection conn, String nameLabel) throws BadServerResponse, XenAPIException, XmlRpcException {
Set<SR> srs = SR.getByNameLabel(conn, nameLabel);
if (srs.size() != 1) {
throw new CloudRuntimeException("storage uuid: " + nameLabel + " is not unique");
}
SR poolsr = srs.iterator().next();
return poolsr;
}
protected VDI createVdi(Connection conn, String vdiName, SR sr, long size) throws BadServerResponse, XenAPIException, XmlRpcException {
VDI.Record vdir = new VDI.Record();
vdir.nameLabel = vdiName;
vdir.SR = sr;
vdir.type = Types.VdiType.USER;
vdir.virtualSize = size;
VDI vdi = VDI.create(conn, vdir);
return vdi;
}
protected void deleteVDI(Connection conn, VDI vdi) throws BadServerResponse, XenAPIException, XmlRpcException {
vdi.destroy(conn);
}
protected CreateVolumeAnswer execute(CreateVolumeCommand cmd) {
VolumeTO volume = cmd.getVolume();
PrimaryDataStoreTO primaryDataStore = volume.getDataStore();
Connection conn = hypervisorResource.getConnection();
VDI vdi = null;
boolean result = false;
String errorMsg = null;
try {
SR primaryDataStoreSR = getSRByNameLabel(conn, primaryDataStore.getUuid());
vdi = createVdi(conn, volume.getName(), primaryDataStoreSR, volume.getSize());
VDI.Record record = vdi.getRecord(conn);
result = true;
return new CreateVolumeAnswer(cmd, record.uuid);
} catch (BadServerResponse e) {
s_logger.debug("Failed to create volume", e);
errorMsg = e.toString();
} catch (XenAPIException e) {
s_logger.debug("Failed to create volume", e);
errorMsg = e.toString();
} catch (XmlRpcException e) {
s_logger.debug("Failed to create volume", e);
errorMsg = e.toString();
} finally {
if (!result && vdi != null) {
try {
deleteVDI(conn, vdi);
} catch (Exception e) {
s_logger.debug("Faled to delete vdi: " + vdi.toString());
}
}
}
return new CreateVolumeAnswer(cmd, false, errorMsg);
}
protected Answer execute(DeleteVolumeCommand cmd) {
VolumeTO volume = cmd.getVolume();
Connection conn = hypervisorResource.getConnection();
String errorMsg = null;
try {
VDI vdi = VDI.getByUuid(conn, volume.getUuid());
deleteVDI(conn, vdi);
return new Answer(cmd);
} catch (BadServerResponse e) {
s_logger.debug("Failed to delete volume", e);
errorMsg = e.toString();
} catch (XenAPIException e) {
s_logger.debug("Failed to delete volume", e);
errorMsg = e.toString();
} catch (XmlRpcException e) {
s_logger.debug("Failed to delete volume", e);
errorMsg = e.toString();
}
return new Answer(cmd, false, errorMsg);
}
protected Answer execute(CreateVolumeFromBaseImageCommand cmd) {
VolumeTO volume = cmd.getVolume();
ImageOnPrimayDataStoreTO baseImage = cmd.getImage();
Connection conn = hypervisorResource.getConnection();

View File

@ -11,11 +11,6 @@ import org.apache.cloudstack.storage.volume.VolumeObject;
public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
@Override
public boolean deleteVolume(VolumeObject vo) {
// TODO Auto-generated method stub
return false;
}
@Override
public String grantAccess(VolumeObject vol, EndPoint ep) {
@ -77,4 +72,10 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
@Override
public void deleteVolumeAsync(VolumeObject vo, AsyncCompletionCallback<CommandResult> callback) {
// TODO Auto-generated method stub
}
}