Repository: rucio/rucio

Issue:
QoS: Core and interfaces
Modification
------------
- [x] API and clients
- [x] DB changes
- [x] Connection of components
- [x] Unit tests




Discussion:

Resolving patch:
diff --git a/lib/rucio/web/rest/flaskapi/v1/rse.py b/lib/rucio/web/rest/flaskapi/v1/rse.py
--- a/lib/rucio/web/rest/flaskapi/v1/rse.py
+++ b/lib/rucio/web/rest/flaskapi/v1/rse.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2012-2018 CERN for the benefit of the ATLAS collaboration.
+# Copyright 2018-2020 CERN for the benefit of the ATLAS collaboration.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,12 +14,10 @@
 # limitations under the License.
 #
 # Authors:
-# - Vincent Garonne <vincent.garonne@cern.ch>, 2012-2017
-# - Thomas Beermann <thomas.beermann@cern.ch>, 2012-2018
-# - Ralph Vigne <ralph.vigne@cern.ch>, 2013-2014
-# - Cedric Serfon <cedric.serfon@cern.ch>, 2014-2018
-# - Martin Barisits <martin.barisits@cern.ch>, 2017
-# - Mario Lassnig <mario.lassnig@cern.ch>, 2018
+# - Thomas Beermann <thomas.beermann@cern.ch>, 2018
+# - Vincent Garonne <vgaronne@gmail.com>, 2018
+# - Mario Lassnig <mario.lassnig@cern.ch>, 2018-2020
+# - Cedric Serfon <cedric.serfon@cern.ch>, 2018
 # - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
 # - Andrew Lister <andrew.lister@stfc.ac.uk>, 2019
 #
@@ -38,7 +36,8 @@
                            update_protocols, get_rse, set_rse_usage,
                            get_rse_usage, list_rse_usage_history,
                            set_rse_limits, get_rse_limits, parse_rse_expression,
-                           add_distance, get_distance, update_distance)
+                           add_distance, get_distance, update_distance,
+                           list_qos_policies, add_qos_policy, delete_qos_policy)
 from rucio.common.exception import (Duplicate, AccessDenied, RSENotFound, RucioException,
                                     RSEOperationNotSupported, RSEProtocolNotSupported,
                                     InvalidObject, RSEProtocolDomainNotSupported,
@@ -927,6 +926,89 @@ def put(self, source, destination):
         return "OK", 200
 
 
+class QoSPolicy(MethodView):
+    """ Create/Update and list QoS policies of RSEs. """
+
+    @check_accept_header_wrapper_flask(['application/json'])
+    def get(self, rse):
+        """
+        List all QoS policies of an RSE.
+
+        .. :quickref: QoSPolicy; List all QoS policies of an RSE.
+
+        :param rse: The RSE name.
+        :resheader Content-Type: application/json
+        :status 200: OK.
+        :status 401: Invalid Auth Token.
+        :status 500: Internal Error.
+        :returns: List of QoS policies
+        """
+
+        try:
+            qos_policies = list_qos_policies(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
+            return Response(dumps(qos_policies, cls=APIEncoder), content_type="application/json")
+        except RSENotFound as error:
+            return generate_http_error_flask(404, 'RSENotFound', error.args[0])
+        except RucioException as error:
+            return generate_http_error_flask(500, error.__class__.__name__, error.args[0])
+        except Exception as error:
+            print(format_exc())
+            return error, 500
+
+    @check_accept_header_wrapper_flask(['application/json'])
+    def post(self, rse, qos_policy):
+        """
+        Add QoS policy to RSE
+
+        .. :quickref: QoSPolicy; Add QoS policy to RSE.
+
+        :param rse: The RSE name.
+        :param qos_policy: The QoS policy name.
+        :status 201: Created.
+        :status 401: Invalid Auth Token.
+        :status 404: RSE Not Found.
+        :status 500: Internal Error.
+        """
+        try:
+            add_qos_policy(rse=rse, qos_policy=qos_policy, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
+        except RSENotFound as error:
+            return generate_http_error_flask(404, 'RSENotFound', error.args[0])
+        except RucioException as error:
+            return generate_http_error_flask(500, error.__class__.__name__, error.args[0])
+        except Exception as error:
+            print(format_exc())
+            return error, 500
+
+        return "Created", 201
+
+    @check_accept_header_wrapper_flask(['application/json'])
+    def delete(self, rse, qos_policy):
+        """
+        Delete QoS policy from RSE.
+
+        .. :quickref: QoSPolicy; Delete QoS policy from RSE.
+
+        :param rse: The RSE name.
+        :param qos_policy: The QoS policy name.
+        :status 200: OK.
+        :status 401: Invalid Auth Token.
+        :status 404: RSE not found.
+        :status 500: Internal Error.
+        """
+
+        try:
+            delete_qos_policy(rse=rse, qos_policy=qos_policy, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
+        except RSENotFound as error:
+            return generate_http_error_flask(404, 'RSENotFound', error.args[0])
+        except RucioException as error:
+            return generate_http_error_flask(500, error.__class__.__name__, error.args[0])
+        except Exception as error:
+            print(format_exc())
+            return error, 500
+
+        return "OK", 200
+
+
 """----------------------
    Web service startup
 ----------------------"""
@@ -952,6 +1034,9 @@ def put(self, source, destination):
 bp.add_url_rule('/<rse>/usage/history', view_func=usage_history_view, methods=['get', ])
 limits_view = Limits.as_view('limits')
 bp.add_url_rule('/<rse>/limits', view_func=limits_view, methods=['get', 'put'])
+qos_policy_view = QoSPolicy.as_view('qos_policy')
+bp.add_url_rule('/<rse>/qos_policy', view_func=qos_policy_view, methods=['get', ])
+bp.add_url_rule('/<rse>/qos_policy/<policy>', view_func=qos_policy_view, methods=['post', 'delete'])
 rse_view = RSE.as_view('rse')
 bp.add_url_rule('/<rse>', view_func=rse_view, methods=['get', 'delete', 'put', 'post'])
 rses_view = RSEs.as_view('rses')


Test patch: