rucio-clients 35.8.2__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.
- rucio/__init__.py +17 -0
- rucio/alembicrevision.py +15 -0
- rucio/client/__init__.py +15 -0
- rucio/client/accountclient.py +433 -0
- rucio/client/accountlimitclient.py +183 -0
- rucio/client/baseclient.py +974 -0
- rucio/client/client.py +76 -0
- rucio/client/configclient.py +126 -0
- rucio/client/credentialclient.py +59 -0
- rucio/client/didclient.py +866 -0
- rucio/client/diracclient.py +56 -0
- rucio/client/downloadclient.py +1785 -0
- rucio/client/exportclient.py +44 -0
- rucio/client/fileclient.py +50 -0
- rucio/client/importclient.py +42 -0
- rucio/client/lifetimeclient.py +90 -0
- rucio/client/lockclient.py +109 -0
- rucio/client/metaconventionsclient.py +140 -0
- rucio/client/pingclient.py +44 -0
- rucio/client/replicaclient.py +454 -0
- rucio/client/requestclient.py +125 -0
- rucio/client/rseclient.py +746 -0
- rucio/client/ruleclient.py +294 -0
- rucio/client/scopeclient.py +90 -0
- rucio/client/subscriptionclient.py +173 -0
- rucio/client/touchclient.py +82 -0
- rucio/client/uploadclient.py +955 -0
- rucio/common/__init__.py +13 -0
- rucio/common/cache.py +74 -0
- rucio/common/config.py +801 -0
- rucio/common/constants.py +159 -0
- rucio/common/constraints.py +17 -0
- rucio/common/didtype.py +189 -0
- rucio/common/exception.py +1151 -0
- rucio/common/extra.py +36 -0
- rucio/common/logging.py +420 -0
- rucio/common/pcache.py +1408 -0
- rucio/common/plugins.py +153 -0
- rucio/common/policy.py +84 -0
- rucio/common/schema/__init__.py +150 -0
- rucio/common/schema/atlas.py +413 -0
- rucio/common/schema/belleii.py +408 -0
- rucio/common/schema/domatpc.py +401 -0
- rucio/common/schema/escape.py +426 -0
- rucio/common/schema/generic.py +433 -0
- rucio/common/schema/generic_multi_vo.py +412 -0
- rucio/common/schema/icecube.py +406 -0
- rucio/common/stomp_utils.py +159 -0
- rucio/common/stopwatch.py +55 -0
- rucio/common/test_rucio_server.py +148 -0
- rucio/common/types.py +403 -0
- rucio/common/utils.py +2238 -0
- rucio/rse/__init__.py +96 -0
- rucio/rse/protocols/__init__.py +13 -0
- rucio/rse/protocols/bittorrent.py +184 -0
- rucio/rse/protocols/cache.py +122 -0
- rucio/rse/protocols/dummy.py +111 -0
- rucio/rse/protocols/gfal.py +703 -0
- rucio/rse/protocols/globus.py +243 -0
- rucio/rse/protocols/gsiftp.py +92 -0
- rucio/rse/protocols/http_cache.py +82 -0
- rucio/rse/protocols/mock.py +123 -0
- rucio/rse/protocols/ngarc.py +209 -0
- rucio/rse/protocols/posix.py +250 -0
- rucio/rse/protocols/protocol.py +594 -0
- rucio/rse/protocols/rclone.py +364 -0
- rucio/rse/protocols/rfio.py +136 -0
- rucio/rse/protocols/srm.py +338 -0
- rucio/rse/protocols/ssh.py +413 -0
- rucio/rse/protocols/storm.py +206 -0
- rucio/rse/protocols/webdav.py +550 -0
- rucio/rse/protocols/xrootd.py +301 -0
- rucio/rse/rsemanager.py +764 -0
- rucio/vcsversion.py +11 -0
- rucio/version.py +38 -0
- rucio_clients-35.8.2.data/data/etc/rse-accounts.cfg.template +25 -0
- rucio_clients-35.8.2.data/data/etc/rucio.cfg.atlas.client.template +42 -0
- rucio_clients-35.8.2.data/data/etc/rucio.cfg.template +257 -0
- rucio_clients-35.8.2.data/data/requirements.client.txt +15 -0
- rucio_clients-35.8.2.data/data/rucio_client/merge_rucio_configs.py +144 -0
- rucio_clients-35.8.2.data/scripts/rucio +2542 -0
- rucio_clients-35.8.2.data/scripts/rucio-admin +2447 -0
- rucio_clients-35.8.2.dist-info/METADATA +50 -0
- rucio_clients-35.8.2.dist-info/RECORD +88 -0
- rucio_clients-35.8.2.dist-info/WHEEL +5 -0
- rucio_clients-35.8.2.dist-info/licenses/AUTHORS.rst +97 -0
- rucio_clients-35.8.2.dist-info/licenses/LICENSE +201 -0
- rucio_clients-35.8.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,866 @@
|
|
|
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
|
+
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from json import dumps
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
18
|
+
from urllib.parse import quote_plus
|
|
19
|
+
|
|
20
|
+
from requests.status_codes import codes
|
|
21
|
+
|
|
22
|
+
from rucio.client.baseclient import BaseClient, choice
|
|
23
|
+
from rucio.common.exception import DeprecationError
|
|
24
|
+
from rucio.common.utils import build_url, date_to_str, render_json
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Iterable, Iterator, Mapping, Sequence
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DIDClient(BaseClient):
|
|
31
|
+
|
|
32
|
+
"""DataIdentifier client class for working with data identifiers"""
|
|
33
|
+
|
|
34
|
+
DIDS_BASEURL = 'dids'
|
|
35
|
+
ARCHIVES_BASEURL = 'archives'
|
|
36
|
+
|
|
37
|
+
def list_dids(
|
|
38
|
+
self,
|
|
39
|
+
scope: str,
|
|
40
|
+
filters: "Sequence[dict[str, Any]]",
|
|
41
|
+
did_type: Literal['all', 'collection', 'dataset', 'container', 'file'] = 'collection',
|
|
42
|
+
long: bool = False,
|
|
43
|
+
recursive: bool = False
|
|
44
|
+
) -> "Iterator[dict[str, Any]]":
|
|
45
|
+
"""
|
|
46
|
+
List all data identifiers in a scope which match a given pattern.
|
|
47
|
+
|
|
48
|
+
:param scope: The scope name.
|
|
49
|
+
:param filters: A nested dictionary of key/value pairs like [{'key1': 'value1', 'key2.lte': 'value2'}, {'key3.gte, 'value3'}].
|
|
50
|
+
Keypairs in the same dictionary are AND'ed together, dictionaries are OR'ed together. Keys should be suffixed
|
|
51
|
+
like <key>.<operation>, e.g. key1 >= value1 is equivalent to {'key1.gte': value}, where <operation> belongs to one
|
|
52
|
+
of the set {'lte', 'gte', 'gt', 'lt', 'ne' or ''}. Equivalence doesn't require an operator.
|
|
53
|
+
:param did_type: The type of the did: 'all'(container, dataset or file)|'collection'(dataset or container)|'dataset'|'container'|'file'
|
|
54
|
+
:param long: Long format option to display more information for each DID.
|
|
55
|
+
:param recursive: Recursively list DIDs content.
|
|
56
|
+
"""
|
|
57
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), 'dids', 'search'])
|
|
58
|
+
|
|
59
|
+
# stringify dates.
|
|
60
|
+
if isinstance(filters, dict): # backwards compatibility for filters as single {}
|
|
61
|
+
filters = [filters]
|
|
62
|
+
for or_group in filters:
|
|
63
|
+
for key, value in or_group.items():
|
|
64
|
+
if isinstance(value, datetime):
|
|
65
|
+
or_group[key] = date_to_str(value)
|
|
66
|
+
|
|
67
|
+
payload = {
|
|
68
|
+
'type': did_type,
|
|
69
|
+
'filters': filters,
|
|
70
|
+
'long': long,
|
|
71
|
+
'recursive': recursive
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
url = build_url(choice(self.list_hosts), path=path, params=payload)
|
|
75
|
+
|
|
76
|
+
r = self._send_request(url, type_='GET')
|
|
77
|
+
|
|
78
|
+
if r.status_code == codes.ok:
|
|
79
|
+
dids = self._load_json_data(r)
|
|
80
|
+
return dids
|
|
81
|
+
else:
|
|
82
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
83
|
+
raise exc_cls(exc_msg)
|
|
84
|
+
|
|
85
|
+
def list_dids_extended(self, scope, filters, did_type='collection', long=False, recursive=False):
|
|
86
|
+
"""
|
|
87
|
+
List all data identifiers in a scope which match a given pattern (DEPRECATED)
|
|
88
|
+
"""
|
|
89
|
+
raise DeprecationError("Command or function has been deprecated. Please use list_dids instead.")
|
|
90
|
+
|
|
91
|
+
def add_did(
|
|
92
|
+
self,
|
|
93
|
+
scope: str,
|
|
94
|
+
name: str,
|
|
95
|
+
did_type: Literal['DATASET', 'CONTAINER'],
|
|
96
|
+
statuses: Optional["Mapping[str, Any]"] = None,
|
|
97
|
+
meta: Optional["Mapping[str, Any]"] = None,
|
|
98
|
+
rules: Optional["Sequence[Mapping[str, Any]]"] = None,
|
|
99
|
+
lifetime: Optional[int] = None,
|
|
100
|
+
dids: Optional["Sequence[Mapping[str, Any]]"] = None,
|
|
101
|
+
rse: Optional[str] = None
|
|
102
|
+
) -> bool:
|
|
103
|
+
"""
|
|
104
|
+
Add data identifier for a dataset or container.
|
|
105
|
+
|
|
106
|
+
:param scope: The scope name.
|
|
107
|
+
:param name: The data identifier name.
|
|
108
|
+
:param did_type: The data identifier type (dataset|container).
|
|
109
|
+
:param statuses: Dictionary with statuses, e.g. {'monotonic':True}.
|
|
110
|
+
:param meta: Meta-data associated with the data identifier is represented using key/value pairs in a dictionary.
|
|
111
|
+
:param rules: Replication rules associated with the data identifier. A list of dictionaries, e.g., [{'copies': 2, 'rse_expression': 'TIERS1'}, ].
|
|
112
|
+
:param lifetime: DID's lifetime (in seconds).
|
|
113
|
+
:param dids: The content.
|
|
114
|
+
:param rse: The RSE name when registering replicas.
|
|
115
|
+
"""
|
|
116
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name)])
|
|
117
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
118
|
+
# Build json
|
|
119
|
+
data: dict[str, Any] = {'type': did_type}
|
|
120
|
+
if statuses:
|
|
121
|
+
data['statuses'] = statuses
|
|
122
|
+
if meta:
|
|
123
|
+
data['meta'] = meta
|
|
124
|
+
if rules:
|
|
125
|
+
data['rules'] = rules
|
|
126
|
+
if lifetime:
|
|
127
|
+
data['lifetime'] = lifetime
|
|
128
|
+
if dids:
|
|
129
|
+
data['dids'] = dids
|
|
130
|
+
if rse:
|
|
131
|
+
data['rse'] = rse
|
|
132
|
+
r = self._send_request(url, type_='POST', data=render_json(**data))
|
|
133
|
+
if r.status_code == codes.created:
|
|
134
|
+
return True
|
|
135
|
+
else:
|
|
136
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
137
|
+
raise exc_cls(exc_msg)
|
|
138
|
+
|
|
139
|
+
def add_dids(self, dids: "Sequence[Mapping[str, Any]]") -> bool:
|
|
140
|
+
"""
|
|
141
|
+
Bulk add datasets/containers.
|
|
142
|
+
"""
|
|
143
|
+
path = '/'.join([self.DIDS_BASEURL])
|
|
144
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
145
|
+
r = self._send_request(url, type_='POST', data=render_json(dids))
|
|
146
|
+
if r.status_code == codes.created:
|
|
147
|
+
return True
|
|
148
|
+
else:
|
|
149
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
150
|
+
raise exc_cls(exc_msg)
|
|
151
|
+
|
|
152
|
+
def add_dataset(
|
|
153
|
+
self,
|
|
154
|
+
scope: str,
|
|
155
|
+
name: str,
|
|
156
|
+
statuses: Optional["Mapping[str, Any]"] = None,
|
|
157
|
+
meta: Optional["Mapping[str, Any]"] = None,
|
|
158
|
+
rules: Optional["Sequence[Mapping[str, Any]]"] = None,
|
|
159
|
+
lifetime: Optional[int] = None,
|
|
160
|
+
files: Optional["Sequence[Mapping[str, Any]]"] = None,
|
|
161
|
+
rse: Optional[str] = None
|
|
162
|
+
) -> bool:
|
|
163
|
+
"""
|
|
164
|
+
Add data identifier for a dataset.
|
|
165
|
+
|
|
166
|
+
:param scope: The scope name.
|
|
167
|
+
:param name: The data identifier name.
|
|
168
|
+
:param statuses: Dictionary with statuses, e.g.g {'monotonic':True}.
|
|
169
|
+
:param meta: Meta-data associated with the data identifier is represented using key/value pairs in a dictionary.
|
|
170
|
+
:param rules: Replication rules associated with the data identifier. A list of dictionaries, e.g., [{'copies': 2, 'rse_expression': 'TIERS1'}, ].
|
|
171
|
+
:param lifetime: DID's lifetime (in seconds).
|
|
172
|
+
:param files: The content.
|
|
173
|
+
:param rse: The RSE name when registering replicas.
|
|
174
|
+
"""
|
|
175
|
+
return self.add_did(scope=scope, name=name, did_type='DATASET',
|
|
176
|
+
statuses=statuses, meta=meta, rules=rules,
|
|
177
|
+
lifetime=lifetime, dids=files, rse=rse)
|
|
178
|
+
|
|
179
|
+
def add_datasets(self, dsns: "Iterable[dict[str, Any]]") -> bool:
|
|
180
|
+
"""
|
|
181
|
+
Bulk add datasets.
|
|
182
|
+
|
|
183
|
+
:param dsns: A list of datasets.
|
|
184
|
+
"""
|
|
185
|
+
return self.add_dids(dids=[dict(list(dsn.items()) + [('type', 'DATASET')]) for dsn in dsns])
|
|
186
|
+
|
|
187
|
+
def add_container(
|
|
188
|
+
self,
|
|
189
|
+
scope: str,
|
|
190
|
+
name: str,
|
|
191
|
+
statuses: Optional["Mapping[str, Any]"] = None,
|
|
192
|
+
meta: Optional["Mapping[str, Any]"] = None,
|
|
193
|
+
rules: Optional["Sequence[Mapping[str, Any]]"] = None,
|
|
194
|
+
lifetime: Optional[int] = None
|
|
195
|
+
) -> bool:
|
|
196
|
+
"""
|
|
197
|
+
Add data identifier for a container.
|
|
198
|
+
|
|
199
|
+
:param scope: The scope name.
|
|
200
|
+
:param name: The data identifier name.
|
|
201
|
+
:param statuses: Dictionary with statuses, e.g.g {'monotonic':True}.
|
|
202
|
+
:param meta: Meta-data associated with the data identifier is represented using key/value pairs in a dictionary.
|
|
203
|
+
:param rules: Replication rules associated with the data identifier. A list of dictionaries, e.g., [{'copies': 2, 'rse_expression': 'TIERS1'}, ].
|
|
204
|
+
:param lifetime: DID's lifetime (in seconds).
|
|
205
|
+
"""
|
|
206
|
+
return self.add_did(scope=scope, name=name, did_type='CONTAINER', statuses=statuses, meta=meta, rules=rules, lifetime=lifetime)
|
|
207
|
+
|
|
208
|
+
def add_containers(self, cnts: "Sequence[dict[str, Any]]") -> bool:
|
|
209
|
+
"""
|
|
210
|
+
Bulk add containers.
|
|
211
|
+
|
|
212
|
+
:param cnts: A list of containers.
|
|
213
|
+
"""
|
|
214
|
+
return self.add_dids(dids=[dict(list(cnt.items()) + [('type', 'CONTAINER')]) for cnt in cnts])
|
|
215
|
+
|
|
216
|
+
def attach_dids(
|
|
217
|
+
self,
|
|
218
|
+
scope: str,
|
|
219
|
+
name: str,
|
|
220
|
+
dids: "Sequence[Mapping[str, Any]]",
|
|
221
|
+
rse: Optional[str] = None
|
|
222
|
+
) -> bool:
|
|
223
|
+
"""
|
|
224
|
+
Attach data identifier.
|
|
225
|
+
|
|
226
|
+
:param scope: The scope name.
|
|
227
|
+
:param name: The data identifier name.
|
|
228
|
+
:param dids: The content.
|
|
229
|
+
:param rse: The RSE name when registering replicas.
|
|
230
|
+
"""
|
|
231
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'dids'])
|
|
232
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
233
|
+
data: dict[str, Any] = {'dids': dids}
|
|
234
|
+
if rse:
|
|
235
|
+
data['rse'] = rse
|
|
236
|
+
r = self._send_request(url, type_='POST', data=render_json(**data))
|
|
237
|
+
if r.status_code == codes.created:
|
|
238
|
+
return True
|
|
239
|
+
else:
|
|
240
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
241
|
+
raise exc_cls(exc_msg)
|
|
242
|
+
|
|
243
|
+
def detach_dids(
|
|
244
|
+
self,
|
|
245
|
+
scope: str,
|
|
246
|
+
name: str,
|
|
247
|
+
dids: Optional["Sequence[Mapping[str, Any]]"] = None
|
|
248
|
+
) -> bool:
|
|
249
|
+
"""
|
|
250
|
+
Detach data identifier
|
|
251
|
+
|
|
252
|
+
:param scope: The scope name.
|
|
253
|
+
:param name: The data identifier name.
|
|
254
|
+
:param dids: The content.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'dids'])
|
|
258
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
259
|
+
data = {'dids': dids}
|
|
260
|
+
r = self._send_request(url, type_='DEL', data=render_json(**data))
|
|
261
|
+
if r.status_code == codes.ok:
|
|
262
|
+
return True
|
|
263
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
264
|
+
raise exc_cls(exc_msg)
|
|
265
|
+
|
|
266
|
+
def attach_dids_to_dids(
|
|
267
|
+
self,
|
|
268
|
+
attachments: "Sequence[dict[str, Union[str, Sequence[dict[str, Any]]]]]",
|
|
269
|
+
ignore_duplicate: bool = False
|
|
270
|
+
) -> bool:
|
|
271
|
+
"""
|
|
272
|
+
Add dids to dids.
|
|
273
|
+
|
|
274
|
+
:param attachments: The attachments.
|
|
275
|
+
attachments is: [attachment, attachment, ...]
|
|
276
|
+
attachment is: {'scope': scope, 'name': name, 'dids': dids}
|
|
277
|
+
dids is: [{'scope': scope, 'name': name}, ...]
|
|
278
|
+
:param ignore_duplicate: If True, ignore duplicate entries.
|
|
279
|
+
"""
|
|
280
|
+
path = '/'.join([self.DIDS_BASEURL, 'attachments'])
|
|
281
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
282
|
+
data = {'ignore_duplicate': ignore_duplicate, 'attachments': attachments}
|
|
283
|
+
r = self._send_request(url, type_='POST', data=dumps(data))
|
|
284
|
+
if r.status_code in (codes.ok, codes.no_content, codes.created):
|
|
285
|
+
return True
|
|
286
|
+
|
|
287
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
288
|
+
raise exc_cls(exc_msg)
|
|
289
|
+
|
|
290
|
+
def add_files_to_datasets(
|
|
291
|
+
self,
|
|
292
|
+
attachments: "Sequence[dict[str, Union[str, Sequence[dict[str, Any]]]]]",
|
|
293
|
+
ignore_duplicate: bool = False
|
|
294
|
+
) -> bool:
|
|
295
|
+
"""
|
|
296
|
+
Add files to datasets.
|
|
297
|
+
|
|
298
|
+
:param attachments: The attachments.
|
|
299
|
+
attachments is: [attachment, attachment, ...]
|
|
300
|
+
attachment is: {'scope': scope, 'name': name, 'dids': dids}
|
|
301
|
+
dids is: [{'scope': scope, 'name': name}, ...]
|
|
302
|
+
:param ignore_duplicate: If True, ignore duplicate entries.
|
|
303
|
+
"""
|
|
304
|
+
return self.attach_dids_to_dids(attachments=attachments,
|
|
305
|
+
ignore_duplicate=ignore_duplicate)
|
|
306
|
+
|
|
307
|
+
def add_datasets_to_containers(
|
|
308
|
+
self,
|
|
309
|
+
attachments: "Sequence[dict[str, Union[str, Sequence[dict[str, Any]]]]]"
|
|
310
|
+
) -> bool:
|
|
311
|
+
"""
|
|
312
|
+
Add datasets_to_containers.
|
|
313
|
+
|
|
314
|
+
:param attachments: The attachments.
|
|
315
|
+
attachments is: [attachment, attachment, ...]
|
|
316
|
+
attachment is: {'scope': scope, 'name': name, 'dids': dids}
|
|
317
|
+
dids is: [{'scope': scope, 'name': name}, ...]
|
|
318
|
+
"""
|
|
319
|
+
return self.attach_dids_to_dids(attachments=attachments)
|
|
320
|
+
|
|
321
|
+
def add_containers_to_containers(
|
|
322
|
+
self,
|
|
323
|
+
attachments: "Sequence[dict[str, Union[str, Sequence[dict[str, Any]]]]]"
|
|
324
|
+
) -> bool:
|
|
325
|
+
"""
|
|
326
|
+
Add containers_to_containers.
|
|
327
|
+
|
|
328
|
+
:param attachments: The attachments.
|
|
329
|
+
attachments is: [attachment, attachment, ...]
|
|
330
|
+
attachment is: {'scope': scope, 'name': name, 'dids': dids}
|
|
331
|
+
dids is: [{'scope': scope, 'name': name}, ...]
|
|
332
|
+
"""
|
|
333
|
+
return self.attach_dids_to_dids(attachments=attachments)
|
|
334
|
+
|
|
335
|
+
def add_files_to_dataset(
|
|
336
|
+
self,
|
|
337
|
+
scope: str,
|
|
338
|
+
name: str,
|
|
339
|
+
files: "Sequence[Mapping[str, Any]]",
|
|
340
|
+
rse: Optional[str] = None
|
|
341
|
+
) -> bool:
|
|
342
|
+
"""
|
|
343
|
+
Add files to datasets.
|
|
344
|
+
|
|
345
|
+
:param scope: The scope name.
|
|
346
|
+
:param name: The dataset name.
|
|
347
|
+
:param files: The content.
|
|
348
|
+
:param rse: The RSE name when registering replicas.
|
|
349
|
+
"""
|
|
350
|
+
return self.attach_dids(scope=scope, name=name, dids=files, rse=rse)
|
|
351
|
+
|
|
352
|
+
def add_files_to_archive(
|
|
353
|
+
self,
|
|
354
|
+
scope: str,
|
|
355
|
+
name: str,
|
|
356
|
+
files: "Sequence[Mapping[str, Any]]"
|
|
357
|
+
) -> bool:
|
|
358
|
+
"""
|
|
359
|
+
Add files to archive.
|
|
360
|
+
|
|
361
|
+
:param scope: The scope name.
|
|
362
|
+
:param name: The dataset name.
|
|
363
|
+
:param files: The content.
|
|
364
|
+
"""
|
|
365
|
+
return self.attach_dids(scope=scope, name=name, dids=files)
|
|
366
|
+
|
|
367
|
+
def add_datasets_to_container(
|
|
368
|
+
self,
|
|
369
|
+
scope: str,
|
|
370
|
+
name: str,
|
|
371
|
+
dsns: "Sequence[Mapping[str, Any]]"
|
|
372
|
+
) -> bool:
|
|
373
|
+
"""
|
|
374
|
+
Add datasets to container.
|
|
375
|
+
|
|
376
|
+
:param scope: The scope name.
|
|
377
|
+
:param name: The dataset name.
|
|
378
|
+
:param dsns: The content.
|
|
379
|
+
"""
|
|
380
|
+
return self.attach_dids(scope=scope, name=name, dids=dsns)
|
|
381
|
+
|
|
382
|
+
def add_containers_to_container(
|
|
383
|
+
self,
|
|
384
|
+
scope: str,
|
|
385
|
+
name: str,
|
|
386
|
+
cnts: "Sequence[Mapping[str, Any]]"
|
|
387
|
+
) -> bool:
|
|
388
|
+
"""
|
|
389
|
+
Add containers to container.
|
|
390
|
+
|
|
391
|
+
:param scope: The scope name.
|
|
392
|
+
:param name: The dataset name.
|
|
393
|
+
:param cnts: The content.
|
|
394
|
+
"""
|
|
395
|
+
return self.attach_dids(scope=scope, name=name, dids=cnts)
|
|
396
|
+
|
|
397
|
+
def list_content(
|
|
398
|
+
self,
|
|
399
|
+
scope: str,
|
|
400
|
+
name: str
|
|
401
|
+
) -> "Iterator[dict[str, Any]]":
|
|
402
|
+
"""
|
|
403
|
+
List data identifier contents.
|
|
404
|
+
|
|
405
|
+
:param scope: The scope name.
|
|
406
|
+
:param name: The data identifier name.
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'dids'])
|
|
410
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
411
|
+
r = self._send_request(url, type_='GET')
|
|
412
|
+
if r.status_code == codes.ok:
|
|
413
|
+
return self._load_json_data(r)
|
|
414
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
415
|
+
raise exc_cls(exc_msg)
|
|
416
|
+
|
|
417
|
+
def list_content_history(
|
|
418
|
+
self,
|
|
419
|
+
scope: str,
|
|
420
|
+
name: str
|
|
421
|
+
) -> "Iterator[dict[str, Any]]":
|
|
422
|
+
"""
|
|
423
|
+
List data identifier contents history.
|
|
424
|
+
|
|
425
|
+
:param scope: The scope name.
|
|
426
|
+
:param name: The data identifier name.
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'dids', 'history'])
|
|
430
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
431
|
+
r = self._send_request(url, type_='GET')
|
|
432
|
+
if r.status_code == codes.ok:
|
|
433
|
+
return self._load_json_data(r)
|
|
434
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
435
|
+
raise exc_cls(exc_msg)
|
|
436
|
+
|
|
437
|
+
def list_files(
|
|
438
|
+
self,
|
|
439
|
+
scope: str,
|
|
440
|
+
name: str,
|
|
441
|
+
long: Optional[bool] = None
|
|
442
|
+
) -> "Iterator[dict[str, Any]]":
|
|
443
|
+
"""
|
|
444
|
+
List data identifier file contents.
|
|
445
|
+
|
|
446
|
+
:param scope: The scope name.
|
|
447
|
+
:param name: The data identifier name.
|
|
448
|
+
:param long: A boolean to choose if GUID is returned or not.
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
payload = {}
|
|
452
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'files'])
|
|
453
|
+
if long:
|
|
454
|
+
payload['long'] = True
|
|
455
|
+
url = build_url(choice(self.list_hosts), path=path, params=payload)
|
|
456
|
+
|
|
457
|
+
r = self._send_request(url, type_='GET')
|
|
458
|
+
if r.status_code == codes.ok:
|
|
459
|
+
return self._load_json_data(r)
|
|
460
|
+
else:
|
|
461
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
462
|
+
raise exc_cls(exc_msg)
|
|
463
|
+
|
|
464
|
+
def bulk_list_files(self, dids: list[dict[str, Any]]) -> "Iterator[dict[str, Any]]":
|
|
465
|
+
"""
|
|
466
|
+
List data identifier file contents.
|
|
467
|
+
|
|
468
|
+
:param dids: The list of DIDs.
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
data = {'dids': dids}
|
|
472
|
+
path = '/'.join([self.DIDS_BASEURL, 'bulkfiles'])
|
|
473
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
474
|
+
|
|
475
|
+
r = self._send_request(url, type_='POST', data=dumps(data), stream=True)
|
|
476
|
+
if r.status_code == codes.ok:
|
|
477
|
+
return self._load_json_data(r)
|
|
478
|
+
else:
|
|
479
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
480
|
+
raise exc_cls(exc_msg)
|
|
481
|
+
|
|
482
|
+
def get_did(
|
|
483
|
+
self,
|
|
484
|
+
scope: str,
|
|
485
|
+
name: str,
|
|
486
|
+
dynamic: bool = False,
|
|
487
|
+
dynamic_depth: Optional[str] = None
|
|
488
|
+
) -> dict[str, Any]:
|
|
489
|
+
"""
|
|
490
|
+
Retrieve a single data identifier.
|
|
491
|
+
|
|
492
|
+
:param scope: The scope name.
|
|
493
|
+
:param name: The data identifier name.
|
|
494
|
+
:param dynamic_depth: The DID type as string ('FILE'/'DATASET') at which to stop the dynamic
|
|
495
|
+
length/bytes calculation. If not set, the size will not be computed dynamically.
|
|
496
|
+
:param dynamic: (Deprecated) same as dynamic_depth = 'FILE'
|
|
497
|
+
"""
|
|
498
|
+
|
|
499
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name)])
|
|
500
|
+
params = {}
|
|
501
|
+
if dynamic_depth:
|
|
502
|
+
params['dynamic_depth'] = dynamic_depth
|
|
503
|
+
elif dynamic:
|
|
504
|
+
params['dynamic_depth'] = 'FILE'
|
|
505
|
+
url = build_url(choice(self.list_hosts), path=path, params=params)
|
|
506
|
+
r = self._send_request(url, type_='GET')
|
|
507
|
+
if r.status_code == codes.ok:
|
|
508
|
+
return next(self._load_json_data(r))
|
|
509
|
+
else:
|
|
510
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
511
|
+
raise exc_cls(exc_msg)
|
|
512
|
+
|
|
513
|
+
def get_metadata(
|
|
514
|
+
self,
|
|
515
|
+
scope: str,
|
|
516
|
+
name: str,
|
|
517
|
+
plugin: str = 'DID_COLUMN'
|
|
518
|
+
) -> dict[str, Any]:
|
|
519
|
+
"""
|
|
520
|
+
Get data identifier metadata
|
|
521
|
+
|
|
522
|
+
:param scope: The scope name.
|
|
523
|
+
:param name: The data identifier name.
|
|
524
|
+
:param plugin: Backend Metadata plugin the Rucio server should use to query data.
|
|
525
|
+
"""
|
|
526
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'meta'])
|
|
527
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
528
|
+
payload = {}
|
|
529
|
+
payload['plugin'] = plugin
|
|
530
|
+
r = self._send_request(url, type_='GET', params=payload)
|
|
531
|
+
if r.status_code == codes.ok:
|
|
532
|
+
meta = self._load_json_data(r)
|
|
533
|
+
return next(meta)
|
|
534
|
+
else:
|
|
535
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
536
|
+
raise exc_cls(exc_msg)
|
|
537
|
+
|
|
538
|
+
def get_metadata_bulk(
|
|
539
|
+
self,
|
|
540
|
+
dids: "Sequence[Mapping[str, Any]]",
|
|
541
|
+
inherit: bool = False
|
|
542
|
+
) -> "Iterator[dict[str, Any]]":
|
|
543
|
+
"""
|
|
544
|
+
Bulk get data identifier metadata
|
|
545
|
+
:param inherit: A boolean. If set to true, the metadata of the parent are concatenated.
|
|
546
|
+
:param dids: A list of dids.
|
|
547
|
+
"""
|
|
548
|
+
data = {'dids': dids, 'inherit': inherit}
|
|
549
|
+
path = '/'.join([self.DIDS_BASEURL, 'bulkmeta'])
|
|
550
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
551
|
+
r = self._send_request(url, type_='POST', data=dumps(data))
|
|
552
|
+
if r.status_code == codes.ok:
|
|
553
|
+
return self._load_json_data(r)
|
|
554
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
555
|
+
raise exc_cls(exc_msg)
|
|
556
|
+
|
|
557
|
+
def set_metadata(
|
|
558
|
+
self,
|
|
559
|
+
scope: str,
|
|
560
|
+
name: str,
|
|
561
|
+
key: str,
|
|
562
|
+
value: Any,
|
|
563
|
+
recursive: bool = False
|
|
564
|
+
) -> bool:
|
|
565
|
+
"""
|
|
566
|
+
Set data identifier metadata
|
|
567
|
+
|
|
568
|
+
:param scope: The scope name.
|
|
569
|
+
:param name: The data identifier name.
|
|
570
|
+
:param key: the key.
|
|
571
|
+
:param value: the value.
|
|
572
|
+
:param recursive: Option to propagate the metadata change to content.
|
|
573
|
+
"""
|
|
574
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'meta', key])
|
|
575
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
576
|
+
data = dumps({'value': value, 'recursive': recursive})
|
|
577
|
+
r = self._send_request(url, type_='POST', data=data)
|
|
578
|
+
if r.status_code == codes.created:
|
|
579
|
+
return True
|
|
580
|
+
else:
|
|
581
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
582
|
+
raise exc_cls(exc_msg)
|
|
583
|
+
|
|
584
|
+
def set_metadata_bulk(
|
|
585
|
+
self,
|
|
586
|
+
scope: str,
|
|
587
|
+
name: str,
|
|
588
|
+
meta: "Mapping[str, Any]",
|
|
589
|
+
recursive: bool = False
|
|
590
|
+
) -> bool:
|
|
591
|
+
"""
|
|
592
|
+
Set data identifier metadata in bulk.
|
|
593
|
+
|
|
594
|
+
:param scope: The scope name.
|
|
595
|
+
:param name: The data identifier name.
|
|
596
|
+
:param meta: the metadata key-values.
|
|
597
|
+
:param recursive: Option to propagate the metadata change to content.
|
|
598
|
+
"""
|
|
599
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'meta'])
|
|
600
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
601
|
+
data = dumps({'meta': meta, 'recursive': recursive})
|
|
602
|
+
r = self._send_request(url, type_='POST', data=data)
|
|
603
|
+
if r.status_code == codes.created:
|
|
604
|
+
return True
|
|
605
|
+
else:
|
|
606
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
607
|
+
raise exc_cls(exc_msg)
|
|
608
|
+
|
|
609
|
+
def set_dids_metadata_bulk(
|
|
610
|
+
self,
|
|
611
|
+
dids: "Sequence[Mapping[str, Any]]",
|
|
612
|
+
recursive: bool = False
|
|
613
|
+
) -> bool:
|
|
614
|
+
"""
|
|
615
|
+
Set metadata to a list of data identifiers.
|
|
616
|
+
|
|
617
|
+
:param dids: A list of dids including metadata, i.e. [{'scope': scope1, 'name': name1, 'meta': {key1: value1, key2: value2}] .
|
|
618
|
+
:param recursive: Option to propagate the metadata update to content.
|
|
619
|
+
"""
|
|
620
|
+
path = '/'.join([self.DIDS_BASEURL, 'bulkdidsmeta'])
|
|
621
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
622
|
+
data = dumps({'dids': dids, 'recursive': recursive})
|
|
623
|
+
r = self._send_request(url, type_='POST', data=data)
|
|
624
|
+
if r.status_code == codes.created:
|
|
625
|
+
return True
|
|
626
|
+
else:
|
|
627
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
628
|
+
raise exc_cls(exc_msg)
|
|
629
|
+
|
|
630
|
+
def set_status(
|
|
631
|
+
self,
|
|
632
|
+
scope: str,
|
|
633
|
+
name: str,
|
|
634
|
+
**kwargs
|
|
635
|
+
) -> bool:
|
|
636
|
+
"""
|
|
637
|
+
Set data identifier status
|
|
638
|
+
|
|
639
|
+
:param scope: The scope name.
|
|
640
|
+
:param name: The data identifier name.
|
|
641
|
+
:param kwargs: Keyword arguments of the form status_name=value.
|
|
642
|
+
"""
|
|
643
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'status'])
|
|
644
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
645
|
+
data = dumps(kwargs)
|
|
646
|
+
r = self._send_request(url, type_='PUT', data=data)
|
|
647
|
+
if r.status_code in (codes.ok, codes.no_content, codes.created):
|
|
648
|
+
return True
|
|
649
|
+
|
|
650
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
651
|
+
raise exc_cls(exc_msg)
|
|
652
|
+
|
|
653
|
+
def close(
|
|
654
|
+
self,
|
|
655
|
+
scope: str,
|
|
656
|
+
name: str
|
|
657
|
+
) -> bool:
|
|
658
|
+
"""
|
|
659
|
+
close dataset/container
|
|
660
|
+
|
|
661
|
+
:param scope: The scope name.
|
|
662
|
+
:param name: The dataset/container name.
|
|
663
|
+
"""
|
|
664
|
+
return self.set_status(scope=scope, name=name, open=False)
|
|
665
|
+
|
|
666
|
+
def delete_metadata(
|
|
667
|
+
self,
|
|
668
|
+
scope: str,
|
|
669
|
+
name: str,
|
|
670
|
+
key: str
|
|
671
|
+
) -> bool:
|
|
672
|
+
"""
|
|
673
|
+
Delete data identifier metadata
|
|
674
|
+
|
|
675
|
+
:param scope: The scope name.
|
|
676
|
+
:param name: The data identifier.
|
|
677
|
+
:param key: the key.
|
|
678
|
+
"""
|
|
679
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'meta'])
|
|
680
|
+
url = build_url(choice(self.list_hosts), path=path, params={'key': key})
|
|
681
|
+
|
|
682
|
+
r = self._send_request(url, type_='DEL')
|
|
683
|
+
if r.status_code == codes.ok:
|
|
684
|
+
return True
|
|
685
|
+
else:
|
|
686
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
687
|
+
raise exc_cls(exc_msg)
|
|
688
|
+
|
|
689
|
+
def list_did_rules(
|
|
690
|
+
self,
|
|
691
|
+
scope: str,
|
|
692
|
+
name: str
|
|
693
|
+
) -> "Iterator[dict[str, Any]]":
|
|
694
|
+
"""
|
|
695
|
+
List the associated rules of a data identifier.
|
|
696
|
+
|
|
697
|
+
:param scope: The scope name.
|
|
698
|
+
:param name: The data identifier name.
|
|
699
|
+
"""
|
|
700
|
+
|
|
701
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'rules'])
|
|
702
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
703
|
+
r = self._send_request(url, type_='GET')
|
|
704
|
+
if r.status_code == codes.ok:
|
|
705
|
+
return self._load_json_data(r)
|
|
706
|
+
else:
|
|
707
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
708
|
+
raise exc_cls(exc_msg)
|
|
709
|
+
|
|
710
|
+
def list_associated_rules_for_file(
|
|
711
|
+
self,
|
|
712
|
+
scope: str,
|
|
713
|
+
name: str
|
|
714
|
+
) -> "Iterator[dict[str, Any]]":
|
|
715
|
+
"""
|
|
716
|
+
List the associated rules a file is affected from..
|
|
717
|
+
|
|
718
|
+
:param scope: The scope name.
|
|
719
|
+
:param name: The file name.
|
|
720
|
+
"""
|
|
721
|
+
|
|
722
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'associated_rules'])
|
|
723
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
724
|
+
r = self._send_request(url, type_='GET')
|
|
725
|
+
if r.status_code == codes.ok:
|
|
726
|
+
return self._load_json_data(r)
|
|
727
|
+
else:
|
|
728
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
729
|
+
raise exc_cls(exc_msg)
|
|
730
|
+
|
|
731
|
+
def get_dataset_by_guid(self, guid: str) -> "Iterator[dict[str, Any]]":
|
|
732
|
+
"""
|
|
733
|
+
Get the parent datasets for a given GUID.
|
|
734
|
+
:param guid: The GUID.
|
|
735
|
+
|
|
736
|
+
:returns: A did
|
|
737
|
+
"""
|
|
738
|
+
|
|
739
|
+
path = '/'.join([self.DIDS_BASEURL, guid, 'guid'])
|
|
740
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
741
|
+
r = self._send_request(url, type_='GET')
|
|
742
|
+
if r.status_code == codes.ok:
|
|
743
|
+
return self._load_json_data(r)
|
|
744
|
+
else:
|
|
745
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
746
|
+
raise exc_cls(exc_msg)
|
|
747
|
+
|
|
748
|
+
def scope_list(
|
|
749
|
+
self,
|
|
750
|
+
scope: str,
|
|
751
|
+
name: Optional[str] = None,
|
|
752
|
+
recursive: bool = False
|
|
753
|
+
) -> "Iterator[dict[str, Any]]":
|
|
754
|
+
"""
|
|
755
|
+
List data identifiers in a scope.
|
|
756
|
+
|
|
757
|
+
:param scope: The scope name.
|
|
758
|
+
:param name: The data identifier name.
|
|
759
|
+
:param recursive: boolean, True or False.
|
|
760
|
+
"""
|
|
761
|
+
|
|
762
|
+
payload = {}
|
|
763
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), ''])
|
|
764
|
+
if name:
|
|
765
|
+
payload['name'] = name
|
|
766
|
+
if recursive:
|
|
767
|
+
payload['recursive'] = True
|
|
768
|
+
url = build_url(choice(self.list_hosts), path=path, params=payload)
|
|
769
|
+
|
|
770
|
+
r = self._send_request(url, type_='GET')
|
|
771
|
+
if r.status_code == codes.ok:
|
|
772
|
+
return self._load_json_data(r)
|
|
773
|
+
else:
|
|
774
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
775
|
+
raise exc_cls(exc_msg)
|
|
776
|
+
|
|
777
|
+
def list_parent_dids(
|
|
778
|
+
self,
|
|
779
|
+
scope: str,
|
|
780
|
+
name: str
|
|
781
|
+
) -> "Iterator[dict[str, Any]]":
|
|
782
|
+
"""
|
|
783
|
+
List parent dataset/containers of a did.
|
|
784
|
+
|
|
785
|
+
:param scope: The scope.
|
|
786
|
+
:param name: The name.
|
|
787
|
+
"""
|
|
788
|
+
|
|
789
|
+
path = '/'.join([self.DIDS_BASEURL, quote_plus(scope), quote_plus(name), 'parents'])
|
|
790
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
791
|
+
|
|
792
|
+
r = self._send_request(url, type_='GET')
|
|
793
|
+
if r.status_code == codes.ok:
|
|
794
|
+
return self._load_json_data(r)
|
|
795
|
+
else:
|
|
796
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
797
|
+
raise exc_cls(exc_msg)
|
|
798
|
+
|
|
799
|
+
def create_did_sample(
|
|
800
|
+
self,
|
|
801
|
+
input_scope: str,
|
|
802
|
+
input_name: str,
|
|
803
|
+
output_scope: str,
|
|
804
|
+
output_name: str,
|
|
805
|
+
nbfiles: int
|
|
806
|
+
) -> bool:
|
|
807
|
+
"""
|
|
808
|
+
Create a sample from an input collection.
|
|
809
|
+
|
|
810
|
+
:param input_scope: The scope of the input DID.
|
|
811
|
+
:param input_name: The name of the input DID.
|
|
812
|
+
:param output_scope: The scope of the output dataset.
|
|
813
|
+
:param output_name: The name of the output dataset.
|
|
814
|
+
:param account: The account.
|
|
815
|
+
:param nbfiles: The number of files to register in the output dataset.
|
|
816
|
+
"""
|
|
817
|
+
path = '/'.join([self.DIDS_BASEURL, 'sample'])
|
|
818
|
+
data = dumps({
|
|
819
|
+
'input_scope': input_scope,
|
|
820
|
+
'input_name': input_name,
|
|
821
|
+
'output_scope': output_scope,
|
|
822
|
+
'output_name': output_name,
|
|
823
|
+
'nbfiles': str(nbfiles)
|
|
824
|
+
})
|
|
825
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
826
|
+
r = self._send_request(url, type_='POST', data=data)
|
|
827
|
+
if r.status_code == codes.created:
|
|
828
|
+
return True
|
|
829
|
+
else:
|
|
830
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
831
|
+
raise exc_cls(exc_msg)
|
|
832
|
+
|
|
833
|
+
def resurrect(self, dids: "Sequence[Mapping[str, Any]]") -> bool:
|
|
834
|
+
"""
|
|
835
|
+
Resurrect a list of dids.
|
|
836
|
+
|
|
837
|
+
:param dids: A list of dids [{'scope': scope, 'name': name}, ...]
|
|
838
|
+
"""
|
|
839
|
+
path = '/'.join([self.DIDS_BASEURL, 'resurrect'])
|
|
840
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
841
|
+
r = self._send_request(url, type_='POST', data=dumps(dids))
|
|
842
|
+
if r.status_code == codes.created:
|
|
843
|
+
return True
|
|
844
|
+
else:
|
|
845
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
846
|
+
raise exc_cls(exc_msg)
|
|
847
|
+
|
|
848
|
+
def list_archive_content(
|
|
849
|
+
self,
|
|
850
|
+
scope: str,
|
|
851
|
+
name: str
|
|
852
|
+
) -> "Iterator[dict[str, Any]]":
|
|
853
|
+
"""
|
|
854
|
+
List archive contents.
|
|
855
|
+
|
|
856
|
+
:param scope: The scope name.
|
|
857
|
+
:param name: The data identifier name.
|
|
858
|
+
"""
|
|
859
|
+
path = '/'.join([self.ARCHIVES_BASEURL, quote_plus(scope), quote_plus(name), 'files'])
|
|
860
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
861
|
+
|
|
862
|
+
r = self._send_request(url, type_='GET')
|
|
863
|
+
if r.status_code == codes.ok:
|
|
864
|
+
return self._load_json_data(r)
|
|
865
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
866
|
+
raise exc_cls(exc_msg)
|