Blob Blame History Raw
From aab181f0a952a56034278b13b1fdc224132d66f4 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 7 Feb 2012 15:26:00 -0500
Subject: [PATCH] Add tool to add memberOf to replication attribute exclusion
 list

We were creating replication agreements without excluding memberOf
which could cause unnecessary replication traffic.

https://fedorahosted.org/freeipa/ticket/2218
---
 install/tools/Makefile.am          |    1 +
 install/tools/ipa-fixreplica       |  129 ++++++++++++++++++++++++++++++++++++
 install/tools/man/Makefile.am      |    1 +
 install/tools/man/ipa-fixreplica.1 |   51 ++++++++++++++
 ipaserver/install/replication.py   |   29 +++++----
 6 files changed, 203 insertions(+), 12 deletions(-)
 create mode 100755 install/tools/ipa-fixreplica
 create mode 100644 install/tools/man/ipa-fixreplica.1

diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 7e071af..cf4d7fd 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -12,6 +12,7 @@ sbin_SCRIPTS =			\
 	ipa-replica-install	\
 	ipa-replica-prepare	\
 	ipa-replica-manage	\
+	ipa-fixreplica		\
 	ipa-csreplica-manage	\
  	ipa-server-certinstall  \
 	ipactl			\
diff --git a/install/tools/ipa-fixreplica b/install/tools/ipa-fixreplica
new file mode 100755
index 0000000..e444853
--- /dev/null
+++ b/install/tools/ipa-fixreplica
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+# Authors: Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2012  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+import os
+import pwd
+try:
+    from optparse import OptionParser
+    from ipapython import ipautil, config
+    from ipaserver import ipaldap
+    from ipaserver.install import installutils, replication
+    from ipaserver.plugins.ldap2 import ldap2
+    from ipalib import api, errors
+except ImportError:
+    print >> sys.stderr, """\
+There was a problem importing one of the required Python modules. The
+error was:
+
+    %s
+""" % sys.exc_value
+    sys.exit(1)
+
+def parse_options():
+    usage = "%prog [options]\n"
+    parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+
+    parser.add_option("-t", "--test", action="store_true", dest="test",
+                      help="Run in test mode, no changes are applied")
+    parser.add_option("-d", "--debug", action="store_true", dest="debug",
+                      help="Display debugging information about the update(s)")
+    parser.add_option("-y", dest="password",
+                      help="File containing the Directory Manager password")
+
+    options, args = parser.parse_args()
+
+    return options, args
+
+def get_dirman_password():
+    """Prompt the user for the Directory Manager password and verify its
+       correctness.
+    """
+    password = installutils.read_password("Directory Manager", confirm=False, validate=False)
+
+    return password
+
+def main():
+    retval = 0
+
+    options, args = parse_options()
+
+    if os.getegid() == 0:
+     installutils.standard_logging_setup("/var/log/ipaserver-fixreplica.log", options.debug, filemode='a')
+
+    api.bootstrap(context='cli', debug=options.debug)
+    api.finalize()
+
+    conn = None
+    dirman_password = ""
+    if os.getegid() == 0:
+        conn = ipaldap.IPAdmin(api.env.host, ldapi=True, realm=api.env.realm)
+        conn.do_external_bind(pwd.getpwuid(os.geteuid()).pw_name)
+    else:
+        if options.password:
+            pw = ipautil.template_file(options.password, [])
+            dirman_password = pw.strip()
+        else:
+            dirman_password = get_dirman_password()
+            if dirman_password is None:
+                sys.exit("\nDirectory Manager password required")
+
+    repl = replication.ReplicationManager(api.env.realm, api.env.host,
+                                          dirman_password, conn=conn)
+    entries = repl.find_replication_agreements()
+    print "Found %d agreement(s)" % len(entries)
+    for replica in entries:
+        print "%s: " % replica.description
+        if 'memberof' not in replica.nsDS5ReplicatedAttributeList:
+            print "    Attribute list needs updating"
+            current = replica.toDict()
+            replica.setValue('nsDS5ReplicatedAttributeList',
+                replica.nsDS5ReplicatedAttributeList + ' memberof')
+            if not options.test:
+                try:
+                    repl.conn.updateEntry(replica.dn, current, replica.toDict())
+                    print "    Updated"
+                except Exception, e:
+                    print "Error caught updating replica: %s" % str(e)
+                    retval = 1
+            else:
+                print "    Test mode, not updating"
+                retval = 2
+        else:
+            print "    Attribute list ok"
+
+    return retval
+
+try:
+    if __name__ == "__main__":
+        sys.exit(main())
+except RuntimeError, e:
+    print "%s" % e
+    sys.exit(1)
+except SystemExit, e:
+    sys.exit(e)
+except KeyboardInterrupt, e:
+    sys.exit(1)
+except config.IPAConfigError, e:
+    print "IPA replica not configured."
+    sys.exit(0)
+except errors.LDAPError, e:
+    print "An error occurred while performing operations: %s" % e
+    sys.exit(1)
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index 43dc57f..fd219f1 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -6,6 +6,7 @@ NULL=

 man1_MANS = 				\
 	ipa-replica-conncheck.1		\
+	ipa-fixreplica.1		\
 	ipa-replica-install.1		\
 	ipa-replica-manage.1		\
 	ipa-csreplica-manage.1		\
diff --git a/install/tools/man/ipa-fixreplica.1 b/install/tools/man/ipa-fixreplica.1
new file mode 100644
index 0000000..9af72cc
--- /dev/null
+++ b/install/tools/man/ipa-fixreplica.1
@@ -0,0 +1,51 @@
+.\" A man page for ipa-fixreplica
+.\" Copyright (C) 2012 Red Hat, Inc.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation, either version 3 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
+.\"
+.\" Author: Rob Crittenden <rcritten@redhat.com>
+.\"
+.TH "ipa-fixreplica" "1" "Jan 6 2012" "FreeIPA" "FreeIPA Manual Pages"
+.SH "NAME"
+ipa\-fixreplica \- Add memberOf as excluded attribute to replication agreements
+.SH "SYNOPSIS"
+ipa\-fixreplica [options]
+.SH "DESCRIPTION"
+The memberOf attribute was not properly added to some replication agreements
+causing unnecessary replication. This tool will update all replication
+agreements for the current host and add memberOf to the exclusion list.
+
+When executed as root no password is required, otherwise it will prompt
+for the Directory Manager password.
+
+This is executed when the package is updated in rpm so should not need
+to be run by an end-user. It is not harmful to run multiple times.
+
+This needs to be run on all replicas to be truly effective.
+.SH "OPTIONS"
+.TP
+\fB\-d\fR, \fB\-\-debug\fR
+Enable debug logging when more verbose output is needed
+.TP
+\fB\-t\fR, \fB\-\-test\fR
+Run in test mode, no changes are applied
+.TP
+\fB\-y\fR \fIfile\fR
+File containing the Directory Manager password
+.SH "EXIT STATUS"
+0 if the command was successful or IPA is not configured
+
+1 if an error occurred
+
+2 If during test mode it determines that there are agreements that need updating
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 8c8502e..79c0fe7 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -99,7 +99,7 @@ def enable_replication_version_checking(hostname, realm, dirman_passwd):
 class ReplicationManager(object):
     """Manage replication agreements between DS servers, and sync
     agreements with Windows servers"""
-    def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False):
+    def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, conn=None):
         self.hostname = hostname
         self.port = port
         self.dirman_passwd = dirman_passwd
@@ -108,18 +108,23 @@ class ReplicationManager(object):
         tmp = util.realm_to_suffix(realm)
         self.suffix = str(DN(tmp)).lower()

-        # If we are passed a password we'll use it as the DM password
-        # otherwise we'll do a GSSAPI bind.
-        if starttls:
-            self.conn = ipaldap.IPAdmin(hostname, port=port)
-            ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
-            self.conn.start_tls_s()
-        else:
-            self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT)
-        if dirman_passwd:
-            self.conn.do_simple_bind(bindpw=dirman_passwd)
+        # The caller is allowed to pass in an existing IPAdmin connection.
+        # Open a new one if not provided
+        if conn is None:
+            # If we are passed a password we'll use it as the DM password
+            # otherwise we'll do a GSSAPI bind.
+            if starttls:
+                self.conn = ipaldap.IPAdmin(hostname, port=port)
+                ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
+                self.conn.start_tls_s()
+            else:
+                self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT)
+            if dirman_passwd:
+                self.conn.do_simple_bind(bindpw=dirman_passwd)
+            else:
+                self.conn.do_sasl_gssapi_bind()
         else:
-            self.conn.do_sasl_gssapi_bind()
+            self.conn = conn

         self.repl_man_passwd = dirman_passwd

--
1.7.6