3 A module to manipulate files on EOS or on the local file system. Intended to have the same interface as castortools.py.
12 """Hack to get the CAF scripts on the PYTHONPATH"""
13 caf =
'/afs/cern.ch/cms/caf/python'
15 if caf
not in sys.path:
21 """Run an xrd command.
23 !!! Will, what is happening in case of problem?
24 ??? At some point, should return a list of lines instead of a string."""
28 tokens = cmsIO.splitPFN(
lfnToPFN(lfn))
30 command = [
'xrd', tokens[1], cmd, tokens[2]]
32 runner = cmsIO.cmsFileManip()
34 return runner.runCommand(command)
37 """Run an eos command.
39 !!! Will, when the EOS command fails, it passes silently...
40 I think we should really try and raise an exception in case of problems.
41 should be possible as the return code is provided in the tuple returned by runner."""
45 tokens = cmsIO.splitPFN(pfn)
48 command = [
'/afs/cern.ch/project/eos/installation/pro/bin/eos.select', cmd]
50 command.append(tokens[2])
51 runner = cmsIO.cmsFileManip()
52 return runner.runCommand(command)
55 """Tests whether this path is a CMS LFN (name starts with /store...)"""
57 return path.startswith(
'/store')
60 """Tests whether this path is a CMS EOS (name starts with /eos...)"""
61 return path.startswith(
'/eos')
or path.startswith(
'root://eoscms.cern.ch//eos/cms')
64 """Converts a EOS PFN to an LFN.
66 Just strip out /eos/cms from path.
67 If this string is not found, return path.
68 ??? Shouldn't we raise an exception instead?"""
69 return path.replace(
'root://eoscms.cern.ch/',
'').
replace(
'/eos/cms',
'')
72 castorToLFN = eosToLFN
75 """Converts an LFN to a PFN. For example:
76 /store/cmst3/user/cbern/CMG/TauPlusX/Run2011A-03Oct2011-v1/AOD/V2/PAT_CMG_V2_4_0/H2TAUTAU_Nov21
78 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
80 This function only checks path, and does not access the storage system.
81 If the path is in /store/cmst3, it assumes that the CMST3 svcClass is to be used.
82 Otherwise, is uses the default one.
84 ??? what is tfcprot? """
86 if path.startswith(
"/store/"):
87 path = path.replace(
"/store/",
"root://eoscms.cern.ch//eos/cms/store/")
88 if path.startswith(
"/pnfs/psi.ch/cms/trivcat/"):
89 path = path.replace(
"/pnfs/psi.ch/cms/trivcat/",
"root://t3se01.psi.ch//")
91 entity = cmsIO.cmsFile( path, tfcProt )
93 pfn =
'%s://%s//%s/' % (entity.protocol,entity.host,entity.path)
96 if tfcProt ==
'rfio' and \
97 entity.path.startswith(
"/eos/cms/")
and \
98 str(entity.stat()).startswith(
"Error 3011: Unable to stat"):
100 pfn.replace(
"/eos/cms",
"/castor/cern.ch/cms")
101 pfn.replace(
"eoscms",
"castorcms")
106 """Converts LFN to EOS.
108 If path is not an LFN in the first place, return path.
109 ??? shouldn't we raise an exception?"""
111 pfn =
'root://eoscms.cern.ch//eos/cms/' + path
112 return pfn.replace(
'//store',
'/store')
117 lfnToCastor = lfnToEOS
120 """Returns True if path is either:
125 root://eoscms.cern.ch//eos/cms/
127 Otherwise, returns False.
129 WARNING!! This function does not check for path existence,
130 and returns true also for plain files.
131 !!! Will, is my summary correct?
133 if os.path.exists( path ):
138 if not path.startswith(
'/eos')
and not path.startswith(
'/store')
and not path.startswith(
'root://eoscms.cern.ch//eos/cms/'):
143 tokens = cmsIO.splitPFN(pfn)
144 return tokens
and tokens[1].lower().startswith(
'eos')
147 isCastorDir = isEOSDir
151 """Returns True if path is a file or directory stored on EOS (checks for path existence)
152 ??? This function does not behave well if passed a non EOS path...
153 returns lots of error messages like:
154 >>> eostools.isEOSFile('/store/asdfasfd')
155 Command (['ls', '/', 's', 't', 'o', 'r', 'e', '/', 'a', 's', 'd', 'f', 'a', 's', 'f', 'd', '/store']) failed with return code: 2
156 ls: s: No such file or directory
157 ls: t: No such file or directory
158 ls: o: No such file or directory
159 ls: r: No such file or directory
160 ls: e: No such file or directory
161 ls: a: No such file or directory
162 ls: s: No such file or directory
163 ls: d: No such file or directory
164 ls: f: No such file or directory
165 ls: a: No such file or directory
166 ls: s: No such file or directory
167 ls: f: No such file or directory
168 ls: d: No such file or directory
169 ls: /store: No such file or directory
171 ls: s: No such file or directory
172 ls: t: No such file or directory
173 ls: o: No such file or directory
174 ls: r: No such file or directory
175 ls: e: No such file or directory
176 ls: a: No such file or directory
177 ls: s: No such file or directory
178 ls: d: No such file or directory
179 ls: f: No such file or directory
180 ls: a: No such file or directory
181 ls: s: No such file or directory
182 ls: f: No such file or directory
183 ls: d: No such file or directory
184 ls: /store: No such file or directory
188 _, _, ret = runEOSCommand( path, 'ls')
191 #also define an alias for backwards compatibility
192 isCastorFile = isEOSFile
195 def fileExists( path ):
196 """Returns true if path is a file or directory stored locally, or on EOS.
198 This function checks for the file or directory existence."""
204 result = isEOSFile(path)
206 # print 'not eos', path
208 result = os.path.exists(path)
213 def eosDirSize(path):
214 '''Returns the size of a directory on EOS in GB.'''
216 res = runEOSCommand(lfn, 'find', '--size')
217 output = res[0].split('\n')
221 size += float(file.split('=')[2])
224 return size/1024/1024/1024
227 def createEOSDir( path ):
228 """Makes a directory in EOS
230 ???Will, I'm quite worried by the fact that if this path already exists, and is
231 a file, everything will 'work'. But then we have a file, and not a directory,
232 while we expect a dir..."""
234 if not isEOSFile(lfn):
235 # if not isDirectory(lfn):
236 runEOSCommand(lfn,'mkdir','-p')
237 # entity = cmsIO.cmsFile( lfn,"stageout")
239 # # print 'created ', path
240 if isDirectory(path):
243 raise OSError('cannot create directory '+ path)
245 #also define an alias for backwards compatibility
246 createCastorDir = createEOSDir
249 """Create a directory, either on EOS or locally"""
250 # print 'mkdir', path
251 if isEOS( path ) or isLFN(path):
254 # recursive directory creation (like mkdir -p)
258 def isDirectory(path):
259 """Returns True if path is a directory on EOS.
261 Tests for file existence.
262 This function returns False for EOS files, and crashes with local paths
264 ???Will, this function also seems to work for paths like:
266 ??? I think that it should work also for local files, see isFile."""
268 out, _, _ = runXRDCommand(path,'existdir')
269 return 'The directory exists' in out
272 """Returns True if a path is a file.
274 Tests for file existence.
275 Returns False for directories.
276 Works on EOS and local paths.
278 ???This function works with local files, so not the same as isDirectory...
279 isFile and isDirectory should behave the same.
282 if not path.startswith('/eos') and not path.startswith('/store'):
283 if( os.path.isfile(path) ):
288 out, _, _ = runXRDCommand(path,'existfile')
289 return 'The file exists' in out
291 def chmod(path, mode):
292 """Does chmod on a file or directory"""
294 return runEOSCommand(path, 'chmod', '-r', str(mode))
297 def listFiles(path, rec = False, full_info = False):
298 """Provides a list of the specified directory
300 # -- listing on the local filesystem --
301 if os.path.isdir( path ):
304 return [ '/'.join([path,file]) for file in os.listdir( path )]
306 # recursive, directories are put in the list first,
307 # followed by the list of all files in the directory tree
310 for root,dirs,files in os.walk(path):
311 result.extend( [ '/'.join([root,dir]) for dir in dirs] )
312 allFiles.extend( [ '/'.join([root,file]) for file in files] )
313 result.extend(allFiles)
315 # -- listing on EOS --
319 files, _, _ = runXRDCommand(path, cmd)
321 for line in files.split('\n'):
322 tokens = [t for t in line.split() if t]
325 # result.append(tuple(tokens))
326 #COLIN need same interface for eos and local fs
328 result.append( tokens)
330 result.append( tokens[4] )
334 command = ['which', cmd]
335 runner = cmsIO.cmsFileManip()
336 out, _, _ = runner.runCommand(command)
338 lines = [line for line in out.split('\n') if line]
341 elif len(lines) == 2:
346 def ls(path, rec = False):
347 """Provides a simple list of the specified directory, works on EOS and locally"""
348 return [eosToLFN(t) for t in listFiles(path, rec)]
350 def ls_EOS(path, rec = False):
351 """Provides a simple list of the specified directory, works on EOS only, but is faster than the xrd version"""
353 stdout, _, ret = runEOSCommand(path,'find','-f')
354 return [eosToLFN(line) for line in stdout.split('\n') if line]
356 stdout, _, ret = runEOSCommand(path,'ls')
358 return [os.path.join(lfn,line) for line in stdout.split('\n') if line]
360 def rm(path, rec=False):
361 """rm, works on EOS and locally.
363 Colin: should implement a -f mode and a confirmation when deleting dirs recursively."""
365 path = lfnToEOS(path)
368 runEOSCommand(path, 'rm', '-r')
370 runEOSCommand(path,'rm')
371 elif os.path.exists(path):
377 raise ValueError(path + ' is not EOS and not local... should not happen!')
379 def remove( files, rec = False):
380 """Remove a list of files and directories, possibly recursively
382 Colin: Is that obsolete? why not use rm?"""
388 #this should be used with care
389 file_list = ls(path, rec = True)
390 file_list.append(lfn)
392 #order the files in depth order - i.e. remove the deepest files first
393 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)
399 """cat, works on EOS and locally"""
400 path = lfnToEOS(path)
402 #print "the file to cat is:", path
403 out, err, _ = runXRDCommand(path,'cat')
406 pattern = re.compile('cat returned [0-9]+')
407 for line in out.split('\n'):
408 match = pattern.search(line)
409 if line and match is not None:
410 lines.append(line.replace(match.group(0),''))
415 print >> sys.stderr, out
416 print >> sys.stderr, err
417 allLines = '\n'.join(lines)
418 if allLines and not allLines.endswith('\n'):
422 content = file(path).read()
423 if content and not content.endswith('\n'):
427 def xrdcp(src, dest):
428 """Does a copy of files using xrd.
430 Colin: implement a generic cp interface as done for rm, ls, etc?"""
436 if os.path.exists(src):
439 if os.path.isdir(src):
441 elif fileExists(src):
443 pfn_src = lfnToPFN(src)
447 raise ValueError(src + ' does not exist.')
452 dest = eosToLFN(dest)
453 pfn_dest = lfnToPFN(dest)
454 if isDirectory(dest):
455 tokens = cmsIO.splitPFN(pfn_dest)
456 pfn_dest = '%s://%s//%s/' % (tokens[0],tokens[1],tokens[2])
457 elif os.path.exists(dest):
463 topDir = src.rstrip('/').split('/')[-1]
465 dest = '/'.join([dest, topDir])
466 # print 'mkdir ' + dest
468 files = listFiles(src, rec=True)
469 # pprint.pprint( [file[4] for file in files] )
470 for srcFile in files:
473 if isEOSDir(srcFile):
474 srcFile = eosToLFN(srcFile)
475 pfnSrcFile = lfnToPFN(srcFile)
476 destFile = srcFile.replace( src, '' )
477 destFile = '/'.join([dest,destFile])
478 pfnDestFile = destFile
479 if isEOSDir(destFile):
480 lfnDestFile = eosToLFN(destFile)
481 pfnDestFile = lfnToPFN(lfnDestFile)
482 # print 'srcFile', pfnSrcFile
483 # print 'destFile', pfnDestFile
485 _xrdcpSingleFile( pfnSrcFile, pfnDestFile )
489 _xrdcpSingleFile( pfn_src, pfn_dest )
492 def _xrdcpSingleFile( pfn_src, pfn_dest):
493 """Copies a single file using xrd."""
496 command.append(pfn_src)
497 command.append(pfn_dest)
498 # print ' '.join(command)
501 runner = cmsIO.cmsFileManip()
502 out, err, ret = runner.runCommand(command)
504 print >> sys.stderr, out
505 print >> sys.stderr, err
509 """Move filename1 to filename2 locally to the same server"""
512 dest = eosToLFN(dest)
514 runXRDCommand(src,'mv', lfnToEOS(dest))
516 def matchingFiles( path, regexp):
517 """Return a list of files matching a regexp"""
520 pattern = re.compile( regexp )
521 #files = ls_EOS(path)
524 return [f for f in files if pattern.match(os.path.basename(f)) is not None]
526 def datasetNotEmpty( path, regexp ):
527 pattern = re.compile( regexp )
531 if pattern.match( os.path.basename(f) ) is not None:
535 def cmsStage( absDestDir, files, force):
536 """Runs cmsStage with LFNs if possible"""
538 destIsEOSDir = isEOSDir(absDestDir)
540 createEOSDir( absDestDir )
543 command = ['cmsStage']
546 command.append(eosToLFN(fname))
547 command.append(eosToLFN(absDestDir))
548 print ' '.join(command)
549 runner = cmsIO.cmsFileManip()
550 runner.runCommand(command)