rucio-clients 35.7.0__py3-none-any.whl → 37.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rucio-clients might be problematic. Click here for more details.

Files changed (85) hide show
  1. rucio/alembicrevision.py +1 -1
  2. rucio/cli/__init__.py +14 -0
  3. rucio/cli/account.py +216 -0
  4. rucio/cli/bin_legacy/__init__.py +13 -0
  5. rucio_clients-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
  6. rucio_clients-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
  7. rucio/cli/command.py +272 -0
  8. rucio/cli/config.py +72 -0
  9. rucio/cli/did.py +191 -0
  10. rucio/cli/download.py +128 -0
  11. rucio/cli/lifetime_exception.py +33 -0
  12. rucio/cli/replica.py +162 -0
  13. rucio/cli/rse.py +293 -0
  14. rucio/cli/rule.py +158 -0
  15. rucio/cli/scope.py +40 -0
  16. rucio/cli/subscription.py +73 -0
  17. rucio/cli/upload.py +60 -0
  18. rucio/cli/utils.py +226 -0
  19. rucio/client/accountclient.py +0 -1
  20. rucio/client/baseclient.py +33 -24
  21. rucio/client/client.py +45 -1
  22. rucio/client/didclient.py +5 -3
  23. rucio/client/downloadclient.py +6 -8
  24. rucio/client/replicaclient.py +0 -2
  25. rucio/client/richclient.py +317 -0
  26. rucio/client/rseclient.py +4 -4
  27. rucio/client/uploadclient.py +26 -12
  28. rucio/common/bittorrent.py +234 -0
  29. rucio/common/cache.py +66 -29
  30. rucio/common/checksum.py +168 -0
  31. rucio/common/client.py +122 -0
  32. rucio/common/config.py +22 -35
  33. rucio/common/constants.py +61 -3
  34. rucio/common/didtype.py +72 -24
  35. rucio/common/exception.py +65 -8
  36. rucio/common/extra.py +5 -10
  37. rucio/common/logging.py +13 -13
  38. rucio/common/pcache.py +8 -7
  39. rucio/common/plugins.py +59 -27
  40. rucio/common/policy.py +12 -3
  41. rucio/common/schema/__init__.py +84 -34
  42. rucio/common/schema/generic.py +0 -17
  43. rucio/common/schema/generic_multi_vo.py +0 -17
  44. rucio/common/test_rucio_server.py +12 -6
  45. rucio/common/types.py +132 -52
  46. rucio/common/utils.py +93 -643
  47. rucio/rse/__init__.py +3 -3
  48. rucio/rse/protocols/bittorrent.py +11 -1
  49. rucio/rse/protocols/cache.py +0 -11
  50. rucio/rse/protocols/dummy.py +0 -11
  51. rucio/rse/protocols/gfal.py +14 -9
  52. rucio/rse/protocols/globus.py +1 -1
  53. rucio/rse/protocols/http_cache.py +1 -1
  54. rucio/rse/protocols/posix.py +2 -2
  55. rucio/rse/protocols/protocol.py +84 -317
  56. rucio/rse/protocols/rclone.py +2 -1
  57. rucio/rse/protocols/rfio.py +10 -1
  58. rucio/rse/protocols/ssh.py +2 -1
  59. rucio/rse/protocols/storm.py +2 -13
  60. rucio/rse/protocols/webdav.py +74 -30
  61. rucio/rse/protocols/xrootd.py +2 -1
  62. rucio/rse/rsemanager.py +170 -53
  63. rucio/rse/translation.py +260 -0
  64. rucio/vcsversion.py +4 -4
  65. rucio/version.py +7 -0
  66. {rucio_clients-35.7.0.data → rucio_clients-37.0.0.data}/data/etc/rucio.cfg.atlas.client.template +3 -2
  67. {rucio_clients-35.7.0.data → rucio_clients-37.0.0.data}/data/etc/rucio.cfg.template +3 -19
  68. {rucio_clients-35.7.0.data → rucio_clients-37.0.0.data}/data/requirements.client.txt +11 -7
  69. rucio_clients-37.0.0.data/scripts/rucio +133 -0
  70. rucio_clients-37.0.0.data/scripts/rucio-admin +97 -0
  71. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0.dist-info}/METADATA +18 -14
  72. rucio_clients-37.0.0.dist-info/RECORD +104 -0
  73. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0.dist-info}/licenses/AUTHORS.rst +3 -0
  74. rucio/common/schema/atlas.py +0 -413
  75. rucio/common/schema/belleii.py +0 -408
  76. rucio/common/schema/domatpc.py +0 -401
  77. rucio/common/schema/escape.py +0 -426
  78. rucio/common/schema/icecube.py +0 -406
  79. rucio/rse/protocols/gsiftp.py +0 -92
  80. rucio_clients-35.7.0.dist-info/RECORD +0 -88
  81. {rucio_clients-35.7.0.data → rucio_clients-37.0.0.data}/data/etc/rse-accounts.cfg.template +0 -0
  82. {rucio_clients-35.7.0.data → rucio_clients-37.0.0.data}/data/rucio_client/merge_rucio_configs.py +0 -0
  83. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0.dist-info}/WHEEL +0 -0
  84. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0.dist-info}/licenses/LICENSE +0 -0
  85. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0.dist-info}/top_level.txt +0 -0
rucio/cli/command.py ADDED
@@ -0,0 +1,272 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import importlib
15
+ import signal
16
+ import time
17
+
18
+ import click
19
+ from rich.console import Console
20
+ from rich.status import Status
21
+ from rich.theme import Theme
22
+ from rich.traceback import install
23
+
24
+ from rucio import version
25
+ from rucio.cli.bin_legacy.rucio import ping, test_server, whoami_account
26
+ from rucio.cli.utils import Arguments, exception_handler, get_client, setup_gfal2_logger, signal_handler
27
+ from rucio.client.richclient import MAX_TRACEBACK_WIDTH, MIN_CONSOLE_WIDTH, CLITheme, get_cli_config, get_pager, setup_rich_logger
28
+ from rucio.common.utils import setup_logger
29
+
30
+
31
+ # Taken directly from https://click.palletsprojects.com/en/stable/complex/#defining-the-lazy-group
32
+ class LazyGroup(click.Group):
33
+ def __init__(self, *args, lazy_subcommands=None, **kwargs):
34
+ super().__init__(*args, **kwargs)
35
+ # lazy_subcommands is a map of the form:
36
+ #
37
+ # {command-name} -> {module-name}.{command-object-name}
38
+ #
39
+ self.lazy_subcommands = lazy_subcommands or {}
40
+
41
+ def list_commands(self, ctx):
42
+ base = super().list_commands(ctx)
43
+ lazy = sorted(self.lazy_subcommands.keys())
44
+ return base + lazy
45
+
46
+ def get_command(self, ctx, cmd_name):
47
+ if cmd_name in self.lazy_subcommands:
48
+ return self._lazy_load(cmd_name)
49
+ return super().get_command(ctx, cmd_name)
50
+
51
+ def _lazy_load(self, cmd_name):
52
+ # lazily loading a command, first get the module name and attribute name
53
+ import_path = self.lazy_subcommands[cmd_name]
54
+ modname, cmd_object_name = import_path.rsplit(".", 1)
55
+ # do the import
56
+ mod = importlib.import_module(modname)
57
+ # get the Command object from that module
58
+ cmd_object = getattr(mod, cmd_object_name)
59
+ # check the result to make debugging easier
60
+ if not isinstance(cmd_object, click.BaseCommand):
61
+ raise ValueError(f"Lazy loading of {import_path} failed by returning " "a non-command object")
62
+ return cmd_object
63
+
64
+
65
+ @click.group(
66
+ cls=LazyGroup,
67
+ lazy_subcommands={
68
+ "account": "rucio.cli.account.account",
69
+ "config": "rucio.cli.config.config",
70
+ "did": "rucio.cli.did.did",
71
+ "download": "rucio.cli.download.download",
72
+ "lifetime-exception": "rucio.cli.lifetime_exception.lifetime_exception",
73
+ "replica": "rucio.cli.replica.replica",
74
+ "rse": "rucio.cli.rse.rse",
75
+ "rule": "rucio.cli.rule.rule",
76
+ "scope": "rucio.cli.scope.scope",
77
+ "subscription": "rucio.cli.subscription.subscription",
78
+ "upload": "rucio.cli.upload.upload_command",
79
+ },
80
+ context_settings={"help_option_names": ["-h", "--help"]}
81
+ ) # TODO: Implement https://click.palletsprojects.com/en/stable/options/#dynamic-defaults-for-prompts for args from config or os
82
+ @click.option("--account", "--issuer", "issuer", help="Rucio account to use.")
83
+ @click.option("--auth-host", help="The Rucio Authentication host")
84
+ @click.option("-S", "--auth-strategy", help="Authentication strategy", type=click.Choice(['userpass', 'x509', 'x509_proxy', 'gss', 'ssh', 'saml', 'oidc']))
85
+ # x509 and x509 proxy auth
86
+ @click.option("--ca-certificate", help="CA certificate to verify peer against (SSL)")
87
+ @click.option("--certificate", help="Client certificate file")
88
+ @click.option("--client-key", help="Client key for x509 Authentication")
89
+ @click.option("--config", help="The Rucio configuration file to use")
90
+ @click.option("-H", "--host", help="The Rucio API host")
91
+ # oidc auth
92
+ @click.option("--oidc-user", help="OIDC username")
93
+ @click.option("--oidc-password", help="OIDC password")
94
+ @click.option("--oidc-audience", help="Defines which audience are tokens requested for.")
95
+ @click.option(
96
+ "--oidc-auto",
97
+ is_flag=True,
98
+ default=False,
99
+ help="""
100
+ If not specified, username and password credentials are not required and users will be given a URL to use in their browser.
101
+ If specified, the users explicitly trust Rucio with their IdP credentials"
102
+ """,
103
+ )
104
+ @click.option(
105
+ "--oidc-issuer",
106
+ help="""
107
+ Defines which Identity Provider is going to be used.
108
+ The issuer string must correspond to the keys configured in the /etc/idpsecrets.json auth server configuration file")
109
+ """,
110
+ )
111
+ @click.option(
112
+ "--oidc-polling",
113
+ is_flag=True,
114
+ default=False,
115
+ help="""
116
+ If not specified, user will be asked to enter a code returned by the browser to the command line.
117
+ If --polling is set, Rucio Client should get the token without any further interaction of the user.
118
+ This option is active only if --auto is *not* specified
119
+ """,
120
+ )
121
+ @click.option(
122
+ "--oidc-refresh-lifetime",
123
+ help="""
124
+ Max lifetime in hours for this access token; the token will be refreshed by an asynchronous Rucio daemon.
125
+ If not specified, refresh will be stopped after 4 days.
126
+ This option is effective only if --oidc-scope includes offline_access scope for a refresh token to be granted to Rucio
127
+ """,
128
+ )
129
+ @click.option(
130
+ "--oidc-scope",
131
+ default="openid profile",
132
+ help="""
133
+ Defines which (OIDC) information user will share with Rucio. Rucio requires at least -sc='openid profile'.
134
+ To request refresh token for Rucio, scope must include 'openid offline_access'
135
+ and there must be no active access token saved on the side of the currently used Rucio Client,
136
+ """,
137
+ )
138
+ @click.option("-T", "--timeout", type=float, help="Set all timeout values to seconds")
139
+ @click.option("-U", "--user-agent", default="rucio-clients", help="Rucio User Agent")
140
+ # userpass/gss/saml auth
141
+ @click.option("-u", "--user", help="Username for userpass")
142
+ @click.option("--password", help="Password for userpass")
143
+ @click.option("--vo", help="VO to authenticate at. Only used in multi-VO mode")
144
+ @click.option("-v", "--verbose", default=False, is_flag=True, help="Print more verbose output")
145
+ @click.version_option(version.version_string(), message="%(prog)s %(version)s")
146
+ # Hidden options at the end
147
+ @click.option("--pager", is_flag=True, default=False, hidden=True)
148
+ @exception_handler
149
+ @click.pass_context
150
+ def main(
151
+ ctx,
152
+ config,
153
+ verbose,
154
+ host,
155
+ auth_host,
156
+ issuer,
157
+ auth_strategy,
158
+ timeout,
159
+ user_agent,
160
+ vo,
161
+ pager,
162
+ user,
163
+ password,
164
+ oidc_user,
165
+ oidc_password,
166
+ oidc_scope,
167
+ oidc_audience,
168
+ oidc_auto,
169
+ oidc_polling,
170
+ oidc_refresh_lifetime,
171
+ oidc_issuer,
172
+ certificate,
173
+ client_key,
174
+ ca_certificate,
175
+ ):
176
+ ctx.ensure_object(Arguments)
177
+ ctx.obj.start_time = time.time()
178
+
179
+ ctx.obj.no_pager = not pager
180
+ pager = get_pager()
181
+ use_rich = get_cli_config() == "rich"
182
+
183
+ console = Console(theme=Theme(CLITheme.LOG_THEMES), soft_wrap=True)
184
+ console.width = max(MIN_CONSOLE_WIDTH, console.width)
185
+ spinner = Status("Initializing spinner", spinner=CLITheme.SPINNER, spinner_style=CLITheme.SPINNER_STYLE, console=console)
186
+
187
+ ctx.obj.use_rich = use_rich
188
+ ctx.obj.spinner = spinner
189
+ ctx.obj.console = console
190
+ ctx.obj.pager = pager
191
+
192
+ if use_rich:
193
+ install(console=console, word_wrap=True, width=min(console.width, MAX_TRACEBACK_WIDTH)) # Make rich exception tracebacks the default.
194
+ logger = setup_rich_logger(module_name=__name__, logger_name="user", verbose=verbose, console=console)
195
+ else:
196
+ logger = setup_logger(module_name=__name__, logger_name="user", verbose=verbose)
197
+ args = Arguments(
198
+ {
199
+ "config": config,
200
+ "host": host,
201
+ "isser": issuer,
202
+ "auth_host": auth_host,
203
+ "auth_strategy": auth_strategy,
204
+ "timeout": timeout,
205
+ "user_agent": user_agent,
206
+ "VO": vo,
207
+ "username": user,
208
+ "password": password,
209
+ "oidc_username": oidc_user,
210
+ "oidc_password": oidc_password,
211
+ "oidc_scope": oidc_scope,
212
+ "oidc_audience": oidc_audience,
213
+ "oidc_auto": oidc_auto,
214
+ "oidc_polling": oidc_polling,
215
+ "oidc_refresh_lifetime": oidc_refresh_lifetime,
216
+ "oidc_issuer": oidc_issuer,
217
+ "certificate": certificate,
218
+ "client_key": client_key,
219
+ "ca_certificate": ca_certificate,
220
+ }
221
+ ) # TODO Future improvement - change `get_client` to take these args directly
222
+ client = get_client(args, logger) # TODO Future improvement - use envvar functionality in click to remove conditionals checking env vars
223
+
224
+ setup_gfal2_logger()
225
+ signal.signal(signal.SIGINT, lambda sig, frame: signal_handler(sig, frame, logger))
226
+
227
+ ctx.obj.client = client
228
+ ctx.obj.logger = logger
229
+
230
+ ctx.call_on_close(_teardown)
231
+
232
+
233
+ @click.pass_context
234
+ def _teardown(ctx):
235
+ time_elapsed = time.time() - ctx.obj.start_time
236
+
237
+ if ctx.obj.use_rich:
238
+ ctx.obj.spinner.stop()
239
+ if ctx.obj.console.is_terminal and not ctx.obj.no_pager:
240
+ command_output = ctx.obj.console.end_capture()
241
+ if command_output == "" and ctx.obj.verbose:
242
+ print("Completed in %-0.4f sec." % (time_elapsed))
243
+ else:
244
+ if ctx.obj.verbose:
245
+ command_output += "Completed in %-0.4f sec." % (time_elapsed)
246
+ # Ignore SIGINT during pager execution.
247
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
248
+ ctx.obj.pager(command_output)
249
+ else:
250
+ if ctx.obj.verbose:
251
+ print("Completed in %-0.4f sec." % (time_elapsed))
252
+
253
+
254
+ @main.command(name="whoami", help="Get information about account whose token is used")
255
+ @click.pass_context
256
+ def exe_whoami(ctx):
257
+ args = Arguments()
258
+ whoami_account(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
259
+
260
+
261
+ @main.command(name="ping", help="Ping Rucio server")
262
+ @click.pass_context
263
+ def exe_ping(ctx):
264
+ args = Arguments()
265
+ ping(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
266
+
267
+
268
+ @main.command(name="test-server", help="Test client against the server")
269
+ @click.pass_context
270
+ def exe_test_server(ctx):
271
+ args = Arguments()
272
+ test_server(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
rucio/cli/config.py ADDED
@@ -0,0 +1,72 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import click
15
+
16
+ from rucio.cli.bin_legacy.rucio_admin import delete_config_option, get_config, set_config_option
17
+ from rucio.cli.utils import Arguments
18
+
19
+
20
+ @click.group()
21
+ def config():
22
+ "Modify the configuration table"
23
+
24
+
25
+ # TODO Limit to just the section names
26
+ @config.command("list")
27
+ @click.option("-s", "--section", help="Filter by sections")
28
+ @click.option("-k", "--key", help="Show key's value, section required.")
29
+ @click.pass_context
30
+ def list_(ctx, section, key):
31
+ """List the sections or content of sections in the rucio.cfg"""
32
+ get_config(Arguments({"section": section, "key": key}), ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
33
+
34
+
35
+ # TODO Change to only add new fields and cannot modify an existing field
36
+ @config.command("add")
37
+ @click.option("-s", "--section", help="Section name", required=True)
38
+ @click.option('--key', help='Attribute key', required=True)
39
+ @click.option('--value', help='Attribute value', required=True)
40
+ @click.pass_context
41
+ def add_(ctx, section, key, value):
42
+ """
43
+ Add a new key/value to a section.
44
+
45
+ \b
46
+ Example, Add a key to an existing section:
47
+ $ rucio config add --section my-section --key key --value value
48
+ """
49
+ args = Arguments({"section": section, "option": key, "value": value})
50
+ set_config_option(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
51
+
52
+
53
+ @config.command("remove")
54
+ @click.option("-s", "--section", help="Section", required=True)
55
+ @click.option("-k", "--key", help="Key in section", required=True)
56
+ @click.pass_context
57
+ def remove(ctx, section, key):
58
+ """Remove the section.key from the config."""
59
+ args = Arguments({"section": section, "option": key})
60
+ delete_config_option(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
61
+
62
+
63
+ # @config.command("show")
64
+ # @click.pass_context
65
+ def show(ctx):
66
+ """Show a single sections options"""
67
+
68
+
69
+ # TODO Change this so that it only modifies existing fields
70
+ def update():
71
+ """Modify an existing command"""
72
+ pass
rucio/cli/did.py ADDED
@@ -0,0 +1,191 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import click
15
+
16
+ from rucio.cli.bin_legacy.rucio import add_container, add_dataset, attach, close, delete_metadata, detach, erase, get_metadata, list_content, list_content_history, list_dids, list_parent_dids, reopen, set_metadata, stat, touch
17
+ from rucio.cli.utils import Arguments
18
+
19
+
20
+ @click.group()
21
+ def did():
22
+ """Manage Data Identifiers - the source data objects"""
23
+
24
+
25
+ @did.command("list")
26
+ @click.option("-r", "--recursive", default=False, is_flag=True, help="List data identifiers recursively.")
27
+ @click.option(
28
+ "--filter", "filter_",
29
+ help="""
30
+ Single or logically combined filtering expression(s) either in the form <key><operator><value>
31
+ or <value1><operator1><key><operator2><value2> (compound inequality).
32
+ Keys are equivalent to columns in the DID table.
33
+ Operators must belong to the set of (<=, >=, ==, !=, >, <). The following conventions for combining expressions
34
+ are used: ";" represents the logical OR operator; "," represents the logical AND operator',
35
+ """,
36
+ ) # TODO Shorten this help and make supplying this easier
37
+ @click.option("--short", is_flag=True, default=False, help="Dump the list of DIDs")
38
+ @click.argument("did-pattern", nargs=-1)
39
+ @click.option("--parent", default=False, is_flag=True, help="List the parents of the DID - must use a full DID scope and name")
40
+ # TODO Implement or remove option - view https://github.com/rucio/rucio/issues/7230
41
+ @click.option("--pfn", hidden=True)
42
+ @click.option("--guid", hidden=True)
43
+ @click.pass_context
44
+ def list_(ctx, did_pattern, recursive, filter_, short, parent, pfn, guid):
45
+ """
46
+ List the Data IDentifiers matching certain pattern.
47
+ Only the collections (i.e. dataset or container) are returned by default.
48
+ With the filter option, you can specify a list of metadata that the Data IDentifier should match
49
+ """
50
+ if parent:
51
+ for did in did_pattern:
52
+ list_parent_dids(Arguments({"did": did}), ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
53
+ else:
54
+ args = Arguments({"did": did_pattern, "recursive": recursive, "filter": filter_, "short": short})
55
+ list_dids(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
56
+
57
+
58
+ @did.command("show")
59
+ @click.argument("dids", nargs=-1)
60
+ @click.pass_context
61
+ def show(ctx, dids):
62
+ """List attributes, statuses, or parents for data identifiers"""
63
+ stat(Arguments({"dids": dids}), ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
64
+
65
+
66
+ @did.command("add")
67
+ @click.argument("did-name")
68
+ @click.option("--type", "dtype", type=click.Choice(["container", "dataset"]))
69
+ @click.option("--monotonic", is_flag=True, default=False, help="Monotonic status to True.")
70
+ @click.option("--lifetime", type=int, help="Lifetime in seconds.")
71
+ @click.pass_context
72
+ def add_(ctx, did_name, dtype, monotonic, lifetime):
73
+ """Create a new collection-type DID"""
74
+ args = Arguments({"did": did_name, "monotonic": monotonic, "lifetime": lifetime})
75
+ if dtype == "container":
76
+ add_container(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
77
+ else:
78
+ add_dataset(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
79
+
80
+
81
+ @did.command("update")
82
+ @click.argument("dids", nargs=-1)
83
+ @click.option("--rse", "--rse-name", help="The RSE of the DIDs")
84
+ @click.option("--touch", "operation", flag_value="touch", default=True, help="Touch one or more DIDs and set the last accessed date to the current date")
85
+ @click.option("--open", "operation", flag_value="open", help="Reopen a dataset or container (only for privileged users)")
86
+ @click.option("--close", "operation", flag_value="close", help="Close a dataset or container.")
87
+ @click.pass_context
88
+ def update(ctx, dids, rse, operation):
89
+ """Touch one or more DIDs and set the last accessed date to the current date, or mark them as open or closed."""
90
+ args = Arguments({"dids": dids, "rse": rse})
91
+ if operation == "touch":
92
+ touch(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
93
+ elif operation == "open":
94
+ reopen(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
95
+ elif operation == "close":
96
+ close(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
97
+ else:
98
+ raise ValueError("No operation specified, please use `--help` to see possibilities") # Should not be possible, but better safe than sorry
99
+
100
+
101
+ @did.command("remove")
102
+ @click.option("--undo", is_flag=True, default=False, help="Undo erase DIDs. Only works if has been less than 24 hours since erase operation.")
103
+ @click.argument("dids", nargs=-1)
104
+ @click.pass_context
105
+ def remove(ctx, dids, undo):
106
+ """
107
+ This command sets the lifetime of the DID in order to expire in the next 24 hours.
108
+ Expired DIDs are force-deleted (and their replicas purged).
109
+ The deletion is not reversible after 24 hours grace time period expired
110
+ """
111
+ erase(Arguments({"dids": dids, "undo": undo}), ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
112
+
113
+
114
+ @did.group()
115
+ def content():
116
+ """Manage contents of collection type DIDs"""
117
+
118
+
119
+ @content.command("history")
120
+ @click.argument("dids", nargs=-1)
121
+ @click.pass_context
122
+ def content_history(ctx, dids):
123
+ """List the content history of a collection-type DID"""
124
+ list_content_history(Arguments({"dids": dids}), ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
125
+
126
+
127
+ @content.command("add")
128
+ @click.option("-to", "--to-did", required=True, help="Collection-type DID to which attach [DIDs]")
129
+ @click.option("-f", "--from-file", is_flag=True, default=False, help="[DIDs] is a file instead of a list of did names. The file should contain one did per line.")
130
+ @click.argument("dids", nargs=-1)
131
+ @click.pass_context
132
+ def content_add_(ctx, to_did, from_file, dids):
133
+ """Attach a list [dids] of Data IDentifiers (file or collection-type) to an other Data IDentifier (collection-type)"""
134
+ args = Arguments({"dids": dids, "todid": to_did, "fromfile": from_file})
135
+ attach(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
136
+
137
+
138
+ @content.command("remove")
139
+ @click.option("-f", "--from-did", help="Collection-type DID to remove DIDs from")
140
+ @click.argument("dids", nargs=-1)
141
+ @click.pass_context
142
+ def content_remove(ctx, dids, from_did):
143
+ """Detach [dids], a list of DIDs (file or collection-type) from an other Data Identifier (collection type)"""
144
+ args = Arguments({"dids": dids, "fromdid": from_did})
145
+ detach(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
146
+
147
+
148
+ @content.command("list")
149
+ @click.argument("dids", nargs=-1)
150
+ @click.option("--short", is_flag=True, default=False, help="Just dump the list of DIDs.")
151
+ @click.pass_context
152
+ def content_list_(ctx, dids, short):
153
+ """List the content of a collection-type DID"""
154
+ args = Arguments({"dids": dids, "short": short})
155
+ list_content(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
156
+
157
+
158
+ @did.group()
159
+ def metadata():
160
+ """Manage metadata for DIDs"""
161
+
162
+
163
+ @metadata.command("add")
164
+ @click.argument("did")
165
+ @click.option('--key', help='Attribute key', required=True)
166
+ @click.option('--value', help='Attribute value', required=True)
167
+ @click.pass_context
168
+ def metadata_add_(ctx, did, key, value):
169
+ """Add metadata to a DID"""
170
+ args = Arguments({"did": did, "key": key, "value": value})
171
+ set_metadata(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
172
+
173
+
174
+ @metadata.command("remove")
175
+ @click.argument("did")
176
+ @click.option("--key", help="Key to remove from a DID's metadata.")
177
+ @click.pass_context
178
+ def metadata_remove(ctx, did, key):
179
+ """Remove metadata from a DID"""
180
+ args = Arguments({"did": did, "key": key})
181
+ delete_metadata(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
182
+
183
+
184
+ @metadata.command("list")
185
+ @click.argument("dids", nargs=-1)
186
+ @click.option("--plugin", help="Filter down to metadata from specific metadata plugin")
187
+ @click.pass_context
188
+ def metadata_list_(ctx, dids, plugin):
189
+ """List metadata for a list of DIDs"""
190
+ args = Arguments({"dids": dids, "plugin": plugin})
191
+ get_metadata(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)
rucio/cli/download.py ADDED
@@ -0,0 +1,128 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import os
15
+
16
+ import click
17
+
18
+ from rucio.cli.bin_legacy.rucio import download as download_exe
19
+ from rucio.cli.utils import Arguments
20
+ from rucio.common.config import config_get_float
21
+
22
+
23
+ @click.command()
24
+ @click.argument("dids", nargs=-1)
25
+ @click.option("--dir", default=".", help="The directory to store the downloaded file.")
26
+ @click.option("--allow-tape", is_flag=True, default=False, help="Also consider tape endpoints as source of the download.")
27
+ @click.option("--rses", "--rse-exp", help="RSE Expression to specify allowed sources")
28
+ @click.option("--impl", help="Transfer protocol implementation to use (e.g: xrootd, gfal.NoRename, webdav, ssh.Rsync, rclone).")
29
+ @click.option("--protocol", help="Force the protocol to use.")
30
+ @click.option("--nrandom", type=int, help="Download N random files from the DID.")
31
+ @click.option("--ndownloader", type=int, default=3, help="Choose the number of parallel processes for download.")
32
+ @click.option("--no-subdir", is_flag=True, default=False, help="Don't create a subdirectory for the scope of the files.")
33
+ @click.option("--pfn", help="Specify the exact PFN for the download.")
34
+ @click.option("--no-resolve-archives", is_flag=True, default=False, help="If set archives will not be considered for download.")
35
+ @click.option("--ignore-checksum", is_flag=True, default=False, help="Don't validate checksum for downloaded files.")
36
+ @click.option("--check-local-with-filesize-only", is_flag=True, default=False, help="Don't use checksum verification for already downloaded files, use filesize instead.")
37
+ @click.option(
38
+ "--transfer-timeout",
39
+ type=float,
40
+ default=config_get_float("download", "transfer_timeout", False, None),
41
+ help="Transfer timeout (in seconds). Default: computed dynamically from --transfer-speed-timeout. If set to any value >= 0, --transfer-speed-timeout is ignored.",
42
+ ) # NOQA: E501
43
+ @click.option("--transfer-speed-timeout", type=float, default=None, help="Minimum allowed average transfer speed (in KBps). Default: 500. Used to dynamically compute the timeout if --transfer-timeout not set. Is not supported for --pfn.") # NOQA: E501
44
+ @click.option("--aria", default=False, help="Use aria2c utility if possible. (EXPERIMENTAL)")
45
+ @click.option("--archive-did", hidden=True) # Download from archive is transparent. This option is obsolete.
46
+ # TODO replace with click functionality to automatically pull env vars
47
+ @click.option("--trace-appid", default=os.environ.get("RUCIO_TRACE_APPID", None), hidden=True)
48
+ @click.option("--trace-dataset", default=os.environ.get("RUCIO_TRACE_DATASET", None), hidden=True)
49
+ @click.option("--trace-datasetscope", default=os.environ.get("RUCIO_TRACE_DATASETSCOPE", None), hidden=True)
50
+ @click.option("--trace-eventtype", default=os.environ.get("RUCIO_TRACE_EVENTTYPE", None), hidden=True)
51
+ @click.option("--trace-pq", default=os.environ.get("RUCIO_TRACE_PQ", None), hidden=True)
52
+ @click.option("--trace-taskid", default=os.environ.get("RUCIO_TRACE_TASKID", None), hidden=True)
53
+ @click.option("--trace-usrdn", default=os.environ.get("RUCIO_TRACE_USRDN", None), hidden=True)
54
+ @click.option("--filter", help="Filter files by key-value pairs like guid=2e2232aafac8324db452070304f8d745.")
55
+ @click.option("--scope", help="Scope if you are using the filter option and no full DID.")
56
+ @click.option("--metalink", help="Path to a metalink file.")
57
+ @click.option("--no-show-download-exceptions", default=False, is_flag=True, help="Does not raise NoFilesDownloaded, NotAllFilesDownloaded or incorrect number of output queue files Exception.") # NOQA: E501
58
+ @click.option("--replica-selection", help="Select the best replica using a replica sorting algorithm provided by replica sorter (e.g., random, geoip).")
59
+ @click.pass_context
60
+ def download(
61
+ ctx,
62
+ dids,
63
+ dir,
64
+ allow_tape,
65
+ rses,
66
+ impl,
67
+ protocol,
68
+ nrandom,
69
+ ndownloader,
70
+ no_subdir,
71
+ pfn,
72
+ no_resolve_archives,
73
+ ignore_checksum,
74
+ check_local_with_filesize_only,
75
+ transfer_timeout,
76
+ transfer_speed_timeout,
77
+ aria,
78
+ archive_did,
79
+ trace_appid,
80
+ trace_dataset,
81
+ trace_datasetscope,
82
+ trace_eventtype,
83
+ trace_pq,
84
+ trace_taskid,
85
+ trace_usrdn,
86
+ filter,
87
+ scope,
88
+ metalink,
89
+ no_show_download_exceptions,
90
+ replica_selection,
91
+ ):
92
+ """
93
+ Download DID(s) (in the form of scope:name) to a local dir
94
+ """
95
+ args = Arguments(
96
+ {
97
+ "dids": dids,
98
+ "dir": dir,
99
+ "allow_tape": allow_tape,
100
+ "rses": rses,
101
+ "impl": impl,
102
+ "protocol": protocol,
103
+ "nrandom": nrandom,
104
+ "ndownloader": ndownloader,
105
+ "no_subdir": no_subdir,
106
+ "pfn": pfn,
107
+ "no_resolve_archives": no_resolve_archives,
108
+ "ignore_checksum": ignore_checksum,
109
+ "check_local_with_filesize_only": check_local_with_filesize_only,
110
+ "transfer_timeout": transfer_timeout,
111
+ "transfer_speed_timeout": transfer_speed_timeout,
112
+ "aria": aria,
113
+ "archive_did": archive_did,
114
+ "trace_appid": trace_appid,
115
+ "trace_dataset": trace_dataset,
116
+ "trace_datasetscope": trace_datasetscope,
117
+ "trace_eventtype": trace_eventtype,
118
+ "trace_pq": trace_pq,
119
+ "trace_taskid": trace_taskid,
120
+ "trace_usrdn": trace_usrdn,
121
+ "filter": filter,
122
+ "scope": scope,
123
+ "metalink_file": metalink,
124
+ "deactivate_file_download_exceptions": no_show_download_exceptions,
125
+ "sort": replica_selection,
126
+ }
127
+ )
128
+ download_exe(args, ctx.obj.client, ctx.obj.logger, ctx.obj.console, ctx.obj.spinner)