OLD | NEW |
| (Empty) |
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 delegator for interacting with cloud storage providers.""" | |
16 | |
17 from __future__ import absolute_import | |
18 | |
19 import boto | |
20 from boto import config | |
21 from gslib.cloud_api import ArgumentException | |
22 from gslib.cloud_api import CloudApi | |
23 from gslib.cs_api_map import ApiMapConstants | |
24 from gslib.cs_api_map import ApiSelector | |
25 | |
26 | |
27 class CloudApiDelegator(CloudApi): | |
28 """Class that handles delegating requests to gsutil Cloud API implementations. | |
29 | |
30 This class is responsible for determining at runtime which gsutil Cloud API | |
31 implementation should service the request based on the Cloud storage provider, | |
32 command-level API support, and configuration file override. | |
33 | |
34 During initialization it takes as an argument a gsutil_api_map which maps | |
35 providers to their default and supported gsutil Cloud API implementations | |
36 (see comments in cs_api_map for details). | |
37 | |
38 Instantiation of multiple delegators per-thread is required for multiprocess | |
39 and/or multithreaded operations. Calling methods on the same delegator in | |
40 multiple threads is unsafe. | |
41 """ | |
42 | |
43 def __init__(self, bucket_storage_uri_class, gsutil_api_map, logger, | |
44 provider=None, debug=0): | |
45 """Performs necessary setup for delegating cloud storage requests. | |
46 | |
47 This function has different arguments than the gsutil Cloud API __init__ | |
48 function because of the delegation responsibilties of this class. | |
49 | |
50 Args: | |
51 bucket_storage_uri_class: boto storage_uri class, used by APIs that | |
52 provide boto translation or mocking. | |
53 gsutil_api_map: Map of providers and API selector tuples to api classes | |
54 which can be used to communicate with those providers. | |
55 logger: logging.logger for outputting log messages. | |
56 provider: Default provider prefix describing cloud storage provider to | |
57 connect to. | |
58 debug: Debug level for the API implementation (0..3). | |
59 """ | |
60 super(CloudApiDelegator, self).__init__(bucket_storage_uri_class, logger, | |
61 provider=provider, debug=debug) | |
62 self.api_map = gsutil_api_map | |
63 self.prefer_api = boto.config.get('GSUtil', 'prefer_api', '').upper() | |
64 self.loaded_apis = {} | |
65 | |
66 if not self.api_map[ApiMapConstants.API_MAP]: | |
67 raise ArgumentException('No apiclass supplied for gsutil Cloud API map.') | |
68 | |
69 def _GetApi(self, provider): | |
70 """Returns a valid CloudApi for use by the caller. | |
71 | |
72 This function lazy-loads connection and credentials using the API map | |
73 and credential store provided during class initialization. | |
74 | |
75 Args: | |
76 provider: Provider to load API for. If None, class-wide default is used. | |
77 | |
78 Raises: | |
79 ArgumentException if there is no matching API available in the API map. | |
80 | |
81 Returns: | |
82 Valid API instance that can be used to communicate with the Cloud | |
83 Storage provider. | |
84 """ | |
85 provider = provider or self.provider | |
86 if not provider: | |
87 raise ArgumentException('No provider selected for _GetApi') | |
88 | |
89 provider = str(provider) | |
90 if provider not in self.loaded_apis: | |
91 self.loaded_apis[provider] = {} | |
92 | |
93 api_selector = self.GetApiSelector(provider) | |
94 if api_selector not in self.loaded_apis[provider]: | |
95 # Need to load the API. | |
96 self._LoadApi(provider, api_selector) | |
97 | |
98 return self.loaded_apis[provider][api_selector] | |
99 | |
100 def _LoadApi(self, provider, api_selector): | |
101 """Loads a CloudApi into the loaded_apis map for this class. | |
102 | |
103 Args: | |
104 provider: Provider to load the API for. | |
105 api_selector: cs_api_map.ApiSelector defining the API type. | |
106 """ | |
107 if provider not in self.api_map[ApiMapConstants.API_MAP]: | |
108 raise ArgumentException( | |
109 'gsutil Cloud API map contains no entry for provider %s.' % provider) | |
110 if api_selector not in self.api_map[ApiMapConstants.API_MAP][provider]: | |
111 raise ArgumentException( | |
112 'gsutil Cloud API map does not support API %s for provider %s.' % | |
113 (api_selector, provider)) | |
114 self.loaded_apis[provider][api_selector] = ( | |
115 self.api_map[ApiMapConstants.API_MAP][provider][api_selector]( | |
116 self.bucket_storage_uri_class, | |
117 self.logger, | |
118 provider=provider, | |
119 debug=self.debug)) | |
120 | |
121 def GetApiSelector(self, provider=None): | |
122 """Returns a cs_api_map.ApiSelector based on input and configuration. | |
123 | |
124 Args: | |
125 provider: Provider to return the ApiSelector for. If None, class-wide | |
126 default is used. | |
127 | |
128 Returns: | |
129 cs_api_map.ApiSelector that will be used for calls to the delegator | |
130 for this provider. | |
131 """ | |
132 selected_provider = provider or self.provider | |
133 if not selected_provider: | |
134 raise ArgumentException('No provider selected for CloudApi') | |
135 | |
136 if (selected_provider not in self.api_map[ApiMapConstants.DEFAULT_MAP] or | |
137 self.api_map[ApiMapConstants.DEFAULT_MAP][selected_provider] not in | |
138 self.api_map[ApiMapConstants.API_MAP][selected_provider]): | |
139 raise ArgumentException('No default api available for provider %s' % | |
140 selected_provider) | |
141 | |
142 if selected_provider not in self.api_map[ApiMapConstants.SUPPORT_MAP]: | |
143 raise ArgumentException('No supported apis available for provider %s' % | |
144 selected_provider) | |
145 | |
146 api = self.api_map[ApiMapConstants.DEFAULT_MAP][selected_provider] | |
147 | |
148 # If we have only HMAC credentials for Google Cloud Storage, we must use | |
149 # the XML API as the JSON API does not support HMAC. | |
150 # | |
151 # Technically if we have only HMAC credentials, we should still be able to | |
152 # access public read resources via the JSON API, but the XML API can do | |
153 # that just as well. It is better to use it than inspect the credentials on | |
154 # every HTTP call. | |
155 if (provider == 'gs' and | |
156 not config.has_option('Credentials', 'gs_oauth2_refresh_token') and | |
157 not (config.has_option('Credentials', 'gs_service_client_id') | |
158 and config.has_option('Credentials', 'gs_service_key_file')) and | |
159 (config.has_option('Credentials', 'gs_access_key_id') | |
160 and config.has_option('Credentials', 'gs_secret_access_key'))): | |
161 api = ApiSelector.XML | |
162 # Try to force the user's preference to a supported API. | |
163 elif self.prefer_api in (self.api_map[ApiMapConstants.SUPPORT_MAP] | |
164 [selected_provider]): | |
165 api = self.prefer_api | |
166 return api | |
167 | |
168 # For function docstrings, see CloudApi class. | |
169 def GetBucket(self, bucket_name, provider=None, fields=None): | |
170 return self._GetApi(provider).GetBucket(bucket_name, fields=fields) | |
171 | |
172 def ListBuckets(self, project_id=None, provider=None, fields=None): | |
173 return self._GetApi(provider).ListBuckets(project_id=project_id, | |
174 fields=fields) | |
175 | |
176 def PatchBucket(self, bucket_name, metadata, canned_acl=None, | |
177 canned_def_acl=None, preconditions=None, provider=None, | |
178 fields=None): | |
179 return self._GetApi(provider).PatchBucket( | |
180 bucket_name, metadata, canned_acl=canned_acl, | |
181 canned_def_acl=canned_def_acl, preconditions=preconditions, | |
182 fields=fields) | |
183 | |
184 def CreateBucket(self, bucket_name, project_id=None, metadata=None, | |
185 provider=None, fields=None): | |
186 return self._GetApi(provider).CreateBucket( | |
187 bucket_name, project_id=project_id, metadata=metadata, fields=fields) | |
188 | |
189 def DeleteBucket(self, bucket_name, preconditions=None, provider=None): | |
190 return self._GetApi(provider).DeleteBucket(bucket_name, | |
191 preconditions=preconditions) | |
192 | |
193 def ListObjects(self, bucket_name, prefix=None, delimiter=None, | |
194 all_versions=None, provider=None, fields=None): | |
195 return self._GetApi(provider).ListObjects( | |
196 bucket_name, prefix=prefix, delimiter=delimiter, | |
197 all_versions=all_versions, fields=fields) | |
198 | |
199 def GetObjectMetadata(self, bucket_name, object_name, generation=None, | |
200 provider=None, fields=None): | |
201 return self._GetApi(provider).GetObjectMetadata( | |
202 bucket_name, object_name, generation=generation, fields=fields) | |
203 | |
204 def PatchObjectMetadata(self, bucket_name, object_name, metadata, | |
205 canned_acl=None, generation=None, preconditions=None, | |
206 provider=None, fields=None): | |
207 return self._GetApi(provider).PatchObjectMetadata( | |
208 bucket_name, object_name, metadata, canned_acl=canned_acl, | |
209 generation=generation, preconditions=preconditions, fields=fields) | |
210 | |
211 def GetObjectMedia( | |
212 self, bucket_name, object_name, download_stream, provider=None, | |
213 generation=None, object_size=None, | |
214 download_strategy=CloudApi.DownloadStrategy.ONE_SHOT, | |
215 start_byte=0, end_byte=None, progress_callback=None, | |
216 serialization_data=None, digesters=None): | |
217 return self._GetApi(provider).GetObjectMedia( | |
218 bucket_name, object_name, download_stream, | |
219 download_strategy=download_strategy, start_byte=start_byte, | |
220 end_byte=end_byte, generation=generation, object_size=object_size, | |
221 progress_callback=progress_callback, | |
222 serialization_data=serialization_data, digesters=digesters) | |
223 | |
224 def UploadObject(self, upload_stream, object_metadata, size=None, | |
225 canned_acl=None, preconditions=None, progress_callback=None, | |
226 provider=None, fields=None): | |
227 return self._GetApi(provider).UploadObject( | |
228 upload_stream, object_metadata, size=size, canned_acl=canned_acl, | |
229 preconditions=preconditions, progress_callback=progress_callback, | |
230 fields=fields) | |
231 | |
232 def UploadObjectStreaming(self, upload_stream, object_metadata, | |
233 canned_acl=None, preconditions=None, | |
234 progress_callback=None, provider=None, fields=None): | |
235 return self._GetApi(provider).UploadObjectStreaming( | |
236 upload_stream, object_metadata, canned_acl=canned_acl, | |
237 preconditions=preconditions, progress_callback=progress_callback, | |
238 fields=fields) | |
239 | |
240 def UploadObjectResumable( | |
241 self, upload_stream, object_metadata, canned_acl=None, preconditions=None, | |
242 provider=None, fields=None, size=None, serialization_data=None, | |
243 tracker_callback=None, progress_callback=None): | |
244 return self._GetApi(provider).UploadObjectResumable( | |
245 upload_stream, object_metadata, canned_acl=canned_acl, | |
246 preconditions=preconditions, size=size, fields=fields, | |
247 serialization_data=serialization_data, | |
248 tracker_callback=tracker_callback, progress_callback=progress_callback) | |
249 | |
250 def CopyObject(self, src_obj_metadata, dst_obj_metadata, src_generation=None, | |
251 canned_acl=None, preconditions=None, progress_callback=None, | |
252 max_bytes_per_call=None, provider=None, fields=None): | |
253 return self._GetApi(provider).CopyObject( | |
254 src_obj_metadata, dst_obj_metadata, src_generation=src_generation, | |
255 canned_acl=canned_acl, preconditions=preconditions, | |
256 progress_callback=progress_callback, | |
257 max_bytes_per_call=max_bytes_per_call, fields=fields) | |
258 | |
259 def ComposeObject(self, src_objs_metadata, dst_obj_metadata, | |
260 preconditions=None, provider=None, fields=None): | |
261 return self._GetApi(provider).ComposeObject( | |
262 src_objs_metadata, dst_obj_metadata, preconditions=preconditions, | |
263 fields=fields) | |
264 | |
265 def DeleteObject(self, bucket_name, object_name, preconditions=None, | |
266 generation=None, provider=None): | |
267 return self._GetApi(provider).DeleteObject( | |
268 bucket_name, object_name, preconditions=preconditions, | |
269 generation=generation) | |
270 | |
271 def WatchBucket(self, bucket_name, address, channel_id, token=None, | |
272 provider=None, fields=None): | |
273 return self._GetApi(provider).WatchBucket( | |
274 bucket_name, address, channel_id, token=token, fields=fields) | |
275 | |
276 def StopChannel(self, channel_id, resource_id, provider=None): | |
277 return self._GetApi(provider).StopChannel(channel_id, resource_id) | |
278 | |
279 def XmlPassThroughGetAcl(self, storage_url, def_obj_acl=False, provider=None): | |
280 """XML compatibility function for getting ACLs. | |
281 | |
282 Args: | |
283 storage_url: StorageUrl object. | |
284 def_obj_acl: If true, get the default object ACL on a bucket. | |
285 provider: Cloud storage provider to connect to. If not present, | |
286 class-wide default is used. | |
287 | |
288 Raises: | |
289 ArgumentException for errors during input validation. | |
290 ServiceException for errors interacting with cloud storage providers. | |
291 | |
292 Returns: | |
293 ACL XML for the resource specified by storage_url. | |
294 """ | |
295 return self._GetApi(provider).XmlPassThroughGetAcl(storage_url, | |
296 def_obj_acl=def_obj_acl) | |
297 | |
298 def XmlPassThroughSetAcl(self, acl_text, storage_url, canned=True, | |
299 def_obj_acl=False, provider=None): | |
300 """XML compatibility function for setting ACLs. | |
301 | |
302 Args: | |
303 acl_text: XML ACL or canned ACL string. | |
304 storage_url: StorageUrl object. | |
305 canned: If true, acl_text is treated as a canned ACL string. | |
306 def_obj_acl: If true, set the default object ACL on a bucket. | |
307 provider: Cloud storage provider to connect to. If not present, | |
308 class-wide default is used. | |
309 | |
310 Raises: | |
311 ArgumentException for errors during input validation. | |
312 ServiceException for errors interacting with cloud storage providers. | |
313 | |
314 Returns: | |
315 None. | |
316 """ | |
317 self._GetApi(provider).XmlPassThroughSetAcl( | |
318 acl_text, storage_url, canned=canned, def_obj_acl=def_obj_acl) | |
319 | |
320 def XmlPassThroughGetCors(self, storage_url, provider=None): | |
321 """XML compatibility function for getting CORS configuration on a bucket. | |
322 | |
323 Args: | |
324 storage_url: StorageUrl object. | |
325 provider: Cloud storage provider to connect to. If not present, | |
326 class-wide default is used. | |
327 | |
328 Raises: | |
329 ArgumentException for errors during input validation. | |
330 ServiceException for errors interacting with cloud storage providers. | |
331 | |
332 Returns: | |
333 CORS configuration XML for the bucket specified by storage_url. | |
334 """ | |
335 return self._GetApi(provider).XmlPassThroughGetCors(storage_url) | |
336 | |
337 def XmlPassThroughSetCors(self, cors_text, storage_url, provider=None): | |
338 """XML compatibility function for setting CORS configuration on a bucket. | |
339 | |
340 Args: | |
341 cors_text: Raw CORS XML string. | |
342 storage_url: StorageUrl object. | |
343 provider: Cloud storage provider to connect to. If not present, | |
344 class-wide default is used. | |
345 | |
346 Raises: | |
347 ArgumentException for errors during input validation. | |
348 ServiceException for errors interacting with cloud storage providers. | |
349 | |
350 Returns: | |
351 None. | |
352 """ | |
353 self._GetApi(provider).XmlPassThroughSetCors(cors_text, storage_url) | |
354 | |
355 def XmlPassThroughGetLifecycle(self, storage_url, provider=None): | |
356 """XML compatibility function for getting lifecycle config on a bucket. | |
357 | |
358 Args: | |
359 storage_url: StorageUrl object. | |
360 provider: Cloud storage provider to connect to. If not present, | |
361 class-wide default is used. | |
362 | |
363 Raises: | |
364 ArgumentException for errors during input validation. | |
365 ServiceException for errors interacting with cloud storage providers. | |
366 | |
367 Returns: | |
368 Lifecycle configuration XML for the bucket specified by storage_url. | |
369 """ | |
370 return self._GetApi(provider).XmlPassThroughGetLifecycle(storage_url) | |
371 | |
372 def XmlPassThroughSetLifecycle(self, lifecycle_text, storage_url, | |
373 provider=None): | |
374 """XML compatibility function for setting CORS configuration on a bucket. | |
375 | |
376 Args: | |
377 lifecycle_text: Raw lifecycle configuration XML string. | |
378 storage_url: StorageUrl object. | |
379 provider: Cloud storage provider to connect to. If not present, | |
380 class-wide default is used. | |
381 | |
382 Raises: | |
383 ArgumentException for errors during input validation. | |
384 ServiceException for errors interacting with cloud storage providers. | |
385 | |
386 Returns: | |
387 None. | |
388 """ | |
389 self._GetApi(provider).XmlPassThroughSetLifecycle(lifecycle_text, | |
390 storage_url) | |
391 | |
392 def XmlPassThroughGetLogging(self, storage_url, provider=None): | |
393 """XML compatibility function for getting logging configuration on a bucket. | |
394 | |
395 Args: | |
396 storage_url: StorageUrl object. | |
397 provider: Cloud storage provider to connect to. If not present, | |
398 class-wide default is used. | |
399 | |
400 Raises: | |
401 ArgumentException for errors during input validation. | |
402 ServiceException for errors interacting with cloud storage providers. | |
403 | |
404 Returns: | |
405 Logging configuration XML for the bucket specified by storage_url. | |
406 """ | |
407 return self._GetApi(provider).XmlPassThroughGetLogging(storage_url) | |
408 | |
409 def XmlPassThroughGetWebsite(self, storage_url, provider=None): | |
410 """XML compatibility function for getting website configuration on a bucket. | |
411 | |
412 Args: | |
413 storage_url: StorageUrl object. | |
414 provider: Cloud storage provider to connect to. If not present, | |
415 class-wide default is used. | |
416 | |
417 Raises: | |
418 ArgumentException for errors during input validation. | |
419 ServiceException for errors interacting with cloud storage providers. | |
420 | |
421 Returns: | |
422 Website configuration XML for the bucket specified by storage_url. | |
423 """ | |
424 return self._GetApi(provider).XmlPassThroughGetWebsite(storage_url) | |
425 | |
OLD | NEW |