summaryrefslogtreecommitdiffstats
path: root/ipa-server
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2008-09-12 18:40:26 -0400
committerRob Crittenden <rcritten@redhat.com>2008-09-12 20:07:48 -0400
commit88960f1597eb4516186bebd1aa624806c55895c1 (patch)
tree7ff4e42741a89e6fedc824118bc1974be3e292f3 /ipa-server
parent1eec34393b8a7ccd420a7fa540462f5d8779977c (diff)
downloadfreeipa-88960f1597eb4516186bebd1aa624806c55895c1.tar.gz
freeipa-88960f1597eb4516186bebd1aa624806c55895c1.tar.xz
freeipa-88960f1597eb4516186bebd1aa624806c55895c1.zip
Sort updates by DN length and by default process all files in the updates dir.
The updates directory is currently hardcoded to /usr/share/ipa/updates. All of the files are read into memory and then sorted by the length of the DN. This is so we can be sure that parent entries are added before children. Also add a man page.
Diffstat (limited to 'ipa-server')
-rwxr-xr-xipa-server/ipa-ldap-updater103
-rw-r--r--ipa-server/ipa-server.spec.in1
-rw-r--r--ipa-server/man/Makefile.am3
-rw-r--r--ipa-server/man/ipa-ldap-updater.173
4 files changed, 157 insertions, 23 deletions
diff --git a/ipa-server/ipa-ldap-updater b/ipa-server/ipa-ldap-updater
index cd6cd2eee..86a9fe9b1 100755
--- a/ipa-server/ipa-ldap-updater
+++ b/ipa-server/ipa-ldap-updater
@@ -23,6 +23,8 @@
# TODO
# save undo files?
+UPDATES_DIR="/usr/share/ipa/updates/"
+
import sys
try:
from optparse import OptionParser
@@ -37,6 +39,8 @@ try:
import shlex
import time
import random
+ import os
+ import fnmatch
except ImportError:
print >> sys.stderr, """\
There was a problem importing one of the required Python modules. The
@@ -57,7 +61,9 @@ class BadSyntax(Exception):
return repr(self.value)
def parse_options():
- parser = OptionParser("%prog [options] input_file(s)")
+ usage = "%prog [options] input_file(s)\n"
+ usage += "%prog [options]\n"
+ parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
@@ -67,8 +73,6 @@ def parse_options():
config.add_standard_options(parser)
options, args = parser.parse_args()
- if len(args) < 1:
- parser.error("missing file operand")
config.init_config(options)
return options, args
@@ -186,7 +190,38 @@ def entry_to_entity(ent):
entry[key] = value[0]
return entity.Entity(entry)
-def parse_update_file(conn, data):
+def combine_updates(dn_list, all_updates, update):
+ """Combine a new update with the list of total updates
+
+ Updates are stored in 2 lists:
+ dn_list: contains a unique list of DNs in the updates
+ all_updates: the actual updates that need to be applied
+
+ We want to apply the updates from the shortest to the longest
+ path so if new child and parent entries are in different updates
+ we can be sure the parent gets written first. This also lets
+ us apply any schema first since it is in the very short cn=schema.
+ """
+ dn = update.get('dn')
+ dns = ldap.explode_dn(dn.lower())
+ l = len(dns)
+ if dn_list.get(l):
+ if dn not in dn_list[l]:
+ dn_list[l].append(dn)
+ else:
+ dn_list[l] = [dn]
+ if not all_updates.get(dn):
+ all_updates[dn] = update
+ return all_updates
+
+ e = all_updates[dn]
+ e['updates'] = e['updates'] + update['updates']
+
+ all_updates[dn] = e
+
+ return all_updates
+
+def parse_update_file(conn, data, all_updates, dn_list):
"""Parse the update file into a dictonary of lists and apply the update
for each DN in the file."""
valid_keywords = ["default", "add", "remove", "only"]
@@ -203,7 +238,7 @@ def parse_update_file(conn, data):
if line.lower().startswith('dn:'):
if dn is not None:
- update_record(conn, update)
+ all_updates = combine_updates(dn_list, all_updates, update)
update = {}
dn = line[3:].strip()
@@ -246,9 +281,9 @@ def parse_update_file(conn, data):
update[index] = d
if dn is not None:
- update_record(conn, update)
+ all_updates = combine_updates(dn_list, all_updates, update)
- return
+ return (all_updates, dn_list)
def create_index_task(conn, attribute):
"""Create a task to update an index for an attribute"""
@@ -487,6 +522,17 @@ def update_record(conn, update):
monitor_index_task(conn, id)
return
+def get_all_files(root, recursive=False):
+ """Get all update files"""
+ f = []
+ for path, subdirs, files in os.walk(root):
+ for name in files:
+ if fnmatch.fnmatch(name, "*.update"):
+ f.append(os.path.join(path, name))
+ if not recursive:
+ break
+ return f
+
def main():
global sub_dict, live_run
loglevel = logging.INFO
@@ -521,22 +567,35 @@ def main():
dirman_password = get_dirman_password(fqdn)
- for a in args:
- try:
- logging.info("Parsing file %s" % a)
- data = read_file(a)
- except Exception, e:
- print e
- sys.exit(1)
-
- conn = None
+ files=[]
+ if len(args) < 1:
+ files = get_all_files(UPDATES_DIR)
+ else:
+ files = args
- try:
- conn = ipaldap.IPAdmin(fqdn)
- conn.do_simple_bind(bindpw=dirman_password)
- parse_update_file(conn, data)
- finally:
- if conn: conn.unbind()
+ conn = None
+ try:
+ conn = ipaldap.IPAdmin(fqdn)
+ conn.do_simple_bind(bindpw=dirman_password)
+ all_updates = {}
+ dn_list = {}
+ for f in files:
+ try:
+ logging.info("Parsing file %s" % f)
+ data = read_file(f)
+ except Exception, e:
+ print e
+ sys.exit(1)
+
+ (all_updates, dn_list) = parse_update_file(conn, data, all_updates, dn_list)
+
+ sortedkeys = dn_list.keys()
+ sortedkeys.sort()
+ for k in sortedkeys:
+ for dn in dn_list[k]:
+ update_record(conn, all_updates[dn])
+ finally:
+ if conn: conn.unbind()
return
diff --git a/ipa-server/ipa-server.spec.in b/ipa-server/ipa-server.spec.in
index ecb27eae9..35bba9be6 100644
--- a/ipa-server/ipa-server.spec.in
+++ b/ipa-server/ipa-server.spec.in
@@ -174,6 +174,7 @@ fi
%{_mandir}/man1/ipa-replica-prepare.1.gz
%{_mandir}/man1/ipa-server-certinstall.1.gz
%{_mandir}/man1/ipa-server-install.1.gz
+%{_mandir}/man1/ipa-ldap-updater.1.gz
%changelog
* Tue Sep 9 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-5
diff --git a/ipa-server/man/Makefile.am b/ipa-server/man/Makefile.am
index 0398dfdd1..40228251a 100644
--- a/ipa-server/man/Makefile.am
+++ b/ipa-server/man/Makefile.am
@@ -9,7 +9,8 @@ man1_MANS = \
ipa-replica-manage.1 \
ipa-replica-prepare.1 \
ipa-server-certinstall.1 \
- ipa-server-install.1
+ ipa-server-install.1 \
+ ipa-ldap-updater.1
man8_MANS = \
ipactl.8 \
diff --git a/ipa-server/man/ipa-ldap-updater.1 b/ipa-server/man/ipa-ldap-updater.1
new file mode 100644
index 000000000..583f8931d
--- /dev/null
+++ b/ipa-server/man/ipa-ldap-updater.1
@@ -0,0 +1,73 @@
+.\" A man page for ipa-ldap-updater
+.\" Copyright (C) 2008 Red Hat, Inc.
+.\"
+.\" This is free software; you can redistribute it and/or modify it under
+.\" the terms of the GNU Library General Public License as published by
+.\" the Free Software Foundation; version 2 only
+.\"
+.\" 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 Library General Public
+.\" License along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\" Author: Rob Crittenden <rcritten@redhat.com>
+.\"
+.TH "ipa-ldap-updater" "1" "Sep 12 2008" "freeipa" ""
+.SH "NAME"
+ipa\-ldap\-updater \- Update the IPA LDAP configuration
+.SH "SYNOPSIS"
+ipa\-ldap\-updater [options] input_file(s)
+ipa\-ldap\-updater [options]
+.SH "DESCRIPTION"
+Run with no file arguments, ipa\-ldap\-updater will process all files with the extension .update in /usr/share/ipa/updates.
+
+An update file describes an LDAP entry and a set of operations to be performed on that entry. It can be used to add new entries or modify existing entries. It cannot remove entries, just specific values in a given attribute.
+
+Blank lines and lines beginning with # are ignored.
+
+There are 4 keywords:
+
+ * default: the starting value
+ * add: add a value (or values) to an attribute
+ * remove: remove a value (or values) from an attribute
+ * only: set an attribute to this
+
+Values is a comma\-separated field so multi\-values may be added at one time. Double or single quotes may be put around individual values that contain embedded commas.
+
+The difference between the default and add keywords is if the DN of the entry exists then default is ignored. So for updating something like schema, which will be under cn=schema, you must always use add (because cn=schema is guaranteed to exist). It will not re\-add the same information again and again.
+
+It alsos provide some things that can be templated such as architecture (for plugin paths), realm and domain name.
+
+The available template variables are:
+
+ * $REALM \- the kerberos realm (EXAMPLE.COM)
+ * $FQDN \- the fully\-qualified domain name of the IPA server being updated (ipa.example.com)
+ * $DOMAIN \- the domain name (example.com)
+ * $SUFFIX \- the IPA LDAP suffix (dc=example,dc=com)
+ * $LIBARCH \- set to 64 on x86_64 systems to be used for plugin paths
+ * $TIME \- an integer representation of current time
+
+A few rules:
+
+ 1. Only one rule per line
+ 2. Each line stands alone (e.g. an only followed by an only results in the last only being used)
+ 3. adding a value that exists is ok. The request is ignored, duplicate values are not added
+ 4. removing a value that doesn't exist is ok. It is simply ignored.
+ 5. If a DN doesn't exist it is created from the 'default' entry and all updates are applied
+ 6. If a DN does exist the default values are skipped
+ 7. Only the first rule on a line is respected
+.SH "OPTIONS"
+.TP
+\fB\-d\fR, \fB\-\-debug
+Enable debug logging when more verbose output is needed
+.TP
+\fB\-t\fR, \fB\-\-test\fR
+Run through the update without changing anything
+.SH "EXIT STATUS"
+0 if the command was successful
+
+1 if an error occurred