12684: Support num_retries in PySDK client constructors
[arvados.git] / sdk / python / arvados / commands / federation_migrate.py
index 35655cc0ea1e3f40253e3e891d313377e120f7cd..32b3211f14c27b968c6396d46c1b778c96072ea8 100755 (executable)
@@ -22,7 +22,9 @@ import hmac
 import urllib.parse
 import os
 import hashlib
+import re
 from arvados._version import __version__
+from . import _util as arv_cmd
 
 EMAIL=0
 USERNAME=1
@@ -42,10 +44,10 @@ def connect_clusters(args):
                 host = r[0]
                 token = r[1]
                 print("Contacting %s" % (host))
-                arv = arvados.api(host=host, token=token, cache=False)
+                arv = arvados.api(host=host, token=token, cache=False, num_retries=args.retries)
                 clusters[arv._rootDesc["uuidPrefix"]] = arv
     else:
-        arv = arvados.api(cache=False)
+        arv = arvados.api(cache=False, num_retries=args.retries)
         rh = arv._rootDesc["remoteHosts"]
         tok = arv.api_client_authorizations().current().execute()
         token = "v2/%s/%s" % (tok["uuid"], tok["api_token"])
@@ -294,7 +296,7 @@ def activate_remote_user(args, email, homearv, migratearv, old_user_uuid, new_us
             return None
 
     if olduser["is_admin"] and not newuser["is_admin"]:
-        print("(%s) Not migrating %s because user is admin but target user %s is not admin on %s" % (email, old_user_uuid, new_user_uuid, migratecluster))
+        print("(%s) Not migrating %s because user is admin but target user %s is not admin on %s. Please ensure the user admin status is the same on both clusters. Note that a federated admin account has admin privileges on the entire federation." % (email, old_user_uuid, new_user_uuid, migratecluster))
         return None
 
     return newuser
@@ -304,7 +306,7 @@ def migrate_user(args, migratearv, email, new_user_uuid, old_user_uuid):
         return
     try:
         new_owner_uuid = new_user_uuid
-        if not args.avoid_subproject_creation:
+        if args.data_into_subproject:
             grp = migratearv.groups().create(body={
                 "owner_uuid": new_user_uuid,
                 "name": "Migrated from %s (%s)" % (email, old_user_uuid),
@@ -316,16 +318,24 @@ def migrate_user(args, migratearv, email, new_user_uuid, old_user_uuid):
                                     new_owner_uuid=new_owner_uuid,
                                     redirect_to_new_user=True).execute()
     except arvados.errors.ApiError as e:
-        print("(%s) Skipping user migration because of error: %s" % (email, e))
+        name_collision = re.search(r'Key \(owner_uuid, name\)=\((.*?), (.*?)\) already exists\.\n.*UPDATE "(.*?)"', e._get_reason())
+        if name_collision:
+            target_owner, rsc_name, rsc_type = name_collision.groups()
+            print("(%s) Cannot migrate to %s because both origin and target users have a %s named '%s'. Please rename the conflicting items or use --data-into-subproject to migrate all users' data into a special subproject." % (email, target_owner, rsc_type[:-1], rsc_name))
+        else:
+            print("(%s) Skipping user migration because of error: %s" % (email, e))
 
 
 def main():
-    parser = argparse.ArgumentParser(description='Migrate users to federated identity, see https://doc.arvados.org/admin/merge-remote-account.html')
+    parser = argparse.ArgumentParser(
+        description='Migrate users to federated identity, see https://doc.arvados.org/admin/merge-remote-account.html',
+        parents=[arv_cmd.retry_opt],
+    )
     parser.add_argument(
         '--version', action='version', version="%s %s" % (sys.argv[0], __version__),
         help='Print version and exit.')
     parser.add_argument('--tokens', type=str, metavar='FILE', required=False, help="Read tokens from FILE. Not needed when using LoginCluster.")
-    parser.add_argument('--avoid-subproject-creation', action="store_true", help="Don't migrate user's data into a separate subproject. This may cause collection or project name collisions from within an account.")
+    parser.add_argument('--data-into-subproject', action="store_true", help="Migrate user's data into a separate subproject. This can be used to avoid name collisions from within an account.")
     group = parser.add_mutually_exclusive_group(required=True)
     group.add_argument('--report', type=str, metavar='FILE', help="Generate report .csv file listing users by email address and their associated Arvados accounts.")
     group.add_argument('--migrate', type=str, metavar='FILE', help="Consume report .csv and migrate users to designated Arvados accounts.")