summaryrefslogtreecommitdiffstats
path: root/ipaserver/install
diff options
context:
space:
mode:
authorMartin Babinsky <mbabinsk@redhat.com>2015-05-04 18:33:44 +0200
committerPetr Vobornik <pvoborni@redhat.com>2015-05-07 13:54:30 +0200
commite2a42efe33d5e6cb08e1988f7253caf56eda11df (patch)
tree3f8a38ffa9d84bbc5d46d7053a289a5ef837a656 /ipaserver/install
parenta1ccdc33dfc5de3480b9ad407f4a95d01258008d (diff)
downloadfreeipa-e2a42efe33d5e6cb08e1988f7253caf56eda11df.tar.gz
freeipa-e2a42efe33d5e6cb08e1988f7253caf56eda11df.tar.xz
freeipa-e2a42efe33d5e6cb08e1988f7253caf56eda11df.zip
prevent duplicate IDs when setting up multiple replicas against single master
This patch forces replicas to use DELETE+ADD operations to increment 'nsDS5ReplicaId' in 'cn=replication,cn=etc,$SUFFIX' on master, and retry multiple times in the case of conflict with another update. Thus when multiple replicas are set-up against single master none of them will have duplicate ID. https://fedorahosted.org/freeipa/ticket/4378 Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
Diffstat (limited to 'ipaserver/install')
-rw-r--r--ipaserver/install/replication.py74
1 files changed, 50 insertions, 24 deletions
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 66764c22f..4c16dc225 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -21,6 +21,7 @@ import time
import datetime
import sys
import os
+from random import randint
import ldap
@@ -230,34 +231,59 @@ class ReplicationManager(object):
# Ok, either the entry doesn't exist or the attribute isn't set
# so get it from the other master
- retval = -1
- dn = DN(('cn','replication'),('cn','etc'), self.suffix)
- try:
- replica = master_conn.get_entry(dn)
- except errors.NotFound:
- root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
- raise
- else:
- id_values = replica.get('nsDS5ReplicaId')
- if not id_values:
- root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
- raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server")
+ return self._get_and_update_id_from_master(master_conn)
- # nsDS5ReplicaId is single-valued now, but historically it could
- # contain multiple values, of which we need the highest.
- # see bug: https://fedorahosted.org/freeipa/ticket/3394
- retval = max(int(v) for v in id_values)
+ def _get_and_update_id_from_master(self, master_conn, attempts=5):
+ """
+ Fetch replica ID from remote master and update nsDS5ReplicaId attribute
+ on 'cn=replication,cn=etc,$SUFFIX' entry. Do it as MOD_DELETE+MOD_ADD
+ operations and retry when conflict occurs, e.g. due to simultaneous
+ update from another replica.
+ :param master_conn: LDAP connection to master
+ :param attempts: number of attempts to update nsDS5ReplicaId
+ :return: value of nsDS5ReplicaId before incrementation
+ """
+ dn = DN(('cn','replication'),('cn','etc'), self.suffix)
- # Now update the value on the master
- mod = [(ldap.MOD_REPLACE, 'nsDS5ReplicaId', str(retval + 1))]
+ for a in range(1, attempts + 1):
+ try:
+ root_logger.debug('Fetching nsDS5ReplicaId from master '
+ '[attempt %d/%d]', a, attempts)
+ replica = master_conn.get_entry(dn)
+ id_values = replica.get('nsDS5ReplicaId')
+ if not id_values:
+ root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
+ raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server")
+ # nsDS5ReplicaId is single-valued now, but historically it could
+ # contain multiple values, of which we need the highest.
+ # see bug: https://fedorahosted.org/freeipa/ticket/3394
+ retval = max(int(v) for v in id_values)
+
+ # Now update the value on the master
+ mod_list = [(ldap.MOD_DELETE, 'nsDS5ReplicaId', str(retval)),
+ (ldap.MOD_ADD, 'nsDS5ReplicaId', str(retval + 1))]
+
+ master_conn.modify_s(dn, mod_list)
+ root_logger.debug('Successfully updated nsDS5ReplicaId.')
+ return retval
- try:
- master_conn.modify_s(dn, mod)
- except Exception, e:
- root_logger.debug("Problem updating nsDS5ReplicaID %s" % e)
- raise
+ except errors.NotFound:
+ root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server")
+ raise
+ # these errors signal a conflict in updating replica ID.
+ # We then wait for a random time interval and try again
+ except (ldap.NO_SUCH_ATTRIBUTE, ldap.OBJECT_CLASS_VIOLATION) as e:
+ sleep_interval = randint(1, 5)
+ root_logger.debug("Update failed (%s). Conflicting operation?",
+ e)
+ time.sleep(sleep_interval)
+ # in case of other error we bail out
+ except ldap.LDAPError as e:
+ root_logger.debug("Problem updating nsDS5ReplicaID %s" % e)
+ raise
- return retval
+ raise RuntimeError("Failed to update nsDS5ReplicaId in %d attempts"
+ % attempts)
def get_agreement_filter(self, agreement_types=None, host=None):
"""