Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: third_party/gsutil/gslib/command.py

Issue 10199002: Upgrade gsutil to 3.4 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed comments Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright 2010 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Base class for gsutil commands.
16
17 In addition to base class code, this file contains helpers that depend on base
18 class state (such as GetAclCommandHelper, which depends on self.gsutil_bin_dir,
19 self.bucket_storage_uri_class, etc.) In general, functions that depend on class
20 state and that are used by multiple commands belong in this file. Functions that
21 don't depend on class state belong in util.py, and non-shared helpers belong in
22 individual subclasses.
23 """
24
25 import boto
26 import getopt
27 import gslib
28 import gslib.util
29 import logging
30 import multiprocessing
31 import os
32 import re
33 import sys
34 import xml.dom.minidom
35 import xml.sax.xmlreader
36
37 from boto import handler
38 from boto.storage_uri import StorageUri
39 from exception import CommandException
40 from getopt import GetoptError
41 from gslib.help_provider import HelpProvider
42 from gslib import util
43 from gslib.name_expansion import NameExpansionHandler
44 from gslib.project_id import ProjectIdHandler
45 from gslib.storage_uri_builder import StorageUriBuilder
46 from gslib.thread_pool import ThreadPool
47 from gslib.util import HAVE_OAUTH2
48 from gslib.util import NO_MAX
49
50 from gslib.wildcard_iterator import ContainsWildcard
51
52
53 def _ThreadedLogger():
54 """Creates a logger that resembles 'print' output, but is thread safe.
55
56 The logger will display all messages logged with level INFO or above. Log
57 propagation is disabled.
58
59 Returns:
60 A logger object.
61 """
62 log = logging.getLogger('threaded-logging')
63 log.propagate = False
64 log.setLevel(logging.INFO)
65 log_handler = logging.StreamHandler()
66 log_handler.setFormatter(logging.Formatter('%(message)s'))
67 log.addHandler(log_handler)
68 return log
69
70 # command_spec key constants.
71 COMMAND_NAME = 'command_name'
72 COMMAND_NAME_ALIASES = 'command_name_aliases'
73 MIN_ARGS = 'min_args'
74 MAX_ARGS = 'max_args'
75 SUPPORTED_SUB_ARGS = 'supported_sub_args'
76 FILE_URIS_OK = 'file_uri_ok'
77 PROVIDER_URIS_OK = 'provider_uri_ok'
78 URIS_START_ARG = 'uris_start_arg'
79 CONFIG_REQUIRED = 'config_required'
80
81
82 class Command(object):
83 # Global instance of a threaded logger object.
84 THREADED_LOGGER = _ThreadedLogger()
85
86 REQUIRED_SPEC_KEYS = [COMMAND_NAME]
87
88 # Each subclass must define the following map, minimally including the
89 # keys in REQUIRED_SPEC_KEYS; other values below will be used as defaults,
90 # although for readbility subclasses should specify the complete map.
91 command_spec = {
92 # Name of command.
93 COMMAND_NAME : None,
94 # List of command name aliases.
95 COMMAND_NAME_ALIASES : [],
96 # Min number of args required by this command.
97 MIN_ARGS : 0,
98 # Max number of args required by this command, or NO_MAX.
99 MAX_ARGS : NO_MAX,
100 # Getopt-style string specifying acceptable sub args.
101 SUPPORTED_SUB_ARGS : '',
102 # True if file URIs are acceptable for this command.
103 FILE_URIS_OK : False,
104 # True if provider-only URIs are acceptable for this command.
105 PROVIDER_URIS_OK : False,
106 # Index in args of first URI arg.
107 URIS_START_ARG : 0,
108 # True if must configure gsutil before running command.
109 CONFIG_REQUIRED : True,
110 }
111 _default_command_spec = command_spec
112 help_spec = HelpProvider.help_spec
113
114 """Define an empty test specification, which derived classes must populate.
115
116 This is a list of tuples containing the following values:
117
118 step_name - mnemonic name for test, displayed when test is run
119 cmd_line - shell command line to run test
120 expect_ret or None - expected return code from test (None means ignore)
121 (result_file, expect_file) or None - tuple of result file and expected
122 file to diff for additional test
123 verification beyond the return code
124 (None means no diff requested)
125 Notes:
126
127 - Setting expected_ret to None means there is no expectation and,
128 hence, any returned value will pass.
129
130 - Any occurrences of the string 'gsutil' in the cmd_line parameter
131 are expanded to the full path to the gsutil command under test.
132
133 - The cmd_line, result_file and expect_file parameters may
134 contain the following special substrings:
135
136 $Bn - converted to one of 10 unique-for-testing bucket names (n=0..9)
137 $On - converted to one of 10 unique-for-testing object names (n=0..9)
138 $Fn - converted to one of 10 unique-for-testing file names (n=0..9)
139
140 - The generated file names are full pathnames, whereas the generated
141 bucket and object names are simple relative names.
142
143 - Tests with a non-None result_file and expect_file automatically
144 trigger an implicit diff of the two files.
145
146 - These test specifications, in combination with the conversion strings
147 allow tests to be constructed parametrically. For example, here's an
148 annotated subset of a test_steps for the cp command:
149
150 # Copy local file to object, verify 0 return code.
151 ('simple cp', 'gsutil cp $F1 gs://$B1/$O1', 0, None, None),
152 # Copy uploaded object back to local file and diff vs. orig file.
153 ('verify cp', 'gsutil cp gs://$B1/$O1 $F2', 0, '$F2', '$F1'),
154
155 - After pattern substitution, the specs are run sequentially, in the
156 order in which they appear in the test_steps list.
157 """
158 test_steps = []
159
160 # Define a convenience property for command name, since it's used many places.
161 def _GetDefaultCommandName(self):
162 return self.command_spec[COMMAND_NAME]
163 command_name = property(_GetDefaultCommandName)
164
165 def __init__(self, command_runner, args, headers, debug, parallel_operations,
166 gsutil_bin_dir, boto_lib_dir, config_file_list, gsutil_ver,
167 bucket_storage_uri_class, test_method=None):
168 """
169 Args:
170 command_runner: CommandRunner (for commands built atop other commands).
171 args: Command-line args (arg0 = actual arg, not command name ala bash).
172 headers: Dictionary containing optional HTTP headers to pass to boto.
173 debug: Debug level to pass in to boto connection (range 0..3).
174 parallel_operations: Should command operations be executed in parallel?
175 gsutil_bin_dir: Bin dir from which gsutil is running.
176 boto_lib_dir: Lib dir where boto runs.
177 config_file_list: Config file list returned by _GetBotoConfigFileList().
178 gsutil_ver: Version string of currently running gsutil command.
179 bucket_storage_uri_class: Class to instantiate for cloud StorageUris.
180 Settable for testing/mocking.
181 test_method: Optional general purpose method for testing purposes.
182 Application and semantics of this method will vary by
183 command and test type.
184
185 Implementation note: subclasses shouldn't need to define an __init__
186 method, and instead depend on the shared initialization that happens
187 here. If you do define an __init__ method in a subclass you'll need to
188 explicitly call super().__init__(). But you're encouraged not to do this,
189 because it will make changing the __init__ interface more painful.
190 """
191 # Save class values from constructor params.
192 self.command_runner = command_runner
193 self.args = args
194 self.unparsed_args = args
195 self.headers = headers
196 self.debug = debug
197 self.parallel_operations = parallel_operations
198 self.gsutil_bin_dir = gsutil_bin_dir
199 self.boto_lib_dir = boto_lib_dir
200 self.config_file_list = config_file_list
201 self.gsutil_ver = gsutil_ver
202 self.bucket_storage_uri_class = bucket_storage_uri_class
203 self.test_method = test_method
204 self.exclude_symlinks = False
205 self.recursion_requested = False
206
207 # Process sub-command instance specifications.
208 # First, ensure subclass implementation sets all required keys.
209 for k in self.REQUIRED_SPEC_KEYS:
210 if k not in self.command_spec or self.command_spec[k] is None:
211 raise CommandException('"%s" command implementation is missing %s '
212 'specification' % (self.command_name, k))
213 # Now override default command_spec with subclass-specified values.
214 tmp = self._default_command_spec
215 tmp.update(self.command_spec)
216 self.command_spec = tmp
217 del tmp
218
219 # Make sure command provides a test specification.
220 if not self.test_steps:
221 # TODO: Uncomment following lines when test feature is ready.
222 #raise CommandException('"%s" command implementation is missing test '
223 #'specification' % self.command_name)
224 pass
225
226 # Parse and validate args.
227 try:
228 (self.sub_opts, self.args) = getopt.getopt(
229 args, self.command_spec[SUPPORTED_SUB_ARGS])
230 except GetoptError, e:
231 raise CommandException('%s for "%s" command.' % (e.msg,
232 self.command_name))
233 if (len(self.args) < self.command_spec[MIN_ARGS]
234 or len(self.args) > self.command_spec[MAX_ARGS]):
235 raise CommandException('Wrong number of arguments for "%s" command.' %
236 self.command_name)
237 if (not self.command_spec[FILE_URIS_OK]
238 and self.HaveFileUris(self.args[self.command_spec[URIS_START_ARG]:])):
239 raise CommandException('"%s" command does not support "file://" URIs. '
240 'Did you mean to use a gs:// URI?' %
241 self.command_name)
242 if (not self.command_spec[PROVIDER_URIS_OK]
243 and self._HaveProviderUris(
244 self.args[self.command_spec[URIS_START_ARG]:])):
245 raise CommandException('"%s" command does not support provider-only '
246 'URIs.' % self.command_name)
247 if self.command_spec[CONFIG_REQUIRED]:
248 self._ConfigureNoOpAuthIfNeeded()
249
250 self.proj_id_handler = ProjectIdHandler()
251 self.suri_builder = StorageUriBuilder(debug, bucket_storage_uri_class)
252
253 # We're treating recursion_requested like it's used by all commands, but
254 # only some of the commands accept the -R option.
255 if self.sub_opts:
256 for o, unused_a in self.sub_opts:
257 if o == '-r' or o == '-R':
258 self.recursion_requested = True
259 break
260
261 self.exp_handler = NameExpansionHandler(
262 self.command_name, self.proj_id_handler, self.headers, self.debug,
263 self.bucket_storage_uri_class)
264
265 def RunCommand(self):
266 """Abstract function in base class. Subclasses must implement this."""
267 raise CommandException('Command %s is missing its RunCommand() '
268 'implementation' % self.command_name)
269
270 ############################################################
271 # Shared helper functions that depend on base class state. #
272 ############################################################
273
274 def UrisAreForSingleProvider(self, uri_args):
275 """Tests whether the uris are all for a single provider.
276
277 Returns: a StorageUri for one of the uris on success, None on failure.
278 """
279 provider = None
280 uri = None
281 for uri_str in uri_args:
282 # validate=False because we allow wildcard uris.
283 uri = boto.storage_uri(
284 uri_str, debug=self.debug, validate=False,
285 bucket_storage_uri_class=self.bucket_storage_uri_class)
286 if not provider:
287 provider = uri.scheme
288 elif uri.scheme != provider:
289 return None
290 return uri
291
292 def SetAclCommandHelper(self):
293 """
294 Common logic for setting ACLs. Sets the standard ACL or the default
295 object ACL depending on self.command_name.
296 """
297 acl_arg = self.args[0]
298 uri_args = self.args[1:]
299 # Disallow multi-provider setacl requests, because there are differences in
300 # the ACL models.
301 storage_uri = self.UrisAreForSingleProvider(uri_args)
302 if not storage_uri:
303 raise CommandException('"%s" command spanning providers not allowed.' %
304 self.command_name)
305
306 # Get ACL object from connection for one URI, for interpreting the ACL.
307 # This won't fail because the main startup code insists on at least 1 arg
308 # for this command.
309 acl_class = storage_uri.acl_class()
310 canned_acls = storage_uri.canned_acls()
311
312 # Determine whether acl_arg names a file containing XML ACL text vs. the
313 # string name of a canned ACL.
314 if os.path.isfile(acl_arg):
315 acl_file = open(acl_arg, 'r')
316 acl_txt = acl_file.read()
317 acl_file.close()
318 acl_obj = acl_class()
319 # Handle wildcard-named bucket.
320 if ContainsWildcard(storage_uri.bucket_name):
321 try:
322 bucket_uri = self.exp_handler.WildcardIterator(
323 storage_uri.clone_replace_name('')).IterUris().next()
324 except StopIteration:
325 raise CommandException('No URIs matched')
326 else:
327 bucket_uri = storage_uri
328 h = handler.XmlHandler(acl_obj, bucket_uri.get_bucket())
329 try:
330 xml.sax.parseString(acl_txt, h)
331 except xml.sax._exceptions.SAXParseException, e:
332 raise CommandException('Requested ACL is invalid: %s at line %s, '
333 'column %s' % (e.getMessage(), e.getLineNumber(),
334 e.getColumnNumber()))
335 acl_arg = acl_obj
336 else:
337 # No file exists, so expect a canned ACL string.
338 if acl_arg not in canned_acls:
339 raise CommandException('Invalid canned ACL "%s".' % acl_arg)
340
341 # Used to track if any ACLs failed to be set.
342 self.everything_set_okay = True
343
344 def _SetAclExceptionHandler(e):
345 """Simple exception handler to allow post-completion status."""
346 self.THREADED_LOGGER.error(str(e))
347 self.everything_set_okay = False
348
349 def _SetAclFunc(src_uri, exp_src_uri, _unused_src_uri_names_container=None,
350 _unused_src_uri_expands_to_multi=None,
351 _unused_have_multiple_srcs=None,
352 _unused_have_existing_dest_subdir=None):
353 # We don't do bucket operations multi-threaded (see comment below).
354 assert self.command_name != 'setdefacl'
355 self.THREADED_LOGGER.info('Setting ACL on %s...' % exp_src_uri)
356 exp_src_uri.set_acl(acl_arg, exp_src_uri.object_name, False,
357 self.headers)
358
359 # If user specified -R option, convert any bucket args to bucket wildcards
360 # (e.g., gs://bucket/*), to prevent the operation from being applied to
361 # the buckets themselves.
362 if self.recursion_requested:
363 for i in range(len(uri_args)):
364 uri = self.suri_builder.StorageUri(uri_args[i])
365 if uri.names_bucket():
366 uri_args[i] = uri.clone_replace_name('*').uri
367 else:
368 # Handle bucket ACL setting operations single-threaded, because
369 # our threading machinery currently assumes it's working with objects
370 # (src_uri_expansion), and normally we wouldn't expect users to need to
371 # set ACLs on huge numbers of buckets at once anyway.
372 for i in range(len(uri_args)):
373 uri_str = uri_args[i]
374 if self.suri_builder.StorageUri(uri_str).names_bucket():
375 self._RunSingleThreadedSetAcl(acl_arg, uri_args)
376 return
377
378 src_uri_expansion = self.exp_handler.ExpandWildcardsAndContainers(
379 uri_args, self.recursion_requested, self.recursion_requested)
380 if src_uri_expansion.IsEmpty():
381 raise CommandException('No URIs matched')
382
383 # Perform requests in parallel (-m) mode, if requested, using
384 # configured number of parallel processes and threads. Otherwise,
385 # perform requests with sequential function calls in current process.
386 self.Apply(_SetAclFunc, src_uri_expansion, _SetAclExceptionHandler)
387
388 if not self.everything_set_okay:
389 raise CommandException('Some files could not be removed.')
390
391 def _RunSingleThreadedSetAcl(self, acl_arg, uri_args):
392 some_matched = False
393 for uri_str in uri_args:
394 for blr in self.exp_handler.WildcardIterator(uri_str):
395 if blr.HasPrefix():
396 continue
397 some_matched = True
398 uri = blr.GetUri()
399 if self.command_name == 'setdefacl':
400 print 'Setting default object ACL on %s...' % uri
401 uri.set_def_acl(acl_arg, uri.object_name, False, self.headers)
402 else:
403 print 'Setting ACL on %s...' % uri
404 uri.set_acl(acl_arg, uri.object_name, False, self.headers)
405 if not some_matched:
406 raise CommandException('No URIs matched')
407
408 def GetAclCommandHelper(self):
409 """Common logic for getting ACLs. Gets the standard ACL or the default
410 object ACL depending on self.command_name."""
411 # Wildcarding is allowed but must resolve to just one object.
412 uris = list(self.exp_handler.WildcardIterator(self.args[0]).IterUris())
413 if len(uris) == 0:
414 raise CommandException('No URIs matched')
415 if len(uris) != 1:
416 raise CommandException('%s matched more than one URI, which is not '
417 'allowed by the %s command' % (self.args[0], self.command_name))
418 uri = uris[0]
419 if not uri.names_bucket() and not uri.names_object():
420 raise CommandException('"%s" command must specify a bucket or '
421 'object.' % self.command_name)
422 if self.command_name == 'getdefacl':
423 acl = uri.get_def_acl(False, self.headers)
424 else:
425 acl = uri.get_acl(False, self.headers)
426 # Pretty-print the XML to make it more easily human editable.
427 parsed_xml = xml.dom.minidom.parseString(acl.to_xml().encode('utf-8'))
428 print parsed_xml.toprettyxml(indent=' ')
429
430 def GetXmlSubresource(self, subresource, uri_arg):
431 """Print an xml subresource, e.g. logging, for a bucket/object.
432
433 Args:
434 subresource: The subresource name.
435 uri_arg: URI for the bucket/object. Wildcards will be expanded.
436
437 Raises:
438 CommandException: if errors encountered.
439 """
440 # Wildcarding is allowed but must resolve to just one bucket.
441 uris = list(self.exp_handler.WildcardIterator(uri_arg).IterUris())
442 if len(uris) != 1:
443 raise CommandException('Wildcards must resolve to exactly one item for '
444 'get %s' % subresource)
445 uri = uris[0]
446 xml_str = uri.get_subresource(subresource, False, self.headers)
447 # Pretty-print the XML to make it more easily human editable.
448 parsed_xml = xml.dom.minidom.parseString(xml_str.encode('utf-8'))
449 print parsed_xml.toprettyxml(indent=' ')
450
451 def Apply(self, func, src_uri_expansion, thr_exc_handler,
452 have_existing_dest_subdir=None, shared_attrs=None):
453 """Dispatch input URI assignments across a pool of parallel OS
454 processes and/or Python threads, based on options (-m or not)
455 and settings in the user's config file. If non-parallel mode
456 or only one OS process requested, execute requests sequentially
457 in the current OS process.
458
459 Args:
460 func: Function to call to process each URI.
461 src_uri_expansion: gslib.name_expansion.NameExpansionResult.
462 thr_exc_handler: Exception handler for ThreadPool class.
463 have_existing_dest_subdir: bool indicator whether dest is an existing
464 subdirectory. Only matters for cp/mv; pass None otherwise.
465 shared_attrs: List of attributes to manage across sub-processes.
466
467 Raises:
468 CommandException if invalid config encountered.
469 """
470 # Set OS process and python thread count as a function of options
471 # and config.
472 if self.parallel_operations:
473 process_count = boto.config.getint(
474 'GSUtil', 'parallel_process_count',
475 gslib.commands.config.DEFAULT_PARALLEL_PROCESS_COUNT)
476 if process_count < 1:
477 raise CommandException('Invalid parallel_process_count "%d".' %
478 process_count)
479 thread_count = boto.config.getint(
480 'GSUtil', 'parallel_thread_count',
481 gslib.commands.config.DEFAULT_PARALLEL_THREAD_COUNT)
482 if thread_count < 1:
483 raise CommandException('Invalid parallel_thread_count "%d".' %
484 thread_count)
485 else:
486 # If -m not specified, then assume 1 OS process and 1 Python thread.
487 process_count = 1
488 thread_count = 1
489
490 if self.debug:
491 self.THREADED_LOGGER.info('process count: %d', process_count)
492 self.THREADED_LOGGER.info('thread count: %d', thread_count)
493
494 # Construct dictionary of assigned URIs containing one list per
495 # OS process/shard. Assignments are stored as tuples containing
496 # (src_uri to be copied,
497 # single URI from wildcard expansion of src_uri,
498 # bool indicator whether src_uri expands to multiple URIs,
499 # bool indicator whether this is a multi-source request,
500 # bool indicator whether dest is an existing subdir).
501 shard = 0
502 assigned_uris = {}
503 have_multiple_srcs = src_uri_expansion.IsMultiSrcRequest()
504 for src_uri in src_uri_expansion.GetSrcUris():
505 src_uri_names_container = src_uri_expansion.NamesContainer(src_uri)
506 for exp_src_bucket_listing_ref in (
507 src_uri_expansion.IterExpandedBucketListingRefsFor(src_uri)):
508 if shard not in assigned_uris:
509 assigned_uris[shard] = []
510 src_uri_expands_to_multi = (
511 src_uri_expansion.SrcUriExpandsToMultipleSources(src_uri))
512 assigned_uris[shard].append((
513 src_uri, exp_src_bucket_listing_ref.GetUri(),
514 src_uri_names_container, src_uri_expands_to_multi,
515 have_multiple_srcs, have_existing_dest_subdir))
516 shard = (shard + 1) % process_count
517
518 if self.parallel_operations and (process_count > 1):
519 procs = []
520 # If any shared attributes passed by caller, create a dictionary of
521 # shared memory variables for every element in the list of shared
522 # attributes.
523 shared_vars = None
524 if shared_attrs:
525 for name in shared_attrs:
526 if not shared_vars:
527 shared_vars = {}
528 shared_vars[name] = multiprocessing.Value('i', 0)
529 for shard in assigned_uris:
530 # Spawn a separate OS process for each shard.
531 if self.debug:
532 self.THREADED_LOGGER.info('spawning process for shard %d', shard)
533 p = multiprocessing.Process(target=self._ApplyThreads,
534 args=(func, assigned_uris[shard], shard,
535 thread_count, thr_exc_handler,
536 shared_vars))
537 procs.append(p)
538 p.start()
539 # Wait for all spawned OS processes to finish.
540 failed_process_count = 0
541 for p in procs:
542 p.join()
543 # Count number of procs that returned non-zero exit code.
544 if p.exitcode != 0:
545 failed_process_count += 1
546 # Abort main process if one or more sub-processes failed.
547 if failed_process_count:
548 plural_str = ''
549 if failed_process_count > 1:
550 plural_str = 'es'
551 raise Exception('unexpected failure in %d sub-process%s, '
552 'aborting...' % (failed_process_count, plural_str))
553 # Propagate shared variables back to caller's attributes.
554 if shared_vars:
555 for (name, var) in shared_vars.items():
556 setattr(self, name, var.value)
557 else:
558 # Only one OS process requested so perform request in current
559 # OS process, in shard zero with thread_count threads.
560 self._ApplyThreads(func, assigned_uris[0], 0, thread_count,
561 thr_exc_handler, None)
562
563 def HaveFileUris(self, args_to_check):
564 """Checks whether args_to_check contain any file URIs.
565
566 Args:
567 args_to_check: Command-line argument subset to check.
568
569 Returns:
570 True if args_to_check contains any file URIs.
571 """
572 for uri_str in args_to_check:
573 if uri_str.lower().startswith('file://') or uri_str.find(':') == -1:
574 return True
575 return False
576
577 ######################
578 # Private functions. #
579 ######################
580
581 def _HaveProviderUris(self, args_to_check):
582 """Checks whether args_to_check contains any provider URIs (like 'gs://').
583
584 Args:
585 args_to_check: Command-line argument subset to check.
586
587 Returns:
588 True if args_to_check contains any provider URIs.
589 """
590 for uri_str in args_to_check:
591 if re.match('^[a-z]+://$', uri_str):
592 return True
593 return False
594
595 def _ConfigureNoOpAuthIfNeeded(self):
596 """Sets up no-op auth handler if no boto credentials are configured."""
597 config = boto.config
598 if not util.HasConfiguredCredentials():
599 if self.config_file_list:
600 if (config.has_option('Credentials', 'gs_oauth2_refresh_token')
601 and not HAVE_OAUTH2):
602 raise CommandException(
603 'Your gsutil is configured with OAuth2 authentication '
604 'credentials.\nHowever, OAuth2 is only supported when running '
605 'under Python 2.6 or later\n(unless additional dependencies are '
606 'installed, see README for details); you are running Python %s.' %
607 sys.version)
608 raise CommandException('You have no storage service credentials in any '
609 'of the following boto config\nfiles. Please '
610 'add your credentials as described in the '
611 'gsutil README file, or else\nre-run '
612 '"gsutil config" to re-create a config '
613 'file:\n%s' % self.config_file_list)
614 else:
615 # With no boto config file the user can still access publicly readable
616 # buckets and objects.
617 from gslib import no_op_auth_plugin
618
619 def _ApplyThreads(self, func, assigned_uris, shard, num_threads,
620 thr_exc_handler=None, shared_vars=None):
621 """
622 Perform subset of required requests across a caller specified
623 number of parallel Python threads, which may be one, in which
624 case the requests are processed in the current thread.
625
626 Args:
627 func: Function to call for each request.
628 assigned_uris: List of tuples to process, of the form:
629 (src_uri, exp_src_uri, src_uri_names_container,
630 src_uri_expands_to_multi, have_multiple_srcs,
631 have_existing_dest_subdir).
632 shard: Assigned subset (shard number) for this function.
633 num_threads: Number of Python threads to spawn to process this shard.
634 thr_exc_handler: Exception handler for ThreadPool class.
635 shared_vars: Dict of shared memory variables to be managed.
636 (only relevant, and non-None, if this function is
637 run in a separate OS process).
638 """
639 # Each OS process needs to establish its own set of connections to
640 # the server to avoid writes from different OS processes interleaving
641 # onto the same socket (and messing up the underlying SSL session).
642 # We ensure each process gets its own set of connections here by
643 # closing all connections in the storage provider connection pool.
644 connection_pool = StorageUri.provider_pool
645 if connection_pool:
646 for i in connection_pool:
647 connection_pool[i].connection.close()
648
649 if num_threads > 1:
650 thread_pool = ThreadPool(num_threads, thr_exc_handler)
651 try:
652 # Iterate over assigned URIs and perform copy operations for each.
653 for (src_uri, exp_src_uri, src_uri_names_container,
654 src_uri_expands_to_multi, have_multiple_srcs,
655 have_existing_dest_subdir) in assigned_uris:
656 if self.debug:
657 self.THREADED_LOGGER.info('process %d shard %d is handling uri %s',
658 os.getpid(), shard, exp_src_uri)
659 if (self.exclude_symlinks and exp_src_uri.is_file_uri()
660 and os.path.islink(exp_src_uri.object_name)):
661 self.THREADED_LOGGER.info('Skipping symbolic link %s...', exp_src_uri)
662 elif num_threads > 1:
663 thread_pool.AddTask(func, src_uri, exp_src_uri,
664 src_uri_names_container, src_uri_expands_to_multi,
665 have_multiple_srcs, have_existing_dest_subdir)
666 else:
667 func(src_uri, exp_src_uri, src_uri_names_container,
668 src_uri_expands_to_multi, have_multiple_srcs,
669 have_existing_dest_subdir)
670 # If any Python threads created, wait here for them to finish.
671 if num_threads > 1:
672 thread_pool.WaitCompletion()
673 finally:
674 if num_threads > 1:
675 thread_pool.Shutdown()
676 # If any shared variables (which means we are running in a separate OS
677 # process), increment value for each shared variable.
678 if shared_vars:
679 for (name, var) in shared_vars.items():
680 var.value += getattr(self, name)
OLDNEW
« no previous file with comments | « third_party/gsutil/gslib/bucket_listing_ref.py ('k') | third_party/gsutil/gslib/command_runner.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698