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