We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Distance exporter fails
Motivation
----------
There is a bug in https://github.com/rucio/rucio/blob/master/lib/rucio/api/exporter.py#L40
(ValueError: too many values to unpack) which does not show up in the test.


Modification
------------
Fix the loop where the ValueError occurs and modify the test in tests/test_import_export.py to have distances to export 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/rucio/api/exporter.py`
Content:
```
1 '''
2   Copyright European Organization for Nuclear Research (CERN)
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   You may not use this file except in compliance with the License.
6   You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7 
8   Authors:
9   - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
10   - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
11 
12   PY3K COMPATIBLE
13 '''
14 
15 from rucio.api import permission
16 from rucio.common import exception
17 from rucio.core import exporter
18 from rucio.core.rse import get_rse_name
19 
20 
21 def export_data(issuer):
22     """
23     Export data from Rucio.
24 
25     :param issuer: the issuer.
26     """
27     kwargs = {'issuer': issuer}
28     if not permission.has_permission(issuer=issuer, action='export', kwargs=kwargs):
29         raise exception.AccessDenied('Account %s can not export data' % issuer)
30 
31     data = exporter.export_data()
32     rses = {}
33     distances = {}
34 
35     for rse_id in data['rses']:
36         rse = data['rses'][rse_id]
37         rses[get_rse_name(rse_id=rse_id)] = rse
38     data['rses'] = rses
39 
40     for src_id, tmp in data['distances']:
41         src = get_rse_name(rse_id=src_id)
42         distances[src] = {}
43         for dst_id, dists in tmp:
44             dst = get_rse_name(rse_id=dst_id)
45             distances[src][dst] = dists
46     data['distances'] = distances
47     return data
48
```

Path: `lib/rucio/core/distance.py`
Content:
```
1 """
2  Copyright European Organization for Nuclear Research (CERN)
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  You may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7  http://www.apache.org/licenses/LICENSE-2.0
8 
9  Authors:
10  - Wen Guan, <wen.guan@cern.ch>, 2015-2016
11  - Cedric Serfon, <cedric.serfon@cern.ch>, 2017
12  - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019
13 
14  PY3K COMPATIBLE
15 """
16 
17 from sqlalchemy.exc import DatabaseError, IntegrityError
18 from sqlalchemy.orm import aliased
19 
20 from rucio.common import exception
21 from rucio.core.rse import get_rse_name
22 from rucio.db.sqla.models import Distance, RSE
23 from rucio.db.sqla.session import transactional_session, read_session
24 
25 
26 @transactional_session
27 def add_distance(src_rse_id, dest_rse_id, ranking=None, agis_distance=None, geoip_distance=None,
28                  active=None, submitted=None, finished=None, failed=None, transfer_speed=None, session=None):
29     """
30     Add a src-dest distance.
31 
32     :param src_rse_id: The source RSE ID.
33     :param dest_rse_id: The destination RSE ID.
34     :param ranking: Ranking as an integer.
35     :param agis_distance: AGIS Distance as an integer.
36     :param geoip_distance: GEOIP Distance as an integer.
37     :param active: Active FTS transfers as an integer.
38     :param submitted: Submitted FTS transfers as an integer.
39     :param finished: Finished FTS transfers as an integer.
40     :param failed: Failed FTS transfers as an integer.
41     :param transfer_speed: FTS transfer speed as an integer.
42     :param session: The database session to use.
43     """
44 
45     try:
46         new_distance = Distance(src_rse_id=src_rse_id, dest_rse_id=dest_rse_id, ranking=ranking, agis_distance=agis_distance, geoip_distance=geoip_distance,
47                                 active=active, submitted=submitted, finished=finished, failed=failed, transfer_speed=transfer_speed)
48         new_distance.save(session=session)
49     except IntegrityError:
50         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)))
51     except DatabaseError as error:
52         raise exception.RucioException(error.args)
53 
54 
55 @transactional_session
56 def add_distance_short(src_rse_id, dest_rse_id, distance=None, session=None):
57     """
58     Add a src-dest distance.
59 
60     :param src_rse_id: The source RSE ID.
61     :param dest_rse_id: The destination RSE ID.
62     :param distance: A dictionary with different values.
63     """
64 
65     add_distance(src_rse_id, dest_rse_id, ranking=distance.get('ranking', None), agis_distance=distance.get('agis_distance', None),
66                  geoip_distance=distance.get('geoip_distance', None), active=distance.get('active', None), submitted=distance.get('submitted', None),
67                  finished=distance.get('finished', None), failed=distance.get('failed', None), transfer_speed=distance.get('transfer_speed', None),
68                  session=session)
69 
70 
71 @read_session
72 def get_distances(src_rse_id=None, dest_rse_id=None, session=None):
73     """
74     Get distances between rses.
75 
76     :param src_rse_id: The source RSE ID.
77     :param dest_rse_id: The destination RSE ID.
78     :param session: The database session to use.
79 
80     :returns distance: List of dictionaries.
81     """
82 
83     try:
84         query = session.query(Distance)
85         if src_rse_id:
86             query = query.filter(Distance.src_rse_id == src_rse_id)
87         if dest_rse_id:
88             query = query.filter(Distance.dest_rse_id == dest_rse_id)
89 
90         distances = []
91         tmp = query.all()
92         if tmp:
93             for t in tmp:
94                 t2 = dict(t)
95                 t2['distance'] = t2['agis_distance']
96                 t2.pop('_sa_instance_state')
97                 distances.append(t2)
98         return distances
99     except IntegrityError as error:
100         raise exception.RucioException(error.args)
101 
102 
103 @transactional_session
104 def delete_distances(src_rse_id=None, dest_rse_id=None, session=None):
105     """
106     Delete distances with the given RSE ids.
107 
108     :param src_rse_id: The source RSE ID.
109     :param dest_rse_id: The destination RSE ID.
110     :param session: The database session to use.
111     """
112 
113     try:
114         query = session.query(Distance)
115 
116         if src_rse_id:
117             query = query.filter(Distance.src_rse_id == src_rse_id)
118         if dest_rse_id:
119             query = query.filter(Distance.dest_rse_id == dest_rse_id)
120 
121         query.delete()
122     except IntegrityError as error:
123         raise exception.RucioException(error.args)
124 
125 
126 @transactional_session
127 def update_distances(src_rse_id=None, dest_rse_id=None, parameters=None, session=None):
128     """
129     Update distances with the given RSE ids.
130 
131     :param src_rse_id: The source RSE ID.
132     :param dest_rse_id: The destination RSE ID.
133     :param  parameters: A dictionnary with property
134     :param session: The database session to use.
135     """
136     params = {}
137     for key in parameters:
138         if key in ['ranking', 'agis_distance', 'geoip_distance', 'active', 'submitted', 'finished', 'failed', 'transfer_speed', 'packet_loss', 'latency', 'mbps_file', 'mbps_link', 'queued_total', 'done_1h', 'done_6h']:
139             params[key] = parameters[key]
140     try:
141         query = session.query(Distance)
142         if src_rse_id:
143             query = query.filter(Distance.src_rse_id == src_rse_id)
144         if dest_rse_id:
145             query = query.filter(Distance.dest_rse_id == dest_rse_id)
146         query.update(params)
147     except IntegrityError as error:
148         raise exception.RucioException(error.args)
149 
150 
151 @read_session
152 def list_distances(filter={}, session=None):
153     """
154     Get distances between all the RSEs.
155 
156     :param filter: dictionary to filter distances.
157     :param session: The database session in use.
158     """
159     return [distance.to_dict() for distance in session.query(Distance).all()]
160 
161 
162 @read_session
163 def export_distances(session=None):
164     """
165     Export distances between all the RSEs using RSE ids.
166     :param session: The database session to use.
167     :returns distance: dictionary of dictionaries with all the distances.
168     """
169 
170     distances = {}
171     try:
172         rse_src = aliased(RSE)
173         rse_dest = aliased(RSE)
174         query = session.query(Distance, rse_src.id, rse_dest.id)\
175                        .join(rse_src, rse_src.id == Distance.src_rse_id)\
176                        .join(rse_dest, rse_dest.id == Distance.dest_rse_id)
177         for result in query.all():
178             distance = result[0]
179             src_id = result[1]
180             dst_id = result[2]
181             if src_id not in distances:
182                 distances[src_id] = {}
183             distances[src_id][dst_id] = {}
184             distances[src_id][dst_id] = distance.to_dict()
185             del distances[src_id][dst_id]['_sa_instance_state']
186         return distances
187     except IntegrityError as error:
188         raise exception.RucioException(error.args)
189
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/rucio/api/exporter.py b/lib/rucio/api/exporter.py
--- a/lib/rucio/api/exporter.py
+++ b/lib/rucio/api/exporter.py
@@ -37,11 +37,12 @@
         rses[get_rse_name(rse_id=rse_id)] = rse
     data['rses'] = rses
 
-    for src_id, tmp in data['distances']:
+    for src_id in data['distances']:
+        dests = data['distances'][src_id]
         src = get_rse_name(rse_id=src_id)
         distances[src] = {}
-        for dst_id, dists in tmp:
-            dst = get_rse_name(rse_id=dst_id)
-            distances[src][dst] = dists
+        for dest_id in dests:
+            dest = get_rse_name(rse_id=dest_id)
+            distances[src][dest] = dests[dest_id]
     data['distances'] = distances
     return data
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
@@ -181,6 +181,7 @@
             if src_id not in distances:
                 distances[src_id] = {}
             distances[src_id][dst_id] = {}
+            distance['distance'] = distance['agis_distance']
             distances[src_id][dst_id] = distance.to_dict()
             del distances[src_id][dst_id]['_sa_instance_state']
         return distances

{"golden_diff": "diff --git a/lib/rucio/api/exporter.py b/lib/rucio/api/exporter.py\n--- a/lib/rucio/api/exporter.py\n+++ b/lib/rucio/api/exporter.py\n@@ -37,11 +37,12 @@\n         rses[get_rse_name(rse_id=rse_id)] = rse\n     data['rses'] = rses\n \n-    for src_id, tmp in data['distances']:\n+    for src_id in data['distances']:\n+        dests = data['distances'][src_id]\n         src = get_rse_name(rse_id=src_id)\n         distances[src] = {}\n-        for dst_id, dists in tmp:\n-            dst = get_rse_name(rse_id=dst_id)\n-            distances[src][dst] = dists\n+        for dest_id in dests:\n+            dest = get_rse_name(rse_id=dest_id)\n+            distances[src][dest] = dests[dest_id]\n     data['distances'] = distances\n     return data\ndiff --git a/lib/rucio/core/distance.py b/lib/rucio/core/distance.py\n--- a/lib/rucio/core/distance.py\n+++ b/lib/rucio/core/distance.py\n@@ -181,6 +181,7 @@\n             if src_id not in distances:\n                 distances[src_id] = {}\n             distances[src_id][dst_id] = {}\n+            distance['distance'] = distance['agis_distance']\n             distances[src_id][dst_id] = distance.to_dict()\n             del distances[src_id][dst_id]['_sa_instance_state']\n         return distances\n", "issue": "Distance exporter fails\nMotivation\r\n----------\r\nThere is a bug in https://github.com/rucio/rucio/blob/master/lib/rucio/api/exporter.py#L40\r\n(ValueError: too many values to unpack) which does not show up in the test.\r\n\r\n\r\nModification\r\n------------\r\nFix the loop where the ValueError occurs and modify the test in tests/test_import_export.py to have distances to export \r\n\r\n\n", "before_files": [{"content": "'''\n  Copyright European Organization for Nuclear Research (CERN)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  You may not use this file except in compliance with the License.\n  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n  Authors:\n  - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018\n  - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019\n\n  PY3K COMPATIBLE\n'''\n\nfrom rucio.api import permission\nfrom rucio.common import exception\nfrom rucio.core import exporter\nfrom rucio.core.rse import get_rse_name\n\n\ndef export_data(issuer):\n    \"\"\"\n    Export data from Rucio.\n\n    :param issuer: the issuer.\n    \"\"\"\n    kwargs = {'issuer': issuer}\n    if not permission.has_permission(issuer=issuer, action='export', kwargs=kwargs):\n        raise exception.AccessDenied('Account %s can not export data' % issuer)\n\n    data = exporter.export_data()\n    rses = {}\n    distances = {}\n\n    for rse_id in data['rses']:\n        rse = data['rses'][rse_id]\n        rses[get_rse_name(rse_id=rse_id)] = rse\n    data['rses'] = rses\n\n    for src_id, tmp in data['distances']:\n        src = get_rse_name(rse_id=src_id)\n        distances[src] = {}\n        for dst_id, dists in tmp:\n            dst = get_rse_name(rse_id=dst_id)\n            distances[src][dst] = dists\n    data['distances'] = distances\n    return data\n", "path": "lib/rucio/api/exporter.py"}, {"content": "\"\"\"\n Copyright European Organization for Nuclear Research (CERN)\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n You may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n\n Authors:\n - Wen Guan, <wen.guan@cern.ch>, 2015-2016\n - Cedric Serfon, <cedric.serfon@cern.ch>, 2017\n - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019\n\n PY3K COMPATIBLE\n\"\"\"\n\nfrom sqlalchemy.exc import DatabaseError, IntegrityError\nfrom sqlalchemy.orm import aliased\n\nfrom rucio.common import exception\nfrom rucio.core.rse import get_rse_name\nfrom rucio.db.sqla.models import Distance, RSE\nfrom rucio.db.sqla.session import transactional_session, read_session\n\n\n@transactional_session\ndef add_distance(src_rse_id, dest_rse_id, ranking=None, agis_distance=None, geoip_distance=None,\n                 active=None, submitted=None, finished=None, failed=None, transfer_speed=None, session=None):\n    \"\"\"\n    Add a src-dest distance.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param ranking: Ranking as an integer.\n    :param agis_distance: AGIS Distance as an integer.\n    :param geoip_distance: GEOIP Distance as an integer.\n    :param active: Active FTS transfers as an integer.\n    :param submitted: Submitted FTS transfers as an integer.\n    :param finished: Finished FTS transfers as an integer.\n    :param failed: Failed FTS transfers as an integer.\n    :param transfer_speed: FTS transfer speed as an integer.\n    :param session: The database session to use.\n    \"\"\"\n\n    try:\n        new_distance = Distance(src_rse_id=src_rse_id, dest_rse_id=dest_rse_id, ranking=ranking, agis_distance=agis_distance, geoip_distance=geoip_distance,\n                                active=active, submitted=submitted, finished=finished, failed=failed, transfer_speed=transfer_speed)\n        new_distance.save(session=session)\n    except IntegrityError:\n        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)))\n    except DatabaseError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef add_distance_short(src_rse_id, dest_rse_id, distance=None, session=None):\n    \"\"\"\n    Add a src-dest distance.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param distance: A dictionary with different values.\n    \"\"\"\n\n    add_distance(src_rse_id, dest_rse_id, ranking=distance.get('ranking', None), agis_distance=distance.get('agis_distance', None),\n                 geoip_distance=distance.get('geoip_distance', None), active=distance.get('active', None), submitted=distance.get('submitted', None),\n                 finished=distance.get('finished', None), failed=distance.get('failed', None), transfer_speed=distance.get('transfer_speed', None),\n                 session=session)\n\n\n@read_session\ndef get_distances(src_rse_id=None, dest_rse_id=None, session=None):\n    \"\"\"\n    Get distances between rses.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param session: The database session to use.\n\n    :returns distance: List of dictionaries.\n    \"\"\"\n\n    try:\n        query = session.query(Distance)\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n\n        distances = []\n        tmp = query.all()\n        if tmp:\n            for t in tmp:\n                t2 = dict(t)\n                t2['distance'] = t2['agis_distance']\n                t2.pop('_sa_instance_state')\n                distances.append(t2)\n        return distances\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef delete_distances(src_rse_id=None, dest_rse_id=None, session=None):\n    \"\"\"\n    Delete distances with the given RSE ids.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param session: The database session to use.\n    \"\"\"\n\n    try:\n        query = session.query(Distance)\n\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n\n        query.delete()\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef update_distances(src_rse_id=None, dest_rse_id=None, parameters=None, session=None):\n    \"\"\"\n    Update distances with the given RSE ids.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param  parameters: A dictionnary with property\n    :param session: The database session to use.\n    \"\"\"\n    params = {}\n    for key in parameters:\n        if key in ['ranking', 'agis_distance', 'geoip_distance', 'active', 'submitted', 'finished', 'failed', 'transfer_speed', 'packet_loss', 'latency', 'mbps_file', 'mbps_link', 'queued_total', 'done_1h', 'done_6h']:\n            params[key] = parameters[key]\n    try:\n        query = session.query(Distance)\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n        query.update(params)\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@read_session\ndef list_distances(filter={}, session=None):\n    \"\"\"\n    Get distances between all the RSEs.\n\n    :param filter: dictionary to filter distances.\n    :param session: The database session in use.\n    \"\"\"\n    return [distance.to_dict() for distance in session.query(Distance).all()]\n\n\n@read_session\ndef export_distances(session=None):\n    \"\"\"\n    Export distances between all the RSEs using RSE ids.\n    :param session: The database session to use.\n    :returns distance: dictionary of dictionaries with all the distances.\n    \"\"\"\n\n    distances = {}\n    try:\n        rse_src = aliased(RSE)\n        rse_dest = aliased(RSE)\n        query = session.query(Distance, rse_src.id, rse_dest.id)\\\n                       .join(rse_src, rse_src.id == Distance.src_rse_id)\\\n                       .join(rse_dest, rse_dest.id == Distance.dest_rse_id)\n        for result in query.all():\n            distance = result[0]\n            src_id = result[1]\n            dst_id = result[2]\n            if src_id not in distances:\n                distances[src_id] = {}\n            distances[src_id][dst_id] = {}\n            distances[src_id][dst_id] = distance.to_dict()\n            del distances[src_id][dst_id]['_sa_instance_state']\n        return distances\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n", "path": "lib/rucio/core/distance.py"}], "after_files": [{"content": "'''\n  Copyright European Organization for Nuclear Research (CERN)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  You may not use this file except in compliance with the License.\n  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n  Authors:\n  - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018\n  - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019\n\n  PY3K COMPATIBLE\n'''\n\nfrom rucio.api import permission\nfrom rucio.common import exception\nfrom rucio.core import exporter\nfrom rucio.core.rse import get_rse_name\n\n\ndef export_data(issuer):\n    \"\"\"\n    Export data from Rucio.\n\n    :param issuer: the issuer.\n    \"\"\"\n    kwargs = {'issuer': issuer}\n    if not permission.has_permission(issuer=issuer, action='export', kwargs=kwargs):\n        raise exception.AccessDenied('Account %s can not export data' % issuer)\n\n    data = exporter.export_data()\n    rses = {}\n    distances = {}\n\n    for rse_id in data['rses']:\n        rse = data['rses'][rse_id]\n        rses[get_rse_name(rse_id=rse_id)] = rse\n    data['rses'] = rses\n\n    for src_id in data['distances']:\n        dests = data['distances'][src_id]\n        src = get_rse_name(rse_id=src_id)\n        distances[src] = {}\n        for dest_id in dests:\n            dest = get_rse_name(rse_id=dest_id)\n            distances[src][dest] = dests[dest_id]\n    data['distances'] = distances\n    return data\n", "path": "lib/rucio/api/exporter.py"}, {"content": "\"\"\"\n Copyright European Organization for Nuclear Research (CERN)\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n You may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n\n Authors:\n - Wen Guan, <wen.guan@cern.ch>, 2015-2016\n - Cedric Serfon, <cedric.serfon@cern.ch>, 2017\n - Andrew Lister, <andrew.lister@stfc.ac.uk>, 2019\n\n PY3K COMPATIBLE\n\"\"\"\n\nfrom sqlalchemy.exc import DatabaseError, IntegrityError\nfrom sqlalchemy.orm import aliased\n\nfrom rucio.common import exception\nfrom rucio.core.rse import get_rse_name\nfrom rucio.db.sqla.models import Distance, RSE\nfrom rucio.db.sqla.session import transactional_session, read_session\n\n\n@transactional_session\ndef add_distance(src_rse_id, dest_rse_id, ranking=None, agis_distance=None, geoip_distance=None,\n                 active=None, submitted=None, finished=None, failed=None, transfer_speed=None, session=None):\n    \"\"\"\n    Add a src-dest distance.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param ranking: Ranking as an integer.\n    :param agis_distance: AGIS Distance as an integer.\n    :param geoip_distance: GEOIP Distance as an integer.\n    :param active: Active FTS transfers as an integer.\n    :param submitted: Submitted FTS transfers as an integer.\n    :param finished: Finished FTS transfers as an integer.\n    :param failed: Failed FTS transfers as an integer.\n    :param transfer_speed: FTS transfer speed as an integer.\n    :param session: The database session to use.\n    \"\"\"\n\n    try:\n        new_distance = Distance(src_rse_id=src_rse_id, dest_rse_id=dest_rse_id, ranking=ranking, agis_distance=agis_distance, geoip_distance=geoip_distance,\n                                active=active, submitted=submitted, finished=finished, failed=failed, transfer_speed=transfer_speed)\n        new_distance.save(session=session)\n    except IntegrityError:\n        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)))\n    except DatabaseError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef add_distance_short(src_rse_id, dest_rse_id, distance=None, session=None):\n    \"\"\"\n    Add a src-dest distance.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param distance: A dictionary with different values.\n    \"\"\"\n\n    add_distance(src_rse_id, dest_rse_id, ranking=distance.get('ranking', None), agis_distance=distance.get('agis_distance', None),\n                 geoip_distance=distance.get('geoip_distance', None), active=distance.get('active', None), submitted=distance.get('submitted', None),\n                 finished=distance.get('finished', None), failed=distance.get('failed', None), transfer_speed=distance.get('transfer_speed', None),\n                 session=session)\n\n\n@read_session\ndef get_distances(src_rse_id=None, dest_rse_id=None, session=None):\n    \"\"\"\n    Get distances between rses.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param session: The database session to use.\n\n    :returns distance: List of dictionaries.\n    \"\"\"\n\n    try:\n        query = session.query(Distance)\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n\n        distances = []\n        tmp = query.all()\n        if tmp:\n            for t in tmp:\n                t2 = dict(t)\n                t2['distance'] = t2['agis_distance']\n                t2.pop('_sa_instance_state')\n                distances.append(t2)\n        return distances\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef delete_distances(src_rse_id=None, dest_rse_id=None, session=None):\n    \"\"\"\n    Delete distances with the given RSE ids.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param session: The database session to use.\n    \"\"\"\n\n    try:\n        query = session.query(Distance)\n\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n\n        query.delete()\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@transactional_session\ndef update_distances(src_rse_id=None, dest_rse_id=None, parameters=None, session=None):\n    \"\"\"\n    Update distances with the given RSE ids.\n\n    :param src_rse_id: The source RSE ID.\n    :param dest_rse_id: The destination RSE ID.\n    :param  parameters: A dictionnary with property\n    :param session: The database session to use.\n    \"\"\"\n    params = {}\n    for key in parameters:\n        if key in ['ranking', 'agis_distance', 'geoip_distance', 'active', 'submitted', 'finished', 'failed', 'transfer_speed', 'packet_loss', 'latency', 'mbps_file', 'mbps_link', 'queued_total', 'done_1h', 'done_6h']:\n            params[key] = parameters[key]\n    try:\n        query = session.query(Distance)\n        if src_rse_id:\n            query = query.filter(Distance.src_rse_id == src_rse_id)\n        if dest_rse_id:\n            query = query.filter(Distance.dest_rse_id == dest_rse_id)\n        query.update(params)\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n\n\n@read_session\ndef list_distances(filter={}, session=None):\n    \"\"\"\n    Get distances between all the RSEs.\n\n    :param filter: dictionary to filter distances.\n    :param session: The database session in use.\n    \"\"\"\n    return [distance.to_dict() for distance in session.query(Distance).all()]\n\n\n@read_session\ndef export_distances(session=None):\n    \"\"\"\n    Export distances between all the RSEs using RSE ids.\n    :param session: The database session to use.\n    :returns distance: dictionary of dictionaries with all the distances.\n    \"\"\"\n\n    distances = {}\n    try:\n        rse_src = aliased(RSE)\n        rse_dest = aliased(RSE)\n        query = session.query(Distance, rse_src.id, rse_dest.id)\\\n                       .join(rse_src, rse_src.id == Distance.src_rse_id)\\\n                       .join(rse_dest, rse_dest.id == Distance.dest_rse_id)\n        for result in query.all():\n            distance = result[0]\n            src_id = result[1]\n            dst_id = result[2]\n            if src_id not in distances:\n                distances[src_id] = {}\n            distances[src_id][dst_id] = {}\n            distance['distance'] = distance['agis_distance']\n            distances[src_id][dst_id] = distance.to_dict()\n            del distances[src_id][dst_id]['_sa_instance_state']\n        return distances\n    except IntegrityError as error:\n        raise exception.RucioException(error.args)\n", "path": "lib/rucio/core/distance.py"}]}