11
11
import time
12
12
import threading
13
13
import tempfile
14
+ import hashlib
14
15
15
16
from DIRAC import gLogger , S_OK , S_ERROR
16
17
from DIRAC .Core .DISET .RequestHandler import RequestHandler
24
25
from DIRAC .RequestManagementSystem .Client .Operation import Operation
25
26
from DIRAC .RequestManagementSystem .Client .File import File
26
27
from DIRAC .Resources .Storage .StorageElement import StorageElement
28
+ from DIRAC .Core .Utilities .File import getGlobbedTotalSize
27
29
28
30
29
- class SandboxStoreHandler ( RequestHandler ) :
31
+ class SandboxStoreHandlerMixin :
30
32
__purgeCount = - 1
31
33
__purgeLock = threading .Lock ()
32
34
__purgeWorking = False
@@ -44,10 +46,10 @@ def initializeHandler(cls, serviceInfoDict):
44
46
return S_ERROR (f"Can't connect to DB: { repr (excp )} " )
45
47
return S_OK ()
46
48
47
- def initialize (self ):
49
+ def initializeRequest (self ):
48
50
self .__backend = self .getCSOption ("Backend" , "local" )
49
51
self .__localSEName = self .getCSOption ("LocalSE" , "SandboxSE" )
50
- self .__maxUploadBytes = self .getCSOption ("MaxSandboxSizeMiB" , 10 ) * 1048576
52
+ self ._maxUploadBytes = self .getCSOption ("MaxSandboxSizeMiB" , 10 ) * 1048576
51
53
if self .__backend .lower () == "local" or self .__backend == self .__localSEName :
52
54
self .__useLocalStorage = True
53
55
self .__seNameToUse = self .__localSEName
@@ -75,13 +77,14 @@ def __getSandboxPath(self, md5):
75
77
pathItems .extend ([md5 [0 :3 ], md5 [3 :6 ], md5 ])
76
78
return os .path .join (* pathItems )
77
79
78
- def transfer_fromClient (self , fileId , token , fileSize , fileHelper ):
80
+ def _getFromClient (self , fileId , token , fileSize , fileHelper = None , data = "" ):
79
81
"""
80
82
Receive a file as a sandbox
81
83
"""
82
84
83
- if self .__maxUploadBytes and fileSize > self .__maxUploadBytes :
84
- fileHelper .markAsTransferred ()
85
+ if self ._maxUploadBytes and fileSize > self ._maxUploadBytes :
86
+ if fileHelper :
87
+ fileHelper .markAsTransferred ()
85
88
return S_ERROR ("Sandbox is too big. Please upload it to a grid storage element" )
86
89
87
90
if isinstance (fileId , (list , tuple )):
@@ -113,7 +116,8 @@ def transfer_fromClient(self, fileId, token, fileSize, fileHelper):
113
116
result = self .sandboxDB .getSandboxId (seName , sePFN , credDict ["username" ], credDict ["group" ])
114
117
if result ["OK" ]:
115
118
gLogger .info ("Sandbox already exists. Skipping upload" )
116
- fileHelper .markAsTransferred ()
119
+ if fileHelper :
120
+ fileHelper .markAsTransferred ()
117
121
sbURL = f"SB:{ seName } |{ sePFN } "
118
122
assignTo = {key : [(sbURL , assignTo [key ])] for key in assignTo }
119
123
result = self .export_assignSandboxesToEntities (assignTo )
@@ -126,14 +130,35 @@ def transfer_fromClient(self, fileId, token, fileSize, fileHelper):
126
130
else :
127
131
hdPath = False
128
132
# Write to local file
129
- result = self .__networkToFile (fileHelper , hdPath )
133
+
134
+ if fileHelper :
135
+ result = self .__networkToFile (fileHelper , hdPath )
136
+ elif data :
137
+ hdPath = os .path .realpath (hdPath )
138
+ mkDir (os .path .dirname (hdPath ))
139
+ with open (hdPath , "bw" ) as output :
140
+ output .write (data )
141
+ result = S_OK (hdPath )
142
+ else :
143
+ result = S_ERROR ("No data provided" )
144
+
130
145
if not result ["OK" ]:
131
146
gLogger .error ("Error while receiving sandbox file" , result ["Message" ])
132
147
return result
133
148
hdPath = result ["Value" ]
134
149
gLogger .info ("Wrote sandbox to file" , hdPath )
135
150
# Check hash!
136
- if fileHelper .getHash () != aHash :
151
+ if fileHelper :
152
+ hdHash = fileHelper .getHash ()
153
+ else :
154
+ oMD5 = hashlib .md5 ()
155
+ with open (hdPath , "rb" ) as fd :
156
+ bData = fd .read (10240 )
157
+ while bData :
158
+ oMD5 .update (bData )
159
+ bData = fd .read (10240 )
160
+ hdHash = oMD5 .hexdigest ()
161
+ if hdHash != aHash :
137
162
self .__secureUnlinkFile (hdPath )
138
163
gLogger .error ("Hashes don't match! Client defined hash is different with received data hash!" )
139
164
return S_ERROR ("Hashes don't match!" )
@@ -147,13 +172,14 @@ def transfer_fromClient(self, fileId, token, fileSize, fileHelper):
147
172
sbPath = result ["Value" ][1 ]
148
173
# Register!
149
174
gLogger .info ("Registering sandbox in the DB with" , f"SB:{ self .__seNameToUse } |{ sbPath } " )
175
+ fSize = getGlobbedTotalSize (hdPath )
150
176
result = self .sandboxDB .registerAndGetSandbox (
151
177
credDict ["username" ],
152
178
credDict ["DN" ],
153
179
credDict ["group" ],
154
180
self .__seNameToUse ,
155
181
sbPath ,
156
- fileHelper . getTransferedBytes () ,
182
+ fSize ,
157
183
)
158
184
if not result ["OK" ]:
159
185
self .__secureUnlinkFile (hdPath )
@@ -166,6 +192,13 @@ def transfer_fromClient(self, fileId, token, fileSize, fileHelper):
166
192
return result
167
193
return S_OK (sbURL )
168
194
195
+ def transfer_fromClient (self , fileId , token , fileSize , fileHelper ):
196
+ """
197
+ Receive a file as a sandbox
198
+ """
199
+
200
+ return self ._getFromClient (fileId , token , fileSize , fileHelper = fileHelper )
201
+
169
202
def transfer_bulkFromClient (self , fileId , token , _fileSize , fileHelper ):
170
203
"""Receive files packed into a tar archive by the fileHelper logic.
171
204
token is used for access rights confirmation.
@@ -259,7 +292,7 @@ def __networkToFile(self, fileHelper, destFileName=False):
259
292
fd = tfd
260
293
else :
261
294
fd = open (destFileName , "wb" )
262
- result = fileHelper .networkToDataSink (fd , maxFileSize = self .__maxUploadBytes )
295
+ result = fileHelper .networkToDataSink (fd , maxFileSize = self ._maxUploadBytes )
263
296
fd .close ()
264
297
except Exception as e :
265
298
gLogger .error ("Cannot open to write destination file" , f"{ destFileName } : { repr (e ).replace (',)' , ')' )} " )
@@ -328,7 +361,7 @@ def export_assignSandboxesToEntities(self, enDict, ownerName="", ownerGroup="",
328
361
Expects a dict of { entityId : [ ( SB, SBType ), ... ] }
329
362
"""
330
363
if not entitySetup :
331
- entitySetup = self .serviceInfoDict [ "clientSetup" ]
364
+ entitySetup = self .diracSetup
332
365
credDict = self .getRemoteCredentials ()
333
366
return self .sandboxDB .assignSandboxesToEntities (
334
367
enDict , credDict ["username" ], credDict ["group" ], entitySetup , ownerName , ownerGroup
@@ -344,7 +377,7 @@ def export_unassignEntities(self, entitiesList, entitiesSetup=False):
344
377
Unassign a list of jobs
345
378
"""
346
379
if not entitiesSetup :
347
- entitiesSetup = self .serviceInfoDict [ "clientSetup" ]
380
+ entitiesSetup = self .diracSetup
348
381
credDict = self .getRemoteCredentials ()
349
382
return self .sandboxDB .unassignEntities ({entitiesSetup : entitiesList }, credDict ["username" ], credDict ["group" ])
350
383
@@ -358,7 +391,7 @@ def export_getSandboxesAssignedToEntity(self, entityId, entitySetup=False):
358
391
Get the sandboxes associated to a job and the association type
359
392
"""
360
393
if not entitySetup :
361
- entitySetup = self .serviceInfoDict [ "clientSetup" ]
394
+ entitySetup = self .diracSetup
362
395
credDict = self .getRemoteCredentials ()
363
396
result = self .sandboxDB .getSandboxesAssignedToEntity (
364
397
entityId , entitySetup , credDict ["username" ], credDict ["group" ]
@@ -400,6 +433,10 @@ def transfer_toClient(self, fileID, token, fileHelper):
400
433
fileID is the local file name in the SE.
401
434
token is used for access rights confirmation.
402
435
"""
436
+
437
+ return self ._sendToClient (fileID , token , fileHelper = fileHelper )
438
+
439
+ def _sendToClient (self , fileID , token , fileHelper = None , raw = False ):
403
440
credDict = self .getRemoteCredentials ()
404
441
serviceURL = self .serviceInfoDict ["URL" ]
405
442
filePath = fileID .replace (serviceURL , "" )
@@ -412,13 +449,20 @@ def transfer_toClient(self, fileID, token, fileHelper):
412
449
hdPath = self .__sbToHDPath (filePath )
413
450
if not os .path .isfile (hdPath ):
414
451
return S_ERROR ("Sandbox does not exist" )
415
- result = fileHelper .getFileDescriptor (hdPath , "rb" )
416
- if not result ["OK" ]:
417
- return S_ERROR (f"Failed to get file descriptor: { result ['Message' ]} " )
418
- fd = result ["Value" ]
419
- result = fileHelper .FDToNetwork (fd )
420
- fileHelper .oFile .close ()
421
- return result
452
+
453
+ if fileHelper :
454
+ result = fileHelper .getFileDescriptor (hdPath , "rb" )
455
+ if not result ["OK" ]:
456
+ return result
457
+ fd = result ["Value" ]
458
+ result = fileHelper .FDToNetwork (fd )
459
+ fileHelper .oFile .close ()
460
+ return result
461
+
462
+ with open (hdPath , "rb" ) as fd :
463
+ if raw :
464
+ return fd .read ()
465
+ return S_OK (fd .read ())
422
466
423
467
##################
424
468
# Purge sandboxes
@@ -531,3 +575,10 @@ def __deleteSandboxFromExternalBackend(self, SEName, SEPFN):
531
575
except Exception :
532
576
gLogger .exception ("RM raised an exception while trying to delete a remote sandbox" )
533
577
return S_ERROR ("RM raised an exception while trying to delete a remote sandbox" )
578
+
579
+
580
+ class SandboxStoreHandler (SandboxStoreHandlerMixin , RequestHandler ):
581
+ def initialize (self ):
582
+ # we need it still in 8.0
583
+ self .diracSetup = self .serviceInfoDict ["clientSetup" ]
584
+ return self .initializeRequest ()
0 commit comments