package org.ovirt.engine.core.bll.gluster;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.VDS;
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.config.ConfigValues;
import org.ovirt.engine.core.common.constants.gluster.GlusterConstants;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
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.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;
@Singleton
public class GlusterSnapshotSyncJob extends GlusterJob {
private static final Logger log = LoggerFactory.getLogger(GlusterSnapshotSyncJob.class);
@Inject
private GlusterVolumeSnapshotDao volumeSnapshotDao;
@Inject
private GlusterVolumeSnapshotConfigDao volumeSnapshotConfigDao;
@Override
public Collection<GlusterJobSchedulingDetails> getSchedulingDetails() {
return Collections.singleton(new GlusterJobSchedulingDetails(
"gluster_snapshot_poll_event", getRefreshRate(ConfigValues.GlusterRefreshRateSnapshotDiscovery)));
}
@OnTimerMethodAnnotation("gluster_snapshot_poll_event")
public void refreshSnapshotData() {
refreshSnapshotList();
refreshSnapshotConfig();
}
public void refreshSnapshotList() {
// get all clusters
List<Cluster> clusters = clusterDao.getAll();
for (Cluster cluster : clusters) {
refreshSnapshotsInCluster(cluster);
}
}
public void refreshSnapshotConfig() {
// get all clusters
List<Cluster> clusters = clusterDao.getAll();
for (Cluster cluster : clusters) {
refreshSnapshotConfigInCluster(cluster);
}
}
private void refreshSnapshotsInCluster(Cluster cluster) {
if (!supportsGlusterSnapshotFeature(cluster)) {
return;
}
final VDS upServer = glusterUtil.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());
// check if the snapshot soft limit reached for a volume and alert
List<GlusterVolumeEntity> volumes = volumeDao.getByClusterId(cluster.getId());
for (final GlusterVolumeEntity volume : volumes) {
// check if the snapshot soft limit reached for the volume and alert
glusterUtil.alertVolumeSnapshotLimitsReached(volume);
// Check and remove soft limit alert for the volume.
// It might have fallen below the soft limit as part of deletions of snapshots
glusterUtil.checkAndRemoveVolumeSnapshotLimitsAlert(volume);
}
} else {
log.error("VDS Error {}", returnValue.getVdsError().getMessage());
log.debug("VDS Error {}", returnValue.getVdsError());
}
}
public void refreshSnapshotConfigInCluster(Cluster cluster) {
if (!supportsGlusterSnapshotFeature(cluster)) {
return;
}
final VDS upServer = glusterUtil.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);
}
Cluster cluster = clusterDao.get(clusterId);
List<GlusterVolumeSnapshotEntity> existingSnapshots = volumeSnapshotDao.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 (final GlusterVolumeSnapshotEntity fetchedSnapshot : fetchedSnapshots) {
GlusterVolumeSnapshotEntity correspondingExistingSnapshot =
existingSnapshotsMap.get(fetchedSnapshot.getId());
if (correspondingExistingSnapshot == null) {
final GlusterVolumeEntity volume = volumeDao.getById(fetchedSnapshot.getVolumeId());
newlyAddedSnapshots.add(fetchedSnapshot);
log.debug("Detected new gluster volume snapshot '{}' for volume '{}' on cluster: '{}'",
fetchedSnapshot.getSnapshotName(),
volume.getName(),
cluster.getName());
Map<String, String> customValues = new HashMap<>();
customValues.put("snapName", fetchedSnapshot.getSnapshotName());
customValues.put(GlusterConstants.VOLUME_NAME, volume.getName());
logUtil.logAuditMessage(clusterId,
volume,
null,
AuditLogType.GLUSTER_VOLUME_SNAPSHOT_DETECTED_NEW,
customValues);
} else if (correspondingExistingSnapshot.getStatus() != fetchedSnapshot.getStatus()) {
correspondingExistingSnapshot.setStatus(fetchedSnapshot.getStatus());
updatedSnapshots.add(correspondingExistingSnapshot);
}
}
for (final GlusterVolumeSnapshotEntity existingSnapshot : existingSnapshots) {
GlusterVolumeSnapshotEntity correspondingFetchedSnapshot =
fetchedSnapshotsMap.get(existingSnapshot.getId());
if (correspondingFetchedSnapshot == null) {
final GlusterVolumeEntity volume = volumeDao.getById(existingSnapshot.getVolumeId());
deletedSnapshots.add(existingSnapshot);
log.debug("Gluster volume snapshot '{}' detected removed for volume '{}' on cluster: '{}'",
existingSnapshot.getSnapshotName(),
volume.getName(),
cluster.getName());
Map<String, String> customValues = new HashMap<>();
customValues.put("snapName", existingSnapshot.getSnapshotName());
customValues.put(GlusterConstants.VOLUME_NAME, volume.getName());
logUtil.logAuditMessage(clusterId,
volume,
null,
AuditLogType.GLUSTER_VOLUME_SNAPSHOT_DELETED_FROM_CLI,
customValues);
}
}
// 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 EngineException(EngineError.GlusterSnapshotInfoFailedException, e.getLocalizedMessage());
}
}
private void addOrUpdateSnapshotsConfig(Guid clusterId, GlusterSnapshotConfigInfo configInfo) {
Cluster cluster = clusterDao.get(clusterId);
try (EngineLock lock = acquireVolumeSnapshotLock(clusterId)) {
for (Map.Entry<String, String> entry : configInfo.getClusterConfigOptions().entrySet()) {
if (entry.getValue() != null) {
addOrUpdateClusterConfig(cluster, 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 EngineException(EngineError.GlusterSnapshotInfoFailedException, e.getLocalizedMessage());
}
Map<String, Map<String, String>> volumeConfigs = configInfo.getVolumeConfigOptions();
for (Map.Entry<String, Map<String, String>> entry : volumeConfigs.entrySet()) {
GlusterVolumeEntity volume = volumeDao.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(cluster,
volume,
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 EngineException(EngineError.GlusterSnapshotInfoFailedException, e.getLocalizedMessage());
}
}
}
private void addOrUpdateClusterConfig(Cluster cluster, final String paramName, final String paramValue) {
GlusterVolumeSnapshotConfig param = new GlusterVolumeSnapshotConfig();
param.setClusterId(cluster.getId());
param.setVolumeId(null);
param.setParamName(paramName);
param.setParamValue(paramValue);
GlusterVolumeSnapshotConfig existingParamDetail =
volumeSnapshotConfigDao.getConfigByClusterIdAndName(cluster.getId(), paramName);
if (existingParamDetail == null) {
volumeSnapshotConfigDao.save(param);
log.debug("Detected new gluster volume snapshot configuration '{}' with value '{}' for cluster: '{}'",
paramName,
paramValue,
cluster.getName());
Map<String, String> customValues = new HashMap<>();
customValues.put("snapConfigName", paramName);
customValues.put("snapConfigValue", paramValue);
logUtil.logAuditMessage(cluster.getId(),
null,
null,
AuditLogType.GLUSTER_VOLUME_SNAPSHOT_CLUSTER_CONFIG_DETECTED_NEW,
customValues);
} else if (!existingParamDetail.getParamValue().equals(paramValue)) {
volumeSnapshotConfigDao.updateConfigByClusterIdAndName(cluster.getId(), paramName, paramValue);
}
}
private void addOrUpdateVolumeConfig(Cluster cluster,
final GlusterVolumeEntity volume,
final String paramName,
final String paramValue) {
GlusterVolumeSnapshotConfig cfg = new GlusterVolumeSnapshotConfig();
cfg.setClusterId(cluster.getId());
cfg.setVolumeId(volume.getId());
cfg.setParamName(paramName);
cfg.setParamValue(paramValue);
GlusterVolumeSnapshotConfig existingParamDetail =
volumeSnapshotConfigDao.getConfigByVolumeIdAndName(cluster.getId(), volume.getId(), paramName);
if (existingParamDetail == null) {
volumeSnapshotConfigDao.save(cfg);
log.debug("Detected new gluster volume snapshot configuration '{}' with value '{}' for volume: '{}' on cluster '{}'",
paramName,
paramValue,
cluster.getName(),
volume.getName());
Map<String, String> customValues = new HashMap<>();
customValues.put("snapConfigName", paramName);
customValues.put("snapConfigValue", paramValue);
customValues.put(GlusterConstants.VOLUME_NAME, volume.getName());
logUtil.logAuditMessage(cluster.getId(),
volume,
null,
AuditLogType.GLUSTER_VOLUME_SNAPSHOT_VOLUME_CONFIG_DETECTED_NEW,
customValues);
} else if (!existingParamDetail.getParamValue().equals(paramValue)) {
volumeSnapshotConfigDao.updateConfigByVolumeIdAndName(cluster.getId(), volume.getId(), paramName, paramValue);
}
}
private void saveNewSnapshots(List<GlusterVolumeSnapshotEntity> snapshots) {
volumeSnapshotDao.saveAll(snapshots);
}
private void updateSnapshots(List<GlusterVolumeSnapshotEntity> snapshots) {
volumeSnapshotDao.updateAllInBatch(snapshots);
}
private void deleteSnapshots(List<GlusterVolumeSnapshotEntity> snaphosts) {
List<Guid> deletedIds = new ArrayList<>();
for (GlusterVolumeSnapshotEntity snapshot : snaphosts) {
deletedIds.add(snapshot.getId());
}
volumeSnapshotDao.removeAll(deletedIds);
}
private boolean supportsGlusterSnapshotFeature(Cluster cluster) {
return cluster.supportsGlusterService();
}
}