From a3415e79d816df0668d4e982ea2645af80ec64f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Franti=C5=A1ek=20=C5=98ezn=C3=AD=C4=8Dek?=
 <246254@mail.muni.cz>
Date: Wed, 19 Jun 2024 20:37:37 +0200
Subject: [PATCH] fix: more robust source cloud keypair selection

This change is triggered by situations like following

2024-06-19 17:07:24,875 project-migrator INFO F.6 Destination OpenStack flavor exists
Traceback (most recent call last):
  File "/builds/cloud/g1-g2-ostack-cloud-migration/ci/project-migrator.py", line 389, in <module>
    sys.exit(main(ARGS))
  File "/builds/cloud/g1-g2-ostack-cloud-migration/ci/project-migrator.py", line 184, in main
    i_destination_server_keypair = olib.get_or_create_dst_server_keypair(args, source_keypairs,
  File "/builds/cloud/g1-g2-ostack-cloud-migration/ci/olib.py", line 342, in get_or_create_dst_server_keypair
    log_or_assert(args,
  File "/builds/cloud/g1-g2-ostack-cloud-migration/ci/lib.py", line 218, in log_or_assert
    assert condition, "\n".join([msg, msg_guidance]) if msg_guidance else msg
AssertionError: F.7 Source OpenStack server keypair found (jhandl).
Current source OpenStack cloud keypair dump is outdated already and does not contain mentioned keypair.Re-dump source OpenStack keypairs to ceph migrator server node and retry migration.

Looking into source keypair data it looks that keypair jhandl is present but under different user-id
---
 CHANGELOG.md           |  4 ++++
 ci/olib.py             | 36 +++++++++++++++++++++++++++++++-----
 ci/project-migrator.py |  4 ++--
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15afa2c..314957e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+## [1.2.2] - 2024-06-19
+### Fixed
+- more robust selection of the source cloud keypair (search with key_name+user_id and then with key_name only)
+
 ## [1.2.1] - 2024-06-11
 ### Fixed
 - corrected argument/switch name from --destination-secgroup-entity-name-prefix to --destination-secgroup-name-prefix
diff --git a/ci/olib.py b/ci/olib.py
index 1da2a1c..f3e96a2 100644
--- a/ci/olib.py
+++ b/ci/olib.py
@@ -311,8 +311,8 @@ def get_dst_server_flavor_name_noassert(args, src_server, dst_ostack_conn):
     return destination_server_flavor_name
 
 
-def get_source_keypairs(args):
-    """ receive source openstack keypairs from ceph migrator host as xml formatted sql dump """
+def download_source_keypairs(args):
+    """ download/receive source openstack keypairs from ceph migrator host as xml formatted sql dump """
     reply_stdout, _, reply_ecode = remote_cmd_exec(args.ceph_migrator_host,
                                                    args.ceph_migrator_user,
                                                    args.ceph_migrator_sshkeyfile.name,
@@ -329,6 +329,13 @@ def get_source_keypair(keypairs, keypair_name, user_id):
         return keypairs_selected[0]
     return None
 
+def get_source_keypairs(keypairs, keypair_name, user_id=None):
+    """ get specific source cloud keypairs based on keypair_name and optionally user_id """
+    if user_id:
+        return [ i_keypair for i_keypair in keypairs if i_keypair.get("name", "") == keypair_name and i_keypair.get("user_id", "") == user_id ]
+    return [ i_keypair for i_keypair in keypairs if i_keypair.get("name", "") == keypair_name ]
+
+
 def create_keypair(args, ostack_connection, keypair):
     """ create openstack keypair object """
     return ostack_connection.compute.create_keypair(name=get_dst_resource_name(args, keypair['name']),
@@ -336,9 +343,28 @@ def create_keypair(args, ostack_connection, keypair):
 
 def get_or_create_dst_server_keypair(args, source_keypairs, src_server, dst_ostack_conn):
     """ assure destination cloud keypair exists """
-    source_server_keypair = get_source_keypair(source_keypairs,
-                                               src_server.key_name,
-                                               src_server.user_id)
+    # select keypairs based on key_name and user_id
+    source_server_keypair = None
+    source_server_keypairs = get_source_keypairs(source_keypairs,
+                                                 src_server.key_name,
+                                                 src_server.user_id)
+    if not(source_server_keypairs):
+        args.logger.warning(f"F.7 No source keypair found when searching with (key_name={src_server.key_name}, used_id={src_server.user_id}). " \
+                            "Retrying with key_name only.")
+        # select keypairs based on key_name only
+        source_server_keypairs = get_source_keypairs(source_keypairs, src_server.key_name)
+        log_or_assert(args,
+                      f"F.7 Single (unambiguous) Source OpenStack server keypair found ({src_server.key_name}) when searching with key_name only.",
+                      len(source_server_keypairs) == 1,
+                      msg_guidance="We encountered situation when we are unable to detect source keypair. Search with (key_name, used_id) returned " \
+                                   "no result and search with key_name only returned multiple results. " \
+                                   "Most likely it is necessary to reimplement olib.get_or_create_dst_server_keypair().")
+    else:
+        source_server_keypair = source_server_keypairs[0]
+        if len(source_server_keypairs) > 1:
+            args.logger.warning(f"F.7 Multiple source keypairs found when searching with (key_name={src_server.key_name}, used_id={src_server.user_id}). " \
+                                 "Picking the first detected keypair.")
+
     log_or_assert(args,
                   f"F.7 Source OpenStack server keypair found ({src_server.key_name}).",
                   source_server_keypair,
diff --git a/ci/project-migrator.py b/ci/project-migrator.py
index a9e4fc7..b09af45 100755
--- a/ci/project-migrator.py
+++ b/ci/project-migrator.py
@@ -100,8 +100,8 @@ def main(args):
         source_rbd_images[i_pool_name] = clib.ceph_rbd_images_list(args, i_pool_name)
         lib.log_or_assert(args, f"D.03 Source cloud RBD images are received ({i_pool_name}).", source_rbd_images[i_pool_name])
 
-    source_keypairs = olib.get_source_keypairs(args)
-    lib.log_or_assert(args, "D.04 Source OpenStack cloud keypairs received.", source_keypairs)
+    source_keypairs = olib.download_source_keypairs(args)
+    lib.log_or_assert(args, "D.04 Source OpenStack cloud keypairs received/downloaded.", source_keypairs)
 
     source_objstore_containers = olib.get_ostack_objstore_containers(source_project_conn)
     if source_objstore_containers:
-- 
GitLab