Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,6 @@ public class ApiConstants {
public static final String SHOW_CAPACITIES = "showcapacities";
public static final String SHOW_REMOVED = "showremoved";
public static final String SHOW_RESOURCE_ICON = "showicon";
public static final String SHOW_COMPLETED = "showcompleted";
public static final String SHOW_INACTIVE = "showinactive";
public static final String SHOW_UNIQUE = "showunique";
public static final String SIGNATURE = "signature";
Expand Down Expand Up @@ -582,6 +581,7 @@ public class ApiConstants {
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
public static final String TARGET_IQN = "targetiqn";
public static final String TASKS_FILTER = "tasksfilter";
public static final String TEMPLATE_FILTER = "templatefilter";
public static final String TEMPLATE_ID = "templateid";
public static final String TEMPLATE_IDS = "templateids";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ public class ListImportVMTasksCmd extends BaseListCmd {
description = "Conversion host of the importing task")
private Long convertHostId;

@Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "Whether to list all import tasks.")
private boolean listAll = false;

@Parameter(name = ApiConstants.SHOW_COMPLETED, type = CommandType.BOOLEAN, description = "Whether to list completed tasks.")
private boolean showCompleted = false;
@Parameter(name = ApiConstants.TASKS_FILTER, type = CommandType.STRING, description = "Filter tasks by state, valid options are: All, Running, Completed, Failed")
private String tasksFilter;

public Long getZoneId() {
return zoneId;
Expand All @@ -97,12 +94,8 @@ public Long getConvertHostId() {
return convertHostId;
}

public boolean isListAll() {
return listAll;
}

public boolean isShowCompleted() {
return showCompleted;
public String getTasksFilter() {
return tasksFilter;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public class ImportVMTaskResponse extends BaseResponse {
@Param(description = "the display name of the importing VM")
private String displayName;

@SerializedName(ApiConstants.STATE)
@Param(description = "the state of the importing VM task")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since?

private String state;

@SerializedName(ApiConstants.VCENTER)
@Param(description = "the vcenter name of the importing VM task")
private String vcenter;
Expand Down Expand Up @@ -242,4 +246,12 @@ public Date getLastUpdated() {
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}
}
15 changes: 14 additions & 1 deletion api/src/main/java/org/apache/cloudstack/vm/ImportVmTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@

public interface ImportVmTask extends Identity, InternalIdentity {
enum Step {
Prepare, CloningInstance, ConvertingInstance, Importing, Cleaning, Completed
Prepare, CloningInstance, ConvertingInstance, Importing, Completed
}

enum TaskState {
Running, Completed, Failed;

public static TaskState getValue(String state) {
for (TaskState s : TaskState.values()) {
if (s.name().equalsIgnoreCase(state)) {
return s;
}
}
throw new IllegalArgumentException("Invalid task state: " + state);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ ImportVmTask createImportVMTaskRecord(DataCenter zone, Account owner, long userI
void updateImportVMTaskStep(ImportVmTask importVMTaskVO, DataCenter zone, Account owner, Host convertHost,
Host importHost, Long vmId, ImportVmTask.Step step);

boolean removeImportVMTask(long taskId);
void updateImportVMTaskErrorState(ImportVmTask importVMTaskVO, ImportVmTask.TaskState state, String errorMsg);
}
11 changes: 11 additions & 0 deletions engine/schema/src/main/java/com/cloud/vm/ImportVMTaskVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public ImportVMTaskVO() {
@Column(name = "step")
private Step step;

@Column(name = "state")
private TaskState state;

@Column(name = "description")
private String description;

Expand Down Expand Up @@ -217,6 +220,14 @@ public void setStep(Step step) {
this.step = step;
}

public TaskState getState() {
return state;
}

public void setState(TaskState state) {
this.state = state;
}

public String getDescription() {
return description;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
//
package com.cloud.vm.dao;

import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDao;
import com.cloud.vm.ImportVMTaskVO;

import org.apache.cloudstack.vm.ImportVmTask;
import java.util.List;

public interface ImportVMTaskDao extends GenericDao<ImportVMTaskVO, Long> {

List<ImportVMTaskVO> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId, boolean showCompleted);
Pair<List<ImportVMTaskVO>, Integer> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId,
ImportVmTask.TaskState state, Long startIndex, Long pageSizeVal);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
// under the License.
package com.cloud.vm.dao;

import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.vm.ImportVMTaskVO;
import org.apache.cloudstack.vm.ImportVmTask;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

Expand All @@ -41,12 +44,14 @@ void init() {
AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("vcenter", AllFieldsSearch.entity().getVcenter(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("convertHostId", AllFieldsSearch.entity().getConvertHostId(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ);
AllFieldsSearch.done();
}


@Override
public List<ImportVMTaskVO> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId, boolean showCompleted) {
public Pair<List<ImportVMTaskVO>, Integer> listImportVMTasks(Long zoneId, Long accountId, String vcenter, Long convertHostId,
ImportVmTask.TaskState state, Long startIndex, Long pageSizeVal) {
SearchCriteria<ImportVMTaskVO> sc = AllFieldsSearch.create();
if (zoneId != null) {
sc.setParameters("zoneId", zoneId);
Expand All @@ -60,6 +65,10 @@ public List<ImportVMTaskVO> listImportVMTasks(Long zoneId, Long accountId, Strin
if (convertHostId != null) {
sc.setParameters("convertHostId", convertHostId);
}
return showCompleted ? listIncludingRemovedBy(sc) : listBy(sc);
if (state != null) {
sc.setParameters("state", state);
}
Filter filter = new Filter(ImportVMTaskVO.class, "created", false, startIndex, pageSizeVal);
return searchAndCount(sc, filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ CREATE TABLE IF NOT EXISTS `cloud`.`import_vm_task`(
`source_vm_name` varchar(255) COMMENT 'Source VM name on vCenter',
`convert_host_id` bigint unsigned COMMENT 'Convert Host ID',
`import_host_id` bigint unsigned COMMENT 'Import Host ID',
`step` varchar(20) NOT NULL COMMENT 'Importing VM Task Step',
`step` varchar(20) COMMENT 'Importing VM Task Step',
`state` varchar(20) COMMENT 'Importing VM Task State',
`description` varchar(255) COMMENT 'Importing VM Task Description',
`duration` bigint unsigned COMMENT 'Duration in milliseconds for the completed tasks',
`created` datetime NOT NULL COMMENT 'date created',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair;
import com.cloud.vm.ImportVMTaskVO;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.dao.ImportVMTaskDao;
import com.cloud.vm.dao.UserVmDao;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.admin.vm.ListImportVMTasksCmd;
import org.apache.cloudstack.api.response.ImportVMTaskResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand Down Expand Up @@ -72,32 +76,41 @@ public ListResponse<ImportVMTaskResponse> listImportVMTasks(ListImportVMTasksCmd
Long accountId = cmd.getAccountId();
String vcenter = cmd.getVcenter();
Long convertHostId = cmd.getConvertHostId();
boolean listAll = cmd.isListAll();
boolean showCompleted = cmd.isShowCompleted();
Long startIndex = cmd.getStartIndex();
Long pageSizeVal = cmd.getPageSizeVal();

List<ImportVMTaskVO> tasks;
if (listAll) {
tasks = importVMTaskDao.listAll();
} else {
tasks = importVMTaskDao.listImportVMTasks(zoneId, accountId, vcenter, convertHostId, showCompleted);
}
ImportVmTask.TaskState state = getStateFromFilter(cmd.getTasksFilter());
Pair<List<ImportVMTaskVO>, Integer> result = importVMTaskDao.listImportVMTasks(zoneId, accountId, vcenter, convertHostId, state, startIndex, pageSizeVal);
List<ImportVMTaskVO> tasks = result.first();

List<ImportVMTaskResponse> responses = new ArrayList<>();
for (ImportVMTaskVO task : tasks) {
responses.add(createImportVMTaskResponse(task));
}
ListResponse<ImportVMTaskResponse> listResponses = new ListResponse<>();
listResponses.setResponses(responses, responses.size());
listResponses.setResponses(responses, result.second());
return listResponses;
}

private ImportVmTask.TaskState getStateFromFilter(String tasksFilter) {
if (StringUtils.isBlank(tasksFilter) || tasksFilter.equalsIgnoreCase("all")) {
return null;
}
try {
return ImportVmTask.TaskState.getValue(tasksFilter);
} catch (IllegalArgumentException e) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Invalid value for task state: %s", tasksFilter));
}
}

@Override
public ImportVmTask createImportVMTaskRecord(DataCenter zone, Account owner, long userId, String displayName, String vcenter, String datacenterName, String sourceVMName, Host convertHost, Host importHost) {
logger.debug("Creating import VM task entry for VM: {} for account {} on zone {} " +
"from the vCenter: {} / datacenter: {} / source VM: {}",
sourceVMName, owner.getAccountName(), zone.getName(), displayName, vcenter, datacenterName);
ImportVMTaskVO importVMTaskVO = new ImportVMTaskVO(zone.getId(), owner.getAccountId(), userId, displayName,
vcenter, datacenterName, sourceVMName, convertHost.getId(), importHost.getId());
importVMTaskVO.setState(ImportVmTask.TaskState.Running);
return importVMTaskDao.persist(importVMTaskVO);
}

Expand All @@ -111,7 +124,6 @@ private String getStepDescription(ImportVMTaskVO importVMTaskVO, Host convertHos
if (Completed == step) {
stringBuilder.append("Completed at ").append(DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), updatedDate));
} else {
stringBuilder.append(String.format("[%s] ", DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), updatedDate)));
if (CloningInstance == step) {
stringBuilder.append(String.format("Cloning source instance: %s on vCenter: %s / datacenter: %s", sourceVMName, vcenter, datacenter));
} else if (ConvertingInstance == step) {
Expand Down Expand Up @@ -142,13 +154,19 @@ public void updateImportVMTaskStep(ImportVmTask importVMTask, DataCenter zone, A
Duration duration = Duration.between(importVMTaskVO.getCreated().toInstant(), updatedDate.toInstant());
importVMTaskVO.setDuration(duration.toMillis());
importVMTaskVO.setVmId(vmId);
importVMTaskVO.setState(ImportVmTask.TaskState.Completed);
}
importVMTaskDao.update(importVMTaskVO.getId(), importVMTaskVO);
}

@Override
public boolean removeImportVMTask(long taskId) {
return importVMTaskDao.remove(taskId);
public void updateImportVMTaskErrorState(ImportVmTask importVMTask, ImportVmTask.TaskState state, String errorMsg) {
ImportVMTaskVO importVMTaskVO = (ImportVMTaskVO) importVMTask;
Date updatedDate = DateUtil.now();
importVMTaskVO.setUpdated(updatedDate);
importVMTaskVO.setState(state);
importVMTaskVO.setDescription(errorMsg);
importVMTaskDao.update(importVMTaskVO.getId(), importVMTaskVO);
}

private ImportVMTaskResponse createImportVMTaskResponse(ImportVMTaskVO task) {
Expand All @@ -169,19 +187,21 @@ private ImportVMTaskResponse createImportVMTaskResponse(ImportVMTaskVO task) {
response.setDisplayName(task.getDisplayName());
response.setStep(getStepDisplayField(task.getStep()));
response.setDescription(task.getDescription());
response.setState(task.getState().name());

Date updated = task.getUpdated();
Date currentDate = new Date();
if (updated != null && Completed != task.getStep()) {
Duration stepDuration = Duration.between(updated.toInstant(), currentDate.toInstant());
response.setStepDuration(getDurationDisplay(stepDuration.toMillis()));
}
if (Completed == task.getStep()) {
response.setStepDuration(getDurationDisplay(task.getDuration()));
} else {
Duration totalDuration = Duration.between(task.getCreated().toInstant(), currentDate.toInstant());
response.setDuration(getDurationDisplay(totalDuration.toMillis()));

if (updated != null) {
if (ImportVmTask.TaskState.Running == task.getState()) {
Duration stepDuration = Duration.between(updated.toInstant(), currentDate.toInstant());
response.setStepDuration(getDurationDisplay(stepDuration.toMillis()));
} else {
Duration totalDuration = Duration.between(task.getCreated().toInstant(), updated.toInstant());
response.setDuration(getDurationDisplay(totalDuration.toMillis()));
}
}

HostVO host = hostDao.findById(task.getConvertHostId());
if (host != null) {
response.setConvertInstanceHostId(host.getUuid());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1779,10 +1779,10 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
logger.debug(String.format("VMware VM %s imported successfully to CloudStack instance %s (%s), Time taken: %d secs, OVF files imported from %s, Source VMware VM details - OS: %s, PowerState: %s, Disks: %s, NICs: %s",
sourceVMName, displayName, displayName, timeElapsedInSecs, (ovfTemplateOnConvertLocation != null)? "MS" : "KVM Host", sourceVMwareInstance.getOperatingSystem(), sourceVMwareInstance.getPowerState(), sourceVMwareInstance.getDisks(), sourceVMwareInstance.getNics()));
importVmTasksManager.updateImportVMTaskStep(importVMTask, zone, owner, convertHost, importHost, userVm.getId(), Completed);
importVmTasksManager.removeImportVMTask(importVMTask.getId());
return userVm;
} catch (CloudRuntimeException e) {
logger.error(String.format("Error importing VM: %s", e.getMessage()), e);
importVmTasksManager.updateImportVMTaskErrorState(importVMTask, ImportVmTask.TaskState.Failed, e.getMessage());
ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT,
cmd.getEventDescription(), null, null, 0);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,6 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter,
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(CheckConvertInstanceCommand.class))).thenReturn(checkConvertInstanceAnswer);
}

when(importVMTaskVO.getId()).thenReturn(1L);
when(importVmTasksManager.createImportVMTaskRecord(any(DataCenter.class), any(Account.class), anyLong(), anyString(),
anyString(), anyString(), anyString(), any(Host.class), any(Host.class))).thenReturn(importVMTaskVO);
when(volumeApiService.doesStoragePoolSupportDiskOffering(any(StoragePool.class), any(DiskOffering.class))).thenReturn(true);
Expand Down
7 changes: 7 additions & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@
"label.continue": "Continue",
"label.continue.install": "Continue with installation",
"label.controlnodes": "Control nodes",
"label.conversionhost": "Conversion Host",
"label.copied.clipboard": "Copied to clipboard",
"label.copy": "Copy",
"label.copy.clipboard": "Copy to clipboard",
Expand Down Expand Up @@ -699,6 +700,8 @@
"label.crosszones": "Cross Zones",
"label.currency": "Currency",
"label.current": "Current",
"label.currentstep": "Current step",
"label.currentstep.duration": "Current step duration",
"label.current.storage": "Current storage",
"label.currentpassword": "Current password",
"label.custom": "Custom",
Expand Down Expand Up @@ -1244,6 +1247,7 @@
"label.import.instance": "Import Instance",
"label.import.offering": "Import Offering",
"label.import.role": "Import Role",
"label.import.vm.tasks": "Import VM Tasks",
"label.import.volume": "Import Volume",
"label.inactive": "Inactive",
"label.inbuilt": "Inbuilt",
Expand Down Expand Up @@ -2312,6 +2316,7 @@
"label.sourcenattype": "Supported source NAT type",
"label.sourceport": "Source port",
"label.sourcetype": "Source type",
"label.sourcevmname": "Source VM Name",
"label.specifyasnumber": "Specify AS Number",
"label.specifyipranges": "Specify IP ranges",
"label.specifyvlan": "Specify VLAN",
Expand Down Expand Up @@ -2879,6 +2884,7 @@
"label.leaseexpiryaction": "Lease expiry action",
"label.remainingdays": "Lease",
"label.leased": "Leased",
"label.totalduration": "Total duration",
"label.usestoragereplication": "Use primary storage replication",
"message.acquire.ip.failed": "Failed to acquire IP.",
"message.action.acquire.ip": "Please confirm that you want to acquire new IP.",
Expand Down Expand Up @@ -3520,6 +3526,7 @@
"message.host.dedication.released": "Host dedication released.",
"message.host.external.datadisk": "Usage of data disks for the selected template is not applicable",
"message.import.running.instance.warning": "The selected VM is powered-on on the VMware Datacenter. The recommended state to convert a VMware VM into KVM is powered-off after a graceful shutdown of the guest OS.",
"message.import.vm.tasks": "Import from VMware to KVM tasks",
"message.import.volume": "Please specify the domain, account or project name. <br>If not set, the volume will be imported for the caller.",
"message.info.cloudian.console": "Cloudian Management Console should open in another window.",
"message.installwizard.cloudstack.helptext.website": " * Project website:\t ",
Expand Down
Loading
Loading