Shubhendu Tripathi has uploaded a new change for review. Change subject: gluster: Sync job for gluster volume snapshots ......................................................................
gluster: Sync job for gluster volume snapshots Sync job for gluster volume snapshots Change-Id: I7b7bf79b72fc5680dab301b290e7aa860d5c714d Signed-off-by: Shubhendu Tripathi <[email protected]> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJob.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJobsManager.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJob.java A backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJobTest.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotConfigInfo.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotStatus.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllErrors.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/locks/LockingGroup.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/VDSCommandType.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/gluster/GlusterVolumeSnapshotVDSParameters.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterVolumeSnapshotDaoDbFacadeImpl.java M backend/manager/modules/dal/src/main/resources/bundles/VdsmErrors.properties M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/AbstractGlusterBrokerCommand.java A backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotConfigInfoVDSCommand.java A backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotInfoVDSCommand.java A backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotConfigReturnForXmlRpc.java A backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotInfoReturnForXmlRpc.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/jsonrpc/JsonRpcVdsServer.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/IVdsServer.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerConnector.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerWrapper.java M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/VdsmErrors.java M frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/VdsmErrors.properties M packaging/dbscripts/gluster_volume_snapshot_sp.sql 26 files changed, 1,070 insertions(+), 5 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/80/39280/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJob.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJob.java index 56242e2..b92ad3b 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJob.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJob.java @@ -161,6 +161,14 @@ LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER, VdcBllMessages.ACTION_TYPE_FAILED_GLUSTER_OPERATION_INPROGRESS)), null); } + protected EngineLock acquireVolumeSnapshotLock(Guid id) { + EngineLock lock = new EngineLock(Collections.singletonMap(id.toString(), + LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER_SNAPSHOT, + VdcBllMessages.ACTION_TYPE_FAILED_VOLUME_SNAPSHOT_LOCKED)), null); + LockManagerFactory.getLockManager().acquireLockWait(lock); + return lock; + } + protected GlusterUtil getGlusterUtil() { return GlusterUtil.getInstance(); } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJobsManager.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJobsManager.java index c0f6a27..d97d92c 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJobsManager.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterJobsManager.java @@ -64,6 +64,14 @@ getRefreshRate(ConfigValues.GlusterRefreshRateTasks), TimeUnit.SECONDS); + scheduler.scheduleAFixedDelayJob(GlusterSnapshotSyncJob.getInstance(), + "gluster_snapshot_poll_event", + new Class[0], + new Class[0], + getRefreshRate(ConfigValues.GlusterRefreshRateSnapshotDiscovery), + getRefreshRate(ConfigValues.GlusterRefreshRateSnapshotDiscovery), + TimeUnit.SECONDS); + } private static boolean glusterModeSupported() { diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJob.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJob.java new file mode 100644 index 0000000..5b3c590b --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJob.java @@ -0,0 +1,269 @@ +package org.ovirt.engine.core.bll.gluster; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterSnapshotConfigInfo; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeSnapshotConfig; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeSnapshotEntity; +import org.ovirt.engine.core.common.errors.VdcBLLException; +import org.ovirt.engine.core.common.errors.VdcBllErrors; +import org.ovirt.engine.core.common.gluster.GlusterFeatureSupported; +import org.ovirt.engine.core.common.vdscommands.VDSCommandType; +import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; +import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeSnapshotVDSParameters; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeSnapshotConfigDao; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeSnapshotDao; +import org.ovirt.engine.core.utils.lock.EngineLock; +import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GlusterSnapshotSyncJob extends GlusterJob { + private static final Logger log = LoggerFactory.getLogger(GlusterSnapshotSyncJob.class); + private static final GlusterSnapshotSyncJob instance = new GlusterSnapshotSyncJob(); + + public void init() { + log.info("Gluster snapshot monitoring has been initialized"); + } + + public static GlusterSnapshotSyncJob getInstance() { + return instance; + } + + @OnTimerMethodAnnotation("gluster_snapshot_poll_event") + public void refreshSnapshotData() { + refreshSnapshotList(); + refreshSnapshotConfig(); + } + + public void refreshSnapshotList() { + // get all clusters + List<VDSGroup> clusters = getClusterDao().getAll(); + + for (VDSGroup cluster : clusters) { + refreshSnapshotsInCluster(cluster); + } + } + + public void refreshSnapshotConfig() { + // get all clusters + List<VDSGroup> clusters = getClusterDao().getAll(); + + for (VDSGroup cluster : clusters) { + refreshSnapshotConfigInCluster(cluster); + } + } + + private void refreshSnapshotsInCluster(VDSGroup cluster) { + if (!supportsGlusterSnapshotFeature(cluster)) { + return; + } + + final VDS upServer = getClusterUtils().getRandomUpServer(cluster.getId()); + if (upServer == null) { + log.info("No UP server found in cluster '{}' for snapshot monitoring", cluster.getName()); + return; + } + + VDSReturnValue returnValue = runVdsCommand(VDSCommandType.GetGlusterVolumeSnapshotInfo, + new GlusterVolumeSnapshotVDSParameters(upServer.getId(), cluster.getId(), null)); + if (returnValue.getSucceeded()) { + addOrUpdateSnapshots(cluster.getId(), (ArrayList<GlusterVolumeSnapshotEntity>) returnValue.getReturnValue()); + } else { + log.error("VDS Error {}", returnValue.getVdsError().getMessage()); + log.debug("VDS Error {}", returnValue.getVdsError()); + } + } + + private void refreshSnapshotConfigInCluster(VDSGroup cluster) { + if (!supportsGlusterSnapshotFeature(cluster)) { + return; + } + + final VDS upServer = getClusterUtils().getRandomUpServer(cluster.getId()); + if (upServer == null) { + log.info("No UP server found in cluster '{}' for snapshot configurations monitoring", cluster.getName()); + return; + } + + VDSReturnValue returnValue = + runVdsCommand(VDSCommandType.GetGlusterVolumeSnapshotConfigInfo, + new GlusterVolumeSnapshotVDSParameters(upServer.getId(), cluster.getId(), null)); + if (returnValue.getSucceeded()) { + addOrUpdateSnapshotsConfig(cluster.getId(), (GlusterSnapshotConfigInfo) returnValue.getReturnValue()); + } else { + log.error("VDS Error {}", returnValue.getVdsError().getMessage()); + log.debug("VDS Error {}", returnValue.getVdsError()); + } + } + + private void addOrUpdateSnapshots(Guid clusterId, List<GlusterVolumeSnapshotEntity> fetchedSnapshots) { + Map<Guid, GlusterVolumeSnapshotEntity> fetchedSnapshotsMap = new HashMap<>(); + for (GlusterVolumeSnapshotEntity fetchedSnapshot : fetchedSnapshots) { + fetchedSnapshotsMap.put(fetchedSnapshot.getId(), fetchedSnapshot); + } + + List<GlusterVolumeSnapshotEntity> existingSnapshots = + getGlusterVolumeSnapshotDao().getAllByClusterId(clusterId); + Map<Guid, GlusterVolumeSnapshotEntity> existingSnapshotsMap = new HashMap<>(); + for (GlusterVolumeSnapshotEntity existingSnapshot : existingSnapshots) { + existingSnapshotsMap.put(existingSnapshot.getId(), existingSnapshot); + } + + List<GlusterVolumeSnapshotEntity> updatedSnapshots = new ArrayList<>(); + List<GlusterVolumeSnapshotEntity> newlyAddedSnapshots = new ArrayList<>(); + List<GlusterVolumeSnapshotEntity> deletedSnapshots = new ArrayList<>(); + + for (GlusterVolumeSnapshotEntity fetchedSnapshot : fetchedSnapshots) { + GlusterVolumeSnapshotEntity correspondingExistingSnapshot = + existingSnapshotsMap.get(fetchedSnapshot.getId()); + if (correspondingExistingSnapshot == null) { + newlyAddedSnapshots.add(fetchedSnapshot); + } else if (correspondingExistingSnapshot.getStatus() != fetchedSnapshot.getStatus()) { + correspondingExistingSnapshot.setStatus(fetchedSnapshot.getStatus()); + updatedSnapshots.add(correspondingExistingSnapshot); + } + } + + for (GlusterVolumeSnapshotEntity existingSnapshot : existingSnapshots) { + GlusterVolumeSnapshotEntity correspondingFetchedSnapshot = + fetchedSnapshotsMap.get(existingSnapshot.getId()); + if (correspondingFetchedSnapshot == null) { + deletedSnapshots.add(existingSnapshot); + } + } + + // update snapshot details + try (EngineLock lock = acquireVolumeSnapshotLock(clusterId)) { + saveNewSnapshots(newlyAddedSnapshots); + updateSnapshots(updatedSnapshots); + deleteSnapshots(deletedSnapshots); + } catch (Exception e) { + log.error("Exception ocuured while adding/updating snapshots from CLI - '{}'", e.getMessage()); + log.debug("Exception", e); + throw new VdcBLLException(VdcBllErrors.GlusterSnapshotInfoFailedException, e.getLocalizedMessage()); + } + } + + private void addOrUpdateSnapshotsConfig(Guid clusterId, GlusterSnapshotConfigInfo configInfo) { + try (EngineLock lock = acquireVolumeSnapshotLock(clusterId)) { + for (Map.Entry<String, String> entry : configInfo.getClusterConfigOptions().entrySet()) { + if (entry.getValue() != null) { + addOrUpdateClusterConfig(clusterId, entry.getKey(), entry.getValue()); + } + } + } catch (Exception e) { + log.error("Exception ocuured while adding/updating snapshots configurations from CLI - '{}'", + e.getMessage()); + log.debug("Exception", e); + throw new VdcBLLException(VdcBllErrors.GlusterSnapshotInfoFailedException, e.getLocalizedMessage()); + } + + Map<String, Map<String, String>> volumeConfigs = configInfo.getVolumeConfigOptions(); + for (Map.Entry<String, Map<String, String>> entry : volumeConfigs.entrySet()) { + GlusterVolumeEntity volume = getGlusterVolumeDao().getByName(clusterId, entry.getKey()); + if (volume == null) { + continue; + } + + try (EngineLock lock = acquireVolumeSnapshotLock(volume.getId())) { + Map<String, String> volumeConfig = entry.getValue(); + if (volumeConfig != null) { + for (Map.Entry<String, String> entry1 : volumeConfig.entrySet()) { + if (entry.getValue() != null) { + addOrUpdateVolumeConfig(clusterId, + volume.getId(), + entry1.getKey(), + entry1.getValue()); + } + } + } + } catch (Exception e) { + log.error("Exception ocuured while adding/updating snapshots configurations from CLI - '{}'", + e.getMessage()); + log.debug("Exception", e); + throw new VdcBLLException(VdcBllErrors.GlusterSnapshotInfoFailedException, e.getLocalizedMessage()); + } + } + } + + private void addOrUpdateClusterConfig(Guid clusterId, String paramName, String paramValue) { + GlusterVolumeSnapshotConfig param = new GlusterVolumeSnapshotConfig(); + param.setClusterId(clusterId); + param.setVolumeId(null); + param.setParamName(paramName); + param.setParamValue(paramValue); + GlusterVolumeSnapshotConfig existingParamDetail = + getGlusterVolumeSnapshotConfigDao().getConfigByClusterIdAndName(clusterId, + paramName); + if (existingParamDetail == null) { + getGlusterVolumeSnapshotConfigDao().save(param); + } else if (!(existingParamDetail.getParamValue().equals(paramValue))) { + getGlusterVolumeSnapshotConfigDao().updateConfigByClusterIdAndName(clusterId, + paramName, + paramValue); + } + } + + private void addOrUpdateVolumeConfig(Guid clusterId, Guid volumeId, String paramName, String paramValue) { + GlusterVolumeSnapshotConfig cfg = new GlusterVolumeSnapshotConfig(); + cfg.setClusterId(clusterId); + cfg.setVolumeId(volumeId); + cfg.setParamName(paramName); + cfg.setParamValue(paramValue); + GlusterVolumeSnapshotConfig existingParamDetail = + getGlusterVolumeSnapshotConfigDao().getConfigByVolumeIdAndName(clusterId, + volumeId, + paramName); + if (existingParamDetail == null) { + getGlusterVolumeSnapshotConfigDao().save(cfg); + } else if (!(existingParamDetail.getParamValue().equals(paramValue))) { + getGlusterVolumeSnapshotConfigDao().updateConfigByVolumeIdAndName(clusterId, + volumeId, + paramName, + paramValue); + } + } + + private void saveNewSnapshots(List<GlusterVolumeSnapshotEntity> snapshots) { + getGlusterVolumeSnapshotDao().saveAll(snapshots); + } + + private void updateSnapshots(List<GlusterVolumeSnapshotEntity> snapshots) { + getGlusterVolumeSnapshotDao().updateAllInBatch(snapshots); + } + + private void deleteSnapshots(List<GlusterVolumeSnapshotEntity> snaphosts) { + List<Guid> deletedIds = new ArrayList<>(); + for (GlusterVolumeSnapshotEntity snapshot : snaphosts) { + deletedIds.add(snapshot.getId()); + } + getGlusterVolumeSnapshotDao().removeAll(deletedIds); + } + + private boolean supportsGlusterSnapshotFeature(VDSGroup cluster) { + return cluster.supportsGlusterService() + && GlusterFeatureSupported.glusterSnapshot(cluster.getcompatibility_version()); + } + + protected GlusterVolumeDao getGlusterVolumeDao() { + return DbFacade.getInstance().getGlusterVolumeDao(); + } + + protected GlusterVolumeSnapshotDao getGlusterVolumeSnapshotDao() { + return DbFacade.getInstance().getGlusterVolumeSnapshotDao(); + } + + protected GlusterVolumeSnapshotConfigDao getGlusterVolumeSnapshotConfigDao() { + return DbFacade.getInstance().getGlusterVolumeSnapshotConfigDao(); + } +} diff --git a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJobTest.java b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJobTest.java new file mode 100644 index 0000000..bcefc69 --- /dev/null +++ b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterSnapshotSyncJobTest.java @@ -0,0 +1,383 @@ +package org.ovirt.engine.core.bll.gluster; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.ovirt.engine.core.utils.MockConfigRule.mockConfig; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.runners.MockitoJUnitRunner; +import org.ovirt.engine.core.bll.utils.ClusterUtils; +import org.ovirt.engine.core.bll.utils.GlusterUtil; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterBrickEntity; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterSnapshotConfigInfo; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterSnapshotStatus; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterStatus; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeSnapshotConfig; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeSnapshotEntity; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.common.vdscommands.VDSCommandType; +import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; +import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeSnapshotVDSParameters; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.compat.Version; +import org.ovirt.engine.core.dal.dbbroker.auditloghandling.gluster.GlusterAuditLogUtil; +import org.ovirt.engine.core.dao.VdsGroupDAO; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeSnapshotConfigDao; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeSnapshotDao; +import org.ovirt.engine.core.utils.MockConfigRule; +import org.ovirt.engine.core.utils.lock.EngineLock; + +@RunWith(MockitoJUnitRunner.class) +public class GlusterSnapshotSyncJobTest { + private static final Guid CLUSTER_ID_1 = Guid.newGuid(); + private static final Guid CLUSTER_ID_2 = Guid.newGuid(); + private static final Guid VOLUME_ID_1 = Guid.newGuid(); + private static final String VOLUME_NAME_1 = "VOL1"; + private static final Guid VOLUME_ID_2 = Guid.newGuid(); + private static final String VOLUME_NAME_2 = "VOL2"; + private static final Guid[] existingSnapshotIds = { Guid.newGuid(), Guid.newGuid() }; + private static final String[] existingSnapshotNames = { "snap-1", "snap-2" }; + private static final Date existingSnapsCreateDate = new Date(); + private static final Guid newSnapshotId = Guid.newGuid(); + private static final String newSnapshotName = "new-snap"; + private static final String PARAM_SNAP_MAX_LIMIT = "snap-max-hard-limit"; + private static final String PARAM_SNAP_MAX_SOFT_LIMIT = "snap-max-soft-limit"; + private static final String PARAM_AUTO_DELETE = "auto-delete"; + + @Mock + private GlusterVolumeDao volumeDao; + + @Mock + private ClusterUtils clusterUtils; + + @Mock + private GlusterVolumeSnapshotDao snapshotDao; + + @Mock + private GlusterVolumeSnapshotConfigDao snapshotConfigDao; + + @Mock + private VdsGroupDAO clusterDao; + + private GlusterSnapshotSyncJob syncJob; + + @Mock + private GlusterAuditLogUtil logUtil; + + @Mock + private GlusterUtil glusterUtil; + + @Mock + private EngineLock engineLock; + + @ClassRule + public static MockConfigRule mcr = new MockConfigRule( + mockConfig(ConfigValues.GlusterVolumeSnapshotSupported, Version.v3_5.toString(), true), + mockConfig(ConfigValues.GlusterVolumeSnapshotSupported, Version.v3_4.toString(), false), + mockConfig(ConfigValues.DefaultMinThreadPoolSize, 10), + mockConfig(ConfigValues.DefaultMaxThreadPoolSize, 20), + mockConfig(ConfigValues.DefaultMaxThreadWaitQueueSize, 10) + ); + + @Before + public void init() { + syncJob = Mockito.spy(GlusterSnapshotSyncJob.getInstance()); + MockitoAnnotations.initMocks(this); + syncJob.setLogUtil(logUtil); + + doReturn(clusterDao).when(syncJob).getClusterDao(); + doReturn(volumeDao).when(syncJob).getGlusterVolumeDao(); + doReturn(snapshotDao).when(syncJob).getGlusterVolumeSnapshotDao(); + doReturn(snapshotConfigDao).when(syncJob).getGlusterVolumeSnapshotConfigDao(); + doReturn(clusterUtils).when(syncJob).getClusterUtils(); + + doReturn(getClusters()).when(clusterDao).getAll(); + doReturn(getVolumes()).when(volumeDao).getByClusterId(argThat(validClusterId())); + doReturn(getVolume(CLUSTER_ID_1, VOLUME_ID_1, VOLUME_NAME_1)).when(volumeDao) + .getByName(argThat(validClusterId()), argThat(validVolumeName())); + doReturn(getServer()).when(clusterUtils).getRandomUpServer(any(Guid.class)); + + doReturn(engineLock).when(syncJob).acquireVolumeSnapshotLock(any(Guid.class)); + } + + @Test + public void testSyncSnapshotsList() { + doReturn(getExistingSnapshots()).when(snapshotDao).getAllByVolumeId(argThat(validVolumeId())); + doReturn(getSnapshotVDSReturnVal(true)).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeSnapshotInfo), + argThat(snapshotInfoParam())); + syncJob.refreshSnapshotList(); + Mockito.verify(snapshotDao, times(1)).saveAll(any(List.class)); + Mockito.verify(snapshotDao, times(1)).removeAll(any(List.class)); + Mockito.verify(snapshotDao, times(1)).updateAllInBatch(any(List.class)); + } + + @Test + public void testSyncSnapshotConfigs() { + doReturn(getClusterSnapMaxLimit()).when(snapshotConfigDao) + .getConfigByClusterIdAndName(CLUSTER_ID_1, PARAM_SNAP_MAX_LIMIT); + doReturn(null).when(snapshotConfigDao).getConfigByClusterIdAndName(CLUSTER_ID_1, + PARAM_SNAP_MAX_SOFT_LIMIT); + doReturn(null).when(snapshotConfigDao).getConfigByClusterIdAndName(CLUSTER_ID_1, + PARAM_AUTO_DELETE); + doReturn(getVolume(CLUSTER_ID_1, VOLUME_ID_1, VOLUME_NAME_1)).when(volumeDao).getByName(CLUSTER_ID_1, + VOLUME_NAME_1); + doReturn(getVolume(CLUSTER_ID_1, VOLUME_ID_2, VOLUME_NAME_2)).when(volumeDao).getByName(CLUSTER_ID_1, + VOLUME_NAME_2); + doReturn(getVolumeSnapMaxLimit()).when(snapshotConfigDao) + .getConfigByVolumeIdAndName(CLUSTER_ID_1, VOLUME_ID_1, PARAM_SNAP_MAX_LIMIT); + doReturn(null).when(snapshotConfigDao).getConfigByVolumeIdAndName(CLUSTER_ID_1, + VOLUME_ID_2, + PARAM_SNAP_MAX_LIMIT); + doReturn(getSnapshotConfigVDSReturnValue()).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeSnapshotConfigInfo), + argThat(snapshotInfoParam())); + syncJob.refreshSnapshotConfig(); + Mockito.verify(snapshotConfigDao, times(3)).save(any(GlusterVolumeSnapshotConfig.class)); + Mockito.verify(snapshotConfigDao, times(1)).updateConfigByClusterIdAndName(any(Guid.class), + any(String.class), + any(String.class)); + Mockito.verify(snapshotConfigDao, times(1)).updateConfigByVolumeIdAndName(any(Guid.class), + any(Guid.class), + any(String.class), + any(String.class)); + } + + private GlusterVolumeSnapshotConfig getClusterSnapMaxLimit() { + GlusterVolumeSnapshotConfig param = new GlusterVolumeSnapshotConfig(); + param.setClusterId(CLUSTER_ID_1); + param.setVolumeId(null); + param.setParamName(PARAM_SNAP_MAX_LIMIT); + param.setParamValue("256"); + return param; + } + + private GlusterVolumeSnapshotConfig getVolumeSnapMaxLimit() { + GlusterVolumeSnapshotConfig param = new GlusterVolumeSnapshotConfig(); + param.setClusterId(CLUSTER_ID_1); + param.setVolumeId(VOLUME_ID_1); + param.setParamName(PARAM_SNAP_MAX_LIMIT); + param.setParamValue("20"); + return param; + } + + private ArgumentMatcher<GlusterVolumeSnapshotVDSParameters> snapshotInfoParam() { + return new ArgumentMatcher<GlusterVolumeSnapshotVDSParameters>() { + + @Override + public boolean matches(Object argument) { + if (!(argument instanceof GlusterVolumeSnapshotVDSParameters)) { + return false; + } + return ((GlusterVolumeSnapshotVDSParameters) argument).getClusterId().equals(CLUSTER_ID_1); + } + }; + } + + private ArgumentMatcher<Guid> validClusterId() { + return new ArgumentMatcher<Guid>() { + @Override + public boolean matches(Object argument) { + if (!(argument instanceof Guid)) { + return false; + } + return ((Guid) argument).equals(CLUSTER_ID_1); + } + }; + } + + private ArgumentMatcher<String> validVolumeName() { + return new ArgumentMatcher<String>() { + @Override + public boolean matches(Object argument) { + if (!(argument instanceof String)) { + return false; + } + return ((String) argument).equals(VOLUME_NAME_1); + } + }; + } + + private ArgumentMatcher<Guid> validVolumeId() { + return new ArgumentMatcher<Guid>() { + @Override + public boolean matches(Object argument) { + if (!(argument instanceof Guid)) { + return false; + } + return ((Guid) argument).equals(VOLUME_ID_1); + } + }; + } + + private List<GlusterVolumeSnapshotEntity> getExistingSnapshots() { + List<GlusterVolumeSnapshotEntity> snapsList = new ArrayList<GlusterVolumeSnapshotEntity>(); + + GlusterVolumeSnapshotEntity snap1 = new GlusterVolumeSnapshotEntity(); + snap1.setId(existingSnapshotIds[0]); + snap1.setClusterId(CLUSTER_ID_1); + snap1.setCreatedAt(existingSnapsCreateDate); + snap1.setDescription(""); + snap1.setSnapshotName(existingSnapshotNames[0]); + snap1.setStatus(GlusterSnapshotStatus.STARTED); + snap1.setVolumeId(VOLUME_ID_1); + snapsList.add(snap1); + + GlusterVolumeSnapshotEntity snap2 = new GlusterVolumeSnapshotEntity(); + snap2.setId(existingSnapshotIds[1]); + snap2.setClusterId(CLUSTER_ID_1); + snap2.setCreatedAt(existingSnapsCreateDate); + snap2.setDescription(""); + snap2.setSnapshotName(existingSnapshotNames[1]); + snap2.setStatus(GlusterSnapshotStatus.STARTED); + snap2.setVolumeId(VOLUME_ID_1); + snapsList.add(snap2); + + return snapsList; + } + + private Object getSnapshotConfigVDSReturnValue() { + VDSReturnValue vdsRetValue = new VDSReturnValue(); + vdsRetValue.setSucceeded(true); + vdsRetValue.setReturnValue(getSnapshotConfigInfo()); + return vdsRetValue; + } + + private GlusterSnapshotConfigInfo getSnapshotConfigInfo() { + GlusterSnapshotConfigInfo config = new GlusterSnapshotConfigInfo(); + Map<String, String> clusterConfigs = new HashMap<String, String>(); + clusterConfigs.put("snap-max-hard-limit", "200"); + clusterConfigs.put("snap-max-soft-limit", "90%"); + clusterConfigs.put("auto-delete", "enable"); + config.setClusterConfigOptions(clusterConfigs); + + Map<String, Map<String, String>> volumeConfigs = new HashMap<String, Map<String, String>>(); + + Map<String, String> volConf1 = new HashMap<String, String>(); + volConf1.put("snap-max-hard-limit", "30"); + volumeConfigs.put(VOLUME_NAME_1, volConf1); + + Map<String, String> volConf2 = new HashMap<String, String>(); + volConf2.put("snap-max-hard-limit", "50"); + volumeConfigs.put(VOLUME_NAME_2, volConf2); + + config.setVolumeConfigOptions(volumeConfigs); + + return config; + } + + private Object getSnapshotVDSReturnVal(boolean ret) { + VDSReturnValue vdsRetValue = new VDSReturnValue(); + vdsRetValue.setSucceeded(ret); + if (ret) { + vdsRetValue.setReturnValue(getSnapshotDetails()); + } else { + vdsRetValue.setReturnValue(null); + } + return vdsRetValue; + } + + private List<GlusterVolumeSnapshotEntity> getSnapshotDetails() { + List<GlusterVolumeSnapshotEntity> snapshots = new ArrayList<GlusterVolumeSnapshotEntity>(); + + GlusterVolumeSnapshotEntity snap1 = new GlusterVolumeSnapshotEntity(); + snap1.setClusterId(CLUSTER_ID_1); + snap1.setCreatedAt(existingSnapsCreateDate); + snap1.setDescription(""); + snap1.setId(existingSnapshotIds[0]); + snap1.setSnapshotName(existingSnapshotNames[0]); + snap1.setStatus(GlusterSnapshotStatus.STOPPED); + snap1.setVolumeId(VOLUME_ID_1); + snapshots.add(snap1); + + GlusterVolumeSnapshotEntity snap2 = new GlusterVolumeSnapshotEntity(); + snap2.setClusterId(CLUSTER_ID_1); + snap2.setCreatedAt(new Date()); + snap2.setDescription(""); + snap2.setId(newSnapshotId); + snap2.setSnapshotName(newSnapshotName); + snap2.setStatus(GlusterSnapshotStatus.STARTED); + snap2.setVolumeId(VOLUME_ID_1); + snapshots.add(snap2); + + return snapshots; + } + + private List<VDSGroup> getClusters() { + List<VDSGroup> list = new ArrayList<>(); + + VDSGroup cluster = new VDSGroup(); + cluster.setId(CLUSTER_ID_1); + cluster.setName("cluster"); + cluster.setGlusterService(true); + cluster.setVirtService(false); + cluster.setcompatibility_version(Version.v3_5); + list.add(cluster); + + VDSGroup cluster1 = new VDSGroup(); + cluster1.setId(CLUSTER_ID_2); + cluster1.setName("cluster1"); + cluster1.setGlusterService(true); + cluster1.setVirtService(false); + cluster1.setcompatibility_version(Version.v3_4); + list.add(cluster1); + + return list; + } + + private VDS getServer() { + VDS vds = new VDS(); + vds.setId(Guid.newGuid()); + return vds; + } + + private List<GlusterVolumeEntity> getVolumes() { + List<GlusterVolumeEntity> volList = new ArrayList<GlusterVolumeEntity>(); + volList.add(getVolume(CLUSTER_ID_1, VOLUME_ID_1, VOLUME_NAME_1)); + return volList; + } + + private GlusterVolumeEntity getVolume(Guid clusterId, Guid volumeId, String volumeName) { + GlusterVolumeEntity volume = new GlusterVolumeEntity(); + volume.setName(volumeName); + volume.setClusterId(clusterId); + volume.setId(volumeId); + volume.setReplicaCount(2); + + GlusterBrickEntity brick = new GlusterBrickEntity(); + brick.setVolumeId(volume.getId()); + brick.setBrickDirectory("/export/testvol1"); + brick.setStatus(GlusterStatus.UP); + brick.setBrickOrder(0); + volume.addBrick(brick); + + GlusterBrickEntity brick2 = new GlusterBrickEntity(); + brick2.setVolumeId(volume.getId()); + brick2.setBrickDirectory("/export/testvol1"); + brick2.setStatus(GlusterStatus.UP); + brick2.setBrickOrder(1); + volume.addBrick(brick2); + + return volume; + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotConfigInfo.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotConfigInfo.java new file mode 100644 index 0000000..6941672 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotConfigInfo.java @@ -0,0 +1,54 @@ +package org.ovirt.engine.core.common.businessentities.gluster; + +import java.io.Serializable; +import java.util.Map; + +import org.ovirt.engine.core.common.utils.ObjectUtils; + +public class GlusterSnapshotConfigInfo implements Serializable { + private static final long serialVersionUID = -768822766895441288L; + + private Map<String, String> clusterConfigOptions; + private Map<String, Map<String, String>> volumeConfigOptions; + + public Map<String, String> getClusterConfigOptions() { + return this.clusterConfigOptions; + } + + public void setClusterConfigOptions(Map<String, String> options) { + this.clusterConfigOptions = options; + } + + public Map<String, Map<String, String>> getVolumeConfigOptions() { + return this.volumeConfigOptions; + } + + public void setVolumeConfigOptions(Map<String, Map<String, String>> options) { + this.volumeConfigOptions = options; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof GlusterSnapshotConfigInfo)) { + return false; + } + GlusterSnapshotConfigInfo configInfo = (GlusterSnapshotConfigInfo) obj; + if (!ObjectUtils.objectsEqual(getClusterConfigOptions(), configInfo.getClusterConfigOptions())) { + return false; + } + if (!ObjectUtils.objectsEqual(getVolumeConfigOptions(), configInfo.getVolumeConfigOptions())) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getClusterConfigOptions() == null) ? 0 : getClusterConfigOptions().hashCode()); + result = + prime * result + ((getVolumeConfigOptions() == null) ? 0 : getVolumeConfigOptions().hashCode()); + return result; + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotStatus.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotStatus.java index 1912c1f..3a171dc 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotStatus.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterSnapshotStatus.java @@ -7,7 +7,7 @@ public static GlusterSnapshotStatus from(String status) { for (GlusterSnapshotStatus snapshotStatus : values()) { - if (snapshotStatus.name().equals(status)) { + if (snapshotStatus.name().equalsIgnoreCase(status)) { return snapshotStatus; } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllErrors.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllErrors.java index 72c5ef9..1a6790c 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllErrors.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllErrors.java @@ -399,6 +399,8 @@ GlfsStatvfsException(4571), GlfsInitException(4572), GlfsFiniException(4573), + GlusterSnapshotException(4700), + GlusterSnapshotInfoFailedException(4708), UnicodeArgumentException(4900), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java index 12c8485..70e32d2 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java @@ -929,6 +929,8 @@ ACTION_TYPE_FAILED_NO_SERVERS_FOR_CLUSTER(ErrorType.BAD_PARAMETERS), ACTION_TYPE_FAILED_VOLUME_OPERATION_IN_PROGRESS(ErrorType.CONFLICT), ACTION_TYPE_FAILED_GLUSTER_OPERATION_INPROGRESS(ErrorType.CONFLICT), + ACTION_TYPE_FAILED_VOLUME_SNAPSHOT_LOCKED(ErrorType.CONFLICT), + // OpenStack Glance ACTION_TYPE_FAILED_IMAGE_DOWNLOAD_ERROR(ErrorType.BAD_PARAMETERS), ACTION_TYPE_FAILED_IMAGE_NOT_SUPPORTED(ErrorType.BAD_PARAMETERS), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java index 564f731..2e72df9 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java @@ -80,4 +80,14 @@ return false; } } + + /** + * @param version + * Compatibility version to check for. + * @return <code>true</code> if gluster snapshot management feature is enabled, + * <code>false</code> if it's not. + */ + public static boolean glusterSnapshot(Version version) { + return supportedInConfig(ConfigValues.GlusterVolumeSnapshotSupported, version); + } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/locks/LockingGroup.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/locks/LockingGroup.java index 83801a6..2b10a8c 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/locks/LockingGroup.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/locks/LockingGroup.java @@ -17,6 +17,8 @@ REGISTER_VDS, VM_SNAPSHOTS, GLUSTER, + /** this group is used for gluster volume snapshot purpose */ + GLUSTER_SNAPSHOT, USER_VM_POOL, /** This group is used to lock template which is in export domain */ REMOTE_TEMPLATE, diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/VDSCommandType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/VDSCommandType.java index 5bb1b87..b2e62da 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/VDSCommandType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/VDSCommandType.java @@ -158,6 +158,8 @@ GetDiskAlignment("org.ovirt.engine.core.vdsbroker.vdsbroker"), GlusterTasksList("org.ovirt.engine.core.vdsbroker.gluster"), GetGlusterVolumeRemoveBricksStatus("org.ovirt.engine.core.vdsbroker.gluster"), + GetGlusterVolumeSnapshotInfo("org.ovirt.engine.core.vdsbroker.gluster"), + GetGlusterVolumeSnapshotConfigInfo("org.ovirt.engine.core.vdsbroker.gluster"), SetNumberOfCpus("org.ovirt.engine.core.vdsbroker"), UpdateVmPolicy("org.ovirt.engine.core.vdsbroker"), List("org.ovirt.engine.core.vdsbroker.vdsbroker"), // get a list of VMs with status only diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/gluster/GlusterVolumeSnapshotVDSParameters.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/gluster/GlusterVolumeSnapshotVDSParameters.java new file mode 100644 index 0000000..99dfab7 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/vdscommands/gluster/GlusterVolumeSnapshotVDSParameters.java @@ -0,0 +1,23 @@ +package org.ovirt.engine.core.common.vdscommands.gluster; + +import org.ovirt.engine.core.compat.Guid; + +public class GlusterVolumeSnapshotVDSParameters extends GlusterVolumeVDSParameters { + protected Guid clusterId; + + public GlusterVolumeSnapshotVDSParameters() { + } + + public GlusterVolumeSnapshotVDSParameters(Guid serverId, Guid clusterId, String volumeName) { + super(serverId, volumeName); + this.clusterId = clusterId; + } + + public Guid getClusterId() { + return this.clusterId; + } + + public void setClusterId(Guid clusterId) { + this.clusterId = clusterId; + } +} diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterVolumeSnapshotDaoDbFacadeImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterVolumeSnapshotDaoDbFacadeImpl.java index bf3137b..1ef2690 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterVolumeSnapshotDaoDbFacadeImpl.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterVolumeSnapshotDaoDbFacadeImpl.java @@ -130,7 +130,8 @@ .addValue("snapshot_name", snapshot.getSnapshotName()) .addValue("volume_id", snapshot.getVolumeId()) .addValue("description", snapshot.getDescription()) - .addValue("status", EnumUtils.nameOrNull(snapshot.getStatus())); + .addValue("status", EnumUtils.nameOrNull(snapshot.getStatus())) + .addValue("_create_date", snapshot.getCreatedAt()); } private MapSqlParameterSource createSnapshotIdParams(Guid id) { diff --git a/backend/manager/modules/dal/src/main/resources/bundles/VdsmErrors.properties b/backend/manager/modules/dal/src/main/resources/bundles/VdsmErrors.properties index f3b06a9..bc0953c 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/VdsmErrors.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/VdsmErrors.properties @@ -372,6 +372,8 @@ GlfsStatvfsException=Failed to get gluster volume size info GlfsInitException=Command failed while mounting gluster volume GlfsFiniException=Command failed while unmounting gluster volume +GlusterSnapshotException=Error in executing gluster snapshot command +GlusterSnapshotInfoFailedException=Gluster snapshot info failed CANT_RECONSTRUCT_WHEN_A_DOMAIN_IN_POOL_IS_LOCKED=Can't reconstruct the Master Domain when the Data Center contains Domains in Locked state.\nPlease wait until the operation for these Domains ends before trying to reconstruct the Master Domain again. NO_IMPLEMENTATION=Not implemented diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/AbstractGlusterBrokerCommand.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/AbstractGlusterBrokerCommand.java index 2b2d573..6578218 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/AbstractGlusterBrokerCommand.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/AbstractGlusterBrokerCommand.java @@ -70,6 +70,9 @@ case GlfsStatvfsException: case GlfsInitException: case GlfsFiniException: + case GlusterSnapshotException: + case GlusterSnapshotInfoFailedException: + // Capture error from gluster command and record failure getVDSReturnValue().setVdsError(new VDSError(returnStatus, getReturnStatus().mMessage)); getVDSReturnValue().setSucceeded(false); diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotConfigInfoVDSCommand.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotConfigInfoVDSCommand.java new file mode 100644 index 0000000..fdcbf21 --- /dev/null +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotConfigInfoVDSCommand.java @@ -0,0 +1,30 @@ +package org.ovirt.engine.core.vdsbroker.gluster; + +import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeSnapshotVDSParameters; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.vdsbroker.vdsbroker.StatusForXmlRpc; + +public class GetGlusterVolumeSnapshotConfigInfoVDSCommand<P extends GlusterVolumeSnapshotVDSParameters> extends AbstractGlusterBrokerCommand<P> { + private GlusterVolumeSnapshotConfigReturnForXmlRpc infoReturn; + + public GetGlusterVolumeSnapshotConfigInfoVDSCommand(P parameters) { + super(parameters); + } + + @Override + protected StatusForXmlRpc getReturnStatus() { + return this.infoReturn.mStatus; + } + + @Override + protected void executeVdsBrokerCommand() { + Guid clusterId = getParameters().getClusterId(); + String volumeName = getParameters().getVolumeName(); + infoReturn = getBroker().glusterVolumeSnapshotConfigGet(clusterId, volumeName); + proceedProxyReturnValue(); + + if (getVDSReturnValue().getSucceeded()) { + setReturnValue(infoReturn.getGlusterSnapshotConfigInfo()); + } + } +} diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotInfoVDSCommand.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotInfoVDSCommand.java new file mode 100644 index 0000000..d4567de --- /dev/null +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GetGlusterVolumeSnapshotInfoVDSCommand.java @@ -0,0 +1,30 @@ +package org.ovirt.engine.core.vdsbroker.gluster; + +import org.ovirt.engine.core.common.vdscommands.gluster.GlusterVolumeSnapshotVDSParameters; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.vdsbroker.vdsbroker.StatusForXmlRpc; + +public class GetGlusterVolumeSnapshotInfoVDSCommand<P extends GlusterVolumeSnapshotVDSParameters> extends AbstractGlusterBrokerCommand<P> { + private GlusterVolumeSnapshotInfoReturnForXmlRpc infoReturn; + + public GetGlusterVolumeSnapshotInfoVDSCommand(P parameters) { + super(parameters); + } + + @Override + protected StatusForXmlRpc getReturnStatus() { + return infoReturn.mStatus; + } + + @Override + protected void executeVdsBrokerCommand() { + Guid clusterId = getParameters().getClusterId(); + String volumeName = getParameters().getVolumeName(); + infoReturn = getBroker().glusterSnapshotInfo(clusterId, volumeName); + proceedProxyReturnValue(); + + if (getVDSReturnValue().getSucceeded()) { + setReturnValue(infoReturn.getSnapshots()); + } + } +} diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotConfigReturnForXmlRpc.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotConfigReturnForXmlRpc.java new file mode 100644 index 0000000..f632f77 --- /dev/null +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotConfigReturnForXmlRpc.java @@ -0,0 +1,67 @@ +package org.ovirt.engine.core.vdsbroker.gluster; + +import java.util.HashMap; +import java.util.Map; + +import org.ovirt.engine.core.common.businessentities.gluster.GlusterSnapshotConfigInfo; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.vdsbroker.irsbroker.StatusReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.vdsbroker.StatusForXmlRpc; + +public class GlusterVolumeSnapshotConfigReturnForXmlRpc extends StatusReturnForXmlRpc { + private static final String STATUS = "status"; + private static final String SNAPSHOT_CONFIG = "snapshotConfig"; + private static final String SYSTEM_CONFIG = "systemConfig"; + private static final String VOLUME_CONFIG = "volumeConfig"; + + private StatusForXmlRpc status; + private GlusterSnapshotConfigInfo glusterSnapshotConfigInfo = new GlusterSnapshotConfigInfo(); + + public GlusterSnapshotConfigInfo getGlusterSnapshotConfigInfo() { + return this.glusterSnapshotConfigInfo; + } + + public GlusterVolumeSnapshotConfigReturnForXmlRpc(Guid clusterId, Map<String, Object> innerMap) { + super(innerMap); + status = new StatusForXmlRpc((Map<String, Object>) innerMap.get(STATUS)); + + Map<String, Object> configInfo = (Map<String, Object>) innerMap.get(SNAPSHOT_CONFIG); + + Map<String, Object> systemConfig = (Map<String, Object>) configInfo.get(SYSTEM_CONFIG); + Map<String, String> clusterConfigs = new HashMap<String, String>(); + for (Map.Entry<String, Object> entry : systemConfig.entrySet()) { + String value = (String) entry.getValue(); + clusterConfigs.put(entry.getKey(), value == null ? "" : value); + } + glusterSnapshotConfigInfo.setClusterConfigOptions(clusterConfigs); + + glusterSnapshotConfigInfo.setVolumeConfigOptions(parseVolumeConfigDetails((Map<String, Object>) configInfo.get(VOLUME_CONFIG))); + } + + private Map<String, Map<String, String>> parseVolumeConfigDetails(Map<String, Object> configs) { + Map<String, Map<String, String>> volumeConfigs = + new HashMap<String, Map<String, String>>(); + + for (Map.Entry<String, Object> entry : configs.entrySet()) { + Map<String, Object> fetchedVolumeConfig = (Map<String, Object>) entry.getValue(); + Map<String, String> volConfig = new HashMap<String, String>(); + + for (Map.Entry<String, Object> config : fetchedVolumeConfig.entrySet()) { + String value = (String) config.getValue(); + volConfig.put(config.getKey(), value); + } + + volumeConfigs.put(entry.getKey(), volConfig); + } + + return volumeConfigs; + } + + public StatusForXmlRpc getStatus() { + return status; + } + + public void setStatus(StatusForXmlRpc status) { + this.status = status; + } +} diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotInfoReturnForXmlRpc.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotInfoReturnForXmlRpc.java new file mode 100644 index 0000000..ee6f3c9 --- /dev/null +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeSnapshotInfoReturnForXmlRpc.java @@ -0,0 +1,98 @@ +package org.ovirt.engine.core.vdsbroker.gluster; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.ovirt.engine.core.common.businessentities.gluster.GlusterSnapshotStatus; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeSnapshotEntity; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; +import org.ovirt.engine.core.vdsbroker.irsbroker.StatusReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.vdsbroker.StatusForXmlRpc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("unchecked") +public final class GlusterVolumeSnapshotInfoReturnForXmlRpc extends StatusReturnForXmlRpc { + private static final String STATUS = "status"; + private static final String SNAPSHOT_LIST = "snapshotList"; + private static final String SNAPSHOTS = "snapshots"; + private static final String NAME = "name"; + private static final String DESCRIPTION = "snapDescription"; + private static final String CREATETIME = "createTime"; + private static final String SNAPSHOT_UUID = "snapshotUuid"; + private static final String SNAPSHOT_NAME = "snapshotName"; + private static final String SNAP_VOLUME_STATUS = "snapVolumeStatus"; + + private StatusForXmlRpc status; + private static final Logger log = LoggerFactory.getLogger(GlusterVolumesListReturnForXmlRpc.class); + private List<GlusterVolumeSnapshotEntity> glusterVolumeSnapshots = new ArrayList<GlusterVolumeSnapshotEntity>(); + + public List<GlusterVolumeSnapshotEntity> getSnapshots() { + return glusterVolumeSnapshots; + } + + public GlusterVolumeSnapshotInfoReturnForXmlRpc(Guid clusterId, Map<String, Object> innerMap) { + super(innerMap); + status = new StatusForXmlRpc((Map<String, Object>) innerMap.get(STATUS)); + + Map<String, Object> snapshots = (Map<String, Object>) innerMap.get(SNAPSHOT_LIST); + + glusterVolumeSnapshots = prepareVolumeSnapshotsList(clusterId, snapshots); + } + + private List<GlusterVolumeSnapshotEntity> prepareVolumeSnapshotsList(Guid clusterId, Map<String, Object> snapshots) { + + List<GlusterVolumeSnapshotEntity> newSnapshotsList = new ArrayList<GlusterVolumeSnapshotEntity>(); + + for (Map.Entry<String, Object> entry : snapshots.entrySet()) { + String volumeName = entry.getKey(); + Map<String, Object> snapshotInfo = (Map<String, Object>) entry.getValue(); + + Object[] volumeSnapshots = (Object[]) (snapshotInfo.get(SNAPSHOTS)); + GlusterVolumeEntity volumeEntity = getGlusterVolumeDao().getByName(clusterId, volumeName); + + for (Object snapshot : volumeSnapshots) { + Map<String, Object> individualSnapshot = (Map<String, Object>) snapshot; + GlusterVolumeSnapshotEntity newSnapshot = new GlusterVolumeSnapshotEntity(); + newSnapshot.setClusterId(clusterId); + newSnapshot.setVolumeId(volumeEntity.getId()); + newSnapshot.setSnapshotId(Guid.createGuidFromString((String) individualSnapshot.get(SNAPSHOT_UUID))); + newSnapshot.setSnapshotName((String) individualSnapshot.get(SNAPSHOT_NAME)); + newSnapshot.setDescription((String) individualSnapshot.get(DESCRIPTION)); + newSnapshot.setStatus(GlusterSnapshotStatus.from((String) individualSnapshot.get(SNAP_VOLUME_STATUS))); + try { + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + newSnapshot.setCreatedAt(df.parse((String) individualSnapshot.get(CREATETIME))); + } catch (Exception e) { + log.info("Could not populate creation time for snapshot '{}' of volume '{}' on cluster '{}': {}", + (String) snapshotInfo.get(NAME), + volumeEntity.getName(), + clusterId, + e.getMessage()); + log.debug("Exception", e); + } + newSnapshotsList.add(newSnapshot); + } + } + + return newSnapshotsList; + } + + private GlusterVolumeDao getGlusterVolumeDao() { + return DbFacade.getInstance().getGlusterVolumeDao(); + } + + public StatusForXmlRpc getStatus() { + return status; + } + + public void setStatus(StatusForXmlRpc status) { + this.status = status; + } +} diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/jsonrpc/JsonRpcVdsServer.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/jsonrpc/JsonRpcVdsServer.java index 732cf7a..a7806e0 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/jsonrpc/JsonRpcVdsServer.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/jsonrpc/JsonRpcVdsServer.java @@ -20,6 +20,8 @@ import org.ovirt.engine.core.vdsbroker.gluster.GlusterTasksListReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeOptionsInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeProfileInfoReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotConfigReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeStatusReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeTaskReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumesListReturnForXmlRpc; @@ -1481,4 +1483,22 @@ new FutureMap(this.client, request); return new StatusOnlyReturnForXmlRpc(response); } + + @Override + public GlusterVolumeSnapshotInfoReturnForXmlRpc glusterSnapshotInfo(Guid clusterId, String volumeName) { + JsonRpcRequest request = + new RequestBuilder("GlusterVolume.snapshotList").withOptionalParameter("volumeName", volumeName) + .build(); + Map<String, Object> response = new FutureMap(this.client, request).withIgnoreResponseKey(); + return new GlusterVolumeSnapshotInfoReturnForXmlRpc(clusterId, response); + } + + @Override + public GlusterVolumeSnapshotConfigReturnForXmlRpc glusterVolumeSnapshotConfigGet(Guid clusterId, String volumeName) { + JsonRpcRequest request = + new RequestBuilder("GlusterSnapshot.configList").withOptionalParameter("volumeName", volumeName) + .build(); + Map<String, Object> response = new FutureMap(this.client, request).withIgnoreResponseKey(); + return new GlusterVolumeSnapshotConfigReturnForXmlRpc(clusterId, response); + } } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/IVdsServer.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/IVdsServer.java index 89f3404..b7421a8 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/IVdsServer.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/IVdsServer.java @@ -15,6 +15,8 @@ import org.ovirt.engine.core.vdsbroker.gluster.GlusterTasksListReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeOptionsInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeProfileInfoReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotConfigReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeStatusReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeTaskReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumesListReturnForXmlRpc; @@ -311,4 +313,8 @@ StatusOnlyReturnForXmlRpc setNumberOfCpus(String vmId, String numberOfCpus); StatusOnlyReturnForXmlRpc updateVmPolicy(Map info); + + GlusterVolumeSnapshotInfoReturnForXmlRpc glusterSnapshotInfo(Guid clusterId, String volumeName); + + GlusterVolumeSnapshotConfigReturnForXmlRpc glusterVolumeSnapshotConfigGet(Guid clusterId, String volumeName); } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerConnector.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerConnector.java index cc778ae..29b661e 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerConnector.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerConnector.java @@ -294,4 +294,8 @@ public Map<String, Object> setNumberOfCpus(String vmId, String numberOfCpus); public Map<String, Object> updateVmPolicy(Map info); + + public Map<String, Object> glusterSnapshotInfo(String snapshotName, String volumeName); + + public Map<String, Object> glusterSnapshotConfigGet(String volumeName); } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerWrapper.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerWrapper.java index 5f43faf..21b5035 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerWrapper.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsServerWrapper.java @@ -16,6 +16,8 @@ import org.ovirt.engine.core.vdsbroker.gluster.GlusterTasksListReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeOptionsInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeProfileInfoReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotConfigReturnForXmlRpc; +import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeSnapshotInfoReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeStatusReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumeTaskReturnForXmlRpc; import org.ovirt.engine.core.vdsbroker.gluster.GlusterVolumesListReturnForXmlRpc; @@ -1438,4 +1440,34 @@ StoragePoolInfoReturnForXmlRpc wrapper = new StoragePoolInfoReturnForXmlRpc(xmlRpcReturnValue); return wrapper; } + + public GlusterVolumeSnapshotInfoReturnForXmlRpc glusterSnapshotInfo(Guid clusterId, + String volumeName) { + try { + Map<String, Object> xmlRpcReturnValue = vdsServer.glusterSnapshotInfo("", volumeName); + GlusterVolumeSnapshotInfoReturnForXmlRpc wrapper = + new GlusterVolumeSnapshotInfoReturnForXmlRpc(clusterId, xmlRpcReturnValue); + return wrapper; + } catch (UndeclaredThrowableException ute) { + throw new XmlRpcRunTimeException(ute); + } + } + + @Override + public GlusterVolumeSnapshotConfigReturnForXmlRpc glusterVolumeSnapshotConfigGet(Guid clusterId, String volumeName) { + try { + Map<String, Object> xmlRpcReturnValue; + if (volumeName == null) { + xmlRpcReturnValue = vdsServer.glusterSnapshotConfigGet(""); + } else { + xmlRpcReturnValue = vdsServer.glusterSnapshotConfigGet(volumeName); + } + + GlusterVolumeSnapshotConfigReturnForXmlRpc wrapper = + new GlusterVolumeSnapshotConfigReturnForXmlRpc(clusterId, xmlRpcReturnValue); + return wrapper; + } catch (UndeclaredThrowableException ute) { + throw new XmlRpcRunTimeException(ute); + } + } } diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/VdsmErrors.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/VdsmErrors.java index b99a183..c2c3be3 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/VdsmErrors.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/VdsmErrors.java @@ -821,4 +821,10 @@ @DefaultStringValue("Cannot get list of images in ISO domain. " + "Please check that the storage domain status is Active") String GetIsoListError(); + + @DefaultStringValue("Error in executing gluster snapshot command") + String GlusterSnapshotException(); + + @DefaultStringValue("Gluster snapshot info failed") + String GlusterSnapshotInfoFailedException(); } diff --git a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/VdsmErrors.properties b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/VdsmErrors.properties index 9315b0b..2645761 100644 --- a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/VdsmErrors.properties +++ b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/VdsmErrors.properties @@ -381,3 +381,5 @@ GlusterHostIsNotPartOfCluster = Host is not part of the cluster GlusterVolumeRebalanceStatusFailedException=Failed to get gluster volume rebalance status GlusterVolumeRemoveBrickStatusFailed=Failed to get status of gluster volume remove bricks +GlusterSnapshotException=Error in executing gluster snapshot command +GlusterSnapshotInfoFailedException=Gluster snapshot info failed diff --git a/packaging/dbscripts/gluster_volume_snapshot_sp.sql b/packaging/dbscripts/gluster_volume_snapshot_sp.sql index 38ba5a2..969e37a 100644 --- a/packaging/dbscripts/gluster_volume_snapshot_sp.sql +++ b/packaging/dbscripts/gluster_volume_snapshot_sp.sql @@ -9,14 +9,15 @@ v_snapshot_name VARCHAR(1000), v_volume_id UUID, v_description VARCHAR(1024), - v_status VARCHAR(32)) + v_status VARCHAR(32), + v__create_date TIMESTAMP WITH TIME ZONE) RETURNS VOID AS $procedure$ BEGIN INSERT INTO gluster_volume_snapshots (snapshot_id, snapshot_name, volume_id, - description, status) + description, status, _create_date) VALUES (v_snapshot_id, v_snapshot_name, v_volume_id, - v_description, v_status); + v_description, v_status, v__create_date); PERFORM UpdateSnapshotCountInc(v_volume_id, 1); END; $procedure$ -- To view, visit https://gerrit.ovirt.org/39280 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I7b7bf79b72fc5680dab301b290e7aa860d5c714d Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: ovirt-engine-3.5-gluster Gerrit-Owner: Shubhendu Tripathi <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
