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.
Files changed (88) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/client/__init__.py +15 -0
  4. rucio/client/accountclient.py +433 -0
  5. rucio/client/accountlimitclient.py +183 -0
  6. rucio/client/baseclient.py +974 -0
  7. rucio/client/client.py +76 -0
  8. rucio/client/configclient.py +126 -0
  9. rucio/client/credentialclient.py +59 -0
  10. rucio/client/didclient.py +866 -0
  11. rucio/client/diracclient.py +56 -0
  12. rucio/client/downloadclient.py +1785 -0
  13. rucio/client/exportclient.py +44 -0
  14. rucio/client/fileclient.py +50 -0
  15. rucio/client/importclient.py +42 -0
  16. rucio/client/lifetimeclient.py +90 -0
  17. rucio/client/lockclient.py +109 -0
  18. rucio/client/metaconventionsclient.py +140 -0
  19. rucio/client/pingclient.py +44 -0
  20. rucio/client/replicaclient.py +454 -0
  21. rucio/client/requestclient.py +125 -0
  22. rucio/client/rseclient.py +746 -0
  23. rucio/client/ruleclient.py +294 -0
  24. rucio/client/scopeclient.py +90 -0
  25. rucio/client/subscriptionclient.py +173 -0
  26. rucio/client/touchclient.py +82 -0
  27. rucio/client/uploadclient.py +955 -0
  28. rucio/common/__init__.py +13 -0
  29. rucio/common/cache.py +74 -0
  30. rucio/common/config.py +801 -0
  31. rucio/common/constants.py +159 -0
  32. rucio/common/constraints.py +17 -0
  33. rucio/common/didtype.py +189 -0
  34. rucio/common/exception.py +1151 -0
  35. rucio/common/extra.py +36 -0
  36. rucio/common/logging.py +420 -0
  37. rucio/common/pcache.py +1408 -0
  38. rucio/common/plugins.py +153 -0
  39. rucio/common/policy.py +84 -0
  40. rucio/common/schema/__init__.py +150 -0
  41. rucio/common/schema/atlas.py +413 -0
  42. rucio/common/schema/belleii.py +408 -0
  43. rucio/common/schema/domatpc.py +401 -0
  44. rucio/common/schema/escape.py +426 -0
  45. rucio/common/schema/generic.py +433 -0
  46. rucio/common/schema/generic_multi_vo.py +412 -0
  47. rucio/common/schema/icecube.py +406 -0
  48. rucio/common/stomp_utils.py +159 -0
  49. rucio/common/stopwatch.py +55 -0
  50. rucio/common/test_rucio_server.py +148 -0
  51. rucio/common/types.py +403 -0
  52. rucio/common/utils.py +2238 -0
  53. rucio/rse/__init__.py +96 -0
  54. rucio/rse/protocols/__init__.py +13 -0
  55. rucio/rse/protocols/bittorrent.py +184 -0
  56. rucio/rse/protocols/cache.py +122 -0
  57. rucio/rse/protocols/dummy.py +111 -0
  58. rucio/rse/protocols/gfal.py +703 -0
  59. rucio/rse/protocols/globus.py +243 -0
  60. rucio/rse/protocols/gsiftp.py +92 -0
  61. rucio/rse/protocols/http_cache.py +82 -0
  62. rucio/rse/protocols/mock.py +123 -0
  63. rucio/rse/protocols/ngarc.py +209 -0
  64. rucio/rse/protocols/posix.py +250 -0
  65. rucio/rse/protocols/protocol.py +594 -0
  66. rucio/rse/protocols/rclone.py +364 -0
  67. rucio/rse/protocols/rfio.py +136 -0
  68. rucio/rse/protocols/srm.py +338 -0
  69. rucio/rse/protocols/ssh.py +413 -0
  70. rucio/rse/protocols/storm.py +206 -0
  71. rucio/rse/protocols/webdav.py +550 -0
  72. rucio/rse/protocols/xrootd.py +301 -0
  73. rucio/rse/rsemanager.py +764 -0
  74. rucio/vcsversion.py +11 -0
  75. rucio/version.py +38 -0
  76. rucio_clients-35.8.2.data/data/etc/rse-accounts.cfg.template +25 -0
  77. rucio_clients-35.8.2.data/data/etc/rucio.cfg.atlas.client.template +42 -0
  78. rucio_clients-35.8.2.data/data/etc/rucio.cfg.template +257 -0
  79. rucio_clients-35.8.2.data/data/requirements.client.txt +15 -0
  80. rucio_clients-35.8.2.data/data/rucio_client/merge_rucio_configs.py +144 -0
  81. rucio_clients-35.8.2.data/scripts/rucio +2542 -0
  82. rucio_clients-35.8.2.data/scripts/rucio-admin +2447 -0
  83. rucio_clients-35.8.2.dist-info/METADATA +50 -0
  84. rucio_clients-35.8.2.dist-info/RECORD +88 -0
  85. rucio_clients-35.8.2.dist-info/WHEEL +5 -0
  86. rucio_clients-35.8.2.dist-info/licenses/AUTHORS.rst +97 -0
  87. rucio_clients-35.8.2.dist-info/licenses/LICENSE +201 -0
  88. rucio_clients-35.8.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,209 @@
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
+ import errno
16
+ import os
17
+
18
+ from rucio.common.exception import FileAlreadyExists, ServiceUnavailable, SourceNotFound
19
+ from rucio.rse.protocols import protocol
20
+
21
+ try:
22
+ import arc # pylint: disable=import-error
23
+ except:
24
+ pass
25
+
26
+
27
+ class DataPoint:
28
+ '''
29
+ Wrapper around arc.datapoint_from_url() which does not clean up DataPoints
30
+ when python objects are destroyed, leading to connection leaking when used
31
+ with gridftp. This class should be used instead of arc.datapoint_from_url().
32
+ It can be called like dp = DataPoint('gsiftp://...', uc); dp.h.Stat()
33
+ where uc is an arc.UserConfig object.
34
+ '''
35
+ def __init__(self, u, uc):
36
+ self.h = arc.datapoint_from_url(u, uc)
37
+
38
+ def __del__(self):
39
+ arc.DataPoint.__swig_destroy__(self.h)
40
+
41
+
42
+ class Default(protocol.RSEProtocol):
43
+ """ Implementing access to RSEs using ARC client."""
44
+
45
+ def __init__(self, protocol_attr, rse_settings, logger=None):
46
+ """
47
+ Set up UserConfig object.
48
+ """
49
+ super(Default, self).__init__(protocol_attr, rse_settings, logger=logger)
50
+
51
+ # Arc logging to stdout, uncomment for debugging. Should use root
52
+ # logger level eventually.
53
+ # root_logger = arc.Logger_getRootLogger()
54
+ # stream = arc.LogStream(sys.stdout)
55
+ # root_logger.addDestination(stream)
56
+ # # Set threshold to VERBOSE or DEBUG for more information
57
+ # root_logger.setThreshold(arc.DEBUG)
58
+
59
+ self.cfg = arc.UserConfig()
60
+ try:
61
+ self.cfg.ProxyPath(os.environ['X509_USER_PROXY'])
62
+ except:
63
+ pass
64
+
65
+ def path2pfn(self, path):
66
+ """
67
+ Returns a fully qualified PFN for the file referred by path.
68
+
69
+ :param path: The path to the file.
70
+
71
+ :returns: Fully qualified PFN.
72
+
73
+ """
74
+ return ''.join([self.rse['scheme'], '://%s' % self.rse['hostname'], path])
75
+
76
+ def exists(self, pfn):
77
+ """ Checks if the requested file is known by the referred RSE.
78
+
79
+ :param pfn: Physical file name
80
+
81
+ :returns: True if the file exists, False if it doesn't
82
+
83
+ :raise ServiceUnavailable
84
+ """
85
+ dp = DataPoint(str(pfn), self.cfg)
86
+ fileinfo = arc.FileInfo()
87
+
88
+ status = dp.h.Stat(fileinfo)
89
+ if not status:
90
+ if status.GetErrno() == errno.ENOENT:
91
+ return False
92
+ raise ServiceUnavailable(str(status))
93
+
94
+ return True
95
+
96
+ def connect(self):
97
+ """ Establishes the actual connection to the referred RSE.
98
+
99
+ :raise RSEAccessDenied
100
+ """
101
+ pass
102
+
103
+ def close(self):
104
+ """ Closes the connection to RSE."""
105
+ pass
106
+
107
+ def __arc_copy(self, src, dest, space_token=None, transfer_timeout=None):
108
+
109
+ # TODO set proxy path
110
+
111
+ # Convert the arguments to DataPoint objects
112
+ source = DataPoint(str(src), self.cfg)
113
+ if source.h is None:
114
+ raise ServiceUnavailable("Can't handle source %s" % src)
115
+
116
+ destination = DataPoint(str(dest), self.cfg)
117
+ if destination.h is None:
118
+ raise ServiceUnavailable("Can't handle destination %s" % dest)
119
+ if space_token:
120
+ destination.h.GetURL().AddOption('spacetoken', space_token)
121
+
122
+ # DataMover does the transfer
123
+ mover = arc.DataMover()
124
+ # Don't attempt to retry on error
125
+ mover.retry(False)
126
+ # Passive and insecure gridftp
127
+ mover.passive(True)
128
+ mover.secure(False)
129
+ # Do the transfer
130
+ status = mover.Transfer(source.h, destination.h, arc.FileCache(), arc.URLMap())
131
+
132
+ if not status:
133
+ if status.GetErrno() == errno.ENOENT:
134
+ raise SourceNotFound()
135
+ if status.GetErrno() == errno.EEXIST:
136
+ raise FileAlreadyExists()
137
+ raise ServiceUnavailable(str(status))
138
+
139
+ def get(self, pfn, dest, transfer_timeout=None):
140
+ """ Provides access to files stored inside connected the RSE.
141
+
142
+ :param pfn: Physical file name of requested file
143
+ :param dest: Name and path of the files when stored at the client
144
+ :param transfer_timeout Transfer timeout (in seconds) - dummy
145
+
146
+ :raises DestinationNotAccessible, ServiceUnavailable, SourceNotFound
147
+ """
148
+ self.__arc_copy(pfn, dest, transfer_timeout=transfer_timeout)
149
+
150
+ def put(self, source, target, source_dir=None, transfer_timeout=None):
151
+ """ Allows to store files inside the referred RSE.
152
+
153
+ :param source: Physical file name
154
+ :param target: Name of the file on the storage system e.g. with prefixed scope
155
+ :param source_dir Path where the to be transferred files are stored in the local file system
156
+ :param transfer_timeout Transfer timeout (in seconds) - dummy
157
+
158
+ :raises DestinationNotAccessible, ServiceUnavailable, SourceNotFound
159
+ """
160
+
161
+ if source_dir:
162
+ sf = source_dir + '/' + source
163
+ else:
164
+ sf = source
165
+
166
+ space_token = None
167
+ if self.attributes['extended_attributes'] is not None and 'space_token' in list(self.attributes['extended_attributes'].keys()):
168
+ space_token = self.attributes['extended_attributes']['space_token']
169
+
170
+ self.__arc_copy(sf, target, space_token, transfer_timeout=transfer_timeout)
171
+
172
+ def delete(self, pfn):
173
+ """ Deletes a file from the connected RSE.
174
+
175
+ :param pfn: Physical file name
176
+
177
+ :raises ServiceUnavailable, SourceNotFound
178
+ """
179
+ dp = DataPoint(str(pfn), self.cfg)
180
+ if dp.h is None:
181
+ raise ServiceUnavailable("Can't handle pfn %s" % pfn)
182
+
183
+ status = dp.h.Remove()
184
+ if not status:
185
+ if status.GetErrno() == errno.ENOENT:
186
+ raise SourceNotFound()
187
+ raise ServiceUnavailable(str(status))
188
+
189
+ def rename(self, pfn, new_pfn):
190
+ """ Allows to rename a file stored inside the connected RSE.
191
+
192
+ :param pfn: Current physical file name
193
+ :param new_pfn New physical file name
194
+
195
+ :raises DestinationNotAccessible, ServiceUnavailable, SourceNotFound
196
+ """
197
+ dp = DataPoint(str(pfn), self.cfg)
198
+ if dp.h is None:
199
+ raise ServiceUnavailable("Can't handle pfn %s" % pfn)
200
+
201
+ url = arc.URL(str(new_pfn))
202
+ if not url:
203
+ raise ServiceUnavailable("Can't handle new pfn %s" % new_pfn)
204
+
205
+ status = dp.h.Rename(url)
206
+ if not status:
207
+ if status.GetErrno() == errno.ENOENT:
208
+ raise SourceNotFound()
209
+ raise ServiceUnavailable(str(status))
@@ -0,0 +1,250 @@
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
+ import logging
16
+ import os
17
+ import os.path
18
+ import shutil
19
+ from subprocess import call
20
+
21
+ from rucio.common import exception
22
+ from rucio.common.utils import adler32
23
+ from rucio.rse.protocols import protocol
24
+
25
+
26
+ class Default(protocol.RSEProtocol):
27
+ """ Implementing access to RSEs using the local filesystem."""
28
+
29
+ def exists(self, pfn):
30
+ """
31
+ Checks if the requested file is known by the referred RSE.
32
+
33
+ :param pfn: Physical file name
34
+
35
+ :returns: True if the file exists, False if it doesn't
36
+
37
+ :raises SourceNotFound: if the source file was not found on the referred storage.
38
+ """
39
+ status = ''
40
+ try:
41
+ status = os.path.exists(self.pfn2path(pfn))
42
+ except Exception as e:
43
+ raise exception.ServiceUnavailable(e)
44
+ return status
45
+
46
+ def connect(self):
47
+ """
48
+ Establishes the actual connection to the referred RSE.
49
+
50
+ :param credentials: needed to establish a connection with the storage.
51
+
52
+ :raises RSEAccessDenied: if no connection could be established.
53
+ """
54
+ pass
55
+
56
+ def close(self):
57
+ """ Closes the connection to RSE."""
58
+ pass
59
+
60
+ def get(self, pfn, dest, transfer_timeout=None):
61
+ """ Provides access to files stored inside connected the RSE.
62
+
63
+ :param pfn: Physical file name of requested file
64
+ :param dest: Name and path of the files when stored at the client
65
+ :param transfer_timeout Transfer timeout (in seconds) - dummy
66
+
67
+ :raises DestinationNotAccessible: if the destination storage was not accessible.
68
+ :raises ServiceUnavailable: if some generic error occurred in the library.
69
+ :raises SourceNotFound: if the source file was not found on the referred storage.
70
+ """
71
+ try:
72
+ shutil.copy(self.pfn2path(pfn), dest)
73
+ except OSError as e:
74
+ try: # To check if the error happened local or remote
75
+ with open(dest, 'wb'):
76
+ pass
77
+ call(['rm', '-rf', dest]) # noqa: S607
78
+ except OSError as e:
79
+ if e.errno == 2:
80
+ raise exception.DestinationNotAccessible(e)
81
+ else:
82
+ raise exception.ServiceUnavailable(e)
83
+ if e.errno == 2:
84
+ raise exception.SourceNotFound(e)
85
+ else:
86
+ raise exception.ServiceUnavailable(e)
87
+
88
+ def put(self, source, target, source_dir=None, transfer_timeout=None):
89
+ """
90
+ Allows to store files inside the referred RSE.
91
+
92
+ :param source: path to the source file on the client file system
93
+ :param target: path to the destination file on the storage
94
+ :param source_dir: Path where the to be transferred files are stored in the local file system
95
+ :param transfer_timeout Transfer timeout (in seconds) - dummy
96
+
97
+ :raises DestinationNotAccessible: if the destination storage was not accessible.
98
+ :raises ServiceUnavailable: if some generic error occurred in the library.
99
+ :raises SourceNotFound: if the source file was not found on the referred storage.
100
+ """
101
+ target = self.pfn2path(target)
102
+
103
+ if source_dir:
104
+ sf = source_dir + '/' + source
105
+ else:
106
+ sf = source
107
+ try:
108
+ dirs = os.path.dirname(target)
109
+ if not os.path.exists(dirs):
110
+ os.makedirs(dirs)
111
+ shutil.copy(sf, target)
112
+ except OSError as e:
113
+ if e.errno == 2:
114
+ raise exception.SourceNotFound(e)
115
+ elif not self.exists(self.rse['prefix']):
116
+ path = ''
117
+ for p in self.rse['prefix'].split('/'):
118
+ path += p + '/'
119
+ os.mkdir(path)
120
+ shutil.copy(sf, self.pfn2path(target))
121
+ else:
122
+ raise exception.DestinationNotAccessible(e)
123
+
124
+ def delete(self, pfn):
125
+ """ Deletes a file from the connected RSE.
126
+
127
+ :param pfn: pfn to the to be deleted file
128
+
129
+ :raises ServiceUnavailable: if some generic error occurred in the library.
130
+ :raises SourceNotFound: if the source file was not found on the referred storage.
131
+ """
132
+ try:
133
+ os.remove(self.pfn2path(pfn))
134
+ except OSError as e:
135
+ if e.errno == 2:
136
+ raise exception.SourceNotFound(e)
137
+
138
+ def rename(self, pfn, new_pfn):
139
+ """ Allows to rename a file stored inside the connected RSE.
140
+
141
+ :param path: path to the current file on the storage
142
+ :param new_path: path to the new file on the storage
143
+
144
+ :raises DestinationNotAccessible: if the destination storage was not accessible.
145
+ :raises ServiceUnavailable: if some generic error occurred in the library.
146
+ :raises SourceNotFound: if the source file was not found on the referred storage.
147
+ """
148
+ path = self.pfn2path(pfn)
149
+ new_path = self.pfn2path(new_pfn)
150
+ try:
151
+ if not os.path.exists(os.path.dirname(new_path)):
152
+ os.makedirs(os.path.dirname(new_path))
153
+ os.rename(path, new_path)
154
+ except OSError as e:
155
+ if e.errno == 2:
156
+ if self.exists(self.pfn2path(path)):
157
+ raise exception.SourceNotFound(e)
158
+ else:
159
+ raise exception.DestinationNotAccessible(e)
160
+ else:
161
+ raise exception.ServiceUnavailable(e)
162
+
163
+ def lfns2pfns(self, lfns):
164
+ """ Returns fully qualified PFNs for the file referred by each lfn in
165
+ the lfns list.
166
+
167
+ :param lfns: List of lfns. If lfn['path'] is present it is used as
168
+ the path to the file, otherwise the path is constructed
169
+ deterministically.
170
+
171
+ :returns: Fully qualified PFNs.
172
+ """
173
+ pfns = {}
174
+ prefix = self.attributes['prefix']
175
+
176
+ if not prefix.startswith('/'):
177
+ prefix = ''.join(['/', prefix])
178
+ if not prefix.endswith('/'):
179
+ prefix = ''.join([prefix, '/'])
180
+
181
+ lfns = [lfns] if isinstance(lfns, dict) else lfns
182
+ for lfn in lfns:
183
+ scope, name = str(lfn['scope']), lfn['name']
184
+ if lfn.get('path'):
185
+ pfns['%s:%s' % (scope, name)] = ''.join([self.attributes['scheme'],
186
+ '://',
187
+ self.attributes['hostname'],
188
+ prefix,
189
+ lfn['path'] if not lfn['path'].startswith('/') else lfn['path'][1:]
190
+ ])
191
+ else:
192
+ pfns['%s:%s' % (scope, name)] = ''.join([self.attributes['scheme'],
193
+ '://',
194
+ self.attributes['hostname'],
195
+ prefix,
196
+ self._get_path(scope=scope, name=name)
197
+ ])
198
+ return pfns
199
+
200
+ def pfn2path(self, pfn):
201
+ tmp = list(self.parse_pfns(pfn).values())[0]
202
+ return '/'.join([tmp['prefix'], tmp['path'], tmp['name']])
203
+
204
+ def stat(self, pfn):
205
+ """ Determines the file size in bytes and checksum (adler32) of the provided file.
206
+
207
+ :param pfn: The PFN the file.
208
+
209
+ :returns: a dict containing the keys filesize and adler32.
210
+ """
211
+ path = self.pfn2path(pfn)
212
+ return {'filesize': os.stat(path)[os.path.stat.ST_SIZE], 'adler32': adler32(path)}
213
+
214
+
215
+ class Symlink(Default):
216
+ """ Implementing access to RSEs using the local filesystem, creating a symlink on a get """
217
+
218
+ def get(self, pfn, dest, transfer_timeout=None):
219
+ """ Provides access to files stored inside connected the RSE.
220
+ A download/get will create a symlink on the local file system pointing to the
221
+ underlying file. Other operations act directly on the remote file.
222
+ :param pfn: Physical file name of requested file
223
+ :param dest: Name and path of the files when stored at the client
224
+ :param transfer_timeout Transfer timeout (in seconds) - dummy
225
+ :raises DestinationNotAccessible: if the destination storage was not accessible.
226
+ :raises ServiceUnavailable: if some generic error occurred in the library.
227
+ :raises SourceNotFound: if the source file was not found on the referred storage.
228
+ """
229
+ path = self.pfn2path(pfn)
230
+ os.symlink(path, dest)
231
+ self.logger(logging.DEBUG,
232
+ 'Symlink {} created for {} from {}'
233
+ .format(dest, path, pfn))
234
+ if not os.lstat(dest):
235
+ # problem in creating the symlink
236
+ self.logger(logging.ERROR, 'Symlink {} could not be created'.format(dest))
237
+ raise exception.DestinationNotAccessible()
238
+ if not os.path.exists(dest):
239
+ # could not find the file following the symlink
240
+ self.logger(logging.ERROR, 'Symlink {} appears to be a broken link to {}'
241
+ .format(dest, path))
242
+ if os.lstat(dest) and os.path.islink(dest):
243
+ os.unlink(dest)
244
+ raise exception.SourceNotFound()
245
+
246
+ def pfn2path(self, pfn):
247
+ # obtain path and sanitise from multiple slashes, etc
248
+ path = os.path.normpath(super().pfn2path(pfn))
249
+ self.logger(logging.DEBUG, 'Extracted path: {} from: {}'.format(path, pfn))
250
+ return path