Sahina Bose has uploaded a new change for review. Change subject: engine: Sync geo-rep config information ......................................................................
engine: Sync geo-rep config information Added method to update database with CLI information for geo-replication configuration Removed the constraint that discovered georep keys needs to be present in config master. Handled possible null default values for config Change-Id: I0ee0bc7e916df3becfc99b701de8a0c9e95e2a4d Signed-off-by: Sahina Bose <[email protected]> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/gluster/GlusterGeoRepSyncJob.java M 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/businessentities/gluster/GlusterGeoRepSessionConfiguration.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterVolumeOptionInfo.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 packaging/dbscripts/create_views.sql M packaging/dbscripts/gluster_georep_sp.sql A packaging/dbscripts/upgrade/03_05_1380_georep_drop_constraint_config_key.sql 12 files changed, 179 insertions(+), 18 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/85/39985/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 index 7210b18..2d369b9 100644 --- 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 @@ -14,6 +14,7 @@ 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.GlusterGeoRepSessionConfiguration; 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; @@ -144,7 +145,6 @@ updateDiscoveredSessions(cluster, sessionsMap); } - private void updateDiscoveredSessions(VDSGroup cluster, Map<String, GlusterGeoRepSession> sessionsMap) { removeDeletedSessions(cluster.getId(), sessionsMap); @@ -191,7 +191,53 @@ getGeoRepDao().updateSession(session); } updateSessionDetailsInDB(session); + updateDiscoveredSessionConfig(cluster, session); } + } + + private void updateDiscoveredSessionConfig(VDSGroup cluster, GlusterGeoRepSession session) { + List<GlusterGeoRepSessionConfiguration> sessionConfigList = getSessionConfigFromCLI(cluster, session); + if (sessionConfigList == null) { + log.info("No configuration information returned from VDS for session '{}'", session.getSessionKey()); + return; + } + List<GlusterGeoRepSessionConfiguration> existingSessionConfigs = + getGeoRepDao().getGeoRepSessionConfig(session.getId()); + Map<String, GlusterGeoRepSessionConfiguration> existingKeyConfigMap = + prepareMapOfExistingConfigs(existingSessionConfigs); + for (GlusterGeoRepSessionConfiguration sessionConfig : sessionConfigList) { + //update sessionId for fetched object. + sessionConfig.setId(session.getId()); + // check if session config not same as in db + if (!existingSessionConfigs.contains(sessionConfig)) { + // confirm that it exists in db, which means config has been updated + if (existingKeyConfigMap.containsKey(sessionConfig.getKey())) { + getGeoRepDao().updateConfig(sessionConfig); + String oldValue = existingKeyConfigMap.get(sessionConfig.getKey()).getValue(); + logGeoRepMessage(AuditLogType.GEOREP_OPTION_CHANGED_FROM_CLI, + cluster.getId(), + getOptionChangedCustomVars(session, + sessionConfig.getKey(), + sessionConfig.getValue(), + oldValue)); + } else { + getGeoRepDao().saveConfig(sessionConfig); + logGeoRepMessage(AuditLogType.GEOREP_OPTION_SET_FROM_CLI, + cluster.getId(), + getOptionChangedCustomVars(session, sessionConfig.getKey(), sessionConfig.getValue(), null)); + } + } + } + } + + private Map<String, GlusterGeoRepSessionConfiguration> prepareMapOfExistingConfigs(List<GlusterGeoRepSessionConfiguration> existingConfigs) { + Map<String, GlusterGeoRepSessionConfiguration> keyConfigMap = new HashMap<>(); + if (existingConfigs != null) { + for (GlusterGeoRepSessionConfiguration config : existingConfigs) { + keyConfigMap.put(config.getKey(), config); + } + } + return keyConfigMap; } private void updateSlaveNodeAndVolumeId(GlusterGeoRepSession session) { @@ -246,6 +292,24 @@ put("geoRepSessionKey", session.getSessionKey()); } }); + } + + private void logGeoRepMessage(AuditLogType logType, Guid clusterId, final HashMap<String, String> customVars) { + logUtil.logAuditMessage(clusterId, null, null, + logType, customVars); + } + + private HashMap<String, String> getOptionChangedCustomVars(final GlusterGeoRepSession session, + String key, + String value, + String oldValue) { + HashMap<String, String> keyValMap = new HashMap<>(); + keyValMap.put(GlusterConstants.VOLUME_NAME, session.getMasterVolumeName()); + keyValMap.put("geoRepSessionKey", session.getSessionKey()); + keyValMap.put("key", key); + keyValMap.put("value", value); + keyValMap.put("oldValue", oldValue); + return keyValMap; } /** @@ -358,6 +422,31 @@ } } + private List<GlusterGeoRepSessionConfiguration> getSessionConfigFromCLI(VDSGroup cluster, + GlusterGeoRepSession session) { + 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; + } + try { + VDSReturnValue returnValue = runVdsCommand(VDSCommandType.GetGlusterVolumeGeoRepConfigList, + new GlusterVolumeGeoRepSessionVDSParameters(upServer.getId(), + session.getMasterVolumeName(), session.getSlaveHostName(), session.getSlaveVolumeName())); + if (returnValue.getSucceeded()) { + return (List<GlusterGeoRepSessionConfiguration>) returnValue.getReturnValue(); + } else { + log.error("VDS error {}", returnValue.getVdsError().getMessage()); + log.debug("VDS error", returnValue.getVdsError()); + return null; + } + } catch (Exception e) { + log.error("Exception getting geo-rep status from vds {}", e.getMessage()); + log.debug("Exception", e); + return null; + } + } + private GlusterVolumeEntity getVolume(VDSGroup cluster, String masterVolumeName) { return getVolumeDao().getByName(cluster.getId(), masterVolumeName); } 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 index d916a42..d1f94cc 100644 --- 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 @@ -23,6 +23,7 @@ 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.GlusterGeoRepSessionConfiguration; 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; @@ -103,6 +104,20 @@ } @Test + public void testDiscoverGeoRepDataWithConfig() { + + doReturn(getSessionsVDSReturnVal(true, 2)).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeGeoRepSessionList), + any(GlusterVolumeGeoRepSessionVDSParameters.class)); + doReturn(getSessionsConfigListVDSReturnVal(true)).when(syncJob) + .runVdsCommand(eq(VDSCommandType.GetGlusterVolumeGeoRepConfigList), + any(GlusterVolumeGeoRepSessionVDSParameters.class)); + syncJob.discoverGeoRepData(); + Mockito.verify(geoRepDao, times(2)).save(any(GlusterGeoRepSession.class)); + Mockito.verify(geoRepDao, times(2)).saveConfig(any(GlusterGeoRepSessionConfiguration.class)); + } + + @Test public void testDiscoverGeoRepDataWhenNoSessions() { doReturn(getSessionsVDSReturnVal(true, 0)).when(syncJob) @@ -163,6 +178,26 @@ return vdsRetValue; } + private Object getSessionsConfigListVDSReturnVal(boolean ret) { + VDSReturnValue vdsRetValue = new VDSReturnValue(); + vdsRetValue.setSucceeded(ret); + if (ret) { + vdsRetValue.setReturnValue(getSessionConfigList()); + } else { + vdsRetValue.setReturnValue(null); + } + return vdsRetValue; + } + + private List<GlusterGeoRepSessionConfiguration> getSessionConfigList() { + List<GlusterGeoRepSessionConfiguration> configList = new ArrayList<>(); + GlusterGeoRepSessionConfiguration config = new GlusterGeoRepSessionConfiguration(); + config.setKey("georep-crawl"); + config.setValue("hybrid"); + configList.add(config); + return configList; + } + private List<GlusterGeoRepSession> getSessions(int count, boolean populateVoId) { List<GlusterGeoRepSession> sessions = new ArrayList<GlusterGeoRepSession>(); for (int i = 0; i < count; i++) { 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 20c2170..f822afd 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 @@ -419,6 +419,8 @@ STORAGE_DEVICE_REMOVED_FROM_THE_HOST(4125), SYNC_STORAGE_DEVICES_IN_HOST(4126), SYNC_STORAGE_DEVICES_IN_HOST_FAILED(4127), + GEOREP_OPTION_SET_FROM_CLI(4128, AuditLogSeverity.WARNING), + GEOREP_OPTION_CHANGED_FROM_CLI(4129, AuditLogSeverity.WARNING), USER_FORCE_SELECTED_SPM(159), USER_VDS_RESTART(41), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterGeoRepSessionConfiguration.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterGeoRepSessionConfiguration.java index 7ce9a6e..0ccc5b1 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterGeoRepSessionConfiguration.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterGeoRepSessionConfiguration.java @@ -42,17 +42,19 @@ @Override public boolean equals(Object obj) { - return obj != null && obj instanceof GlusterGeoRepSessionConfiguration - && ObjectUtils.objectsEqual(getId(), ((GlusterGeoRepSessionConfiguration) obj).getId()) - && super.equals(obj); + if (obj == null || !(obj instanceof GlusterGeoRepSessionConfiguration)) { + return false; + } + if (!super.equals(obj)) { + return false; + } + return ObjectUtils.objectsEqual(getId(), ((GlusterGeoRepSessionConfiguration) obj).getId()); } @Override public int hashCode() { final int prime = 31; - int result = 1; - result = prime * result + sessionId.hashCode(); - result = prime * result + super.hashCode(); - return result; + int result = super.hashCode(); + return prime * result + ((sessionId == null) ? 0 : sessionId.hashCode()); } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterVolumeOptionInfo.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterVolumeOptionInfo.java index b6a2424..ae81b7d 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterVolumeOptionInfo.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/gluster/GlusterVolumeOptionInfo.java @@ -2,6 +2,8 @@ import java.io.Serializable; +import org.ovirt.engine.core.common.utils.ObjectUtils; + /** * Class representing information of a Gluster Volume Option * @@ -53,7 +55,6 @@ int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode()); - result = prime * result + ((description == null) ? 0 : description.hashCode()); return result; } @@ -64,10 +65,8 @@ } GlusterVolumeOptionInfo option = (GlusterVolumeOptionInfo) obj; - return (option.getKey().equals(key) - && option.getDefaultValue().equals(defaultValue) - && option.getDescription() - .equals(description)); + return ObjectUtils.objectsEqual(option.getKey(), key) + && ObjectUtils.objectsEqual(option.getDefaultValue(), defaultValue); } @Override 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 b4058da..153fd1d 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 @@ -54,5 +54,7 @@ public List<GlusterGeoRepSessionConfiguration> getGeoRepSessionConfig(Guid sessionId); + public GlusterGeoRepSessionConfiguration getGeoRepSessionConfigByKey(Guid sessionId, String configKey); + public List<GlusterGeoRepSession> getAllSessions(); } 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 f20120d..400be02 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 @@ -60,7 +60,9 @@ entity.setKey(rs.getString("config_key")); entity.setValue(rs.getString("config_value")); entity.setDescription(rs.getString("config_description")); - entity.setAllowedValues(Arrays.asList(rs.getString("config_possible_values").split(";"))); + entity.setAllowedValues(rs.getString("config_possible_values") != null ? Arrays.asList(rs.getString("config_possible_values") + .split(";")) + : null); return entity; } } @@ -156,6 +158,12 @@ } @Override + public GlusterGeoRepSessionConfiguration getGeoRepSessionConfigByKey(Guid sessionId, String configKey) { + return getCallsHandler().executeRead("GetGlusterGeoRepSessionConfigByKey", georepSessionConfigRowMapper, + createIdParameterMapper(sessionId).addValue("config_key", configKey)); + } + + @Override protected MapSqlParameterSource createFullParametersMapper( GlusterGeoRepSession geoRepSession) { return createIdParameterMapper(geoRepSession.getId()) 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 266129e..c3598ec 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties @@ -841,6 +841,12 @@ STORAGE_DEVICE_REMOVED_FROM_THE_HOST=Detected deletion of storage device ${storageDevice} on host ${VdsName}, and deleting it from engine DB." SYNC_STORAGE_DEVICES_IN_HOST=Manually synced the storage devices from host ${VdsName} SYNC_STORAGE_DEVICES_IN_HOST_FAILED=Failed to synced storage devices from host ${VdsName} +GEOREP_SESSION_STARTED=Geo-replication session on volume ${glusterVolumeName} has been started. +GEOREP_SESSION_START_FAILED=Failed to start geo-replication session on volume ${glusterVolumeName} +GEOREP_OPTION_SET_FROM_CLI=Detected new option ${key}=${value} for geo-replication session ${geoRepSessionKey} on volume ${glusterVolumeName} of cluster ${VdsGroupName}, and added it to engine. +GEOREP_OPTION_CHANGED_FROM_CLI=Detected change in value of option ${key} from ${oldValue} to ${value} for geo-replication session on volume ${glusterVolumeName} of cluster ${VdsGroupName}, and updated 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 71b9995..0726a6d 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 @@ -18,6 +18,7 @@ public class GlusterGeoRepDaoTest extends BaseDAOTestCase { + private static final String GEOREP_CONFIG_CRAWL = "georep-crawl"; private static final Guid SESSION_ID = new Guid("4f4f751e-549b-4e7a-aff6-32d36856c125"); private static final Guid NONEXIST_SESSION_ID = new Guid("5e5e751e-549b-4e7a-aff6-32d36856c125"); @@ -55,7 +56,7 @@ private GlusterGeoRepSessionConfiguration getGlusterGeoRepSessionConfig() { GlusterGeoRepSessionConfiguration sessionConfig = new GlusterGeoRepSessionConfiguration(); sessionConfig.setId(FixturesTool.GLUSTER_GEOREP_SESSION_ID); - sessionConfig.setKey("georep-crawl"); + sessionConfig.setKey(GEOREP_CONFIG_CRAWL); sessionConfig.setDescription("Geo-replication session crawl"); sessionConfig.setValue("changelog"); return sessionConfig; @@ -100,8 +101,12 @@ public void testSaveConfig() { GlusterGeoRepSessionConfiguration sessionConfig = getGlusterGeoRepSessionConfig(); dao.saveConfig(sessionConfig); - List<GlusterGeoRepSessionConfiguration> fetchedSessionConfig = dao.getGeoRepSessionConfig(FixturesTool.GLUSTER_GEOREP_SESSION_ID); - assertEquals(sessionConfig, fetchedSessionConfig.get(0)); + List<GlusterGeoRepSessionConfiguration> fetchedSessionConfigList = + dao.getGeoRepSessionConfig(FixturesTool.GLUSTER_GEOREP_SESSION_ID); + assertEquals(sessionConfig, fetchedSessionConfigList.get(0)); + GlusterGeoRepSessionConfiguration fetchedSessionConfig = + dao.getGeoRepSessionConfigByKey(FixturesTool.GLUSTER_GEOREP_SESSION_ID, GEOREP_CONFIG_CRAWL); + assertEquals(sessionConfig, fetchedSessionConfig); } @Test diff --git a/packaging/dbscripts/create_views.sql b/packaging/dbscripts/create_views.sql index fc78ce4..eb75c0a 100644 --- a/packaging/dbscripts/create_views.sql +++ b/packaging/dbscripts/create_views.sql @@ -1794,7 +1794,7 @@ AS SELECT session_id, georepConfig.config_key, config_value, config_description, config_possible_values, _update_date FROM gluster_georep_config georepConfig -INNER JOIN gluster_config_master ON gluster_config_master.config_key = georepConfig.config_key AND gluster_config_master.config_feature='geo_replication'; +LEFT OUTER JOIN gluster_config_master ON gluster_config_master.config_key = georepConfig.config_key AND gluster_config_master.config_feature='geo_replication'; -- Affinity Groups view, including members CREATE OR REPLACE VIEW affinity_groups_view diff --git a/packaging/dbscripts/gluster_georep_sp.sql b/packaging/dbscripts/gluster_georep_sp.sql index 59b9568..0a91991 100644 --- a/packaging/dbscripts/gluster_georep_sp.sql +++ b/packaging/dbscripts/gluster_georep_sp.sql @@ -223,6 +223,18 @@ END; $procedure$ LANGUAGE plpgsql; +Create or replace FUNCTION GetGlusterGeoRepSessionConfigByKey(v_session_id UUID, + v_config_key VARCHAR(50)) +RETURNS SETOF gluster_geo_rep_config_view STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT * + FROM gluster_geo_rep_config_view + WHERE session_id = v_session_id + AND config_key = v_config_key; +END; $procedure$ +LANGUAGE plpgsql; + Create or replace FUNCTION GetAllGlusterGeoRepSessions() RETURNS SETOF gluster_georep_sessions_view STABLE AS $procedure$ diff --git a/packaging/dbscripts/upgrade/03_05_1380_georep_drop_constraint_config_key.sql b/packaging/dbscripts/upgrade/03_05_1380_georep_drop_constraint_config_key.sql new file mode 100644 index 0000000..ddd3480 --- /dev/null +++ b/packaging/dbscripts/upgrade/03_05_1380_georep_drop_constraint_config_key.sql @@ -0,0 +1 @@ +SELECT fn_db_drop_constraint('gluster_georep_config','fk_config_key'); -- To view, visit https://gerrit.ovirt.org/39985 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0ee0bc7e916df3becfc99b701de8a0c9e95e2a4d 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
