package org.ovirt.engine.core.bll.gluster; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.action.gluster.GlusterVolumeActionParameters; import org.ovirt.engine.core.common.action.gluster.GlusterVolumeGeoRepSessionParameters; import org.ovirt.engine.core.common.action.gluster.GlusterVolumeSnapshotActionParameters; import org.ovirt.engine.core.common.asynctasks.gluster.GlusterTaskType; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.gluster.GeoRepSessionStatus; import org.ovirt.engine.core.common.businessentities.gluster.GlusterBrickEntity; import org.ovirt.engine.core.common.businessentities.gluster.GlusterGeoRepSession; import org.ovirt.engine.core.common.businessentities.gluster.GlusterStatus; import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; import org.ovirt.engine.core.common.errors.EngineError; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.job.JobExecutionStatus; import org.ovirt.engine.core.common.locks.LockingGroup; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeInfoVDSParameters; import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeSnapshotActionVDSParameters; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.utils.lock.EngineLock; import org.ovirt.engine.core.utils.transaction.TransactionSupport; @NonTransactiveCommandAttribute public class RestoreGlusterVolumeSnapshotCommand extends GlusterVolumeSnapshotCommandBase<GlusterVolumeSnapshotActionParameters> { private List<GlusterGeoRepSession> georepSessions; private List<GlusterGeoRepSession> engineStoppedSessions; public RestoreGlusterVolumeSnapshotCommand(GlusterVolumeSnapshotActionParameters params, CommandContext commandContext) { super(params, commandContext); engineStoppedSessions = new ArrayList<>(); } @Override protected void init() { super.init(); georepSessions = glusterGeoRepDao.getGeoRepSessions(getGlusterVolumeId()); } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__RESTORE); super.setActionMessageParameters(); } private boolean stopGeoReplicationSessions(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { if (!(session.getStatus() == GeoRepSessionStatus.STOPPED || session.getStatus() == GeoRepSessionStatus.CREATED)) { try (EngineLock lock = acquireGeoRepSessionLock(session.getId())) { VdcReturnValueBase retVal = runInternalAction(VdcActionType.StopGeoRepSession, new GlusterVolumeGeoRepSessionParameters(getGlusterVolumeId(), session.getId())); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GEOREP_SESSION_STOP_FAILED, retVal.getExecuteFailedMessages() .toString()); setSucceeded(false); return false; } session.setStatus(GeoRepSessionStatus.STOPPED); engineStoppedSessions.add(session); } } } return true; } private boolean stopVolume(GlusterVolumeEntity volume) { if (volume != null && volume.getStatus() == GlusterStatus.UP) { VdcReturnValueBase retVal = runInternalAction(VdcActionType.StopGlusterVolume, new GlusterVolumeActionParameters(volume.getId(), true)); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GLUSTER_VOLUME_STOP_FAILED, retVal.getExecuteFailedMessages() .toString()); setSucceeded(false); return false; } } return true; } private boolean stopSlaveVolumes(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { GlusterVolumeEntity slaveVolume = glusterVolumeDao.getById(session.getSlaveVolumeId()); if (slaveVolume == null) { // continue with other sessions and try to stop continue; } try (EngineLock lock = acquireEngineLock(session.getSlaveVolumeId(), LockingGroup.GLUSTER)) { if (!stopVolume(slaveVolume)) { return false; } } } return true; } private boolean restoreVolumeToSnapshot(Guid upServerId, GlusterVolumeEntity volume, String snapshotName) { if (volume != null) { VDSReturnValue retVal = runVdsCommand(VDSCommandType.RestoreGlusterVolumeSnapshot, new GlusterVolumeSnapshotActionVDSParameters(upServerId, volume.getName(), snapshotName)); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GLUSTER_VOLUME_SNAPSHOT_RESTORE_FAILED, retVal.getVdsError() .getMessage()); setSucceeded(false); return false; } else { glusterVolumeSnapshotDao.removeByName(volume.getId(), snapshotName); // Sync the new bricks of the volume immediately VDS upServer = glusterUtil.getRandomUpServer(volume.getClusterId()); VDSReturnValue volDetailsRetVal = runVdsCommand(VDSCommandType.GetGlusterVolumeInfo, new GlusterVolumeInfoVDSParameters(upServer.getId(), volume.getClusterId(), volume.getName())); GlusterVolumeEntity fetchedVolume = ((Map<Guid, GlusterVolumeEntity>) volDetailsRetVal.getReturnValue()).get(volume.getId()); List<GlusterBrickEntity> fetchedBricks = fetchedVolume.getBricks(); if (fetchedBricks != null) { glusterBrickDao.removeAllInBatch(volume.getBricks()); for (GlusterBrickEntity fetchdBrick : fetchedVolume.getBricks()) { if (fetchdBrick.getServerId() != null) { fetchdBrick.setStatus(GlusterStatus.UP); glusterBrickDao.save(fetchdBrick); } else { log.warn("Invalid server details for brick " + fetchdBrick.getName() + ". Not adding now."); } } } } } return true; } private boolean restoreSlaveVolumesToSnapshot(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { GlusterVolumeEntity slaveVolume = glusterVolumeDao.getById(session.getSlaveVolumeId()); if (slaveVolume == null) { // continue with other sessions and try to pause continue; } VDS slaveUpServer = glusterUtil.getRandomUpServer(slaveVolume.getClusterId()); if (slaveUpServer == null) { handleVdsError(AuditLogType.GLUSTER_VOLUME_SNAPSHOT_RESTORE_FAILED, EngineError.NoUpServerFoundInRemoteCluster.name()); setSucceeded(false); return false; } try (EngineLock lock = acquireEngineLock(slaveVolume.getClusterId(), LockingGroup.GLUSTER_SNAPSHOT)) { if (!restoreVolumeToSnapshot(slaveUpServer.getId(), slaveVolume, getSnapshot().getSnapshotName())) { return false; } } } return true; } private boolean startVolume(Guid volumeId) { VdcReturnValueBase retVal = runInternalAction(VdcActionType.StartGlusterVolume, new GlusterVolumeActionParameters(volumeId, true)); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GLUSTER_VOLUME_START_FAILED, retVal.getExecuteFailedMessages().toString()); setSucceeded(false); return false; } return true; } private boolean startSlaveVolumes(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { GlusterVolumeEntity slaveVolume = glusterVolumeDao.getById(session.getSlaveVolumeId()); if (slaveVolume == null) { // continue with other sessions and try to stop continue; } try (EngineLock lock = acquireEngineLock(slaveVolume.getClusterId(), LockingGroup.GLUSTER)) { if (!startVolume(slaveVolume.getId())) { return false; } } } return true; } private boolean resumeGeoRepSessions(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { GlusterVolumeEntity slaveVolume = glusterVolumeDao.getById(session.getSlaveVolumeId()); if (slaveVolume == null) { // continue with other sessions and try to pause continue; } try (EngineLock lock = acquireGeoRepSessionLock(session.getId())) { VdcReturnValueBase retVal = runInternalAction(VdcActionType.ResumeGeoRepSession, new GlusterVolumeGeoRepSessionParameters(getGlusterVolumeId(), session.getId())); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GLUSTER_VOLUME_GEO_REP_RESUME_FAILED, retVal.getExecuteFailedMessages() .toString()); setSucceeded(false); return false; } } } return true; } private boolean startGeoRepSessions(List<GlusterGeoRepSession> geoRepSessions) { for (GlusterGeoRepSession session : geoRepSessions) { try (EngineLock lock = acquireGeoRepSessionLock(session.getId())) { VdcReturnValueBase retVal = runInternalAction(VdcActionType.StartGlusterVolumeGeoRep, new GlusterVolumeGeoRepSessionParameters(getGlusterVolumeId(), session.getId())); if (!retVal.getSucceeded()) { handleVdsError(AuditLogType.GLUSTER_VOLUME_GEO_REP_START_FAILED_EXCEPTION, retVal.getExecuteFailedMessages() .toString()); setSucceeded(false); return false; } } } return true; } @Override public void executeCommand() { Boolean tranRetVal = TransactionSupport.executeInNewTransaction(() -> { if (georepSessions != null) { // Stop the geo-replication session if (!stopGeoReplicationSessions(georepSessions)) { return false; } // Stop the slave volumes if (!stopSlaveVolumes(georepSessions)) { return false; } // Restore the slave volumes to said the snapshot if (!restoreSlaveVolumesToSnapshot(georepSessions)) { return false; } } return true; }); if (!tranRetVal) { return; } // Stop the master volume if (!stopVolume(getGlusterVolume())) { if (!georepSessions.isEmpty()) { handleVdsError(AuditLogType.GLUSTER_MASTER_VOLUME_STOP_FAILED_DURING_SNAPSHOT_RESTORE, EngineError.FailedToStopMasterVolumeDuringVolumeSnapshotRestore.name()); } return; } // Restore the master volume to the said snapshot try (EngineLock lock = acquireEngineLock(getGlusterVolume().getClusterId(), LockingGroup.GLUSTER_SNAPSHOT)) { if (!restoreVolumeToSnapshot(upServer.getId(), getGlusterVolume(), getParameters().getSnapshotName())) { if (!georepSessions.isEmpty()) { handleVdsError(AuditLogType.GLUSTER_MASTER_VOLUME_SNAPSHOT_RESTORE_FAILED, EngineError.FailedToRestoreMasterVolumeDuringVolumeSnapshotRestore.name()); } return; } } // Start the slave volumes if (engineStoppedSessions != null && !startSlaveVolumes(engineStoppedSessions)) { return; } // Start the master volume if (!startVolume(getGlusterVolumeId())) { return; } if (engineStoppedSessions != null) { // Start the geo-replication sessions if (!startGeoRepSessions(engineStoppedSessions)) { return; } // Resume the geo-replication sessions if (!resumeGeoRepSessions(engineStoppedSessions)) { return; } } setSucceeded(true); } @Override protected boolean validate() { if (!super.validate()) { return false; } GlusterVolumeEntity volume = getGlusterVolume(); if (volume.getAsyncTask() != null && (volume.getAsyncTask().getType() == GlusterTaskType.REBALANCE || volume.getAsyncTask().getType() == GlusterTaskType.REMOVE_BRICK) && volume.getAsyncTask().getStatus() == JobExecutionStatus.STARTED) { addValidationMessageVariable("asyncTask", volume.getAsyncTask().getType().name().toLowerCase()); return failValidation(EngineMessage.ACTION_TYPE_FAILED_VOLUME_ASYNC_OPERATION_IN_PROGRESS); } for (GlusterGeoRepSession session : georepSessions) { if (session.getSlaveVolumeId() == null || session.getSlaveNodeUuid() == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_REMOTE_CLUSTER_NOT_MAINTAINED_BY_ENGINE); } } return true; } @Override public AuditLogType getAuditLogTypeValue() { if (getSucceeded()) { return AuditLogType.GLUSTER_VOLUME_SNAPSHOT_RESTORED; } else { return AuditLogType.GLUSTER_VOLUME_SNAPSHOT_RESTORE_FAILED; } } }