From 2aa63fe4a98f6bae755b4ede607adc2068103c42 Mon Sep 17 00:00:00 2001 From: Martin Kosek Date: Mon, 3 Oct 2011 16:01:01 +0200 Subject: Improve handling of GIDs when migrating groups Since IPA v2 server already contain predefined groups that may collide with groups in migrated (IPA v1) server (for example admins, ipausers), users having colliding group as their primary group may happen to belong to an unknown group on new IPA v2 server. Implement --group-overwrite-gid option to overwrite GID of already existing groups to prevent this issue. https://fedorahosted.org/freeipa/ticket/1866 --- API.txt | 7 +++-- VERSION | 2 +- ipalib/plugins/migration.py | 73 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/API.txt b/API.txt index 10b3f86a8..77ff362f9 100644 --- a/API.txt +++ b/API.txt @@ -1701,9 +1701,9 @@ output: Output('summary', (, ), 'User-friendly output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', , "The primary_key value of the entry, e.g. 'jdoe' for a user") command: migrate_ds -args: 2,13,3 +args: 2,14,3 arg: Str('ldapuri', validate_ldapuri, cli_name='ldap_uri', label=Gettext('LDAP URI', domain='ipa', localedir=None)) -arg: Password('bindpw', cli_name='password', label=Gettext('Password', domain='ipa', localedir=None)) +arg: Password('bindpw', cli_name='password', confirm=False, label=Gettext('Password', domain='ipa', localedir=None)) option: Str('binddn?', autofill=True, cli_name='bind_dn', default=u'cn=directory manager', label=Gettext('Bind DN', domain='ipa', localedir=None)) option: Str('usercontainer?', autofill=True, cli_name='user_container', default=u'ou=people', label=Gettext('User container', domain='ipa', localedir=None)) option: Str('groupcontainer?', autofill=True, cli_name='group_container', default=u'ou=groups', label=Gettext('Group container', domain='ipa', localedir=None)) @@ -1713,8 +1713,9 @@ option: List('userignoreobjectclass?', autofill=True, cli_name='user_ignore_obje option: List('userignoreattribute?', autofill=True, cli_name='user_ignore_attribute', default=(), label=Gettext('Ignore user attribute', domain='ipa', localedir=None), multivalue=True) option: List('groupignoreobjectclass?', autofill=True, cli_name='group_ignore_objectclass', default=(), label=Gettext('Ignore group object class', domain='ipa', localedir=None), multivalue=True) option: List('groupignoreattribute?', autofill=True, cli_name='group_ignore_attribute', default=(), label=Gettext('Ignore group attribute', domain='ipa', localedir=None), multivalue=True) +option: Flag('groupoverwritegid', autofill=True, cli_name='group_overwrite_gid', default=False, label=Gettext('Overwrite GID', domain='ipa', localedir=None)) option: StrEnum('schema?', autofill=True, cli_name='schema', default=u'RFC2307bis', label=Gettext('LDAP schema', domain='ipa', localedir=None), values=(u'RFC2307bis', u'RFC2307')) -option: Flag('continue?', autofill=True, default=False) +option: Flag('continue?', autofill=True, default=False, label=Gettext('Continue', domain='ipa', localedir=None)) option: List('exclude_groups?', autofill=True, cli_name='exclude_groups', default=(), multivalue=True) option: List('exclude_users?', autofill=True, cli_name='exclude_users', default=(), multivalue=True) output: Output('result', , Gettext('Lists of objects migrated; categorized by type.', domain='ipa', localedir=None)) diff --git a/VERSION b/VERSION index 51711efe3..024fd8ebc 100644 --- a/VERSION +++ b/VERSION @@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=12 +IPA_API_VERSION_MINOR=13 diff --git a/ipalib/plugins/migration.py b/ipalib/plugins/migration.py index 93ac114d8..852cc9d64 100644 --- a/ipalib/plugins/migration.py +++ b/ipalib/plugins/migration.py @@ -71,6 +71,15 @@ EXAMPLES: Specify the user and group container. This can be used to migrate user and group data from an IPA v1 server: ipa migrate-ds --user-container='cn=users,cn=accounts' --group-container='cn=groups,cn=accounts' ldap://ds.example.com:389 + + Since IPA v2 server already contain predefined groups that may collide with + groups in migrated (IPA v1) server (for example admins, ipausers), users having + colliding group as their primary group may happen to belong to an unknown group + on new IPA v2 server. + Use --group-overwrite-gid option to overwrite GID of already existing groups + to prevent this issue: + ipa migrate-ds --group-overwrite-gid --user-container='cn=users,cn=accounts' --group-container='cn=groups,cn=accounts' ldap://ds.example.com:389 + """) # USER MIGRATION CALLBACKS AND VARS @@ -228,6 +237,28 @@ def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx, **kwarg return dn +def _group_exc_callback(ldap, dn, entry_attrs, exc, options): + if isinstance(exc, errors.DuplicateEntry): + if options.get('groupoverwritegid', False) and \ + entry_attrs.get('gidnumber') is not None: + try: + new_entry_attrs = {'gidnumber':entry_attrs['gidnumber']} + ldap.update_entry(dn, new_entry_attrs) + except errors.EmptyModlist: + # no change to the GID + pass + # mark as success + return + elif not options.get('groupoverwritegid', False) and \ + entry_attrs.get('gidnumber') is not None: + msg = unicode(exc) + # add information about possibility to overwrite GID + msg = msg + unicode(_('. Check GID of the existing group. ' \ + 'Use --group-overwrite-gid option to overwrite the GID')) + raise errors.DuplicateEntry(message=msg) + + raise exc + # DS MIGRATION PLUGIN def construct_filter(template, oc_list): @@ -252,6 +283,7 @@ class migrate_ds(Command): # pre_callback - is called for each object just after it was # retrieved from DS and before being added to IPA # post_callback - is called for each object after it was added to IPA + # exc_callback - is called when adding entry to IPA raises an exception # # {pre, post}_callback parameters: # ldap - ldap2 instance connected to IPA @@ -270,7 +302,8 @@ class migrate_ds(Command): 'oc_blacklist_option' : 'userignoreobjectclass', 'attr_blacklist_option' : 'userignoreattribute', 'pre_callback' : _pre_migrate_user, - 'post_callback' : _post_migrate_user + 'post_callback' : _post_migrate_user, + 'exc_callback' : None }, 'group': { 'filter_template' : '(&(|%s)(cn=*))', @@ -278,7 +311,8 @@ class migrate_ds(Command): 'oc_blacklist_option' : 'groupignoreobjectclass', 'attr_blacklist_option' : 'groupignoreattribute', 'pre_callback' : _pre_migrate_group, - 'post_callback' : None + 'post_callback' : None, + 'exc_callback' : _group_exc_callback, }, } migrate_order = ('user', 'group') @@ -292,6 +326,7 @@ class migrate_ds(Command): Password('bindpw', cli_name='password', label=_('Password'), + confirm=False, doc=_('bind password'), ), ) @@ -359,6 +394,12 @@ class migrate_ds(Command): default=tuple(), autofill=True, ), + Flag('groupoverwritegid', + cli_name='group_overwrite_gid', + label=_('Overwrite GID'), + doc=_('When migrating a group already existing in IPA domain overwrite the '\ + 'group GID and report as success'), + ), StrEnum('schema?', cli_name='schema', label=_('LDAP schema'), @@ -368,6 +409,7 @@ class migrate_ds(Command): autofill=True, ), Flag('continue?', + label=_('Continue'), doc=_('Continuous operation mode. Errors are reported but the process continues'), default=False, ), @@ -539,16 +581,25 @@ can use their Kerberos accounts.''') try: ldap.add_entry(dn, entry_attrs) except errors.ExecutionError, e: - failed[ldap_obj_name][pkey] = unicode(e) - else: - migrated[ldap_obj_name].append(pkey) - - callback = self.migrate_objects[ldap_obj_name]['post_callback'] + callback = self.migrate_objects[ldap_obj_name]['exc_callback'] if callable(callback): - callback( - ldap, pkey, dn, entry_attrs, failed[ldap_obj_name], - config, context - ) + try: + callback(ldap, dn, entry_attrs, e, options) + except errors.ExecutionError, e: + failed[ldap_obj_name][pkey] = unicode(e) + continue + else: + failed[ldap_obj_name][pkey] = unicode(e) + continue + + migrated[ldap_obj_name].append(pkey) + + callback = self.migrate_objects[ldap_obj_name]['post_callback'] + if callable(callback): + callback( + ldap, pkey, dn, entry_attrs, failed[ldap_obj_name], + config, context, + ) return (migrated, failed) -- cgit