3 A module to manipulate files on EOS or on the local file system. Intended to have the same interface as castortools.py.
5 from __future__
import print_function
12 eos_select =
'/afs/cern.ch/project/eos/installation/cms/bin/eos.select'
15 """Hack to get the CAF scripts on the PYTHONPATH"""
16 caf =
'/afs/cern.ch/cms/caf/python'
18 if caf
not in sys.path:
24 """Run an xrd command.
26 !!! Will, what is happening in case of problem?
27 ??? At some point, should return a list of lines instead of a string."""
31 tokens = cmsIO.splitPFN(
lfnToPFN(lfn))
33 command = [
'xrd', tokens[1], cmd, tokens[2]]
35 runner = cmsIO.cmsFileManip()
37 return runner.runCommand(command)
40 """Run an eos command.
42 !!! Will, when the EOS command fails, it passes silently...
43 I think we should really try and raise an exception in case of problems.
44 should be possible as the return code is provided in the tuple returned by runner."""
48 tokens = cmsIO.splitPFN(pfn)
51 command = [eos_select, cmd]
53 command.append(tokens[2])
54 runner = cmsIO.cmsFileManip()
55 return runner.runCommand(command)
58 """Tests whether this path is a CMS LFN (name starts with /store...)"""
60 return path.startswith(
'/store')
63 """Tests whether this path is a CMS EOS (name starts with /eos...)"""
64 return path.startswith(
'/eos')
or path.startswith(
'root://eoscms.cern.ch//eos/cms')
67 """Converts a EOS PFN to an LFN.
69 Just strip out /eos/cms from path.
70 If this string is not found, return path.
71 ??? Shouldn't we raise an exception instead?"""
72 return path.replace(
'root://eoscms.cern.ch/',
'').
replace(
'/eos/cms',
'')
75 castorToLFN = eosToLFN
78 """Converts an LFN to a PFN. For example:
79 /store/cmst3/user/cbern/CMG/TauPlusX/Run2011A-03Oct2011-v1/AOD/V2/PAT_CMG_V2_4_0/H2TAUTAU_Nov21
81 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
83 This function only checks path, and does not access the storage system.
84 If the path is in /store/cmst3, it assumes that the CMST3 svcClass is to be used.
85 Otherwise, is uses the default one.
87 ??? what is tfcprot? """
89 if path.startswith(
"/store/"):
90 path = path.replace(
"/store/",
"root://eoscms.cern.ch//eos/cms/store/")
91 if path.startswith(
"/pnfs/psi.ch/cms/trivcat/"):
92 path = path.replace(
"/pnfs/psi.ch/cms/trivcat/",
"root://t3se01.psi.ch//")
94 entity = cmsIO.cmsFile( path, tfcProt )
96 pfn =
'%s://%s//%s/' % (entity.protocol,entity.host,entity.path)
99 if tfcProt ==
'rfio' and \
100 entity.path.startswith(
"/eos/cms/")
and \
101 str(entity.stat()).startswith(
"Error 3011: Unable to stat"):
103 pfn.replace(
"/eos/cms",
"/castor/cern.ch/cms")
104 pfn.replace(
"eoscms",
"castorcms")
109 """Converts LFN to EOS.
111 If path is not an LFN in the first place, return path.
112 ??? shouldn't we raise an exception?"""
114 pfn =
'root://eoscms.cern.ch//eos/cms/' + path
115 return pfn.replace(
'//store',
'/store')
120 lfnToCastor = lfnToEOS
123 """Returns True if path is either:
128 root://eoscms.cern.ch//eos/cms/
130 Otherwise, returns False.
132 WARNING!! This function does not check for path existence,
133 and returns true also for plain files.
134 !!! Will, is my summary correct?
136 if os.path.exists( path ):
141 if not path.startswith(
'/eos')
and not path.startswith(
'/store')
and not path.startswith(
'root://eoscms.cern.ch//eos/cms/'):
146 tokens = cmsIO.splitPFN(pfn)
147 return tokens
and tokens[1].lower().startswith(
'eos')
150 isCastorDir = isEOSDir
154 """Returns True if path is a file or directory stored on EOS (checks for path existence)
155 ??? This function does not behave well if passed a non EOS path...
156 returns lots of error messages like:
157 >>> eostools.isEOSFile('/store/asdfasfd')
158 Command (['ls', '/', 's', 't', 'o', 'r', 'e', '/', 'a', 's', 'd', 'f', 'a', 's', 'f', 'd', '/store']) failed with return code: 2
159 ls: s: No such file or directory
160 ls: t: No such file or directory
161 ls: o: No such file or directory
162 ls: r: No such file or directory
163 ls: e: No such file or directory
164 ls: a: No such file or directory
165 ls: s: No such file or directory
166 ls: d: No such file or directory
167 ls: f: No such file or directory
168 ls: a: No such file or directory
169 ls: s: No such file or directory
170 ls: f: No such file or directory
171 ls: d: No such file or directory
172 ls: /store: No such file or directory
174 ls: s: No such file or directory
175 ls: t: No such file or directory
176 ls: o: No such file or directory
177 ls: r: No such file or directory
178 ls: e: No such file or directory
179 ls: a: No such file or directory
180 ls: s: No such file or directory
181 ls: d: No such file or directory
182 ls: f: No such file or directory
183 ls: a: No such file or directory
184 ls: s: No such file or directory
185 ls: f: No such file or directory
186 ls: d: No such file or directory
187 ls: /store: No such file or directory
195 isCastorFile = isEOSFile
199 """Returns true if path is a file or directory stored locally, or on EOS.
201 This function checks for the file or directory existence."""
211 result = os.path.exists(path)
217 '''Returns the size of a directory on EOS in GB.'''
220 output = res[0].
split(
'\n')
224 size +=
float(file.split(
'=')[2])
227 return size/1024/1024/1024
231 """Makes a directory in EOS
233 ???Will, I'm quite worried by the fact that if this path already exists, and is
234 a file, everything will 'work'. But then we have a file, and not a directory,
235 while we expect a dir..."""
246 raise OSError(
'cannot create directory '+ path)
249 createCastorDir = createEOSDir
252 """Create a directory, either on EOS or locally"""
262 """Returns True if path is a directory on EOS.
264 Tests for file existence.
265 This function returns False for EOS files, and crashes with local paths
267 ???Will, this function also seems to work for paths like:
269 ??? I think that it should work also for local files, see isFile."""
272 return 'The directory exists' in out
275 """Returns True if a path is a file.
277 Tests for file existence.
278 Returns False for directories.
279 Works on EOS and local paths.
281 ???This function works with local files, so not the same as isDirectory...
282 isFile and isDirectory should behave the same.
285 if not path.startswith(
'/eos')
and not path.startswith(
'/store'):
286 if( os.path.isfile(path) ):
292 return 'The file exists' in out
295 """Does chmod on a file or directory"""
301 """Provides a list of the specified directory
304 if os.path.isdir( path ):
307 return [
'/'.
join([path,file])
for file
in os.listdir( path )]
313 for root,dirs,files
in os.walk(path):
314 result.extend( [
'/'.
join([root,dir])
for dir
in dirs] )
315 allFiles.extend( [
'/'.
join([root,file])
for file
in files] )
316 result.extend(allFiles)
324 for line
in files.split(
'\n'):
325 tokens = [t
for t
in line.split()
if t]
331 result.append( tokens)
333 result.append( tokens[4] )
337 command = [
'which', cmd]
338 runner = cmsIO.cmsFileManip()
339 out, _, _ = runner.runCommand(command)
341 lines = [line
for line
in out.split(
'\n')
if line]
344 elif len(lines) == 2:
349 def ls(path, rec = False):
350 """Provides a simple list of the specified directory, works on EOS and locally"""
354 """Provides a simple list of the specified directory, works on EOS only, but is faster than the xrd version"""
357 return [
eosToLFN(line)
for line
in stdout.split(
'\n')
if line]
361 return [os.path.join(lfn,line)
for line
in stdout.split(
'\n')
if line]
363 def rm(path, rec=False):
364 """rm, works on EOS and locally.
366 Colin: should implement a -f mode and a confirmation when deleting dirs recursively."""
374 elif os.path.exists(path):
380 raise ValueError(path +
' is not EOS and not local... should not happen!')
383 """Remove a list of files and directories, possibly recursively
385 Colin: Is that obsolete? why not use rm?"""
392 file_list =
ls(path, rec =
True)
393 file_list.append(lfn)
396 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)
402 """cat, works on EOS and locally"""
409 pattern = re.compile(
'cat returned [0-9]+')
410 for line
in out.split(
'\n'):
411 match = pattern.search(line)
412 if line
and match
is not None:
413 lines.append(line.replace(match.group(0),
''))
418 print(out, file=sys.stderr)
419 print(err, file=sys.stderr)
420 allLines =
'\n'.
join(lines)
421 if allLines
and not allLines.endswith(
'\n'):
426 if content
and not content.endswith(
'\n'):
431 """Does a copy of files using xrd.
433 Colin: implement a generic cp interface as done for rm, ls, etc?"""
439 if os.path.exists(src):
442 if os.path.isdir(src):
450 raise ValueError(src +
' does not exist.')
458 tokens = cmsIO.splitPFN(pfn_dest)
459 pfn_dest =
'%s://%s//%s/' % (tokens[0],tokens[1],tokens[2])
460 elif os.path.exists(dest):
466 topDir = src.rstrip(
'/').
split(
'/')[-1]
468 dest =
'/'.
join([dest, topDir])
473 for srcFile
in files:
479 destFile = srcFile.replace( src,
'' )
480 destFile =
'/'.
join([dest,destFile])
481 pfnDestFile = destFile
496 """Copies a single file using xrd."""
499 command.append(pfn_src)
500 command.append(pfn_dest)
504 runner = cmsIO.cmsFileManip()
505 out, err, ret = runner.runCommand(command)
507 print(out, file=sys.stderr)
508 print(err, file=sys.stderr)
512 """Move filename1 to filename2 locally to the same server"""
520 """Return a list of files matching a regexp"""
523 pattern = re.compile( regexp )
527 return [f
for f
in files
if pattern.match(os.path.basename(f))
is not None]
530 pattern = re.compile( regexp )
534 if pattern.match( os.path.basename(f) )
is not None:
539 """Runs cmsStage with LFNs if possible"""
546 command = [
'cmsStage']
550 command.append(
eosToLFN(absDestDir))
552 runner = cmsIO.cmsFileManip()
553 runner.runCommand(command)