3 A module to manipulate files on EOS or on the local file system. Intended to have the same interface as castortools.py. 11 eos_select =
'/afs/cern.ch/project/eos/installation/cms/bin/eos.select' 14 """Hack to get the CAF scripts on the PYTHONPATH""" 15 caf =
'/afs/cern.ch/cms/caf/python' 17 if caf
not in sys.path:
23 """Run an xrd command. 25 !!! Will, what is happening in case of problem? 26 ??? At some point, should return a list of lines instead of a string.""" 30 tokens = cmsIO.splitPFN(
lfnToPFN(lfn))
32 command = [
'xrd', tokens[1], cmd, tokens[2]]
34 runner = cmsIO.cmsFileManip()
36 return runner.runCommand(command)
39 """Run an eos command. 41 !!! Will, when the EOS command fails, it passes silently... 42 I think we should really try and raise an exception in case of problems. 43 should be possible as the return code is provided in the tuple returned by runner.""" 47 tokens = cmsIO.splitPFN(pfn)
50 command = [eos_select, cmd]
52 command.append(tokens[2])
53 runner = cmsIO.cmsFileManip()
54 return runner.runCommand(command)
57 """Tests whether this path is a CMS LFN (name starts with /store...)""" 59 return path.startswith(
'/store')
62 """Tests whether this path is a CMS EOS (name starts with /eos...)""" 63 return path.startswith(
'/eos')
or path.startswith(
'root://eoscms.cern.ch//eos/cms')
66 """Converts a EOS PFN to an LFN. 68 Just strip out /eos/cms from path. 69 If this string is not found, return path. 70 ??? Shouldn't we raise an exception instead?""" 71 return path.replace(
'root://eoscms.cern.ch/',
'').
replace(
'/eos/cms',
'')
74 castorToLFN = eosToLFN
77 """Converts an LFN to a PFN. For example: 78 /store/cmst3/user/cbern/CMG/TauPlusX/Run2011A-03Oct2011-v1/AOD/V2/PAT_CMG_V2_4_0/H2TAUTAU_Nov21 80 root://eoscms//eos/cms/store/cmst3/user/cbern/CMG/TauPlusX/Run2011A-03Oct2011-v1/AOD/V2/PAT_CMG_V2_4_0/H2TAUTAU_Nov21?svcClass=cmst3&stageHost=castorcms 82 This function only checks path, and does not access the storage system. 83 If the path is in /store/cmst3, it assumes that the CMST3 svcClass is to be used. 84 Otherwise, is uses the default one. 86 ??? what is tfcprot? """ 88 if path.startswith(
"/store/"):
89 path = path.replace(
"/store/",
"root://eoscms.cern.ch//eos/cms/store/")
90 if path.startswith(
"/pnfs/psi.ch/cms/trivcat/"):
91 path = path.replace(
"/pnfs/psi.ch/cms/trivcat/",
"root://t3se01.psi.ch//")
93 entity = cmsIO.cmsFile( path, tfcProt )
95 pfn =
'%s://%s//%s/' % (entity.protocol,entity.host,entity.path)
98 if tfcProt ==
'rfio' and \
99 entity.path.startswith(
"/eos/cms/")
and \
100 str(entity.stat()).startswith(
"Error 3011: Unable to stat"):
102 pfn.replace(
"/eos/cms",
"/castor/cern.ch/cms")
103 pfn.replace(
"eoscms",
"castorcms")
108 """Converts LFN to EOS. 110 If path is not an LFN in the first place, return path. 111 ??? shouldn't we raise an exception?""" 113 pfn =
'root://eoscms.cern.ch//eos/cms/' + path
114 return pfn.replace(
'//store',
'/store')
119 lfnToCastor = lfnToEOS
122 """Returns True if path is either: 127 root://eoscms.cern.ch//eos/cms/ 129 Otherwise, returns False. 131 WARNING!! This function does not check for path existence, 132 and returns true also for plain files. 133 !!! Will, is my summary correct? 135 if os.path.exists( path ):
140 if not path.startswith(
'/eos')
and not path.startswith(
'/store')
and not path.startswith(
'root://eoscms.cern.ch//eos/cms/'):
145 tokens = cmsIO.splitPFN(pfn)
146 return tokens
and tokens[1].lower().startswith(
'eos')
149 isCastorDir = isEOSDir
153 """Returns True if path is a file or directory stored on EOS (checks for path existence) 154 ??? This function does not behave well if passed a non EOS path... 155 returns lots of error messages like: 156 >>> eostools.isEOSFile('/store/asdfasfd') 157 Command (['ls', '/', 's', 't', 'o', 'r', 'e', '/', 'a', 's', 'd', 'f', 'a', 's', 'f', 'd', '/store']) failed with return code: 2
158 ls: s: No such file or directory
159 ls: t: No such file or directory
160 ls: o: No such file or directory
161 ls: r: No such file or directory
162 ls: e: No such file or directory
163 ls: a: No such file or directory
164 ls: s: No such file or directory
165 ls: d: No such file or directory
166 ls: f: No such file or directory
167 ls: a: No such file or directory
168 ls: s: No such file or directory
169 ls: f: No such file or directory
170 ls: d: No such file or directory
171 ls: /store: No such file or directory
173 ls: s: No such file or directory
174 ls: t: No such file or directory
175 ls: o: No such file or directory
176 ls: r: No such file or directory
177 ls: e: No such file or directory
178 ls: a: No such file or directory
179 ls: s: No such file or directory
180 ls: d: No such file or directory
181 ls: f: No such file or directory
182 ls: a: No such file or directory
183 ls: s: No such file or directory
184 ls: f: No such file or directory
185 ls: d: No such file or directory
186 ls: /store: No such file or directory
190 _, _, ret = runEOSCommand( path, 'ls')
193 #also define an alias for backwards compatibility
194 isCastorFile = isEOSFile
197 def fileExists( path ):
198 """Returns true if path is a file or directory stored locally, or on EOS.
200 This function checks for the file or directory existence."""
206 result = isEOSFile(path)
208 # print 'not eos', path
210 result = os.path.exists(path)
215 def eosDirSize(path):
216 '''Returns the size of a directory on EOS in GB.'''
218 res = runEOSCommand(lfn, 'find', '--size')
219 output = res[0].split('\n')
223 size += float(file.split('=')[2])
226 return size/1024/1024/1024
229 def createEOSDir( path ):
230 """Makes a directory in EOS
232 ???Will, I'm quite worried by the fact that if this path already exists, and is
233 a file, everything will 'work'. But then we have a file, and not a directory,
234 while we expect a dir..."""
236 if not isEOSFile(lfn):
237 # if not isDirectory(lfn):
238 runEOSCommand(lfn,'mkdir','-p')
239 # entity = cmsIO.cmsFile( lfn,"stageout")
241 # # print 'created ', path
242 if isDirectory(path):
245 raise OSError('cannot create directory '+ path)
247 #also define an alias for backwards compatibility
248 createCastorDir = createEOSDir
251 """Create a directory, either on EOS or locally"""
252 # print 'mkdir', path
253 if isEOS( path ) or isLFN(path):
256 # recursive directory creation (like mkdir -p)
260 def isDirectory(path):
261 """Returns True if path is a directory on EOS.
263 Tests for file existence.
264 This function returns False for EOS files, and crashes with local paths
266 ???Will, this function also seems to work for paths like:
268 ??? I think that it should work also for local files, see isFile."""
270 out, _, _ = runXRDCommand(path,'existdir')
271 return 'The directory exists' in out
274 """Returns True if a path is a file.
276 Tests for file existence.
277 Returns False for directories.
278 Works on EOS and local paths.
280 ???This function works with local files, so not the same as isDirectory...
281 isFile and isDirectory should behave the same.
284 if not path.startswith('/eos') and not path.startswith('/store'):
285 if( os.path.isfile(path) ):
290 out, _, _ = runXRDCommand(path,'existfile')
291 return 'The file exists' in out
293 def chmod(path, mode):
294 """Does chmod on a file or directory"""
296 return runEOSCommand(path, 'chmod', '-r', str(mode))
299 def listFiles(path, rec = False, full_info = False):
300 """Provides a list of the specified directory
302 # -- listing on the local filesystem --
303 if os.path.isdir( path ):
306 return [ '/'.join([path,file]) for file in os.listdir( path )]
308 # recursive, directories are put in the list first,
309 # followed by the list of all files in the directory tree
312 for root,dirs,files in os.walk(path):
313 result.extend( [ '/'.join([root,dir]) for dir in dirs] )
314 allFiles.extend( [ '/'.join([root,file]) for file in files] )
315 result.extend(allFiles)
317 # -- listing on EOS --
321 files, _, _ = runXRDCommand(path, cmd)
323 for line in files.split('\n'):
324 tokens = [t for t in line.split() if t]
327 # result.append(tuple(tokens))
328 #COLIN need same interface for eos and local fs
330 result.append( tokens)
332 result.append( tokens[4] )
336 command = ['which', cmd]
337 runner = cmsIO.cmsFileManip()
338 out, _, _ = runner.runCommand(command)
340 lines = [line for line in out.split('\n') if line]
343 elif len(lines) == 2:
348 def ls(path, rec = False):
349 """Provides a simple list of the specified directory, works on EOS and locally"""
350 return [eosToLFN(t) for t in listFiles(path, rec)]
352 def ls_EOS(path, rec = False):
353 """Provides a simple list of the specified directory, works on EOS only, but is faster than the xrd version"""
355 stdout, _, ret = runEOSCommand(path,'find','-f')
356 return [eosToLFN(line) for line in stdout.split('\n') if line]
358 stdout, _, ret = runEOSCommand(path,'ls')
360 return [os.path.join(lfn,line) for line in stdout.split('\n') if line]
362 def rm(path, rec=False):
363 """rm, works on EOS and locally.
365 Colin: should implement a -f mode and a confirmation when deleting dirs recursively."""
367 path = lfnToEOS(path)
370 runEOSCommand(path, 'rm', '-r')
372 runEOSCommand(path,'rm')
373 elif os.path.exists(path):
379 raise ValueError(path + ' is not EOS and not local... should not happen!')
381 def remove( files, rec = False):
382 """Remove a list of files and directories, possibly recursively
384 Colin: Is that obsolete? why not use rm?"""
390 #this should be used with care
391 file_list = ls(path, rec = True)
392 file_list.append(lfn)
394 #order the files in depth order - i.e. remove the deepest files first
395 files_rec = sorted([(len([ff for ff in f.split('/') if ff]), f) for f in file_list if f and f.startswith(lfn)], reverse = True)
401 """cat, works on EOS and locally"""
402 path = lfnToEOS(path)
404 #print "the file to cat is:", path
405 out, err, _ = runXRDCommand(path,'cat')
408 pattern = re.compile('cat returned [0-9]+')
409 for line in out.split('\n'):
410 match = pattern.search(line)
411 if line and match is not None:
412 lines.append(line.replace(match.group(0),''))
417 print >> sys.stderr, out
418 print >> sys.stderr, err
419 allLines = '\n'.join(lines)
420 if allLines and not allLines.endswith('\n'):
424 content = file(path).read()
425 if content and not content.endswith('\n'):
429 def xrdcp(src, dest):
430 """Does a copy of files using xrd.
432 Colin: implement a generic cp interface as done for rm, ls, etc?"""
438 if os.path.exists(src):
441 if os.path.isdir(src):
443 elif fileExists(src):
445 pfn_src = lfnToPFN(src)
449 raise ValueError(src + ' does not exist.')
454 dest = eosToLFN(dest)
455 pfn_dest = lfnToPFN(dest)
456 if isDirectory(dest):
457 tokens = cmsIO.splitPFN(pfn_dest)
458 pfn_dest = '%s://%s//%s/' % (tokens[0],tokens[1],tokens[2])
459 elif os.path.exists(dest):
465 topDir = src.rstrip('/').split('/')[-1]
467 dest = '/'.join([dest, topDir])
468 # print 'mkdir ' + dest
470 files = listFiles(src, rec=True)
471 # pprint.pprint( [file[4] for file in files] )
472 for srcFile in files:
475 if isEOSDir(srcFile):
476 srcFile = eosToLFN(srcFile)
477 pfnSrcFile = lfnToPFN(srcFile)
478 destFile = srcFile.replace( src, '' )
479 destFile = '/'.join([dest,destFile])
480 pfnDestFile = destFile
481 if isEOSDir(destFile):
482 lfnDestFile = eosToLFN(destFile)
483 pfnDestFile = lfnToPFN(lfnDestFile)
484 # print 'srcFile', pfnSrcFile
485 # print 'destFile', pfnDestFile
487 _xrdcpSingleFile( pfnSrcFile, pfnDestFile )
491 _xrdcpSingleFile( pfn_src, pfn_dest )
494 def _xrdcpSingleFile( pfn_src, pfn_dest):
495 """Copies a single file using xrd."""
498 command.append(pfn_src)
499 command.append(pfn_dest)
500 # print ' '.join(command)
503 runner = cmsIO.cmsFileManip()
504 out, err, ret = runner.runCommand(command)
506 print >> sys.stderr, out
507 print >> sys.stderr, err
511 """Move filename1 to filename2 locally to the same server"""
514 dest = eosToLFN(dest)
516 runXRDCommand(src,'mv', lfnToEOS(dest))
518 def matchingFiles( path, regexp):
519 """Return a list of files matching a regexp"""
522 pattern = re.compile( regexp )
523 #files = ls_EOS(path)
526 return [f for f in files if pattern.match(os.path.basename(f)) is not None]
528 def datasetNotEmpty( path, regexp ):
529 pattern = re.compile( regexp )
533 if pattern.match( os.path.basename(f) ) is not None:
537 def cmsStage( absDestDir, files, force):
538 """Runs cmsStage with LFNs if possible"""
540 destIsEOSDir = isEOSDir(absDestDir)
542 createEOSDir( absDestDir )
545 command = ['cmsStage']
548 command.append(eosToLFN(fname))
549 command.append(eosToLFN(absDestDir))
550 print ' '.join(command)
551 runner = cmsIO.cmsFileManip()
552 runner.runCommand(command)
def replace(string, replacements)