| OLD | NEW |
| 1 # -*- coding: utf-8 -*- |
| 1 # Copyright 2013 Google Inc. All Rights Reserved. | 2 # Copyright 2013 Google Inc. All Rights Reserved. |
| 2 # | 3 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 4 # 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 not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 6 # You may obtain a copy of the License at |
| 6 # | 7 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 8 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 9 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 10 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 11 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 13 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 14 # limitations under the License. |
| 15 """Implementation of Unix-like stat command for cloud storage providers.""" |
| 14 | 16 |
| 15 from __future__ import absolute_import | 17 from __future__ import absolute_import |
| 16 import binascii | 18 |
| 17 import logging | 19 import logging |
| 18 import re | |
| 19 import sys | |
| 20 | 20 |
| 21 from gslib.bucket_listing_ref import BucketListingObject |
| 22 from gslib.cloud_api import AccessDeniedException |
| 23 from gslib.cloud_api import NotFoundException |
| 21 from gslib.command import Command | 24 from gslib.command import Command |
| 22 from gslib.command import COMMAND_NAME | 25 from gslib.cs_api_map import ApiSelector |
| 23 from gslib.command import COMMAND_NAME_ALIASES | |
| 24 from gslib.command import FILE_URIS_OK | |
| 25 from gslib.command import MAX_ARGS | |
| 26 from gslib.command import MIN_ARGS | |
| 27 from gslib.command import PROVIDER_URIS_OK | |
| 28 from gslib.command import SUPPORTED_SUB_ARGS | |
| 29 from gslib.command import URIS_START_ARG | |
| 30 from gslib.exception import CommandException | 26 from gslib.exception import CommandException |
| 31 from gslib.help_provider import HELP_NAME | 27 from gslib.exception import InvalidUrlError |
| 32 from gslib.help_provider import HELP_NAME_ALIASES | 28 from gslib.storage_url import ContainsWildcard |
| 33 from gslib.help_provider import HELP_ONE_LINE_SUMMARY | 29 from gslib.storage_url import StorageUrlFromString |
| 34 from gslib.help_provider import HELP_TEXT | |
| 35 from gslib.help_provider import HelpType | |
| 36 from gslib.help_provider import HELP_TYPE | |
| 37 from gslib.util import PrintFullInfoAboutUri | |
| 38 from gslib.util import NO_MAX | 30 from gslib.util import NO_MAX |
| 39 from boto import InvalidUriError | 31 from gslib.util import PrintFullInfoAboutObject |
| 40 | 32 |
| 41 _detailed_help_text = (""" | 33 |
| 34 _DETAILED_HELP_TEXT = (""" |
| 42 <B>SYNOPSIS</B> | 35 <B>SYNOPSIS</B> |
| 43 gsutil stat uri... | 36 gsutil stat url... |
| 44 | 37 |
| 45 | 38 |
| 46 <B>DESCRIPTION</B> | 39 <B>DESCRIPTION</B> |
| 47 The stat command will output details about the specified object URIs. | 40 The stat command will output details about the specified object URLs. |
| 48 It is similar to running: | 41 It is similar to running: |
| 49 | 42 |
| 50 gsutil ls -L gs://some-bucket/some-object | 43 gsutil ls -L gs://some-bucket/some-object |
| 51 | 44 |
| 52 but is more efficient because it avoids performing bucket listings and extra | 45 but is more efficient because it avoids performing bucket listings gets the |
| 53 ACL GET's before reading each object's metadata. It performs a single HTTP | 46 minimum necessary amount of object metadata. |
| 54 HEAD request per listed object. | |
| 55 | 47 |
| 56 The gsutil stat command will, however, perform bucket listings if you specify | 48 The gsutil stat command will, however, perform bucket listings if you specify |
| 57 URIs using wildcards. | 49 URLs using wildcards. |
| 58 | 50 |
| 59 If run with the gsutil -q option nothing will be printed, e.g.: | 51 If run with the gsutil -q option nothing will be printed, e.g.: |
| 60 | 52 |
| 61 gsutil -q stat gs://some-bucket/some-object | 53 gsutil -q stat gs://some-bucket/some-object |
| 62 | 54 |
| 63 This can be useful for writing scripts, because the exit status will be 0 for | 55 This can be useful for writing scripts, because the exit status will be 0 for |
| 64 an existing object and 1 for a non-existent object. | 56 an existing object and 1 for a non-existent object. |
| 65 | 57 |
| 66 Note: Unlike the gsutil ls command, the stat command does not support | 58 Note: Unlike the gsutil ls command, the stat command does not support |
| 67 operations on sub-directories. For example, if you run the command: | 59 operations on sub-directories. For example, if you run the command: |
| 68 | 60 |
| 69 gsutil -q stat gs://some-bucket/some-object/ | 61 gsutil -q stat gs://some-bucket/some-object/ |
| 70 | 62 |
| 71 gsutil will look up information about the object "some-object/" (with a | 63 gsutil will look up information about the object "some-object/" (with a |
| 72 trailing slash) inside bucket "some-bucket", as opposed to operating on | 64 trailing slash) inside bucket "some-bucket", as opposed to operating on |
| 73 objects nested under gs://some-bucket/some-object. Unless you actually have an | 65 objects nested under gs://some-bucket/some-object. Unless you actually have an |
| 74 object with that name, the operation will fail. | 66 object with that name, the operation will fail. |
| 75 """) | 67 """) |
| 76 | 68 |
| 69 |
| 77 # TODO: Add ability to stat buckets. | 70 # TODO: Add ability to stat buckets. |
| 78 | |
| 79 class StatCommand(Command): | 71 class StatCommand(Command): |
| 80 """Implementation of gsutil stat command.""" | 72 """Implementation of gsutil stat command.""" |
| 81 | 73 |
| 82 # Command specification (processed by parent class). | 74 # Command specification. See base class for documentation. |
| 83 command_spec = { | 75 command_spec = Command.CreateCommandSpec( |
| 84 # Name of command. | 76 'stat', |
| 85 COMMAND_NAME : 'stat', | 77 command_name_aliases=[], |
| 86 # List of command name aliases. | 78 min_args=1, |
| 87 COMMAND_NAME_ALIASES : [], | 79 max_args=NO_MAX, |
| 88 # Min number of args required by this command. | 80 supported_sub_args='', |
| 89 MIN_ARGS : 1, | 81 file_url_ok=False, |
| 90 # Max number of args required by this command, or NO_MAX. | 82 provider_url_ok=False, |
| 91 MAX_ARGS : NO_MAX, | 83 urls_start_arg=0, |
| 92 # Getopt-style string specifying acceptable sub args. | 84 gs_api_support=[ApiSelector.XML, ApiSelector.JSON], |
| 93 SUPPORTED_SUB_ARGS : '', | 85 gs_default_api=ApiSelector.JSON, |
| 94 # True if file URIs acceptable for this command. | 86 ) |
| 95 FILE_URIS_OK : False, | 87 # Help specification. See help_provider.py for documentation. |
| 96 # True if provider-only URIs acceptable for this command. | 88 help_spec = Command.HelpSpec( |
| 97 PROVIDER_URIS_OK : False, | 89 help_name='stat', |
| 98 # Index in args of first URI arg. | 90 help_name_aliases=[], |
| 99 URIS_START_ARG : 0, | 91 help_type='command_help', |
| 100 } | 92 help_one_line_summary='Display object status', |
| 101 help_spec = { | 93 help_text=_DETAILED_HELP_TEXT, |
| 102 # Name of command or auxiliary help info for which this help applies. | 94 subcommand_help_text={}, |
| 103 HELP_NAME : 'stat', | 95 ) |
| 104 # List of help name aliases. | |
| 105 HELP_NAME_ALIASES : [], | |
| 106 # Type of help: | |
| 107 HELP_TYPE : HelpType.COMMAND_HELP, | |
| 108 # One line summary of this help. | |
| 109 HELP_ONE_LINE_SUMMARY : 'Display object status', | |
| 110 # The full help text. | |
| 111 HELP_TEXT : _detailed_help_text, | |
| 112 } | |
| 113 | 96 |
| 114 # Command entry point. | |
| 115 def RunCommand(self): | 97 def RunCommand(self): |
| 116 for uri_str in self.args: | 98 """Command entry point for stat command.""" |
| 117 uri = self.suri_builder.StorageUri(uri_str) | 99 # List of fields we'll print for stat objects. |
| 118 if not uri.names_object(): | 100 stat_fields = ['updated', 'cacheControl', 'contentDisposition', |
| 119 raise CommandException('The stat command only works with object URIs') | 101 'contentEncoding', 'contentLanguage', 'size', 'contentType', |
| 120 for blr in self.WildcardIterator(uri): | 102 'componentCount', 'metadata', 'crc32c', 'md5Hash', 'etag', |
| 103 'generation', 'metageneration'] |
| 104 found_nonmatching_arg = False |
| 105 for url_str in self.args: |
| 106 arg_matches = 0 |
| 107 url = StorageUrlFromString(url_str) |
| 108 if not url.IsObject(): |
| 109 raise CommandException('The stat command only works with object URLs') |
| 110 try: |
| 111 if ContainsWildcard(url_str): |
| 112 blr_iter = self.WildcardIterator(url_str).IterObjects( |
| 113 bucket_listing_fields=stat_fields) |
| 114 else: |
| 115 single_obj = self.gsutil_api.GetObjectMetadata( |
| 116 url.bucket_name, url.object_name, generation=url.generation, |
| 117 provider=url.scheme, fields=stat_fields) |
| 118 blr_iter = [BucketListingObject(url, root_object=single_obj)] |
| 119 for blr in blr_iter: |
| 120 if blr.IsObject(): |
| 121 arg_matches += 1 |
| 122 if logging.getLogger().isEnabledFor(logging.INFO): |
| 123 PrintFullInfoAboutObject(blr, incl_acl=False) |
| 124 except AccessDeniedException: |
| 125 print 'You aren\'t authorized to read %s - skipping' % url_str |
| 126 except InvalidUrlError: |
| 127 raise |
| 128 except NotFoundException: |
| 129 pass |
| 130 if not arg_matches: |
| 121 if logging.getLogger().isEnabledFor(logging.INFO): | 131 if logging.getLogger().isEnabledFor(logging.INFO): |
| 122 PrintFullInfoAboutUri(blr.uri, False, self.headers) | 132 print 'No URLs matched %s' % url_str |
| 123 else: | 133 found_nonmatching_arg = True |
| 124 try: | 134 if found_nonmatching_arg: |
| 125 uri.get_key(False, headers=self.headers) | 135 return 1 |
| 126 except InvalidUriError as e: | |
| 127 return 1 | |
| 128 return 0 | 136 return 0 |
| OLD | NEW |