Skip to content

Commit

Permalink
Merge pull request #3596 from stfc/patch-3582-client_subscriptions_by…
Browse files Browse the repository at this point in the history
…_name

Subscriptions: Add client retrieval of subscriptions by name; Fix #3582
  • Loading branch information
bari12 committed May 28, 2020
2 parents 297fe41 + 5dcca66 commit f27f723
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib/rucio/client/subscriptionclient.py
Expand Up @@ -17,6 +17,7 @@
# - Martin Barisits <martin.barisits@cern.ch>, 2013-2018
# - Ralph Vigne <ralph.vigne@cern.ch>, 2015
# - Vincent Garonne <vgaronne@gmail.com>, 2015-2018
# - Eli Chadwick <eli.chadwick@stfc.ac.uk>, 2020
#
# PY3K COMPATIBLE

Expand Down Expand Up @@ -96,6 +97,8 @@ def list_subscriptions(self, name=None, account=None):
path += '/%s' % (account)
if name:
path += '/%s' % (name)
elif name:
path += '/Name/%s' % (name)
else:
path += '/'
url = build_url(choice(self.list_hosts), path=path)
Expand Down
22 changes: 21 additions & 1 deletion lib/rucio/tests/test_subscription.py
Expand Up @@ -21,6 +21,7 @@
# - Mario Lassnig <mario.lassnig@cern.ch>, 2018
# - Andrew Lister <andrew.lister@stfc.ac.uk>, 2019
# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2019
# - Eli Chadwick <eli.chadwick@stfc.ac.uk>, 2020

from __future__ import print_function

Expand All @@ -35,13 +36,14 @@
from rucio.common.exception import InvalidObject, SubscriptionNotFound, SubscriptionDuplicate
from rucio.common.types import InternalAccount, InternalScope
from rucio.common.utils import generate_uuid as uuid
from rucio.core.account import add_account
from rucio.core.account_limit import set_local_account_limit
from rucio.core.did import add_did, set_new_dids
from rucio.core.rse import add_rse
from rucio.core.rule import add_rule
from rucio.core.scope import add_scope
from rucio.daemons.transmogrifier.transmogrifier import run
from rucio.db.sqla.constants import DIDType
from rucio.db.sqla.constants import AccountType, DIDType
from rucio.web.rest.authentication import APP as auth_app
from rucio.web.rest.subscription import APP as subs_app

Expand Down Expand Up @@ -333,6 +335,24 @@ def test_update_nonexisting_subscription(self):
subscription_name = uuid()
self.sub_client.update_subscription(name=subscription_name, filter={'project': ['toto', ]})

def test_create_and_list_subscription_by_account(self):
""" SUBSCRIPTION (CLIENT): Test retrieval of subscriptions for an account """
subscription_name = uuid()
account_name = uuid()[:10]
add_account(InternalAccount(account_name), AccountType.USER, 'rucio@email.com')
subid = self.sub_client.add_subscription(name=subscription_name, account=account_name, filter={'project': self.projects, 'datatype': ['AOD', ], 'excluded_pattern': self.pattern1, 'account': ['tier0', ]},
replication_rules=[{'lifetime': 86400, 'rse_expression': 'MOCK|MOCK2', 'copies': 2, 'activity': 'Data Brokering'}], lifetime=100000, retroactive=0, dry_run=0, comments='Ni ! Ni!')
result = [sub['id'] for sub in self.sub_client.list_subscriptions(account=account_name)]
assert_equal(subid, result[0])

def test_create_and_list_subscription_by_name(self):
""" SUBSCRIPTION (CLIENT): Test retrieval of subscriptions for an account """
subscription_name = uuid()
subid = self.sub_client.add_subscription(name=subscription_name, account='root', filter={'project': self.projects, 'datatype': ['AOD', ], 'excluded_pattern': self.pattern1, 'account': ['tier0', ]},
replication_rules=[{'lifetime': 86400, 'rse_expression': 'MOCK|MOCK2', 'copies': 2, 'activity': 'Data Brokering'}], lifetime=100000, retroactive=0, dry_run=0, comments='Ni ! Ni!')
result = [sub['id'] for sub in self.sub_client.list_subscriptions(name=subscription_name)]
assert_equal(subid, result[0])

def test_run_transmogrifier(self):
""" SUBSCRIPTION (DAEMON): Test the transmogrifier and the split_rule mode """
tmp_scope = InternalScope('mock_' + uuid()[:8])
Expand Down
32 changes: 32 additions & 0 deletions lib/rucio/web/rest/flaskapi/v1/subscription.py
Expand Up @@ -18,6 +18,7 @@
# - Thomas Beermann <thomas.beermann@cern.ch>, 2014-2018
# - Mario Lassnig <mario.lassnig@cern.ch>, 2018
# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
# - Eli Chadwick <eli.chadwick@stfc.ac.uk>, 2020
#
# PY3K COMPATIBLE

Expand Down Expand Up @@ -153,6 +154,35 @@ def post(self, account, name):
return Response(subscription_id, status=201)


class SubscriptionName(MethodView):

@check_accept_header_wrapper_flask(['application/x-json-stream'])
def get(self, name=None):
"""
Retrieve a subscription by name.
.. :quickref: SubscriptionName; Get subscriptions by name.
:param name: The subscription name.
:resheader Content-Type: application/x-json-stream
:status 200: OK.
:status 401: Invalid Auth Token.
:status 404: Subscription Not Found.
:status 406: Not Acceptable.
:status 500: Internal Error.
:returns: Line separated list of dictionaries with subscription information.
"""
try:
data = ""
for subscription in list_subscriptions(name=name):
data += dumps(subscription, cls=APIEncoder) + '\n'
return Response(data, content_type="application/x-json-stream")
except SubscriptionNotFound as error:
return generate_http_error_flask(404, 'SubscriptionNotFound', error.args[0])
except Exception as error:
return error, 500


class Rules(MethodView):

@check_accept_header_wrapper_flask(['application/x-json-stream'])
Expand Down Expand Up @@ -270,6 +300,8 @@ def get(self, subscription_id):
bp.add_url_rule('/<account>/<name>', view_func=subscription_view, methods=['get', 'post', 'put'])
bp.add_url_rule('/<account>', view_func=subscription_view, methods=['get', ])
bp.add_url_rule('/', view_func=subscription_view, methods=['get', ])
subscription_name_view = SubscriptionName.as_view('subscription_name')
bp.add_url_rule('/Name/<name>', view_func=subscription_name_view, methods=['get', ])

application = Flask(__name__)
application.register_blueprint(bp)
Expand Down
38 changes: 38 additions & 0 deletions lib/rucio/web/rest/webpy/v1/subscription.py
Expand Up @@ -18,6 +18,7 @@
# - Thomas Beermann <thomas.beermann@cern.ch>, 2014
# - Mario Lassnig <mario.lassnig@cern.ch>, 2018
# - Hannes Hansen <hannes.jakob.hansen@cern.ch>, 2018-2019
# - Eli Chadwick <eli.chadwick@stfc.ac.uk>, 2020
#
# PY3K COMPATIBLE

Expand All @@ -41,6 +42,7 @@
'/(.*)/(.*)/Rules/States', 'States',
'/(.*)/Rules/States', 'States',
'/(.*)/(.*)/Rules', 'Rules',
'/Name/(.*)', 'SubscriptionName',
'/(.*)/(.*)', 'Subscription',
'/(.*)', 'Subscription',
'/', 'Subscription',
Expand Down Expand Up @@ -163,6 +165,42 @@ def DELETE(self):
raise BadRequest()


class SubscriptionName:

@check_accept_header_wrapper(['application/x-json-stream'])
def GET(self, name):
"""
Retrieve a subscription by name.
HTTP Success:
200 OK
HTTP Error:
404 Not Found
500 Internal Error
406 Not Acceptable
:param name: The subscription name.
"""
header('Content-Type', 'application/x-json-stream')
try:
for subscription in list_subscriptions(name=name):
yield dumps(subscription, cls=APIEncoder) + '\n'
except SubscriptionNotFound as error:
raise generate_http_error(404, 'SubscriptionNotFound', error.args[0])
except Exception as error:
raise InternalError(error)

def PUT(self, name):
raise BadRequest()

def POST(self, name):
raise BadRequest()

def DELETE(self):
raise BadRequest()


class Rules:

@check_accept_header_wrapper(['application/x-json-stream'])
Expand Down

0 comments on commit f27f723

Please sign in to comment.