Repository: rucio/rucio

Issue:
Dark reaper crashes due to None scope
Motivation
----------
The dark-reaper crashes with

```
2019-08-29 13:56:57,631 18591   CRITICAL        Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/rucio/daemons/reaper/dark_reaper.py", line 106, in reaper
    lfns=[{'scope': replica['scope'].external, 'name': replica['name'], 'path': replica['path']}],
AttributeError: 'NoneType' object has no attribute 'external'
```

This is due to the fact that for quarantined replicas the scope (and name) can sometimes be NULL. But due to the InternalScope change where the external is forcefully read, the daemon crashes. This needs to be adapted to handle NULL scopes.


Discussion:

Resolving patch:
diff --git a/lib/rucio/daemons/reaper/dark_reaper.py b/lib/rucio/daemons/reaper/dark_reaper.py
--- a/lib/rucio/daemons/reaper/dark_reaper.py
+++ b/lib/rucio/daemons/reaper/dark_reaper.py
@@ -18,6 +18,7 @@
 # - Thomas Beermann <thomas.beermann@cern.ch>, 2016-2019
 # - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
 # - Andrew Lister <andrew.lister@stfc.ac.uk>, 2019
+# - Cedric Serfon <cedric.serfon@cern.ch>, 2020
 #
 # PY3K COMPATIBLE
 
@@ -101,16 +102,19 @@ def reaper(rses=[], worker_number=1, total_workers=1, chunk_size=100, once=False
                     prot.connect()
                     for replica in replicas:
                         nothing_to_do = False
+                        scope = ''
+                        if replica['scope']:
+                            scope = replica['scope'].external
                         try:
                             pfn = str(rsemgr.lfns2pfns(rse_settings=rse_info,
-                                                       lfns=[{'scope': replica['scope'].external, 'name': replica['name'], 'path': replica['path']}],
+                                                       lfns=[{'scope': scope, 'name': replica['name'], 'path': replica['path']}],
                                                        operation='delete', scheme=scheme).values()[0])
-                            logging.info('Dark Reaper %s-%s: Deletion ATTEMPT of %s:%s as %s on %s', worker_number, total_workers, replica['scope'], replica['name'], pfn, rse)
+                            logging.info('Dark Reaper %s-%s: Deletion ATTEMPT of %s:%s as %s on %s', worker_number, total_workers, scope, replica['name'], pfn, rse)
                             start = time.time()
                             prot.delete(pfn)
                             duration = time.time() - start
-                            logging.info('Dark Reaper %s-%s: Deletion SUCCESS of %s:%s as %s on %s in %s seconds', worker_number, total_workers, replica['scope'], replica['name'], pfn, rse, duration)
-                            add_message('deletion-done', {'scope': replica['scope'].external,
+                            logging.info('Dark Reaper %s-%s: Deletion SUCCESS of %s:%s as %s on %s in %s seconds', worker_number, total_workers, scope, replica['name'], pfn, rse, duration)
+                            add_message('deletion-done', {'scope': scope,
                                                           'name': replica['name'],
                                                           'rse': rse,
                                                           'rse_id': rse_id,
@@ -121,13 +125,13 @@ def reaper(rses=[], worker_number=1, total_workers=1, chunk_size=100, once=False
                                                           'protocol': prot.attributes['scheme']})
                             deleted_replicas.append(replica)
                         except SourceNotFound:
-                            err_msg = 'Dark Reaper %s-%s: Deletion NOTFOUND of %s:%s as %s on %s' % (worker_number, total_workers, replica['scope'], replica['name'], pfn, rse)
+                            err_msg = 'Dark Reaper %s-%s: Deletion NOTFOUND of %s:%s as %s on %s' % (worker_number, total_workers, scope, replica['name'], pfn, rse)
                             logging.warning(err_msg)
                             deleted_replicas.append(replica)
                         except (ServiceUnavailable, RSEAccessDenied, ResourceTemporaryUnavailable) as error:
-                            err_msg = 'Dark Reaper %s-%s: Deletion NOACCESS of %s:%s as %s on %s: %s' % (worker_number, total_workers, replica['scope'], replica['name'], pfn, rse, str(error))
+                            err_msg = 'Dark Reaper %s-%s: Deletion NOACCESS of %s:%s as %s on %s: %s' % (worker_number, total_workers, scope, replica['name'], pfn, rse, str(error))
                             logging.warning(err_msg)
-                            add_message('deletion-failed', {'scope': replica['scope'].external,
+                            add_message('deletion-failed', {'scope': scope,
                                                             'name': replica['name'],
                                                             'rse': rse,
                                                             'rse_id': rse_id,
@@ -137,7 +141,7 @@ def reaper(rses=[], worker_number=1, total_workers=1, chunk_size=100, once=False
                                                             'reason': str(error),
                                                             'protocol': prot.attributes['scheme']})
 
-                        except:
+                        except Exception:
                             logging.critical(traceback.format_exc())
                 finally:
                     prot.close()
@@ -156,7 +160,7 @@ def reaper(rses=[], worker_number=1, total_workers=1, chunk_size=100, once=False
 
         except DatabaseException as error:
             logging.warning('Reaper:  %s', str(error))
-        except:
+        except Exception:
             logging.critical(traceback.format_exc())
 
     die(executable=executable, hostname=hostname, pid=pid, thread=thread, hash_executable=hash_executable)


Test patch: