1# ------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for 4# license information. 5# -------------------------------------------------------------------------- 6 7from typing import ( # pylint: disable=unused-import 8 Union, Optional, Any, Iterable, Dict, List, Type, Tuple, 9 TYPE_CHECKING 10) 11import logging 12 13from azure.core.pipeline.policies import ContentDecodePolicy 14from azure.core.exceptions import ( 15 HttpResponseError, 16 ResourceNotFoundError, 17 ResourceModifiedError, 18 ResourceExistsError, 19 ClientAuthenticationError, 20 DecodeError) 21 22from .parser import _to_utc_datetime 23from .models import StorageErrorCode, UserDelegationKey, get_enum_value 24 25 26if TYPE_CHECKING: 27 from datetime import datetime 28 from azure.core.exceptions import AzureError 29 30 31_LOGGER = logging.getLogger(__name__) 32 33 34class PartialBatchErrorException(HttpResponseError): 35 """There is a partial failure in batch operations. 36 37 :param str message: The message of the exception. 38 :param response: Server response to be deserialized. 39 :param list parts: A list of the parts in multipart response. 40 """ 41 42 def __init__(self, message, response, parts): 43 self.parts = parts 44 super(PartialBatchErrorException, self).__init__(message=message, response=response) 45 46 47def parse_length_from_content_range(content_range): 48 ''' 49 Parses the blob length from the content range header: bytes 1-3/65537 50 ''' 51 if content_range is None: 52 return None 53 54 # First, split in space and take the second half: '1-3/65537' 55 # Next, split on slash and take the second half: '65537' 56 # Finally, convert to an int: 65537 57 return int(content_range.split(' ', 1)[1].split('/', 1)[1]) 58 59 60def normalize_headers(headers): 61 normalized = {} 62 for key, value in headers.items(): 63 if key.startswith('x-ms-'): 64 key = key[5:] 65 normalized[key.lower().replace('-', '_')] = get_enum_value(value) 66 return normalized 67 68 69def deserialize_metadata(response, obj, headers): # pylint: disable=unused-argument 70 raw_metadata = {k: v for k, v in response.headers.items() if k.startswith("x-ms-meta-")} 71 return {k[10:]: v for k, v in raw_metadata.items()} 72 73 74def return_response_headers(response, deserialized, response_headers): # pylint: disable=unused-argument 75 return normalize_headers(response_headers) 76 77 78def return_headers_and_deserialized(response, deserialized, response_headers): # pylint: disable=unused-argument 79 return normalize_headers(response_headers), deserialized 80 81 82def return_context_and_deserialized(response, deserialized, response_headers): # pylint: disable=unused-argument 83 return response.location_mode, deserialized 84 85 86def process_storage_error(storage_error): 87 raise_error = HttpResponseError 88 error_code = storage_error.response.headers.get('x-ms-error-code') 89 error_message = storage_error.message 90 additional_data = {} 91 try: 92 error_body = ContentDecodePolicy.deserialize_from_http_generics(storage_error.response) 93 if error_body: 94 for info in error_body.iter(): 95 if info.tag.lower() == 'code': 96 error_code = info.text 97 elif info.tag.lower() == 'message': 98 error_message = info.text 99 else: 100 additional_data[info.tag] = info.text 101 except DecodeError: 102 pass 103 104 try: 105 if error_code: 106 error_code = StorageErrorCode(error_code) 107 if error_code in [StorageErrorCode.condition_not_met, 108 StorageErrorCode.blob_overwritten]: 109 raise_error = ResourceModifiedError 110 if error_code in [StorageErrorCode.invalid_authentication_info, 111 StorageErrorCode.authentication_failed]: 112 raise_error = ClientAuthenticationError 113 if error_code in [StorageErrorCode.resource_not_found, 114 StorageErrorCode.cannot_verify_copy_source, 115 StorageErrorCode.blob_not_found, 116 StorageErrorCode.queue_not_found, 117 StorageErrorCode.container_not_found, 118 StorageErrorCode.parent_not_found, 119 StorageErrorCode.share_not_found]: 120 raise_error = ResourceNotFoundError 121 if error_code in [StorageErrorCode.account_already_exists, 122 StorageErrorCode.account_being_created, 123 StorageErrorCode.resource_already_exists, 124 StorageErrorCode.resource_type_mismatch, 125 StorageErrorCode.blob_already_exists, 126 StorageErrorCode.queue_already_exists, 127 StorageErrorCode.container_already_exists, 128 StorageErrorCode.container_being_deleted, 129 StorageErrorCode.queue_being_deleted, 130 StorageErrorCode.share_already_exists, 131 StorageErrorCode.share_being_deleted]: 132 raise_error = ResourceExistsError 133 except ValueError: 134 # Got an unknown error code 135 pass 136 137 try: 138 error_message += "\nErrorCode:{}".format(error_code.value) 139 except AttributeError: 140 error_message += "\nErrorCode:{}".format(error_code) 141 for name, info in additional_data.items(): 142 error_message += "\n{}:{}".format(name, info) 143 144 error = raise_error(message=error_message, response=storage_error.response) 145 error.error_code = error_code 146 error.additional_info = additional_data 147 raise error 148 149 150def parse_to_internal_user_delegation_key(service_user_delegation_key): 151 internal_user_delegation_key = UserDelegationKey() 152 internal_user_delegation_key.signed_oid = service_user_delegation_key.signed_oid 153 internal_user_delegation_key.signed_tid = service_user_delegation_key.signed_tid 154 internal_user_delegation_key.signed_start = _to_utc_datetime(service_user_delegation_key.signed_start) 155 internal_user_delegation_key.signed_expiry = _to_utc_datetime(service_user_delegation_key.signed_expiry) 156 internal_user_delegation_key.signed_service = service_user_delegation_key.signed_service 157 internal_user_delegation_key.signed_version = service_user_delegation_key.signed_version 158 internal_user_delegation_key.value = service_user_delegation_key.value 159 return internal_user_delegation_key 160