dafab-client 2.2.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.
- dafab_client/__init__.py +65 -0
- dafab_client/_rucio/__init__.py +1 -0
- dafab_client/_rucio/client/__init__.py +15 -0
- dafab_client/_rucio/client/accountclient.py +578 -0
- dafab_client/_rucio/client/baseclient.py +1070 -0
- dafab_client/_rucio/client/client.py +133 -0
- dafab_client/_rucio/client/credentialclient.py +110 -0
- dafab_client/_rucio/client/didclient.py +1057 -0
- dafab_client/_rucio/client/downloadclient.py +2058 -0
- dafab_client/_rucio/client/pingclient.py +78 -0
- dafab_client/_rucio/client/replicaclient.py +570 -0
- dafab_client/_rucio/client/rseclient.py +974 -0
- dafab_client/_rucio/client/scopeclient.py +116 -0
- dafab_client/_rucio/client/uploadclient.py +1514 -0
- dafab_client/_rucio/common/cache.py +110 -0
- dafab_client/_rucio/common/checksum.py +168 -0
- dafab_client/_rucio/common/client.py +96 -0
- dafab_client/_rucio/common/config.py +786 -0
- dafab_client/_rucio/common/constants.py +247 -0
- dafab_client/_rucio/common/constraints.py +17 -0
- dafab_client/_rucio/common/didtype.py +237 -0
- dafab_client/_rucio/common/exception.py +1305 -0
- dafab_client/_rucio/common/extra.py +31 -0
- dafab_client/_rucio/common/filter.py +202 -0
- dafab_client/_rucio/common/logging.py +420 -0
- dafab_client/_rucio/common/pcache.py +1404 -0
- dafab_client/_rucio/common/plugins.py +236 -0
- dafab_client/_rucio/common/types.py +481 -0
- dafab_client/_rucio/common/utils.py +1886 -0
- dafab_client/_rucio/core/rse.py +2080 -0
- dafab_client/_rucio/dafab_lib.py +317 -0
- dafab_client/_rucio/global_utils.py +101 -0
- dafab_client/_rucio/overridden_rucio_client.py +788 -0
- dafab_client/_rucio/rse/__init__.py +124 -0
- dafab_client/_rucio/rse/protocols/__init__.py +13 -0
- dafab_client/_rucio/rse/protocols/gfal.py +708 -0
- dafab_client/_rucio/rse/protocols/protocol.py +365 -0
- dafab_client/_rucio/rse/protocols/webdav.py +621 -0
- dafab_client/_rucio/rse/rsemanager.py +897 -0
- dafab_client/_rucio/rse/translation.py +271 -0
- dafab_client/_rucio/secrets/user_dafab/config +12 -0
- dafab_client/_rucio/secrets/user_dafab/rucio_ca_bundle.pem +167 -0
- dafab_client/_rucio/version.py +42 -0
- dafab_client/example_notebooks/dafab_dasi_workflows.ipynb +315 -0
- dafab_client/example_notebooks/dafab_operator_workflows.ipynb +450 -0
- dafab_client/example_notebooks/dafab_simple_user_workflows.ipynb +917 -0
- dafab_client/example_notebooks/dafab_skim_workflows.ipynb +497 -0
- dafab_client/helpers/System/check_available_accounts.py +179 -0
- dafab_client/helpers/System/check_storage.py +96 -0
- dafab_client/helpers/__init__.py +15 -0
- dafab_client/helpers/check_server_health.py +86 -0
- dafab_client/helpers/container_management/ensure_container.py +94 -0
- dafab_client/helpers/container_management/list_containers.py +128 -0
- dafab_client/helpers/dataset_management/ensure_dataset.py +90 -0
- dafab_client/helpers/dataset_management/list_datasets.py +76 -0
- dafab_client/helpers/dataset_management/scope_management.py +183 -0
- dafab_client/helpers/db_bootstrap/account_manager.py +919 -0
- dafab_client/helpers/demo_imports.py +96 -0
- dafab_client/helpers/did_management/operations.py +160 -0
- dafab_client/helpers/file_management/Deletion/delete_file_of_scope_name.py +68 -0
- dafab_client/helpers/file_management/Deletion/delete_replica_of_scope_name.py +74 -0
- dafab_client/helpers/file_management/Insertion/upload_file_of_scope_name.py +142 -0
- dafab_client/helpers/file_management/Retrieval/download_file_of_scope_name.py +150 -0
- dafab_client/helpers/file_management/Workflows/manage_derived_assets.py +1240 -0
- dafab_client/helpers/file_management/list_files.py +121 -0
- dafab_client/helpers/filtering/filter_by_bbox.py +170 -0
- dafab_client/helpers/filtering/filter_by_bbox_and_timerange.py +203 -0
- dafab_client/helpers/filtering/filter_by_enhanced_filter.py +119 -0
- dafab_client/helpers/filtering/filter_by_relationships.py +207 -0
- dafab_client/helpers/filtering/filter_by_timerange.py +159 -0
- dafab_client/helpers/filtering/mapper.py +388 -0
- dafab_client/helpers/metadata_management/Insertion/set_metadata.py +110 -0
- dafab_client/helpers/metadata_management/Operations/common.py +100 -0
- dafab_client/helpers/metadata_management/Operations/delete_metadata_for_did.py +41 -0
- dafab_client/helpers/metadata_management/Operations/insert_metadata_for_did.py +50 -0
- dafab_client/helpers/metadata_management/Operations/update_metadata_for_did.py +50 -0
- dafab_client/helpers/metadata_management/Operations/upsert_metadata_for_did.py +50 -0
- dafab_client/helpers/metadata_management/Patch/patch_metadata_for_dids.py +104 -0
- dafab_client/helpers/metadata_management/Preflight/validate_derived_item_file.py +1707 -0
- dafab_client/helpers/metadata_management/Preflight/validate_facet_value_catalog_file.py +438 -0
- dafab_client/helpers/metadata_management/Preflight/validate_original_item_file.py +1326 -0
- dafab_client/helpers/metadata_management/Retrieval/Complete/get_metadata_for_scope_name.py +141 -0
- dafab_client/helpers/metadata_management/Retrieval/Partial/get_single_metadata_for_scope_name.py +116 -0
- dafab_client/helpers/metadata_management/Workflows/sync_spatial_bbox_integrity.py +1805 -0
- dafab_client/helpers/metadata_management/Workflows/update_derived_item.py +999 -0
- dafab_client/helpers/metadata_management/Workflows/update_original_item.py +575 -0
- dafab_client/helpers/metadata_management/document_api.py +64 -0
- dafab_client/helpers/resources/schemas/Schema_Copernicus_with_dafab.json +4781 -0
- dafab_client/helpers/resources/schemas/Schema_DaFab_Facet_Value_Catalog.json +52 -0
- dafab_client/helpers/resources/schemas/dafab-smart_agriculture-item.schema.json +413 -0
- dafab_client/helpers/resources/schemas/dafab-water_analysis-item.schema.json +1134 -0
- dafab_client/helpers/runtime_paths.py +80 -0
- dafab_client-2.2.0.dist-info/METADATA +122 -0
- dafab_client-2.2.0.dist-info/RECORD +96 -0
- dafab_client-2.2.0.dist-info/WHEEL +5 -0
- dafab_client-2.2.0.dist-info/top_level.txt +1 -0
dafab_client/__init__.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Public import namespace for the DaFab client package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from shutil import copy2
|
|
7
|
+
|
|
8
|
+
from . import helpers
|
|
9
|
+
from ._rucio.global_utils import get_active_account, set_active_account
|
|
10
|
+
|
|
11
|
+
_ACCOUNT_API = ("get_active_account", "set_active_account")
|
|
12
|
+
_EXAMPLE_NOTEBOOKS_DIR = Path(__file__).resolve().parent / "example_notebooks"
|
|
13
|
+
_EXAMPLE_PROFILE_TO_NOTEBOOK = {
|
|
14
|
+
"user": "dafab_simple_user_workflows.ipynb",
|
|
15
|
+
"admin": "dafab_operator_workflows.ipynb",
|
|
16
|
+
"skim": "dafab_skim_workflows.ipynb",
|
|
17
|
+
"dasi": "dafab_dasi_workflows.ipynb",
|
|
18
|
+
}
|
|
19
|
+
_EXAMPLE_PROFILE_ALIASES = {
|
|
20
|
+
"operator": "admin",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
__all__ = ("helpers", *_ACCOUNT_API, "get_example", *helpers.__all__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_example(profile: str | None = None, destination: str | Path = ".") -> list[Path]:
|
|
27
|
+
"""Copy packaged notebooks into ``destination`` and return copied local paths."""
|
|
28
|
+
if profile is None:
|
|
29
|
+
notebook_names = sorted(set(_EXAMPLE_PROFILE_TO_NOTEBOOK.values()))
|
|
30
|
+
else:
|
|
31
|
+
normalized = str(profile).strip().lower()
|
|
32
|
+
normalized = _EXAMPLE_PROFILE_ALIASES.get(normalized, normalized)
|
|
33
|
+
notebook_name = _EXAMPLE_PROFILE_TO_NOTEBOOK.get(normalized)
|
|
34
|
+
if notebook_name is None:
|
|
35
|
+
valid_profiles = ", ".join(sorted(_EXAMPLE_PROFILE_TO_NOTEBOOK))
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Unknown profile '{profile}'. Supported values: {valid_profiles}."
|
|
38
|
+
)
|
|
39
|
+
notebook_names = [notebook_name]
|
|
40
|
+
|
|
41
|
+
target_dir = Path(destination).expanduser().resolve()
|
|
42
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
copied_paths: list[Path] = []
|
|
45
|
+
for notebook_name in notebook_names:
|
|
46
|
+
source_path = _EXAMPLE_NOTEBOOKS_DIR / notebook_name
|
|
47
|
+
if not source_path.is_file():
|
|
48
|
+
raise FileNotFoundError(f"Missing packaged notebook: {source_path}")
|
|
49
|
+
target_path = target_dir / notebook_name
|
|
50
|
+
copy2(source_path, target_path)
|
|
51
|
+
copied_paths.append(target_path)
|
|
52
|
+
|
|
53
|
+
return copied_paths
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def __getattr__(name: str):
|
|
57
|
+
if name in helpers.__all__:
|
|
58
|
+
return getattr(helpers, name)
|
|
59
|
+
if name in _ACCOUNT_API:
|
|
60
|
+
return globals()[name]
|
|
61
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def __dir__() -> list[str]:
|
|
65
|
+
return sorted(set(globals()) | set(__all__))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Internal vendored Rucio modules used by dafab_client."""
|
|
@@ -0,0 +1,15 @@
|
|
|
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 .client import Client # noqa: F401
|
|
@@ -0,0 +1,578 @@
|
|
|
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 json import dumps
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
17
|
+
from urllib.parse import quote_plus
|
|
18
|
+
|
|
19
|
+
from requests.status_codes import codes
|
|
20
|
+
|
|
21
|
+
from dafab_client._rucio.client.baseclient import BaseClient, choice
|
|
22
|
+
from dafab_client._rucio.common.constants import HTTPMethod
|
|
23
|
+
from dafab_client._rucio.common.utils import build_url
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Iterator
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AccountClient(BaseClient):
|
|
30
|
+
|
|
31
|
+
"""Account client class for working with rucio accounts"""
|
|
32
|
+
|
|
33
|
+
ACCOUNTS_BASEURL = 'accounts'
|
|
34
|
+
|
|
35
|
+
def add_account(self, account: str, type_: str, email: str) -> bool:
|
|
36
|
+
"""
|
|
37
|
+
Sends the request to create a new account.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
account :
|
|
42
|
+
The name of the account.
|
|
43
|
+
type_ :
|
|
44
|
+
The account type.
|
|
45
|
+
email :
|
|
46
|
+
The Email address associated with the account.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
|
|
51
|
+
True if account was created successfully else False.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
Duplicate
|
|
56
|
+
If account already exists.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
data = dumps({'type': type_, 'email': email})
|
|
60
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account])
|
|
61
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
62
|
+
|
|
63
|
+
res = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
64
|
+
if res.status_code == codes.created:
|
|
65
|
+
return True
|
|
66
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
67
|
+
raise exc_cls(exc_msg)
|
|
68
|
+
|
|
69
|
+
def delete_account(self, account: str) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Send the request to disable an account.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
account :
|
|
76
|
+
The name of the account.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
|
|
81
|
+
True if account was disabled successfully. False otherwise.
|
|
82
|
+
|
|
83
|
+
Raises
|
|
84
|
+
------
|
|
85
|
+
AccountNotFound
|
|
86
|
+
If account doesn't exist.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account])
|
|
90
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
91
|
+
|
|
92
|
+
res = self._send_request(url, method=HTTPMethod.DELETE)
|
|
93
|
+
|
|
94
|
+
if res.status_code == codes.ok:
|
|
95
|
+
return True
|
|
96
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
97
|
+
raise exc_cls(exc_msg)
|
|
98
|
+
|
|
99
|
+
def get_account(self, account: str) -> Optional[dict[str, Any]]:
|
|
100
|
+
"""
|
|
101
|
+
Send the request to get information about a given account.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
account :
|
|
106
|
+
The name of the account.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
|
|
111
|
+
A dictionary of attributes for the account. None if failure.
|
|
112
|
+
|
|
113
|
+
Raises
|
|
114
|
+
------
|
|
115
|
+
AccountNotFound
|
|
116
|
+
If account doesn't exist.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account])
|
|
120
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
121
|
+
|
|
122
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
123
|
+
if res.status_code == codes.ok:
|
|
124
|
+
acc = self._load_json_data(res)
|
|
125
|
+
return next(acc)
|
|
126
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
127
|
+
raise exc_cls(exc_msg)
|
|
128
|
+
|
|
129
|
+
def update_account(self, account: str, key: str, value: Any) -> bool:
|
|
130
|
+
"""
|
|
131
|
+
Update a property of an account.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
account :
|
|
136
|
+
Name of the account.
|
|
137
|
+
key :
|
|
138
|
+
Account property like status.
|
|
139
|
+
value :
|
|
140
|
+
Property value.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
|
|
145
|
+
True if successful.
|
|
146
|
+
|
|
147
|
+
Raises
|
|
148
|
+
------
|
|
149
|
+
Exception
|
|
150
|
+
If update fails.
|
|
151
|
+
"""
|
|
152
|
+
data = dumps({key: value})
|
|
153
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account])
|
|
154
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
155
|
+
|
|
156
|
+
res = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
157
|
+
|
|
158
|
+
if res.status_code == codes.ok:
|
|
159
|
+
return True
|
|
160
|
+
else:
|
|
161
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
162
|
+
raise exc_cls(exc_msg)
|
|
163
|
+
|
|
164
|
+
def list_accounts(
|
|
165
|
+
self,
|
|
166
|
+
account_type: Optional[str] = None,
|
|
167
|
+
identity: Optional[str] = None,
|
|
168
|
+
filters: Optional[dict[str, Any]] = None
|
|
169
|
+
) -> "Iterator[dict[str, Any]]":
|
|
170
|
+
"""
|
|
171
|
+
Send the request to list all rucio accounts.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
account_type :
|
|
176
|
+
The account type.
|
|
177
|
+
identity :
|
|
178
|
+
The identity key name. For example x509 DN, or a username.
|
|
179
|
+
filters :
|
|
180
|
+
A dictionary key:account attribute to use for the filtering.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
|
|
185
|
+
An iterator of dictionaries containing account information.
|
|
186
|
+
|
|
187
|
+
Raises
|
|
188
|
+
------
|
|
189
|
+
AccountNotFound
|
|
190
|
+
If account doesn't exist.
|
|
191
|
+
"""
|
|
192
|
+
path = '/'.join([self.ACCOUNTS_BASEURL])
|
|
193
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
194
|
+
params = {}
|
|
195
|
+
if account_type:
|
|
196
|
+
params['account_type'] = account_type
|
|
197
|
+
if identity:
|
|
198
|
+
params['identity'] = identity
|
|
199
|
+
if filters:
|
|
200
|
+
for key in filters:
|
|
201
|
+
params[key] = filters[key]
|
|
202
|
+
|
|
203
|
+
res = self._send_request(url, method=HTTPMethod.GET, params=params)
|
|
204
|
+
|
|
205
|
+
if res.status_code == codes.ok:
|
|
206
|
+
accounts = self._load_json_data(res)
|
|
207
|
+
return accounts
|
|
208
|
+
else:
|
|
209
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
210
|
+
raise exc_cls(exc_msg)
|
|
211
|
+
|
|
212
|
+
def whoami(self) -> Optional[dict[str, Any]]:
|
|
213
|
+
"""
|
|
214
|
+
Get information about account whose token is used.
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
|
|
219
|
+
A dictionary of attributes for the account. None if failure.
|
|
220
|
+
|
|
221
|
+
Raises
|
|
222
|
+
------
|
|
223
|
+
AccountNotFound
|
|
224
|
+
If account doesn't exist.
|
|
225
|
+
"""
|
|
226
|
+
return self.get_account('whoami')
|
|
227
|
+
|
|
228
|
+
def add_identity(
|
|
229
|
+
self,
|
|
230
|
+
account: str,
|
|
231
|
+
identity: str,
|
|
232
|
+
authtype: str,
|
|
233
|
+
email: str,
|
|
234
|
+
default: bool = False,
|
|
235
|
+
password: Optional[str] = None
|
|
236
|
+
) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Add a membership association between identity and account.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
account :
|
|
243
|
+
The account name.
|
|
244
|
+
identity :
|
|
245
|
+
The identity key name. For example x509 DN, or a username.
|
|
246
|
+
authtype :
|
|
247
|
+
The type of the authentication (x509, gss, userpass).
|
|
248
|
+
email :
|
|
249
|
+
The Email address associated with the identity.
|
|
250
|
+
default :
|
|
251
|
+
If True, the account should be used by default with the provided identity.
|
|
252
|
+
password :
|
|
253
|
+
Password if authtype is userpass.
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
|
|
258
|
+
True if successful.
|
|
259
|
+
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
data = dumps({'identity': identity, 'authtype': authtype, 'default': default, 'email': email, 'password': password})
|
|
263
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'identities'])
|
|
264
|
+
|
|
265
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
266
|
+
|
|
267
|
+
res = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
268
|
+
|
|
269
|
+
if res.status_code == codes.created:
|
|
270
|
+
return True
|
|
271
|
+
else:
|
|
272
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
273
|
+
raise exc_cls(exc_msg)
|
|
274
|
+
|
|
275
|
+
def del_identity(
|
|
276
|
+
self,
|
|
277
|
+
account: str,
|
|
278
|
+
identity: str,
|
|
279
|
+
authtype: str
|
|
280
|
+
) -> bool:
|
|
281
|
+
"""
|
|
282
|
+
Delete an identity's membership association with an account.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
account :
|
|
287
|
+
The account name.
|
|
288
|
+
identity :
|
|
289
|
+
The identity key name. For example x509 DN, or a username.
|
|
290
|
+
authtype :
|
|
291
|
+
The type of the authentication (x509, gss, userpass).
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
|
|
296
|
+
True if successful.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
data = dumps({'identity': identity, 'authtype': authtype})
|
|
300
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'identities'])
|
|
301
|
+
|
|
302
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
303
|
+
|
|
304
|
+
res = self._send_request(url, method=HTTPMethod.DELETE, data=data)
|
|
305
|
+
|
|
306
|
+
if res.status_code == codes.ok:
|
|
307
|
+
return True
|
|
308
|
+
else:
|
|
309
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
310
|
+
raise exc_cls(exc_msg)
|
|
311
|
+
|
|
312
|
+
def list_identities(self, account: str) -> "Iterator[dict[str, Any]]":
|
|
313
|
+
"""
|
|
314
|
+
List all identities on an account.
|
|
315
|
+
|
|
316
|
+
Parameters
|
|
317
|
+
----------
|
|
318
|
+
account :
|
|
319
|
+
The account name.
|
|
320
|
+
"""
|
|
321
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'identities'])
|
|
322
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
323
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
324
|
+
if res.status_code == codes.ok:
|
|
325
|
+
identities = self._load_json_data(res)
|
|
326
|
+
return identities
|
|
327
|
+
else:
|
|
328
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
329
|
+
raise exc_cls(exc_msg)
|
|
330
|
+
|
|
331
|
+
def list_account_rules(self, account: str) -> "Iterator[dict[str, Any]]":
|
|
332
|
+
"""
|
|
333
|
+
List the associated rules of an account.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
account :
|
|
338
|
+
The account name.
|
|
339
|
+
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'rules'])
|
|
343
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
344
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
345
|
+
if res.status_code == codes.ok:
|
|
346
|
+
return self._load_json_data(res)
|
|
347
|
+
else:
|
|
348
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
349
|
+
raise exc_cls(exc_msg)
|
|
350
|
+
|
|
351
|
+
def get_account_limits(self, account: str, rse_expression: str, locality: str) -> dict[str, Any]:
|
|
352
|
+
"""
|
|
353
|
+
Return the correct account limits for the given locality.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
account :
|
|
358
|
+
The account name.
|
|
359
|
+
rse_expression :
|
|
360
|
+
Valid RSE expression.
|
|
361
|
+
locality :
|
|
362
|
+
The scope of the account limit. 'local' or 'global'.
|
|
363
|
+
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
if locality == 'local':
|
|
367
|
+
return self.get_local_account_limit(account, rse_expression)
|
|
368
|
+
elif locality == 'global':
|
|
369
|
+
return self.get_global_account_limit(account, rse_expression)
|
|
370
|
+
else:
|
|
371
|
+
from dafab_client._rucio.common.exception import UnsupportedOperation
|
|
372
|
+
raise UnsupportedOperation('The provided locality (%s) for the account limit was invalid' % locality)
|
|
373
|
+
|
|
374
|
+
def get_global_account_limit(self, account: str, rse_expression: str) -> dict[str, Any]:
|
|
375
|
+
"""
|
|
376
|
+
List the account limit for the specific RSE expression.
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
account :
|
|
381
|
+
The account name.
|
|
382
|
+
rse_expression :
|
|
383
|
+
The rse expression.
|
|
384
|
+
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'limits', 'global', quote_plus(rse_expression)])
|
|
388
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
389
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
390
|
+
if res.status_code == codes.ok:
|
|
391
|
+
return next(self._load_json_data(res))
|
|
392
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
393
|
+
raise exc_cls(exc_msg)
|
|
394
|
+
|
|
395
|
+
def get_global_account_limits(self, account: str) -> dict[str, Any]:
|
|
396
|
+
"""
|
|
397
|
+
List all RSE expression limits of this account.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
account :
|
|
402
|
+
The account name.
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'limits', 'global'])
|
|
406
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
407
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
408
|
+
if res.status_code == codes.ok:
|
|
409
|
+
return next(self._load_json_data(res))
|
|
410
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
411
|
+
raise exc_cls(exc_msg)
|
|
412
|
+
|
|
413
|
+
def get_local_account_limits(self, account: str) -> dict[str, Any]:
|
|
414
|
+
"""
|
|
415
|
+
List the account rse limits of this account.
|
|
416
|
+
|
|
417
|
+
Parameters
|
|
418
|
+
----------
|
|
419
|
+
account :
|
|
420
|
+
The account name.
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'limits', 'local'])
|
|
424
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
425
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
426
|
+
if res.status_code == codes.ok:
|
|
427
|
+
return next(self._load_json_data(res))
|
|
428
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
429
|
+
raise exc_cls(exc_msg)
|
|
430
|
+
|
|
431
|
+
def get_local_account_limit(self, account: str, rse: str) -> dict[str, Any]:
|
|
432
|
+
"""
|
|
433
|
+
List the account rse limits of this account for the specific rse.
|
|
434
|
+
|
|
435
|
+
Parameters
|
|
436
|
+
----------
|
|
437
|
+
account :
|
|
438
|
+
The account name.
|
|
439
|
+
rse :
|
|
440
|
+
The rse name.
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'limits', 'local', rse])
|
|
444
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
445
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
446
|
+
if res.status_code == codes.ok:
|
|
447
|
+
return next(self._load_json_data(res))
|
|
448
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
449
|
+
raise exc_cls(exc_msg)
|
|
450
|
+
|
|
451
|
+
def get_local_account_usage(self, account: str, rse: Optional[str] = None) -> "Iterator[dict[str, Any]]":
|
|
452
|
+
"""
|
|
453
|
+
List the account usage for one or all rses of this account.
|
|
454
|
+
|
|
455
|
+
Parameters
|
|
456
|
+
----------
|
|
457
|
+
account :
|
|
458
|
+
The account name.
|
|
459
|
+
rse :
|
|
460
|
+
The rse name.
|
|
461
|
+
"""
|
|
462
|
+
if rse:
|
|
463
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'usage', 'local', rse])
|
|
464
|
+
else:
|
|
465
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'usage', 'local'])
|
|
466
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
467
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
468
|
+
if res.status_code == codes.ok:
|
|
469
|
+
return self._load_json_data(res)
|
|
470
|
+
else:
|
|
471
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
472
|
+
raise exc_cls(exc_msg)
|
|
473
|
+
|
|
474
|
+
def get_global_account_usage(self, account: str, rse_expression: Optional[str] = None) -> "Iterator[dict[str, Any]]":
|
|
475
|
+
"""
|
|
476
|
+
List the account usage for one or all RSE expressions of this account.
|
|
477
|
+
|
|
478
|
+
Parameters
|
|
479
|
+
----------
|
|
480
|
+
account :
|
|
481
|
+
The account name.
|
|
482
|
+
rse_expression :
|
|
483
|
+
The rse expression.
|
|
484
|
+
"""
|
|
485
|
+
if rse_expression:
|
|
486
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'usage', 'global', quote_plus(rse_expression)])
|
|
487
|
+
else:
|
|
488
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'usage', 'global'])
|
|
489
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
490
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
491
|
+
if res.status_code == codes.ok:
|
|
492
|
+
return self._load_json_data(res)
|
|
493
|
+
else:
|
|
494
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
495
|
+
raise exc_cls(exc_msg)
|
|
496
|
+
|
|
497
|
+
def get_account_usage_history(self, account: str, rse: str) -> dict[str, Any]:
|
|
498
|
+
"""
|
|
499
|
+
List the account usage history of this account on rse.
|
|
500
|
+
|
|
501
|
+
Parameters
|
|
502
|
+
----------
|
|
503
|
+
account :
|
|
504
|
+
The account name.
|
|
505
|
+
rse :
|
|
506
|
+
The rse name.
|
|
507
|
+
"""
|
|
508
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'usage/history', rse])
|
|
509
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
510
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
511
|
+
if res.status_code == codes.ok:
|
|
512
|
+
return next(self._load_json_data(res))
|
|
513
|
+
else:
|
|
514
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
515
|
+
raise exc_cls(exc_msg)
|
|
516
|
+
|
|
517
|
+
def list_account_attributes(self, account: str) -> "Iterator[dict[dict[str, Any], Any]]":
|
|
518
|
+
"""
|
|
519
|
+
List the attributes for an account.
|
|
520
|
+
|
|
521
|
+
Parameters
|
|
522
|
+
----------
|
|
523
|
+
account :
|
|
524
|
+
The account name.
|
|
525
|
+
"""
|
|
526
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'attr/'])
|
|
527
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
528
|
+
res = self._send_request(url, method=HTTPMethod.GET)
|
|
529
|
+
if res.status_code == codes.ok:
|
|
530
|
+
return self._load_json_data(res)
|
|
531
|
+
else:
|
|
532
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
533
|
+
raise exc_cls(exc_msg)
|
|
534
|
+
|
|
535
|
+
def add_account_attribute(self, account: str, key: str, value: Any) -> bool:
|
|
536
|
+
"""
|
|
537
|
+
Add an attribute to an account.
|
|
538
|
+
|
|
539
|
+
Parameters
|
|
540
|
+
----------
|
|
541
|
+
account :
|
|
542
|
+
The account name.
|
|
543
|
+
key :
|
|
544
|
+
The attribute key.
|
|
545
|
+
value :
|
|
546
|
+
The attribute value.
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
data = dumps({'key': key, 'value': value})
|
|
550
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'attr', key])
|
|
551
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
552
|
+
res = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
553
|
+
if res.status_code == codes.created:
|
|
554
|
+
return True
|
|
555
|
+
else:
|
|
556
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
557
|
+
raise exc_cls(exc_msg)
|
|
558
|
+
|
|
559
|
+
def delete_account_attribute(self, account: str, key: str) -> bool:
|
|
560
|
+
"""
|
|
561
|
+
Delete an attribute for an account.
|
|
562
|
+
|
|
563
|
+
Parameters
|
|
564
|
+
----------
|
|
565
|
+
account :
|
|
566
|
+
The account name.
|
|
567
|
+
key :
|
|
568
|
+
The attribute key.
|
|
569
|
+
"""
|
|
570
|
+
|
|
571
|
+
path = '/'.join([self.ACCOUNTS_BASEURL, account, 'attr', key])
|
|
572
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
573
|
+
res = self._send_request(url, method=HTTPMethod.DELETE, data=None)
|
|
574
|
+
if res.status_code == codes.ok:
|
|
575
|
+
return True
|
|
576
|
+
else:
|
|
577
|
+
exc_cls, exc_msg = self._get_exception(headers=res.headers, status_code=res.status_code, data=res.content)
|
|
578
|
+
raise exc_cls(exc_msg)
|