diff options
-rw-r--r-- | API.txt | 7 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | ipalib/plugins/migration.py | 73 |
3 files changed, 67 insertions, 15 deletions
@@ -1701,9 +1701,9 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, "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', <type 'dict'>, Gettext('Lists of objects migrated; categorized by type.', domain='ipa', localedir=None)) @@ -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) |