On 07/12/2012 07:46 AM, Martin Kosek wrote:
> On 07/11/2012 09:27 PM, Rob Crittenden wrote:
>> Martin Kosek wrote:
>>> IPA 3.0 introduced range ID objects in replicated space which specify
>>> a range of IDs assigned via DNA plugin. ipa-ldap-updater generates the
>>> default ID range which should correspond with IDs assigned to IPA
>>> users.
>>>
>>> However, since correct range size is not known, we should at least
>>> warn that a range with invalid size was created so that user can
>>> amend it.
>>>
>>>
>>> I created 2 new tickets to add further improve this area:
>>>
>>> 1) #2918: [doc] Upgrade procedure section should mention ipa-ldap-updater
>>> 2) #2919: Improve safety checks in range command
>>>
>>>
>>> To test this patch, you can:
>>> 1) Install unpatched IPA server (and you may install replicas too) with 
>>> custom
>>> --idstart and --idmax options where difference is greater then 200000
>>> 2) Remove default range with range-del command (will be restored during 
>>> upgrade)
>>> 3) Run RPM upgrade with RPMs built from patched sources - ERROR should now 
>>> be
>>> printed during update stating that a new range was created but its size is 
>>> not
>>> right
>>
>> I don't understand step 2, why would someone remove their range before 
>> upgrading?
>>
>> I installed with a 50k range, didn't remove it, then upgraded with no 
>> warning.
>> I deleted the range and re-installed the packages again, still no warning 
>> but a
>> new 200k range was created for me.
>>
>> rob
> 
> The step 2 is artificial and is only done to force the default_range update
> plugin to create/restore the default IPA range. The plugin would just be
> skipped otherwise.
> 
> We can only detect ranges larger than 200k - judging just from the number of
> free IDs. Thus, 50k range will pass without any warning or error. If you 
> create
> a bigger range (this can be detected unless you deplete all IDs below 200k
> mark), you will receive the warning. All this procedure will not handle all
> situations ATM, its just heuristics to cover most cases...
> 
> Martin

Sending an updated patch with 2 small changes:
1) Console error formatting was changed similar to ipa-client-install
2) ipa-ldap-updater does not print information message when IPA is not
configured to stderr so that rpm update output stays clean when updating rpms
in machine without IPA installed

This is the output of RPM with the new patch set:

# ipa range-del IDM.LAB.BOS.REDHAT.COM_id_range
--------------------------------------------------
Deleted ID range "IDM.LAB.BOS.REDHAT.COM_id_range"
--------------------------------------------------
# rpm -Uvh --force freeipa-*
Preparing...                ########################################### [100%]
   1:freeipa-python         ########################################### [ 14%]
   2:freeipa-client         ########################################### [ 29%]
   3:freeipa-admintools     ########################################### [ 43%]
   4:freeipa-server         ########################################### [ 57%]
   5:freeipa-server-selinux ########################################### [ 71%]
   6:freeipa-server-trust-ad########################################### [ 86%]
   7:freeipa-debuginfo      ########################################### [100%]
ERROR: default_range: could not verify default ID range size
Please use the following command to set correct ID range size
  $ ipa range-mod IDM.LAB.BOS.REDHAT.COM_id_range --range-size=RANGE_SIZE
RANGE_SIZE may be computed from --idstart and --idmax options used during IPA
server installation:
  RANGE_SIZE = (--idmax) - (--idstart) + 1

Martin
>From a61488b5fa77ed983c8de11d211ebb56d2337fee Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Wed, 11 Jul 2012 14:09:17 +0200
Subject: [PATCH 1/3] Add range-mod command

range plugin was missing range-mod command that could be used for
example to fix a size for a range generated during upgrades. The
range should be updated with a caution though, a misconfiguration
could break trusts.

iparangetype is now also handled better and filled in all commands
instead of just range-show. objectclass attribute is deleted only
when really needed now.
---
 API.txt                                |   19 +++++++++++++++
 VERSION                                |    2 +-
 ipalib/plugins/range.py                |   41 +++++++++++++++++++++++++++-----
 tests/test_xmlrpc/test_range_plugin.py |   23 ++++++++++++++++--
 4 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/API.txt b/API.txt
index 54313404142129a863792c67b706262973a268d6..691a9c4dec69f1006e52eafd3a94e351750165b7 100644
--- a/API.txt
+++ b/API.txt
@@ -2411,6 +2411,25 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
 output: Output('count', <type 'int'>, None)
 output: Output('truncated', <type 'bool'>, None)
+command: range_mod
+args: 1,13,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Int('ipabaseid', attribute=True, autofill=False, cli_name='base_id', multivalue=False, required=False)
+option: Int('ipaidrangesize', attribute=True, autofill=False, cli_name='range_size', multivalue=False, required=False)
+option: Int('ipabaserid', attribute=True, autofill=False, cli_name='rid_base', multivalue=False, required=False)
+option: Int('ipasecondarybaserid', attribute=True, autofill=False, cli_name='secondary_rid_base', multivalue=False, required=False)
+option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='dom_sid', multivalue=False, required=False)
+option: Str('iparangetype', attribute=True, autofill=False, cli_name='iparangetype', multivalue=False, required=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
 command: range_show
 args: 1,4,3
 arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
diff --git a/VERSION b/VERSION
index 542dd5fcaa6627711cb59f93d1a7585f6a02241a..8d9efe6573bd148f2e74961c6ce7247f9bc3393d 100644
--- a/VERSION
+++ b/VERSION
@@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=39
+IPA_API_VERSION_MINOR=40
diff --git a/ipalib/plugins/range.py b/ipalib/plugins/range.py
index 4448aad818ea4c9065c387e11035e165d52e4328..39849b661e35cc9092221b3d2bf7d13fb27f506c 100644
--- a/ipalib/plugins/range.py
+++ b/ipalib/plugins/range.py
@@ -80,6 +80,16 @@ class range(LDAPObject):
         )
     )
 
+    def handle_iparangetype(self, entry_attrs, options, keep_objectclass=False):
+        if not options.get('pkey_only', False):
+            if 'ipatrustedaddomainrange' in entry_attrs.get('objectclass', []):
+                entry_attrs['iparangetype'] = [unicode(_('Active Directory domain range'))]
+            else:
+                entry_attrs['iparangetype'] = [unicode(_(u'local domain range'))]
+        if not keep_objectclass:
+            if not options.get('all', False) or options.get('pkey_only', False):
+                entry_attrs.pop('objectclass', None)
+
 class range_add(LDAPCreate):
     __doc__ = _('Add new ID range.')
 
@@ -99,6 +109,10 @@ class range_add(LDAPCreate):
 
         return dn
 
+    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        self.obj.handle_iparangetype(entry_attrs, options, keep_objectclass=True)
+        return dn
+
 class range_del(LDAPDelete):
     __doc__ = _('Delete an ID range.')
 
@@ -114,8 +128,14 @@ class range_find(LDAPSearch):
     # Since all range types are stored within separate containers under
     # 'cn=ranges,cn=etc' search can be done on a one-level scope
     def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+        attrs_list.append('objectclass')
         return (filters, base_dn, ldap.SCOPE_ONELEVEL)
 
+    def post_callback(self, ldap, entries, truncated, *args, **options):
+        for dn,entry in entries:
+            self.obj.handle_iparangetype(entry, options)
+        return truncated
+
 class range_show(LDAPRetrieve):
     __doc__ = _('Display information about a range.')
 
@@ -124,16 +144,25 @@ class range_show(LDAPRetrieve):
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
-        if 'ipatrustedaddomainrange' in entry_attrs['objectclass']:
-            entry_attrs['iparangetype']=(u'Active Directory domain range')
-        else:
-            entry_attrs['iparangetype']=(u'local domain range')
-        del entry_attrs['objectclass']
+        self.obj.handle_iparangetype(entry_attrs, options)
+        return dn
+
+class range_mod(LDAPUpdate):
+    __doc__ = _('Modify ID range.')
+
+    msg_summary = _('Modified ID range "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        attrs_list.append('objectclass')
+        return dn
+
+    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        self.obj.handle_iparangetype(entry_attrs, options)
         return dn
 
 api.register(range)
 api.register(range_add)
-#api.register(range_mod)
+api.register(range_mod)
 api.register(range_del)
 api.register(range_find)
 api.register(range_show)
diff --git a/tests/test_xmlrpc/test_range_plugin.py b/tests/test_xmlrpc/test_range_plugin.py
index 7c95cd57a4a2fdd91862a7659cb2aac19c5edfcc..76ffc58b724e50529b8617f7d2d96923342eb0d8 100644
--- a/tests/test_xmlrpc/test_range_plugin.py
+++ b/tests/test_xmlrpc/test_range_plugin.py
@@ -49,7 +49,8 @@ class test_range(Declarative):
                     ipabaseid=[u'900000'],
                     ipabaserid=[u'1000'],
                     ipasecondarybaserid=[u'20000'],
-                    ipaidrangesize=[u'99999']
+                    ipaidrangesize=[u'99999'],
+                    iparangetype=[u'local domain range'],
                 ),
                 value=testrange1,
                 summary=u'Added ID range "%s"' % (testrange1),
@@ -69,11 +70,29 @@ class test_range(Declarative):
                     ipabaserid=[u'1000'],
                     ipasecondarybaserid=[u'20000'],
                     ipaidrangesize=[u'99999'],
-                    iparangetype=u'local domain range',
+                    iparangetype=[u'local domain range'],
                 ),
                 value=testrange1,
                 summary=None,
             ),
         ),
 
+
+        dict(
+            desc='Modify range %r' % (testrange1),
+            command=('range_mod', [testrange1], dict(ipaidrangesize=90000)),
+            expected=dict(
+                result=dict(
+                    cn=[testrange1],
+                    ipabaseid=[u'900000'],
+                    ipabaserid=[u'1000'],
+                    ipasecondarybaserid=[u'20000'],
+                    ipaidrangesize=[u'90000'],
+                    iparangetype=[u'local domain range'],
+                ),
+                value=testrange1,
+                summary=u'Modified ID range "%s"' % (testrange1),
+            ),
+        ),
+
     ]
-- 
1.7.10.4

>From eaacfa4b40929fe6ec0a156b39cbde0a9896c037 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Wed, 11 Jul 2012 16:13:25 +0200
Subject: [PATCH 2/3] Warn user if an ID range with incorrect size was created

IPA 3.0 introduced range ID objects in replicated space which specify
a range of IDs assigned via DNA plugin. ipa-ldap-updater generates the
default ID range which should correspond with IDs assigned to IPA
users.

However, since correct range size is not known, we should at least
warn that a range with invalid size was created so that user can
amend it.

https://fedorahosted.org/freeipa/ticket/2892
---
 ipalib/constants.py                  |    2 ++
 ipaserver/install/plugins/adtrust.py |   59 ++++++++++++++++++++++++++++++----
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/ipalib/constants.py b/ipalib/constants.py
index ad6d188692aa3de70cf29d5662d84672054ab4ef..f0f89a3b3ff51e06709809d4d606af96d13a7063 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -105,6 +105,8 @@ DEFAULT_CONFIG = (
     ('container_trusts', 'cn=trusts'),
     ('container_adtrusts', 'cn=ad,cn=trusts'),
     ('container_ranges', 'cn=ranges,cn=etc'),
+    ('container_dna', 'cn=dna,cn=ipa,cn=etc'),
+    ('container_dna_posix_ids', 'cn=posix-ids,cn=dna,cn=ipa,cn=etc'),
 
     # Ports, hosts, and URIs:
     # FIXME: let's renamed xmlrpc_uri to rpc_xml_uri
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index abd676a2bcbec57f59eb6710560281d945cf0cea..c32d82e3a8814840e75c74b86618e4098b2fb351 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -23,6 +23,8 @@ from ipalib import api, errors
 from ipalib.dn import DN
 from ipapython.ipa_log_manager import *
 
+DEFAULT_ID_RANGE_SIZE = 200000
+
 class update_default_range(PostUpdate):
     """
     Create default ID range for upgraded servers.
@@ -46,18 +48,19 @@ class update_default_range(PostUpdate):
         try:
             (dn, admins_entry) = ldap.get_entry(dn, ['gidnumber'])
         except errors.NotFound:
-            root_logger.error("No local ID range and no admins group found. "
-                              "Cannot create default ID range")
+            root_logger.error("default_range: No local ID range and no admins "
+                              "group found. Cannot create default ID range")
             return (False, False, [])
 
-        base_id = admins_entry['gidnumber'][0]
-        id_range_size = 200000
+        id_range_base_id = admins_entry['gidnumber'][0]
+        id_range_name = '%s_id_range' % api.env.realm
+        id_range_size = DEFAULT_ID_RANGE_SIZE
 
         range_entry = ['objectclass:top',
                        'objectclass:ipaIDrange',
                        'objectclass:ipaDomainIDRange',
-                       'cn:%s_id_range' % api.env.realm,
-                       'ipabaseid:%s' % base_id,
+                       'cn:%s' % id_range_name,
+                       'ipabaseid:%s' % id_range_base_id,
                        'ipaidrangesize:%s' % id_range_size,
                       ]
 
@@ -69,6 +72,50 @@ class update_default_range(PostUpdate):
         range_entry = map(str, range_entry)
         updates[dn] = {'dn' : dn, 'default' : range_entry}
 
+        # Default range entry has a hard-coded range size to 200000 which is
+        # a default range size in ipa-server-install. This could cause issues
+        # if user did not use a default range, but rather defined an own,
+        # bigger range (option --idmax).
+        # We should make our best to check if this is the case and provide
+        # user with an information how to fix it.
+        dn = str(DN(api.env.container_dna_posix_ids, api.env.basedn))
+        search_filter = "objectclass=dnaSharedConfig"
+        attrs = ['dnaHostname', 'dnaRemainingValues']
+        try:
+            (entries, truncated) = ldap.find_entries(search_filter, attrs, dn)
+        except errors.NotFound:
+            root_logger.warning("default_range: no dnaSharedConfig object found. "
+                                "Cannot check default range size.")
+        else:
+            masters = set()
+            remaining_values_sum = 0
+            for entry_dn, entry in entries:
+                hostname = entry.get('dnahostname', [None])[0]
+                if hostname is None or hostname in masters:
+                    continue
+                remaining_values = entry.get('dnaremainingvalues', [''])[0]
+                try:
+                    remaining_values = int(remaining_values)
+                except ValueError:
+                    root_logger.warning("default_range: could not parse "
+                        "remaining values from '%s'", remaining_values)
+                    continue
+                else:
+                    remaining_values_sum += remaining_values
+
+                masters.add(hostname)
+
+            if remaining_values_sum > DEFAULT_ID_RANGE_SIZE:
+                msg = ['could not verify default ID range size',
+                       'Please use the following command to set correct ID range size',
+                       '  $ ipa range-mod %s --range-size=RANGE_SIZE' % id_range_name,
+                       'RANGE_SIZE may be computed from --idstart and --idmax options '
+                       'used during IPA server installation:',
+                       '  RANGE_SIZE = (--idmax) - (--idstart) + 1'
+                      ]
+
+                root_logger.error("default_range: %s", "\n".join(msg))
+
         return (False, True, [updates])
 
 api.register(update_default_range)
-- 
1.7.10.4

>From 0ac610eef3f216b75c017d30e59f6468ca121fee Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Wed, 11 Jul 2012 16:22:13 +0200
Subject: [PATCH 3/3] Print ipa-ldap-updater errors during RPM upgrade

ipa-ldap-updater does a lot of essential LDAP changes and if it
fails, user may be surprised after the upgrade why things does not
work.

Modify ipa-ldap-updater to print ERROR logging messages by default
and modify RPM upgrade scriptlet to show these errors to user. Console
error messages are now formated in a more user-friendly way.

Information message stating that IPA is not configured and i.e. there
is nothing to be updated is not printer to stderr so that it does
not pop up for every freeipa-server package update when IPA is not
configured.

https://fedorahosted.org/freeipa/ticket/2892
---
 freeipa.spec.in                |    2 +-
 install/tools/ipa-ldap-updater |   16 ++++++++++++----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index deeb3341b9379a2cc82e50ca37889287c4c74813..7106310915c8a4e52a009036f7152a38a4c5f18d 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -465,7 +465,7 @@ fi
 %posttrans server
 # This must be run in posttrans so that updates from previous
 # execution that may no longer be shipped are not applied.
-/usr/sbin/ipa-ldap-updater --upgrade >/dev/null 2>&1 || :
+/usr/sbin/ipa-ldap-updater --upgrade >/dev/null || :
 
 %preun server
 if [ $1 = 0 ]; then
diff --git a/install/tools/ipa-ldap-updater b/install/tools/ipa-ldap-updater
index 197b840b07867269340f56e32989820d8af59ae1..d5cae32bd958db06a3fa2133bc5732fc17c3a9da 100755
--- a/install/tools/ipa-ldap-updater
+++ b/install/tools/ipa-ldap-updater
@@ -96,10 +96,15 @@ def main():
         run_plugins = True
 
     if os.getegid() == 0:
-        installutils.check_server_configuration()
+        try:
+            installutils.check_server_configuration()
+        except RuntimeError, e:
+            print unicode(e)
+            sys.exit()
     else:
         if not os.path.exists('/etc/ipa/default.conf'):
-            sys.exit("IPA is not configured on this system.")
+            print "IPA is not configured on this system."
+            sys.exit()
         if options.upgrade:
             sys.exit('Upgrade can only be done as root')
         if run_plugins:
@@ -115,10 +120,13 @@ def main():
             if dirman_password is None:
                 sys.exit("\nDirectory Manager password required")
 
+    console_format = '%(levelname)s: %(message)s'
     if options.upgrade:
-        standard_logging_setup('/var/log/ipaupgrade.log', verbose=True, debug=options.debug, filemode='a')
+        standard_logging_setup('/var/log/ipaupgrade.log', debug=options.debug,
+                console_format=console_format, filemode='a')
     else:
-        standard_logging_setup(None, verbose=True, debug=options.debug)
+        standard_logging_setup(None, console_format=console_format,
+                debug=options.debug)
 
     cfg = dict (
         in_server=True,
-- 
1.7.10.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to