1# -*- coding: utf-8 -*-
2# Copyright 2013 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"""Gsutil API for interacting with cloud storage providers."""
16
17from __future__ import absolute_import
18
19
20class CloudApi(object):
21  """Abstract base class for interacting with cloud storage providers.
22
23  Implementations of the gsutil Cloud API are not guaranteed to be thread-safe.
24  Behavior when calling a gsutil Cloud API instance simultaneously across
25  threads is undefined and doing so will likely cause errors. Therefore,
26  a separate instance of the gsutil Cloud API should be instantiated per-thread.
27  """
28
29  def __init__(self, bucket_storage_uri_class, logger, provider=None,
30               debug=0, trace_token=None):
31    """Performs necessary setup for interacting with the cloud storage provider.
32
33    Args:
34      bucket_storage_uri_class: boto storage_uri class, used by APIs that
35                                provide boto translation or mocking.
36      logger: logging.logger for outputting log messages.
37      provider: Default provider prefix describing cloud storage provider to
38                connect to.
39      debug: Debug level for the API implementation (0..3).
40      trace_token: Google internal trace token to pass to the API
41                   implementation (string).
42    """
43    self.bucket_storage_uri_class = bucket_storage_uri_class
44    self.logger = logger
45    self.provider = provider
46    self.debug = debug
47    self.trace_token = trace_token
48
49  def GetBucket(self, bucket_name, provider=None, fields=None):
50    """Gets Bucket metadata.
51
52    Args:
53      bucket_name: Name of the bucket.
54      provider: Cloud storage provider to connect to.  If not present,
55                class-wide default is used.
56      fields: If present, return only these Bucket metadata fields, for
57              example, ['logging', 'defaultObjectAcl']
58
59    Raises:
60      ArgumentException for errors during input validation.
61      ServiceException for errors interacting with cloud storage providers.
62
63    Returns:
64      Bucket object.
65    """
66    raise NotImplementedError('GetBucket must be overloaded')
67
68  def ListBuckets(self, project_id=None, provider=None, fields=None):
69    """Lists bucket metadata for the given project.
70
71    Args:
72      project_id: Project owning the buckets, default from config if None.
73      provider: Cloud storage provider to connect to.  If not present,
74                class-wide default is used.
75      fields: If present, return only these metadata fields for the listing,
76              for example:
77              ['items/logging', 'items/defaultObjectAcl'].
78              Note that the WildcardIterator class should be used to list
79              buckets instead of calling this function directly.  It amends
80              the fields definition from get-like syntax such as
81              ['logging', 'defaultObjectAcl'] so that the caller does not
82              need to prepend 'items/' or specify fields necessary for listing
83              (like nextPageToken).
84
85    Raises:
86      ArgumentException for errors during input validation.
87      ServiceException for errors interacting with cloud storage providers.
88
89    Returns:
90      Iterator over Bucket objects.
91    """
92    raise NotImplementedError('ListBuckets must be overloaded')
93
94  def PatchBucket(self, bucket_name, metadata, canned_acl=None,
95                  canned_def_acl=None, preconditions=None, provider=None,
96                  fields=None):
97    """Updates bucket metadata for the bucket with patch semantics.
98
99    Args:
100      bucket_name: Name of bucket to update.
101      metadata: Bucket object defining metadata to be updated.
102      canned_acl: Canned ACL to apply to the bucket.
103      canned_def_acl: Canned default object ACL to apply to the bucket.
104      preconditions: Preconditions for the request.
105      provider: Cloud storage provider to connect to.  If not present,
106                class-wide default is used.
107      fields: If present, return only these Bucket metadata fields.
108
109    Raises:
110      ArgumentException for errors during input validation.
111      ServiceException for errors interacting with cloud storage providers.
112
113    Returns:
114      Bucket object describing new bucket metadata.
115    """
116    raise NotImplementedError('PatchBucket must be overloaded')
117
118  def CreateBucket(self, bucket_name, project_id=None, metadata=None,
119                   provider=None, fields=None):
120    """Creates a new bucket with the specified metadata.
121
122    Args:
123      bucket_name: Name of the new bucket.
124      project_id: Project owner of the new bucket, default from config if None.
125      metadata: Bucket object defining new bucket metadata.
126      provider: Cloud storage provider to connect to.  If not present,
127                class-wide default is used.
128      fields: If present, return only these Bucket metadata fields.
129
130    Raises:
131      ArgumentException for errors during input validation.
132      ServiceException for errors interacting with cloud storage providers.
133
134    Returns:
135      Bucket object describing new bucket metadata.
136    """
137    raise NotImplementedError('CreateBucket must be overloaded')
138
139  def DeleteBucket(self, bucket_name, preconditions=None, provider=None):
140    """Deletes a bucket.
141
142    Args:
143      bucket_name: Name of the bucket to delete.
144      preconditions: Preconditions for the request.
145      provider: Cloud storage provider to connect to.  If not present,
146                class-wide default is used.
147
148    Raises:
149      ArgumentException for errors during input validation.
150      ServiceException for errors interacting with cloud storage providers.
151
152    Returns:
153      None.
154    """
155    raise NotImplementedError('DeleteBucket must be overloaded')
156
157  class CsObjectOrPrefixType(object):
158    """Enum class for describing CsObjectOrPrefix types."""
159    OBJECT = 'object'  # Cloud object
160    PREFIX = 'prefix'  # Cloud bucket subdirectory
161
162  class CsObjectOrPrefix(object):
163    """Container class for ListObjects results."""
164
165    def __init__(self, data, datatype):
166      """Stores a ListObjects result.
167
168      Args:
169        data: Root object, either an apitools Object or a string Prefix.
170        datatype: CsObjectOrPrefixType of data.
171      """
172      self.data = data
173      self.datatype = datatype
174
175  def ListObjects(self, bucket_name, prefix=None, delimiter=None,
176                  all_versions=None, provider=None, fields=None):
177    """Lists objects (with metadata) and prefixes in a bucket.
178
179    Args:
180      bucket_name: Bucket containing the objects.
181      prefix: Prefix for directory-like behavior.
182      delimiter: Delimiter for directory-like behavior.
183      all_versions: If true, list all object versions.
184      provider: Cloud storage provider to connect to.  If not present,
185                class-wide default is used.
186      fields: If present, return only these metadata fields for the listing,
187              for example:
188              ['items/acl', 'items/updated', 'prefixes'].
189              Note that the WildcardIterator class should be used to list
190              objects instead of calling this function directly.  It amends
191              the fields definition from get-like syntax such as
192              ['acl', 'updated'] so that the caller does not need to
193              prepend 'items/' or specify any fields necessary for listing
194              (such as prefixes or nextPageToken).
195
196    Raises:
197      ArgumentException for errors during input validation.
198      ServiceException for errors interacting with cloud storage providers.
199
200    Returns:
201      Iterator over CsObjectOrPrefix wrapper class.
202    """
203    raise NotImplementedError('ListObjects must be overloaded')
204
205  def GetObjectMetadata(self, bucket_name, object_name, generation=None,
206                        provider=None, fields=None):
207    """Gets object metadata.
208
209    Args:
210      bucket_name: Bucket containing the object.
211      object_name: Object name.
212      generation: Generation of the object to retrieve.
213      provider: Cloud storage provider to connect to.  If not present,
214                class-wide default is used.
215      fields: If present, return only these Object metadata fields, for
216              example, ['acl', 'updated'].
217
218    Raises:
219      ArgumentException for errors during input validation.
220      ServiceException for errors interacting with cloud storage providers.
221
222    Returns:
223      Object object.
224    """
225    raise NotImplementedError('GetObjectMetadata must be overloaded')
226
227  def PatchObjectMetadata(self, bucket_name, object_name, metadata,
228                          canned_acl=None, generation=None, preconditions=None,
229                          provider=None, fields=None):
230    """Updates object metadata with patch semantics.
231
232    Args:
233      bucket_name: Bucket containing the object.
234      object_name: Object name for object.
235      metadata: Object object defining metadata to be updated.
236      canned_acl: Canned ACL to be set on the object.
237      generation: Generation (or version) of the object to update.
238      preconditions: Preconditions for the request.
239      provider: Cloud storage provider to connect to.  If not present,
240                class-wide default is used.
241      fields: If present, return only these Object metadata fields.
242
243    Raises:
244      ArgumentException for errors during input validation.
245      ServiceException for errors interacting with cloud storage providers.
246
247    Returns:
248      Updated object metadata.
249    """
250    raise NotImplementedError('PatchObjectMetadata must be overloaded')
251
252  class DownloadStrategy(object):
253    """Enum class for specifying download strategy."""
254    ONE_SHOT = 'oneshot'
255    RESUMABLE = 'resumable'
256
257  def GetObjectMedia(self, bucket_name, object_name, download_stream,
258                     provider=None, generation=None, object_size=None,
259                     download_strategy=DownloadStrategy.ONE_SHOT, start_byte=0,
260                     end_byte=None, progress_callback=None,
261                     serialization_data=None, digesters=None):
262    """Gets object data.
263
264    Args:
265      bucket_name: Bucket containing the object.
266      object_name: Object name.
267      download_stream: Stream to send the object data to.
268      provider: Cloud storage provider to connect to.  If not present,
269                class-wide default is used.
270      generation: Generation of the object to retrieve.
271      object_size: Total size of the object being downloaded.
272      download_strategy: Cloud API download strategy to use for download.
273      start_byte: Starting point for download (for resumable downloads and
274                  range requests). Can be set to negative to request a range
275                  of bytes (python equivalent of [:-3])
276      end_byte: Ending byte number, inclusive, for download (for range
277                requests). If None, download the rest of the object.
278      progress_callback: Optional callback function for progress notifications.
279                         Receives calls with arguments
280                         (bytes_transferred, total_size).
281      serialization_data: Implementation-specific JSON string of a dict
282                          containing serialization information for the download.
283      digesters: Dict of {string : digester}, where string is a name of a hash
284                 algorithm, and digester is a validation digester that supports
285                 update(bytes) and digest() using that algorithm.
286                 Implementation can set the digester value to None to indicate
287                 bytes were not successfully digested on-the-fly.
288
289    Raises:
290      ArgumentException for errors during input validation.
291      ServiceException for errors interacting with cloud storage providers.
292
293    Returns:
294      Content-encoding string if it was detected that the server sent an encoded
295      object during transfer, None otherwise.
296    """
297    raise NotImplementedError('GetObjectMedia must be overloaded')
298
299  def UploadObject(self, upload_stream, object_metadata, canned_acl=None,
300                   size=None, preconditions=None, progress_callback=None,
301                   provider=None, fields=None):
302    """Uploads object data and metadata.
303
304    Args:
305      upload_stream: Seekable stream of object data.
306      object_metadata: Object metadata for new object.  Must include bucket
307                       and object name.
308      canned_acl: Optional canned ACL to apply to object. Overrides ACL set
309                  in object_metadata.
310      size: Optional object size.
311      preconditions: Preconditions for the request.
312      progress_callback: Optional callback function for progress notifications.
313                         Receives calls with arguments
314                         (bytes_transferred, total_size).
315      provider: Cloud storage provider to connect to.  If not present,
316                class-wide default is used.
317      fields: If present, return only these Object metadata fields.
318
319    Raises:
320      ArgumentException for errors during input validation.
321      ServiceException for errors interacting with cloud storage providers.
322
323    Returns:
324      Object object for newly created destination object.
325    """
326    raise NotImplementedError('UploadObject must be overloaded')
327
328  def UploadObjectStreaming(self, upload_stream, object_metadata,
329                            canned_acl=None, preconditions=None,
330                            progress_callback=None, provider=None,
331                            fields=None):
332    """Uploads object data and metadata.
333
334    Args:
335      upload_stream: Stream of object data. May not be seekable.
336      object_metadata: Object metadata for new object.  Must include bucket
337                       and object name.
338      canned_acl: Optional canned ACL to apply to object. Overrides ACL set
339                  in object_metadata.
340      preconditions: Preconditions for the request.
341      progress_callback: Optional callback function for progress notifications.
342                         Receives calls with arguments
343                         (bytes_transferred, total_size), but fills in only
344                         bytes_transferred.
345      provider: Cloud storage provider to connect to.  If not present,
346                class-wide default is used.
347      fields: If present, return only these Object metadata fields.
348
349    Raises:
350      ArgumentException for errors during input validation.
351      ServiceException for errors interacting with cloud storage providers.
352
353    Returns:
354      Object object for newly created destination object.
355    """
356    raise NotImplementedError('UploadObjectStreaming must be overloaded')
357
358  def UploadObjectResumable(
359      self, upload_stream, object_metadata, canned_acl=None,
360      size=None, preconditions=None, serialization_data=None,
361      tracker_callback=None, progress_callback=None, provider=None,
362      fields=None):
363    """Uploads object data and metadata using a resumable upload strategy.
364
365    Args:
366      upload_stream: Seekable stream of object data.
367      object_metadata: Object metadata for new object.  Must include bucket
368                       and object name.
369      canned_acl: Optional canned ACL to apply to object. Overrides ACL set
370                  in object_metadata.
371      size: Total size of the object.
372      preconditions: Preconditions for the request.
373      serialization_data: Dict of {'url' : UploadURL} allowing for uploads to
374                          be resumed.
375      tracker_callback: Callback function taking a upload URL string.
376                        Guaranteed to be called when the implementation gets an
377                        upload URL, allowing the caller to resume the upload
378                        across process breaks by saving the upload URL in
379                        a tracker file.
380      progress_callback: Optional callback function for progress notifications.
381                         Receives calls with arguments
382                         (bytes_transferred, total_size).
383      provider: Cloud storage provider to connect to.  If not present,
384                class-wide default is used.
385      fields: If present, return only these Object metadata fields when the
386              upload is complete.
387
388    Raises:
389      ArgumentException for errors during input validation.
390      ServiceException for errors interacting with cloud storage providers.
391
392    Returns:
393      Object object for newly created destination object.
394    """
395    raise NotImplementedError('UploadObjectResumable must be overloaded')
396
397  def CopyObject(self, src_obj_metadata, dst_obj_metadata, src_generation=None,
398                 canned_acl=None, preconditions=None, progress_callback=None,
399                 max_bytes_per_call=None, provider=None, fields=None):
400    """Copies an object in the cloud.
401
402    Args:
403      src_obj_metadata: Object metadata for source object.  Must include
404                        bucket name, object name, and etag.
405      dst_obj_metadata: Object metadata for new object.  Must include bucket
406                        and object name.
407      src_generation: Generation of the source object to copy.
408      canned_acl: Optional canned ACL to apply to destination object. Overrides
409                  ACL set in dst_obj_metadata.
410      preconditions: Destination object preconditions for the request.
411      progress_callback: Optional callback function for progress notifications.
412                         Receives calls with arguments
413                         (bytes_transferred, total_size).
414      max_bytes_per_call: Integer describing maximum number of bytes
415                          to rewrite per service call.
416      provider: Cloud storage provider to connect to.  If not present,
417                class-wide default is used.
418      fields: If present, return only these Object metadata fields.
419
420    Raises:
421      ArgumentException for errors during input validation.
422      ServiceException for errors interacting with cloud storage providers.
423
424    Returns:
425      Object object for newly created destination object.
426    """
427    raise NotImplementedError('CopyObject must be overloaded')
428
429  def ComposeObject(self, src_objs_metadata, dst_obj_metadata,
430                    preconditions=None, provider=None, fields=None):
431    """Composes an object in the cloud.
432
433    Args:
434      src_objs_metadata: List of ComposeRequest.SourceObjectsValueListEntries
435                         specifying the objects to compose.
436      dst_obj_metadata: Metadata for the destination object including bucket
437                        and object name.
438      preconditions: Destination object preconditions for the request.
439      provider: Cloud storage provider to connect to.  If not present,
440                class-wide default is used.
441      fields: If present, return only these Object metadata fields.
442
443    Raises:
444      ArgumentException for errors during input validation.
445      ServiceException for errors interacting with cloud storage providers.
446
447    Returns:
448      Composed object metadata.
449    """
450    raise NotImplementedError('ComposeObject must be overloaded')
451
452  def DeleteObject(self, bucket_name, object_name, preconditions=None,
453                   generation=None, provider=None):
454    """Deletes an object.
455
456    Args:
457      bucket_name: Name of the containing bucket.
458      object_name: Name of the object to delete.
459      preconditions: Preconditions for the request.
460      generation: Generation (or version) of the object to delete; if None,
461                  deletes the live object.
462      provider: Cloud storage provider to connect to.  If not present,
463                class-wide default is used.
464
465    Raises:
466      ArgumentException for errors during input validation.
467      ServiceException for errors interacting with cloud storage providers.
468
469    Returns:
470      None.
471    """
472    raise NotImplementedError('DeleteObject must be overloaded')
473
474  def WatchBucket(self, bucket_name, address, channel_id, token=None,
475                  provider=None, fields=None):
476    """Creates a notification subscription for changes to objects in a bucket.
477
478    Args:
479      bucket_name: Bucket containing the objects.
480      address: Address to which to send notifications.
481      channel_id: Unique ID string for the channel.
482      token: If present, token string is delivered with each notification.
483      provider: Cloud storage provider to connect to.  If not present,
484                class-wide default is used.
485      fields: If present, return only these Channel metadata fields.
486
487    Raises:
488      ArgumentException for errors during input validation.
489      ServiceException for errors interacting with cloud storage providers.
490
491    Returns:
492      Channel object describing the notification subscription.
493    """
494    raise NotImplementedError('WatchBucket must be overloaded')
495
496  def StopChannel(self, channel_id, resource_id, provider=None):
497    """Stops a notification channel.
498
499    Args:
500      channel_id: Unique ID string for the channel.
501      resource_id: Version-agnostic ID string for the channel.
502      provider: Cloud storage provider to connect to.  If not present,
503                class-wide default is used.
504
505    Raises:
506      ArgumentException for errors during input validation.
507      ServiceException for errors interacting with cloud storage providers.
508
509    Returns:
510      None.
511    """
512    raise NotImplementedError('StopChannel must be overloaded')
513
514
515class Preconditions(object):
516  """Preconditions class for specifying preconditions to cloud API requests."""
517
518  def __init__(self, gen_match=None, meta_gen_match=None):
519    """Instantiates a Preconditions object.
520
521    Args:
522      gen_match: Perform request only if generation of target object
523                 matches the given integer. Ignored for bucket requests.
524      meta_gen_match: Perform request only if metageneration of target
525                      object/bucket matches the given integer.
526    """
527    self.gen_match = gen_match
528    self.meta_gen_match = meta_gen_match
529
530
531class ArgumentException(Exception):
532  """Exception raised when arguments to a Cloud API method are invalid.
533
534    This exception is never raised as a result of a failed call to a cloud
535    storage provider.
536  """
537
538  def __init__(self, reason):
539    Exception.__init__(self)
540    self.reason = reason
541
542  def __repr__(self):
543    return str(self)
544
545  def __str__(self):
546    return '%s: %s' % (self.__class__.__name__, self.reason)
547
548
549class ProjectIdException(ArgumentException):
550  """Exception raised when a Project ID argument is required but not present."""
551
552
553class ServiceException(Exception):
554  """Exception raised when a cloud storage provider request fails.
555
556    This exception is raised only as a result of a failed remote call.
557  """
558
559  def __init__(self, reason, status=None, body=None):
560    Exception.__init__(self)
561    self.reason = reason
562    self.status = status
563    self.body = body
564
565  def __repr__(self):
566    return str(self)
567
568  def __str__(self):
569    message = '%s:' % self.__class__.__name__
570    if self.status:
571      message += ' %s' % self.status
572    message += ' %s' % self.reason
573    if self.body:
574      message += '\n%s' % self.body
575    return message
576
577
578class RetryableServiceException(ServiceException):
579  """Exception class for retryable exceptions."""
580
581
582class ResumableDownloadException(RetryableServiceException):
583  """Exception raised for res. downloads that can be retried later."""
584
585
586class ResumableUploadException(RetryableServiceException):
587  """Exception raised for res. uploads that can be retried w/ same upload ID."""
588
589
590class ResumableUploadStartOverException(RetryableServiceException):
591  """Exception raised for res. uploads that can be retried w/ new upload ID."""
592
593
594class ResumableUploadAbortException(ServiceException):
595  """Exception raised for resumable uploads that cannot be retried later."""
596
597
598class AuthenticationException(ServiceException):
599  """Exception raised for errors during the authentication process."""
600
601
602class PreconditionException(ServiceException):
603  """Exception raised for precondition failures."""
604
605
606class NotFoundException(ServiceException):
607  """Exception raised when a resource is not found (404)."""
608
609
610class BucketNotFoundException(NotFoundException):
611  """Exception raised when a bucket resource is not found (404)."""
612
613  def __init__(self, reason, bucket_name, status=None, body=None):
614    super(BucketNotFoundException, self).__init__(reason, status=status,
615                                                  body=body)
616    self.bucket_name = bucket_name
617
618
619class NotEmptyException(ServiceException):
620  """Exception raised when trying to delete a bucket is not empty."""
621
622
623class BadRequestException(ServiceException):
624  """Exception raised for malformed requests.
625
626    Where it is possible to detect invalid arguments prior to sending them
627    to the server, an ArgumentException should be raised instead.
628  """
629
630
631class AccessDeniedException(ServiceException):
632  """Exception raised  when authenticated user has insufficient access rights.
633
634    This is raised when the authentication process succeeded but the
635    authenticated user does not have access rights to the requested resource.
636  """
637
638
639