Please see the rebased patches attached.

On 01/13/2016 02:01 PM, Martin Basti wrote:


On 18.12.2015 12:46, Stanislav Laznicka wrote:
Hi,

Attached are the patches for auto-find and clean of dangling (cs)ruvs. Currently, the cleaning of an RUV waits for all replicas to be online, even on --force. If that were an issue, I can make the command fail before trying to clean any of RUVs. However, the user is shown a replica is offline and is prompted to confirm the cleaning so the possible wait should not be a problem I believe.

Standa L.


Hello,

patches needs rebase, I cannot apply them.

From 46ccb4fc174d1cab8b6631e16689a1047b8dbb28 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 18 Dec 2015 10:30:44 +0100
Subject: [PATCH 1/2] Listing and cleaning RUV extended for CA suffix

https://fedorahosted.org/freeipa/ticket/5411
---
 install/tools/ipa-replica-manage | 36 +++++++++++++++++++++++-------------
 ipaserver/install/replication.py |  2 +-
 2 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index e4af7b2fd9a40482dfa75d275d528221a1bc22ad..188e2c73a41aa1fd476475f74128b85b7383b09e 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -345,7 +345,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
 
     return True
 
-def get_ruv(realm, host, dirman_passwd, nolookup=False):
+def get_ruv(realm, host, dirman_passwd, nolookup=False, ca=False):
     """
     Return the RUV entries as a list of tuples: (hostname, rid)
     """
@@ -354,7 +354,10 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False):
         enforce_host_existence(host)
 
     try:
-        thisrepl = replication.ReplicationManager(realm, host, dirman_passwd)
+        if ca:
+            thisrepl = replication.get_cs_replication_manager(realm, host, dirman_passwd)
+        else:
+            thisrepl = replication.ReplicationManager(realm, host, dirman_passwd)
     except Exception as e:
         print("Failed to connect to server %s: %s" % (host, e))
         sys.exit(1)
@@ -362,7 +365,7 @@ def get_ruv(realm, host, dirman_passwd, nolookup=False):
     search_filter = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
     try:
         entries = thisrepl.conn.get_entries(
-            api.env.basedn, thisrepl.conn.SCOPE_SUBTREE, search_filter,
+            thisrepl.db_suffix, thisrepl.conn.SCOPE_SUBTREE, search_filter,
             ['nsds50ruv'])
     except errors.NotFound:
         print("No RUV records found.")
@@ -402,7 +405,7 @@ def get_rid_by_host(realm, sourcehost, host, dirman_passwd, nolookup=False):
         if '%s:389' % host == netloc:
             return int(rid)
 
-def clean_ruv(realm, ruv, options):
+def clean_ruv(realm, ruv, options, ca=False):
     """
     Given an RID create a CLEANALLRUV task to clean it up.
     """
@@ -412,7 +415,7 @@ def clean_ruv(realm, ruv, options):
         sys.exit("Replica ID must be an integer: %s" % ruv)
 
     servers = get_ruv(realm, options.host, options.dirman_passwd,
-                      options.nolookup)
+                      options.nolookup, ca=ca)
     found = False
     for (netloc, rid) in servers:
         if ruv == int(rid):
@@ -424,14 +427,21 @@ def clean_ruv(realm, ruv, options):
         sys.exit("Replica ID %s not found" % ruv)
 
     print("Clean the Replication Update Vector for %s" % hostname)
-    print()
-    print("Cleaning the wrong replica ID will cause that server to no")
-    print("longer replicate so it may miss updates while the process")
-    print("is running. It would need to be re-initialized to maintain")
-    print("consistency. Be very careful.")
-    if not options.force and not ipautil.user_input("Continue to clean?", False):
-        sys.exit("Aborted")
-    thisrepl = replication.ReplicationManager(realm, options.host,
+
+    if not options.force:
+        print()
+        print("Cleaning the wrong replica ID will cause that server to no")
+        print("longer replicate so it may miss updates while the process")
+        print("is running. It would need to be re-initialized to maintain")
+        print("consistency. Be very careful.")
+        if not ipautil.user_input("Continue to clean?", False):
+            sys.exit("Aborted")
+
+    if ca:
+        thisrepl = replication.get_cs_replication_manager(realm, options.host,
+                                                        options.dirman_passwd)
+    else:
+        thisrepl = replication.ReplicationManager(realm, options.host,
                                               options.dirman_passwd)
     thisrepl.cleanallruv(ruv)
     print("Cleanup task created")
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 19592e21f32b2013225036b3ce692f6cdee15a73..3221a1bd00bf9375d4348e5ba44d1645f0911b3e 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -1343,7 +1343,7 @@ class ReplicationManager(object):
             {
                 'objectclass': ['top', 'extensibleObject'],
                 'cn': ['clean %d' % replicaId],
-                'replica-base-dn': [api.env.basedn],
+                'replica-base-dn': [self.db_suffix],
                 'replica-id': [replicaId],
             }
         )
-- 
2.5.0

From 566cd6b4aca4164327391a63c42d20da4373b6b6 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 18 Dec 2015 10:34:52 +0100
Subject: [PATCH 2/2] Automatically detect and remove dangling RUVs

https://fedorahosted.org/freeipa/ticket/5411
---
 install/tools/ipa-replica-manage | 158 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)

diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index 188e2c73a41aa1fd476475f74128b85b7383b09e..b1f644b64a8c5f8612fd651af3b525d607c3062d 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -60,6 +60,7 @@ commands = {
     "clean-ruv":(1, 1, "Replica ID of to clean", "must provide replica ID to clean"),
     "abort-clean-ruv":(1, 1, "Replica ID to abort cleaning", "must provide replica ID to abort cleaning"),
     "list-clean-ruv":(0, 0, "", ""),
+    "clean-dangling-ruv":(0, 0, "", ""),
     "dnarange-show":(0, 1, "[master fqdn]", ""),
     "dnanextrange-show":(0, 1, "", ""),
     "dnarange-set":(2, 2, "<master fqdn> <range>", "must provide a master and ID range"),
@@ -528,6 +529,161 @@ def list_clean_ruv(realm, host, dirman_passwd, verbose, nolookup=False):
                 print(str(dn))
                 print(entry.single_value.get('nstasklog'))
 
+
+def clean_dangling_ruvs(realm, host, options):
+    """
+    Cleans all RUVs that are left in the system from uninstalled replicas
+    """
+    # get the Directory Manager password
+    if options.dirman_passwd:
+        dirman_passwd = options.dirman_passwd
+    else:
+        dirman_passwd = installutils.read_password('Directory Manager',
+            confirm=False, validate=False, retry=False)
+        if dirman_passwd is None:
+            sys.exit('Directory Manager password is required')
+
+    options.dirman_passwd = dirman_passwd
+
+    try:
+        conn = ipaldap.IPAdmin(host, 636, cacert=CACERT)
+        conn.do_simple_bind(bindpw=dirman_passwd)
+
+        # get all masters
+        masters_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+                        ipautil.realm_to_suffix(realm))
+        masters = conn.get_entries(masters_dn, conn.SCOPE_ONELEVEL)
+        info = dict()
+
+        # check whether CAs are configured on those masters
+        for master in masters:
+            info[master.single_value['cn']] = {
+                    'online': False, 'ca': False, 'ruvs': list(),
+                    'csruvs': list(), 'clean_ruv': list(),
+                    'clean_csruv': list()
+                    }
+            try:
+                ca_dn = DN(('cn', 'ca'), DN(master.dn))
+                entry = conn.get_entry(ca_dn)
+                info[master.single_value['cn']]['ca'] = True
+            except errors.NotFound:
+                continue
+
+    except Exception as e:
+        sys.exit(
+            "Failed to get data from '%s' while trying to list replicas: %s" %
+            (host, e)
+        )
+    finally:
+        conn.unbind()
+
+    # Get realm string for config tree
+    s = realm.split('.')
+    s = ['dc={dc},'.format(dc=x.lower()) for x in s]
+    realm_config = DN(('cn', ''.join(s)[0:-1]))
+
+    replica_dn = DN(('cn', 'replica'), realm_config,
+                    ('cn', 'mapping tree'), ('cn', 'config'))
+
+    csreplica_dn = DN(('cn', 'replica'), ('cn', 'o=ipaca'),
+                      ('cn', 'mapping tree'), ('cn', 'config'))
+
+    masters = [x.single_value['cn'] for x in masters]
+
+    ruvs = list()
+    csruvs = list()
+    offlines = list()
+    for master in masters:
+        try:
+            conn = ipaldap.IPAdmin(master, 636, cacert=CACERT)
+            conn.do_simple_bind(bindpw=dirman_passwd)
+            info[master]['online'] = True
+        except:
+            print("The server '%s' appears to be offline." % master)
+            offlines.append(master)
+            continue
+
+        try:
+            entry = conn.get_entry(replica_dn)
+            ruv = (master, entry.single_value.get('nsDS5ReplicaID'))
+            if ruv not in ruvs:
+                ruvs.append(ruv)
+
+            if(info[master]['ca']):
+                entry = conn.get_entry(csreplica_dn)
+                csruv = (master, entry.single_value.get('nsDS5ReplicaID'))
+                if csruv not in csruvs:
+                    csruvs.append(csruv)
+
+            # get_ruv returns server names with :port which needs to be split off
+            ruv_list = get_ruv(realm, master, dirman_passwd, options.nolookup)
+            info[master]['ruvs'] = [
+                (re.sub(':\d+', '', x), y)
+                for (x, y) in ruv_list
+                ]
+
+            ruv_list = get_ruv(realm, master, dirman_passwd, options.nolookup,
+                               ca=True)
+            info[master]['csruvs'] = [
+                (re.sub(':\d+', '', x), y)
+                for (x, y) in ruv_list
+                ]
+        except Exception as e:
+            sys.exit("Failed to obtain information from '%s': %s" %
+                     (master, str(e)))
+        finally:
+            conn.unbind()
+
+    clean_list = list()
+    dangles = False
+    # get the dangling RUVs
+    for master in masters:
+        if info[master]['online']:
+            for ruv in info[master]['ruvs']:
+                if (ruv not in ruvs) and (ruv[0] not in offlines):
+                    info[master]['clean_ruv'].append(ruv)
+                    dangles = True
+
+            if info[master]['ca']:
+                for csruv in info[master]['csruvs']:
+                    if (csruv not in csruvs) and (csruv[0] not in offlines):
+                        info[master]['clean_csruv'].append(csruv)
+                        dangles = True
+
+    if not dangles:
+        print('No dangling RUVs found')
+        sys.exit(0)
+
+    print('These RUVs are dangling and will be removed:')
+    for master in masters:
+        if info[master]['online'] and (info[master]['clean_ruv'] or
+                                       info[master]['clean_csruv']):
+            print('Host: {m}'.format(m=master))
+            print('\tRUVs:')
+            for ruv in info[master]['clean_ruv']:
+                print('\t\tid: {id}, hostname: {host}'.format(id=ruv[1], host=ruv[0]))
+
+            print('\tCS-RUVs:')
+            for csruv in info[master]['clean_csruv']:
+                print('\t\tid: {id}, hostname: {host}'.format(id=csruv[1], host=csruv[0]))
+
+    if not options.force and not ipautil.user_input("Proceed with cleaning?", False):
+        sys.exit("Aborted")
+
+    options.force = True
+    cleaned = list()
+    for master in masters:
+        options.host = master
+        for ruv in info[master]['clean_ruv']:
+            if ruv[1] not in cleaned:
+                cleaned.append(ruv[1])
+                clean_ruv(realm, ruv[1], options)
+        for csruv in info[master]['clean_csruv']:
+            if csruv[1] not in cleaned:
+                cleaned.append(csruv[1])
+                clean_ruv(realm, csruv[1], options, ca=True)
+
+
 def check_last_link(delrepl, realm, dirman_passwd, force):
     """
     We don't want to orphan a server when deleting another one. If you have
@@ -1460,6 +1616,8 @@ def main():
     elif args[0] == "list-clean-ruv":
         list_clean_ruv(realm, host, dirman_passwd, options.verbose,
                        options.nolookup)
+    elif args[0] == "clean-dangling-ruv":
+        clean_dangling_ruvs(realm, host, options)
     elif args[0] == "dnarange-show":
         if len(args) == 2:
             master = args[1]
-- 
2.5.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to