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# --------------------------------------------------------------------------
6import sys
7from abc import ABCMeta
8
9from azure.common import AzureHttpError
10
11from ..common._auth import (
12    _StorageSASAuthentication,
13    _StorageSharedKeyAuthentication,
14    _StorageNoAuthentication,
15)
16from ..common._common_conversion import (
17    _int_to_str,
18    _to_str,
19    _datetime_to_utc_string,
20)
21from ..common._connection import _ServiceParameters
22from ..common._constants import (
23    SERVICE_HOST_BASE,
24    DEFAULT_PROTOCOL,
25)
26from ..common._deserialization import (
27    _convert_xml_to_service_properties,
28    _parse_metadata,
29    _parse_properties,
30    _convert_xml_to_service_stats,
31    _parse_length_from_content_range,
32)
33from ..common._error import (
34    _dont_fail_not_exist,
35    _dont_fail_on_exist,
36    _validate_not_none,
37    _validate_decryption_required,
38    _validate_access_policies,
39    _ERROR_PARALLEL_NOT_SEEKABLE,
40    _validate_user_delegation_key,
41)
42from ..common._http import HTTPRequest
43from ..common._serialization import (
44    _get_request_body,
45    _convert_signed_identifiers_to_xml,
46    _convert_service_properties_to_xml,
47    _add_metadata_headers,
48)
49from ..common.models import (
50    Services,
51    ListGenerator,
52    _OperationContext,
53)
54from .sharedaccesssignature import (
55    BlobSharedAccessSignature,
56)
57from ..common.storageclient import StorageClient
58from ._deserialization import (
59    _convert_xml_to_containers,
60    _parse_blob,
61    _convert_xml_to_blob_list,
62    _convert_xml_to_blob_name_list,
63    _parse_container,
64    _parse_snapshot_blob,
65    _parse_lease,
66    _convert_xml_to_signed_identifiers_and_access,
67    _parse_base_properties,
68    _parse_account_information,
69    _convert_xml_to_user_delegation_key,
70)
71from ._download_chunking import _download_blob_chunks
72from ._error import (
73    _ERROR_INVALID_LEASE_DURATION,
74    _ERROR_INVALID_LEASE_BREAK_PERIOD,
75)
76from ._serialization import (
77    _get_path,
78    _validate_and_format_range_headers,
79    _convert_delegation_key_info_to_xml,
80)
81from .models import (
82    BlobProperties,
83    _LeaseActions,
84    ContainerPermissions,
85    BlobPermissions,
86)
87
88from ._constants import (
89    X_MS_VERSION,
90    __version__ as package_version,
91)
92
93_CONTAINER_ALREADY_EXISTS_ERROR_CODE = 'ContainerAlreadyExists'
94_BLOB_NOT_FOUND_ERROR_CODE = 'BlobNotFound'
95_CONTAINER_NOT_FOUND_ERROR_CODE = 'ContainerNotFound'
96
97if sys.version_info >= (3,):
98    from io import BytesIO
99else:
100    from cStringIO import StringIO as BytesIO
101
102
103class BaseBlobService(StorageClient):
104    '''
105    This is the main class managing Blob resources.
106
107    The Blob service stores text and binary data as blobs in the cloud.
108    The Blob service offers the following three resources: the storage account,
109    containers, and blobs. Within your storage account, containers provide a
110    way to organize sets of blobs. For more information please see:
111    https://msdn.microsoft.com/en-us/library/azure/ee691964.aspx
112
113    :ivar int MAX_SINGLE_GET_SIZE:
114        The size of the first range get performed by get_blob_to_* methods if
115        max_connections is greater than 1. Less data will be returned if the
116        blob is smaller than this.
117    :ivar int MAX_CHUNK_GET_SIZE:
118        The size of subsequent range gets performed by get_blob_to_* methods if
119        max_connections is greater than 1 and the blob is larger than MAX_SINGLE_GET_SIZE.
120        Less data will be returned if the remainder of the blob is smaller than
121        this. If this is set to larger than 4MB, content_validation will throw an
122        error if enabled. However, if content_validation is not desired a size
123        greater than 4MB may be optimal. Setting this below 4MB is not recommended.
124    :ivar object key_encryption_key:
125        The key-encryption-key optionally provided by the user. If provided, will be used to
126        encrypt/decrypt in supported methods.
127        For methods requiring decryption, either the key_encryption_key OR the resolver must be provided.
128        If both are provided, the resolver will take precedence.
129        Must implement the following methods for APIs requiring encryption:
130        wrap_key(key)--wraps the specified key (bytes) using an algorithm of the user's choice. Returns the encrypted key as bytes.
131        get_key_wrap_algorithm()--returns the algorithm used to wrap the specified symmetric key.
132        get_kid()--returns a string key id for this key-encryption-key.
133        Must implement the following methods for APIs requiring decryption:
134        unwrap_key(key, algorithm)--returns the unwrapped form of the specified symmetric key using the string-specified algorithm.
135        get_kid()--returns a string key id for this key-encryption-key.
136    :ivar function key_resolver_function(kid):
137        A function to resolve keys optionally provided by the user. If provided, will be used to decrypt in supported methods.
138        For methods requiring decryption, either the key_encryption_key OR
139        the resolver must be provided. If both are provided, the resolver will take precedence.
140        It uses the kid string to return a key-encryption-key implementing the interface defined above.
141    :ivar bool require_encryption:
142        A flag that may be set to ensure that all messages successfully uploaded to the queue and all those downloaded and
143        successfully read from the queue are/were encrypted while on the server. If this flag is set, all required
144        parameters for encryption/decryption must be provided. See the above comments on the key_encryption_key and resolver.
145    '''
146
147    __metaclass__ = ABCMeta
148    MAX_SINGLE_GET_SIZE = 32 * 1024 * 1024
149    MAX_CHUNK_GET_SIZE = 4 * 1024 * 1024
150
151    def __init__(self, account_name=None, account_key=None, sas_token=None, is_emulated=False,
152                 protocol=DEFAULT_PROTOCOL, endpoint_suffix=SERVICE_HOST_BASE, custom_domain=None, request_session=None,
153                 connection_string=None, socket_timeout=None, token_credential=None):
154        '''
155        :param str account_name:
156            The storage account name. This is used to authenticate requests
157            signed with an account key and to construct the storage endpoint. It
158            is required unless a connection string is given, or if a custom
159            domain is used with anonymous authentication.
160        :param str account_key:
161            The storage account key. This is used for shared key authentication.
162            If neither account key or sas token is specified, anonymous access
163            will be used.
164        :param str sas_token:
165             A shared access signature token to use to authenticate requests
166             instead of the account key. If account key and sas token are both
167             specified, account key will be used to sign. If neither are
168             specified, anonymous access will be used.
169        :param bool is_emulated:
170            Whether to use the emulator. Defaults to False. If specified, will
171            override all other parameters besides connection string and request
172            session.
173        :param str protocol:
174            The protocol to use for requests. Defaults to https.
175        :param str endpoint_suffix:
176            The host base component of the url, minus the account name. Defaults
177            to Azure (core.windows.net). Override this to use the China cloud
178            (core.chinacloudapi.cn).
179        :param str custom_domain:
180            The custom domain to use. This can be set in the Azure Portal. For
181            example, 'www.mydomain.com'.
182        :param requests.Session request_session:
183            The session object to use for http requests.
184        :param str connection_string:
185            If specified, this will override all other parameters besides
186            request session. See
187            http://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/
188            for the connection string format
189        :param int socket_timeout:
190            If specified, this will override the default socket timeout. The timeout specified is in seconds.
191            See DEFAULT_SOCKET_TIMEOUT in _constants.py for the default value.
192        :param token_credential:
193            A token credential used to authenticate HTTPS requests. The token value
194            should be updated before its expiration.
195        :type `~azure.storage.common.TokenCredential`
196        '''
197        service_params = _ServiceParameters.get_service_parameters(
198            'blob',
199            account_name=account_name,
200            account_key=account_key,
201            sas_token=sas_token,
202            token_credential=token_credential,
203            is_emulated=is_emulated,
204            protocol=protocol,
205            endpoint_suffix=endpoint_suffix,
206            custom_domain=custom_domain,
207            request_session=request_session,
208            connection_string=connection_string,
209            socket_timeout=socket_timeout)
210
211        super(BaseBlobService, self).__init__(service_params)
212
213        if self.account_key:
214            self.authentication = _StorageSharedKeyAuthentication(
215                self.account_name,
216                self.account_key,
217                self.is_emulated
218            )
219        elif self.sas_token:
220            self.authentication = _StorageSASAuthentication(self.sas_token)
221        elif self.token_credential:
222            self.authentication = self.token_credential
223        else:
224            self.authentication = _StorageNoAuthentication()
225
226        self.require_encryption = False
227        self.key_encryption_key = None
228        self.key_resolver_function = None
229        self._X_MS_VERSION = X_MS_VERSION
230        self._update_user_agent_string(package_version)
231
232    def make_blob_url(self, container_name, blob_name, protocol=None, sas_token=None, snapshot=None):
233        '''
234        Creates the url to access a blob.
235
236        :param str container_name:
237            Name of container.
238        :param str blob_name:
239            Name of blob.
240        :param str protocol:
241            Protocol to use: 'http' or 'https'. If not specified, uses the
242            protocol specified when BaseBlobService was initialized.
243        :param str sas_token:
244            Shared access signature token created with
245            generate_shared_access_signature.
246        :param str snapshot:
247            An string value that uniquely identifies the snapshot. The value of
248            this query parameter indicates the snapshot version.
249        :return: blob access URL.
250        :rtype: str
251        '''
252
253        url = '{}://{}/{}/{}'.format(
254            protocol or self.protocol,
255            self.primary_endpoint,
256            container_name,
257            blob_name,
258        )
259
260        if snapshot and sas_token:
261            url = '{}?snapshot={}&{}'.format(url, snapshot, sas_token)
262        elif snapshot:
263            url = '{}?snapshot={}'.format(url, snapshot)
264        elif sas_token:
265            url = '{}?{}'.format(url, sas_token)
266
267        return url
268
269    def make_container_url(self, container_name, protocol=None, sas_token=None):
270        '''
271        Creates the url to access a container.
272
273        :param str container_name:
274            Name of container.
275        :param str protocol:
276            Protocol to use: 'http' or 'https'. If not specified, uses the
277            protocol specified when BaseBlobService was initialized.
278        :param str sas_token:
279            Shared access signature token created with
280            generate_shared_access_signature.
281        :return: container access URL.
282        :rtype: str
283        '''
284
285        url = '{}://{}/{}?restype=container'.format(
286            protocol or self.protocol,
287            self.primary_endpoint,
288            container_name,
289        )
290
291        if sas_token:
292            url = '{}&{}'.format(url, sas_token)
293
294        return url
295
296    def generate_account_shared_access_signature(self, resource_types, permission,
297                                                 expiry, start=None, ip=None, protocol=None):
298        '''
299        Generates a shared access signature for the blob service.
300        Use the returned signature with the sas_token parameter of any BlobService.
301
302        :param ResourceTypes resource_types:
303            Specifies the resource types that are accessible with the account SAS.
304        :param AccountPermissions permission:
305            The permissions associated with the shared access signature. The
306            user is restricted to operations allowed by the permissions.
307            Required unless an id is given referencing a stored access policy
308            which contains this field. This field must be omitted if it has been
309            specified in an associated stored access policy.
310        :param expiry:
311            The time at which the shared access signature becomes invalid.
312            Required unless an id is given referencing a stored access policy
313            which contains this field. This field must be omitted if it has
314            been specified in an associated stored access policy. Azure will always
315            convert values to UTC. If a date is passed in without timezone info, it
316            is assumed to be UTC.
317        :type expiry: datetime or str
318        :param start:
319            The time at which the shared access signature becomes valid. If
320            omitted, start time for this call is assumed to be the time when the
321            storage service receives the request. Azure will always convert values
322            to UTC. If a date is passed in without timezone info, it is assumed to
323            be UTC.
324        :type start: datetime or str
325        :param str ip:
326            Specifies an IP address or a range of IP addresses from which to accept requests.
327            If the IP address from which the request originates does not match the IP address
328            or address range specified on the SAS token, the request is not authenticated.
329            For example, specifying sip=168.1.5.65 or sip=168.1.5.60-168.1.5.70 on the SAS
330            restricts the request to those IP addresses.
331        :param str protocol:
332            Specifies the protocol permitted for a request made. The default value
333            is https,http. See :class:`~azure.storage.common.models.Protocol` for possible values.
334        :return: A Shared Access Signature (sas) token.
335        :rtype: str
336        '''
337        _validate_not_none('self.account_name', self.account_name)
338        _validate_not_none('self.account_key', self.account_key)
339
340        sas = BlobSharedAccessSignature(self.account_name, self.account_key)
341        return sas.generate_account(Services.BLOB, resource_types, permission,
342                                    expiry, start=start, ip=ip, protocol=protocol)
343
344    def generate_container_shared_access_signature(self, container_name,
345                                                   permission=None, expiry=None,
346                                                   start=None, id=None, ip=None, protocol=None,
347                                                   cache_control=None, content_disposition=None,
348                                                   content_encoding=None, content_language=None,
349                                                   content_type=None, user_delegation_key=None):
350        '''
351        Generates a shared access signature for the container.
352        Use the returned signature with the sas_token parameter of any BlobService.
353
354        :param str container_name:
355            Name of container.
356        :param ContainerPermissions permission:
357            The permissions associated with the shared access signature. The
358            user is restricted to operations allowed by the permissions.
359            Permissions must be ordered read, write, delete, list.
360            Required unless an id is given referencing a stored access policy
361            which contains this field. This field must be omitted if it has been
362            specified in an associated stored access policy.
363        :param expiry:
364            The time at which the shared access signature becomes invalid.
365            Required unless an id is given referencing a stored access policy
366            which contains this field. This field must be omitted if it has
367            been specified in an associated stored access policy. Azure will always
368            convert values to UTC. If a date is passed in without timezone info, it
369            is assumed to be UTC.
370        :type expiry: datetime or str
371        :param start:
372            The time at which the shared access signature becomes valid. If
373            omitted, start time for this call is assumed to be the time when the
374            storage service receives the request. Azure will always convert values
375            to UTC. If a date is passed in without timezone info, it is assumed to
376            be UTC.
377        :type start: datetime or str
378        :param str id:
379            A unique value up to 64 characters in length that correlates to a
380            stored access policy. To create a stored access policy, use
381            set_blob_service_properties.
382        :param str ip:
383            Specifies an IP address or a range of IP addresses from which to accept requests.
384            If the IP address from which the request originates does not match the IP address
385            or address range specified on the SAS token, the request is not authenticated.
386            For example, specifying sip=168.1.5.65 or sip=168.1.5.60-168.1.5.70 on the SAS
387            restricts the request to those IP addresses.
388        :param str protocol:
389            Specifies the protocol permitted for a request made. The default value
390            is https,http. See :class:`~azure.storage.common.models.Protocol` for possible values.
391        :param str cache_control:
392            Response header value for Cache-Control when resource is accessed
393            using this shared access signature.
394        :param str content_disposition:
395            Response header value for Content-Disposition when resource is accessed
396            using this shared access signature.
397        :param str content_encoding:
398            Response header value for Content-Encoding when resource is accessed
399            using this shared access signature.
400        :param str content_language:
401            Response header value for Content-Language when resource is accessed
402            using this shared access signature.
403        :param str content_type:
404            Response header value for Content-Type when resource is accessed
405            using this shared access signature.
406        :param ~azure.storage.blob.models.UserDelegationKey user_delegation_key:
407            Instead of an account key, the user could pass in a user delegation key.
408            A user delegation key can be obtained from the service by authenticating with an AAD identity;
409            this can be accomplished by calling get_user_delegation_key.
410            When present, the SAS is signed with the user delegation key instead.
411        :return: A Shared Access Signature (sas) token.
412        :rtype: str
413        '''
414        _validate_not_none('container_name', container_name)
415        _validate_not_none('self.account_name', self.account_name)
416
417        if user_delegation_key is not None:
418            _validate_user_delegation_key(user_delegation_key)
419            sas = BlobSharedAccessSignature(self.account_name, user_delegation_key=user_delegation_key)
420        else:
421            _validate_not_none('self.account_key', self.account_key)
422            sas = BlobSharedAccessSignature(self.account_name, account_key=self.account_key)
423
424        return sas.generate_container(
425            container_name,
426            permission,
427            expiry,
428            start=start,
429            id=id,
430            ip=ip,
431            protocol=protocol,
432            cache_control=cache_control,
433            content_disposition=content_disposition,
434            content_encoding=content_encoding,
435            content_language=content_language,
436            content_type=content_type,
437        )
438
439    def generate_blob_shared_access_signature(
440            self, container_name, blob_name, snapshot=None, permission=None,
441            expiry=None, start=None, id=None, ip=None, protocol=None,
442            cache_control=None, content_disposition=None,
443            content_encoding=None, content_language=None,
444            content_type=None, user_delegation_key=None):
445        '''
446        Generates a shared access signature for the blob or one of its snapshots.
447        Use the returned signature with the sas_token parameter of any BlobService.
448
449        :param str container_name:
450            Name of container.
451        :param str blob_name:
452            Name of blob.
453        :param str snapshot:
454            The snapshot parameter is an opaque DateTime value that,
455            when present, specifies the blob snapshot to grant permission.
456        :param BlobPermissions permission:
457            The permissions associated with the shared access signature. The
458            user is restricted to operations allowed by the permissions.
459            Permissions must be ordered read, write, delete, list.
460            Required unless an id is given referencing a stored access policy
461            which contains this field. This field must be omitted if it has been
462            specified in an associated stored access policy.
463        :param expiry:
464            The time at which the shared access signature becomes invalid.
465            Required unless an id is given referencing a stored access policy
466            which contains this field. This field must be omitted if it has
467            been specified in an associated stored access policy. Azure will always
468            convert values to UTC. If a date is passed in without timezone info, it
469            is assumed to be UTC.
470        :type expiry: datetime or str
471        :param start:
472            The time at which the shared access signature becomes valid. If
473            omitted, start time for this call is assumed to be the time when the
474            storage service receives the request. Azure will always convert values
475            to UTC. If a date is passed in without timezone info, it is assumed to
476            be UTC.
477        :type start: datetime or str
478        :param str id:
479            A unique value up to 64 characters in length that correlates to a
480            stored access policy. To create a stored access policy, use :func:`~set_container_acl`.
481        :param str ip:
482            Specifies an IP address or a range of IP addresses from which to accept requests.
483            If the IP address from which the request originates does not match the IP address
484            or address range specified on the SAS token, the request is not authenticated.
485            For example, specifying sip=168.1.5.65 or sip=168.1.5.60-168.1.5.70 on the SAS
486            restricts the request to those IP addresses.
487        :param str protocol:
488            Specifies the protocol permitted for a request made. The default value
489            is https,http. See :class:`~azure.storage.common.models.Protocol` for possible values.
490        :param str cache_control:
491            Response header value for Cache-Control when resource is accessed
492            using this shared access signature.
493        :param str content_disposition:
494            Response header value for Content-Disposition when resource is accessed
495            using this shared access signature.
496        :param str content_encoding:
497            Response header value for Content-Encoding when resource is accessed
498            using this shared access signature.
499        :param str content_language:
500            Response header value for Content-Language when resource is accessed
501            using this shared access signature.
502        :param str content_type:
503            Response header value for Content-Type when resource is accessed
504            using this shared access signature.
505        :param ~azure.storage.blob.models.UserDelegationKey user_delegation_key:
506            Instead of an account key, the user could pass in a user delegation key.
507            A user delegation key can be obtained from the service by authenticating with an AAD identity;
508            this can be accomplished by calling get_user_delegation_key.
509            When present, the SAS is signed with the user delegation key instead.
510        :return: A Shared Access Signature (sas) token.
511        :rtype: str
512        '''
513        _validate_not_none('container_name', container_name)
514        _validate_not_none('blob_name', blob_name)
515        _validate_not_none('self.account_name', self.account_name)
516
517        if user_delegation_key is not None:
518            _validate_user_delegation_key(user_delegation_key)
519            sas = BlobSharedAccessSignature(self.account_name, user_delegation_key=user_delegation_key)
520        else:
521            _validate_not_none('self.account_key', self.account_key)
522            sas = BlobSharedAccessSignature(self.account_name, account_key=self.account_key)
523
524        return sas.generate_blob(
525            container_name=container_name,
526            blob_name=blob_name,
527            snapshot=snapshot,
528            permission=permission,
529            expiry=expiry,
530            start=start,
531            id=id,
532            ip=ip,
533            protocol=protocol,
534            cache_control=cache_control,
535            content_disposition=content_disposition,
536            content_encoding=content_encoding,
537            content_language=content_language,
538            content_type=content_type,
539        )
540
541    def get_user_delegation_key(self, key_start_time, key_expiry_time, timeout=None):
542        """
543        Obtain a user delegation key for the purpose of signing SAS tokens.
544        A token credential must be present on the service object for this request to succeed.
545
546        :param datetime key_start_time:
547            A DateTime value. Indicates when the key becomes valid.
548        :param datetime key_expiry_time:
549            A DateTime value. Indicates when the key stops being valid.
550        :param int timeout:
551            The timeout parameter is expressed in seconds.
552        :return:
553        """
554        _validate_not_none('key_start_time', key_start_time)
555        _validate_not_none('key_end_time', key_expiry_time)
556
557        request = HTTPRequest()
558        request.method = 'POST'
559        request.host_locations = self._get_host_locations(secondary=True)
560        request.query = {
561            'restype': 'service',
562            'comp': 'userdelegationkey',
563            'timeout': _int_to_str(timeout),
564        }
565        request.body = _get_request_body(_convert_delegation_key_info_to_xml(key_start_time, key_expiry_time))
566        return self._perform_request(request, _convert_xml_to_user_delegation_key)
567
568    def list_containers(self, prefix=None, num_results=None, include_metadata=False,
569                        marker=None, timeout=None):
570        '''
571        Returns a generator to list the containers under the specified account.
572        The generator will lazily follow the continuation tokens returned by
573        the service and stop when all containers have been returned or num_results is reached.
574
575        If num_results is specified and the account has more than that number of
576        containers, the generator will have a populated next_marker field once it
577        finishes. This marker can be used to create a new generator if more
578        results are desired.
579
580        :param str prefix:
581            Filters the results to return only containers whose names
582            begin with the specified prefix.
583        :param int num_results:
584            Specifies the maximum number of containers to return. A single list
585            request may return up to 1000 contianers and potentially a continuation
586            token which should be followed to get additional resutls.
587        :param bool include_metadata:
588            Specifies that container metadata be returned in the response.
589        :param str marker:
590            An opaque continuation token. This value can be retrieved from the
591            next_marker field of a previous generator object if num_results was
592            specified and that generator has finished enumerating results. If
593            specified, this generator will begin returning results from the point
594            where the previous generator stopped.
595        :param int timeout:
596            The timeout parameter is expressed in seconds.
597        '''
598        include = 'metadata' if include_metadata else None
599        operation_context = _OperationContext(location_lock=True)
600        kwargs = {'prefix': prefix, 'marker': marker, 'max_results': num_results,
601                  'include': include, 'timeout': timeout, '_context': operation_context}
602        resp = self._list_containers(**kwargs)
603
604        return ListGenerator(resp, self._list_containers, (), kwargs)
605
606    def _list_containers(self, prefix=None, marker=None, max_results=None,
607                         include=None, timeout=None, _context=None):
608        '''
609        Returns a list of the containers under the specified account.
610
611        :param str prefix:
612            Filters the results to return only containers whose names
613            begin with the specified prefix.
614        :param str marker:
615            A string value that identifies the portion of the list
616            to be returned with the next list operation. The operation returns
617            a next_marker value within the response body if the list returned was
618            not complete. The marker value may then be used in a subsequent
619            call to request the next set of list items. The marker value is
620            opaque to the client.
621        :param int max_results:
622            Specifies the maximum number of containers to return. A single list
623            request may return up to 1000 contianers and potentially a continuation
624            token which should be followed to get additional resutls.
625        :param str include:
626            Include this parameter to specify that the container's
627            metadata be returned as part of the response body. set this
628            parameter to string 'metadata' to get container's metadata.
629        :param int timeout:
630            The timeout parameter is expressed in seconds.
631        '''
632        request = HTTPRequest()
633        request.method = 'GET'
634        request.host_locations = self._get_host_locations(secondary=True)
635        request.path = _get_path()
636        request.query = {
637            'comp': 'list',
638            'prefix': _to_str(prefix),
639            'marker': _to_str(marker),
640            'maxresults': _int_to_str(max_results),
641            'include': _to_str(include),
642            'timeout': _int_to_str(timeout)
643        }
644
645        return self._perform_request(request, _convert_xml_to_containers, operation_context=_context)
646
647    def create_container(self, container_name, metadata=None,
648                         public_access=None, fail_on_exist=False, timeout=None):
649        '''
650        Creates a new container under the specified account. If the container
651        with the same name already exists, the operation fails if
652        fail_on_exist is True.
653
654        :param str container_name:
655            Name of container to create.
656        :param metadata:
657            A dict with name_value pairs to associate with the
658            container as metadata. Example:{'Category':'test'}
659        :type metadata: dict(str, str)
660        :param ~azure.storage.blob.models.PublicAccess public_access:
661            Possible values include: container, blob.
662        :param bool fail_on_exist:
663            Specify whether to throw an exception when the container exists.
664        :param int timeout:
665            The timeout parameter is expressed in seconds.
666        :return: True if container is created, False if container already exists.
667        :rtype: bool
668        '''
669        _validate_not_none('container_name', container_name)
670        request = HTTPRequest()
671        request.method = 'PUT'
672        request.host_locations = self._get_host_locations()
673        request.path = _get_path(container_name)
674        request.query = {
675            'restype': 'container',
676            'timeout': _int_to_str(timeout),
677        }
678        request.headers = {
679            'x-ms-blob-public-access': _to_str(public_access)
680        }
681        _add_metadata_headers(metadata, request)
682
683        if not fail_on_exist:
684            try:
685                self._perform_request(request, expected_errors=[_CONTAINER_ALREADY_EXISTS_ERROR_CODE])
686                return True
687            except AzureHttpError as ex:
688                _dont_fail_on_exist(ex)
689                return False
690        else:
691            self._perform_request(request)
692            return True
693
694    def get_container_properties(self, container_name, lease_id=None, timeout=None):
695        '''
696        Returns all user-defined metadata and system properties for the specified
697        container. The data returned does not include the container's list of blobs.
698
699        :param str container_name:
700            Name of existing container.
701        :param str lease_id:
702            If specified, get_container_properties only succeeds if the
703            container's lease is active and matches this ID.
704        :param int timeout:
705            The timeout parameter is expressed in seconds.
706        :return: properties for the specified container within a container object.
707        :rtype: :class:`~azure.storage.blob.models.Container`
708        '''
709        _validate_not_none('container_name', container_name)
710        request = HTTPRequest()
711        request.method = 'GET'
712        request.host_locations = self._get_host_locations(secondary=True)
713        request.path = _get_path(container_name)
714        request.query = {
715            'restype': 'container',
716            'timeout': _int_to_str(timeout),
717        }
718        request.headers = {'x-ms-lease-id': _to_str(lease_id)}
719
720        return self._perform_request(request, _parse_container, [container_name])
721
722    def get_container_metadata(self, container_name, lease_id=None, timeout=None):
723        '''
724        Returns all user-defined metadata for the specified container.
725
726        :param str container_name:
727            Name of existing container.
728        :param str lease_id:
729            If specified, get_container_metadata only succeeds if the
730            container's lease is active and matches this ID.
731        :param int timeout:
732            The timeout parameter is expressed in seconds.
733        :return:
734            A dictionary representing the container metadata name, value pairs.
735        :rtype: dict(str, str)
736        '''
737        _validate_not_none('container_name', container_name)
738        request = HTTPRequest()
739        request.method = 'GET'
740        request.host_locations = self._get_host_locations(secondary=True)
741        request.path = _get_path(container_name)
742        request.query = {
743            'restype': 'container',
744            'comp': 'metadata',
745            'timeout': _int_to_str(timeout),
746        }
747        request.headers = {'x-ms-lease-id': _to_str(lease_id)}
748
749        return self._perform_request(request, _parse_metadata)
750
751    def set_container_metadata(self, container_name, metadata=None,
752                               lease_id=None, if_modified_since=None, timeout=None):
753        '''
754        Sets one or more user-defined name-value pairs for the specified
755        container. Each call to this operation replaces all existing metadata
756        attached to the container. To remove all metadata from the container,
757        call this operation with no metadata dict.
758
759        :param str container_name:
760            Name of existing container.
761        :param metadata:
762            A dict containing name-value pairs to associate with the container as
763            metadata. Example: {'category':'test'}
764        :type metadata: dict(str, str)
765        :param str lease_id:
766            If specified, set_container_metadata only succeeds if the
767            container's lease is active and matches this ID.
768        :param datetime if_modified_since:
769            A DateTime value. Azure expects the date value passed in to be UTC.
770            If timezone is included, any non-UTC datetimes will be converted to UTC.
771            If a date is passed in without timezone info, it is assumed to be UTC.
772            Specify this header to perform the operation only
773            if the resource has been modified since the specified time.
774        :param int timeout:
775            The timeout parameter is expressed in seconds.
776        :return: ETag and last modified properties for the updated Container
777        :rtype: :class:`~azure.storage.blob.models.ResourceProperties`
778        '''
779        _validate_not_none('container_name', container_name)
780        request = HTTPRequest()
781        request.method = 'PUT'
782        request.host_locations = self._get_host_locations()
783        request.path = _get_path(container_name)
784        request.query = {
785            'restype': 'container',
786            'comp': 'metadata',
787            'timeout': _int_to_str(timeout),
788        }
789        request.headers = {
790            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
791            'x-ms-lease-id': _to_str(lease_id),
792        }
793        _add_metadata_headers(metadata, request)
794
795        return self._perform_request(request, _parse_base_properties)
796
797    def get_container_acl(self, container_name, lease_id=None, timeout=None):
798        '''
799        Gets the permissions for the specified container.
800        The permissions indicate whether container data may be accessed publicly.
801
802        :param str container_name:
803            Name of existing container.
804        :param lease_id:
805            If specified, get_container_acl only succeeds if the
806            container's lease is active and matches this ID.
807        :param int timeout:
808            The timeout parameter is expressed in seconds.
809        :return: A dictionary of access policies associated with the container. dict of str to
810            :class:`azure.storage.common.models.AccessPolicy` and a public_access property
811            if public access is turned on
812        '''
813        _validate_not_none('container_name', container_name)
814        request = HTTPRequest()
815        request.method = 'GET'
816        request.host_locations = self._get_host_locations(secondary=True)
817        request.path = _get_path(container_name)
818        request.query = {
819            'restype': 'container',
820            'comp': 'acl',
821            'timeout': _int_to_str(timeout),
822        }
823        request.headers = {'x-ms-lease-id': _to_str(lease_id)}
824
825        return self._perform_request(request, _convert_xml_to_signed_identifiers_and_access)
826
827    def set_container_acl(self, container_name, signed_identifiers=None,
828                          public_access=None, lease_id=None,
829                          if_modified_since=None, if_unmodified_since=None, timeout=None):
830        '''
831        Sets the permissions for the specified container or stored access
832        policies that may be used with Shared Access Signatures. The permissions
833        indicate whether blobs in a container may be accessed publicly.
834
835        :param str container_name:
836            Name of existing container.
837        :param signed_identifiers:
838            A dictionary of access policies to associate with the container. The
839            dictionary may contain up to 5 elements. An empty dictionary
840            will clear the access policies set on the service.
841        :type signed_identifiers: dict(str, :class:`~azure.storage.common.models.AccessPolicy`)
842        :param ~azure.storage.blob.models.PublicAccess public_access:
843            Possible values include: container, blob.
844        :param str lease_id:
845            If specified, set_container_acl only succeeds if the
846            container's lease is active and matches this ID.
847        :param datetime if_modified_since:
848            A datetime value. Azure expects the date value passed in to be UTC.
849            If timezone is included, any non-UTC datetimes will be converted to UTC.
850            If a date is passed in without timezone info, it is assumed to be UTC.
851            Specify this header to perform the operation only
852            if the resource has been modified since the specified date/time.
853        :param datetime if_unmodified_since:
854            A datetime value. Azure expects the date value passed in to be UTC.
855            If timezone is included, any non-UTC datetimes will be converted to UTC.
856            If a date is passed in without timezone info, it is assumed to be UTC.
857            Specify this header to perform the operation only if
858            the resource has not been modified since the specified date/time.
859        :param int timeout:
860            The timeout parameter is expressed in seconds.
861        :return: ETag and last modified properties for the updated Container
862        :rtype: :class:`~azure.storage.blob.models.ResourceProperties`
863        '''
864        _validate_not_none('container_name', container_name)
865        _validate_access_policies(signed_identifiers)
866        request = HTTPRequest()
867        request.method = 'PUT'
868        request.host_locations = self._get_host_locations()
869        request.path = _get_path(container_name)
870        request.query = {
871            'restype': 'container',
872            'comp': 'acl',
873            'timeout': _int_to_str(timeout),
874        }
875        request.headers = {
876            'x-ms-blob-public-access': _to_str(public_access),
877            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
878            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
879            'x-ms-lease-id': _to_str(lease_id),
880        }
881        request.body = _get_request_body(
882            _convert_signed_identifiers_to_xml(signed_identifiers))
883
884        return self._perform_request(request, _parse_base_properties)
885
886    def delete_container(self, container_name, fail_not_exist=False,
887                         lease_id=None, if_modified_since=None,
888                         if_unmodified_since=None, timeout=None):
889        '''
890        Marks the specified container for deletion. The container and any blobs
891        contained within it are later deleted during garbage collection.
892
893        :param str container_name:
894            Name of container to delete.
895        :param bool fail_not_exist:
896            Specify whether to throw an exception when the container doesn't
897            exist.
898        :param str lease_id:
899            If specified, delete_container only succeeds if the
900            container's lease is active and matches this ID.
901            Required if the container has an active lease.
902        :param datetime if_modified_since:
903            A DateTime value. Azure expects the date value passed in to be UTC.
904            If timezone is included, any non-UTC datetimes will be converted to UTC.
905            If a date is passed in without timezone info, it is assumed to be UTC.
906            Specify this header to perform the operation only
907            if the resource has been modified since the specified time.
908        :param datetime if_unmodified_since:
909            A DateTime value. Azure expects the date value passed in to be UTC.
910            If timezone is included, any non-UTC datetimes will be converted to UTC.
911            If a date is passed in without timezone info, it is assumed to be UTC.
912            Specify this header to perform the operation only if
913            the resource has not been modified since the specified date/time.
914        :param int timeout:
915            The timeout parameter is expressed in seconds.
916        :return: True if container is deleted, False container doesn't exist.
917        :rtype: bool
918        '''
919        _validate_not_none('container_name', container_name)
920        request = HTTPRequest()
921        request.method = 'DELETE'
922        request.host_locations = self._get_host_locations()
923        request.path = _get_path(container_name)
924        request.query = {
925            'restype': 'container',
926            'timeout': _int_to_str(timeout),
927        }
928        request.headers = {
929            'x-ms-lease-id': _to_str(lease_id),
930            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
931            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
932        }
933
934        if not fail_not_exist:
935            try:
936                self._perform_request(request, expected_errors=[_CONTAINER_NOT_FOUND_ERROR_CODE])
937                return True
938            except AzureHttpError as ex:
939                _dont_fail_not_exist(ex)
940                return False
941        else:
942            self._perform_request(request)
943            return True
944
945    def _lease_container_impl(
946            self, container_name, lease_action, lease_id, lease_duration,
947            lease_break_period, proposed_lease_id, if_modified_since,
948            if_unmodified_since, timeout):
949        '''
950        Establishes and manages a lease on a container.
951        The Lease Container operation can be called in one of five modes
952            Acquire, to request a new lease
953            Renew, to renew an existing lease
954            Change, to change the ID of an existing lease
955            Release, to free the lease if it is no longer needed so that another
956                client may immediately acquire a lease against the container
957            Break, to end the lease but ensure that another client cannot acquire
958                a new lease until the current lease period has expired
959
960        :param str container_name:
961            Name of existing container.
962        :param str lease_action:
963            Possible _LeaseActions values: acquire|renew|release|break|change
964        :param str lease_id:
965            Required if the container has an active lease.
966        :param int lease_duration:
967            Specifies the duration of the lease, in seconds, or negative one
968            (-1) for a lease that never expires. A non-infinite lease can be
969            between 15 and 60 seconds. A lease duration cannot be changed
970            using renew or change. For backwards compatibility, the default is
971            60, and the value is only used on an acquire operation.
972        :param int lease_break_period:
973            For a break operation, this is the proposed duration of
974            seconds that the lease should continue before it is broken, between
975            0 and 60 seconds. This break period is only used if it is shorter
976            than the time remaining on the lease. If longer, the time remaining
977            on the lease is used. A new lease will not be available before the
978            break period has expired, but the lease may be held for longer than
979            the break period. If this header does not appear with a break
980            operation, a fixed-duration lease breaks after the remaining lease
981            period elapses, and an infinite lease breaks immediately.
982        :param str proposed_lease_id:
983            Optional for Acquire, required for Change. Proposed lease ID, in a
984            GUID string format. The Blob service returns 400 (Invalid request)
985            if the proposed lease ID is not in the correct format.
986        :param datetime if_modified_since:
987            A DateTime value. Azure expects the date value passed in to be UTC.
988            If timezone is included, any non-UTC datetimes will be converted to UTC.
989            If a date is passed in without timezone info, it is assumed to be UTC.
990            Specify this header to perform the operation only
991            if the resource has been modified since the specified time.
992        :param datetime if_unmodified_since:
993            A DateTime value. Azure expects the date value passed in to be UTC.
994            If timezone is included, any non-UTC datetimes will be converted to UTC.
995            If a date is passed in without timezone info, it is assumed to be UTC.
996            Specify this header to perform the operation only if
997            the resource has not been modified since the specified date/time.
998        :param int timeout:
999            The timeout parameter is expressed in seconds.
1000        :return:
1001            Response headers returned from the service call.
1002        :rtype: dict(str, str)
1003        '''
1004        _validate_not_none('container_name', container_name)
1005        _validate_not_none('lease_action', lease_action)
1006        request = HTTPRequest()
1007        request.method = 'PUT'
1008        request.host_locations = self._get_host_locations()
1009        request.path = _get_path(container_name)
1010        request.query = {
1011            'restype': 'container',
1012            'comp': 'lease',
1013            'timeout': _int_to_str(timeout),
1014        }
1015        request.headers = {
1016            'x-ms-lease-id': _to_str(lease_id),
1017            'x-ms-lease-action': _to_str(lease_action),
1018            'x-ms-lease-duration': _to_str(lease_duration),
1019            'x-ms-lease-break-period': _to_str(lease_break_period),
1020            'x-ms-proposed-lease-id': _to_str(proposed_lease_id),
1021            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
1022            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
1023        }
1024
1025        return self._perform_request(request, _parse_lease)
1026
1027    def acquire_container_lease(
1028            self, container_name, lease_duration=-1, proposed_lease_id=None,
1029            if_modified_since=None, if_unmodified_since=None, timeout=None):
1030        '''
1031        Requests a new lease. If the container does not have an active lease,
1032        the Blob service creates a lease on the container and returns a new
1033        lease ID.
1034
1035        :param str container_name:
1036            Name of existing container.
1037        :param int lease_duration:
1038            Specifies the duration of the lease, in seconds, or negative one
1039            (-1) for a lease that never expires. A non-infinite lease can be
1040            between 15 and 60 seconds. A lease duration cannot be changed
1041            using renew or change. Default is -1 (infinite lease).
1042        :param str proposed_lease_id:
1043            Proposed lease ID, in a GUID string format. The Blob service returns
1044            400 (Invalid request) if the proposed lease ID is not in the correct format.
1045        :param datetime if_modified_since:
1046            A DateTime value. Azure expects the date value passed in to be UTC.
1047            If timezone is included, any non-UTC datetimes will be converted to UTC.
1048            If a date is passed in without timezone info, it is assumed to be UTC.
1049            Specify this header to perform the operation only
1050            if the resource has been modified since the specified time.
1051        :param datetime if_unmodified_since:
1052            A DateTime value. Azure expects the date value passed in to be UTC.
1053            If timezone is included, any non-UTC datetimes will be converted to UTC.
1054            If a date is passed in without timezone info, it is assumed to be UTC.
1055            Specify this header to perform the operation only if
1056            the resource has not been modified since the specified date/time.
1057        :param int timeout:
1058            The timeout parameter is expressed in seconds.
1059        :return: the lease ID of the newly created lease.
1060        :return: str
1061        '''
1062        _validate_not_none('lease_duration', lease_duration)
1063        if lease_duration != -1 and \
1064                (lease_duration < 15 or lease_duration > 60):
1065            raise ValueError(_ERROR_INVALID_LEASE_DURATION)
1066
1067        lease = self._lease_container_impl(container_name,
1068                                           _LeaseActions.Acquire,
1069                                           None,  # lease_id
1070                                           lease_duration,
1071                                           None,  # lease_break_period
1072                                           proposed_lease_id,
1073                                           if_modified_since,
1074                                           if_unmodified_since,
1075                                           timeout)
1076        return lease['id']
1077
1078    def renew_container_lease(
1079            self, container_name, lease_id, if_modified_since=None,
1080            if_unmodified_since=None, timeout=None):
1081        '''
1082        Renews the lease. The lease can be renewed if the lease ID specified
1083        matches that associated with the container. Note that
1084        the lease may be renewed even if it has expired as long as the container
1085        has not been leased again since the expiration of that lease. When you
1086        renew a lease, the lease duration clock resets.
1087
1088        :param str container_name:
1089            Name of existing container.
1090        :param str lease_id:
1091            Lease ID for active lease.
1092        :param datetime if_modified_since:
1093            A DateTime value. Azure expects the date value passed in to be UTC.
1094            If timezone is included, any non-UTC datetimes will be converted to UTC.
1095            If a date is passed in without timezone info, it is assumed to be UTC.
1096            Specify this header to perform the operation only
1097            if the resource has been modified since the specified time.
1098        :param datetime if_unmodified_since:
1099            A DateTime value. Azure expects the date value passed in to be UTC.
1100            If timezone is included, any non-UTC datetimes will be converted to UTC.
1101            If a date is passed in without timezone info, it is assumed to be UTC.
1102            Specify this header to perform the operation only if
1103            the resource has not been modified since the specified date/time.
1104        :param int timeout:
1105            The timeout parameter is expressed in seconds.
1106        :return: the lease ID of the renewed lease.
1107        :return: str
1108        '''
1109        _validate_not_none('lease_id', lease_id)
1110
1111        lease = self._lease_container_impl(container_name,
1112                                           _LeaseActions.Renew,
1113                                           lease_id,
1114                                           None,  # lease_duration
1115                                           None,  # lease_break_period
1116                                           None,  # proposed_lease_id
1117                                           if_modified_since,
1118                                           if_unmodified_since,
1119                                           timeout)
1120        return lease['id']
1121
1122    def release_container_lease(
1123            self, container_name, lease_id, if_modified_since=None,
1124            if_unmodified_since=None, timeout=None):
1125        '''
1126        Release the lease. The lease may be released if the lease_id specified matches
1127        that associated with the container. Releasing the lease allows another client
1128        to immediately acquire the lease for the container as soon as the release is complete.
1129
1130        :param str container_name:
1131            Name of existing container.
1132        :param str lease_id:
1133            Lease ID for active lease.
1134        :param datetime if_modified_since:
1135            A DateTime value. Azure expects the date value passed in to be UTC.
1136            If timezone is included, any non-UTC datetimes will be converted to UTC.
1137            If a date is passed in without timezone info, it is assumed to be UTC.
1138            Specify this header to perform the operation only
1139            if the resource has been modified since the specified time.
1140        :param datetime if_unmodified_since:
1141            A DateTime value. Azure expects the date value passed in to be UTC.
1142            If timezone is included, any non-UTC datetimes will be converted to UTC.
1143            If a date is passed in without timezone info, it is assumed to be UTC.
1144            Specify this header to perform the operation only if
1145            the resource has not been modified since the specified date/time.
1146        :param int timeout:
1147            The timeout parameter is expressed in seconds.
1148        '''
1149        _validate_not_none('lease_id', lease_id)
1150
1151        self._lease_container_impl(container_name,
1152                                   _LeaseActions.Release,
1153                                   lease_id,
1154                                   None,  # lease_duration
1155                                   None,  # lease_break_period
1156                                   None,  # proposed_lease_id
1157                                   if_modified_since,
1158                                   if_unmodified_since,
1159                                   timeout)
1160
1161    def break_container_lease(
1162            self, container_name, lease_break_period=None,
1163            if_modified_since=None, if_unmodified_since=None, timeout=None):
1164        '''
1165        Break the lease, if the container has an active lease. Once a lease is
1166        broken, it cannot be renewed. Any authorized request can break the lease;
1167        the request is not required to specify a matching lease ID. When a lease
1168        is broken, the lease break period is allowed to elapse, during which time
1169        no lease operation except break and release can be performed on the container.
1170        When a lease is successfully broken, the response indicates the interval
1171        in seconds until a new lease can be acquired.
1172
1173        :param str container_name:
1174            Name of existing container.
1175        :param int lease_break_period:
1176            This is the proposed duration of seconds that the lease
1177            should continue before it is broken, between 0 and 60 seconds. This
1178            break period is only used if it is shorter than the time remaining
1179            on the lease. If longer, the time remaining on the lease is used.
1180            A new lease will not be available before the break period has
1181            expired, but the lease may be held for longer than the break
1182            period. If this header does not appear with a break
1183            operation, a fixed-duration lease breaks after the remaining lease
1184            period elapses, and an infinite lease breaks immediately.
1185        :param datetime if_modified_since:
1186            A DateTime value. Azure expects the date value passed in to be UTC.
1187            If timezone is included, any non-UTC datetimes will be converted to UTC.
1188            If a date is passed in without timezone info, it is assumed to be UTC.
1189            Specify this header to perform the operation only
1190            if the resource has been modified since the specified time.
1191        :param datetime if_unmodified_since:
1192            A DateTime value. Azure expects the date value passed in to be UTC.
1193            If timezone is included, any non-UTC datetimes will be converted to UTC.
1194            If a date is passed in without timezone info, it is assumed to be UTC.
1195            Specify this header to perform the operation only if
1196            the resource has not been modified since the specified date/time.
1197        :param int timeout:
1198            The timeout parameter is expressed in seconds.
1199        :return: Approximate time remaining in the lease period, in seconds.
1200        :return: int
1201        '''
1202        if (lease_break_period is not None) and (lease_break_period < 0 or lease_break_period > 60):
1203            raise ValueError(_ERROR_INVALID_LEASE_BREAK_PERIOD)
1204
1205        lease = self._lease_container_impl(container_name,
1206                                           _LeaseActions.Break,
1207                                           None,  # lease_id
1208                                           None,  # lease_duration
1209                                           lease_break_period,
1210                                           None,  # proposed_lease_id
1211                                           if_modified_since,
1212                                           if_unmodified_since,
1213                                           timeout)
1214        return lease['time']
1215
1216    def change_container_lease(
1217            self, container_name, lease_id, proposed_lease_id,
1218            if_modified_since=None, if_unmodified_since=None, timeout=None):
1219        '''
1220        Change the lease ID of an active lease. A change must include the current
1221        lease ID and a new lease ID.
1222
1223        :param str container_name:
1224            Name of existing container.
1225        :param str lease_id:
1226            Lease ID for active lease.
1227        :param str proposed_lease_id:
1228            Proposed lease ID, in a GUID string format. The Blob service returns 400
1229            (Invalid request) if the proposed lease ID is not in the correct format.
1230        :param datetime if_modified_since:
1231            A DateTime value. Azure expects the date value passed in to be UTC.
1232            If timezone is included, any non-UTC datetimes will be converted to UTC.
1233            If a date is passed in without timezone info, it is assumed to be UTC.
1234            Specify this header to perform the operation only
1235            if the resource has been modified since the specified time.
1236        :param datetime if_unmodified_since:
1237            A DateTime value. Azure expects the date value passed in to be UTC.
1238            If timezone is included, any non-UTC datetimes will be converted to UTC.
1239            If a date is passed in without timezone info, it is assumed to be UTC.
1240            Specify this header to perform the operation only if
1241            the resource has not been modified since the specified date/time.
1242        :param int timeout:
1243            The timeout parameter is expressed in seconds.
1244        '''
1245        _validate_not_none('lease_id', lease_id)
1246
1247        self._lease_container_impl(container_name,
1248                                   _LeaseActions.Change,
1249                                   lease_id,
1250                                   None,  # lease_duration
1251                                   None,  # lease_break_period
1252                                   proposed_lease_id,
1253                                   if_modified_since,
1254                                   if_unmodified_since,
1255                                   timeout)
1256
1257    def list_blobs(self, container_name, prefix=None, num_results=None, include=None,
1258                   delimiter=None, marker=None, timeout=None):
1259        '''
1260        Returns a generator to list the blobs under the specified container.
1261        The generator will lazily follow the continuation tokens returned by
1262        the service and stop when all blobs have been returned or num_results is reached.
1263
1264        If num_results is specified and the account has more than that number of
1265        blobs, the generator will have a populated next_marker field once it
1266        finishes. This marker can be used to create a new generator if more
1267        results are desired.
1268
1269        :param str container_name:
1270            Name of existing container.
1271        :param str prefix:
1272            Filters the results to return only blobs whose names
1273            begin with the specified prefix.
1274        :param int num_results:
1275            Specifies the maximum number of blobs to return,
1276            including all :class:`BlobPrefix` elements. If the request does not specify
1277            num_results or specifies a value greater than 5,000, the server will
1278            return up to 5,000 items. Setting num_results to a value less than
1279            or equal to zero results in error response code 400 (Bad Request).
1280        :param ~azure.storage.blob.models.Include include:
1281            Specifies one or more additional datasets to include in the response.
1282        :param str delimiter:
1283            When the request includes this parameter, the operation
1284            returns a :class:`~azure.storage.blob.models.BlobPrefix` element in the
1285            result list that acts as a placeholder for all blobs whose names begin
1286            with the same substring up to the appearance of the delimiter character.
1287            The delimiter may be a single character or a string.
1288        :param str marker:
1289            An opaque continuation token. This value can be retrieved from the
1290            next_marker field of a previous generator object if num_results was
1291            specified and that generator has finished enumerating results. If
1292            specified, this generator will begin returning results from the point
1293            where the previous generator stopped.
1294        :param int timeout:
1295            The timeout parameter is expressed in seconds.
1296        '''
1297        operation_context = _OperationContext(location_lock=True)
1298        args = (container_name,)
1299        kwargs = {'prefix': prefix, 'marker': marker, 'max_results': num_results,
1300                  'include': include, 'delimiter': delimiter, 'timeout': timeout,
1301                  '_context': operation_context,
1302                  '_converter': _convert_xml_to_blob_list}
1303        resp = self._list_blobs(*args, **kwargs)
1304
1305        return ListGenerator(resp, self._list_blobs, args, kwargs)
1306
1307    def list_blob_names(self, container_name, prefix=None, num_results=None,
1308                        include=None, delimiter=None, marker=None,
1309                        timeout=None):
1310        '''
1311        Returns a generator to list the blob names under the specified container.
1312        The generator will lazily follow the continuation tokens returned by
1313        the service and stop when all blobs have been returned or num_results is reached.
1314
1315        If num_results is specified and the account has more than that number of
1316        blobs, the generator will have a populated next_marker field once it
1317        finishes. This marker can be used to create a new generator if more
1318        results are desired.
1319
1320        :param str container_name:
1321            Name of existing container.
1322        :param str prefix:
1323            Filters the results to return only blobs whose names
1324            begin with the specified prefix.
1325        :param int num_results:
1326            Specifies the maximum number of blobs to return,
1327            including all :class:`BlobPrefix` elements. If the request does not specify
1328            num_results or specifies a value greater than 5,000, the server will
1329            return up to 5,000 items. Setting num_results to a value less than
1330            or equal to zero results in error response code 400 (Bad Request).
1331        :param ~azure.storage.blob.models.Include include:
1332            Specifies one or more additional datasets to include in the response.
1333        :param str delimiter:
1334            When the request includes this parameter, the operation
1335            returns a :class:`~azure.storage.blob.models.BlobPrefix` element in the
1336            result list that acts as a placeholder for all blobs whose names begin
1337            with the same substring up to the appearance of the delimiter character.
1338            The delimiter may be a single character or a string.
1339        :param str marker:
1340            An opaque continuation token. This value can be retrieved from the
1341            next_marker field of a previous generator object if num_results was
1342            specified and that generator has finished enumerating results. If
1343            specified, this generator will begin returning results from the point
1344            where the previous generator stopped.
1345        :param int timeout:
1346            The timeout parameter is expressed in seconds.
1347        '''
1348        operation_context = _OperationContext(location_lock=True)
1349        args = (container_name,)
1350        kwargs = {'prefix': prefix, 'marker': marker, 'max_results': num_results,
1351                  'include': include, 'delimiter': delimiter, 'timeout': timeout,
1352                  '_context': operation_context,
1353                  '_converter': _convert_xml_to_blob_name_list}
1354        resp = self._list_blobs(*args, **kwargs)
1355
1356        return ListGenerator(resp, self._list_blobs, args, kwargs)
1357
1358    def _list_blobs(self, container_name, prefix=None, marker=None,
1359                    max_results=None, include=None, delimiter=None, timeout=None,
1360                    _context=None, _converter=None):
1361        '''
1362        Returns the list of blobs under the specified container.
1363
1364        :param str container_name:
1365            Name of existing container.
1366        :parm str prefix:
1367            Filters the results to return only blobs whose names
1368            begin with the specified prefix.
1369        :param str marker:
1370            A string value that identifies the portion of the list
1371            to be returned with the next list operation. The operation returns
1372            a next_marker value within the response body if the list returned was
1373            not complete. The marker value may then be used in a subsequent
1374            call to request the next set of list items. The marker value is
1375            opaque to the client.
1376        :param int max_results:
1377            Specifies the maximum number of blobs to return,
1378            including all :class:`~azure.storage.blob.models.BlobPrefix` elements. If the request does not specify
1379            max_results or specifies a value greater than 5,000, the server will
1380            return up to 5,000 items. Setting max_results to a value less than
1381            or equal to zero results in error response code 400 (Bad Request).
1382        :param str include:
1383            Specifies one or more datasets to include in the
1384            response. To specify more than one of these options on the URI,
1385            you must separate each option with a comma. Valid values are:
1386                snapshots:
1387                    Specifies that snapshots should be included in the
1388                    enumeration. Snapshots are listed from oldest to newest in
1389                    the response.
1390                metadata:
1391                    Specifies that blob metadata be returned in the response.
1392                uncommittedblobs:
1393                    Specifies that blobs for which blocks have been uploaded,
1394                    but which have not been committed using Put Block List
1395                    (REST API), be included in the response.
1396                copy:
1397                    Version 2012-02-12 and newer. Specifies that metadata
1398                    related to any current or previous Copy Blob operation
1399                    should be included in the response.
1400                deleted:
1401                    Version 2017-07-29 and newer. Specifies that soft deleted blobs
1402                    which are retained by the service should be included
1403                    in the response.
1404        :param str delimiter:
1405            When the request includes this parameter, the operation
1406            returns a :class:`~azure.storage.blob.models.BlobPrefix` element in the response body that acts as a
1407            placeholder for all blobs whose names begin with the same
1408            substring up to the appearance of the delimiter character. The
1409            delimiter may be a single character or a string.
1410        :param int timeout:
1411            The timeout parameter is expressed in seconds.
1412        '''
1413        _validate_not_none('container_name', container_name)
1414        request = HTTPRequest()
1415        request.method = 'GET'
1416        request.host_locations = self._get_host_locations(secondary=True)
1417        request.path = _get_path(container_name)
1418        request.query = {
1419            'restype': 'container',
1420            'comp': 'list',
1421            'prefix': _to_str(prefix),
1422            'delimiter': _to_str(delimiter),
1423            'marker': _to_str(marker),
1424            'maxresults': _int_to_str(max_results),
1425            'include': _to_str(include),
1426            'timeout': _int_to_str(timeout),
1427        }
1428
1429        return self._perform_request(request, _converter, operation_context=_context)
1430
1431    def get_blob_account_information(self, container_name=None, blob_name=None, timeout=None):
1432        """
1433        Gets information related to the storage account.
1434        The information can also be retrieved if the user has a SAS to a container or blob.
1435
1436        :param str container_name:
1437            Name of existing container.
1438            Optional, unless using a SAS token to a specific container or blob, in which case it's required.
1439        :param str blob_name:
1440            Name of existing blob.
1441            Optional, unless using a SAS token to a specific blob, in which case it's required.
1442        :param int timeout:
1443            The timeout parameter is expressed in seconds.
1444        :return: The :class:`~azure.storage.blob.models.AccountInformation`.
1445        """
1446        request = HTTPRequest()
1447        request.method = 'HEAD'
1448        request.host_locations = self._get_host_locations(secondary=True)
1449        request.path = _get_path(container_name, blob_name)
1450        request.query = {
1451            'restype': 'account',
1452            'comp': 'properties',
1453            'timeout': _int_to_str(timeout),
1454        }
1455
1456        return self._perform_request(request, _parse_account_information)
1457
1458    def get_blob_service_stats(self, timeout=None):
1459        '''
1460        Retrieves statistics related to replication for the Blob service. It is
1461        only available when read-access geo-redundant replication is enabled for
1462        the storage account.
1463
1464        With geo-redundant replication, Azure Storage maintains your data durable
1465        in two locations. In both locations, Azure Storage constantly maintains
1466        multiple healthy replicas of your data. The location where you read,
1467        create, update, or delete data is the primary storage account location.
1468        The primary location exists in the region you choose at the time you
1469        create an account via the Azure Management Azure classic portal, for
1470        example, North Central US. The location to which your data is replicated
1471        is the secondary location. The secondary location is automatically
1472        determined based on the location of the primary; it is in a second data
1473        center that resides in the same region as the primary location. Read-only
1474        access is available from the secondary location, if read-access geo-redundant
1475        replication is enabled for your storage account.
1476
1477        :param int timeout:
1478            The timeout parameter is expressed in seconds.
1479        :return: The blob service stats.
1480        :rtype: :class:`~azure.storage.common.models.ServiceStats`
1481        '''
1482        request = HTTPRequest()
1483        request.method = 'GET'
1484        request.host_locations = self._get_host_locations(primary=False, secondary=True)
1485        request.path = _get_path()
1486        request.query = {
1487            'restype': 'service',
1488            'comp': 'stats',
1489            'timeout': _int_to_str(timeout),
1490        }
1491
1492        return self._perform_request(request, _convert_xml_to_service_stats)
1493
1494    def set_blob_service_properties(
1495            self, logging=None, hour_metrics=None, minute_metrics=None,
1496            cors=None, target_version=None, timeout=None, delete_retention_policy=None, static_website=None):
1497        '''
1498        Sets the properties of a storage account's Blob service, including
1499        Azure Storage Analytics. If an element (ex Logging) is left as None, the
1500        existing settings on the service for that functionality are preserved.
1501
1502        :param logging:
1503            Groups the Azure Analytics Logging settings.
1504        :type logging:
1505            :class:`~azure.storage.common.models.Logging`
1506        :param hour_metrics:
1507            The hour metrics settings provide a summary of request
1508            statistics grouped by API in hourly aggregates for blobs.
1509        :type hour_metrics:
1510            :class:`~azure.storage.common.models.Metrics`
1511        :param minute_metrics:
1512            The minute metrics settings provide request statistics
1513            for each minute for blobs.
1514        :type minute_metrics:
1515            :class:`~azure.storage.common.models.Metrics`
1516        :param cors:
1517            You can include up to five CorsRule elements in the
1518            list. If an empty list is specified, all CORS rules will be deleted,
1519            and CORS will be disabled for the service.
1520        :type cors: list(:class:`~azure.storage.common.models.CorsRule`)
1521        :param str target_version:
1522            Indicates the default version to use for requests if an incoming
1523            request's version is not specified.
1524        :param int timeout:
1525            The timeout parameter is expressed in seconds.
1526        :param delete_retention_policy:
1527            The delete retention policy specifies whether to retain deleted blobs.
1528            It also specifies the number of days and versions of blob to keep.
1529        :type delete_retention_policy:
1530            :class:`~azure.storage.common.models.DeleteRetentionPolicy`
1531        :param static_website:
1532            Specifies whether the static website feature is enabled,
1533            and if yes, indicates the index document and 404 error document to use.
1534        :type static_website:
1535            :class:`~azure.storage.common.models.StaticWebsite`
1536        '''
1537        if all(parameter is None for parameter in [logging, hour_metrics, minute_metrics, cors, target_version,
1538                                                   delete_retention_policy, static_website]):
1539
1540            raise ValueError("set_blob_service_properties should be called with at least one parameter")
1541
1542        request = HTTPRequest()
1543        request.method = 'PUT'
1544        request.host_locations = self._get_host_locations()
1545        request.path = _get_path()
1546        request.query = {
1547            'restype': 'service',
1548            'comp': 'properties',
1549            'timeout': _int_to_str(timeout),
1550        }
1551        request.body = _get_request_body(
1552            _convert_service_properties_to_xml(logging, hour_metrics, minute_metrics,
1553                                               cors, target_version, delete_retention_policy, static_website))
1554
1555        self._perform_request(request)
1556
1557    def get_blob_service_properties(self, timeout=None):
1558        '''
1559        Gets the properties of a storage account's Blob service, including
1560        Azure Storage Analytics.
1561
1562        :param int timeout:
1563            The timeout parameter is expressed in seconds.
1564        :return: The blob :class:`~azure.storage.common.models.ServiceProperties` with an attached
1565            target_version property.
1566        '''
1567        request = HTTPRequest()
1568        request.method = 'GET'
1569        request.host_locations = self._get_host_locations(secondary=True)
1570        request.path = _get_path()
1571        request.query = {
1572            'restype': 'service',
1573            'comp': 'properties',
1574            'timeout': _int_to_str(timeout),
1575        }
1576
1577        return self._perform_request(request, _convert_xml_to_service_properties)
1578
1579    def get_blob_properties(
1580            self, container_name, blob_name, snapshot=None, lease_id=None,
1581            if_modified_since=None, if_unmodified_since=None, if_match=None,
1582            if_none_match=None, timeout=None):
1583        '''
1584        Returns all user-defined metadata, standard HTTP properties, and
1585        system properties for the blob. It does not return the content of the blob.
1586        Returns :class:`~azure.storage.blob.models.Blob`
1587        with :class:`~azure.storage.blob.models.BlobProperties` and a metadata dict.
1588
1589        :param str container_name:
1590            Name of existing container.
1591        :param str blob_name:
1592            Name of existing blob.
1593        :param str snapshot:
1594            The snapshot parameter is an opaque DateTime value that,
1595            when present, specifies the blob snapshot to retrieve.
1596        :param str lease_id:
1597            Required if the blob has an active lease.
1598        :param datetime if_modified_since:
1599            A DateTime value. Azure expects the date value passed in to be UTC.
1600            If timezone is included, any non-UTC datetimes will be converted to UTC.
1601            If a date is passed in without timezone info, it is assumed to be UTC.
1602            Specify this header to perform the operation only
1603            if the resource has been modified since the specified time.
1604        :param datetime if_unmodified_since:
1605            A DateTime value. Azure expects the date value passed in to be UTC.
1606            If timezone is included, any non-UTC datetimes will be converted to UTC.
1607            If a date is passed in without timezone info, it is assumed to be UTC.
1608            Specify this header to perform the operation only if
1609            the resource has not been modified since the specified date/time.
1610        :param str if_match:
1611            An ETag value, or the wildcard character (*). Specify this header to perform
1612            the operation only if the resource's ETag matches the value specified.
1613        :param str if_none_match:
1614            An ETag value, or the wildcard character (*). Specify this header
1615            to perform the operation only if the resource's ETag does not match
1616            the value specified. Specify the wildcard character (*) to perform
1617            the operation only if the resource does not exist, and fail the
1618            operation if it does exist.
1619        :param int timeout:
1620            The timeout parameter is expressed in seconds.
1621        :return: a blob object including properties and metadata.
1622        :rtype: :class:`~azure.storage.blob.models.Blob`
1623        '''
1624        _validate_not_none('container_name', container_name)
1625        _validate_not_none('blob_name', blob_name)
1626        request = HTTPRequest()
1627        request.method = 'HEAD'
1628        request.host_locations = self._get_host_locations(secondary=True)
1629        request.path = _get_path(container_name, blob_name)
1630        request.query = {
1631            'snapshot': _to_str(snapshot),
1632            'timeout': _int_to_str(timeout),
1633        }
1634        request.headers = {
1635            'x-ms-lease-id': _to_str(lease_id),
1636            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
1637            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
1638            'If-Match': _to_str(if_match),
1639            'If-None-Match': _to_str(if_none_match),
1640        }
1641
1642        return self._perform_request(request, _parse_blob, [blob_name, snapshot])
1643
1644    def set_blob_properties(
1645            self, container_name, blob_name, content_settings=None, lease_id=None,
1646            if_modified_since=None, if_unmodified_since=None, if_match=None,
1647            if_none_match=None, timeout=None):
1648        '''
1649        Sets system properties on the blob. If one property is set for the
1650        content_settings, all properties will be overriden.
1651
1652        :param str container_name:
1653            Name of existing container.
1654        :param str blob_name:
1655            Name of existing blob.
1656        :param ~azure.storage.blob.models.ContentSettings content_settings:
1657            ContentSettings object used to set blob properties.
1658        :param str lease_id:
1659            Required if the blob has an active lease.
1660        :param datetime if_modified_since:
1661            A DateTime value. Azure expects the date value passed in to be UTC.
1662            If timezone is included, any non-UTC datetimes will be converted to UTC.
1663            If a date is passed in without timezone info, it is assumed to be UTC.
1664            Specify this header to perform the operation only
1665            if the resource has been modified since the specified time.
1666        :param datetime if_unmodified_since:
1667            A DateTime value. Azure expects the date value passed in to be UTC.
1668            If timezone is included, any non-UTC datetimes will be converted to UTC.
1669            If a date is passed in without timezone info, it is assumed to be UTC.
1670            Specify this header to perform the operation only if
1671            the resource has not been modified since the specified date/time.
1672        :param str if_match:
1673            An ETag value, or the wildcard character (*). Specify this header to perform
1674            the operation only if the resource's ETag matches the value specified.
1675        :param str if_none_match:
1676            An ETag value, or the wildcard character (*). Specify this header
1677            to perform the operation only if the resource's ETag does not match
1678            the value specified. Specify the wildcard character (*) to perform
1679            the operation only if the resource does not exist, and fail the
1680            operation if it does exist.
1681        :param int timeout:
1682            The timeout parameter is expressed in seconds.
1683        :return: ETag and last modified properties for the updated Blob
1684        :rtype: :class:`~azure.storage.blob.models.ResourceProperties`
1685        '''
1686        _validate_not_none('container_name', container_name)
1687        _validate_not_none('blob_name', blob_name)
1688        request = HTTPRequest()
1689        request.method = 'PUT'
1690        request.host_locations = self._get_host_locations()
1691        request.path = _get_path(container_name, blob_name)
1692        request.query = {
1693            'comp': 'properties',
1694            'timeout': _int_to_str(timeout),
1695        }
1696        request.headers = {
1697            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
1698            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
1699            'If-Match': _to_str(if_match),
1700            'If-None-Match': _to_str(if_none_match),
1701            'x-ms-lease-id': _to_str(lease_id)
1702        }
1703        if content_settings is not None:
1704            request.headers.update(content_settings._to_headers())
1705
1706        return self._perform_request(request, _parse_base_properties)
1707
1708    def exists(self, container_name, blob_name=None, snapshot=None, timeout=None):
1709        '''
1710        Returns a boolean indicating whether the container exists (if blob_name
1711        is None), or otherwise a boolean indicating whether the blob exists.
1712
1713        :param str container_name:
1714            Name of a container.
1715        :param str blob_name:
1716            Name of a blob. If None, the container will be checked for existence.
1717        :param str snapshot:
1718            The snapshot parameter is an opaque DateTime value that,
1719            when present, specifies the snapshot.
1720        :param int timeout:
1721            The timeout parameter is expressed in seconds.
1722        :return: A boolean indicating whether the resource exists.
1723        :rtype: bool
1724        '''
1725        _validate_not_none('container_name', container_name)
1726        try:
1727            # make head request to see if container/blob/snapshot exists
1728            request = HTTPRequest()
1729            request.method = 'GET' if blob_name is None else 'HEAD'
1730            request.host_locations = self._get_host_locations(secondary=True)
1731            request.path = _get_path(container_name, blob_name)
1732            request.query = {
1733                'snapshot': _to_str(snapshot),
1734                'timeout': _int_to_str(timeout),
1735                'restype': 'container' if blob_name is None else None,
1736            }
1737
1738            expected_errors = [_CONTAINER_NOT_FOUND_ERROR_CODE] if blob_name is None \
1739                else [_CONTAINER_NOT_FOUND_ERROR_CODE, _BLOB_NOT_FOUND_ERROR_CODE]
1740            self._perform_request(request, expected_errors=expected_errors)
1741
1742            return True
1743        except AzureHttpError as ex:
1744            _dont_fail_not_exist(ex)
1745            return False
1746
1747    def _get_blob(
1748            self, container_name, blob_name, snapshot=None, start_range=None,
1749            end_range=None, validate_content=False, lease_id=None, if_modified_since=None,
1750            if_unmodified_since=None, if_match=None, if_none_match=None, timeout=None,
1751            _context=None):
1752        '''
1753        Downloads a blob's content, metadata, and properties. You can also
1754        call this API to read a snapshot. You can specify a range if you don't
1755        need to download the blob in its entirety. If no range is specified,
1756        the full blob will be downloaded.
1757
1758        See get_blob_to_* for high level functions that handle the download
1759        of large blobs with automatic chunking and progress notifications.
1760
1761        :param str container_name:
1762            Name of existing container.
1763        :param str blob_name:
1764            Name of existing blob.
1765        :param str snapshot:
1766            The snapshot parameter is an opaque DateTime value that,
1767            when present, specifies the blob snapshot to retrieve.
1768        :param int start_range:
1769            Start of byte range to use for downloading a section of the blob.
1770            If no end_range is given, all bytes after the start_range will be downloaded.
1771            The start_range and end_range params are inclusive.
1772            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
1773        :param int end_range:
1774            End of byte range to use for downloading a section of the blob.
1775            If end_range is given, start_range must be provided.
1776            The start_range and end_range params are inclusive.
1777            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
1778        :param bool validate_content:
1779            When this is set to True and specified together with the Range header,
1780            the service returns the MD5 hash for the range, as long as the range
1781            is less than or equal to 4 MB in size.
1782        :param str lease_id:
1783            Required if the blob has an active lease.
1784        :param datetime if_modified_since:
1785            A DateTime value. Azure expects the date value passed in to be UTC.
1786            If timezone is included, any non-UTC datetimes will be converted to UTC.
1787            If a date is passed in without timezone info, it is assumed to be UTC.
1788            Specify this header to perform the operation only
1789            if the resource has been modified since the specified time.
1790        :param datetime if_unmodified_since:
1791            A DateTime value. Azure expects the date value passed in to be UTC.
1792            If timezone is included, any non-UTC datetimes will be converted to UTC.
1793            If a date is passed in without timezone info, it is assumed to be UTC.
1794            Specify this header to perform the operation only if
1795            the resource has not been modified since the specified date/time.
1796        :param str if_match:
1797            An ETag value, or the wildcard character (*). Specify this header to perform
1798            the operation only if the resource's ETag matches the value specified.
1799        :param str if_none_match:
1800            An ETag value, or the wildcard character (*). Specify this header
1801            to perform the operation only if the resource's ETag does not match
1802            the value specified. Specify the wildcard character (*) to perform
1803            the operation only if the resource does not exist, and fail the
1804            operation if it does exist.
1805        :param int timeout:
1806            The timeout parameter is expressed in seconds.
1807        :return: A Blob with content, properties, and metadata.
1808        :rtype: :class:`~azure.storage.blob.models.Blob`
1809        '''
1810        _validate_not_none('container_name', container_name)
1811        _validate_not_none('blob_name', blob_name)
1812        _validate_decryption_required(self.require_encryption,
1813                                      self.key_encryption_key,
1814                                      self.key_resolver_function)
1815
1816        start_offset, end_offset = 0, 0
1817        if self.key_encryption_key is not None or self.key_resolver_function is not None:
1818            if start_range is not None:
1819                # Align the start of the range along a 16 byte block
1820                start_offset = start_range % 16
1821                start_range -= start_offset
1822
1823                # Include an extra 16 bytes for the IV if necessary
1824                # Because of the previous offsetting, start_range will always
1825                # be a multiple of 16.
1826                if start_range > 0:
1827                    start_offset += 16
1828                    start_range -= 16
1829
1830            if end_range is not None:
1831                # Align the end of the range along a 16 byte block
1832                end_offset = 15 - (end_range % 16)
1833                end_range += end_offset
1834
1835        request = HTTPRequest()
1836        request.method = 'GET'
1837        request.host_locations = self._get_host_locations(secondary=True)
1838        request.path = _get_path(container_name, blob_name)
1839        request.query = {
1840            'snapshot': _to_str(snapshot),
1841            'timeout': _int_to_str(timeout),
1842        }
1843        request.headers = {
1844            'x-ms-lease-id': _to_str(lease_id),
1845            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
1846            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
1847            'If-Match': _to_str(if_match),
1848            'If-None-Match': _to_str(if_none_match),
1849        }
1850        _validate_and_format_range_headers(
1851            request,
1852            start_range,
1853            end_range,
1854            start_range_required=False,
1855            end_range_required=False,
1856            check_content_md5=validate_content)
1857
1858        return self._perform_request(request, _parse_blob,
1859                                     [blob_name, snapshot, validate_content, self.require_encryption,
1860                                      self.key_encryption_key, self.key_resolver_function,
1861                                      start_offset, end_offset],
1862                                     operation_context=_context)
1863
1864    def get_blob_to_path(
1865            self, container_name, blob_name, file_path, open_mode='wb',
1866            snapshot=None, start_range=None, end_range=None,
1867            validate_content=False, progress_callback=None,
1868            max_connections=2, lease_id=None, if_modified_since=None,
1869            if_unmodified_since=None, if_match=None, if_none_match=None,
1870            timeout=None):
1871        '''
1872        Downloads a blob to a file path, with automatic chunking and progress
1873        notifications. Returns an instance of :class:`~azure.storage.blob.models.Blob` with
1874        properties and metadata.
1875
1876        :param str container_name:
1877            Name of existing container.
1878        :param str blob_name:
1879            Name of existing blob.
1880        :param str file_path:
1881            Path of file to write out to.
1882        :param str open_mode:
1883            Mode to use when opening the file. Note that specifying append only
1884            open_mode prevents parallel download. So, max_connections must be set
1885            to 1 if this open_mode is used.
1886        :param str snapshot:
1887            The snapshot parameter is an opaque DateTime value that,
1888            when present, specifies the blob snapshot to retrieve.
1889        :param int start_range:
1890            Start of byte range to use for downloading a section of the blob.
1891            If no end_range is given, all bytes after the start_range will be downloaded.
1892            The start_range and end_range params are inclusive.
1893            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
1894        :param int end_range:
1895            End of byte range to use for downloading a section of the blob.
1896            If end_range is given, start_range must be provided.
1897            The start_range and end_range params are inclusive.
1898            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
1899        :param bool validate_content:
1900            If set to true, validates an MD5 hash for each retrieved portion of
1901            the blob. This is primarily valuable for detecting bitflips on the wire
1902            if using http instead of https as https (the default) will already
1903            validate. Note that the service will only return transactional MD5s
1904            for chunks 4MB or less so the first get request will be of size
1905            self.MAX_CHUNK_GET_SIZE instead of self.MAX_SINGLE_GET_SIZE. If
1906            self.MAX_CHUNK_GET_SIZE was set to greater than 4MB an error will be
1907            thrown. As computing the MD5 takes processing time and more requests
1908            will need to be done due to the reduced chunk size there may be some
1909            increase in latency.
1910        :param progress_callback:
1911            Callback for progress with signature function(current, total)
1912            where current is the number of bytes transfered so far, and total is
1913            the size of the blob if known.
1914        :type progress_callback: func(current, total)
1915        :param int max_connections:
1916            If set to 2 or greater, an initial get will be done for the first
1917            self.MAX_SINGLE_GET_SIZE bytes of the blob. If this is the entire blob,
1918            the method returns at this point. If it is not, it will download the
1919            remaining data parallel using the number of threads equal to
1920            max_connections. Each chunk will be of size self.MAX_CHUNK_GET_SIZE.
1921            If set to 1, a single large get request will be done. This is not
1922            generally recommended but available if very few threads should be
1923            used, network requests are very expensive, or a non-seekable stream
1924            prevents parallel download. This may also be useful if many blobs are
1925            expected to be empty as an extra request is required for empty blobs
1926            if max_connections is greater than 1.
1927        :param str lease_id:
1928            Required if the blob has an active lease.
1929        :param datetime if_modified_since:
1930            A DateTime value. Azure expects the date value passed in to be UTC.
1931            If timezone is included, any non-UTC datetimes will be converted to UTC.
1932            If a date is passed in without timezone info, it is assumed to be UTC.
1933            Specify this header to perform the operation only
1934            if the resource has been modified since the specified time.
1935        :param datetime if_unmodified_since:
1936            A DateTime value. Azure expects the date value passed in to be UTC.
1937            If timezone is included, any non-UTC datetimes will be converted to UTC.
1938            If a date is passed in without timezone info, it is assumed to be UTC.
1939            Specify this header to perform the operation only if
1940            the resource has not been modified since the specified date/time.
1941        :param str if_match:
1942            An ETag value, or the wildcard character (*). Specify this header to perform
1943            the operation only if the resource's ETag matches the value specified.
1944        :param str if_none_match:
1945            An ETag value, or the wildcard character (*). Specify this header
1946            to perform the operation only if the resource's ETag does not match
1947            the value specified. Specify the wildcard character (*) to perform
1948            the operation only if the resource does not exist, and fail the
1949            operation if it does exist.
1950        :param int timeout:
1951            The timeout parameter is expressed in seconds. This method may make
1952            multiple calls to the Azure service and the timeout will apply to
1953            each call individually.
1954        :return: A Blob with properties and metadata. If max_connections is greater
1955            than 1, the content_md5 (if set on the blob) will not be returned. If you
1956            require this value, either use get_blob_properties or set max_connections
1957            to 1.
1958        :rtype: :class:`~azure.storage.blob.models.Blob`
1959        '''
1960        _validate_not_none('container_name', container_name)
1961        _validate_not_none('blob_name', blob_name)
1962        _validate_not_none('file_path', file_path)
1963        _validate_not_none('open_mode', open_mode)
1964
1965        if max_connections > 1 and 'a' in open_mode:
1966            raise ValueError(_ERROR_PARALLEL_NOT_SEEKABLE)
1967
1968        with open(file_path, open_mode) as stream:
1969            blob = self.get_blob_to_stream(
1970                container_name,
1971                blob_name,
1972                stream,
1973                snapshot,
1974                start_range,
1975                end_range,
1976                validate_content,
1977                progress_callback,
1978                max_connections,
1979                lease_id,
1980                if_modified_since,
1981                if_unmodified_since,
1982                if_match,
1983                if_none_match,
1984                timeout)
1985
1986        return blob
1987
1988    def get_blob_to_stream(
1989            self, container_name, blob_name, stream, snapshot=None,
1990            start_range=None, end_range=None, validate_content=False,
1991            progress_callback=None, max_connections=2, lease_id=None,
1992            if_modified_since=None, if_unmodified_since=None, if_match=None,
1993            if_none_match=None, timeout=None):
1994
1995        '''
1996        Downloads a blob to a stream, with automatic chunking and progress
1997        notifications. Returns an instance of :class:`~azure.storage.blob.models.Blob` with
1998        properties and metadata.
1999
2000        :param str container_name:
2001            Name of existing container.
2002        :param str blob_name:
2003            Name of existing blob.
2004        :param io.IOBase stream:
2005            Opened stream to write to.
2006        :param str snapshot:
2007            The snapshot parameter is an opaque DateTime value that,
2008            when present, specifies the blob snapshot to retrieve.
2009        :param int start_range:
2010            Start of byte range to use for downloading a section of the blob.
2011            If no end_range is given, all bytes after the start_range will be downloaded.
2012            The start_range and end_range params are inclusive.
2013            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2014        :param int end_range:
2015            End of byte range to use for downloading a section of the blob.
2016            If end_range is given, start_range must be provided.
2017            The start_range and end_range params are inclusive.
2018            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2019        :param bool validate_content:
2020            If set to true, validates an MD5 hash for each retrieved portion of
2021            the blob. This is primarily valuable for detecting bitflips on the wire
2022            if using http instead of https as https (the default) will already
2023            validate. Note that the service will only return transactional MD5s
2024            for chunks 4MB or less so the first get request will be of size
2025            self.MAX_CHUNK_GET_SIZE instead of self.MAX_SINGLE_GET_SIZE. If
2026            self.MAX_CHUNK_GET_SIZE was set to greater than 4MB an error will be
2027            thrown. As computing the MD5 takes processing time and more requests
2028            will need to be done due to the reduced chunk size there may be some
2029            increase in latency.
2030        :param progress_callback:
2031            Callback for progress with signature function(current, total)
2032            where current is the number of bytes transfered so far, and total is
2033            the size of the blob if known.
2034        :type progress_callback: func(current, total)
2035        :param int max_connections:
2036            If set to 2 or greater, an initial get will be done for the first
2037            self.MAX_SINGLE_GET_SIZE bytes of the blob. If this is the entire blob,
2038            the method returns at this point. If it is not, it will download the
2039            remaining data parallel using the number of threads equal to
2040            max_connections. Each chunk will be of size self.MAX_CHUNK_GET_SIZE.
2041            If set to 1, a single large get request will be done. This is not
2042            generally recommended but available if very few threads should be
2043            used, network requests are very expensive, or a non-seekable stream
2044            prevents parallel download. This may also be useful if many blobs are
2045            expected to be empty as an extra request is required for empty blobs
2046            if max_connections is greater than 1.
2047        :param str lease_id:
2048            Required if the blob has an active lease.
2049        :param datetime if_modified_since:
2050            A DateTime value. Azure expects the date value passed in to be UTC.
2051            If timezone is included, any non-UTC datetimes will be converted to UTC.
2052            If a date is passed in without timezone info, it is assumed to be UTC.
2053            Specify this header to perform the operation only
2054            if the resource has been modified since the specified time.
2055        :param datetime if_unmodified_since:
2056            A DateTime value. Azure expects the date value passed in to be UTC.
2057            If timezone is included, any non-UTC datetimes will be converted to UTC.
2058            If a date is passed in without timezone info, it is assumed to be UTC.
2059            Specify this header to perform the operation only if
2060            the resource has not been modified since the specified date/time.
2061        :param str if_match:
2062            An ETag value, or the wildcard character (*). Specify this header to perform
2063            the operation only if the resource's ETag matches the value specified.
2064        :param str if_none_match:
2065            An ETag value, or the wildcard character (*). Specify this header
2066            to perform the operation only if the resource's ETag does not match
2067            the value specified. Specify the wildcard character (*) to perform
2068            the operation only if the resource does not exist, and fail the
2069            operation if it does exist.
2070        :param int timeout:
2071            The timeout parameter is expressed in seconds. This method may make
2072            multiple calls to the Azure service and the timeout will apply to
2073            each call individually.
2074        :return: A Blob with properties and metadata. If max_connections is greater
2075            than 1, the content_md5 (if set on the blob) will not be returned. If you
2076            require this value, either use get_blob_properties or set max_connections
2077            to 1.
2078        :rtype: :class:`~azure.storage.blob.models.Blob`
2079        '''
2080        _validate_not_none('container_name', container_name)
2081        _validate_not_none('blob_name', blob_name)
2082        _validate_not_none('stream', stream)
2083
2084        if end_range is not None:
2085            _validate_not_none("start_range", start_range)
2086
2087        # the stream must be seekable if parallel download is required
2088        if max_connections > 1:
2089            if sys.version_info >= (3,) and not stream.seekable():
2090                raise ValueError(_ERROR_PARALLEL_NOT_SEEKABLE)
2091
2092            try:
2093                stream.seek(stream.tell())
2094            except (NotImplementedError, AttributeError):
2095                raise ValueError(_ERROR_PARALLEL_NOT_SEEKABLE)
2096
2097        # The service only provides transactional MD5s for chunks under 4MB.
2098        # If validate_content is on, get only self.MAX_CHUNK_GET_SIZE for the first
2099        # chunk so a transactional MD5 can be retrieved.
2100        first_get_size = self.MAX_SINGLE_GET_SIZE if not validate_content else self.MAX_CHUNK_GET_SIZE
2101
2102        initial_request_start = start_range if start_range is not None else 0
2103
2104        if end_range is not None and end_range - start_range < first_get_size:
2105            initial_request_end = end_range
2106        else:
2107            initial_request_end = initial_request_start + first_get_size - 1
2108
2109        # Send a context object to make sure we always retry to the initial location
2110        operation_context = _OperationContext(location_lock=True)
2111        try:
2112            blob = self._get_blob(container_name,
2113                                  blob_name,
2114                                  snapshot,
2115                                  start_range=initial_request_start,
2116                                  end_range=initial_request_end,
2117                                  validate_content=validate_content,
2118                                  lease_id=lease_id,
2119                                  if_modified_since=if_modified_since,
2120                                  if_unmodified_since=if_unmodified_since,
2121                                  if_match=if_match,
2122                                  if_none_match=if_none_match,
2123                                  timeout=timeout,
2124                                  _context=operation_context)
2125
2126            # Parse the total blob size and adjust the download size if ranges
2127            # were specified
2128            blob_size = _parse_length_from_content_range(blob.properties.content_range)
2129            if end_range is not None:
2130                # Use the end_range unless it is over the end of the blob
2131                download_size = min(blob_size, end_range - start_range + 1)
2132            elif start_range is not None:
2133                download_size = blob_size - start_range
2134            else:
2135                download_size = blob_size
2136        except AzureHttpError as ex:
2137            if start_range is None and ex.status_code == 416:
2138                # Get range will fail on an empty blob. If the user did not
2139                # request a range, do a regular get request in order to get
2140                # any properties.
2141                blob = self._get_blob(container_name,
2142                                      blob_name,
2143                                      snapshot,
2144                                      validate_content=validate_content,
2145                                      lease_id=lease_id,
2146                                      if_modified_since=if_modified_since,
2147                                      if_unmodified_since=if_unmodified_since,
2148                                      if_match=if_match,
2149                                      if_none_match=if_none_match,
2150                                      timeout=timeout,
2151                                      _context=operation_context)
2152
2153                # Set the download size to empty
2154                download_size = 0
2155            else:
2156                raise ex
2157
2158        # Mark the first progress chunk. If the blob is small or this is a single
2159        # shot download, this is the only call
2160        if progress_callback:
2161            progress_callback(blob.properties.content_length, download_size)
2162
2163        # Write the content to the user stream
2164        # Clear blob content since output has been written to user stream
2165        if blob.content is not None:
2166            stream.write(blob.content)
2167            blob.content = None
2168
2169        # If the blob is small, the download is complete at this point.
2170        # If blob size is large, download the rest of the blob in chunks.
2171        if blob.properties.content_length != download_size:
2172            # Lock on the etag. This can be overriden by the user by specifying '*'
2173            if_match = if_match if if_match is not None else blob.properties.etag
2174
2175            end_blob = blob_size
2176            if end_range is not None:
2177                # Use the end_range unless it is over the end of the blob
2178                end_blob = min(blob_size, end_range + 1)
2179
2180            _download_blob_chunks(
2181                self,
2182                container_name,
2183                blob_name,
2184                snapshot,
2185                download_size,
2186                self.MAX_CHUNK_GET_SIZE,
2187                first_get_size,
2188                initial_request_end + 1,  # start where the first download ended
2189                end_blob,
2190                stream,
2191                max_connections,
2192                progress_callback,
2193                validate_content,
2194                lease_id,
2195                if_modified_since,
2196                if_unmodified_since,
2197                if_match,
2198                if_none_match,
2199                timeout,
2200                operation_context
2201            )
2202
2203            # Set the content length to the download size instead of the size of
2204            # the last range
2205            blob.properties.content_length = download_size
2206
2207            # Overwrite the content range to the user requested range
2208            blob.properties.content_range = 'bytes {0}-{1}/{2}'.format(start_range, end_range, blob_size)
2209
2210            # Overwrite the content MD5 as it is the MD5 for the last range instead
2211            # of the stored MD5
2212            # TODO: Set to the stored MD5 when the service returns this
2213            blob.properties.content_md5 = None
2214
2215        return blob
2216
2217    def get_blob_to_bytes(
2218            self, container_name, blob_name, snapshot=None,
2219            start_range=None, end_range=None, validate_content=False,
2220            progress_callback=None, max_connections=2, lease_id=None,
2221            if_modified_since=None, if_unmodified_since=None, if_match=None,
2222            if_none_match=None, timeout=None):
2223        '''
2224        Downloads a blob as an array of bytes, with automatic chunking and
2225        progress notifications. Returns an instance of :class:`~azure.storage.blob.models.Blob` with
2226        properties, metadata, and content.
2227
2228        :param str container_name:
2229            Name of existing container.
2230        :param str blob_name:
2231            Name of existing blob.
2232        :param str snapshot:
2233            The snapshot parameter is an opaque DateTime value that,
2234            when present, specifies the blob snapshot to retrieve.
2235        :param int start_range:
2236            Start of byte range to use for downloading a section of the blob.
2237            If no end_range is given, all bytes after the start_range will be downloaded.
2238            The start_range and end_range params are inclusive.
2239            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2240        :param int end_range:
2241            End of byte range to use for downloading a section of the blob.
2242            If end_range is given, start_range must be provided.
2243            The start_range and end_range params are inclusive.
2244            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2245        :param bool validate_content:
2246            If set to true, validates an MD5 hash for each retrieved portion of
2247            the blob. This is primarily valuable for detecting bitflips on the wire
2248            if using http instead of https as https (the default) will already
2249            validate. Note that the service will only return transactional MD5s
2250            for chunks 4MB or less so the first get request will be of size
2251            self.MAX_CHUNK_GET_SIZE instead of self.MAX_SINGLE_GET_SIZE. If
2252            self.MAX_CHUNK_GET_SIZE was set to greater than 4MB an error will be
2253            thrown. As computing the MD5 takes processing time and more requests
2254            will need to be done due to the reduced chunk size there may be some
2255            increase in latency.
2256        :param progress_callback:
2257            Callback for progress with signature function(current, total)
2258            where current is the number of bytes transfered so far, and total is
2259            the size of the blob if known.
2260        :type progress_callback: func(current, total)
2261        :param int max_connections:
2262            If set to 2 or greater, an initial get will be done for the first
2263            self.MAX_SINGLE_GET_SIZE bytes of the blob. If this is the entire blob,
2264            the method returns at this point. If it is not, it will download the
2265            remaining data parallel using the number of threads equal to
2266            max_connections. Each chunk will be of size self.MAX_CHUNK_GET_SIZE.
2267            If set to 1, a single large get request will be done. This is not
2268            generally recommended but available if very few threads should be
2269            used, network requests are very expensive, or a non-seekable stream
2270            prevents parallel download. This may also be useful if many blobs are
2271            expected to be empty as an extra request is required for empty blobs
2272            if max_connections is greater than 1.
2273        :param str lease_id:
2274            Required if the blob has an active lease.
2275        :param datetime if_modified_since:
2276            A DateTime value. Azure expects the date value passed in to be UTC.
2277            If timezone is included, any non-UTC datetimes will be converted to UTC.
2278            If a date is passed in without timezone info, it is assumed to be UTC.
2279            Specify this header to perform the operation only
2280            if the resource has been modified since the specified time.
2281        :param datetime if_unmodified_since:
2282            A DateTime value. Azure expects the date value passed in to be UTC.
2283            If timezone is included, any non-UTC datetimes will be converted to UTC.
2284            If a date is passed in without timezone info, it is assumed to be UTC.
2285            Specify this header to perform the operation only if
2286            the resource has not been modified since the specified date/time.
2287        :param str if_match:
2288            An ETag value, or the wildcard character (*). Specify this header to perform
2289            the operation only if the resource's ETag matches the value specified.
2290        :param str if_none_match:
2291            An ETag value, or the wildcard character (*). Specify this header
2292            to perform the operation only if the resource's ETag does not match
2293            the value specified. Specify the wildcard character (*) to perform
2294            the operation only if the resource does not exist, and fail the
2295            operation if it does exist.
2296        :param int timeout:
2297            The timeout parameter is expressed in seconds. This method may make
2298            multiple calls to the Azure service and the timeout will apply to
2299            each call individually.
2300        :return: A Blob with properties and metadata. If max_connections is greater
2301            than 1, the content_md5 (if set on the blob) will not be returned. If you
2302            require this value, either use get_blob_properties or set max_connections
2303            to 1.
2304        :rtype: :class:`~azure.storage.blob.models.Blob`
2305        '''
2306        _validate_not_none('container_name', container_name)
2307        _validate_not_none('blob_name', blob_name)
2308
2309        stream = BytesIO()
2310        blob = self.get_blob_to_stream(
2311            container_name,
2312            blob_name,
2313            stream,
2314            snapshot,
2315            start_range,
2316            end_range,
2317            validate_content,
2318            progress_callback,
2319            max_connections,
2320            lease_id,
2321            if_modified_since,
2322            if_unmodified_since,
2323            if_match,
2324            if_none_match,
2325            timeout)
2326
2327        blob.content = stream.getvalue()
2328        return blob
2329
2330    def get_blob_to_text(
2331            self, container_name, blob_name, encoding='utf-8', snapshot=None,
2332            start_range=None, end_range=None, validate_content=False,
2333            progress_callback=None, max_connections=2, lease_id=None,
2334            if_modified_since=None, if_unmodified_since=None, if_match=None,
2335            if_none_match=None, timeout=None):
2336        '''
2337        Downloads a blob as unicode text, with automatic chunking and progress
2338        notifications. Returns an instance of :class:`~azure.storage.blob.models.Blob` with
2339        properties, metadata, and content.
2340
2341        :param str container_name:
2342            Name of existing container.
2343        :param str blob_name:
2344            Name of existing blob.
2345        :param str encoding:
2346            Python encoding to use when decoding the blob data.
2347        :param str snapshot:
2348            The snapshot parameter is an opaque DateTime value that,
2349            when present, specifies the blob snapshot to retrieve.
2350        :param int start_range:
2351            Start of byte range to use for downloading a section of the blob.
2352            If no end_range is given, all bytes after the start_range will be downloaded.
2353            The start_range and end_range params are inclusive.
2354            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2355        :param int end_range:
2356            End of byte range to use for downloading a section of the blob.
2357            If end_range is given, start_range must be provided.
2358            The start_range and end_range params are inclusive.
2359            Ex: start_range=0, end_range=511 will download first 512 bytes of blob.
2360        :param bool validate_content:
2361            If set to true, validates an MD5 hash for each retrieved portion of
2362            the blob. This is primarily valuable for detecting bitflips on the wire
2363            if using http instead of https as https (the default) will already
2364            validate. Note that the service will only return transactional MD5s
2365            for chunks 4MB or less so the first get request will be of size
2366            self.MAX_CHUNK_GET_SIZE instead of self.MAX_SINGLE_GET_SIZE. If
2367            self.MAX_CHUNK_GET_SIZE was set to greater than 4MB an error will be
2368            thrown. As computing the MD5 takes processing time and more requests
2369            will need to be done due to the reduced chunk size there may be some
2370            increase in latency.
2371        :param progress_callback:
2372            Callback for progress with signature function(current, total)
2373            where current is the number of bytes transfered so far, and total is
2374            the size of the blob if known.
2375        :type progress_callback: func(current, total)
2376        :param int max_connections:
2377            If set to 2 or greater, an initial get will be done for the first
2378            self.MAX_SINGLE_GET_SIZE bytes of the blob. If this is the entire blob,
2379            the method returns at this point. If it is not, it will download the
2380            remaining data parallel using the number of threads equal to
2381            max_connections. Each chunk will be of size self.MAX_CHUNK_GET_SIZE.
2382            If set to 1, a single large get request will be done. This is not
2383            generally recommended but available if very few threads should be
2384            used, network requests are very expensive, or a non-seekable stream
2385            prevents parallel download. This may also be useful if many blobs are
2386            expected to be empty as an extra request is required for empty blobs
2387            if max_connections is greater than 1.
2388        :param str lease_id:
2389            Required if the blob has an active lease.
2390        :param datetime if_modified_since:
2391            A DateTime value. Azure expects the date value passed in to be UTC.
2392            If timezone is included, any non-UTC datetimes will be converted to UTC.
2393            If a date is passed in without timezone info, it is assumed to be UTC.
2394            Specify this header to perform the operation only
2395            if the resource has been modified since the specified time.
2396        :param datetime if_unmodified_since:
2397            A DateTime value. Azure expects the date value passed in to be UTC.
2398            If timezone is included, any non-UTC datetimes will be converted to UTC.
2399            If a date is passed in without timezone info, it is assumed to be UTC.
2400            Specify this header to perform the operation only if
2401            the resource has not been modified since the specified date/time.
2402        :param str if_match:
2403            An ETag value, or the wildcard character (*). Specify this header to perform
2404            the operation only if the resource's ETag matches the value specified.
2405        :param str if_none_match:
2406            An ETag value, or the wildcard character (*). Specify this header
2407            to perform the operation only if the resource's ETag does not match
2408            the value specified. Specify the wildcard character (*) to perform
2409            the operation only if the resource does not exist, and fail the
2410            operation if it does exist.
2411        :param int timeout:
2412            The timeout parameter is expressed in seconds. This method may make
2413            multiple calls to the Azure service and the timeout will apply to
2414            each call individually.
2415        :return: A Blob with properties and metadata. If max_connections is greater
2416            than 1, the content_md5 (if set on the blob) will not be returned. If you
2417            require this value, either use get_blob_properties or set max_connections
2418            to 1.
2419        :rtype: :class:`~azure.storage.blob.models.Blob`
2420        '''
2421        _validate_not_none('container_name', container_name)
2422        _validate_not_none('blob_name', blob_name)
2423        _validate_not_none('encoding', encoding)
2424
2425        blob = self.get_blob_to_bytes(container_name,
2426                                      blob_name,
2427                                      snapshot,
2428                                      start_range,
2429                                      end_range,
2430                                      validate_content,
2431                                      progress_callback,
2432                                      max_connections,
2433                                      lease_id,
2434                                      if_modified_since,
2435                                      if_unmodified_since,
2436                                      if_match,
2437                                      if_none_match,
2438                                      timeout)
2439        blob.content = blob.content.decode(encoding)
2440        return blob
2441
2442    def get_blob_metadata(
2443            self, container_name, blob_name, snapshot=None, lease_id=None,
2444            if_modified_since=None, if_unmodified_since=None, if_match=None,
2445            if_none_match=None, timeout=None):
2446        '''
2447        Returns all user-defined metadata for the specified blob or snapshot.
2448
2449        :param str container_name:
2450            Name of existing container.
2451        :param str blob_name:
2452            Name of existing blob.
2453        :param str snapshot:
2454            The snapshot parameter is an opaque value that,
2455            when present, specifies the blob snapshot to retrieve.
2456        :param str lease_id:
2457            Required if the blob has an active lease.
2458        :param datetime if_modified_since:
2459            A DateTime value. Azure expects the date value passed in to be UTC.
2460            If timezone is included, any non-UTC datetimes will be converted to UTC.
2461            If a date is passed in without timezone info, it is assumed to be UTC.
2462            Specify this header to perform the operation only
2463            if the resource has been modified since the specified time.
2464        :param datetime if_unmodified_since:
2465            A DateTime value. Azure expects the date value passed in to be UTC.
2466            If timezone is included, any non-UTC datetimes will be converted to UTC.
2467            If a date is passed in without timezone info, it is assumed to be UTC.
2468            Specify this header to perform the operation only if
2469            the resource has not been modified since the specified date/time.
2470        :param str if_match:
2471            An ETag value, or the wildcard character (*). Specify this header to perform
2472            the operation only if the resource's ETag matches the value specified.
2473        :param str if_none_match:
2474            An ETag value, or the wildcard character (*). Specify this header
2475            to perform the operation only if the resource's ETag does not match
2476            the value specified. Specify the wildcard character (*) to perform
2477            the operation only if the resource does not exist, and fail the
2478            operation if it does exist.
2479        :param int timeout:
2480            The timeout parameter is expressed in seconds.
2481        :return:
2482            A dictionary representing the blob metadata name, value pairs.
2483        :rtype: dict(str, str)
2484        '''
2485        _validate_not_none('container_name', container_name)
2486        _validate_not_none('blob_name', blob_name)
2487        request = HTTPRequest()
2488        request.method = 'GET'
2489        request.host_locations = self._get_host_locations(secondary=True)
2490        request.path = _get_path(container_name, blob_name)
2491        request.query = {
2492            'snapshot': _to_str(snapshot),
2493            'comp': 'metadata',
2494            'timeout': _int_to_str(timeout),
2495        }
2496        request.headers = {
2497            'x-ms-lease-id': _to_str(lease_id),
2498            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
2499            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
2500            'If-Match': _to_str(if_match),
2501            'If-None-Match': _to_str(if_none_match),
2502        }
2503
2504        return self._perform_request(request, _parse_metadata)
2505
2506    def set_blob_metadata(self, container_name, blob_name,
2507                          metadata=None, lease_id=None,
2508                          if_modified_since=None, if_unmodified_since=None,
2509                          if_match=None, if_none_match=None, timeout=None):
2510        '''
2511        Sets user-defined metadata for the specified blob as one or more
2512        name-value pairs.
2513
2514        :param str container_name:
2515            Name of existing container.
2516        :param str blob_name:
2517            Name of existing blob.
2518        :param metadata:
2519            Dict containing name and value pairs. Each call to this operation
2520            replaces all existing metadata attached to the blob. To remove all
2521            metadata from the blob, call this operation with no metadata headers.
2522        :type metadata: dict(str, str)
2523        :param str lease_id:
2524            Required if the blob has an active lease.
2525        :param datetime if_modified_since:
2526            A DateTime value. Azure expects the date value passed in to be UTC.
2527            If timezone is included, any non-UTC datetimes will be converted to UTC.
2528            If a date is passed in without timezone info, it is assumed to be UTC.
2529            Specify this header to perform the operation only
2530            if the resource has been modified since the specified time.
2531        :param datetime if_unmodified_since:
2532            A DateTime value. Azure expects the date value passed in to be UTC.
2533            If timezone is included, any non-UTC datetimes will be converted to UTC.
2534            If a date is passed in without timezone info, it is assumed to be UTC.
2535            Specify this header to perform the operation only if
2536            the resource has not been modified since the specified date/time.
2537        :param str if_match:
2538            An ETag value, or the wildcard character (*). Specify this header to perform
2539            the operation only if the resource's ETag matches the value specified.
2540        :param str if_none_match:
2541            An ETag value, or the wildcard character (*). Specify this header
2542            to perform the operation only if the resource's ETag does not match
2543            the value specified. Specify the wildcard character (*) to perform
2544            the operation only if the resource does not exist, and fail the
2545            operation if it does exist.
2546        :param int timeout:
2547            The timeout parameter is expressed in seconds.
2548        :return: ETag and last modified properties for the updated Blob
2549        :rtype: :class:`~azure.storage.blob.models.ResourceProperties`
2550        '''
2551        _validate_not_none('container_name', container_name)
2552        _validate_not_none('blob_name', blob_name)
2553        request = HTTPRequest()
2554        request.method = 'PUT'
2555        request.host_locations = self._get_host_locations()
2556        request.path = _get_path(container_name, blob_name)
2557        request.query = {
2558            'comp': 'metadata',
2559            'timeout': _int_to_str(timeout),
2560        }
2561        request.headers = {
2562            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
2563            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
2564            'If-Match': _to_str(if_match),
2565            'If-None-Match': _to_str(if_none_match),
2566            'x-ms-lease-id': _to_str(lease_id),
2567        }
2568        _add_metadata_headers(metadata, request)
2569
2570        return self._perform_request(request, _parse_base_properties)
2571
2572    def _lease_blob_impl(self, container_name, blob_name,
2573                         lease_action, lease_id,
2574                         lease_duration, lease_break_period,
2575                         proposed_lease_id, if_modified_since,
2576                         if_unmodified_since, if_match, if_none_match, timeout=None):
2577        '''
2578        Establishes and manages a lease on a blob for write and delete operations.
2579        The Lease Blob operation can be called in one of five modes:
2580            Acquire, to request a new lease.
2581            Renew, to renew an existing lease.
2582            Change, to change the ID of an existing lease.
2583            Release, to free the lease if it is no longer needed so that another
2584                client may immediately acquire a lease against the blob.
2585            Break, to end the lease but ensure that another client cannot acquire
2586                a new lease until the current lease period has expired.
2587
2588        :param str container_name:
2589            Name of existing container.
2590        :param str blob_name:
2591            Name of existing blob.
2592        :param str lease_action:
2593            Possible _LeaseActions acquire|renew|release|break|change
2594        :param str lease_id:
2595            Required if the blob has an active lease.
2596        :param int lease_duration:
2597            Specifies the duration of the lease, in seconds, or negative one
2598            (-1) for a lease that never expires. A non-infinite lease can be
2599            between 15 and 60 seconds. A lease duration cannot be changed
2600            using renew or change.
2601        :param int lease_break_period:
2602            For a break operation, this is the proposed duration of
2603            seconds that the lease should continue before it is broken, between
2604            0 and 60 seconds. This break period is only used if it is shorter
2605            than the time remaining on the lease. If longer, the time remaining
2606            on the lease is used. A new lease will not be available before the
2607            break period has expired, but the lease may be held for longer than
2608            the break period. If this header does not appear with a break
2609            operation, a fixed-duration lease breaks after the remaining lease
2610            period elapses, and an infinite lease breaks immediately.
2611        :param str proposed_lease_id:
2612            Optional for acquire, required for change. Proposed lease ID, in a
2613            GUID string format. The Blob service returns 400 (Invalid request)
2614            if the proposed lease ID is not in the correct format.
2615        :param datetime if_modified_since:
2616            A DateTime value. Azure expects the date value passed in to be UTC.
2617            If timezone is included, any non-UTC datetimes will be converted to UTC.
2618            If a date is passed in without timezone info, it is assumed to be UTC.
2619            Specify this header to perform the operation only
2620            if the resource has been modified since the specified time.
2621        :param datetime if_unmodified_since:
2622            A DateTime value. Azure expects the date value passed in to be UTC.
2623            If timezone is included, any non-UTC datetimes will be converted to UTC.
2624            If a date is passed in without timezone info, it is assumed to be UTC.
2625            Specify this header to perform the operation only if
2626            the resource has not been modified since the specified date/time.
2627        :param str if_match:
2628            An ETag value, or the wildcard character (*). Specify this header to perform
2629            the operation only if the resource's ETag matches the value specified.
2630        :param str if_none_match:
2631            An ETag value, or the wildcard character (*). Specify this header
2632            to perform the operation only if the resource's ETag does not match
2633            the value specified. Specify the wildcard character (*) to perform
2634            the operation only if the resource does not exist, and fail the
2635            operation if it does exist.
2636        :param int timeout:
2637            The timeout parameter is expressed in seconds.
2638        :return:
2639            Response headers returned from the service call.
2640        :rtype: dict(str, str)
2641        '''
2642        _validate_not_none('container_name', container_name)
2643        _validate_not_none('blob_name', blob_name)
2644        _validate_not_none('lease_action', lease_action)
2645        request = HTTPRequest()
2646        request.method = 'PUT'
2647        request.host_locations = self._get_host_locations()
2648        request.path = _get_path(container_name, blob_name)
2649        request.query = {
2650            'comp': 'lease',
2651            'timeout': _int_to_str(timeout),
2652        }
2653        request.headers = {
2654            'x-ms-lease-id': _to_str(lease_id),
2655            'x-ms-lease-action': _to_str(lease_action),
2656            'x-ms-lease-duration': _to_str(lease_duration),
2657            'x-ms-lease-break-period': _to_str(lease_break_period),
2658            'x-ms-proposed-lease-id': _to_str(proposed_lease_id),
2659            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
2660            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
2661            'If-Match': _to_str(if_match),
2662            'If-None-Match': _to_str(if_none_match),
2663        }
2664
2665        return self._perform_request(request, _parse_lease)
2666
2667    def acquire_blob_lease(self, container_name, blob_name,
2668                           lease_duration=-1,
2669                           proposed_lease_id=None,
2670                           if_modified_since=None,
2671                           if_unmodified_since=None,
2672                           if_match=None,
2673                           if_none_match=None, timeout=None):
2674        '''
2675        Requests a new lease. If the blob does not have an active lease, the Blob
2676        service creates a lease on the blob and returns a new lease ID.
2677
2678        :param str container_name:
2679            Name of existing container.
2680        :param str blob_name:
2681            Name of existing blob.
2682        :param int lease_duration:
2683            Specifies the duration of the lease, in seconds, or negative one
2684            (-1) for a lease that never expires. A non-infinite lease can be
2685            between 15 and 60 seconds. A lease duration cannot be changed
2686            using renew or change. Default is -1 (infinite lease).
2687        :param str proposed_lease_id:
2688            Proposed lease ID, in a GUID string format. The Blob service
2689            returns 400 (Invalid request) if the proposed lease ID is not
2690            in the correct format.
2691        :param datetime if_modified_since:
2692            A DateTime value. Azure expects the date value passed in to be UTC.
2693            If timezone is included, any non-UTC datetimes will be converted to UTC.
2694            If a date is passed in without timezone info, it is assumed to be UTC.
2695            Specify this header to perform the operation only
2696            if the resource has been modified since the specified time.
2697        :param datetime if_unmodified_since:
2698            A DateTime value. Azure expects the date value passed in to be UTC.
2699            If timezone is included, any non-UTC datetimes will be converted to UTC.
2700            If a date is passed in without timezone info, it is assumed to be UTC.
2701            Specify this header to perform the operation only if
2702            the resource has not been modified since the specified date/time.
2703        :param str if_match:
2704            An ETag value, or the wildcard character (*). Specify this header to perform
2705            the operation only if the resource's ETag matches the value specified.
2706        :param str if_none_match:
2707            An ETag value, or the wildcard character (*). Specify this header
2708            to perform the operation only if the resource's ETag does not match
2709            the value specified. Specify the wildcard character (*) to perform
2710            the operation only if the resource does not exist, and fail the
2711            operation if it does exist.
2712        :param int timeout:
2713            The timeout parameter is expressed in seconds.
2714        :return: the lease ID of the newly created lease.
2715        :return: str
2716        '''
2717        _validate_not_none('lease_duration', lease_duration)
2718
2719        if lease_duration != -1 and \
2720                (lease_duration < 15 or lease_duration > 60):
2721            raise ValueError(_ERROR_INVALID_LEASE_DURATION)
2722        lease = self._lease_blob_impl(container_name,
2723                                      blob_name,
2724                                      _LeaseActions.Acquire,
2725                                      None,  # lease_id
2726                                      lease_duration,
2727                                      None,  # lease_break_period
2728                                      proposed_lease_id,
2729                                      if_modified_since,
2730                                      if_unmodified_since,
2731                                      if_match,
2732                                      if_none_match,
2733                                      timeout)
2734        return lease['id']
2735
2736    def renew_blob_lease(self, container_name, blob_name,
2737                         lease_id, if_modified_since=None,
2738                         if_unmodified_since=None, if_match=None,
2739                         if_none_match=None, timeout=None):
2740        '''
2741        Renews the lease. The lease can be renewed if the lease ID specified on
2742        the request matches that associated with the blob. Note that the lease may
2743        be renewed even if it has expired as long as the blob has not been modified
2744        or leased again since the expiration of that lease. When you renew a lease,
2745        the lease duration clock resets.
2746
2747        :param str container_name:
2748            Name of existing container.
2749        :param str blob_name:
2750            Name of existing blob.
2751        :param str lease_id:
2752            Lease ID for active lease.
2753        :param datetime if_modified_since:
2754            A DateTime value. Azure expects the date value passed in to be UTC.
2755            If timezone is included, any non-UTC datetimes will be converted to UTC.
2756            If a date is passed in without timezone info, it is assumed to be UTC.
2757            Specify this header to perform the operation only
2758            if the resource has been modified since the specified time.
2759        :param datetime if_unmodified_since:
2760            A DateTime value. Azure expects the date value passed in to be UTC.
2761            If timezone is included, any non-UTC datetimes will be converted to UTC.
2762            If a date is passed in without timezone info, it is assumed to be UTC.
2763            Specify this header to perform the operation only if
2764            the resource has not been modified since the specified date/time.
2765        :param str if_match:
2766            An ETag value, or the wildcard character (*). Specify this header to perform
2767            the operation only if the resource's ETag matches the value specified.
2768        :param str if_none_match:
2769            An ETag value, or the wildcard character (*). Specify this header
2770            to perform the operation only if the resource's ETag does not match
2771            the value specified. Specify the wildcard character (*) to perform
2772            the operation only if the resource does not exist, and fail the
2773            operation if it does exist.
2774        :param int timeout:
2775            The timeout parameter is expressed in seconds.
2776        :return: the lease ID of the renewed lease.
2777        :return: str
2778        '''
2779        _validate_not_none('lease_id', lease_id)
2780
2781        lease = self._lease_blob_impl(container_name,
2782                                      blob_name,
2783                                      _LeaseActions.Renew,
2784                                      lease_id,
2785                                      None,  # lease_duration
2786                                      None,  # lease_break_period
2787                                      None,  # proposed_lease_id
2788                                      if_modified_since,
2789                                      if_unmodified_since,
2790                                      if_match,
2791                                      if_none_match,
2792                                      timeout)
2793        return lease['id']
2794
2795    def release_blob_lease(self, container_name, blob_name,
2796                           lease_id, if_modified_since=None,
2797                           if_unmodified_since=None, if_match=None,
2798                           if_none_match=None, timeout=None):
2799        '''
2800        Releases the lease. The lease may be released if the lease ID specified on the
2801        request matches that associated with the blob. Releasing the lease allows another
2802        client to immediately acquire the lease for the blob as soon as the release is complete.
2803
2804        :param str container_name:
2805            Name of existing container.
2806        :param str blob_name:
2807            Name of existing blob.
2808        :param str lease_id:
2809            Lease ID for active lease.
2810        :param datetime if_modified_since:
2811            A DateTime value. Azure expects the date value passed in to be UTC.
2812            If timezone is included, any non-UTC datetimes will be converted to UTC.
2813            If a date is passed in without timezone info, it is assumed to be UTC.
2814            Specify this header to perform the operation only
2815            if the resource has been modified since the specified time.
2816        :param datetime if_unmodified_since:
2817            A DateTime value. Azure expects the date value passed in to be UTC.
2818            If timezone is included, any non-UTC datetimes will be converted to UTC.
2819            If a date is passed in without timezone info, it is assumed to be UTC.
2820            Specify this header to perform the operation only if
2821            the resource has not been modified since the specified date/time.
2822        :param str if_match:
2823            An ETag value, or the wildcard character (*). Specify this header to perform
2824            the operation only if the resource's ETag matches the value specified.
2825        :param str if_none_match:
2826            An ETag value, or the wildcard character (*). Specify this header
2827            to perform the operation only if the resource's ETag does not match
2828            the value specified. Specify the wildcard character (*) to perform
2829            the operation only if the resource does not exist, and fail the
2830            operation if it does exist.
2831        :param int timeout:
2832            The timeout parameter is expressed in seconds.
2833        '''
2834        _validate_not_none('lease_id', lease_id)
2835
2836        self._lease_blob_impl(container_name,
2837                              blob_name,
2838                              _LeaseActions.Release,
2839                              lease_id,
2840                              None,  # lease_duration
2841                              None,  # lease_break_period
2842                              None,  # proposed_lease_id
2843                              if_modified_since,
2844                              if_unmodified_since,
2845                              if_match,
2846                              if_none_match,
2847                              timeout)
2848
2849    def break_blob_lease(self, container_name, blob_name,
2850                         lease_break_period=None,
2851                         if_modified_since=None,
2852                         if_unmodified_since=None,
2853                         if_match=None,
2854                         if_none_match=None, timeout=None):
2855        '''
2856        Breaks the lease, if the blob has an active lease. Once a lease is broken,
2857        it cannot be renewed. Any authorized request can break the lease; the request
2858        is not required to specify a matching lease ID. When a lease is broken,
2859        the lease break period is allowed to elapse, during which time no lease operation
2860        except break and release can be performed on the blob. When a lease is successfully
2861        broken, the response indicates the interval in seconds until a new lease can be acquired.
2862
2863        A lease that has been broken can also be released, in which case another client may
2864        immediately acquire the lease on the blob.
2865
2866        :param str container_name:
2867            Name of existing container.
2868        :param str blob_name:
2869            Name of existing blob.
2870        :param int lease_break_period:
2871            For a break operation, this is the proposed duration of
2872            seconds that the lease should continue before it is broken, between
2873            0 and 60 seconds. This break period is only used if it is shorter
2874            than the time remaining on the lease. If longer, the time remaining
2875            on the lease is used. A new lease will not be available before the
2876            break period has expired, but the lease may be held for longer than
2877            the break period. If this header does not appear with a break
2878            operation, a fixed-duration lease breaks after the remaining lease
2879            period elapses, and an infinite lease breaks immediately.
2880        :param datetime if_modified_since:
2881            A DateTime value. Azure expects the date value passed in to be UTC.
2882            If timezone is included, any non-UTC datetimes will be converted to UTC.
2883            If a date is passed in without timezone info, it is assumed to be UTC.
2884            Specify this header to perform the operation only
2885            if the resource has been modified since the specified time.
2886        :param datetime if_unmodified_since:
2887            A DateTime value. Azure expects the date value passed in to be UTC.
2888            If timezone is included, any non-UTC datetimes will be converted to UTC.
2889            If a date is passed in without timezone info, it is assumed to be UTC.
2890            Specify this header to perform the operation only if
2891            the resource has not been modified since the specified date/time.
2892        :param str if_match:
2893            An ETag value, or the wildcard character (*). Specify this header to perform
2894            the operation only if the resource's ETag matches the value specified.
2895        :param str if_none_match:
2896            An ETag value, or the wildcard character (*). Specify this header
2897            to perform the operation only if the resource's ETag does not match
2898            the value specified. Specify the wildcard character (*) to perform
2899            the operation only if the resource does not exist, and fail the
2900            operation if it does exist.
2901        :param int timeout:
2902            The timeout parameter is expressed in seconds.
2903        :return: Approximate time remaining in the lease period, in seconds.
2904        :return: int
2905        '''
2906        if (lease_break_period is not None) and (lease_break_period < 0 or lease_break_period > 60):
2907            raise ValueError(_ERROR_INVALID_LEASE_BREAK_PERIOD)
2908
2909        lease = self._lease_blob_impl(container_name,
2910                                      blob_name,
2911                                      _LeaseActions.Break,
2912                                      None,  # lease_id
2913                                      None,  # lease_duration
2914                                      lease_break_period,
2915                                      None,  # proposed_lease_id
2916                                      if_modified_since,
2917                                      if_unmodified_since,
2918                                      if_match,
2919                                      if_none_match,
2920                                      timeout)
2921        return lease['time']
2922
2923    def change_blob_lease(self, container_name, blob_name,
2924                          lease_id,
2925                          proposed_lease_id,
2926                          if_modified_since=None,
2927                          if_unmodified_since=None,
2928                          if_match=None,
2929                          if_none_match=None, timeout=None):
2930        '''
2931        Changes the lease ID of an active lease. A change must include the current
2932        lease ID and a new lease ID.
2933
2934        :param str container_name:
2935            Name of existing container.
2936        :param str blob_name:
2937            Name of existing blob.
2938        :param str lease_id:
2939            Required if the blob has an active lease.
2940        :param str proposed_lease_id:
2941            Proposed lease ID, in a GUID string format. The Blob service returns
2942            400 (Invalid request) if the proposed lease ID is not in the correct format.
2943        :param datetime if_modified_since:
2944            A DateTime value. Azure expects the date value passed in to be UTC.
2945            If timezone is included, any non-UTC datetimes will be converted to UTC.
2946            If a date is passed in without timezone info, it is assumed to be UTC.
2947            Specify this header to perform the operation only
2948            if the resource has been modified since the specified time.
2949        :param datetime if_unmodified_since:
2950            A DateTime value. Azure expects the date value passed in to be UTC.
2951            If timezone is included, any non-UTC datetimes will be converted to UTC.
2952            If a date is passed in without timezone info, it is assumed to be UTC.
2953            Specify this header to perform the operation only if
2954            the resource has not been modified since the specified date/time.
2955        :param str if_match:
2956            An ETag value, or the wildcard character (*). Specify this header to perform
2957            the operation only if the resource's ETag matches the value specified.
2958        :param str if_none_match:
2959            An ETag value, or the wildcard character (*). Specify this header
2960            to perform the operation only if the resource's ETag does not match
2961            the value specified. Specify the wildcard character (*) to perform
2962            the operation only if the resource does not exist, and fail the
2963            operation if it does exist.
2964        :param int timeout:
2965            The timeout parameter is expressed in seconds.
2966        '''
2967        self._lease_blob_impl(container_name,
2968                              blob_name,
2969                              _LeaseActions.Change,
2970                              lease_id,
2971                              None,  # lease_duration
2972                              None,  # lease_break_period
2973                              proposed_lease_id,
2974                              if_modified_since,
2975                              if_unmodified_since,
2976                              if_match,
2977                              if_none_match,
2978                              timeout)
2979
2980    def snapshot_blob(self, container_name, blob_name,
2981                      metadata=None, if_modified_since=None,
2982                      if_unmodified_since=None, if_match=None,
2983                      if_none_match=None, lease_id=None, timeout=None):
2984        '''
2985        Creates a read-only snapshot of a blob.
2986
2987        :param str container_name:
2988            Name of existing container.
2989        :param str blob_name:
2990            Name of existing blob.
2991        :param metadata:
2992            Specifies a user-defined name-value pair associated with the blob.
2993            If no name-value pairs are specified, the operation will copy the
2994            base blob metadata to the snapshot. If one or more name-value pairs
2995            are specified, the snapshot is created with the specified metadata,
2996            and metadata is not copied from the base blob.
2997        :type metadata: dict(str, str)
2998        :param datetime if_modified_since:
2999            A DateTime value. Azure expects the date value passed in to be UTC.
3000            If timezone is included, any non-UTC datetimes will be converted to UTC.
3001            If a date is passed in without timezone info, it is assumed to be UTC.
3002            Specify this header to perform the operation only
3003            if the resource has been modified since the specified time.
3004        :param datetime if_unmodified_since:
3005            A DateTime value. Azure expects the date value passed in to be UTC.
3006            If timezone is included, any non-UTC datetimes will be converted to UTC.
3007            If a date is passed in without timezone info, it is assumed to be UTC.
3008            Specify this header to perform the operation only if
3009            the resource has not been modified since the specified date/time.
3010        :param str if_match:
3011            An ETag value, or the wildcard character (*). Specify this header to perform
3012            the operation only if the resource's ETag matches the value specified.
3013        :param str if_none_match:
3014            An ETag value, or the wildcard character (*). Specify this header
3015            to perform the operation only if the resource's ETag does not match
3016            the value specified. Specify the wildcard character (*) to perform
3017            the operation only if the resource does not exist, and fail the
3018            operation if it does exist.
3019        :param str lease_id:
3020            Required if the blob has an active lease.
3021        :param int timeout:
3022            The timeout parameter is expressed in seconds.
3023        :return: snapshot properties
3024        :rtype: :class:`~azure.storage.blob.models.Blob`
3025        '''
3026        _validate_not_none('container_name', container_name)
3027        _validate_not_none('blob_name', blob_name)
3028        request = HTTPRequest()
3029        request.method = 'PUT'
3030        request.host_locations = self._get_host_locations()
3031        request.path = _get_path(container_name, blob_name)
3032        request.query = {
3033            'comp': 'snapshot',
3034            'timeout': _int_to_str(timeout),
3035        }
3036        request.headers = {
3037            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
3038            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
3039            'If-Match': _to_str(if_match),
3040            'If-None-Match': _to_str(if_none_match),
3041            'x-ms-lease-id': _to_str(lease_id)
3042        }
3043        _add_metadata_headers(metadata, request)
3044
3045        return self._perform_request(request, _parse_snapshot_blob, [blob_name])
3046
3047    def copy_blob(self, container_name, blob_name, copy_source,
3048                  metadata=None,
3049                  source_if_modified_since=None,
3050                  source_if_unmodified_since=None,
3051                  source_if_match=None, source_if_none_match=None,
3052                  destination_if_modified_since=None,
3053                  destination_if_unmodified_since=None,
3054                  destination_if_match=None,
3055                  destination_if_none_match=None,
3056                  destination_lease_id=None,
3057                  source_lease_id=None, timeout=None):
3058        '''
3059        Copies a blob asynchronously. This operation returns a copy operation
3060        properties object, including a copy ID you can use to check or abort the
3061        copy operation. The Blob service copies blobs on a best-effort basis.
3062
3063        The source blob for a copy operation may be a block blob, an append blob,
3064        or a page blob. If the destination blob already exists, it must be of the
3065        same blob type as the source blob. Any existing destination blob will be
3066        overwritten. The destination blob cannot be modified while a copy operation
3067        is in progress.
3068
3069        When copying from a page blob, the Blob service creates a destination page
3070        blob of the source blob's length, initially containing all zeroes. Then
3071        the source page ranges are enumerated, and non-empty ranges are copied.
3072
3073        For a block blob or an append blob, the Blob service creates a committed
3074        blob of zero length before returning from this operation. When copying
3075        from a block blob, all committed blocks and their block IDs are copied.
3076        Uncommitted blocks are not copied. At the end of the copy operation, the
3077        destination blob will have the same committed block count as the source.
3078
3079        When copying from an append blob, all committed blocks are copied. At the
3080        end of the copy operation, the destination blob will have the same committed
3081        block count as the source.
3082
3083        For all blob types, you can call get_blob_properties on the destination
3084        blob to check the status of the copy operation. The final blob will be
3085        committed when the copy completes.
3086
3087        :param str container_name:
3088            Name of the destination container. The container must exist.
3089        :param str blob_name:
3090            Name of the destination blob. If the destination blob exists, it will
3091            be overwritten. Otherwise, it will be created.
3092        :param str copy_source:
3093            A URL of up to 2 KB in length that specifies an Azure file or blob.
3094            The value should be URL-encoded as it would appear in a request URI.
3095            If the source is in another account, the source must either be public
3096            or must be authenticated via a shared access signature. If the source
3097            is public, no authentication is required.
3098            Examples:
3099            https://myaccount.blob.core.windows.net/mycontainer/myblob
3100            https://myaccount.blob.core.windows.net/mycontainer/myblob?snapshot=<DateTime>
3101            https://otheraccount.blob.core.windows.net/mycontainer/myblob?sastoken
3102        :param metadata:
3103            Name-value pairs associated with the blob as metadata. If no name-value
3104            pairs are specified, the operation will copy the metadata from the
3105            source blob or file to the destination blob. If one or more name-value
3106            pairs are specified, the destination blob is created with the specified
3107            metadata, and metadata is not copied from the source blob or file.
3108        :type metadata: dict(str, str)
3109        :param datetime source_if_modified_since:
3110            A DateTime value. Azure expects the date value passed in to be UTC.
3111            If timezone is included, any non-UTC datetimes will be converted to UTC.
3112            If a date is passed in without timezone info, it is assumed to be UTC.
3113            Specify this conditional header to copy the blob only if the source
3114            blob has been modified since the specified date/time.
3115        :param datetime source_if_unmodified_since:
3116            A DateTime value. Azure expects the date value passed in to be UTC.
3117            If timezone is included, any non-UTC datetimes will be converted to UTC.
3118            If a date is passed in without timezone info, it is assumed to be UTC.
3119            Specify this conditional header to copy the blob only if the source blob
3120            has not been modified since the specified date/time.
3121        :param ETag source_if_match:
3122            An ETag value, or the wildcard character (*). Specify this conditional
3123            header to copy the source blob only if its ETag matches the value
3124            specified. If the ETag values do not match, the Blob service returns
3125            status code 412 (Precondition Failed). This header cannot be specified
3126            if the source is an Azure File.
3127        :param ETag source_if_none_match:
3128            An ETag value, or the wildcard character (*). Specify this conditional
3129            header to copy the blob only if its ETag does not match the value
3130            specified. If the values are identical, the Blob service returns status
3131            code 412 (Precondition Failed). This header cannot be specified if the
3132            source is an Azure File.
3133        :param datetime destination_if_modified_since:
3134            A DateTime value. Azure expects the date value passed in to be UTC.
3135            If timezone is included, any non-UTC datetimes will be converted to UTC.
3136            If a date is passed in without timezone info, it is assumed to be UTC.
3137            Specify this conditional header to copy the blob only
3138            if the destination blob has been modified since the specified date/time.
3139            If the destination blob has not been modified, the Blob service returns
3140            status code 412 (Precondition Failed).
3141        :param datetime destination_if_unmodified_since:
3142            A DateTime value. Azure expects the date value passed in to be UTC.
3143            If timezone is included, any non-UTC datetimes will be converted to UTC.
3144            If a date is passed in without timezone info, it is assumed to be UTC.
3145            Specify this conditional header to copy the blob only
3146            if the destination blob has not been modified since the specified
3147            date/time. If the destination blob has been modified, the Blob service
3148            returns status code 412 (Precondition Failed).
3149        :param ETag destination_if_match:
3150            An ETag value, or the wildcard character (*). Specify an ETag value for
3151            this conditional header to copy the blob only if the specified ETag value
3152            matches the ETag value for an existing destination blob. If the ETag for
3153            the destination blob does not match the ETag specified for If-Match, the
3154            Blob service returns status code 412 (Precondition Failed).
3155        :param ETag destination_if_none_match:
3156            An ETag value, or the wildcard character (*). Specify an ETag value for
3157            this conditional header to copy the blob only if the specified ETag value
3158            does not match the ETag value for the destination blob. Specify the wildcard
3159            character (*) to perform the operation only if the destination blob does not
3160            exist. If the specified condition isn't met, the Blob service returns status
3161            code 412 (Precondition Failed).
3162        :param str destination_lease_id:
3163            The lease ID specified for this header must match the lease ID of the
3164            destination blob. If the request does not include the lease ID or it is not
3165            valid, the operation fails with status code 412 (Precondition Failed).
3166        :param str source_lease_id:
3167            Specify this to perform the Copy Blob operation only if
3168            the lease ID given matches the active lease ID of the source blob.
3169        :param int timeout:
3170            The timeout parameter is expressed in seconds.
3171        :return: Copy operation properties such as status, source, and ID.
3172        :rtype: :class:`~azure.storage.blob.models.CopyProperties`
3173        '''
3174        return self._copy_blob(container_name, blob_name, copy_source,
3175                               metadata,
3176                               None,
3177                               source_if_modified_since, source_if_unmodified_since,
3178                               source_if_match, source_if_none_match,
3179                               destination_if_modified_since,
3180                               destination_if_unmodified_since,
3181                               destination_if_match,
3182                               destination_if_none_match,
3183                               destination_lease_id,
3184                               source_lease_id, timeout,
3185                               False, False)
3186
3187    def _copy_blob(self, container_name, blob_name, copy_source,
3188                   metadata=None,
3189                   premium_page_blob_tier=None,
3190                   source_if_modified_since=None,
3191                   source_if_unmodified_since=None,
3192                   source_if_match=None, source_if_none_match=None,
3193                   destination_if_modified_since=None,
3194                   destination_if_unmodified_since=None,
3195                   destination_if_match=None,
3196                   destination_if_none_match=None,
3197                   destination_lease_id=None,
3198                   source_lease_id=None, timeout=None,
3199                   incremental_copy=False,
3200                   requires_sync=None):
3201        '''
3202        See copy_blob for more details. This helper method
3203        allows for standard copies as well as incremental copies which are only supported for page blobs and sync
3204        copies which are only supported for block blobs.
3205        :param bool incremental_copy:
3206            Performs an incremental copy operation on a page blob instead of a standard copy operation.
3207        :param bool requires_sync:
3208            Enforces that the service will not return a response until the copy is complete.
3209        '''
3210        _validate_not_none('container_name', container_name)
3211        _validate_not_none('blob_name', blob_name)
3212        _validate_not_none('copy_source', copy_source)
3213
3214        if copy_source.startswith('/'):
3215            # Backwards compatibility for earlier versions of the SDK where
3216            # the copy source can be in the following formats:
3217            # - Blob in named container:
3218            #     /accountName/containerName/blobName
3219            # - Snapshot in named container:
3220            #     /accountName/containerName/blobName?snapshot=<DateTime>
3221            # - Blob in root container:
3222            #     /accountName/blobName
3223            # - Snapshot in root container:
3224            #     /accountName/blobName?snapshot=<DateTime>
3225            account, _, source = \
3226                copy_source.partition('/')[2].partition('/')
3227            copy_source = self.protocol + '://' + \
3228                          self.primary_endpoint + '/' + source
3229
3230        request = HTTPRequest()
3231        request.method = 'PUT'
3232        request.host_locations = self._get_host_locations()
3233        request.path = _get_path(container_name, blob_name)
3234
3235        if incremental_copy:
3236            request.query = {
3237                'comp': 'incrementalcopy',
3238                'timeout': _int_to_str(timeout),
3239            }
3240        else:
3241            request.query = {'timeout': _int_to_str(timeout)}
3242
3243        request.headers = {
3244            'x-ms-copy-source': _to_str(copy_source),
3245            'x-ms-source-if-modified-since': _to_str(source_if_modified_since),
3246            'x-ms-source-if-unmodified-since': _to_str(source_if_unmodified_since),
3247            'x-ms-source-if-match': _to_str(source_if_match),
3248            'x-ms-source-if-none-match': _to_str(source_if_none_match),
3249            'If-Modified-Since': _datetime_to_utc_string(destination_if_modified_since),
3250            'If-Unmodified-Since': _datetime_to_utc_string(destination_if_unmodified_since),
3251            'If-Match': _to_str(destination_if_match),
3252            'If-None-Match': _to_str(destination_if_none_match),
3253            'x-ms-lease-id': _to_str(destination_lease_id),
3254            'x-ms-source-lease-id': _to_str(source_lease_id),
3255            'x-ms-access-tier': _to_str(premium_page_blob_tier),
3256            'x-ms-requires-sync': _to_str(requires_sync)
3257        }
3258
3259        _add_metadata_headers(metadata, request)
3260
3261        return self._perform_request(request, _parse_properties, [BlobProperties]).copy
3262
3263    def abort_copy_blob(self, container_name, blob_name, copy_id,
3264                        lease_id=None, timeout=None):
3265        '''
3266         Aborts a pending copy_blob operation, and leaves a destination blob
3267         with zero length and full metadata.
3268
3269         :param str container_name:
3270             Name of destination container.
3271         :param str blob_name:
3272             Name of destination blob.
3273         :param str copy_id:
3274             Copy identifier provided in the copy.id of the original
3275             copy_blob operation.
3276         :param str lease_id:
3277             Required if the destination blob has an active infinite lease.
3278         :param int timeout:
3279             The timeout parameter is expressed in seconds.
3280        '''
3281        _validate_not_none('container_name', container_name)
3282        _validate_not_none('blob_name', blob_name)
3283        _validate_not_none('copy_id', copy_id)
3284        request = HTTPRequest()
3285        request.method = 'PUT'
3286        request.host_locations = self._get_host_locations()
3287        request.path = _get_path(container_name, blob_name)
3288        request.query = {
3289            'comp': 'copy',
3290            'copyid': _to_str(copy_id),
3291            'timeout': _int_to_str(timeout),
3292        }
3293        request.headers = {
3294            'x-ms-lease-id': _to_str(lease_id),
3295            'x-ms-copy-action': 'abort',
3296        }
3297
3298        self._perform_request(request)
3299
3300    def delete_blob(self, container_name, blob_name, snapshot=None,
3301                    lease_id=None, delete_snapshots=None,
3302                    if_modified_since=None, if_unmodified_since=None,
3303                    if_match=None, if_none_match=None, timeout=None):
3304        '''
3305        Marks the specified blob or snapshot for deletion.
3306        The blob is later deleted during garbage collection.
3307
3308        Note that in order to delete a blob, you must delete all of its
3309        snapshots. You can delete both at the same time with the Delete
3310        Blob operation.
3311
3312        If a delete retention policy is enabled for the service, then this operation soft deletes the blob or snapshot
3313        and retains the blob or snapshot for specified number of days.
3314        After specified number of days, blob's data is removed from the service during garbage collection.
3315        Soft deleted blob or snapshot is accessible through List Blobs API specifying include=Include.Deleted option.
3316        Soft-deleted blob or snapshot can be restored using Undelete API.
3317
3318        :param str container_name:
3319            Name of existing container.
3320        :param str blob_name:
3321            Name of existing blob.
3322        :param str snapshot:
3323            The snapshot parameter is an opaque DateTime value that,
3324            when present, specifies the blob snapshot to delete.
3325        :param str lease_id:
3326            Required if the blob has an active lease.
3327        :param ~azure.storage.blob.models.DeleteSnapshot delete_snapshots:
3328            Required if the blob has associated snapshots.
3329        :param datetime if_modified_since:
3330            A DateTime value. Azure expects the date value passed in to be UTC.
3331            If timezone is included, any non-UTC datetimes will be converted to UTC.
3332            If a date is passed in without timezone info, it is assumed to be UTC.
3333            Specify this header to perform the operation only
3334            if the resource has been modified since the specified time.
3335        :param datetime if_unmodified_since:
3336            A DateTime value. Azure expects the date value passed in to be UTC.
3337            If timezone is included, any non-UTC datetimes will be converted to UTC.
3338            If a date is passed in without timezone info, it is assumed to be UTC.
3339            Specify this header to perform the operation only if
3340            the resource has not been modified since the specified date/time.
3341        :param str if_match:
3342            An ETag value, or the wildcard character (*). Specify this header to perform
3343            the operation only if the resource's ETag matches the value specified.
3344        :param str if_none_match:
3345            An ETag value, or the wildcard character (*). Specify this header
3346            to perform the operation only if the resource's ETag does not match
3347            the value specified. Specify the wildcard character (*) to perform
3348            the operation only if the resource does not exist, and fail the
3349            operation if it does exist.
3350        :param int timeout:
3351            The timeout parameter is expressed in seconds.
3352        '''
3353        _validate_not_none('container_name', container_name)
3354        _validate_not_none('blob_name', blob_name)
3355        request = HTTPRequest()
3356        request.method = 'DELETE'
3357        request.host_locations = self._get_host_locations()
3358        request.path = _get_path(container_name, blob_name)
3359        request.headers = {
3360            'x-ms-lease-id': _to_str(lease_id),
3361            'x-ms-delete-snapshots': _to_str(delete_snapshots),
3362            'If-Modified-Since': _datetime_to_utc_string(if_modified_since),
3363            'If-Unmodified-Since': _datetime_to_utc_string(if_unmodified_since),
3364            'If-Match': _to_str(if_match),
3365            'If-None-Match': _to_str(if_none_match),
3366        }
3367        request.query = {
3368            'snapshot': _to_str(snapshot),
3369            'timeout': _int_to_str(timeout)
3370        }
3371
3372        self._perform_request(request)
3373
3374    def undelete_blob(self, container_name, blob_name, timeout=None):
3375        '''
3376        The undelete Blob operation restores the contents and metadata of soft deleted blob or snapshot.
3377        Attempting to undelete a blob or snapshot that is not soft deleted will succeed without any changes.
3378
3379        :param str container_name:
3380            Name of existing container.
3381        :param str blob_name:
3382            Name of existing blob.
3383        :param int timeout:
3384            The timeout parameter is expressed in seconds.
3385        '''
3386        _validate_not_none('container_name', container_name)
3387        _validate_not_none('blob_name', blob_name)
3388        request = HTTPRequest()
3389        request.method = 'PUT'
3390        request.host_locations = self._get_host_locations()
3391        request.path = _get_path(container_name, blob_name)
3392        request.query = {
3393            'comp': 'undelete',
3394            'timeout': _int_to_str(timeout)
3395        }
3396
3397        self._perform_request(request)
3398