All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
larbatch_posix.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 ######################################################################
3 #
4 # Name: larbatch_posix.py
5 #
6 # Purpose: Python module containing posix-like interfaces for
7 # accessing files in dCache (/pnfs/...) that do not
8 # require the /pnfs filesystem to be nfs-mounted.
9 #
10 # Created: 8-Dec-2016 Herbert Greenlee
11 #
12 # Classes:
13 #
14 # dcache_file - File-like class for dCache files. This class operates
15 # on a local copy of the file, which is transferred
16 # to/from dCache when the file is closed or opened using
17 # ifdh. For compatibility, this class can be used for
18 # non-dCache files, in which case calls are simply
19 # passed to standard python File calls, and there is no
20 # initial or final file transfer. This class implements
21 # the following methods.
22 #
23 # - close
24 # - flush
25 # - fileno
26 # - next
27 # - read
28 # - readline
29 # - readlines
30 # - seek
31 # - tell
32 # - truncate
33 # - write
34 # - writelines
35 #
36 # Functions:
37 #
38 # The following posix-like global functions are provided by this
39 # module. These mostly correspond to functions having the same name
40 # in the os or shutil modules. Typically, these functions handle paths
41 # corresponding to dCache files (/pnfs/...) in some special way. The
42 # /pnfs filesystem does not have to be nfs-mounted. Non-dCache paths
43 # are simply passed to corresponding standard python functions.
44 #
45 # open - Similar as built-in python function open. Returns a
46 # file-like object of type dcache_file (in case of dCache
47 # paths) or built-in type File (in case of regular files).
48 # readlines - Open in read mode using this module and call readlines().
49 # copy - Similar as shutil.copy. Copy file.
50 # listdir - Similar as os.listdir. List directory contents.
51 # exists - Similar as os.path.exists. Return True if path exists.
52 # isdir - Simmilar as os.path.isdir. Return True if path is a directory.
53 # stat - Similar as os.stat. Return status information about a file.
54 # access - Similar as os.access. Test file access.
55 # walk - Similar as os.walk. Walk directory tree.
56 # mkdir - Similar as os.mkdir. Make directory.
57 # makedirs - Similar as os.makedirs. Make directory and parent directories.
58 # rename - Similar as os.rename. Rename file.
59 # remove - Similar as os.remove. Delete file.
60 # rmdir - Similar as os.rmdir. Delete empty directory.
61 # rmtree - Similar as shutil.rmtree. Delete a directory tree.
62 # chmod - Similar as os.chmod. Change file permissions.
63 # symlink - Similar as os.symlink. Make a symbolic link.
64 # readlink - Similar as os.readlink. Read a symbolic link.
65 # root_stream - Convert path to streamable path or uri.
66 #
67 # Utility functions:
68 #
69 # use_grid - Force (or not) the use of grid tools when possible.
70 #
71 # Notes on the use of grid tools.
72 #
73 # 1. The functions in this module may process calls using either
74 # standard python posix tools or grid tools. In general, there
75 # are three ways that any request might be handled.
76 #
77 # a) Process requrest using standard posix tools.
78 #
79 # b) Process request using posix tools, but with some extra
80 # protections, like timeouts.
81 #
82 # c) Process request using grid tools.
83 #
84 # 2. The following rules are followed in deciding which of the above
85 # ways of handling requests is used.
86 #
87 # a) Non-dCache paths (that is paths that do not start with
88 # "/pnfs/", including all relative paths) are always handled
89 # using standard posix tools.
90 #
91 # b) Absolute paths that start with "/pnfs/" may be handled by any
92 # of the methods listed in item 1, depending on the enviromnent
93 # and configuration.
94 #
95 # c) If the /pnfs filesystem is not nfs-mounted, then paths that
96 # start with "/pnfs/" are always handled using grid tools.
97 #
98 # d) If the /pnfs filesystem is nfs-mounted, the functions in this
99 # module may be configured to choose which set of tools to use
100 # according to the following rules.
101 #
102 # 1) By default, this module prefers posix tools.
103 #
104 # 2) The preference for grid or posix tools may be set by
105 # calling function use_grid.
106 #
107 # 3) A preference for grid tools may be set by setting
108 # environment variable LARBATCH_GRID.
109 #
110 #
111 # 3. Environment variables:
112 #
113 # a) If LARBATCH_DEBUG is defined, then a message is printed for
114 # function call in this module which informs which method is
115 # used to process it.
116 #
117 # b) If LARBATCH_GRID is defined, then this module will be
118 # configured to prefer grid tools.
119 #
120 #
121 ######################################################################
122 
123 from __future__ import absolute_import
124 from __future__ import print_function
125 import os, shutil
126 import stat as statmod
127 import subprocess
128 import threading
129 try:
130  import queue
131 except ImportError:
132  import Queue as queue
133 import uuid
134 import larbatch_utilities
135 from larbatch_utilities import convert_str
136 from project_modules.ifdherror import IFDHError
137 
138 # Global flags.
139 
140 pnfs_is_mounted = os.path.isdir('/pnfs')
141 prefer_grid = 'LARBATCH_GRID' in os.environ
142 debug = 'LARBATCH_DEBUG' in os.environ
143 if debug:
144  print('*** Larbatch_posix: Debugging enabled.')
145 
146 
147 
148 # Force grid function.
149 
150 def use_grid(force=True):
151  prefer_grid = force
152 
153 
154 # File-like class for dCache files.
155 
157 
158  # Default constructor.
159 
160  def __init__(self):
161  self.path = '' # Path of file.
162  self.mode = '' # File mode.
163  self.local_path = '' # Path of local copy of file.
164  self.local_file = None # Open File object of local copy of file.
165 
166  # Initializing constructor.
167 
168  def __init__(self, path, mode='r', buf=-1):
169 
170  self.path = path
171  self.mode = mode
172  if path.startswith('/pnfs/'):
173  self.local_path = str(uuid.uuid4()) + os.path.basename(path)
174  else:
175  self.local_path = path
176 
177  # Fetch copy of file from dCache, if necessary.
178 
179  if path != self.local_path and (mode.find('r') >= 0 or mode.find('a') >= 0):
180  larbatch_utilities.ifdh_cp(path, self.local_path)
181 
182  # Open local copy of file.
183 
184  self.local_file = __builtins__['open'](self.local_path, mode, buf)
185 
186  # Destructor.
187 
188  def __del__(self):
189  self.close()
190 
191  # Close file.
192 
193  def close(self):
194 
195  if self.local_file and not self.local_file.closed:
196 
197  # Close local copy of file.
198 
199  self.local_file.close()
200 
201  # If the local path and real path are different, do some cleanups.
202 
203  if self.path != self.local_path:
204 
205  # If file was opend for writing, transfer local copy to dCache.
206 
207  if self.mode.find('w') >= 0 or self.mode.find('a') >= 0 or self.mode.find('+') >= 0:
208  larbatch_utilities.ifdh_cp(self.local_path, self.path)
209 
210  # Delete the local copy regardless of whether the file was open for
211  # reading or writing.
212 
213  os.remove(self.local_path)
214 
215  # Flush file.
216 
217  def flush(self):
218  self.local_file.flush()
219 
220  # File descriptor.
221 
222  def fileno(self):
223  return self.local_file.fileno()
224 
225  # Iterator.
226 
227  def __next__(self):
228  return next(self.local_file)
229 
230  # Read the specified number of bytes.
231 
232  def read(self, size=-1):
233  return self.local_file.read(size)
234 
235  # Read one line, up to specified number of bytes.
236 
237  def readline(self, size=-1):
238  return self.local_file.readline(size)
239 
240  # Read multiple lines.
241 
242  def readlines(self, sizehint=-1):
243  return self.local_file.readlines()
244 
245  # Return file position.
246 
247  def tell(self):
248  return self.local_file.tell()
249 
250  # Truncate file (no argument version).
251 
252  def truncate(self):
253  self.local_file.truncate()
254 
255  # Truncate file (position argument).
256 
257  def truncate(self, pos):
258  self.local_file.truncate(pos)
259 
260  # Write a string.
261 
262  def write(self, str):
263  self.local_file.write(str)
264 
265  # Write multiple strings.
266 
267  def writelines(self, strs):
268  self.local_file.writelines(strs)
269 
270 # Global functions.
271 
272 
273 # Open file
274 
275 def open(path, mode='r', buf=-1):
276  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
277  if debug:
278  print('*** Larbatch_posix: Opening dcache_file %s using mode %s.' % (path, mode))
279  return dcache_file(path, mode, buf)
280  else:
281  if debug:
282  print('*** Larbatch_posix: Opening normal file %s using mode %s.' % (path, mode))
283  return __builtins__['open'](path, mode, buf)
284 
285 
286 # Read lines from a file.
287 
288 def readlines(path):
289  return open(path).readlines()
290 
291 
292 # Copy file.
293 
294 def copy(src, dest):
295  if exists(dest):
296  remove(dest)
297  if (src.startswith('/pnfs/') or dest.startswith('/pnfs/')):
298  if prefer_grid or not pnfs_is_mounted:
299  if debug:
300  print('*** Larbatch_posix: Copy %s to %s using ifdh.' % (src, dest))
301  larbatch_utilities.ifdh_cp(src, dest)
302  else:
303  if debug:
304  print('*** Larbatch_posix: Copy %s to %s using posix with timeout.' % (src, dest))
305  larbatch_utilities.posix_cp(src, dest)
306  else:
307  if debug:
308  print('*** Larbatch_posix: Copy %s to %s using posix.' % (src, dest))
309  shutil.copy(src, dest)
310 
311  # Done
312 
313  return
314 
315 
316 # List directory contents.
317 
318 def listdir(path):
319 
320  if not isdir(path):
321  raise OSError('%s is not a directory.' % path)
322  result = []
323  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
324  if debug:
325  print('*** Larbatch_posix: Listdir %s using ifdh.' % path)
326 
327  # Get normalized tail.
328 
329  tail = os.path.normpath(path[-6:])
330 
331  # Call "ifdh ls".
332 
333  contents = larbatch_utilities.ifdh_ls(path, 1)
334 
335  # Loop over contents returned by ifdh.
336  # Normalize the paths returned by "ifdh ls", which in this context mainly
337  # has the effect of stripping off trailing '/' on directories.
338  # Filter out parent directory, which ifdh sometimes (usually?) includes in result.
339 
340  for c in contents:
341  nc = os.path.normpath(c.strip())
342  if not nc.endswith(tail):
343  result.append(os.path.basename(nc))
344 
345  else:
346  if debug:
347  print('*** Larbatch_posix: Listdir %s using posix.' % path)
348  #result = os.listdir(path)
349 
350  # To reduce hang risk, read contents of directory in a subprocess with
351  # a timeout.
352 
353  cmd = ['ls', path]
354  jobinfo = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
355 
356  q = queue.Queue()
357  thread = threading.Thread(target=larbatch_utilities.wait_for_subprocess, args=[jobinfo, q])
358  thread.start()
359  thread.join(timeout=60)
360  if thread.is_alive():
361  if debug:
362  print('*** Larbatch_posix: Terminating subprocess.')
363  jobinfo.terminate()
364  thread.join()
365  rc = q.get()
366  jobout = convert_str(q.get())
367  joberr = convert_str(q.get())
368  if rc == 0:
369  for word in jobout.split():
370  result.append(word)
371 
372  # Done.
373 
374  return result
375 
376 
377 # Test existence. Works for files and directories.
378 
379 def exists(path):
380 
381  result = False
382  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
383  if debug:
384  print('*** Larbatch_posix: Check existence of %s using ifdh.' % path)
385 
386  # Do "ifdh ls."
387 
388  try:
389  larbatch_utilities.ifdh_ls(path, 0)
390  result = True
391  except:
392  result = False
393 
394  else:
395  if debug:
396  print('*** Larbatch_posix: Check existence of %s using posix.' % path)
397  #result = os.path.exists(path)
398 
399  # In order to reduce hang risk from stat'ing file,
400  # check existence by getting contents of parent directory.
401 
402  npath = os.path.normpath(path) # Strip trailing '/'.
403  dir = os.path.dirname(npath)
404  base = os.path.basename(npath)
405  if dir == '':
406  dir = '.'
407  if isdir(dir):
408  files = listdir(dir)
409  for filename in files:
410  if base == filename:
411  result = True
412 
413  # Done.
414 
415  return result
416 
417 
418 # Test existence if directory.
419 
420 def isdir(path):
421 
422  result = False
423 
424  # Optimizations for speed and to reduce hang risk by not stat'ing every file.
425 
426  if path[-5:] == '.list' or \
427  path[-5:] == '.root' or \
428  path[-5:] == '.json' or \
429  path[-4:] == '.txt' or \
430  path[-4:] == '.fcl' or \
431  path[-4:] == '.out' or \
432  path[-4:] == '.err' or \
433  path[-3:] == '.sh' or \
434  path[-5:] == '.stat':
435  return False
436 
437  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
438  if debug:
439  print('*** Larbatch_posix: Check existence of directory %s using ifdh.' % path)
440 
441  # Make sure path exists before trying to determine if it is a directory.
442 
443  if exists(path):
444 
445  # The only reliable way to get information about a directory is
446  # to get a listing of the parent directory.
447 
448  npath = os.path.normpath(path) # Strip trailing '/'
449  name = os.path.basename(npath)
450  dir = os.path.dirname(npath)
451  lines = larbatch_utilities.ifdh_ll(dir, 1)
452  for line in lines:
453  words = line.split()
454  if len(words) > 5 and words[-1] == name:
455  if words[0][0] == 'd':
456  result = True
457 
458 
459  else:
460  if debug:
461  print('*** Larbatch_posix: Check existence of directory %s using posix.' % path)
462  result = os.path.isdir(path)
463 
464  # Done.
465 
466  return result
467 
468 
469 # Get file status.
470 #
471 # This function is a partial emulation of os.stat. The return value
472 # is a type os.stat_result, which is a 10-tuple of values. The following
473 # values are filled by this function, since information is not always
474 # available from grid tools.
475 #
476 # mode - File type and permissions.
477 # uid - Owner uid. For compatibility, always set to the process uid.
478 # gid - Owner gid. For compatibility, always set to the process gid.
479 # nlink - Number of links.
480 # size - Object size.
481 
482 def stat(path):
483 
484  result = None
485  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
486  if debug:
487  print('*** Larbatch_posix: Stat %s using ifdh.' % path)
488 
489  # The only reliable way to get information about a directory is
490  # to get a listing of the parent directory.
491 
492  npath = os.path.normpath(path) # Strip trailing '/'
493  name = os.path.basename(npath)
494  dir = os.path.dirname(npath)
495  lines = larbatch_utilities.ifdh_ll(dir, 1)
496  for line in lines:
497  words = line.split()
498  if len(words) >5 and words[-1] == name:
499 
500  # Found thie path. Fill result.
501 
502  # Interpret mode string.
503 
504  mode = larbatch_utilities.parse_mode(words[0])
505 
506  # Get remaining fields.
507 
508  nlinks = int(words[1])
509  size = int(words[4])
510  result = os.stat_result((mode, # Mode
511  0, # Inode
512  0, # Device
513  nlinks, # Number of links
514  os.getuid(), # Uid
515  os.getgid(), # Gid
516  size, # Size
517  0, # Access time
518  0, # Mod time
519  0)) # Creation time
520 
521  else:
522  if debug:
523  print('*** Larbatch_posix: Stat %s using posix.' % path)
524  result = os.stat(path)
525 
526  if result == None:
527  raise OSError('No such file or directory.')
528 
529  # Done.
530 
531  return result
532 
533 
534 # Test file access.
535 # This implementation only tests access to dCache files via the
536 # user permission, which is a limitation of grid tools.
537 
538 def access(path, mode):
539 
540  result = False
541  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
542  if debug:
543  print('*** Larbatch_posix: Check access for %s using ifdh.' % path)
544  sr = stat(path)
545  if sr.st_mode != 0:
546 
547  # File exists.
548 
549  result = True
550 
551  # Test permissions.
552 
553  if mode & os.R_OK:
554  if not sr.st_mode & statmod.S_IRUSR:
555  result = False
556  if mode & os.W_OK:
557  if not sr.st_mode & statmod.S_IWUSR:
558  result = False
559  if mode & os.X_OK:
560  if not sr.st_mode & statmod.S_IXUSR:
561  result = False
562 
563  else:
564  if debug:
565  print('*** Larbatch_posix: Check access for %s using posix.' % path)
566  result = os.access(path, mode)
567 
568  # Done.
569 
570  return result
571 
572 
573 # Walk directory tree. Like os.walk, this function returns an iterator over
574 # 3-tuples, one for each directory in the tree rooted in the specified
575 # top directory. Each 3-tuple contains the following information.
576 #
577 # 1. Path of directory.
578 # 2. List of dictory names in this directory.
579 # 3. List of non-directory files in this directory.
580 #
581 # In case of posix mode, this function includes its own implementation
582 # of the walking algorithm so that we can take advantage of optimzations
583 # contained in this module's implementation of isdir.
584 
585 def walk(top, topdown=True):
586 
587  # Quit if top directory doesn't exist.
588 
589  if not exists(top):
590  return
591 
592  # Get contents of top directory using either ifdh or posix.
593 
594  dirs = []
595  files = []
596  if top.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
597  if debug:
598  print('*** Larbatch_posix: Walk directory tree for %s using ifdh.' % top)
599 
600  # Retrieve the contents of this directory using ifdh.
601 
602  lines = larbatch_utilities.ifdh_ll(top, 1)
603  for line in lines:
604  words = line.split()
605  if len(words) > 5 and words[-1] != '__pycache__':
606  if words[0][0] == 'd':
607  dirs.append(words[-1])
608  else:
609  files.append(words[-1])
610  else:
611  if debug:
612  print('*** Larbatch_posix: Walk directory tree for %s using posix.' % top)
613  contents = listdir(top)
614  for obj in contents:
615  if obj != '__pycache__':
616  if isdir(os.path.join(top, obj)):
617  dirs.append(obj)
618  else:
619  files.append(obj)
620 
621  if topdown:
622  yield top, dirs, files
623 
624  # Recursively descend into subdirectories.
625 
626  for dir in dirs:
627  for result in walk(os.path.join(top, dir), topdown):
628  yield result
629 
630  if not topdown:
631  yield top, dirs, files
632 
633  # Done.
634 
635  return
636 
637 
638 # Make directory (parent directory must exist).
639 # Mode argument is ignored for dCache files, but is passed to os.mkdir
640 # for non-dCache files.
641 
642 def mkdir(path, mode=0o777):
643  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
644  if debug:
645  print('*** Larbatch_posix: Make directory for %s using ifdh.' % path)
646  larbatch_utilities.ifdh_mkdir(path)
647  else:
648  if debug:
649  print('*** Larbatch_posix: Make directory for %s using posix.' % path)
650  os.mkdir(path, mode)
651 
652 
653 # Make directory and parents.
654 # Mode argument is ignored for dCache files, but is passed to os.mkdir
655 # for non-dCache files.
656 # "ifdh mkdir_p" is buggy, so we do the recursion locally.
657 
658 def makedirs(path, mode=0o777):
659  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
660  if debug:
661  print('*** Larbatch_posix: Make directory recursively for %s using ifdh.' % path)
662 
663  # Make sure parent directory exists.
664 
665  np = os.path.normpath(path) # Stip trailing '/', if present.
666  parent = os.path.dirname(np)
667  if not isdir(parent):
668  makedirs(parent, mode)
669 
670  # Now make directory itself.
671 
672  larbatch_utilities.ifdh_mkdir(path)
673  else:
674  if debug:
675  print('*** Larbatch_posix: Make directory recursively for %s using posix.' % path)
676  os.makedirs(path, mode)
677 
678 
679 # Rename file.
680 # "ifdh mv" seems to be buggy, so use uberftp.
681 
682 def rename(src, dest):
683  if (src.startswith('/pnfs/') or
684  dest.startswith('/pnfs/')) and (prefer_grid or not pnfs_is_mounted):
685  if debug:
686  print('*** Larbatch_posix: Rename %s to %s using ifdh.' % (src, dest))
687 
688  src_uri = larbatch_utilities.gridftp_uri(src)
689  dest_path = larbatch_utilities.dcache_path(dest)
690  cmd = ['uberftp', '-rename', src_uri, dest_path]
691  jobinfo = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
692 
693  q = queue.Queue()
694  thread = threading.Thread(target=larbatch_utilities.wait_for_subprocess, args=[jobinfo, q])
695  thread.start()
696  thread.join(timeout=60)
697  if thread.is_alive():
698  if debug:
699  print('*** Larbatch_posix: Terminating subprocess.')
700  jobinfo.terminate()
701  thread.join()
702  rc = q.get()
703  jobout = convert_str(q.get())
704  joberr = convert_str(q.get())
705  if rc != 0:
706  raise IFDHError(cmd, rc, jobout, joberr)
707  else:
708  if debug:
709  print('*** Larbatch_posix: Rename %s to %s using posix.' % (src, dest))
710  os.rename(src, dest)
711 
712 
713 # Delete file.
714 
715 def remove(path):
716  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
717  if debug:
718  print('*** Larbatch_posix: Delete file %s using ifdh.' % path)
719  larbatch_utilities.ifdh_rm(path)
720  else:
721  if debug:
722  print('*** Larbatch_posix: Delete file %s using posix.' % path)
723 
724  # Deleting a file is a hang risk, especially, but not only, in dCache.
725  # Therefore, use the following procedure.
726  #
727  # 1. Rename file to a random name (this can usually be done, even
728  # for undeletable files).
729  #
730  # 2. Delete renamed file in a subprocess. No need to wait for
731  # subprocess to finish, or check its exit status.
732 
733  #os.remove(path)
734  newpath = path + '_' + str(uuid.uuid4())
735  try:
736  os.rename(path, newpath)
737  os.system('rm -f %s &' % newpath)
738  return
739  except:
740  pass
741  os.remove(path)
742 
743 # Delete empty directory.
744 
745 def rmdir(path):
746  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
747  if debug:
748  print('*** Larbatch_posix: Delete directoroy %s using ifdh.' % path)
749  larbatch_utilities.ifdh_rmdir(path)
750  else:
751  if debug:
752  print('*** Larbatch_posix: Delete directoroy %s using posix.' % path)
753  os.rmdir(path)
754 
755 
756 # Delete directory tree.
757 
758 def rmtree(path):
759  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
760  if debug:
761  print('*** Larbatch_posix: Delete directoroy tree %s using ifdh.' % path)
762 
763  # Delete contents recursively.
764 
765  lines = larbatch_utilities.ifdh_ll(path, 1)
766  for line in lines:
767  words = line.split()
768  if len(words) > 5:
769  if words[0][0] == 'd':
770  rmtree(os.path.join(path, words[-1]))
771  else:
772  remove(os.path.join(path, words[-1]))
773 
774  # Directory should be empty when we get to here.
775 
776  rmdir(path)
777 
778  else:
779  if debug:
780  print('*** Larbatch_posix: Delete directoroy tree %s using posix.' % path)
781 
782  # Deleting a directory tree is a hang risk, especially, but not only, in dCache.
783  # Therefore, use the following procedure.
784  #
785  # 1. Rename directory to a random name (this can usually be done, even
786  # for undeletable directories).
787  #
788  # 2. Delete renamed directory in a subprocess. No need to wait for
789  # subprocess to finish, or check its exit status.
790 
791  #shutil.rmtree(path)
792  npath = os.path.normpath(path) # Strip trailing '/'
793  newpath = npath + '_' + str(uuid.uuid4())
794  os.rename(npath, newpath)
795  os.system('rm -rf %s &' % newpath)
796 
797  # Done
798 
799  return
800 
801 
802 # Change mode.
803 
804 def chmod(path, mode):
805  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
806  if debug:
807  print('*** Larbatch_posix: Change mode for %s using ifdh.' % path)
808  larbatch_utilities.ifdh_chmod(path, mode)
809  else:
810  if debug:
811  print('*** Larbatch_posix: Change mode for %s using posix.' % path)
812  try:
813  os.chmod(path, mode)
814  except:
815  print('Warning: chmod failed for path %s' % path)
816 
817 
818 # Make symbolic link.
819 # Use nfs.
820 
821 def symlink(src, dest):
822 
823  # Make sure we have a kerberos ticket.
824 
825  if src.startswith('/pnfs/') and not pnfs_is_mounted:
826  if debug:
827  print('*** Larbatch_posix: Make symbolic link from %s to %s using nfs server.' % (src, dest))
828  larbatch_utilities.test_ticket()
829  cmd = ['ssh', larbatch_utilities.nfs_server(), 'ln', '-s', src, dest]
830  jobinfo = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
831 
832  q = queue.Queue()
833  thread = threading.Thread(target=larbatch_utilities.wait_for_subprocess, args=[jobinfo, q])
834  thread.start()
835  thread.join(timeout=60)
836  if thread.is_alive():
837  if debug:
838  print('*** Larbatch_posix: Terminating subprocess.')
839  jobinfo.terminate()
840  thread.join()
841  rc = q.get()
842  jobout = convert_str(q.get())
843  joberr = convert_str(q.get())
844  if rc != 0:
845  raise IFDHError(cmd, rc, jobout, joberr)
846 
847  else:
848  if debug:
849  print('*** Larbatch_posix: Make symbolic link from %s to %s using posix.' % (src, dest))
850  os.symlink(src, dest)
851 
852 
853 # Read a symbolic link.
854 # Use nfs.
855 
856 def readlink(path):
857 
858  result = ''
859 
860  # Make sure we have a kerberos ticket.
861 
862  if path.startswith('/pnfs/') and not_pnfs_is_mounted:
863  if debug:
864  print('*** Larbatch_posix: Read symbolic link %s using nfs server.' % path)
865  larbatch_utilities.test_ticket()
866  cmd = ['ssh', larbatch_utilities.nfs_server(), 'readlink', path]
867  jobinfo = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
868 
869  q = queue.Queue()
870  thread = threading.Thread(target=larbatch_utilities.wait_for_subprocess, args=[jobinfo, q])
871  thread.start()
872  thread.join(timeout=60)
873  if thread.is_alive():
874  if debug:
875  print('*** Larbatch_posix: Terminating subprocess.')
876  jobinfo.terminate()
877  thread.join()
878  rc = q.get()
879  jobout = convert_str(q.get())
880  joberr = convert_str(q.get())
881  if rc != 0:
882  raise IFDHError(cmd, rc, jobout, joberr)
883  result = jobout.strip()
884 
885  else:
886  if debug:
887  print('*** Larbatch_posix: Read symbolic link %s using posix.' % path)
888  result = os.readlink(path)
889 
890  # Done.
891 
892  return result
893 
894 
895 # Convert a root file path to a streamable path or uri that can be opened
896 # using TFile::Open.
897 # Non-dCache paths (paths not starting with '/pnfs/') are not changed.
898 # dCache paths (patsh starting with '/pnfs/') may be converted to an xrootd uri.
899 
900 def root_stream(path):
901 
902  stream = path
903  if path.startswith('/pnfs/') and (prefer_grid or not pnfs_is_mounted):
904  if debug:
905  print('*** Larbatch_posix: Stream path %s using xrootd.' % path)
906  larbatch_utilities.test_proxy()
907  stream = larbatch_utilities.xrootd_uri(path)
908  else:
909  if debug:
910  print('*** Larbatch_posix: Stream path %s as normal file.' % path)
911  return stream
do one_file $F done echo for F in find $TOP name CMakeLists txt print