Skip to content

Commit 8917fcd

Browse files
voetbergrdimaio
authored andcommitted
Client: Replace failure mapping to 1 to raising an error in cli functions rucio#7720
* Removes instances of a function in bin_legacy returning a FAILURE status when raising an error is more descriptive * Raises a "InputValidationError" when conflicting arguments are given * Raises a specific error when schemas cannot be validated or objects not found` * Raises a general "RucioException" with a verbose description when no existing error matches * Removes error catches that result in returning FAILURE * Expands the "@exception_handler" decorator to catch input validation errors Note: All exitcode=1's are still in place, just shifted to being handled in the exception_handler, instead of being handled twice.
1 parent e50362f commit 8917fcd

File tree

3 files changed

+64
-123
lines changed

3 files changed

+64
-123
lines changed

lib/rucio/cli/bin_legacy/rucio.py

Lines changed: 38 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@
2020
import signal
2121
import sys
2222
import time
23-
import traceback
2423
import unittest
2524
import uuid
2625
from copy import deepcopy
2726
from datetime import datetime
28-
from logging import DEBUG
2927
from typing import TYPE_CHECKING, Optional
3028

3129
from rich.console import Console
@@ -45,13 +43,13 @@
4543
from rucio.common.config import config_get, config_get_float
4644
from rucio.common.constants import ReplicaState
4745
from rucio.common.exception import (
48-
DIDFilterSyntaxError,
49-
DuplicateCriteriaInDIDFilter,
5046
DuplicateRule,
5147
InputValidationError,
5248
InvalidObject,
5349
InvalidType,
50+
RSENotFound,
5451
RucioException,
52+
ScopeNotFound,
5553
UnsupportedOperation,
5654
)
5755
from rucio.common.extra import import_extras
@@ -109,8 +107,7 @@ def ping(args, client, logger, console, spinner):
109107
if server_info:
110108
print(server_info['version'])
111109
return SUCCESS
112-
logger.error('Ping failed')
113-
return FAILURE
110+
raise RucioException('Ping failed')
114111

115112

116113
@exception_handler
@@ -232,11 +229,9 @@ def list_file_replicas(args, client, logger, console, spinner):
232229
table_data = []
233230
dids = []
234231
if args.missing and not args.rses:
235-
print('Cannot use --missing without specifying a RSE')
236-
return FAILURE
232+
raise InputValidationError('Cannot use --missing without specifying a RSE')
237233
if args.link and ':' not in args.link:
238-
print('The substitution parameter must equal --link="/pfn/dir:/dst/dir"')
239-
return FAILURE
234+
raise ValueError('The substitution parameter must equal --link="/pfn/dir:/dst/dir"')
240235

241236
if cli_config == 'rich':
242237
spinner.update(status='Fetching file replicas')
@@ -398,14 +393,13 @@ def attach(args, client, logger, console, spinner):
398393

399394
if args.fromfile:
400395
if len(dids) > 1:
401-
logger.error("If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line.")
402-
return FAILURE
396+
raise ValueError('If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line.')
403397
try:
404398
f = open(dids[0], 'r')
405399
dids = [did.rstrip() for did in f.readlines()]
406-
except OSError:
400+
except OSError as error:
407401
logger.error("Can't open file '" + dids[0] + "'.")
408-
return FAILURE
402+
raise OSError from error
409403

410404
dids = [{'scope': get_scope(did, client)[0], 'name': get_scope(did, client)[1]} for did in dids]
411405
if len(dids) <= limit:
@@ -468,35 +462,16 @@ def list_dids(args, client, logger, console, spinner):
468462
name = '*'
469463

470464
if scope not in client.list_scopes():
471-
logger.error('Scope not found.')
472-
return FAILURE
465+
raise ScopeNotFound
473466

474467
if args.recursive and '*' in name:
475-
logger.error('Option recursive cannot be used with wildcards.')
476-
return FAILURE
468+
raise InputValidationError('Option recursive cannot be used with wildcards.')
477469
else:
478470
if filters:
479471
if ('name' in filters) and (name != '*'):
480-
logger.error('Must have a wildcard in did name if filtering by name.')
481-
return FAILURE
472+
raise ValueError('Must have a wildcard in did name if filtering by name.')
482473

483-
try:
484-
filters, type_ = parse_did_filter_from_string_fe(args.filter, name)
485-
except InvalidType as error:
486-
logger.error(error)
487-
return FAILURE
488-
except DuplicateCriteriaInDIDFilter as error:
489-
logger.error(error)
490-
return FAILURE
491-
except DIDFilterSyntaxError as error:
492-
logger.error(error)
493-
return FAILURE
494-
except ValueError as error:
495-
logger.error(error)
496-
return FAILURE
497-
except Exception as e:
498-
logger.error(e)
499-
return FAILURE
474+
filters, type_ = parse_did_filter_from_string_fe(args.filter, name)
500475

501476
if cli_config == 'rich':
502477
spinner.update(status='Fetching DIDs')
@@ -531,8 +506,7 @@ def list_dids_extended(args, client, logger, console, spinner):
531506
532507
List the data identifiers for a given scope (DEPRECATED).
533508
"""
534-
logger.error("This command has been deprecated. Please use list_dids instead.")
535-
return FAILURE
509+
raise UnsupportedOperation('This command has been deprecated. Please use list_dids instead.')
536510

537511

538512
@exception_handler
@@ -813,8 +787,7 @@ def list_parent_dids(args, client, logger, console, spinner):
813787
else:
814788
print(tabulate(table_data, tablefmt=tablefmt, headers=['SCOPE:NAME', '[DID TYPE]']))
815789
else:
816-
print('At least one option has to be given. Use -h to list the options.')
817-
return FAILURE
790+
raise InputValidationError('At least one option has to be given. Use -h to list the options.')
818791
return SUCCESS
819792

820793

@@ -931,13 +904,11 @@ def upload(args, client, logger, console, spinner):
931904
Upload files into Rucio
932905
"""
933906
if args.lifetime and args.expiration_date:
934-
logger.error("--lifetime and --expiration-date cannot be specified at the same time.")
935-
return FAILURE
907+
raise InputValidationError("--lifetime and --expiration-date cannot be specified at the same time.")
936908
elif args.expiration_date:
937909
expiration_date = datetime.strptime(args.expiration_date, "%Y-%m-%d-%H:%M:%S")
938910
if expiration_date < datetime.utcnow():
939-
logger.error("The specified expiration date should be in the future!")
940-
return FAILURE
911+
raise ValueError("The specified expiration date should be in the future!")
941912
args.lifetime = (expiration_date - datetime.utcnow()).total_seconds()
942913

943914
dsscope = None
@@ -1023,23 +994,18 @@ def download(args, client, logger, console, spinner):
1023994
"""
1024995
# Input validation
1025996
if not args.dids and not args.filter and not args.metalink_file:
1026-
logger.error('At least one did is mandatory')
1027-
return FAILURE
997+
raise InputValidationError('At least one did is mandatory')
1028998
elif not args.dids and args.filter and not args.scope:
1029-
logger.error('The argument scope is mandatory')
1030-
return FAILURE
999+
raise InputValidationError('The argument scope is mandatory')
10311000

10321001
if args.filter and args.metalink_file:
1033-
logger.error('Arguments filter and metalink cannot be used together.')
1034-
return FAILURE
1002+
raise InputValidationError('Arguments filter and metalink cannot be used together.')
10351003

10361004
if args.dids and args.metalink_file:
1037-
logger.error('Arguments dids and metalink cannot be used together.')
1038-
return FAILURE
1005+
raise InputValidationError('Arguments dids and metalink cannot be used together.')
10391006

10401007
if args.ignore_checksum and args.check_local_with_filesize_only:
1041-
logger.error('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.')
1042-
return FAILURE
1008+
raise InputValidationError('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.')
10431009

10441010
trace_pattern = {}
10451011

@@ -1083,16 +1049,12 @@ def download(args, client, logger, console, spinner):
10831049
filters, type_ = parse_did_filter_from_string(args.filter)
10841050
if args.scope:
10851051
filters['scope'] = args.scope
1086-
except InvalidType as error:
1087-
logger.error(error)
1088-
return FAILURE
1089-
except ValueError as error:
1052+
except (InvalidType, ValueError) as error:
10901053
logger.error(error)
1091-
return FAILURE
1054+
raise error
10921055
except Exception as error:
1093-
logger.error(error)
10941056
logger.error("Invalid Filter. Filter must be 'key=value', 'key>=value', 'key>value', 'key<=value', 'key<value'")
1095-
return FAILURE
1057+
raise error
10961058
item_defaults['filters'] = filters
10971059

10981060
if not args.pfn:
@@ -1148,8 +1110,7 @@ def download(args, client, logger, console, spinner):
11481110

11491111
download_rse = _get_rse_for_pfn(replicas, args.pfn)
11501112
if download_rse is None:
1151-
logger.error("Could not find RSE for pfn %s", args.pfn)
1152-
return FAILURE
1113+
raise RSENotFound("Could not find RSE for pfn %s" % args.pfn)
11531114
else:
11541115
item_defaults['rse'] = download_rse
11551116

@@ -1354,8 +1315,7 @@ def delete_rule(args, client, logger, console, spinner):
13541315
except ValueError:
13551316
# Otherwise, trying to extract the scope, name from args.rule_id
13561317
if not args.rses:
1357-
logger.error('A RSE expression must be specified if you do not provide a rule_id but a DID')
1358-
return FAILURE
1318+
raise InputValidationError('A RSE expression must be specified if you do not provide a rule_id but a DID')
13591319
scope, name = get_scope(args.rule_id, client)
13601320
rules = client.list_did_rules(scope=scope, name=name)
13611321
if args.rule_account is None:
@@ -1372,8 +1332,7 @@ def delete_rule(args, client, logger, console, spinner):
13721332
client.delete_replication_rule(rule_id=rule['id'], purge_replicas=args.purge_replicas)
13731333
deletion_success = True
13741334
if not deletion_success:
1375-
logger.error('No replication rule was deleted from the DID')
1376-
return FAILURE
1335+
raise RucioException('No replication rule was deleted from the DID')
13771336
return SUCCESS
13781337

13791338

@@ -1394,8 +1353,8 @@ def update_rule(args, client, logger, console, spinner):
13941353
elif args.locked.title() == "False":
13951354
options['locked'] = False
13961355
else:
1397-
logger.error('Locked must be True or False')
1398-
return FAILURE
1356+
raise InputValidationError('Locked must be True or False')
1357+
13991358
if args.comment:
14001359
options['comment'] = args.comment
14011360
if args.rule_account:
@@ -1410,8 +1369,7 @@ def update_rule(args, client, logger, console, spinner):
14101369
options['source_replica_expression'] = None if args.source_replica_expression.lower() == 'none' else args.source_replica_expression
14111370
if args.cancel_requests:
14121371
if 'state' not in options:
1413-
logger.error('--stuck or --suspend must be specified when running --cancel-requests')
1414-
return FAILURE
1372+
raise InputValidationError('--stuck or --suspend must be specified when running --cancel-requests')
14151373
options['cancel_requests'] = True
14161374
if args.priority:
14171375
options['priority'] = int(args.priority)
@@ -1582,8 +1540,7 @@ def list_rules(args, client, logger, console, spinner):
15821540
name = args.subscription[1]
15831541
rules = client.list_subscription_rules(account=account, name=name)
15841542
else:
1585-
print('At least one option has to be given. Use -h to list the options.')
1586-
return FAILURE
1543+
raise InputValidationError('At least one option has to be given. Use -h to list the options.')
15871544
if args.csv:
15881545
for rule in rules:
15891546
print(rule['id'],
@@ -1990,21 +1947,18 @@ def add_lifetime_exception(args, client, logger, console, spinner):
19901947
"""
19911948

19921949
if not args.reason:
1993-
logger.error('reason for the extension is mandatory')
1994-
return FAILURE
1950+
raise InputValidationError('reason for the extension is mandatory')
19951951
reason = args.reason
19961952
if not args.expiration:
1997-
logger.error('expiration is mandatory')
1998-
return FAILURE
1953+
raise InputValidationError('expiration is mandatory')
19991954
try:
20001955
expiration = datetime.strptime(args.expiration, "%Y-%m-%d")
20011956
except Exception as err:
2002-
logger.error(err)
2003-
return FAILURE
1957+
msg = f'Cannot parse expiration date: {err}'
1958+
raise ValueError(msg)
20041959

20051960
if not args.inputfile:
2006-
logger.error('inputfile is mandatory')
2007-
return FAILURE
1961+
raise InputValidationError('inputfile is mandatory')
20081962
with open(args.inputfile) as infile:
20091963
# Deduplicate the content of the input file and ignore empty lines.
20101964
dids = set(did for line in infile if (did := line.strip()))
@@ -2067,22 +2021,8 @@ def add_lifetime_exception(args, client, logger, console, spinner):
20672021
if not datasets:
20682022
logger.error('Nothing to submit')
20692023
return SUCCESS
2070-
try:
2071-
client.add_exception(dids=datasets, account=client.account, pattern='', comments=reason, expires_at=expiration)
2072-
except UnsupportedOperation as err:
2073-
logger.error(err)
2074-
return FAILURE
2075-
except Exception:
2076-
error_message = 'Failure to submit exception. Please retry.'
2077-
if cli_config == 'rich':
2078-
if logger.level == DEBUG:
2079-
logger.exception(error_message)
2080-
else:
2081-
logger.error(error_message)
2082-
else:
2083-
logger.error(error_message)
2084-
logger.debug(traceback.format_exc())
2085-
return FAILURE
2024+
2025+
client.add_exception(dids=datasets, account=client.account, pattern='', comments=reason, expires_at=expiration)
20862026

20872027
logger.info('Exception successfully submitted. Summary below:')
20882028
for key, data in error_summary.items():

0 commit comments

Comments
 (0)