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