Repository: rucio/rucio

Issue:
Adding distance fails wiht 500 if already exists
When distance is added (accidentally) for second time, Rucio responds with 500 Internal server error instead of some reasonable error:

```shell
$ rucio-admin rse add-distance --distance 1 --ranking 1 WLCG_CERN-EOS_H WLCG_INFN-T1-STO_H
$ rucio-admin rse add-distance --distance 1 --ranking 1 WLCG_CERN-EOS_H WLCG_INFN-T1-STO_H
An unknown exception occurred.
Details: no error information passed (http status code: 500 ('internal_server_error', 'server_error', '/o\\', '\xe2\x9c\x97'))
Rucio exited with an unexpected/unknown error, please provide the traceback below to the developers.
Traceback (most recent call last):
  File "/home/vokac/dpm/tpc/rucio-doma/rucio/bin/rucio-admin", line 132, in new_funct
    return function(*args, **kwargs)
  File "/home/vokac/dpm/tpc/rucio-doma/rucio/bin/rucio-admin", line 625, in add_distance_rses
    client.add_distance(args.source, args.destination, params)
  File "/home/vokac/dpm/tpc/rucio-doma/rucio/lib/python2.7/site-packages/rucio/client/rseclient.py", line 542, in add_distance
    raise exc_cls(exc_msg)
RucioException: An unknown exception occurred.
Details: no error information passed (http status code: 500 ('internal_server_error', 'server_error', '/o\\', '\xe2\x9c\x97'))
```



Discussion:
Probably due to Duplicate Entry

Resolving patch:
diff --git a/lib/rucio/api/rse.py b/lib/rucio/api/rse.py
--- a/lib/rucio/api/rse.py
+++ b/lib/rucio/api/rse.py
@@ -416,12 +416,16 @@ def add_distance(source, destination, issuer, vo='def', ranking=None, distance=N
     kwargs = {'source': source, 'destination': destination}
     if not permission.has_permission(issuer=issuer, vo=vo, action='add_distance', kwargs=kwargs):
         raise exception.AccessDenied('Account %s can not add RSE distances' % (issuer))
-    return distance_module.add_distance(src_rse_id=rse_module.get_rse_id(source, vo=vo),
-                                        dest_rse_id=rse_module.get_rse_id(destination, vo=vo),
-                                        ranking=ranking, agis_distance=distance,
-                                        geoip_distance=geoip_distance, active=active,
-                                        submitted=submitted, finished=finished,
-                                        failed=failed, transfer_speed=transfer_speed)
+    try:
+        return distance_module.add_distance(src_rse_id=rse_module.get_rse_id(source, vo=vo),
+                                            dest_rse_id=rse_module.get_rse_id(destination, vo=vo),
+                                            ranking=ranking, agis_distance=distance,
+                                            geoip_distance=geoip_distance, active=active,
+                                            submitted=submitted, finished=finished,
+                                            failed=failed, transfer_speed=transfer_speed)
+    except exception.Duplicate:
+        # use source and destination RSE names
+        raise exception.Duplicate('Distance from %s to %s already exists!' % (source, destination))
 
 
 def update_distance(source, destination, parameters, issuer, vo='def'):
diff --git a/lib/rucio/core/distance.py b/lib/rucio/core/distance.py
--- a/lib/rucio/core/distance.py
+++ b/lib/rucio/core/distance.py
@@ -1,24 +1,32 @@
-"""
- Copyright European Organization for Nuclear Research (CERN)
-
- Licensed under the Apache License, Version 2.0 (the "License");
- You may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
-
- Authors:
- - Wen Guan, <wen.guan@cern.ch>, 2015-2016
- - Cedric Serfon, <cedric.serfon@cern.ch>, 2017
- - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
-
- PY3K COMPATIBLE
-"""
+# -*- coding: utf-8 -*-
+# Copyright 2015-2020 CERN
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Authors:
+# - Wen Guan <wen.guan@cern.ch>, 2015-2016
+# - Vincent Garonne <vincent.garonne@cern.ch>, 2015-2017
+# - Cedric Serfon <cedric.serfon@cern.ch>, 2017
+# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
+# - Andrew Lister <andrew.lister@stfc.ac.uk>, 2019
+# - Benedikt Ziemons <benedikt.ziemons@cern.ch>, 2020
+#
+# PY3K COMPATIBLE
 
 from sqlalchemy.exc import DatabaseError, IntegrityError
 from sqlalchemy.orm import aliased
 
 from rucio.common import exception
-from rucio.core.rse import get_rse_name
 from rucio.db.sqla.models import Distance, RSE
 from rucio.db.sqla.session import transactional_session, read_session
 
@@ -47,7 +55,7 @@ def add_distance(src_rse_id, dest_rse_id, ranking=None, agis_distance=None, geoi
                                 active=active, submitted=submitted, finished=finished, failed=failed, transfer_speed=transfer_speed)
         new_distance.save(session=session)
     except IntegrityError:
-        raise exception.Duplicate('Distance from %s to %s already exists!' % (get_rse_name(rse_id=src_rse_id, session=session), get_rse_name(rse_id=dest_rse_id, session=session)))
+        raise exception.Duplicate()
     except DatabaseError as error:
         raise exception.RucioException(error.args)
 


Test patch:
diff --git a/lib/rucio/tests/test_bin_rucio.py b/lib/rucio/tests/test_bin_rucio.py
--- a/lib/rucio/tests/test_bin_rucio.py
+++ b/lib/rucio/tests/test_bin_rucio.py
@@ -226,6 +226,39 @@ def test_list_rses(self):
         print(out, )
         assert tmp_val in out
 
+    def test_rse_add_distance(self):
+        """CLIENT (ADMIN): Add distance to RSE"""
+        # add RSEs
+        temprse1 = rse_name_generator()
+        cmd = 'rucio-admin rse add %s' % temprse1
+        exitcode, out, err = execute(cmd)
+        print(out, err)
+        assert exitcode == 0
+        temprse2 = rse_name_generator()
+        cmd = 'rucio-admin rse add %s' % temprse2
+        exitcode, out, err = execute(cmd)
+        print(out, err)
+        assert exitcode == 0
+
+        # add distance between the RSEs
+        cmd = 'rucio-admin rse add-distance --distance 1 --ranking 1 %s %s' % (temprse1, temprse2)
+        print(self.marker + cmd)
+        exitcode, out, err = execute(cmd)
+        print(out, err)
+        assert exitcode == 0
+        cmd = 'rucio-admin rse add-distance --distance 1 --ranking 1 %s %s' % (temprse2, temprse1)
+        print(self.marker + cmd)
+        exitcode, out, err = execute(cmd)
+        print(out, err)
+        assert exitcode == 0
+
+        # add duplicate distance
+        print(self.marker + cmd)
+        exitcode, out, err = execute(cmd)
+        print(out, err, exitcode)
+        assert exitcode != 0
+        assert 'Distance from %s to %s already exists!' % (temprse2, temprse1) in err
+
     def test_upload(self):
         """CLIENT(USER): Upload"""
         tmp_val = rse_name_generator()