Yair Zaslavsky has uploaded a new change for review. Change subject: aaa: Fix sync ......................................................................
aaa: Fix sync Change-Id: I3a66a7c235f16ff26cac3a779de688c73d67f4a2 Topic: AAA Signed-off-by: Yair Zaslavsky <yzasl...@redhat.com> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/aaa/DirectoryUtils.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddUserCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DbUserCacheManager.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DirectoryEntryKey.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/LoginBaseCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SyncUsers.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/DbUser.java 7 files changed, 170 insertions(+), 235 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/06/29706/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/aaa/DirectoryUtils.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/aaa/DirectoryUtils.java index aa3f5d4..64739ec 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/aaa/DirectoryUtils.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/aaa/DirectoryUtils.java @@ -14,13 +14,13 @@ import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dao.DbGroupDAO; import org.ovirt.engine.core.extensions.mgr.ExtensionProxy; +import org.ovirt.engine.core.utils.extensionsmgr.EngineExtensionsManager; public class DirectoryUtils { public static HashSet<Guid> getGroupIdsFromUser(DirectoryUser directoryUser) { HashSet<Guid> results = new HashSet<Guid>(); Set<DirectoryGroup> groupsSet = new HashSet<DirectoryGroup>(); - flatGroups(groupsSet, directoryUser.getGroups()); DbGroupDAO dao = DbFacade.getInstance().getDbGroupDao(); if (groupsSet != null) { for (DirectoryGroup group : groupsSet) { @@ -32,6 +32,14 @@ } return results; } + + public static List<DirectoryUser> fetchUsersByIdsRecursively( + final String authz, + final String namespace, + final List<String> ids) { + return fetchUsersByIdsRecursively(EngineExtensionsManager.getInstance().getExtensionByName(authz), namespace, ids); + } + public static List<DirectoryUser> fetchUsersByIdsRecursively( final ExtensionProxy extension, @@ -178,6 +186,12 @@ return directoryGroup; } + public static void flatGroups(DirectoryUser directoryUser) { + Set<DirectoryGroup> accumulator = new HashSet<DirectoryGroup>(); + flatGroups(accumulator, directoryUser.getGroups()); + directoryUser.setGroups(new ArrayList<DirectoryGroup>(accumulator)); + } + public static List<DirectoryGroup> mapGroupRecords(final String authzName, final List<ExtMap> groups) { List<DirectoryGroup> results = new ArrayList<>(); for (ExtMap group : groups) { diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddUserCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddUserCommand.java index 5e9fe93..2ee2650 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddUserCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddUserCommand.java @@ -78,7 +78,7 @@ boolean foundUser = false; for (String namespace : getParameters().getNamespace() != null ? Arrays.asList(getParameters().getNamespace()) : authz.getContext().<List<String>> get(Authz.ContextKeys.AVAILABLE_NAMESPACES)) { - directoryUser = DirectoryUtils.findDirectoryUserById(authz, namespace, id, true, true); + directoryUser = DirectoryUtils.fetchUsersByIdsRecursively(authz, namespace, Arrays.asList(id)).get(0); if (directoryUser != null) { foundUser = true; break; @@ -107,6 +107,7 @@ // First check if the user is already in the database, if it is we need to update, if not we need to insert: HashSet<Guid> groupIds = DirectoryUtils.getGroupIdsFromUser(directoryUser); + DirectoryUtils.flatGroups(directoryUser); DbUser dbUser = dao.getByExternalId(directoryUser.getDirectoryName(), directoryUser.getId()); if (dbUser == null) { dbUser = new DbUser(directoryUser); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DbUserCacheManager.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DbUserCacheManager.java index b873251..f7d0589 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DbUserCacheManager.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DbUserCacheManager.java @@ -1,29 +1,13 @@ package org.ovirt.engine.core.bll; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang.StringUtils; -import org.ovirt.engine.core.aaa.AuthzUtils; -import org.ovirt.engine.core.aaa.DirectoryGroup; -import org.ovirt.engine.core.aaa.DirectoryUser; -import org.ovirt.engine.core.aaa.DirectoryUtils; import org.ovirt.engine.core.common.businessentities.DbGroup; -import org.ovirt.engine.core.common.businessentities.DbUser; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; -import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.DbFacade; -import org.ovirt.engine.core.dao.DbGroupDAO; -import org.ovirt.engine.core.dao.DbUserDAO; -import org.ovirt.engine.core.extensions.mgr.ExtensionProxy; -import org.ovirt.engine.core.utils.collections.MultiValueMapUtils; -import org.ovirt.engine.core.utils.extensionsmgr.EngineExtensionsManager; import org.ovirt.engine.core.utils.log.Log; import org.ovirt.engine.core.utils.log.LogFactory; import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; @@ -68,196 +52,7 @@ */ @OnTimerMethodAnnotation("refreshAllUsers") public void refreshAllUsers() { - // We will need the DAO: - DbUserDAO dao = DbFacade.getInstance().getDbUserDao(); - - // Retrieve all the users from the database: - List<DbUser> dbUsers = dao.getAll(); - List<DbGroup> dbGroups = DbFacade.getInstance().getDbGroupDao().getAll(); - for (DbGroup group : dbGroups) { - groupsMap.put(group.getExternalId(), group); - } - - // Classify the users by directory. Note that the resulting map may have an entry with a null key, that - // corresponds to the users whose directory has been removed from the configuration. - Map<String, List<DbUser>> authzToPrincipalMap = new HashMap<>(); - Map<String, ExtensionProxy> authzMap = new HashMap<>(); - - for (DbUser dbUser : dbUsers) { - ExtensionProxy authz = EngineExtensionsManager.getInstance().getExtensionByName(dbUser.getDomain()); - if (authz == null) { - log.warn(String.format("No authz extension was found for user %1$s. It is possible that the relevant " + - "domain for the user was removed for the user. Marking the user as inactive", - dbUser.getLoginName())); - if (dbUser.isActive()) { - dbUser.setActive(false); - dao.update(dbUser); - } - continue; - - } - String auhtzName = AuthzUtils.getName(authz); - MultiValueMapUtils.addToMap(auhtzName, dbUser, authzToPrincipalMap); - authzMap.put(auhtzName, authz); - } - - // Refresh the users for each directory: - List<DbUser> updates = new ArrayList<>(); - for (Map.Entry<String, List<DbUser>> entry : authzToPrincipalMap.entrySet()) { - List<DbUser> refreshed = refreshUsers(entry.getValue(), authzMap.get(entry.getKey())); - updates.addAll(refreshed); - } - - // Actually update the users in the database (note that this should be done with a batch update, but we don't - // have support for that yet): - for (DbUser dbUser : updates) { - dao.update(dbUser); - } + SyncUsers.sync(DbFacade.getInstance().getDbUserDao().getAll()); } - /** - * Refresh a list of users retrieving their data from a given directory. - * - * @param dbUsers the list of users to refresh - * @param authz the authz extension where the data of the users will be extracted from, it may be {@code null} if the - * directory has already been removed from the configuration - * @return the list of database users that have been actually modified and that need to be updated in the database - */ - private List<DbUser> refreshUsers(List<DbUser> dbUsers, ExtensionProxy authz) { - // Find all the users in the directory using a batch operation to improve performance: - Map<String, List<String>> idsPerNamespace = new HashMap<>(); - - List<DbUser> refreshed = new ArrayList<>(); - List<String> ids = new ArrayList<>(dbUsers.size()); - for (DbUser dbUser : dbUsers) { - MultiValueMapUtils.addToMap(dbUser.getNamespace(), dbUser.getExternalId(), idsPerNamespace); - } - for (String namespace : idsPerNamespace.keySet()) { - List<DirectoryUser> directoryUsers = null; - if (authz != null) { - directoryUsers = DirectoryUtils.findDirectoryUserByIds(authz, namespace, ids, true, false); - } - else { - directoryUsers = Collections.emptyList(); - } - - // Build a map of users indexed by directory id to simplify the next step where we want to find the directory - // user corresponding to each database user: - Map<String, DirectoryUser> index = new HashMap<>(); - for (DirectoryUser directoryUser : directoryUsers) { - index.put(directoryUser.getId(), directoryUser); - } - - // For each database user refresh it using the corresponding directory user and collect those users that need to - // be updated: - for (DbUser dbUser : dbUsers) { - DirectoryUser directoryUser = index.get(dbUser.getExternalId()); - if (directoryUser != null) { - dbUser.setActive(false); - dbUser.setGroupIds(DirectoryUtils.getGroupIdsFromUser(directoryUser)); - dbUser = refreshUser(dbUser, directoryUser); - if (dbUser != null) { - refreshed.add(dbUser); - } - } - } - - } - - return refreshed; - } - - /** - * Detect differences between a user as stored in the database and the same user retrieved from the directory. - * - * @param dbUser the database user - * @param directoryUser the directory user, it may be {@code null} if the user doesn't exist in the directory - * @return the updated database user if it was actually updated or {@code null} if it doesn't need to be updated - */ - private DbUser refreshUser(DbUser dbUser, DirectoryUser directoryUser) { - // If there the user doesn't exist in the directory then we only need to mark the database user as not active - // if it isn't marked already: - if (directoryUser == null) { - if (dbUser.isActive()) { - log.warnFormat( - "User \"{0}\" will be marked as not active as it wasn't found in the directory \"{1}\".", - dbUser.getLoginName(), dbUser.getDomain() - ); - } - return dbUser; - } - - // This flag indicates if there are any differences and thus if the database update should actually be - // performed: - boolean update = false; - - // If the user was marked as not active in the database then mark it as active: - if (!dbUser.isActive()) { - log.infoFormat( - "User \"{0}\" will be marked as active as it was found in directory \"{1}\".", - dbUser.getLoginName(), dbUser.getDomain() - ); - dbUser.setActive(true); - update = true; - } - - // Compare the attributes of the database user with those of the directory, copy those that changed and update - // the flag that indicates that the database needs to be updated: - if (!StringUtils.equals(dbUser.getFirstName(), directoryUser.getFirstName())) { - dbUser.setFirstName(directoryUser.getFirstName()); - update = true; - } - if (!StringUtils.equals(dbUser.getLastName(), directoryUser.getLastName())) { - dbUser.setLastName(directoryUser.getLastName()); - update = true; - } - if (!StringUtils.equals(dbUser.getDomain(), directoryUser.getDirectoryName())) { - dbUser.setDomain(directoryUser.getDirectoryName()); - update = true; - } - if (!StringUtils.equals(dbUser.getLoginName(), directoryUser.getName())) { - dbUser.setLoginName(directoryUser.getName()); - update = true; - } - if (!StringUtils.equals(dbUser.getDepartment(), directoryUser.getDepartment())) { - dbUser.setDepartment(directoryUser.getDepartment()); - update = true; - } - if (!StringUtils.equals(dbUser.getRole(), directoryUser.getTitle())) { - dbUser.setRole(directoryUser.getTitle()); - update = true; - } - if (!StringUtils.equals(dbUser.getEmail(), directoryUser.getEmail())) { - dbUser.setEmail(directoryUser.getEmail()); - update = true; - } - - // Compare the new list of group names and identifiers: - List<String> groupNamesFromDirectory = new ArrayList<>(); - DbGroupDAO groupDao = DbFacade.getInstance().getDbGroupDao(); - HashSet<Guid> groupIds = new HashSet<>(); - for (DirectoryGroup directoryGroup : directoryUser.getGroups()) { - DbGroup dbGroup = groupsMap.get(directoryGroup.getId()); - if (dbGroup == null) { - dbGroup = groupDao.getByExternalId(dbUser.getDomain(), directoryGroup.getId()); - } - if (dbGroup != null) { - groupIds.add(dbGroup.getId()); - } - groupNamesFromDirectory.add(directoryGroup.getName()); - } - Collections.sort(groupNamesFromDirectory); - List<String> groupNamesFromDb = new ArrayList<String>(dbUser.getGroupNames()); - Collections.sort(groupNamesFromDb); - if (!groupNamesFromDb.equals(groupNamesFromDirectory)) { - dbUser.setGroupNames(new HashSet<String>(groupNamesFromDirectory)); - update = true; - } - - if (!groupIds.equals(dbUser.getGroupIds())) { - dbUser.setGroupIds(groupIds); - update = true; - } - return update? dbUser: null; - } } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DirectoryEntryKey.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DirectoryEntryKey.java new file mode 100644 index 0000000..c3964a9 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/DirectoryEntryKey.java @@ -0,0 +1,60 @@ +package org.ovirt.engine.core.bll; + +import org.ovirt.engine.core.common.businessentities.DbUser; + + +public class DirectoryEntryKey { + + private String authz; + private String externalId; + + public DirectoryEntryKey(String authz, String externalId) { + this.authz = authz; + this.externalId = externalId; + } + + public DirectoryEntryKey(DbUser dbUser) { + this(dbUser.getDomain(), dbUser.getExternalId()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((authz == null) ? 0 : authz.hashCode()); + result = prime * result + ((externalId == null) ? 0 : externalId.hashCode()); + return result; + } + + public String getAuthz() { + return authz; + } + + public String getExternalId() { + return externalId; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DirectoryEntryKey other = (DirectoryEntryKey) obj; + if (authz == null) { + if (other.authz != null) + return false; + } else if (!authz.equals(other.authz)) + return false; + if (externalId == null) { + if (other.externalId != null) + return false; + } else if (!externalId.equals(other.externalId)) + return false; + return true; + } + +} + diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/LoginBaseCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/LoginBaseCommand.java index 94fdb64..88562bd 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/LoginBaseCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/LoginBaseCommand.java @@ -265,6 +265,7 @@ dbUser = new DbUser(directoryUser); dbUser.setId(Guid.newGuid()); } + DirectoryUtils.flatGroups(directoryUser); dbUser.setGroupIds(DirectoryUtils.getGroupIdsFromUser(directoryUser)); getDbUserDAO().saveOrUpdate(dbUser); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SyncUsers.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SyncUsers.java new file mode 100644 index 0000000..b9b625c --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SyncUsers.java @@ -0,0 +1,88 @@ +package org.ovirt.engine.core.bll; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.ovirt.engine.core.aaa.DirectoryUser; +import org.ovirt.engine.core.aaa.DirectoryUtils; +import org.ovirt.engine.core.common.businessentities.DbUser; +import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.extensions.mgr.ConfigurationException; +import org.ovirt.engine.core.extensions.mgr.ExtensionProxy; +import org.ovirt.engine.core.utils.collections.MultiValueMapUtils; +import org.ovirt.engine.core.utils.extensionsmgr.EngineExtensionsManager; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class SyncUsers { + + private static final Log log = LogFactory.getLog(SyncUsers.class); + + public static void sync(List<DbUser> dbUsers) { + Map<String, Map<String, Set<String>>> authzToNamespaceToUserIds = new HashMap<>(); + Map<DirectoryEntryKey, DbUser> dbUsersMap = new HashMap<>(); + Map<DirectoryEntryKey, DbUser> allUsers = new HashMap<>(); + Map<DirectoryEntryKey, DbUser> activeUsers = new HashMap<>(); + + //Initialize the entries based on authz in the map + for (DbUser dbUser : dbUsers) { + allUsers.put(new DirectoryEntryKey(dbUser.getDomain(), dbUser.getExternalId()), dbUser); + if (!authzToNamespaceToUserIds.containsKey(dbUser.getDomain())) { + authzToNamespaceToUserIds.put(dbUser.getDomain(), new HashMap<String, Set<String>>()); + } + MultiValueMapUtils.addToMapOfSets(dbUser.getNamespace(), dbUser.getExternalId(), authzToNamespaceToUserIds.get(dbUser.getDomain())); + dbUsersMap.put(new DirectoryEntryKey(dbUser.getDomain(), dbUser.getExternalId()), dbUser); + } + + for (String authz : authzToNamespaceToUserIds.keySet()) { + try { + ExtensionProxy authzExtension = EngineExtensionsManager.getInstance().getExtensionByName(authz); + for (Entry<String, Set<String>> userIdsPerNamespace : authzToNamespaceToUserIds.get(authz).entrySet()) { + Set<String> userIds = userIdsPerNamespace.getValue(); + for (DirectoryUser directoryUser : DirectoryUtils.fetchUsersByIdsRecursively(authzExtension, userIdsPerNamespace.getKey(), new ArrayList<String>(userIds))) { + DirectoryUtils.flatGroups(directoryUser); + DbUser dbUser = new DbUser(directoryUser); + dbUser.setGroupIds(DirectoryUtils.getGroupIdsFromUser(directoryUser)); + activeUsers.put(new DirectoryEntryKey(directoryUser.getDirectoryName(), directoryUser.getId()), dbUser); + } + } + } catch (ConfigurationException ex) { + log.error(String.format("The authz extension for %1%s has not been found. Users belonging to this extension will be marked as inactive", + authz)); + } + } + + for (DbUser dbUser: dbUsers) { + DbUser activeUser = activeUsers.get(new DirectoryEntryKey(dbUser)); + boolean updated = false; + if (activeUser == null) { + if (dbUser.isActive()) { + updated = true; + dbUser.setActive(false); + log.info(String.format("The user %1$s from authz extension %2$s could not be found, and will be marked as inactive", + dbUser.getLoginName(), + dbUser.getDomain())); + } + } else { + if (!dbUser.isActive()) { + updated = true; + dbUser.setActive(true); + log.info(String.format("The inactive user %1$s from authz extension %2$s was found, and will be marked as active", + dbUser.getLoginName(), + dbUser.getDomain())); + } + if (!dbUser.equals(activeUser)) { + updated = true; + } + } + if (updated) { + activeUser.setId(dbUser.getId()); + DbFacade.getInstance().getDbUserDao().update(dbUser); + } + } + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/DbUser.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/DbUser.java index 9ca0d96..a98b0fb 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/DbUser.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/DbUser.java @@ -109,6 +109,7 @@ active = true; role = ""; note = ""; + groupNames = new HashSet<String>(); for (DirectoryGroup directoryGroup : directoryUser.getGroups()) { groupNames.add(directoryGroup.getName()); @@ -251,20 +252,8 @@ public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((externalId == null) ? 0 : externalId.hashCode()); - result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((domain == null) ? 0 : domain.hashCode()); - result = prime * result + ((namespace == null) ? 0 : namespace.hashCode()); - result = prime * result + ((email == null) ? 0 : email.hashCode()); - result = prime * result + ((groupNames == null) ? 0 : groupNames.hashCode()); - result = prime * result + (isAdmin ? 1231 : 1237); - result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); - result = prime * result + ((note == null) ? 0 : note.hashCode()); - result = prime * result + ((role == null) ? 0 : role.hashCode()); - result = prime * result + (active ? 1231 : 1237); - result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); - result = prime * result + ((loginName == null) ? 0 : loginName.hashCode()); return result; } @@ -280,21 +269,8 @@ return false; } DbUser other = (DbUser) obj; - return (ObjectUtils.objectsEqual(id, other.id) - && ObjectUtils.objectsEqual(externalId, other.externalId) - && ObjectUtils.objectsEqual(department, other.department) - && ObjectUtils.objectsEqual(domain, other.domain) - && ObjectUtils.objectsEqual(namespace, other.namespace) - && ObjectUtils.objectsEqual(email, other.email) - && ObjectUtils.objectsEqual(groupNames, other.groupNames) - && isAdmin == other.isAdmin - && ObjectUtils.objectsEqual(firstName, other.firstName) - && ObjectUtils.objectsEqual(note, other.note) - && ObjectUtils.objectsEqual(role, other.role) - && active == other.active - && ObjectUtils.objectsEqual(lastName, other.lastName) - && ObjectUtils.objectsEqual(loginName, other.loginName)); - + return ObjectUtils.objectsEqual(externalId, other.externalId) + && ObjectUtils.objectsEqual(domain, other.domain); } private void populateGroupNames(StringBuilder namesBuffer, DirectoryGroup directoryGroup) { -- To view, visit http://gerrit.ovirt.org/29706 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I3a66a7c235f16ff26cac3a779de688c73d67f4a2 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Yair Zaslavsky <yzasl...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches