Sahina Bose has uploaded a new change for review. Change subject: engine: Geo-rep monitoring ......................................................................
engine: Geo-rep monitoring Monitoring details of geo rep sessions - detects new sessions and updates the database Consolidates the session status based on individual node status Removes sessions from database if deleted from CLI Corrected errors with wrong session id while retrieving from VDS and status while saving to DB. Change-Id: I3304addda17cf49eee93f311e043b0e15212cd91 Bug-Url: https://bugzilla.redhat.com/1138116 Signed-off-by: Sahina Bose <[email protected]> --- A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJob.java 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/test/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJobTest.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/gluster/GlusterFeatureSupported.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDao.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoDbFacadeImpl.java M backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties M backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoTest.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeGeoRepStatusForXmlRpc.java M packaging/dbscripts/gluster_georep_sp.sql A packaging/dbscripts/upgrade/03_05_1270_add_idx_gluster_georep_slavevolname.sql M packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql 15 files changed, 563 insertions(+), 8 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/74/39574/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJob.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJob.java new file mode 100644 index 0000000..362fb3f --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJob.java @@ -0,0 +1,233 @@ +package org.ovirt.engine.core.bll.gluster; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.ArrayUtils; +import org.ovirt.engine.core.common.AuditLogType; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +import org.ovirt.engine.core.common.businessentities.gluster.GeoRepSessionStatus; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterGeoRepSession; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterGeoRepSessionDetails; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; +import org.ovirt.engine.core.common.constants.gluster.GlusterConstants; +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.GlusterVolumeGeoRepSessionVDSParameters; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GlusterGeoRepSyncJob extends GlusterJob { + private static final Logger log = LoggerFactory.getLogger(GlusterGeoRepSyncJob.class); + + private static final GlusterGeoRepSyncJob instance = new GlusterGeoRepSyncJob(); + private static final GeoRepSessionStatus[] overridableStatuses = { GeoRepSessionStatus.ACTIVE, + GeoRepSessionStatus.INITIALIZING, + GeoRepSessionStatus.NOTSTARTED + }; + + public void init() { + log.info("Gluster georeplication monitoring has been initialized"); + } + + public static GlusterGeoRepSyncJob getInstance() { + return instance; + } + + @OnTimerMethodAnnotation("gluster_georep_poll_event") + public void refreshGeoRepData() { + // get all clusters + List<VDSGroup> clusters = getClusterDao().getAll(); + // for every cluster that supports geo-rep monitoring + for (VDSGroup cluster : clusters) { + refreshGeoRepDataInCluster(cluster); + } + + } + + public void refreshGeoRepDataInCluster(VDSGroup cluster) { + if (!supportsGlusterGeoRepFeature(cluster)) { + return; + } + + Map<String, GlusterGeoRepSession> sessionsMap = getSessionsForCluster(cluster); + if (sessionsMap == null) { + log.debug("Error in retrieving sessions for cluster '{}' from CLI, nothing to do", cluster.getName()); + return; + } + + removeDeletedSessions(cluster.getId(), sessionsMap); + + // for each geo-rep session, find session in database and update details. + for (GlusterGeoRepSession session : sessionsMap.values()) { + GlusterVolumeEntity masterVolume = getVolume(cluster, session.getMasterVolumeName()); + if (masterVolume == null) { + log.info("Could not find corresponding volume for geo-rep session '{}' and volume '{}' - status will not be updated.", + session.getSessionKey(), + session.getMasterVolumeName()); + } else { + session.setMasterVolumeId(masterVolume.getId()); + // update consolidated status + updateGeoRepStatus(masterVolume, session); + } + + // check if session exists in database + GlusterGeoRepSession sessionInDb = getGeoRepDao().getGeoRepSession(session.getSessionKey()); + if (sessionInDb == null) { + // save the session in database first. + log.debug("detected new geo-rep session '{}' for volume '{}'", + session.getSessionKey(), + session.getMasterVolumeName()); + if (Guid.isNullOrEmpty(session.getId())) { + session.setId(Guid.newGuid()); + } + getGeoRepDao().save(session); + logGeoRepMessage(AuditLogType.GLUSTER_GEOREP_SESSION_DETECTED_FROM_CLI, cluster.getId(), session); + } else { + session.setId(sessionInDb.getId()); + getGeoRepDao().updateSession(session); + } + //update the session details object with session id. + for (GlusterGeoRepSessionDetails sessDetails : session.getSessionDetails()) { + sessDetails.setSessionId(session.getId()); + } + getGeoRepDao().saveOrUpdateDetailsInBatch(session.getSessionDetails()); + } + + } + + private void removeDeletedSessions(Guid clusterId, final Map<String, GlusterGeoRepSession> sessionsMap) { + List<GlusterGeoRepSession> sessionsInDb = getGeoRepDao().getGeoRepSessionsInCluster(clusterId); + if (sessionsInDb == null || sessionsInDb.isEmpty()) { + return; + } + List<GlusterGeoRepSession> sessionsToDelete = new ArrayList<>(); + for (GlusterGeoRepSession grepSession: sessionsInDb) { + if (sessionsMap.get(grepSession.getSessionKey()) == null) { + sessionsToDelete.add(grepSession); + } + } + + for (final GlusterGeoRepSession session : sessionsToDelete) { + log.debug("geo-rep session '{}' detected removed for volume '{}'", + session.getSessionKey(), + session.getMasterVolumeName()); + getGeoRepDao().remove(session.getId()); + logGeoRepMessage(AuditLogType.GLUSTER_GEOREP_SESSION_DELETED_FROM_CLI, clusterId, session); + } + } + + private void logGeoRepMessage(AuditLogType logType, Guid clusterId, final GlusterGeoRepSession session) { + logUtil.logAuditMessage(clusterId, null, null, + logType, + new HashMap<String, String>() { + { + put(GlusterConstants.VOLUME_NAME, session.getMasterVolumeName()); + put("geoRepSessionKey", session.getSessionKey()); + } + }); + } + + /** + * This method updates the status depending on health of individual nodes + * + * @param volume + * @param session + */ + private void updateGeoRepStatus(GlusterVolumeEntity volume, GlusterGeoRepSession session) { + + List<HashSet<GeoRepSessionStatus>> list = new ArrayList<>(); + // grouped node status + int replicaCount = volume.getReplicaCount() == 0 ? 1 : volume.getReplicaCount(); + for (int i = 0; i < volume.getBricks().size(); i = i + replicaCount) { + HashSet<GeoRepSessionStatus> subVolumeStatusSet = new HashSet<>(); + int j = 0; + while (j < replicaCount) { + Guid brickId = volume.getBricks().get(i + j).getId(); + subVolumeStatusSet.add(getStatusForBrickFromSession(session, brickId)); + j++; + } + list.add(subVolumeStatusSet); + } + + session.setStatus(GeoRepSessionStatus.ACTIVE); + // iterate through grouped status to set consolidated status + for (HashSet<GeoRepSessionStatus> subVolumeStatusValues : list) { + if (subVolumeStatusValues.contains(GeoRepSessionStatus.ACTIVE)) { + // healthy + continue; + } else if (subVolumeStatusValues.contains(GeoRepSessionStatus.FAULTY)) { + session.setStatus(GeoRepSessionStatus.FAULTY); + // if any one of the sub-volume is faulty, the overall session status if faulty + return; + } + // override status in case of these values + if (ArrayUtils.contains(overridableStatuses, session.getStatus())) { + if (subVolumeStatusValues.size() == 1) { + session.setStatus((GeoRepSessionStatus) subVolumeStatusValues.toArray()[0]); + } else { + // if status values in sub-volume are not the same, what do we do? + // this should not happen, so we'll log it for now + log.info("Multiple status values found in volume '{}'", session.getMasterVolumeName()); + } + } + } + + } + + private GeoRepSessionStatus getStatusForBrickFromSession(GlusterGeoRepSession session, Guid masterBrickId) { + if (session.getSessionDetails() == null) { + return null; + } + for (GlusterGeoRepSessionDetails sessionDetail : session.getSessionDetails()) { + if (sessionDetail.getMasterBrickId().equals(masterBrickId)) { + return sessionDetail.getStatus(); + } + } + return GeoRepSessionStatus.UNKNOWN; + } + + private Map<String, GlusterGeoRepSession> getSessionsForCluster(VDSGroup cluster) { + VDS upServer = getClusterUtils().getRandomUpServer(cluster.getId()); + if (upServer == null) { + log.debug("No UP server found in cluster '{}' for geo-rep monitoring", cluster.getName()); + return null; + } + // get details of geo-rep sessions in cluster + VDSReturnValue returnValue = runVdsCommand(VDSCommandType.GetGlusterVolumeGeoRepStatus, + new GlusterVolumeGeoRepSessionVDSParameters(upServer.getId(), null)); + if (returnValue.getSucceeded()) { + List<GlusterGeoRepSession> sessions = (List<GlusterGeoRepSession>) returnValue.getReturnValue(); + HashMap<String, GlusterGeoRepSession> sessionsMap = new HashMap<>(); + if (sessions == null) { + return sessionsMap; + } + for (GlusterGeoRepSession session : sessions) { + sessionsMap.put(session.getSessionKey(), session); + } + return sessionsMap; + } else { + log.error("VDS error {}", returnValue.getVdsError().getMessage()); + log.debug("VDS error", returnValue.getVdsError()); + return null; + } + + } + + private GlusterVolumeEntity getVolume(VDSGroup cluster, String masterVolumeName) { + return getVolumeDao().getByName(cluster.getId(), masterVolumeName); + } + + private boolean supportsGlusterGeoRepFeature(VDSGroup cluster) { + return cluster.supportsGlusterService() + && GlusterFeatureSupported.glusterGeoReplication(cluster.getcompatibility_version()); + } + +} 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..57beb7b 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 @@ -26,6 +26,7 @@ import org.ovirt.engine.core.dao.VdsStatisticsDAO; import org.ovirt.engine.core.dao.gluster.GlusterBrickDao; import org.ovirt.engine.core.dao.gluster.GlusterClusterServiceDao; +import org.ovirt.engine.core.dao.gluster.GlusterGeoRepDao; import org.ovirt.engine.core.dao.gluster.GlusterHooksDao; import org.ovirt.engine.core.dao.gluster.GlusterOptionDao; import org.ovirt.engine.core.dao.gluster.GlusterServerDao; @@ -129,6 +130,10 @@ return DbFacade.getInstance().getStepDao(); } + protected GlusterGeoRepDao getGeoRepDao() { + return DbFacade.getInstance().getGlusterGeoRepDao(); + } + /** * Acquires a lock on the cluster with given id and locking group {@link LockingGroup#GLUSTER} * 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..96e19aa 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(GlusterGeoRepSyncJob.getInstance(), + "gluster_georep_poll_event", + new Class[0] , + new Class [0], + getRefreshRate(ConfigValues.GlusterRefreshRateGeoRepDiscovery), + getRefreshRate(ConfigValues.GlusterRefreshRateGeoRepDiscovery), + TimeUnit.SECONDS); + } private static boolean glusterModeSupported() { diff --git a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJobTest.java b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJobTest.java new file mode 100644 index 0000000..3f4de6d --- /dev/null +++ b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJobTest.java @@ -0,0 +1,185 @@ +package org.ovirt.engine.core.bll.gluster; + +import static org.mockito.Matchers.any; +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.List; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +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.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +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.GlusterGeoRepSessionDetails; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterStatus; +import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; +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.GlusterVolumeGeoRepSessionVDSParameters; +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.GlusterGeoRepDao; +import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao; +import org.ovirt.engine.core.utils.MockConfigRule; + +@RunWith(MockitoJUnitRunner.class) +public class GlusterGeoRepSyncJobTest { + + @Mock + private GlusterGeoRepDao geoRepDao; + + @Mock + private ClusterUtils clusterUtils; + + @Mock + private VdsGroupDAO clusterDao; + + @Mock + private GlusterVolumeDao volumeDao; + + private GlusterGeoRepSyncJob syncJob; + + @Mock + private GlusterAuditLogUtil logUtil; + + @ClassRule + public static MockConfigRule mcr = new MockConfigRule( + mockConfig(ConfigValues.GlusterGeoReplicationEnabled, Version.v3_5.toString(), true), + mockConfig(ConfigValues.GlusterGeoReplicationEnabled, Version.v3_4.toString(), false) + ); + + @Before + public void init() { + syncJob = Mockito.spy(GlusterGeoRepSyncJob.getInstance()); + MockitoAnnotations.initMocks(this); + doReturn(clusterDao).when(syncJob).getClusterDao(); + doReturn(geoRepDao).when(syncJob).getGeoRepDao(); + doReturn(volumeDao).when(syncJob).getVolumeDao(); + doReturn(clusterUtils).when(syncJob).getClusterUtils(); + syncJob.setLogUtil(logUtil); + doReturn(getClusters()).when(clusterDao).getAll(); + doReturn(getVolume()).when(volumeDao).getByName(any(Guid.class), any(String.class)); + doReturn(getServer()).when(clusterUtils).getRandomUpServer(any(Guid.class)); + } + + @Test + public void testSync() { + + doReturn(getSessionsVDSReturnVal(true, 2)).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeGeoRepStatus), + any(GlusterVolumeGeoRepSessionVDSParameters.class)); + syncJob.refreshGeoRepData(); + Mockito.verify(geoRepDao, times(2)).save(any(GlusterGeoRepSession.class)); + } + + @Test + public void testSyncWhenNoSessions() { + + doReturn(getSessionsVDSReturnVal(true, 0)).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeGeoRepStatus), + any(GlusterVolumeGeoRepSessionVDSParameters.class)); + syncJob.refreshGeoRepData(); + Mockito.verify(geoRepDao, times(0)).save(any(GlusterGeoRepSession.class)); + } + + private Object getSessionsVDSReturnVal(boolean ret, int count) { + VDSReturnValue vdsRetValue = new VDSReturnValue(); + vdsRetValue.setSucceeded(ret); + if (ret) { + vdsRetValue.setReturnValue(getSessions(count)); + } else { + vdsRetValue.setReturnValue(null); + } + return vdsRetValue; + } + + private List<GlusterGeoRepSession> getSessions(int count) { + List<GlusterGeoRepSession> sessions = new ArrayList<GlusterGeoRepSession>(); + for (int i = 0; i < count; i++) { + sessions.add(getSession()); + } + return sessions; + } + + private GlusterGeoRepSession getSession() { + GlusterGeoRepSession session = new GlusterGeoRepSession(); + session.setMasterVolumeName("VOL1"); + session.setId(Guid.newGuid()); + session.setSessionKey(session.getId() + session.getMasterVolumeName()); + session.setStatus(GeoRepSessionStatus.ACTIVE); + session.setSessionDetails(getSessionDetailsList()); + return session; + } + + private ArrayList<GlusterGeoRepSessionDetails> getSessionDetailsList() { + ArrayList<GlusterGeoRepSessionDetails> list = new ArrayList<>(); + GlusterGeoRepSessionDetails details = new GlusterGeoRepSessionDetails(); + details.setMasterBrickId(Guid.newGuid()); + list.add(details); + return list; + } + + private List<VDSGroup> getClusters() { + List<VDSGroup> list = new ArrayList<>(); + list.add(createCluster(Version.v3_4)); + list.add(createCluster(Version.v3_5)); + return list; + } + + private VDSGroup createCluster(Version v) { + VDSGroup cluster = new VDSGroup(); + cluster.setId(Guid.newGuid()); + cluster.setName("cluster"); + cluster.setGlusterService(true); + cluster.setVirtService(false); + cluster.setcompatibility_version(v); + return cluster; + } + + private VDS getServer() { + VDS vds = new VDS(); + vds.setId(Guid.newGuid()); + return vds; + } + + private GlusterVolumeEntity getVolume() { + GlusterVolumeEntity volume = new GlusterVolumeEntity(); + volume.setName("VOL1"); + volume.setClusterId(Guid.newGuid()); + volume.setId(Guid.newGuid()); + 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/AuditLogType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java index a2254a1..6043d94 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java @@ -382,6 +382,8 @@ GLUSTER_VOLUME_DETAILS_REFRESH(4093), GLUSTER_VOLUME_DETAILS_REFRESH_FAILED(4094, AuditLogSeverity.ERROR), USER_FORCE_SELECTED_SPM_STOP_FAILED(4096, AuditLogSeverity.ERROR), + GLUSTER_GEOREP_SESSION_DELETED_FROM_CLI(4097, AuditLogSeverity.WARNING), + GLUSTER_GEOREP_SESSION_DETECTED_FROM_CLI(4098, AuditLogSeverity.WARNING), USER_FORCE_SELECTED_SPM(159), USER_VDS_RESTART(41), USER_FAILED_VDS_RESTART(107, AuditLogSeverity.ERROR), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java index 0b73082..4c97df3 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java @@ -1457,6 +1457,14 @@ @DefaultValueAttribute("300") GlusterRefreshRateSnapshotDiscovery, + @TypeConverterAttribute(Boolean.class) + @DefaultValueAttribute("true") + GlusterGeoReplicationEnabled, + + @TypeConverterAttribute(Integer.class) + @DefaultValueAttribute("3600") + GlusterRefreshRateGeoRepDiscovery, + @TypeConverterAttribute(String.class) @DefaultValueAttribute("AttestationService/resources/PollHosts") PollUri, 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..2b2a529 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,15 @@ return false; } } + + /** + * + * @param version + * Compatibility version to check for. + * @return <code>true</code> if gluster geo-replication management feature is enabled, + * <code>false</code> if it's not. + */ + public static boolean glusterGeoReplication(Version version) { + return supportedInConfig(ConfigValues.GlusterGeoReplicationEnabled, version); + } } diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDao.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDao.java index 1e8f470..d1664f0 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDao.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDao.java @@ -17,6 +17,8 @@ public void saveDetails(GlusterGeoRepSessionDetails geoRepSessionDetails); + public void saveDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsList); + public void saveConfig(GlusterGeoRepSessionConfiguration geoRepSessionConfig); /** @@ -30,16 +32,24 @@ public List<GlusterGeoRepSession> getGeoRepSessions(Guid masterVolumeId); + public List<GlusterGeoRepSession> getGeoRepSessionsInCluster(Guid clusterId); + public void remove(Guid id); public void updateSession(GlusterGeoRepSession geoRepSession); public void updateDetails(GlusterGeoRepSessionDetails geoRepSessionDetails); + public void updateDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsObjs); + + public void saveOrUpdateDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsObjs); + public void updateConfig(GlusterGeoRepSessionConfiguration geoRepSessionConfig); public List<GlusterGeoRepSessionDetails> getGeoRepSessionDetails(Guid sessionId); + public GlusterGeoRepSessionDetails getGeoRepSessionDetails(Guid sessionId, Guid masterBrickId); + public List<GlusterGeoRepSessionConfiguration> getGeoRepSessionConfig(Guid sessionId); } diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoDbFacadeImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoDbFacadeImpl.java index 6bb6771..864c1b5 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoDbFacadeImpl.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoDbFacadeImpl.java @@ -2,6 +2,7 @@ import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import org.ovirt.engine.core.common.businessentities.gluster.GeoRepCrawlStatus; @@ -119,9 +120,21 @@ } @Override + public List<GlusterGeoRepSession> getGeoRepSessionsInCluster(Guid clusterId) { + return getCallsHandler().executeReadList("GetGlusterGeoRepSessionsByClusterId", georepSessionRowMapper, + getCustomMapSqlParameterSource().addValue("cluster_id", clusterId)); + } + + @Override public List<GlusterGeoRepSessionDetails> getGeoRepSessionDetails(Guid sessionId) { return getCallsHandler().executeReadList("GetGlusterGeoRepSessionDetails", georepSessionDetailsRowMapper, createIdParameterMapper(sessionId)); + } + + @Override + public GlusterGeoRepSessionDetails getGeoRepSessionDetails(Guid sessionId, Guid masterBrickId) { + return getCallsHandler().executeRead("GetGlusterGeoRepSessionDetailsForBrick", georepSessionDetailsRowMapper, + createIdParameterMapper(sessionId).addValue("master_brick_id", masterBrickId)); } @Override @@ -178,7 +191,7 @@ public void updateSession(GlusterGeoRepSession geoRepSession) { getCallsHandler().executeModification("UpdateGlusterGeoRepSessionStatus", createIdParameterMapper(geoRepSession.getId()) - .addValue("status", geoRepSession.getStatus())); + .addValue("status", EnumUtils.nameOrNull(geoRepSession.getStatus()))); } @Override @@ -196,6 +209,42 @@ } @Override + public void saveOrUpdateDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsObjs) { + List<GlusterGeoRepSessionDetails> insertList = new ArrayList<>(); + List<GlusterGeoRepSessionDetails> updateList = new ArrayList<>(); + for (GlusterGeoRepSessionDetails details : geoRepSessionDetailsObjs) { + if (getGeoRepSessionDetails(details.getSessionId(), details.getMasterBrickId()) == null) { + insertList.add(details); + } else { + updateList.add(details); + } + } + saveDetailsInBatch(insertList); + updateDetailsInBatch(updateList); + } + + @Override + public void saveDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsList) { + getCallsHandler().executeStoredProcAsBatch("InsertGlusterGeoRepSessionDetail", + geoRepSessionDetailsList, getDetailsBatchMapper()); + } + + @Override + public void updateDetailsInBatch(List<GlusterGeoRepSessionDetails> geoRepSessionDetailsObjs) { + getCallsHandler().executeStoredProcAsBatch("UpdateGlusterGeoRepSessionDetail", + geoRepSessionDetailsObjs, getDetailsBatchMapper()); + } + + public MapSqlParameterMapper<GlusterGeoRepSessionDetails> getDetailsBatchMapper() { + return new MapSqlParameterMapper<GlusterGeoRepSessionDetails>() { + @Override + public MapSqlParameterSource map(GlusterGeoRepSessionDetails entity) { + return createFullParametersMapper(entity); + } + }; + } + + @Override public MapSqlParameterMapper<GlusterGeoRepSession> getBatchMapper() { return new MapSqlParameterMapper<GlusterGeoRepSession>() { @Override diff --git a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties index 1304f5a..d506a6d 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties @@ -804,6 +804,8 @@ GLUSTER_SERVICE_RESTART_FAILED=Could not re-start ${servicetype} service on host ${VdsName} on cluster ${VdsGroupName}. GLUSTER_VOLUME_BRICK_ADDED=Brick [${brickpath}] on host [${servername}] added to volume [${glusterVolumeName}] GLUSTER_BRICK_STATUS_CHANGED=Detected change in status of brick ${brickpath} of volume ${glusterVolumeName} from ${oldValue} to ${newValue}. +GLUSTER_GEOREP_SESSION_DELETED_FROM_CLI=Detected deletion of geo-replication session ${geoRepSessionKey} from volume ${glusterVolumeName} +GLUSTER_GEOREP_SESSION_DETECTED_FROM_CLI=Detected new geo-replication session ${geoRepSessionKey} for volume ${glusterVolumeName}. Adding it to engine. VDS_UNTRUSTED=Host ${VdsName} was set to non-operational. Host is not trusted by the attestation service. USER_ADDED_NETWORK_QOS=Network QoS ${QosName} was added. (User: ${UserName}) USER_FAILED_TO_ADD_NETWORK_QOS=Failed to add Network QoS ${QosName}. (User: ${UserName}) diff --git a/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoTest.java b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoTest.java index b93c72e..dab2f92 100644 --- a/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoTest.java +++ b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/gluster/GlusterGeoRepDaoTest.java @@ -136,4 +136,9 @@ assertEquals("NEW_VAL", fetchedSessionConfig.get(0).getValue()); } + public void testGetAllInCluster() { + List<GlusterGeoRepSession> fetchedSessions = dao.getGeoRepSessionsInCluster(FixturesTool.GLUSTER_CLUSTER_ID); + assertEquals(1, fetchedSessions.size()); + } + } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeGeoRepStatusForXmlRpc.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeGeoRepStatusForXmlRpc.java index 30d0e5f..5bc5a70 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeGeoRepStatusForXmlRpc.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/gluster/GlusterVolumeGeoRepStatusForXmlRpc.java @@ -35,7 +35,6 @@ protected GlusterGeoRepSessionDetails getSessionDetails(Map<String, Object> innerMap, GlusterGeoRepSession session) { GlusterGeoRepSessionDetails details = new GlusterGeoRepSessionDetails(); - details.setSessionId(session.getId()); Guid masterNodeGlusterId; if (innerMap.containsKey(MASTER_NODE_UUID)) { masterNodeGlusterId = new Guid(innerMap.get(MASTER_NODE_UUID).toString()); @@ -71,14 +70,12 @@ protected GlusterGeoRepSession getSession(String masterVolumeName, Map<String, Object> innerMap) { GlusterGeoRepSession geoRepSession = new GlusterGeoRepSession(); - // sessionslave in the form - + // sessionslave in the form - the uuid is the gluster server uuid on master // <session_slave>11ae7a03-e793-4270-8fc4-b42def8b3051:ssh://192.168.122.14::slave2</session_slave> String sessionKey = (String) innerMap.get(SESSION_SLAVE); String sessSplit[] = sessionKey.split("([://]+)"); - String sessionId = sessSplit[0]; String slaveNode = sessSplit[sessSplit.length - 2]; String slaveVolume = sessSplit[sessSplit.length - 1]; - geoRepSession.setId(Guid.createGuidFromString(sessionId)); geoRepSession.setSlaveHostName(slaveNode); geoRepSession.setSlaveVolumeName(slaveVolume); geoRepSession.setSessionKey(sessionKey); diff --git a/packaging/dbscripts/gluster_georep_sp.sql b/packaging/dbscripts/gluster_georep_sp.sql index b365231..61ae9dd 100644 --- a/packaging/dbscripts/gluster_georep_sp.sql +++ b/packaging/dbscripts/gluster_georep_sp.sql @@ -84,7 +84,8 @@ files_pending = v_files_pending, bytes_pending = v_bytes_pending, deletes_pending = v_deletes_pending, - files_skipped = v_files_skipped + files_skipped = v_files_skipped, + _update_date = LOCALTIMESTAMP WHERE session_id = v_session_id AND master_brick_id = v_master_brick_id; END; $procedure$ LANGUAGE plpgsql; @@ -96,7 +97,8 @@ AS $procedure$ BEGIN UPDATE gluster_georep_config - SET config_value = v_config_value + SET config_value = v_config_value, + _update_date = LOCALTIMESTAMP WHERE session_id = v_session_id AND config_key = v_config_key; END; $procedure$ LANGUAGE plpgsql; @@ -127,6 +129,18 @@ END; $procedure$ LANGUAGE plpgsql; +Create or replace FUNCTION GetGlusterGeoRepSessionsByClusterId(v_cluster_id UUID) +RETURNS SETOF gluster_georep_session STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT session_id, master_volume_id, session_key, slave_host_uuid, + slave_host_name, slave_volume_id, slave_volume_name, georep.status, + georep._create_date, georep._update_date + FROM gluster_georep_session georep JOIN gluster_volumes ON master_volume_id = id + WHERE cluster_id = v_cluster_id order by slave_volume_name asc; +END; $procedure$ +LANGUAGE plpgsql; + Create or replace FUNCTION GetGlusterGeoRepSessionByKey(v_session_key VARCHAR(150)) RETURNS SETOF gluster_georep_session STABLE AS $procedure$ @@ -145,7 +159,8 @@ AS $procedure$ BEGIN UPDATE gluster_georep_session - SET status = v_status + SET status = v_status, + _update_date = LOCALTIMESTAMP WHERE session_id = v_session_id; END; $procedure$ LANGUAGE plpgsql; @@ -162,6 +177,20 @@ END; $procedure$ LANGUAGE plpgsql; +Create or replace FUNCTION GetGlusterGeoRepSessionDetailsForBrick(v_session_id UUID, + v_master_brick_id UUID) +RETURNS SETOF gluster_georep_session_details STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT session_id, master_brick_id, slave_host_uuid, + slave_host_name, status, checkpoint_status, crawl_status, files_synced, files_pending, + bytes_pending, deletes_pending, files_skipped, _update_date + FROM gluster_georep_session_details + WHERE session_id = v_session_id AND master_brick_id = v_master_brick_id; +END; $procedure$ +LANGUAGE plpgsql; + + Create or replace FUNCTION GetGlusterGeoRepSessionConfig(v_session_id UUID) RETURNS SETOF gluster_georep_config STABLE AS $procedure$ diff --git a/packaging/dbscripts/upgrade/03_05_1270_add_idx_gluster_georep_slavevolname.sql b/packaging/dbscripts/upgrade/03_05_1270_add_idx_gluster_georep_slavevolname.sql new file mode 100644 index 0000000..6795496 --- /dev/null +++ b/packaging/dbscripts/upgrade/03_05_1270_add_idx_gluster_georep_slavevolname.sql @@ -0,0 +1,2 @@ +DROP INDEX if exists IDX_georep_slave_volume_name; +CREATE INDEX IDX_georep_slave_volume_name ON gluster_georep_session(slave_volume_name); diff --git a/packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql b/packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql index 8ffdc89..3150905 100644 --- a/packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql +++ b/packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql @@ -191,6 +191,15 @@ select fn_db_add_config_value('GlusterVolumeSnapshotSupported', 'false', '3.4'); select fn_db_add_config_value('GlusterVolumeSnapshotSupported', 'false', '3.5'); select fn_db_add_config_value('GlusterRefreshRateSnapshotDiscovery', '300', 'general'); +-- Gluster Geo-replication -- +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.0'); +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.1'); +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.2'); +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.3'); +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.4'); +select fn_db_add_config_value('GlusterGeoReplicationEnabled', 'false', '3.5'); +select fn_db_add_config_value('GlusterRefreshRateGeoRepStatus', '300', 'general'); +select fn_db_add_config_value('GlusterRefreshRateGeoRepDiscovery', '3600', 'general'); -- OpenStack related select fn_db_add_config_value('KeystoneAuthUrl', '', 'general'); -- To view, visit https://gerrit.ovirt.org/39574 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I3304addda17cf49eee93f311e043b0e15212cd91 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: ovirt-engine-3.5-gluster Gerrit-Owner: Sahina Bose <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
