1# ------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for 4# license information. 5# -------------------------------------------------------------------------- 6 7import functools 8from typing import ( # pylint: disable=unused-import 9 Union, Optional, Any, Iterable, Dict, List, 10 TYPE_CHECKING 11) 12 13from azure.core.tracing.decorator import distributed_trace 14from azure.core.pipeline import AsyncPipeline 15from azure.core.tracing.decorator_async import distributed_trace_async 16from azure.core.async_paging import AsyncItemPaged 17 18from .._shared.models import LocationMode 19from .._shared.policies_async import ExponentialRetry 20from .._shared.base_client_async import AsyncStorageAccountHostsMixin, AsyncTransportWrapper 21from .._shared.response_handlers import return_response_headers, process_storage_error 22from .._shared.parser import _to_utc_datetime 23from .._shared.response_handlers import parse_to_internal_user_delegation_key 24from .._generated.aio import AzureBlobStorage 25from .._generated.models import StorageErrorException, StorageServiceProperties, KeyInfo 26from .._blob_service_client import BlobServiceClient as BlobServiceClientBase 27from ._container_client_async import ContainerClient 28from ._blob_client_async import BlobClient 29from .._models import ( 30 ContainerProperties, 31 service_stats_deserialize, 32 service_properties_deserialize, 33) 34from ._models import ContainerPropertiesPaged 35 36if TYPE_CHECKING: 37 from datetime import datetime 38 from azure.core.pipeline.transport import HttpTransport 39 from azure.core.pipeline.policies import HTTPPolicy 40 from .._shared.models import AccountSasPermissions, ResourceTypes, UserDelegationKey 41 from ._lease_async import BlobLeaseClient 42 from .._models import ( 43 BlobProperties, 44 PublicAccess, 45 BlobAnalyticsLogging, 46 Metrics, 47 CorsRule, 48 RetentionPolicy, 49 StaticWebsite, 50 ) 51 52 53class BlobServiceClient(AsyncStorageAccountHostsMixin, BlobServiceClientBase): 54 """A client to interact with the Blob Service at the account level. 55 56 This client provides operations to retrieve and configure the account properties 57 as well as list, create and delete containers within the account. 58 For operations relating to a specific container or blob, clients for those entities 59 can also be retrieved using the `get_client` functions. 60 61 :param str account_url: 62 The URL to the blob storage account. Any other entities included 63 in the URL path (e.g. container or blob) will be discarded. This URL can be optionally 64 authenticated with a SAS token. 65 :param credential: 66 The credentials with which to authenticate. This is optional if the 67 account URL already has a SAS token. The value can be a SAS token string, an account 68 shared access key, or an instance of a TokenCredentials class from azure.identity. 69 If the URL already has a SAS token, specifying an explicit credential will take priority. 70 :keyword str secondary_hostname: 71 The hostname of the secondary endpoint. 72 :keyword int max_block_size: The maximum chunk size for uploading a block blob in chunks. 73 Defaults to 4*1024*1024, or 4MB. 74 :keyword int max_single_put_size: If the blob size is less than max_single_put_size, then the blob will be 75 uploaded with only one http PUT request. If the blob size is larger than max_single_put_size, 76 the blob will be uploaded in chunks. Defaults to 64*1024*1024, or 64MB. 77 :keyword int min_large_block_upload_threshold: The minimum chunk size required to use the memory efficient 78 algorithm when uploading a block blob. Defaults to 4*1024*1024+1. 79 :keyword bool use_byte_buffer: Use a byte buffer for block blob uploads. Defaults to False. 80 :keyword int max_page_size: The maximum chunk size for uploading a page blob. Defaults to 4*1024*1024, or 4MB. 81 :keyword int max_single_get_size: The maximum size for a blob to be downloaded in a single call, 82 the exceeded part will be downloaded in chunks (could be parallel). Defaults to 32*1024*1024, or 32MB. 83 :keyword int max_chunk_get_size: The maximum chunk size used for downloading a blob. Defaults to 4*1024*1024, 84 or 4MB. 85 86 .. admonition:: Example: 87 88 .. literalinclude:: ../samples/blob_samples_authentication_async.py 89 :start-after: [START create_blob_service_client] 90 :end-before: [END create_blob_service_client] 91 :language: python 92 :dedent: 8 93 :caption: Creating the BlobServiceClient with account url and credential. 94 95 .. literalinclude:: ../samples/blob_samples_authentication_async.py 96 :start-after: [START create_blob_service_client_oauth] 97 :end-before: [END create_blob_service_client_oauth] 98 :language: python 99 :dedent: 8 100 :caption: Creating the BlobServiceClient with Azure Identity credentials. 101 """ 102 103 def __init__( 104 self, account_url, # type: str 105 credential=None, # type: Optional[Any] 106 **kwargs # type: Any 107 ): 108 # type: (...) -> None 109 kwargs['retry_policy'] = kwargs.get('retry_policy') or ExponentialRetry(**kwargs) 110 super(BlobServiceClient, self).__init__( 111 account_url, 112 credential=credential, 113 **kwargs) 114 self._client = AzureBlobStorage(url=self.url, pipeline=self._pipeline) 115 self._loop = kwargs.get('loop', None) 116 117 @distributed_trace_async 118 async def get_user_delegation_key(self, key_start_time, # type: datetime 119 key_expiry_time, # type: datetime 120 **kwargs # type: Any 121 ): 122 # type: (...) -> UserDelegationKey 123 """ 124 Obtain a user delegation key for the purpose of signing SAS tokens. 125 A token credential must be present on the service object for this request to succeed. 126 127 :param ~datetime.datetime key_start_time: 128 A DateTime value. Indicates when the key becomes valid. 129 :param ~datetime.datetime key_expiry_time: 130 A DateTime value. Indicates when the key stops being valid. 131 :keyword int timeout: 132 The timeout parameter is expressed in seconds. 133 :return: The user delegation key. 134 :rtype: ~azure.storage.blob.UserDelegationKey 135 """ 136 key_info = KeyInfo(start=_to_utc_datetime(key_start_time), expiry=_to_utc_datetime(key_expiry_time)) 137 timeout = kwargs.pop('timeout', None) 138 try: 139 user_delegation_key = await self._client.service.get_user_delegation_key(key_info=key_info, 140 timeout=timeout, 141 **kwargs) # type: ignore 142 except StorageErrorException as error: 143 process_storage_error(error) 144 145 return parse_to_internal_user_delegation_key(user_delegation_key) # type: ignore 146 147 @distributed_trace_async 148 async def get_account_information(self, **kwargs): 149 # type: (Any) -> Dict[str, str] 150 """Gets information related to the storage account. 151 152 The information can also be retrieved if the user has a SAS to a container or blob. 153 The keys in the returned dictionary include 'sku_name' and 'account_kind'. 154 155 :returns: A dict of account information (SKU and account type). 156 :rtype: dict(str, str) 157 158 .. admonition:: Example: 159 160 .. literalinclude:: ../samples/blob_samples_service_async.py 161 :start-after: [START get_blob_service_account_info] 162 :end-before: [END get_blob_service_account_info] 163 :language: python 164 :dedent: 12 165 :caption: Getting account information for the blob service. 166 """ 167 try: 168 return await self._client.service.get_account_info(cls=return_response_headers, **kwargs) # type: ignore 169 except StorageErrorException as error: 170 process_storage_error(error) 171 172 @distributed_trace_async 173 async def get_service_stats(self, **kwargs): 174 # type: (Any) -> Dict[str, Any] 175 """Retrieves statistics related to replication for the Blob service. 176 177 It is only available when read-access geo-redundant replication is enabled for 178 the storage account. 179 180 With geo-redundant replication, Azure Storage maintains your data durable 181 in two locations. In both locations, Azure Storage constantly maintains 182 multiple healthy replicas of your data. The location where you read, 183 create, update, or delete data is the primary storage account location. 184 The primary location exists in the region you choose at the time you 185 create an account via the Azure Management Azure classic portal, for 186 example, North Central US. The location to which your data is replicated 187 is the secondary location. The secondary location is automatically 188 determined based on the location of the primary; it is in a second data 189 center that resides in the same region as the primary location. Read-only 190 access is available from the secondary location, if read-access geo-redundant 191 replication is enabled for your storage account. 192 193 :keyword int timeout: 194 The timeout parameter is expressed in seconds. 195 :return: The blob service stats. 196 :rtype: Dict[str, Any] 197 198 .. admonition:: Example: 199 200 .. literalinclude:: ../samples/blob_samples_service_async.py 201 :start-after: [START get_blob_service_stats] 202 :end-before: [END get_blob_service_stats] 203 :language: python 204 :dedent: 12 205 :caption: Getting service stats for the blob service. 206 """ 207 timeout = kwargs.pop('timeout', None) 208 try: 209 stats = await self._client.service.get_statistics( # type: ignore 210 timeout=timeout, use_location=LocationMode.SECONDARY, **kwargs) 211 return service_stats_deserialize(stats) 212 except StorageErrorException as error: 213 process_storage_error(error) 214 215 @distributed_trace_async 216 async def get_service_properties(self, **kwargs): 217 # type: (Any) -> Dict[str, Any] 218 """Gets the properties of a storage account's Blob service, including 219 Azure Storage Analytics. 220 221 :keyword int timeout: 222 The timeout parameter is expressed in seconds. 223 :returns: An object containing blob service properties such as 224 analytics logging, hour/minute metrics, cors rules, etc. 225 :rtype: Dict[str, Any] 226 227 .. admonition:: Example: 228 229 .. literalinclude:: ../samples/blob_samples_service_async.py 230 :start-after: [START get_blob_service_properties] 231 :end-before: [END get_blob_service_properties] 232 :language: python 233 :dedent: 12 234 :caption: Getting service properties for the blob service. 235 """ 236 timeout = kwargs.pop('timeout', None) 237 try: 238 service_props = await self._client.service.get_properties(timeout=timeout, **kwargs) 239 return service_properties_deserialize(service_props) 240 except StorageErrorException as error: 241 process_storage_error(error) 242 243 @distributed_trace_async 244 async def set_service_properties( 245 self, analytics_logging=None, # type: Optional[BlobAnalyticsLogging] 246 hour_metrics=None, # type: Optional[Metrics] 247 minute_metrics=None, # type: Optional[Metrics] 248 cors=None, # type: Optional[List[CorsRule]] 249 target_version=None, # type: Optional[str] 250 delete_retention_policy=None, # type: Optional[RetentionPolicy] 251 static_website=None, # type: Optional[StaticWebsite] 252 **kwargs 253 ): 254 # type: (...) -> None 255 """Sets the properties of a storage account's Blob service, including 256 Azure Storage Analytics. 257 258 If an element (e.g. analytics_logging) is left as None, the 259 existing settings on the service for that functionality are preserved. 260 261 :param analytics_logging: 262 Groups the Azure Analytics Logging settings. 263 :type analytics_logging: ~azure.storage.blob.BlobAnalyticsLogging 264 :param hour_metrics: 265 The hour metrics settings provide a summary of request 266 statistics grouped by API in hourly aggregates for blobs. 267 :type hour_metrics: ~azure.storage.blob.Metrics 268 :param minute_metrics: 269 The minute metrics settings provide request statistics 270 for each minute for blobs. 271 :type minute_metrics: ~azure.storage.blob.Metrics 272 :param cors: 273 You can include up to five CorsRule elements in the 274 list. If an empty list is specified, all CORS rules will be deleted, 275 and CORS will be disabled for the service. 276 :type cors: list[~azure.storage.blob.CorsRule] 277 :param str target_version: 278 Indicates the default version to use for requests if an incoming 279 request's version is not specified. 280 :param delete_retention_policy: 281 The delete retention policy specifies whether to retain deleted blobs. 282 It also specifies the number of days and versions of blob to keep. 283 :type delete_retention_policy: ~azure.storage.blob.RetentionPolicy 284 :param static_website: 285 Specifies whether the static website feature is enabled, 286 and if yes, indicates the index document and 404 error document to use. 287 :type static_website: ~azure.storage.blob.StaticWebsite 288 :keyword int timeout: 289 The timeout parameter is expressed in seconds. 290 :rtype: None 291 292 .. admonition:: Example: 293 294 .. literalinclude:: ../samples/blob_samples_service_async.py 295 :start-after: [START set_blob_service_properties] 296 :end-before: [END set_blob_service_properties] 297 :language: python 298 :dedent: 12 299 :caption: Setting service properties for the blob service. 300 """ 301 props = StorageServiceProperties( 302 logging=analytics_logging, 303 hour_metrics=hour_metrics, 304 minute_metrics=minute_metrics, 305 cors=cors, 306 default_service_version=target_version, 307 delete_retention_policy=delete_retention_policy, 308 static_website=static_website 309 ) 310 timeout = kwargs.pop('timeout', None) 311 try: 312 await self._client.service.set_properties(props, timeout=timeout, **kwargs) 313 except StorageErrorException as error: 314 process_storage_error(error) 315 316 @distributed_trace 317 def list_containers( 318 self, name_starts_with=None, # type: Optional[str] 319 include_metadata=False, # type: Optional[bool] 320 **kwargs 321 ): 322 # type: (...) -> AsyncItemPaged[ContainerProperties] 323 """Returns a generator to list the containers under the specified account. 324 325 The generator will lazily follow the continuation tokens returned by 326 the service and stop when all containers have been returned. 327 328 :param str name_starts_with: 329 Filters the results to return only containers whose names 330 begin with the specified prefix. 331 :param bool include_metadata: 332 Specifies that container metadata to be returned in the response. 333 The default value is `False`. 334 :keyword int results_per_page: 335 The maximum number of container names to retrieve per API 336 call. If the request does not specify the server will return up to 5,000 items. 337 :keyword int timeout: 338 The timeout parameter is expressed in seconds. 339 :returns: An iterable (auto-paging) of ContainerProperties. 340 :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.storage.blob.ContainerProperties] 341 342 .. admonition:: Example: 343 344 .. literalinclude:: ../samples/blob_samples_service_async.py 345 :start-after: [START bsc_list_containers] 346 :end-before: [END bsc_list_containers] 347 :language: python 348 :dedent: 16 349 :caption: Listing the containers in the blob service. 350 """ 351 include = 'metadata' if include_metadata else None 352 timeout = kwargs.pop('timeout', None) 353 results_per_page = kwargs.pop('results_per_page', None) 354 command = functools.partial( 355 self._client.service.list_containers_segment, 356 prefix=name_starts_with, 357 include=include, 358 timeout=timeout, 359 **kwargs) 360 return AsyncItemPaged( 361 command, 362 prefix=name_starts_with, 363 results_per_page=results_per_page, 364 page_iterator_class=ContainerPropertiesPaged 365 ) 366 367 @distributed_trace_async 368 async def create_container( 369 self, name, # type: str 370 metadata=None, # type: Optional[Dict[str, str]] 371 public_access=None, # type: Optional[Union[PublicAccess, str]] 372 **kwargs 373 ): 374 # type: (...) -> ContainerClient 375 """Creates a new container under the specified account. 376 377 If the container with the same name already exists, a ResourceExistsError will 378 be raised. This method returns a client with which to interact with the newly 379 created container. 380 381 :param str name: The name of the container to create. 382 :param metadata: 383 A dict with name-value pairs to associate with the 384 container as metadata. Example: `{'Category':'test'}` 385 :type metadata: dict(str, str) 386 :param public_access: 387 Possible values include: 'container', 'blob'. 388 :type public_access: str or ~azure.storage.blob.PublicAccess 389 :keyword int timeout: 390 The timeout parameter is expressed in seconds. 391 :rtype: ~azure.storage.blob.aio.ContainerClient 392 393 .. admonition:: Example: 394 395 .. literalinclude:: ../samples/blob_samples_service_async.py 396 :start-after: [START bsc_create_container] 397 :end-before: [END bsc_create_container] 398 :language: python 399 :dedent: 16 400 :caption: Creating a container in the blob service. 401 """ 402 container = self.get_container_client(name) 403 timeout = kwargs.pop('timeout', None) 404 kwargs.setdefault('merge_span', True) 405 await container.create_container( 406 metadata=metadata, public_access=public_access, timeout=timeout, **kwargs) 407 return container 408 409 @distributed_trace_async 410 async def delete_container( 411 self, container, # type: Union[ContainerProperties, str] 412 lease=None, # type: Optional[Union[BlobLeaseClient, str]] 413 **kwargs 414 ): 415 # type: (...) -> None 416 """Marks the specified container for deletion. 417 418 The container and any blobs contained within it are later deleted during garbage collection. 419 If the container is not found, a ResourceNotFoundError will be raised. 420 421 :param container: 422 The container to delete. This can either be the name of the container, 423 or an instance of ContainerProperties. 424 :type container: str or ~azure.storage.blob.ContainerProperties 425 :param lease: 426 If specified, delete_container only succeeds if the 427 container's lease is active and matches this ID. 428 Required if the container has an active lease. 429 :paramtype lease: ~azure.storage.blob.aio.BlobLeaseClient or str 430 :keyword ~datetime.datetime if_modified_since: 431 A DateTime value. Azure expects the date value passed in to be UTC. 432 If timezone is included, any non-UTC datetimes will be converted to UTC. 433 If a date is passed in without timezone info, it is assumed to be UTC. 434 Specify this header to perform the operation only 435 if the resource has been modified since the specified time. 436 :keyword ~datetime.datetime if_unmodified_since: 437 A DateTime value. Azure expects the date value passed in to be UTC. 438 If timezone is included, any non-UTC datetimes will be converted to UTC. 439 If a date is passed in without timezone info, it is assumed to be UTC. 440 Specify this header to perform the operation only if 441 the resource has not been modified since the specified date/time. 442 :keyword str etag: 443 An ETag value, or the wildcard character (*). Used to check if the resource has changed, 444 and act according to the condition specified by the `match_condition` parameter. 445 :keyword ~azure.core.MatchConditions match_condition: 446 The match condition to use upon the etag. 447 :keyword int timeout: 448 The timeout parameter is expressed in seconds. 449 :rtype: None 450 451 .. admonition:: Example: 452 453 .. literalinclude:: ../samples/blob_samples_service_async.py 454 :start-after: [START bsc_delete_container] 455 :end-before: [END bsc_delete_container] 456 :language: python 457 :dedent: 16 458 :caption: Deleting a container in the blob service. 459 """ 460 container = self.get_container_client(container) # type: ignore 461 kwargs.setdefault('merge_span', True) 462 timeout = kwargs.pop('timeout', None) 463 await container.delete_container( # type: ignore 464 lease=lease, 465 timeout=timeout, 466 **kwargs) 467 468 def get_container_client(self, container): 469 # type: (Union[ContainerProperties, str]) -> ContainerClient 470 """Get a client to interact with the specified container. 471 472 The container need not already exist. 473 474 :param container: 475 The container. This can either be the name of the container, 476 or an instance of ContainerProperties. 477 :type container: str or ~azure.storage.blob.ContainerProperties 478 :returns: A ContainerClient. 479 :rtype: ~azure.storage.blob.aio.ContainerClient 480 481 .. admonition:: Example: 482 483 .. literalinclude:: ../samples/blob_samples_service_async.py 484 :start-after: [START bsc_get_container_client] 485 :end-before: [END bsc_get_container_client] 486 :language: python 487 :dedent: 12 488 :caption: Getting the container client to interact with a specific container. 489 """ 490 try: 491 container_name = container.name 492 except AttributeError: 493 container_name = container 494 _pipeline = AsyncPipeline( 495 transport=AsyncTransportWrapper(self._pipeline._transport), # pylint: disable = protected-access 496 policies=self._pipeline._impl_policies # pylint: disable = protected-access 497 ) 498 return ContainerClient( 499 self.url, container_name=container_name, 500 credential=self.credential, _configuration=self._config, 501 _pipeline=_pipeline, _location_mode=self._location_mode, _hosts=self._hosts, 502 require_encryption=self.require_encryption, key_encryption_key=self.key_encryption_key, 503 key_resolver_function=self.key_resolver_function, loop=self._loop) 504 505 def get_blob_client( 506 self, container, # type: Union[ContainerProperties, str] 507 blob, # type: Union[BlobProperties, str] 508 snapshot=None # type: Optional[Union[Dict[str, Any], str]] 509 ): 510 # type: (...) -> BlobClient 511 """Get a client to interact with the specified blob. 512 513 The blob need not already exist. 514 515 :param container: 516 The container that the blob is in. This can either be the name of the container, 517 or an instance of ContainerProperties. 518 :type container: str or ~azure.storage.blob.ContainerProperties 519 :param blob: 520 The blob with which to interact. This can either be the name of the blob, 521 or an instance of BlobProperties. 522 :type blob: str or ~azure.storage.blob.BlobProperties 523 :param snapshot: 524 The optional blob snapshot on which to operate. This can either be the ID of the snapshot, 525 or a dictionary output returned by 526 :func:`~azure.storage.blob.aio.BlobClient.create_snapshot()`. 527 :type snapshot: str or dict(str, Any) 528 :returns: A BlobClient. 529 :rtype: ~azure.storage.blob.aio.BlobClient 530 531 .. admonition:: Example: 532 533 .. literalinclude:: ../samples/blob_samples_service_async.py 534 :start-after: [START bsc_get_blob_client] 535 :end-before: [END bsc_get_blob_client] 536 :language: python 537 :dedent: 16 538 :caption: Getting the blob client to interact with a specific blob. 539 """ 540 try: 541 container_name = container.name 542 except AttributeError: 543 container_name = container 544 545 try: 546 blob_name = blob.name 547 except AttributeError: 548 blob_name = blob 549 _pipeline = AsyncPipeline( 550 transport=AsyncTransportWrapper(self._pipeline._transport), # pylint: disable = protected-access 551 policies=self._pipeline._impl_policies # pylint: disable = protected-access 552 ) 553 return BlobClient( # type: ignore 554 self.url, container_name=container_name, blob_name=blob_name, snapshot=snapshot, 555 credential=self.credential, _configuration=self._config, 556 _pipeline=_pipeline, _location_mode=self._location_mode, _hosts=self._hosts, 557 require_encryption=self.require_encryption, key_encryption_key=self.key_encryption_key, 558 key_resolver_function=self.key_resolver_function, loop=self._loop) 559