# Copyright European Organization for Nuclear Research (CERN) since 2012
#
# 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.

from json import dumps
from typing import TYPE_CHECKING

from flask import Flask, Response, jsonify, request

from rucio.common.exception import (
    AccessDenied,
    CounterNotFound,
    Duplicate,
    InputValidationError,
    InvalidObject,
    InvalidPath,
    InvalidRSEExpression,
    ReplicaNotFound,
    RSEAttributeNotFound,
    RSENotFound,
    RSEOperationNotSupported,
    RSEProtocolDomainNotSupported,
    RSEProtocolNotSupported,
    RSEProtocolPriorityError,
)
from rucio.common.utils import APIEncoder, Availability, render_json
from rucio.gateway.account_limit import get_rse_account_usage
from rucio.gateway.rse import (
    add_distance,
    add_protocol,
    add_qos_policy,
    add_rse,
    add_rse_attribute,
    del_protocols,
    del_rse,
    del_rse_attribute,
    delete_distance,
    delete_qos_policy,
    delete_rse_limits,
    get_distance,
    get_rse,
    get_rse_limits,
    get_rse_protocols,
    get_rse_usage,
    list_qos_policies,
    list_rse_attributes,
    list_rse_usage_history,
    list_rses,
    parse_rse_expression,
    set_rse_limits,
    set_rse_usage,
    update_distance,
    update_protocols,
    update_rse,
)
from rucio.rse import rsemanager
from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, param_get, response_headers, try_stream

if TYPE_CHECKING:
    from rucio.common.types import LFNDict


class RSEs(ErrorHandlingMethodView):
    """ List all RSEs in the database. """

    @check_accept_header_wrapper_flask(['application/x-json-stream'])
    def get(self):
        """
        ---
        summary: List RSEs
        description: Lists all RSEs.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: expression
          in: query
          description: RSE expression to select RSEs.
          schema:
            type: string
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: A list with the corresponding rses.
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        description: The rse id.
                        type: string
                      rse:
                        description: The name of the rse.
                        type: string
                      rse_type:
                        description: The type of the rse.
                        type: string
                      deterministic:
                        description: If the rse is deterministic.
                        type: boolean
                      volatile:
                        description: If the rse is volatile.
                        type: boolean
                      staging_area:
                        description: Is this rse a staging area?
                        type: boolean
                      city:
                        description: The city of the rse.
                        type: string
                      region_code:
                        description: The region_code of the rse.
                        type: string
                      country_name:
                        description: The country name of the rse.
                        type: string
                      continent:
                        description: The continent of the rse.
                        type: string
                      time_zone:
                        description: The time zone of the rse.
                        type: string
                      ISP:
                        description: The isp of the rse.
                        type: string
                      ASN:
                        description: The asn of the rse.
                        type: string
                      longitude:
                        description: The longitude of the rse.
                        type: number
                      latitude:
                        description: The latitude of the rse.
                        type: number
                      availability:
                        description: The availability of the rse.
                        type: integer
                        deprecated: true
                      availability_read:
                        description: If the RSE is readable.
                        type: integer
                      availability_write:
                        description: If the RSE is writable.
                        type: integer
                      availability_delete:
                        description: If the RSE is deletable.
                        type: integer
                      usage:
                        description: The usage of the rse.
                        type: integer
                      qos_class:
                        description: The quality of service class.
                        type: string
          400:
            description: Invalid RSE expression
          401:
            description: Invalid Auth Token
          406:
            description: Not acceptable
        """
        expression = request.args.get('expression', default=None)

        if expression:
            try:
                def generate(vo):
                    for rse in parse_rse_expression(expression, vo=vo):
                        yield render_json(rse=rse) + '\n'

                return try_stream(generate(vo=request.environ.get('vo')))
            except (InvalidRSEExpression, InvalidObject) as error:
                return generate_http_error_flask(400, error)
        else:
            def generate(vo):
                for rse in list_rses(vo=vo):
                    rse['availability'] = Availability(rse['availability_read'], rse['availability_write'], rse['availability_delete']).integer
                    yield render_json(**rse) + '\n'

            return try_stream(generate(vo=request.environ.get('vo')))


class RSE(ErrorHandlingMethodView):
    """ Create, update, get and disable RSE. """

    def post(self, rse):
        """
        ---
        summary: Create RSE
        description: Creates a RSE with all the metadata.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  deterministic:
                    description: If the pfn is generated deterministicly.
                    type: boolean
                  volatile:
                    description: RSE cache.
                    type: boolean
                  city:
                    description: The city of the RSE.
                    type: string
                  staging_area:
                    description: Staging area.
                    type: string
                  region_code:
                    description: The region code of the RSE.
                    type: string
                  country_name:
                    description: The country name of the RSE.
                    type: string
                  continent:
                    description: The continent of the RSE.
                    type: string
                  time_zone:
                    description: The time zone of the RSE.
                    type: string
                  ISP:
                    description: The internet service provider of the RSE.
                    type: string
                  rse_type:
                    description: The rse type.
                    type: string
                    enum: ["DISK", "TAPE"]
                  latitude:
                    description: The latitude of the RSE.
                    type: number
                  longitude:
                    description: The longitude of the RSE.
                    type: number
                  ASN:
                    description: The access service network of the RSE.
                    type: string
                  availability:
                    description: The availability of the RSE.
                    type: integer
                    deprecated: true
                  availability_read:
                    description: If the RSE is readable.
                    type: boolean
                  availability_write:
                    description: If the RSE is writable.
                    type: boolean
                  availability_delete:
                    description: If the RSE is deletable.
                    type: boolean
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          400:
            description: Cannot decode json parameter dictionary
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
          409:
            description: RSE already exists.
        """
        kwargs = {
            'deterministic': True,
            'volatile': False,
            'city': None,
            'staging_area': False,
            'region_code': None,
            'country_name': None,
            'continent': None,
            'time_zone': None,
            'ISP': None,
            'rse_type': None,
            'latitude': None,
            'longitude': None,
            'ASN': None,
            'availability_read': None,
            'availability_write': None,
            'availability_delete': None,
        }
        if request.get_data(as_text=True):
            parameters = json_parameters()

            if "availability" in parameters and ("availability_read" in parameters or "availability_write" in parameters or "availability_delete" in parameters):
                return generate_http_error_flask(422, InputValidationError("'availability' can not be specified with 'availability_read', 'availability_write' or 'availability_delete'"))  # noqa: E501

            if "availability" in parameters:
                availability = Availability.from_integer(parameters["availability"])
                kwargs["availability_read"] = availability.read
                kwargs["availability_write"] = availability.write
                kwargs["availability_delete"] = availability.delete

            for keyword in kwargs.keys():
                kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword])

        kwargs['issuer'] = request.environ.get('issuer')
        kwargs['vo'] = request.environ.get('vo')
        try:
            add_rse(rse, **kwargs)
        except InvalidObject as error:
            return generate_http_error_flask(400, error)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)
        except Duplicate as error:
            return generate_http_error_flask(409, error)

        return 'Created', 201

    def put(self, rse):
        """
        ---
        summary: Update RSE
        description: Update RSE properties.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  availability_read:
                    description: The vailability of the RSE.
                    type: boolean
                  availability_write:
                    description: The vailability of the RSE.
                    type: boolean
                  availability_delete:
                    description: The vailability of the RSE.
                    type: boolean
                  deterministic:
                    description: If the pfn is generated deterministicly.
                    type: boolean
                  volatile:
                    description: RSE cache.
                    type: boolean
                  city:
                    description: The city of the RSE.
                    type: string
                  staging_area:
                    description: Staging area.
                    type: string
                  region_code:
                    description: The region code of the RSE.
                    type: string
                  country_name:
                    description: The country name of the RSE.
                    type: string
                  time_zone:
                    description: The time zone of the RSE.
                    type: string
                  rse_type:
                    description: The rse type.
                    type: string
                    enum: ["DISK", "TAPE"]
                  latitude:
                    description: The latitude of the RSE.
                    type: number
                  longitude:
                    description: The longitude of the RSE.
                    type: number
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          400:
            description: Cannot decode json parameter dictionary or invalid option provided
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
        """
        kwargs = {
            'parameters': json_parameters(optional=True),
            'issuer': request.environ.get('issuer'),
            'vo': request.environ.get('vo'),
        }
        try:
            update_rse(rse, **kwargs)
        except (InvalidObject, InputValidationError) as error:
            return generate_http_error_flask(400, error)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)
        except Duplicate as error:
            return generate_http_error_flask(409, error)

        return 'Created', 201

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Get RSE
        description: Get details about a specific RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: The RSE properties.
                  type: object
                  properties:
                    deterministic:
                      description: If the pfn is generated deterministicly.
                      type: boolean
                    volatile:
                      description: RSE cache.
                      type: boolean
                    city:
                      description: The city of the RSE.
                      type: string
                    staging_area:
                      description: Staging area.
                      type: string
                    region_code:
                      description: The region code of the RSE.
                      type: string
                    country_name:
                      description: The country name of the RSE.
                      type: string
                    continent:
                      description: The continent of the RSE.
                      type: string
                    time_zone:
                      description: The time zone of the RSE.
                      type: string
                    ISP:
                      description: The internet service provider of the RSE.
                      type: string
                    rse_type:
                      description: The rse type.
                      type: string
                      enum: ["DISK", "TAPE"]
                    latitude:
                      description: The latitude of the RSE.
                      type: number
                    longitude:
                      description: The longitude of the RSE.
                      type: number
                    ASN:
                      description: The access service network of the RSE.
                      type: string
                    availability:
                      description: The availability of the RSE.
                      type: integer
                      deprecated: true
                    availability_read:
                      description: If the RSE is readable.
                      type: integer
                    availability_write:
                      description: If the RSE is writable.
                      type: integer
                    availability_delete:
                      description: If the RSE is deletable.
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
          406:
            description: Not acceptable
        """
        try:
            rse = get_rse(rse=rse, vo=request.environ.get('vo'))
            rse['availability'] = Availability(rse['availability_read'], rse['availability_write'], rse['availability_delete']).integer
            return Response(render_json(**rse), content_type="application/json")
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

    def delete(self, rse):
        """
        ---
        summary: Disable RSE
        description: Disable a specific RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
        """
        try:
            del_rse(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except (RSENotFound, RSEOperationNotSupported, CounterNotFound) as error:
            return generate_http_error_flask(404, error)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)

        return '', 200


class Attributes(ErrorHandlingMethodView):
    """ Create, update, get and disable RSE attribute."""

    def post(self, rse, key):
        """
        ---
        summary: Create RSE Attribute
        description: Create a RSE attribute with given RSE name.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: key
          in: path
          description: The name of the attribute of the RSE.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                required:
                - value
                properties:
                  value:
                    description: The value of the RSE attribute.
                    type: string
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          400:
            description: Cannot decode json parameter dictionary
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
          409:
            description: Attribute already exists
        """
        parameters = json_parameters()
        value = param_get(parameters, 'value')
        try:
            add_rse_attribute(rse=rse, key=key, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except Duplicate as error:
            return generate_http_error_flask(409, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return 'Created', 201

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Get RSE Attributes
        description: Lists all RSE attributes for a RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: The RSE attribute list. Returns a dictionary with the attribute names as keys and the values as values.
                  type: object
                  additionalProperties:
                    type: string
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found
          406:
            description: Not acceptable
        """
        try:
            rse_attr = list_rse_attributes(rse, vo=request.environ.get('vo'))
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return jsonify(rse_attr)

    def delete(self, rse, key):
        """
        ---
        summary: Delete RSE Attribute
        description: Delete an RSE attribute for given RSE name.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: key
          in: path
          description: The name of the attribute of the RSE.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: RSE or RSE attribute not found
        """
        try:
            del_rse_attribute(rse=rse, key=key, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except (RSENotFound, RSEAttributeNotFound) as error:
            return generate_http_error_flask(404, error)

        return '', 200


class ProtocolList(ErrorHandlingMethodView):
    """ List supported protocols. """

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: List RSE Protocols
        description: List all supported protocols of the given RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: Supported RSE Protocols and other information.
                  type: object
                  properties:
                    deterministic:
                      description: If the pfn is generated deterministicly.
                      type: boolean
                    volatile:
                      description: RSE cache.
                      type: boolean
                    staging_area:
                      description: Staging area.
                      type: string
                    rse_type:
                      description: The rse type.
                      type: string
                      enum: ["DISK", "TAPE"]
                    availability_read:
                      description: The read availability of the RSE.
                      type: boolean
                    availability_write:
                      description: The write availability of the RSE.
                      type: boolean
                    availability_delete:
                      description: The delete availability of the RSE.
                      type: boolean
                    credentials:
                      description: The credentials, currently None.
                      type: string
                    domain:
                      description: The domains of the RSE protocols.
                      type: array
                    id:
                      description: The RSE id.
                      type: string
                    lfn2pfn_algorithm:
                      description: The algorithm used to translate the logical file names to the physical ones.
                      type: string
                    qos_class:
                      description: The qos class of the RSE.
                      type: string
                    rse:
                      description: The name of the RSE.
                      type: string
                    sign_url:
                      description: The sign url of the RSE.
                      type: string
                    verify_checksum:
                      description: If the checksum of the files should be verified.
                      type: boolean
                    protocols:
                      description: All supported protocols of the RSE.
                      type: array
                      items:
                        type: object
                        description: A supported RSE protocol.
                        properties:
                          hostname:
                            description: The hostname of the protocol.
                            type: string
                          scheme:
                            description: The scheme of the protocol.
                            type: string
                          port:
                            description: The port of the protocol.
                            type: integer
                          prefix:
                            description: The prefix of the protocol.
                            type: string
                          impl:
                            description: The implementation of the protocol.
                            type: string
                          domains:
                            description: The domains of the protocol.
                            type: object
                            properties:
                              lan:
                                description: The lan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the lan protocol.
                                    type: integer
                                  write:
                                    description: The write value of the lan protocol.
                                    type: integer
                                  delete:
                                    description: The delete value of the lan protocol.
                                    type: integer
                              wan:
                                description: The wan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  write:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  delete:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  third_party_copy_read:
                                    description: The third party copy read value of the wan protocol.
                                    type: integer
                                  third_party_copy_write:
                                    description: The third party copy write value of the wan protocol.
                                    type: integer
                          extended_attributes:
                            description: The extended attributes of the protocol.
                            type: string
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found or RSE Operation, RSE Protocol Domain, RSE Protocol not supported
          406:
            description: Not acceptable
        """
        try:
            p_list = get_rse_protocols(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except (RSEOperationNotSupported, RSENotFound, RSEProtocolNotSupported, RSEProtocolDomainNotSupported) as error:
            return generate_http_error_flask(404, error)

        if len(p_list['protocols']):
            return jsonify(p_list['protocols'])
        else:
            return generate_http_error_flask(404, RSEProtocolNotSupported.__name__, 'No protocols found for this RSE')


class LFNS2PFNS(ErrorHandlingMethodView):
    """ Translate one-or-more LFNs to corresponding PFNs. """

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Translate LFNs to PFNs
        description: Return PFNs for a set of LFNs.  Formatted as a JSON object where the key is a LFN and the value is the corresponding PFN.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: lfn
          in: query
          description: The lfns of the request.
          schema:
            type: string
          required: True
        - name: scheme
          in: query
          description: Optional argument to help with the protocol selection (e.g., http / gsiftp / srm)
          schema:
            type: string
        - name: domain
          in: query
          description: Optional argument used to select the protocol for wan or lan use cases.
          schema:
            type: string
        - name: operation
          in: query
          description: Optional query argument to select the protocol for read-vs-writes.
          schema:
            type: string
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: The PFNs to the LFNs. Dictionary with lfns as keys and pfns as values.
                  type: object
                  additionalProperties:
                    type:
                      string
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found or RSE Protocol or RSE Protocol Domain not supported
          406:
            description: Not acceptable
        """
        lfns = request.args.getlist('lfn')
        lfns = list(map(lambda lfn: lfn.split(":", 1), lfns))
        if any(filter(lambda info: len(info) != 2, lfns)):
            invalid_lfns = ', '.join(filter(lambda info: len(info) != 2, lfns))
            return generate_http_error_flask(400, InvalidPath.__name__, 'LFN(s) in invalid format: ' + invalid_lfns)
        lfns_list: list["LFNDict"] = list(map(lambda info: {'scope': info[0], 'name': info[1]}, lfns))
        scheme = request.args.get('scheme', default=None)
        domain = request.args.get('domain', default='wan')
        operation = request.args.get('operation', default='write')

        try:
            rse_settings = get_rse_protocols(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except (RSENotFound, RSEProtocolNotSupported, RSEProtocolDomainNotSupported) as error:
            return generate_http_error_flask(404, error)

        pfns = rsemanager.lfns2pfns(rse_settings, lfns_list, operation=operation, scheme=scheme, domain=domain)
        if not pfns:
            return generate_http_error_flask(404, ReplicaNotFound.__name__, 'No replicas found')

        return jsonify(pfns)


class Protocol(ErrorHandlingMethodView):
    """ Create, Update, Read and delete a specific protocol. """

    def post(self, rse, scheme):
        """
        ---
        summary: Create RSE Protocol
        description: Create a protocol for a given RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: scheme
          in: path
          description: The protocol identifier.
          schema:
            type: string
          style: simple
          required: False
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  domains:
                    description: The domains for the protocol.
                    type: array
                  port:
                    description: The port the protocol uses.
                    type: integer
                  hostname:
                    description: The hostname of the protocol.
                    type: string
                  extended_attributes:
                    description: Extended attributes for the protocol.
                    type: string
                  prefix:
                    description: The prefix of the Protocol.
                    type: string
                  impl:
                    description: The impl used by the Protocol.
                    type: string
                  read_lan:
                    description: If the protocol is readable via lan.
                    type: integer
                  write_lan:
                    description: If the protocol is writable via lan.
                    type: integer
                  delete_lan:
                    description: If the protocol is deletable via lan.
                    type: integer
                  read_wan:
                    description: If the protocol is readable via wan.
                    type: integer
                  write_wan:
                    description: If the protocol is writable via wan.
                    type: integer
                  delete_wan:
                    description: If the protocol is deletable via wan.
                    type: integer
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          400:
            description: Cannot decode json parameter dictionary
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found or RSE Protocol Domain not supported
          409:
            description: RSE protocol priority error
        """
        parameters = json_parameters()

        # Fill defaults and check mandatory parameters
        parameters['scheme'] = scheme

        try:
            add_protocol(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), data=parameters)
        except (RSENotFound, RSEProtocolDomainNotSupported) as error:
            return generate_http_error_flask(404, error)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except (Duplicate, RSEProtocolPriorityError) as error:
            return generate_http_error_flask(409, error)
        except InvalidObject as error:
            return generate_http_error_flask(400, error)

        return 'Created', 201

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse, scheme):
        """
        ---
        summary: Get Protocols
        description: List all references of the provided RSE for the given protocol.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: scheme
          in: path
          description: The protocol identifier.
          schema:
            type: string
          style: simple
          required: False
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: A dict with RSE information and supported protocols.
                  type: object
                  properties:
                    deterministic:
                      description: If the pfn is generated deterministicly.
                      type: boolean
                    volatile:
                      description: RSE cache.
                      type: boolean
                    staging_area:
                      description: Staging area.
                      type: string
                    rse_type:
                      description: The rse type.
                      type: string
                      enum: ["DISK", "TAPE"]
                    availability_read:
                      description: The read availability of the RSE.
                      type: boolean
                    availability_write:
                      description: The write availability of the RSE.
                      type: boolean
                    availability_delete:
                      description: The delete availability of the RSE.
                      type: boolean
                    credentials:
                      description: The credentials, currently None.
                      type: string
                    domain:
                      description: The domains of the RSE protocols.
                      type: array
                    id:
                      description: The RSE id.
                      type: string
                    lfn2pfn_algorithm:
                      description: The algorithm used to translate the logical file names to the physical ones.
                      type: string
                    qos_class:
                      description: The qos class of the RSE.
                      type: string
                    rse:
                      description: The name of the RSE.
                      type: string
                    sign_url:
                      description: The sign url of the RSE.
                      type: string
                    verify_checksum:
                      description: If the checksum of the files should be verified.
                      type: boolean
                    protocols:
                      description: All supported protocols of the RSE.
                      type: array
                      items:
                        type: object
                        description: A supported RSE protocol.
                        properties:
                          hostname:
                            description: The hostname of the protocol.
                            type: string
                          scheme:
                            description: The scheme of the protocol.
                            type: string
                          port:
                            description: The port of the protocol.
                            type: integer
                          prefix:
                            description: The prefix of the protocol.
                            type: string
                          impl:
                            description: The implementation of the protocol.
                            type: string
                          domains:
                            description: The domains of the protocol.
                            type: object
                            properties:
                              lan:
                                description: The lan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the lan protocol.
                                    type: integer
                                  write:
                                    description: The write value of the lan protocol.
                                    type: integer
                                  delete:
                                    description: The delete value of the lan protocol.
                                    type: integer
                              wan:
                                description: The wan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  write:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  delete:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  third_party_copy_read:
                                    description: The third party copy read value of the wan protocol.
                                    type: integer
                                  third_party_copy_write:
                                    description: The third party copy write value of the wan protocol.
                                    type: integer
                          extended_attributes:
                            description: The extended attributes of the protocol.
                            type: string
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found or Protocol or Protocol domain not Supported.
          406:
            description: Not acceptable
        """
        try:
            p_list = get_rse_protocols(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except (RSENotFound, RSEProtocolNotSupported, RSEProtocolDomainNotSupported) as error:
            return generate_http_error_flask(404, error)

        return jsonify(p_list)

    def put(self, rse, scheme, hostname=None, port=None):
        """
        ---
        summary: Update Protocol Attributes
        description: Updates attributes of an existing protocol entry. Because protocol identifier, hostname, and port are used as unique identifier they are immutable.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: scheme
          in: path
          description: The protocol identifier.
          schema:
            type: string
          style: simple
        - name: hostname
          in: path
          description: The hostname of the protocol.
          schema:
            type: string
          style: simple
          required: False
        - name: port
          in: path
          description: The port of the protocol.
          schema:
            type: integer
          style: simple
          required: False
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: A dict with RSE information and supported protocols.
                  type: object
                  properties:
                    deterministic:
                      description: If the pfn is generated deterministicly.
                      type: boolean
                    volatile:
                      description: RSE cache.
                      type: boolean
                    staging_area:
                      description: Staging area.
                      type: string
                    rse_type:
                      description: The rse type.
                      type: string
                      enum: ["DISK", "TAPE"]
                    availability_read:
                      description: The read availability of the RSE.
                      type: boolean
                    availability_write:
                      description: The write availability of the RSE.
                      type: boolean
                    availability_delete:
                      description: The delete availability of the RSE.
                      type: boolean
                    credentials:
                      description: The credentials, currently None.
                      type: string
                    domain:
                      description: The domains of the RSE protocols.
                      type: array
                    id:
                      description: The RSE id.
                      type: string
                    lfn2pfn_algorithm:
                      description: The algorithm used to translate the logical file names to the physical ones.
                      type: string
                    qos_class:
                      description: The qos class of the RSE.
                      type: string
                    rse:
                      description: The name of the RSE.
                      type: string
                    sign_url:
                      description: The sign url of the RSE.
                      type: string
                    verify_checksum:
                      description: If the checksum of the files should be verified.
                      type: boolean
                    protocols:
                      description: All supported protocols of the RSE.
                      type: array
                      items:
                        type: object
                        description: A supported RSE protocol.
                        properties:
                          hostname:
                            description: The hostname of the protocol.
                            type: string
                          scheme:
                            description: The scheme of the protocol.
                            type: string
                          port:
                            description: The port of the protocol.
                            type: integer
                          prefix:
                            description: The prefix of the protocol.
                            type: string
                          impl:
                            description: The implementation of the protocol.
                            type: string
                          domains:
                            description: The domains of the protocol.
                            type: object
                            properties:
                              lan:
                                description: The lan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the lan protocol.
                                    type: integer
                                  write:
                                    description: The write value of the lan protocol.
                                    type: integer
                                  delete:
                                    description: The delete value of the lan protocol.
                                    type: integer
                              wan:
                                description: The wan domain
                                type: object
                                properties:
                                  read:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  write:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  delete:
                                    description: The read value of the wan protocol.
                                    type: integer
                                  third_party_copy_read:
                                    description: The third party copy read value of the wan protocol.
                                    type: integer
                                  third_party_copy_write:
                                    description: The third party copy write value of the wan protocol.
                                    type: integer
                          extended_attributes:
                            description: The extended attributes of the protocol.
                            type: string
          401:
            description: Invalid Auth Token
          404:
            description: RSE not found or Protocol or Protocol domain not Supported.
          406:
            description: Not acceptable
        """
        parameters = json_parameters()
        try:
            update_protocols(
                rse,
                issuer=request.environ.get('issuer'),
                vo=request.environ.get('vo'),
                scheme=scheme,
                hostname=hostname,
                port=int(port) if port else None,
                data=parameters,
            )
        except InvalidObject as error:
            return generate_http_error_flask(400, error)
        except (RSEProtocolNotSupported, RSENotFound, RSEProtocolDomainNotSupported) as error:
            return generate_http_error_flask(404, error)
        except (RSEProtocolPriorityError, Duplicate) as error:
            return generate_http_error_flask(409, error)

        return '', 200

    def delete(self, rse, scheme, hostname=None, port=None):
        """
        ---
        summary: Delete Protocol Attributes
        description: Delete all protocol attributes.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: scheme
          in: path
          description: The protocol identifier.
          schema:
            type: string
          style: simple
        - name: hostname
          in: path
          description: The hostname of the protocol.
          schema:
            type: string
          style: simple
          required: False
        - name: port
          in: path
          description: The port of the protocol.
          schema:
            type: integer
          style: simple
          required: False
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found or protocol not supported
        """
        try:
            del_protocols(
                rse,
                issuer=request.environ.get('issuer'),
                vo=request.environ.get('vo'),
                scheme=scheme,
                hostname=hostname,
                port=int(port) if port else None
            )
        except (RSEProtocolNotSupported, RSENotFound) as error:
            return generate_http_error_flask(404, error)

        return '', 200


class Usage(ErrorHandlingMethodView):
    """ Update and read RSE space usage information. """

    @check_accept_header_wrapper_flask(['application/x-json-stream'])
    def get(self, rse):
        """
        ---
        summary: Get Rse Usage Information
        description: Get rse usage information.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: per_account
          in: query
          description: Boolean whether the usage should be also calculated per account or not.
          schema:
            type: boolean
        - name: source
          in: query
          description: The information source, e.g., srm.
          schema:
            type: string
        responses:
          200:
            description: OK
            content:
              application/x-json-stream:
                schema:
                  description: A list with the rse usage.
                  type: array
                  items:
                    type: object
                    properties:
                      rse_id:
                        description: The id of the rse.
                        type: string
                      rse:
                        description: The name of the rse.
                        type: string
                      source:
                        description: The source of the rse.
                        type: string
                      used:
                        description: The number of used bytes.
                        type: integer
                      free:
                        description: The number of free bytes.
                        type: integer
                      total:
                        description: The number of total bytes.
                        type: integer
                      files:
                        description: The number of files.
                        type: integer
                      updated_at:
                        description: The last time it got updated.
                        type: string
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        per_account = request.args.get('per_account') == 'True'
        try:
            def generate(issuer, source, per_account, vo):
                for usage in get_rse_usage(rse, issuer=issuer, source=source, per_account=per_account, vo=vo):
                    yield render_json(**usage) + '\n'

            return try_stream(
                generate(
                    issuer=request.environ.get('issuer'),
                    source=request.args.get('source'),
                    per_account=per_account,
                    vo=request.environ.get('vo'),
                )
            )
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

    def put(self, rse):
        """
        ---
        summary: Update Rse Usage
        description: Update the RSE Update information.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  source:
                    description: The information source, e.g. srm.
                    type: string
                  used:
                    description: The number of used bytes.
                    type: integer
                  free:
                    description: The number of free bytes.
                    type: integer
                  files:
                    description: The number of files.
                    type: integer
        responses:
          200:
            description: OK
          400:
            description: Can not decode json parameter list.
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        parameters = json_parameters()
        kwargs = {'source': None, 'used': None, 'free': None, 'files': None}
        for keyword in kwargs.keys():
            kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword])

        try:
            set_rse_usage(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), **kwargs)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return '', 200


class UsageHistory(ErrorHandlingMethodView):
    """ Read RSE space usage history information. """

    @check_accept_header_wrapper_flask(['application/x-json-stream'])
    def get(self, rse):
        """
        ---
        summary: Get Rse Usage History
        description: Get the rse usage history
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/x-json-stream:
                schema:
                  description: A list with the rse usage history items.
                  type: array
                  items:
                    type: object
                    properties:
                      rse_id:
                        description: The id of the rse.
                        type: string
                      rse:
                        description: The name of the rse.
                        type: string
                      source:
                        description: The source of the rse.
                        type: string
                      used:
                        description: The number of used bytes.
                        type: integer
                      free:
                        description: The number of free bytes.
                        type: integer
                      total:
                        description: The number of total bytes.
                        type: integer
                      updated_at:
                        description: The last time it got updated.
                        type: string
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            def generate(issuer, source, vo):
                for usage in list_rse_usage_history(rse=rse, issuer=issuer, source=source, vo=vo):
                    yield render_json(**usage) + '\n'

            return try_stream(generate(issuer=request.environ.get('issuer'), source=request.args.get('source'), vo=request.environ.get('vo')))
        except RSENotFound as error:
            return generate_http_error_flask(404, error)


class Limits(ErrorHandlingMethodView):
    """ Create, Update, Read and delete RSE limits. """

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Get Rse Limits
        description: Get the rse limits.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: The limits.
                  type: object
                  additionalProperties:
                    x-additionalPropertiesName: limit name
                    description: An item with the name as key and the value as value.
                    type: integer
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            limits = get_rse_limits(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
            return Response(render_json(**limits), content_type="application/json")
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

    def put(self, rse):
        """
        ---
        summary: Update Rse Limit
        description: Update an rse limit.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    description: The name of the limit.
                    type: string
                  value:
                    description: The value of the limit.
                    type: integer
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        parameters = json_parameters()
        kwargs = {'name': None, 'value': None}
        for keyword in kwargs.keys():
            kwargs[keyword] = param_get(parameters, keyword, default=kwargs[keyword])

        try:
            set_rse_limits(rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), **kwargs)
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return '', 200

    def delete(self, rse):
        """
        ---
        summary: Delete Rse Limit
        description: Delete an rse limit
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                required:
                - name
                properties:
                  name:
                    description: The name of the limit.
                    type: string
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        parameters = json_parameters()
        name = param_get(parameters, 'name')

        try:
            delete_rse_limits(rse=rse, name=name, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return '', 200


class RSEAccountUsageLimit(ErrorHandlingMethodView):
    """ Read and delete RSE limits for accounts. """

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Get Rse Account Usage and Limit
        description: Returns the usage and limit of an account for a rse.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: A list with the rse account limits and usages.
                  type: array
                  items:
                    type: object
                    properties:
                      rse_id:
                        description: The id of the rse.
                        type: string
                      rse:
                        description: The name of the rse.
                        type: string
                      account:
                        description: The account.
                        type: string
                      used_files:
                        description: The number of used files.
                        type: integer
                      used_bytes:
                        description: The number of used bytes.
                        type: integer
                      quota_bytes:
                        description: The number of quota bytes.
                        type: integer
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            def generate(vo):
                for usage in get_rse_account_usage(rse=rse, vo=vo):
                    yield render_json(**usage) + '\n'

            return try_stream(generate(vo=request.environ.get('vo')), content_type='application/json')
        except RSENotFound as error:
            return generate_http_error_flask(404, error)


class Distance(ErrorHandlingMethodView):
    """ Create/Update and read distances between RSEs. """

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, source, destination):
        """
        ---
        summary: Get Rse Distances
        description: Returns the distances between a source and destination rse.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: source
          in: path
          description: The name of the source Rucio Storage Element.
          schema:
            type: string
          style: simple
        - name: destination
          in: path
          description: The name of the destination Rucio Storage Element.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: The distances between the Rses.
                  type: array
                  items:
                    type: object
                    description: One distance between source and destination.
                    properties:
                      src_rse_id:
                        description: The source rse id.
                        type: string
                      dest_rse_id:
                        description: The destination rse id.
                        type: string
                      distance:
                        description: The distance between RSEs.
                        type: integer
                      ranking:
                        deprecated: true
                        description: Same as distance.
                        type: integer
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            distance = get_distance(source=source, destination=destination, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
            return Response(dumps(distance, cls=APIEncoder), content_type="application/json")
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

    def post(self, source, destination):
        """
        ---
        summary: Create Rse Distance
        description: Post a rse distance.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: source
          in: path
          description: The name of the source Rucio Storage Element.
          schema:
            type: string
          style: simple
        - name: destination
          in: path
          description: The name of the destination Rucio Storage Element.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  distance:
                    description: The distance between RSEs.
                    type: integer
                  ranking:
                    deprecated: true
                    description: Same as distance.
                    type: integer
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        parameters = json_parameters()

        distance = param_get(parameters, 'distance', default=None)
        if distance is None:
            distance = param_get(parameters, 'ranking', default=None)

        try:
            add_distance(
                source=source,
                destination=destination,
                distance=distance,
                issuer=request.environ.get('issuer'),
                vo=request.environ.get('vo'),
            )
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)
        except Duplicate as error:
            return generate_http_error_flask(409, error)

        return 'Created', 201

    def put(self, source, destination):
        """
        ---
        summary: Update Rse Distance
        description: Update rse distance information.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: source
          in: path
          description: The name of the source Rucio Storage Element.
          schema:
            type: string
          style: simple
        - name: destination
          in: path
          description: The name of the destination Rucio Storage Element.
          schema:
            type: string
          style: simple
        requestBody:
          content:
            application/json:
              schema:
                type: object
                properties:
                  distance:
                    description: The distance between the RSEs.
                    type: integer
                  ranking:
                    deprecated: true
                    description: Same as distance.
                    type: integer
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        parameters = json_parameters()

        distance = param_get(parameters, 'distance', default=None)
        if distance is None:
            distance = param_get(parameters, 'ranking', default=None)

        try:
            update_distance(
                source=source,
                destination=destination,
                distance=distance,
                issuer=request.environ.get('issuer'),
                vo=request.environ.get('vo'),
            )
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return '', 200

    def delete(self, source, destination):
        """
        ---
        summary: Delete Rse Distance
        description: Delete distance information between source RSE and destination RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: source
          in: path
          description: The name of the source Rucio Storage Element.
          schema:
            type: string
          style: simple
        - name: destination
          in: path
          description: The name of the destination Rucio Storage Element.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Deleted"]
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            delete_distance(
                source=source,
                destination=destination,
                issuer=request.environ.get('issuer'),
                vo=request.environ.get('vo')
            )
        except AccessDenied as error:
            return generate_http_error_flask(401, error)
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return 'Deleted', 200


class QoSPolicy(ErrorHandlingMethodView):
    """ Add/Delete/List QoS policies on an RSE. """

    @check_accept_header_wrapper_flask(['application/json'])
    def post(self, rse, policy):
        """
        ---
        summary: Add QoS policy
        description: Add a QoS Policy to a RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: policy
          in: path
          description: The QoS policy to add to and rse.
          schema:
            type: string
          style: simple
        responses:
          201:
            description: OK
            content:
              application/json:
                schema:
                  type: string
                  enum: ["Created"]
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            add_qos_policy(rse=rse, qos_policy=policy, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return 'Created', 201

    @check_accept_header_wrapper_flask(['application/json'])
    def delete(self, rse, policy):
        """
        ---
        summary: Delete QoS Policy
        description: Delete QoS policy from RSE.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        - name: policy
          in: path
          description: The QoS policy to add to and rse.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        try:
            delete_qos_policy(rse=rse, qos_policy=policy, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
        except RSENotFound as error:
            return generate_http_error_flask(404, error)

        return '', 200

    @check_accept_header_wrapper_flask(['application/json'])
    def get(self, rse):
        """
        ---
        summary: Gett QoS Policies
        description: List all QoS policies for an Rse.
        tags:
          - Rucio Storage Elements
        parameters:
        - name: rse
          in: path
          description: The name of the Rucio Storage Element name.
          schema:
            type: string
          style: simple
        responses:
          200:
            description: OK
            content:
              application/json:
                schema:
                  description: A list with all the QoS policies for an Rse.
                  type: array
                  items:
                    type: object
                    properties:
                      rse_id:
                        description: The rse id.
                        type: string
                      qos_policy:
                        description: The qos policy.
                        type: string
          401:
            description: Invalid Auth Token
          404:
            description: Rse not found
          406:
            description: Not acceptable
        """
        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, error)


def blueprint():
    bp = AuthenticatedBlueprint('rses', __name__, url_prefix='/rses')

    attributes_view = Attributes.as_view('attributes')
    bp.add_url_rule('/<rse>/attr/<key>', view_func=attributes_view, methods=['post', 'delete'])
    bp.add_url_rule('/<rse>/attr/', view_func=attributes_view, methods=['get', ])
    distance_view = Distance.as_view('distance')
    bp.add_url_rule('/<source>/distances/<destination>', view_func=distance_view, methods=['get', 'post', 'put', 'delete'])
    protocol_view = Protocol.as_view('protocol')
    bp.add_url_rule('/<rse>/protocols/<scheme>/<hostname>/<port>', view_func=protocol_view, methods=['delete', 'put'])
    bp.add_url_rule('/<rse>/protocols/<scheme>/<hostname>', view_func=protocol_view, methods=['delete', 'put'])
    bp.add_url_rule('/<rse>/protocols/<scheme>', view_func=protocol_view, methods=['get', 'post', 'delete', 'put'])
    protocol_list_view = ProtocolList.as_view('protocol_list')
    bp.add_url_rule('/<rse>/protocols', view_func=protocol_list_view, methods=['get', ])
    lfns2pfns_view = LFNS2PFNS.as_view('lfns2pfns')
    bp.add_url_rule('/<rse>/lfns2pfns', view_func=lfns2pfns_view, methods=['get', ])
    rse_account_usage_limit_view = RSEAccountUsageLimit.as_view('rse_account_usage_limit')
    bp.add_url_rule('/<rse>/accounts/usage', view_func=rse_account_usage_limit_view, methods=['get', ])
    usage_view = Usage.as_view('usage')
    bp.add_url_rule('/<rse>/usage', view_func=usage_view, methods=['get', 'put'])
    usage_history_view = UsageHistory.as_view('usage_history')
    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', 'delete'])
    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')
    bp.add_url_rule('/', view_func=rses_view, methods=['get', ])

    bp.after_request(response_headers)
    return bp


def make_doc():
    """ Only used for sphinx documentation """
    doc_app = Flask(__name__)
    doc_app.register_blueprint(blueprint())
    return doc_app
