source: gs2-extensions/parallel-building/trunk/src/perllib/FileUtils/HDThriftFS.pm@ 27591

Last change on this file since 27591 was 27591, checked in by jmt12, 11 years ago

Ensure Thrift will, be default, attempt to connect to the local machine (by proper name, and only localhost if nothing else is found) and also allow a configuration file to contain only a port number (by just going having:<empty_string><colon><number>)

File size: 20.8 KB
Line 
1################################################################################
2#
3# HDThriftFS.pm -- file functions acting upon a HDFS via thrift
4#
5# A component of the Greenstone digital library software from the New Zealand
6# Digital Library Project at the University of Waikato, New Zealand.
7#
8# Copyright (C) 2013 New Zealand Digital Library Project
9#
10# This program is free software; you can redistribute it and/or modify it under
11# the terms of the GNU General Public License as published by the Free Software
12# Foundation; either version 2 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful, but WITHOUT
16# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18# details.
19#
20# You should have received a copy of the GNU General Public License along with
21# this program; if not, write to the Free Software Foundation, Inc., 675 Mass
22# Ave, Cambridge, MA 02139, USA.
23#
24###############################################################################
25
26# Thrift acts as client-server 'relay' between the Perl code and the HDFS. It
27# allows for persistant connections and so is significantly faster than
28# repeatedly starting Hadoop's Java application over and over. In order to
29# connect to the Thrift server this code needs to know the host and port the
30# server may be found on - information currently hard-coded near the top of the
31# script. There are also a number of Perl module API 'bindings' generated by
32# the Thrift compilation process... currently located within the packages of
33# the Parallel Processing extension. Note that I make use of some tie() magic
34# so as to provide calling code with 'file handle'-like objects to interact
35# with (print, readline etc), so that is pretty cool.
36
37package FileUtils::HDThriftFS;
38
39# Pragma
40use strict;
41
42# Setup Environment
43BEGIN
44{
45 die "GEXTPARALLELBUILDING not set\n" unless defined $ENV{'GEXTPARALLELBUILDING'};
46 die "GSDLOS not set\n" unless defined $ENV{'GSDLOS'};
47 die "GSDLCOLLECTDIR not set\n" unless defined $ENV{'GSDLCOLLECTDIR'};
48 # We need the Perl version before continuing
49 if (!defined $ENV{'PERL_VERSION'})
50 {
51 $ENV{'PERL_VERSION'} = `perl -S $ENV{'GEXTPARALLELBUILDING'}/bin/script/perl-version.pl`;
52 }
53 die "PERL_VERSION not set\n" unless defined $ENV{'PERL_VERSION'};
54 # Bit::Vector and Thrift modules
55 unshift (@INC, $ENV{'GEXTPARALLELBUILDING'} . '/' . $ENV{'GSDLOS'} . '/lib/perl/' . $ENV{'PERL_VERSION'});
56 # ThriftFS Perl API
57 unshift (@INC, $ENV{'GEXTPARALLELBUILDING'} . '/packages/ThriftFS-0.9.0/gen-perl');
58}
59
60# Modules - Core
61use Devel::Peek;
62use MIME::Base64;
63use POSIX qw(floor);
64use Symbol;
65use Thrift::Socket;
66use Thrift::BufferedTransport;
67use Thrift::BinaryProtocol;
68# Modules - Thrift
69use HadoopFS::FileSystem;
70# Modules - Greenstone
71use FileUtils::HDThriftFS::ThriftFH;
72use MIME::Base91;
73
74# Configuration
75my $host = (`hostname -s` || `hostname -a` || `hostname` || $ENV{'HOSTNAME'} || 'localhost');
76chomp($host);
77my $port = 58660;
78my $debug = 0;
79my $debug_encoding = 0;
80# Testing shows 64k is pretty optimal
81#my $buffer_length = 4 * 1024; # 4k blocks
82#my $buffer_length = 8 * 1024; # 8k blocks
83#my $buffer_length = 16 * 1024; # 16K blocks
84#my $buffer_length = 32 * 1024; # 32k blocks
85#my $buffer_length = 64 * 1024; # 64k blocks
86my $buffer_length = 128 * 1024; # 128k blocks
87#my $buffer_length = 256 * 1024; # 256k blocks
88#my $buffer_length = 512 * 1024; # 512k blocks
89#my $buffer_length = 1024 * 1024; # 1M blocks
90#my $buffer_length = 2048 * 1024; # 2M blocks
91## These cause "OUT OF MEMORY" errors on Medusa
92#my $buffer_length = 4096 * 1024; # 4M blocks
93#my $buffer_length = 8192 * 1024; # 8M blocks
94
95# Globals
96my $transport;
97my $thrift_client;
98
99
100## @function END()
101#
102# Ensure the transport layer, if open, is properly closed
103#
104END
105{
106 if (defined $transport)
107 {
108 $transport->close();
109 }
110}
111## END()
112
113
114## @function _establishClient()
115#
116sub _establishClient
117{
118 if (!defined $thrift_client)
119 {
120 # Look for a configuration file to override the default localhost:58660
121 # settings
122 my $conf_file_path = &FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'}, 'etc', 'thrift.conf');
123 if (&FileUtils::fileExists($conf_file_path))
124 {
125 print " * Found Thrift configuration file:\n";
126 my $conf_raw = &FileUtils::fileGetContents($conf_file_path);
127 if ($conf_raw =~ /^([^:]*):(\d+)/)
128 {
129 my $new_host = $1;
130 $port = $2;
131 if ($new_host ne '' && $new_host ne 'localhost')
132 {
133 $host = $new_host;
134 print " - Host: " . $host . "\n";
135 }
136 print " - Port: " . $port . "\n";
137 }
138 }
139
140 print " * Creating Thrift client connected to: $host:$port\n";
141 my $socket = Thrift::Socket->new($host, $port);
142 $socket->setSendTimeout(10000);
143 $socket->setRecvTimeout(20000);
144
145 $transport = Thrift::BufferedTransport->new($socket);
146 my $protocol = Thrift::BinaryProtocol->new($transport);
147 $thrift_client = HadoopFS::FileSystemClient->new($protocol);
148
149 eval { $transport->open(); };
150 if ($@)
151 {
152 &FileUtils::printError('Unable to connect: ' . $@->{message}, 1);
153 }
154 }
155}
156## _establishClient()
157
158
159## @function _generateHDFSPath()
160#
161sub _generateHDFSPath
162{
163 my ($path) = @_;
164 if (ref($path) ne 'HadoopFS::Pathname')
165 {
166 if ($path !~ /HDThriftFS:\/\//)
167 {
168 &FileUtils::printError('Not a valid thrift URI: ' . $path);
169 }
170 else
171 {
172 # Remove protocol and any host and port information
173 $path =~ s/HDThriftFS:\/\/[^\/]*//;
174 $path = HadoopFS::Pathname->new( { pathname => $path } );
175 }
176 }
177 return $path;
178}
179## _generateHDFSPath()
180
181
182## @function _printDebug()
183#
184sub _printDebug
185{
186 my ($msg) = @_;
187 if ($debug)
188 {
189 my ($package, $filename, $line, $function) = caller(1);
190 print STDERR '[DEBUG] ' . $function . ': ' . $msg . "\n";
191 }
192}
193## _printDebug()
194
195################################################################################
196################################################################################
197################################################################################
198
199
200## @function canRead()
201#
202sub canRead
203{
204 my $path = shift(@_);
205 return &checkPermission($path, 'r', @_);
206}
207## canRead()
208
209
210## @function checkPermission()
211#
212sub checkPermission
213{
214 my ($path, $mode, $username, $usergroup) = @_;
215 my $offsets = {'r' => 0, 'w' => 1, 'x' => 2};
216 # - ensure we have a connection to the thrift server
217 &_establishClient();
218 # - convert the path into a proper thrift path object
219 $path = &_generateHDFSPath($path);
220 # - determine the user (defaults to current user)
221 if (!defined $username)
222 {
223 if ($ENV{'GSDLOS'} =~ /^windows$/i)
224 {
225 require Win32;
226 $username = Win32::LoginName();
227 }
228 else
229 {
230 $username = getlogin || getpwuid($<);
231 }
232 }
233 # - determine the group
234 my $usergroups = {};
235 if (defined $usergroup)
236 {
237 $usergroups = (ref $usergroup eq "HASH") ? $usergroup : {$usergroup => 1};
238 }
239 else
240 {
241 if ($ENV{'GSDLOS'} =~ /^windows$/i)
242 {
243 # dunno
244 }
245 else
246 {
247 my $raw_groups = `groups`;
248 foreach my $group ( split(/\s/, $raw_groups))
249 {
250 $usergroups->{$group} = 1;
251 }
252 }
253 }
254 # Retrieve details from the file
255 my $file_stat = $thrift_client->stat($path);
256 my $owner = $file_stat->{'owner'};
257 my $group = $file_stat->{'group'};
258 my $permissions = $file_stat->{'permission'};
259 # Begin the cascade of tests to determine if the identified user belonging to
260 # the identified group can perform 'mode' access to the file.
261 my $has_permission = 0;
262 # - start with [u]ser permission
263 if (defined $owner && $username eq $owner)
264 {
265 my $target_char = substr($permissions, $offsets->{$mode}, 1);
266 if ($mode eq $target_char)
267 {
268 $has_permission = 1;
269 }
270 }
271 # - failing that, try [g]roup level permissions
272 if (!$has_permission && defined $group && defined $usergroups->{$group})
273 {
274 my $target_char = substr($permissions, 3 + $offsets->{$mode}, 1);
275 if ($mode eq $target_char)
276 {
277 $has_permission = 1;
278 }
279 }
280 # - and finally try [o]ther level permission
281 if (!$has_permission)
282 {
283 my $target_char = substr($permissions, 6 + $offsets->{$mode}, 1);
284 if ($mode eq $target_char)
285 {
286 $has_permission = 1;
287 }
288 }
289 return $has_permission;
290}
291## checkPermission
292
293
294## @function closeFileHandle()
295#
296sub closeFileHandle
297{
298 my $fh_ref = shift(@_);
299 &_printDebug('');
300 close($$fh_ref);
301 untie($$fh_ref);
302 return 1;
303}
304## closeFileHandle()
305
306
307## @function fileSize()
308#
309sub fileSize
310{
311 my ($path, $test_op) = @_;
312 # ensure we have a connection to the thrift server
313 &_establishClient();
314 # - convert the path into a proper thrift path object
315 $path = &_generateHDFSPath($path);
316 my $file_stat = $thrift_client->stat($path);
317 return $file_stat->{length};
318}
319## fileSize()
320
321
322## @function fileTest()
323#
324sub fileTest
325{
326 my ($raw_path, $test_op) = @_;
327 my $result = 0;
328 # ensure we have a connection to the thrift server
329 &_establishClient();
330 # - convert the path into a proper thrift path object
331 my $path = &_generateHDFSPath($raw_path);
332 # note: symbolic linking not supported within HDFS
333 if (!defined $test_op || $test_op eq '-l')
334 {
335 $test_op = '-e';
336 }
337 if ($test_op eq '-d')
338 {
339 if ($thrift_client->exists($path))
340 {
341 my $file = $thrift_client->stat($path);
342 if ($file->{'isdir'})
343 {
344 $result = 1;
345 }
346 }
347 }
348 elsif ($test_op eq '-e')
349 {
350 if ($thrift_client->exists($path))
351 {
352 $result = 1;
353 }
354 }
355 elsif ($test_op eq '-f')
356 {
357 if ($thrift_client->exists($path))
358 {
359 my $file = $thrift_client->stat($path);
360 if (!$file->{'isdir'})
361 {
362 $result = 1;
363 }
364 }
365 }
366 else
367 {
368 &FileUtils::printError('Unknown or unsupported test mode: ' . $test_op);
369 }
370 return $result;
371}
372## fileTest()
373
374
375## @function filenameConcatenate()
376#
377sub filenameConcatenate
378{
379 my $protocol = shift(@_);
380 my $filename = join('/', @_);
381 # remove repeated slashes
382 $filename =~ s/[\/]+/\//g;
383 # append protocol (which may cause multiple slashes)
384 $filename = $protocol . '/' . $filename;
385 # strip any trailing slashes
386 $filename =~ s/[\\\/]$//;
387 return $filename;
388}
389## filenameConcatenate()
390
391
392## @function isFilenameAbsolute()
393#
394sub isFilenameAbsolute
395{
396 # File paths against HDFS must be.
397 return 1;
398}
399# isFilenameAbsolute()
400
401
402## @function isHDFS
403#
404sub isHDFS
405{
406 return 1;
407}
408## isHDFS()
409
410
411## @function makeDirectory()
412#
413sub makeDirectory
414{
415 my ($raw_path) = @_;
416 my $result = 0;
417 # ensure we have a connection to the thrift server
418 &_establishClient();
419 # - convert the path into a proper thrift path object
420 my $path = &_generateHDFSPath($raw_path);
421 if (!&fileTest($path, '-d'))
422 {
423 # - create the directory
424 $thrift_client->mkdirs($path);
425 }
426 # - check that it exists
427 return (&fileTest($path, '-d'));
428}
429## makeDirectory()
430
431
432## @function modificationTime()
433#
434sub modificationTime
435{
436 my ($path) = @_;
437 # ensure we have a connection to the thrift server
438 &_establishClient();
439 # - convert the path into a proper thrift path object
440 $path = &_generateHDFSPath($path);
441 my $file_stat = $thrift_client->stat($path);
442 return floor($file_stat->{modificationTime} / 1000);
443}
444## modificationTime()
445
446
447## @function openFileHandle()
448#
449sub openFileHandle
450{
451 my ($raw_path, $mode, $fh_ref) = @_;
452 &_printDebug('path: ' . $raw_path . ', mode: ' . $mode . ', fh_ref');
453 # ensure we have a connection to the thrift server
454 &_establishClient();
455 #rint STDERR "DEBUG: openFileHandle($raw_path, $mode, fh_ref)\n";
456 my $path = &_generateHDFSPath($raw_path);
457 my $fh = gensym();
458 tie(*$fh, "FileUtils::HDThriftFS::ThriftFH", $thrift_client);
459 open($fh, $path, $mode) or die("Failed to open thriftfs");
460 $$fh_ref = $fh;
461 return 1;
462}
463## openFileHandle()
464
465
466## @function readDirectory()
467#
468sub readDirectory
469{
470 my ($raw_path) = @_;
471 my @files;
472 # ensure we have a connection to the thrift server
473 &_establishClient();
474 my $path = &_generateHDFSPath($raw_path);
475 my $raw_files = $thrift_client->listStatus($path);
476 if ($raw_files && @{$raw_files} > 0)
477 {
478 foreach my $file_stat (@{$raw_files})
479 {
480 my $file_path = $file_stat->{'path'};
481 my ($filename) = $file_path =~ /([^\\\/]+)$/;
482 push(@files, $filename);
483 }
484 }
485 return \@files;
486}
487## readDirectory()
488
489
490## @function removeFiles()
491#
492sub removeFiles
493{
494 my ($path, $recursive) = @_;
495 my $result = 0;
496 if (!defined $recursive)
497 {
498 $recursive = 0;
499 }
500 # ensure we have a connection to the thrift server
501 &_establishClient();
502 # - convert the path into a proper thrift path object as necessary
503 $path = &_generateHDFSPath($path);
504 if ($thrift_client->exists($path) && ($recursive || &fileTest($path, '-f')))
505 {
506 $thrift_client->rm($path, $recursive);
507 $result = !$thrift_client->exists($path);
508 }
509 return $result;
510}
511## removeFiles()
512
513
514## @function removeFilesFiltered()
515#
516sub removeFilesFiltered
517{
518 my ($paths, $accept_re, $reject_re) = @_;
519 # ensure we have a connection to the thrift server
520 &_establishClient();
521 # Perform a depth first, recursive, removal of files and directories that
522 # match the given accept and reject patterns
523 my @paths_array = (ref $paths eq "ARRAY") ? @$paths : ($paths);
524 my $num_removed = 0;
525 foreach my $raw_path (@paths_array)
526 {
527 # remove trailing slashes
528 $raw_path =~ s/[\/\\]+$//;
529 my $path = &_generateHDFSPath($raw_path);
530 if (!$thrift_client->exists($path))
531 {
532 print STDERR "HDThriftFS::removeFilesFiltered() path does not exist: " . $raw_path . "\n";
533 }
534 elsif (&fileTest($path, '-d'))
535 {
536 my @files = @{&readDirectory($path)};
537 foreach my $file (@files)
538 {
539 my $child_path = $raw_path . '/' . $file;
540 $num_removed += &removeFilesFiltered($child_path, $accept_re, $reject_re);
541 }
542 if (!defined $accept_re && !defined $reject_re)
543 {
544 # remove this directory - non-recursively so that the command fails
545 # if there are (somehow) still files contained within
546 $thrift_client->rm($path, 0);
547 if ($thrift_client->exists($path))
548 {
549 print STDERR "HDThriftFS::removeFilesFiltered() couldn't remove directory: " . $raw_path . "\n";
550 }
551 else
552 {
553 $num_removed++;
554 }
555 }
556 }
557 else
558 {
559 if (defined $reject_re && ($raw_path =~ m/$reject_re/))
560 {
561 next;
562 }
563 if ((!defined $accept_re) || ($raw_path =~ m/$accept_re/))
564 {
565 # remove this file
566 $thrift_client->rm($path, 0);
567 if ($thrift_client->exists($path))
568 {
569 print STDERR "HDThriftFS::removeFilesFiltered() couldn't remove file: " . $raw_path . "\n";
570 }
571 else
572 {
573 $num_removed++;
574 }
575 }
576 }
577 }
578 return $num_removed;
579}
580## removeFilesFiltered()
581
582
583## @function removeFilesRecursive()
584#
585sub removeFilesRecursive
586{
587 my ($path) = @_;
588 # use the more general removeFilesFiltered() function with no accept or reject
589 # expressions
590 return &removeFilesFiltered($path, undef, undef);
591}
592## removeFilesRecursive()
593
594
595## @function supportsSymbolicLink
596#
597sub supportsSymbolicLink
598{
599 return 0;
600}
601## supportsSymbolicLink()
602
603
604## @function transferFile()
605#
606sub transferFile
607{
608 my ($mode, $src, $dst) = @_;
609 # ensure we have a connection to the thrift server
610 &_establishClient();
611 #rint STDERR "transferFile($mode, $src, $dst)\n";
612 my $src_path = &_generateHDFSPath($src);
613 my $dst_path = &_generateHDFSPath($dst);
614 if (&fileTest($dst_path, '-d'))
615 {
616 my ($filename) = $src =~ /([^\\\/]+)$/;
617 $dst .= '/' . $filename;
618 $dst_path = &_generateHDFSPath($dst);
619 }
620 if (!$thrift_client->exists($src_path))
621 {
622 &FileUtils::printError('Source file (during ' . $mode . ') does not exist: ' . $src);
623 return 0;
624 }
625 if ($thrift_client->exists($dst_path))
626 {
627 &FileUtils::printError('Destination file (during ' . $mode . ') already exists: ' . $dst);
628 return 0;
629 }
630 # what happens next depends on the mode, and is either very easy or really
631 # hard
632 if ($mode eq 'MOVE')
633 {
634 $thrift_client->rename($src_path, $dst_path);
635 }
636 elsif ($mode eq 'COPY')
637 {
638 # Open the src file for reading
639 #rint STDERR "DEBUG: FHIN opened (should be 'r'): $src\n";
640 my $fhin = $thrift_client->open($src_path);
641 # Create the dst file for writing
642 #rint STDERR "DEBUG: FHOUT created (should be 'w'): $dst\n";
643 my $fhout = $thrift_client->create($dst_path);
644 # Read all of src file writing to dst file
645 # - this is where things have the potential to go wrong, as it doesn't seem
646 # thrift supports writing bytes
647 # - only strings. May need to see if I can make Perl behave using black
648 # magic flags (marking string as binary etc) It'll work fine for text
649 # files though
650 my $data = undef;
651 my $offset = 0;
652 # Read 4K blocks at a time
653 while ($data = $thrift_client->read($fhin, $offset, $buffer_length))
654 {
655 $thrift_client->write($fhout, $data);
656 $offset += $buffer_length;
657 if (length ($data) < $buffer_length)
658 {
659 last;
660 }
661 }
662 # Close files
663 $thrift_client->close($fhout);
664 $thrift_client->close($fhin);
665 }
666 my $result = ($thrift_client->exists($dst_path));
667 #rint STDERR "transferFile() => $result\n";
668 return $result;
669}
670## transferFile()
671
672
673## @function transferFileFromLocal()
674#
675sub transferFileFromLocal
676{
677 my ($mode, $src, $dst) = @_;
678 # ensure we have a connection to the thrift server
679 &_establishClient();
680 # destination is remote
681 my $dst_path = &_generateHDFSPath($dst);
682 if (&fileTest($dst_path, '-d'))
683 {
684 my ($filename) = $src =~ /([^\\\/]+)$/;
685 $dst .= '/' . $filename;
686 $dst_path = &_generateHDFSPath($dst);
687 }
688 # can't replace - if the file already exists
689 if (&fileTest($dst_path, '-f'))
690 {
691 &FileUtils::printError('Destination file (during ' . $mode . ') already exists: ' . $dst);
692 return 0;
693 }
694 # copy the file
695 my $fhin;
696 open($fhin, '<:raw', $src) or die("Failed to open file for reading: " . $src . " (" . $! . ")");
697 my $decoded = '';
698 my $fhout = $thrift_client->create($dst_path);
699 while (read($fhin, $decoded, $buffer_length))
700 {
701 if ($debug_encoding)
702 {
703 print STDERR "Writing Data: \n=== START ===\n"; Dump($decoded); print STDERR "\n=== END ===\n\n";
704 }
705 # Base64 encode to protect binary
706 #my $encoded = encode_base64($decoded);
707 # Base91 encode to protect binary - we add a Byte Order Marker so the
708 # Thrift Server can detect the need to decode the string sent
709 my $encoded = MIME::Base91::encode($decoded);
710 if ($debug_encoding)
711 {
712 print STDERR "Encoded: \n=== START ===\n"; Dump($encoded); print STDERR "\n=== END ===\n\n";
713 }
714 $thrift_client->write($fhout, $encoded);
715 }
716 close($fhin);
717 $thrift_client->close($fhout);
718 # in general, the transfer has worked if the destination file exists
719 my $result = $thrift_client->exists($dst_path);
720 # if moving, remove the source file from the local filesystem
721 if ($mode eq 'MOVE')
722 {
723 unlink($src);
724 # update result to reflect if we successfully removed the src file
725 $result = $result && (!-f $src);
726 }
727 return $result
728}
729## transferFileFromLocal()
730
731
732## @function transferFileToLocal()
733#
734sub transferFileToLocal
735{
736 my ($mode, $src, $dst) = @_;
737 # ensure we have a connection to the thrift server
738 &_establishClient();
739 # source is remote
740 my $src_path = &_generateHDFSPath($src);
741 if (!$thrift_client->exists($src_path))
742 {
743 &FileUtils::printError('Source file (during ' . $mode . ') does not exist: ' . $src);
744 return 0;
745 }
746 if (-d $dst)
747 {
748 my ($filename) = $src =~ /([^\\\/]+)$/;
749 $dst .= '/' . $filename;
750 }
751 if (-e $dst)
752 {
753 &FileUtils::printError('Destination file (during ' . $mode . ') already exists: ' . $dst);
754 return 0;
755 }
756 # open local file
757 my $fhout;
758 my $encoded = undef;
759 my $offset = 0;
760 open($fhout, '>:raw', $dst) or die("Failed to open file for writing: " . $dst);
761 my $fhin = $thrift_client->open($src_path);
762 while ($encoded = $thrift_client->read($fhin, $offset, $buffer_length))
763 {
764 if ($debug_encoding)
765 {
766 print STDERR "Reading Data: \n=== START ===\n"; Dump($encoded); print STDERR "\n=== END ===\n\n";
767 }
768 my $decoded = MIME::Base91::decode($encoded);
769 if ($debug_encoding)
770 {
771 print STDERR "Decoded: \n=== START ===\n"; Dump($decoded); print STDERR "\n=== END ===\n\n";
772 }
773 print $fhout $decoded;
774 if (length ($decoded) < $buffer_length)
775 {
776 last;
777 }
778 else
779 {
780 $offset += $buffer_length;
781 }
782 }
783 close($fhout);
784 $thrift_client->close($fhin);
785 # in general, the transfer has worked if the destination file exists
786 my $result = (-f $dst);
787 # if moving, remove the source file from the HDFS filesystem
788 if ($mode eq 'MOVE')
789 {
790 $thrift_client->rm($src_path, 0);
791 # update result to reflect if we successfully removed the src file
792 $result = $result && !$thrift_client->exists($src_path);
793 }
794 return $result;
795}
796## transferFileToLocal()
797
798
7991;
Note: See TracBrowser for help on using the repository browser.