package org.ovirt.engine.core.bll.gluster.tasks; import java.text.NumberFormat; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import org.ovirt.engine.core.bll.LockMessagesMatchUtil; import org.ovirt.engine.core.bll.gluster.GlusterTasksSyncJob; import org.ovirt.engine.core.bll.job.ExecutionContext; import org.ovirt.engine.core.bll.job.ExecutionHandler; import org.ovirt.engine.core.bll.job.JobRepository; import org.ovirt.engine.core.bll.utils.GlusterAuditLogUtil; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.asynctasks.gluster.GlusterAsyncTask; import org.ovirt.engine.core.common.asynctasks.gluster.GlusterTaskParameters; import org.ovirt.engine.core.common.asynctasks.gluster.GlusterTaskType; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.gluster.GlusterTaskSupport; import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeTaskStatusDetail; import org.ovirt.engine.core.common.constants.gluster.GlusterConstants; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.job.JobExecutionStatus; import org.ovirt.engine.core.common.job.Step; import org.ovirt.engine.core.common.job.StepEnum; import org.ovirt.engine.core.common.locks.LockingGroup; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.common.utils.SizeConverter; import org.ovirt.engine.core.common.utils.SizeConverter.SizeUnit; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.job.ExecutionMessageDirector; import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; import org.ovirt.engine.core.utils.lock.EngineLock; import org.ovirt.engine.core.utils.lock.LockManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton public class GlusterTaskUtils { @Inject private ExecutionHandler executionHandler; @Inject private GlusterVolumeDao volumeDao; @Inject private JobRepository jobRepository; @Inject private LockManager lockManager; @Inject private GlusterAuditLogUtil logUtil; private static final String REBALANCE_IN_PROGRESS = "IN PROGRESS"; private static final String REMOVE_BRICK_FAILED = "MIGRATION FAILED"; private static final String REMOVE_BRICK_IN_PROGRESS = "MIGRATION IN PROGRESS"; private static final String REMOVE_BRICK_FINISHED = "MIGRATION COMPLETE"; private static final Map<GlusterTaskType, String> taskTypeStrMap = new HashMap<>(); private static final Map<GlusterTaskType, AuditLogType> taskTypeAuditMsg = new HashMap<>(); static { taskTypeStrMap.put(GlusterTaskType.REBALANCE, "Rebalance"); taskTypeStrMap.put(GlusterTaskType.REMOVE_BRICK, "Data Migration"); taskTypeAuditMsg.put(GlusterTaskType.REBALANCE, AuditLogType.GLUSTER_VOLUME_REBALANCE_FINISHED); taskTypeAuditMsg.put(GlusterTaskType.REMOVE_BRICK, AuditLogType.GLUSTER_VOLUME_MIGRATE_BRICK_DATA_FINISHED); } private static final Logger log = LoggerFactory.getLogger(GlusterTasksSyncJob.class); public boolean isTaskOfType(GlusterTaskSupport supportObj, GlusterTaskType type) { return supportObj.getAsyncTask() != null && supportObj.getAsyncTask().getType() == type; } public boolean isTaskStatus(GlusterTaskSupport supportObj, JobExecutionStatus status) { return supportObj.getAsyncTask() != null && supportObj.getAsyncTask().getStatus() == status; } /** * Releases the lock held on the cluster having given id and locking group {@link LockingGroup#GLUSTER} * * @param clusterId * ID of the cluster on which the lock is to be released */ public void releaseLock(Guid clusterId) { lockManager.releaseLock(getEngineLock(clusterId)); } /** * Returns an {@link EngineLock} instance that represents a lock on a cluster with given id and the locking group * {@link LockingGroup#GLUSTER} */ private EngineLock getEngineLock(Guid clusterId) { return new EngineLock(Collections.singletonMap(clusterId.toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)), null); } public void releaseVolumeLock(Guid taskId) { // get volume associated with task GlusterVolumeEntity vol = volumeDao.getVolumeByGlusterTask(taskId); if (vol != null) { // release lock on volume releaseLock(vol.getId()); } else { log.debug("Did not find a volume associated with task '{}'", taskId); } } public void endStepJob(Step step) { jobRepository.updateStep(step); ExecutionContext finalContext = executionHandler.createFinalizingContext(step.getId()); executionHandler.endTaskStepAndJob(finalContext, isTaskSuccess(step.getStatus())); } public boolean isTaskSuccess(JobExecutionStatus status) { switch (status) { case ABORTED: case FAILED: return false; case FINISHED: return true; default: return false; } } public boolean hasTaskCompleted(GlusterAsyncTask task) { // Remove brick task is marked completed only if committed or aborted. return JobExecutionStatus.ABORTED == task.getStatus() || (JobExecutionStatus.FINISHED == task.getStatus() && task.getType() != GlusterTaskType.REMOVE_BRICK) || JobExecutionStatus.FAILED == task.getStatus(); } public String getTaskMessage(Cluster cluster, StepEnum stepType, GlusterAsyncTask task) { if (task == null) { return null; } Map<String, String> values = getMessageMap(cluster, task); return ExecutionMessageDirector.resolveStepMessage(stepType, values); } public Map<String, String> getMessageMap(Cluster cluster, GlusterAsyncTask task) { Map<String, String> values = new HashMap<>(); values.put(GlusterConstants.CLUSTER, cluster.getName()); GlusterTaskParameters params = task.getTaskParameters(); values.put(GlusterConstants.VOLUME, params != null ? params.getVolumeName() : ""); String jobStatus = getJobStatusInfo(task); values.put(GlusterConstants.JOB_STATUS, jobStatus); values.put(GlusterConstants.JOB_INFO, task.getMessage()); return values; } private String getJobStatusInfo(GlusterAsyncTask task) { String jobStatus = task.getStatus().toString(); if (task.getType() == GlusterTaskType.REMOVE_BRICK) { switch (task.getStatus()) { case FINISHED: jobStatus = REMOVE_BRICK_FINISHED; break; case STARTED: jobStatus = REMOVE_BRICK_IN_PROGRESS; break; case FAILED: jobStatus = REMOVE_BRICK_FAILED; break; default: break; } } if (task.getType() == GlusterTaskType.REBALANCE) { switch (task.getStatus()) { case STARTED: jobStatus = REBALANCE_IN_PROGRESS; break; default: break; } } return jobStatus; } public void updateSteps(Cluster cluster, GlusterAsyncTask task, List<Step> steps) { // update status in step table for (Step step : steps) { if (step.getEndTime() != null) { // we have already processed the task continue; } JobExecutionStatus oldStatus = step.getStatus(); step.setDescription(getTaskMessage(cluster, step.getStepType(), task)); step.setStatus(task.getStatus()); logEventMessage(task, oldStatus, cluster); if (hasTaskCompleted(task)) { step.markStepEnded(task.getStatus()); endStepJob(step); releaseVolumeLock(task.getTaskId()); } else { jobRepository.updateStep(step); } } } public void logEventMessage(GlusterAsyncTask task, JobExecutionStatus oldStatus, Cluster cluster) { GlusterVolumeEntity volume = volumeDao.getVolumeByGlusterTask(task.getTaskId()); if ( volume == null){ if(task.getTaskParameters() != null) { String volName = task.getTaskParameters().getVolumeName(); volume = volumeDao.getByName(cluster.getId(), volName); } else { return; } } if (JobExecutionStatus.ABORTED == task.getStatus() || JobExecutionStatus.FINISHED == task.getStatus() || JobExecutionStatus.FAILED == task.getStatus()){ if(oldStatus != task.getStatus()){ logMessage(cluster.getId(), volume , taskTypeStrMap.get(task.getType()), task.getStatus().name().toLowerCase(), taskTypeAuditMsg.get(task.getType())); } } } @SuppressWarnings("serial") private void logMessage(Guid clusterId, GlusterVolumeEntity volume, final String action, final String status, AuditLogType logType) { Map<String, String> customValues = new HashMap<>(); customValues.put("action", action); customValues.put("status", status); logUtil.logAuditMessage(clusterId, volume, null, logType, customValues); } public String getSummaryMessage(GlusterVolumeTaskStatusDetail statusSummary) { NumberFormat formatSize = NumberFormat.getInstance(); formatSize.setMaximumFractionDigits(2); formatSize.setMinimumFractionDigits(2); Pair<SizeConverter.SizeUnit, Double> sizeMoved = SizeConverter.autoConvert(statusSummary.getTotalSizeMoved(), SizeUnit.BYTES); return "Files [scanned: " + statusSummary.getFilesScanned() + ", moved: " + statusSummary.getFilesMoved() + ", failed: " + statusSummary.getFilesFailed() + ", Total size moved: " + formatSize.format(sizeMoved.getSecond().doubleValue()) + " " + sizeMoved.getFirst(); } }