1# -*- coding: utf-8 -*- 2# Copyright 2011 Google Inc. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Implementation of logging configuration command for buckets.""" 16 17from __future__ import absolute_import 18 19import sys 20 21from apitools.base.py import encoding 22 23from gslib.command import Command 24from gslib.command_argument import CommandArgument 25from gslib.cs_api_map import ApiSelector 26from gslib.exception import CommandException 27from gslib.help_provider import CreateHelpText 28from gslib.storage_url import StorageUrlFromString 29from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages 30from gslib.util import NO_MAX 31from gslib.util import UrlsAreForSingleProvider 32 33_SET_SYNOPSIS = """ 34 gsutil logging set on -b logging_bucket [-o log_object_prefix] url... 35 gsutil logging set off url... 36""" 37 38_GET_SYNOPSIS = """ 39 gsutil logging get url 40""" 41 42_SYNOPSIS = _SET_SYNOPSIS + _GET_SYNOPSIS.lstrip('\n') + '\n' 43 44_SET_DESCRIPTION = """ 45<B>SET</B> 46 The set sub-command has two sub-commands: 47 48<B>ON</B> 49 The "gsutil set on" command will enable access logging of the 50 buckets named by the specified URLs, outputting log files in the specified 51 logging_bucket. logging_bucket must already exist, and all URLs must name 52 buckets (e.g., gs://bucket). The required bucket parameter specifies the 53 bucket to which the logs are written, and the optional log_object_prefix 54 parameter specifies the prefix for log object names. The default prefix 55 is the bucket name. For example, the command: 56 57 gsutil logging set on -b gs://my_logging_bucket -o AccessLog \\ 58 gs://my_bucket1 gs://my_bucket2 59 60 will cause all read and write activity to objects in gs://mybucket1 and 61 gs://mybucket2 to be logged to objects prefixed with the name "AccessLog", 62 with those log objects written to the bucket gs://my_logging_bucket. 63 64 Next, you need to grant cloud-storage-analytics@google.com write access to 65 the log bucket, using this command: 66 67 gsutil acl ch -g cloud-storage-analytics@google.com:W gs://my_logging_bucket 68 69 Note that log data may contain sensitive information, so you should make 70 sure to set an appropriate default bucket ACL to protect that data. (See 71 "gsutil help defacl".) 72 73<B>OFF</B> 74 This command will disable access logging of the buckets named by the 75 specified URLs. All URLs must name buckets (e.g., gs://bucket). 76 77 No logging data is removed from the log buckets when you disable logging, 78 but Google Cloud Storage will stop delivering new logs once you have 79 run this command. 80 81""" 82 83_GET_DESCRIPTION = """ 84<B>GET</B> 85 If logging is enabled for the specified bucket url, the server responds 86 with a JSON document that looks something like this: 87 88 { 89 "logObjectPrefix": "AccessLog", 90 "logBucket": "my_logging_bucket" 91 } 92 93 You can download log data from your log bucket using the gsutil cp command. 94 95""" 96 97_DESCRIPTION = """ 98 Google Cloud Storage offers access logs and storage data in the form of 99 CSV files that you can download and view. Access logs provide information 100 for all of the requests made on a specified bucket in the last 24 hours, 101 while the storage logs provide information about the storage consumption of 102 that bucket for the last 24 hour period. The logs and storage data files 103 are automatically created as new objects in a bucket that you specify, in 104 24 hour intervals. 105 106 The logging command has two sub-commands: 107""" + _SET_DESCRIPTION + _GET_DESCRIPTION + """ 108 109<B>ACCESS LOG AND STORAGE DATA FIELDS</B> 110 For a complete list of access log fields and storage data fields, see: 111 https://developers.google.com/storage/docs/accesslogs#reviewing 112""" 113 114_DETAILED_HELP_TEXT = CreateHelpText(_SYNOPSIS, _DESCRIPTION) 115 116_get_help_text = CreateHelpText(_GET_SYNOPSIS, _GET_DESCRIPTION) 117_set_help_text = CreateHelpText(_SET_SYNOPSIS, _SET_DESCRIPTION) 118 119 120class LoggingCommand(Command): 121 """Implementation of gsutil logging command.""" 122 123 # Command specification. See base class for documentation. 124 command_spec = Command.CreateCommandSpec( 125 'logging', 126 command_name_aliases=['disablelogging', 'enablelogging', 'getlogging'], 127 usage_synopsis=_SYNOPSIS, 128 min_args=2, 129 max_args=NO_MAX, 130 supported_sub_args='b:o:', 131 file_url_ok=False, 132 provider_url_ok=False, 133 urls_start_arg=0, 134 gs_api_support=[ApiSelector.XML, ApiSelector.JSON], 135 gs_default_api=ApiSelector.JSON, 136 argparse_arguments=[ 137 CommandArgument('mode', choices=['on', 'off']), 138 CommandArgument.MakeZeroOrMoreCloudBucketURLsArgument() 139 ] 140 ) 141 # Help specification. See help_provider.py for documentation. 142 help_spec = Command.HelpSpec( 143 help_name='logging', 144 help_name_aliases=['loggingconfig', 'logs', 'log', 'getlogging', 145 'enablelogging', 'disablelogging'], 146 help_type='command_help', 147 help_one_line_summary='Configure or retrieve logging on buckets', 148 help_text=_DETAILED_HELP_TEXT, 149 subcommand_help_text={'get': _get_help_text, 'set': _set_help_text}, 150 ) 151 152 def _Get(self): 153 """Gets logging configuration for a bucket.""" 154 bucket_url, bucket_metadata = self.GetSingleBucketUrlFromArg( 155 self.args[0], bucket_fields=['logging']) 156 157 if bucket_url.scheme == 's3': 158 sys.stdout.write(self.gsutil_api.XmlPassThroughGetLogging( 159 bucket_url, provider=bucket_url.scheme)) 160 else: 161 if (bucket_metadata.logging and bucket_metadata.logging.logBucket and 162 bucket_metadata.logging.logObjectPrefix): 163 sys.stdout.write(str(encoding.MessageToJson( 164 bucket_metadata.logging)) + '\n') 165 else: 166 sys.stdout.write('%s has no logging configuration.\n' % bucket_url) 167 return 0 168 169 def _Enable(self): 170 """Enables logging configuration for a bucket.""" 171 # Disallow multi-provider 'logging set on' calls, because the schemas 172 # differ. 173 if not UrlsAreForSingleProvider(self.args): 174 raise CommandException('"logging set on" command spanning providers not ' 175 'allowed.') 176 target_bucket_url = None 177 target_prefix = None 178 for opt, opt_arg in self.sub_opts: 179 if opt == '-b': 180 target_bucket_url = StorageUrlFromString(opt_arg) 181 if opt == '-o': 182 target_prefix = opt_arg 183 184 if not target_bucket_url: 185 raise CommandException('"logging set on" requires \'-b <log_bucket>\' ' 186 'option') 187 if not target_bucket_url.IsBucket(): 188 raise CommandException('-b option must specify a bucket URL.') 189 190 # Iterate over URLs, expanding wildcards and setting logging on each. 191 some_matched = False 192 for url_str in self.args: 193 bucket_iter = self.GetBucketUrlIterFromArg(url_str, bucket_fields=['id']) 194 for blr in bucket_iter: 195 url = blr.storage_url 196 some_matched = True 197 self.logger.info('Enabling logging on %s...', blr) 198 logging = apitools_messages.Bucket.LoggingValue( 199 logBucket=target_bucket_url.bucket_name, 200 logObjectPrefix=target_prefix or url.bucket_name) 201 202 bucket_metadata = apitools_messages.Bucket(logging=logging) 203 self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata, 204 provider=url.scheme, fields=['id']) 205 if not some_matched: 206 raise CommandException('No URLs matched') 207 return 0 208 209 def _Disable(self): 210 """Disables logging configuration for a bucket.""" 211 # Iterate over URLs, expanding wildcards, and disabling logging on each. 212 some_matched = False 213 for url_str in self.args: 214 bucket_iter = self.GetBucketUrlIterFromArg(url_str, bucket_fields=['id']) 215 for blr in bucket_iter: 216 url = blr.storage_url 217 some_matched = True 218 self.logger.info('Disabling logging on %s...', blr) 219 logging = apitools_messages.Bucket.LoggingValue() 220 221 bucket_metadata = apitools_messages.Bucket(logging=logging) 222 self.gsutil_api.PatchBucket(url.bucket_name, bucket_metadata, 223 provider=url.scheme, fields=['id']) 224 if not some_matched: 225 raise CommandException('No URLs matched') 226 return 0 227 228 def RunCommand(self): 229 """Command entry point for the logging command.""" 230 # Parse the subcommand and alias for the new logging command. 231 action_subcommand = self.args.pop(0) 232 if action_subcommand == 'get': 233 func = self._Get 234 elif action_subcommand == 'set': 235 state_subcommand = self.args.pop(0) 236 if not self.args: 237 self.RaiseWrongNumberOfArgumentsException() 238 if state_subcommand == 'on': 239 func = self._Enable 240 elif state_subcommand == 'off': 241 func = self._Disable 242 else: 243 raise CommandException(( 244 'Invalid subcommand "%s" for the "%s %s" command.\n' 245 'See "gsutil help logging".') % ( 246 state_subcommand, self.command_name, action_subcommand)) 247 else: 248 raise CommandException(('Invalid subcommand "%s" for the %s command.\n' 249 'See "gsutil help logging".') % 250 (action_subcommand, self.command_name)) 251 self.ParseSubOpts(check_args=True) 252 func() 253 return 0 254