1# ------------------------------------------------------------------------- 2# Copyright (c) Microsoft. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# -------------------------------------------------------------------------- 15from contextlib import contextmanager 16 17from azure.common import ( 18 AzureHttpError, 19) 20 21from ..common._auth import ( 22 _StorageSASAuthentication, 23 _StorageTableSharedKeyAuthentication, 24) 25from ..common._common_conversion import ( 26 _int_to_str, 27 _to_str, 28) 29from ..common._connection import _ServiceParameters 30from ..common._constants import ( 31 SERVICE_HOST_BASE, 32 DEFAULT_PROTOCOL, 33 DEV_ACCOUNT_NAME, 34) 35from ..common._deserialization import ( 36 _convert_xml_to_service_properties, 37 _convert_xml_to_signed_identifiers, 38 _convert_xml_to_service_stats, 39) 40from ..common._error import ( 41 _dont_fail_not_exist, 42 _dont_fail_on_exist, 43 _validate_not_none, 44 _ERROR_STORAGE_MISSING_INFO, 45 _validate_access_policies, 46) 47from ..common._http import HTTPRequest 48from ..common._serialization import ( 49 _get_request_body, 50 _update_request, 51 _convert_signed_identifiers_to_xml, 52 _convert_service_properties_to_xml, 53) 54from ..common.models import ( 55 Services, 56 ListGenerator, 57 _OperationContext, 58) 59from ..common.sharedaccesssignature import ( 60 SharedAccessSignature, 61) 62from ..common.storageclient import StorageClient 63from ._deserialization import ( 64 _convert_json_response_to_entity, 65 _convert_json_response_to_tables, 66 _convert_json_response_to_entities, 67 _parse_batch_response, 68 _extract_etag, 69) 70from ._request import ( 71 _get_entity, 72 _insert_entity, 73 _update_entity, 74 _merge_entity, 75 _delete_entity, 76 _insert_or_replace_entity, 77 _insert_or_merge_entity, 78) 79from ._serialization import ( 80 _convert_table_to_json, 81 _convert_batch_to_json, 82 _update_storage_table_header, 83 _get_entity_path, 84 _DEFAULT_ACCEPT_HEADER, 85 _DEFAULT_CONTENT_TYPE_HEADER, 86 _DEFAULT_PREFER_HEADER, 87) 88from .models import TablePayloadFormat 89from .tablebatch import TableBatch 90 91 92class TableService(StorageClient): 93 ''' 94 This is the main class managing Azure Table resources. 95 96 The Azure Table service offers structured storage in the form of tables. Tables 97 store data as collections of entities. Entities are similar to rows. An entity 98 has a primary key and a set of properties. A property is a name, typed-value pair, 99 similar to a column. The Table service does not enforce any schema for tables, 100 so two entities in the same table may have different sets of properties. Developers 101 may choose to enforce a schema on the client side. A table may contain any number 102 of entities. 103 104 :ivar object key_encryption_key: 105 The key-encryption-key optionally provided by the user. If provided, will be used to 106 encrypt/decrypt in supported methods. 107 For methods requiring decryption, either the key_encryption_key OR the resolver must be provided. 108 If both are provided, the resolver will take precedence. 109 Must implement the following methods for APIs requiring encryption: 110 wrap_key(key)--wraps the specified key (bytes) using an algorithm of the user's choice. Returns the encrypted key as bytes. 111 get_key_wrap_algorithm()--returns the algorithm used to wrap the specified symmetric key. 112 get_kid()--returns a string key id for this key-encryption-key. 113 Must implement the following methods for APIs requiring decryption: 114 unwrap_key(key, algorithm)--returns the unwrapped form of the specified symmetric key using the string-specified algorithm. 115 get_kid()--returns a string key id for this key-encryption-key. 116 :ivar function key_resolver_function(kid): 117 A function to resolve keys optionally provided by the user. If provided, will be used to decrypt in supported methods. 118 For methods requiring decryption, either the key_encryption_key OR 119 the resolver must be provided. If both are provided, the resolver will take precedence. 120 It uses the kid string to return a key-encryption-key implementing the interface defined above. 121 :ivar function(partition_key, row_key, property_name) encryption_resolver_functions: 122 A function that takes in an entity's partition key, row key, and property name and returns 123 a boolean that indicates whether that property should be encrypted. 124 :ivar bool require_encryption: 125 A flag that may be set to ensure that all messages successfully uploaded to the queue and all those downloaded and 126 successfully read from the queue are/were encrypted while on the server. If this flag is set, all required 127 parameters for encryption/decryption must be provided. See the above comments on the key_encryption_key and resolver. 128 ''' 129 130 def __init__(self, account_name=None, account_key=None, sas_token=None, 131 is_emulated=False, protocol=DEFAULT_PROTOCOL, endpoint_suffix=SERVICE_HOST_BASE, 132 request_session=None, connection_string=None, socket_timeout=None): 133 ''' 134 :param str account_name: 135 The storage account name. This is used to authenticate requests 136 signed with an account key and to construct the storage endpoint. It 137 is required unless a connection string is given. 138 :param str account_key: 139 The storage account key. This is used for shared key authentication. 140 :param str sas_token: 141 A shared access signature token to use to authenticate requests 142 instead of the account key. If account key and sas token are both 143 specified, account key will be used to sign. 144 :param bool is_emulated: 145 Whether to use the emulator. Defaults to False. If specified, will 146 override all other parameters besides connection string and request 147 session. 148 :param str protocol: 149 The protocol to use for requests. Defaults to https. 150 :param str endpoint_suffix: 151 The host base component of the url, minus the account name. Defaults 152 to Azure (core.windows.net). Override this to use the China cloud 153 (core.chinacloudapi.cn). 154 :param requests.Session request_session: 155 The session object to use for http requests. 156 :param str connection_string: 157 If specified, this will override all other parameters besides 158 request session. See 159 http://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/ 160 for the connection string format. 161 :param int socket_timeout: 162 If specified, this will override the default socket timeout. The timeout specified is in seconds. 163 See DEFAULT_SOCKET_TIMEOUT in _constants.py for the default value. 164 ''' 165 service_params = _ServiceParameters.get_service_parameters( 166 'table', 167 account_name=account_name, 168 account_key=account_key, 169 sas_token=sas_token, 170 is_emulated=is_emulated, 171 protocol=protocol, 172 endpoint_suffix=endpoint_suffix, 173 request_session=request_session, 174 connection_string=connection_string, 175 socket_timeout=socket_timeout) 176 177 super(TableService, self).__init__(service_params) 178 179 if self.account_key: 180 self.authentication = _StorageTableSharedKeyAuthentication( 181 self.account_name, 182 self.account_key, 183 ) 184 elif self.sas_token: 185 self.authentication = _StorageSASAuthentication(self.sas_token) 186 else: 187 raise ValueError(_ERROR_STORAGE_MISSING_INFO) 188 189 self.require_encryption = False 190 self.key_encryption_key = None 191 self.key_resolver_function = None 192 self.encryption_resolver_function = None 193 194 def generate_account_shared_access_signature(self, resource_types, permission, 195 expiry, start=None, ip=None, protocol=None): 196 ''' 197 Generates a shared access signature for the table service. 198 Use the returned signature with the sas_token parameter of TableService. 199 200 :param ResourceTypes resource_types: 201 Specifies the resource types that are accessible with the account SAS. 202 :param AccountPermissions permission: 203 The permissions associated with the shared access signature. The 204 user is restricted to operations allowed by the permissions. 205 Required unless an id is given referencing a stored access policy 206 which contains this field. This field must be omitted if it has been 207 specified in an associated stored access policy. 208 :param expiry: 209 The time at which the shared access signature becomes invalid. 210 Required unless an id is given referencing a stored access policy 211 which contains this field. This field must be omitted if it has 212 been specified in an associated stored access policy. Azure will always 213 convert values to UTC. If a date is passed in without timezone info, it 214 is assumed to be UTC. 215 :type expiry: datetime or str 216 :param start: 217 The time at which the shared access signature becomes valid. If 218 omitted, start time for this call is assumed to be the time when the 219 storage service receives the request. Azure will always convert values 220 to UTC. If a date is passed in without timezone info, it is assumed to 221 be UTC. 222 :type start: datetime or str 223 :param str ip: 224 Specifies an IP address or a range of IP addresses from which to accept requests. 225 If the IP address from which the request originates does not match the IP address 226 or address range specified on the SAS token, the request is not authenticated. 227 For example, specifying sip=168.1.5.65 or sip=168.1.5.60-168.1.5.70 on the SAS 228 restricts the request to those IP addresses. 229 :param str protocol: 230 Specifies the protocol permitted for a request made. The default value 231 is https,http. See :class:`~azure.storage.common.models.Protocol` for possible values. 232 :return: A Shared Access Signature (sas) token. 233 :rtype: str 234 ''' 235 _validate_not_none('self.account_name', self.account_name) 236 _validate_not_none('self.account_key', self.account_key) 237 238 sas = SharedAccessSignature(self.account_name, self.account_key) 239 return sas.generate_account(Services.TABLE, resource_types, permission, 240 expiry, start=start, ip=ip, protocol=protocol) 241 242 def generate_table_shared_access_signature(self, table_name, permission=None, 243 expiry=None, start=None, id=None, 244 ip=None, protocol=None, 245 start_pk=None, start_rk=None, 246 end_pk=None, end_rk=None): 247 ''' 248 Generates a shared access signature for the table. 249 Use the returned signature with the sas_token parameter of TableService. 250 251 :param str table_name: 252 The name of the table to create a SAS token for. 253 :param TablePermissions permission: 254 The permissions associated with the shared access signature. The 255 user is restricted to operations allowed by the permissions. 256 Required unless an id is given referencing a stored access policy 257 which contains this field. This field must be omitted if it has been 258 specified in an associated stored access policy. 259 :param expiry: 260 The time at which the shared access signature becomes invalid. 261 Required unless an id is given referencing a stored access policy 262 which contains this field. This field must be omitted if it has 263 been specified in an associated stored access policy. Azure will always 264 convert values to UTC. If a date is passed in without timezone info, it 265 is assumed to be UTC. 266 :type expiry: datetime or str 267 :param start: 268 The time at which the shared access signature becomes valid. If 269 omitted, start time for this call is assumed to be the time when the 270 storage service receives the request. Azure will always convert values 271 to UTC. If a date is passed in without timezone info, it is assumed to 272 be UTC. 273 :type start: datetime or str 274 :param str id: 275 A unique value up to 64 characters in length that correlates to a 276 stored access policy. To create a stored access policy, use :func:`~set_table_acl`. 277 :param str ip: 278 Specifies an IP address or a range of IP addresses from which to accept requests. 279 If the IP address from which the request originates does not match the IP address 280 or address range specified on the SAS token, the request is not authenticated. 281 For example, specifying sip='168.1.5.65' or sip='168.1.5.60-168.1.5.70' on the SAS 282 restricts the request to those IP addresses. 283 :param str protocol: 284 Specifies the protocol permitted for a request made. The default value 285 is https,http. See :class:`~azure.storage.common.models.Protocol` for possible values. 286 :param str start_pk: 287 The minimum partition key accessible with this shared access 288 signature. startpk must accompany startrk. Key values are inclusive. 289 If omitted, there is no lower bound on the table entities that can 290 be accessed. 291 :param str start_rk: 292 The minimum row key accessible with this shared access signature. 293 startpk must accompany startrk. Key values are inclusive. If 294 omitted, there is no lower bound on the table entities that can be 295 accessed. 296 :param str end_pk: 297 The maximum partition key accessible with this shared access 298 signature. endpk must accompany endrk. Key values are inclusive. If 299 omitted, there is no upper bound on the table entities that can be 300 accessed. 301 :param str end_rk: 302 The maximum row key accessible with this shared access signature. 303 endpk must accompany endrk. Key values are inclusive. If omitted, 304 there is no upper bound on the table entities that can be accessed. 305 :return: A Shared Access Signature (sas) token. 306 :rtype: str 307 ''' 308 _validate_not_none('table_name', table_name) 309 _validate_not_none('self.account_name', self.account_name) 310 _validate_not_none('self.account_key', self.account_key) 311 312 sas = SharedAccessSignature(self.account_name, self.account_key) 313 return sas.generate_table( 314 table_name, 315 permission=permission, 316 expiry=expiry, 317 start=start, 318 id=id, 319 ip=ip, 320 protocol=protocol, 321 start_pk=start_pk, 322 start_rk=start_rk, 323 end_pk=end_pk, 324 end_rk=end_rk, 325 ) 326 327 def get_table_service_stats(self, timeout=None): 328 ''' 329 Retrieves statistics related to replication for the Table service. It is 330 only available when read-access geo-redundant replication is enabled for 331 the storage account. 332 333 With geo-redundant replication, Azure Storage maintains your data durable 334 in two locations. In both locations, Azure Storage constantly maintains 335 multiple healthy replicas of your data. The location where you read, 336 create, update, or delete data is the primary storage account location. 337 The primary location exists in the region you choose at the time you 338 create an account via the Azure Management Azure classic portal, for 339 example, North Central US. The location to which your data is replicated 340 is the secondary location. The secondary location is automatically 341 determined based on the location of the primary; it is in a second data 342 center that resides in the same region as the primary location. Read-only 343 access is available from the secondary location, if read-access geo-redundant 344 replication is enabled for your storage account. 345 346 :param int timeout: 347 The timeout parameter is expressed in seconds. 348 :return: The table service stats. 349 :rtype: :class:`~azure.storage.common.models.ServiceStats` 350 ''' 351 request = HTTPRequest() 352 request.method = 'GET' 353 request.host_locations = self._get_host_locations(primary=False, secondary=True) 354 request.path = '/' 355 request.query = { 356 'restype': 'service', 357 'comp': 'stats', 358 'timeout': _int_to_str(timeout), 359 } 360 361 return self._perform_request(request, _convert_xml_to_service_stats) 362 363 def get_table_service_properties(self, timeout=None): 364 ''' 365 Gets the properties of a storage account's Table service, including 366 logging, analytics and CORS rules. 367 368 :param int timeout: 369 The server timeout, expressed in seconds. 370 :return: The table service properties. 371 :rtype: :class:`~azure.storage.common.models.ServiceProperties` 372 ''' 373 request = HTTPRequest() 374 request.method = 'GET' 375 request.host_locations = self._get_host_locations(secondary=True) 376 request.path = '/' 377 request.query = { 378 'restype': 'service', 379 'comp': 'properties', 380 'timeout': _int_to_str(timeout), 381 } 382 383 return self._perform_request(request, _convert_xml_to_service_properties) 384 385 def set_table_service_properties(self, logging=None, hour_metrics=None, 386 minute_metrics=None, cors=None, timeout=None): 387 ''' 388 Sets the properties of a storage account's Table service, including 389 Azure Storage Analytics. If an element (ex Logging) is left as None, the 390 existing settings on the service for that functionality are preserved. 391 For more information on Azure Storage Analytics, see 392 https://msdn.microsoft.com/en-us/library/azure/hh343270.aspx. 393 394 :param Logging logging: 395 The logging settings provide request logs. 396 :param Metrics hour_metrics: 397 The hour metrics settings provide a summary of request 398 statistics grouped by API in hourly aggregates for tables. 399 :param Metrics minute_metrics: 400 The minute metrics settings provide request statistics 401 for each minute for tables. 402 :param cors: 403 You can include up to five CorsRule elements in the 404 list. If an empty list is specified, all CORS rules will be deleted, 405 and CORS will be disabled for the service. For detailed information 406 about CORS rules and evaluation logic, see 407 https://msdn.microsoft.com/en-us/library/azure/dn535601.aspx. 408 :type cors: list(:class:`~azure.storage.common.models.CorsRule`) 409 :param int timeout: 410 The server timeout, expressed in seconds. 411 ''' 412 request = HTTPRequest() 413 request.method = 'PUT' 414 request.host_locations = self._get_host_locations() 415 request.path = '/' 416 request.query = { 417 'restype': 'service', 418 'comp': 'properties', 419 'timeout': _int_to_str(timeout), 420 } 421 request.body = _get_request_body( 422 _convert_service_properties_to_xml(logging, hour_metrics, minute_metrics, cors)) 423 424 self._perform_request(request) 425 426 def list_tables(self, num_results=None, marker=None, timeout=None): 427 ''' 428 Returns a generator to list the tables. The generator will lazily follow 429 the continuation tokens returned by the service and stop when all tables 430 have been returned or num_results is reached. 431 432 If num_results is specified and the account has more than that number of 433 tables, the generator will have a populated next_marker field once it 434 finishes. This marker can be used to create a new generator if more 435 results are desired. 436 437 :param int num_results: 438 The maximum number of tables to return. 439 :param marker: 440 An opaque continuation object. This value can be retrieved from the 441 next_marker field of a previous generator object if num_results was 442 specified and that generator has finished enumerating results. If 443 specified, this generator will begin returning results from the point 444 where the previous generator stopped. 445 :type marker: obj 446 :param int timeout: 447 The server timeout, expressed in seconds. This function may make multiple 448 calls to the service in which case the timeout value specified will be 449 applied to each individual call. 450 :return: A generator which produces :class:`~azure.storage.common.models.table.Table` objects. 451 :rtype: :class:`~azure.storage.common.models.ListGenerator`: 452 ''' 453 operation_context = _OperationContext(location_lock=True) 454 kwargs = {'max_results': num_results, 'marker': marker, 'timeout': timeout, 455 '_context': operation_context} 456 resp = self._list_tables(**kwargs) 457 458 return ListGenerator(resp, self._list_tables, (), kwargs) 459 460 def _list_tables(self, max_results=None, marker=None, timeout=None, _context=None): 461 ''' 462 Returns a list of tables under the specified account. Makes a single list 463 request to the service. Used internally by the list_tables method. 464 465 :param int max_results: 466 The maximum number of tables to return. A single list request may 467 return up to 1000 tables and potentially a continuation token which 468 should be followed to get additional resutls. 469 :param marker: 470 A dictionary which identifies the portion of the query to be 471 returned with the next query operation. The operation returns a 472 next_marker element within the response body if the list returned 473 was not complete. This value may then be used as a query parameter 474 in a subsequent call to request the next portion of the list of 475 tables. The marker value is opaque to the client. 476 :type marker: obj 477 :param int timeout: 478 The server timeout, expressed in seconds. 479 :return: A list of tables, potentially with a next_marker property. 480 :rtype: list(:class:`~azure.storage.common.models.table.Table`) 481 ''' 482 request = HTTPRequest() 483 request.method = 'GET' 484 request.host_locations = self._get_host_locations(secondary=True) 485 request.path = '/Tables' 486 request.headers = {'Accept': TablePayloadFormat.JSON_NO_METADATA} 487 request.query = { 488 '$top': _int_to_str(max_results), 489 'NextTableName': _to_str(marker), 490 'timeout': _int_to_str(timeout), 491 } 492 493 return self._perform_request(request, _convert_json_response_to_tables, 494 operation_context=_context) 495 496 def create_table(self, table_name, fail_on_exist=False, timeout=None): 497 ''' 498 Creates a new table in the storage account. 499 500 :param str table_name: 501 The name of the table to create. The table name may contain only 502 alphanumeric characters and cannot begin with a numeric character. 503 It is case-insensitive and must be from 3 to 63 characters long. 504 :param bool fail_on_exist: 505 Specifies whether to throw an exception if the table already exists. 506 :param int timeout: 507 The server timeout, expressed in seconds. 508 :return: 509 A boolean indicating whether the table was created. If fail_on_exist 510 was set to True, this will throw instead of returning false. 511 :rtype: bool 512 ''' 513 _validate_not_none('table', table_name) 514 request = HTTPRequest() 515 request.method = 'POST' 516 request.host_locations = self._get_host_locations() 517 request.path = '/Tables' 518 request.query = {'timeout': _int_to_str(timeout)} 519 request.headers = { 520 _DEFAULT_CONTENT_TYPE_HEADER[0]: _DEFAULT_CONTENT_TYPE_HEADER[1], 521 _DEFAULT_PREFER_HEADER[0]: _DEFAULT_PREFER_HEADER[1], 522 _DEFAULT_ACCEPT_HEADER[0]: _DEFAULT_ACCEPT_HEADER[1] 523 } 524 request.body = _get_request_body(_convert_table_to_json(table_name)) 525 526 if not fail_on_exist: 527 try: 528 self._perform_request(request) 529 return True 530 except AzureHttpError as ex: 531 _dont_fail_on_exist(ex) 532 return False 533 else: 534 self._perform_request(request) 535 return True 536 537 def exists(self, table_name, timeout=None): 538 ''' 539 Returns a boolean indicating whether the table exists. 540 541 :param str table_name: 542 The name of table to check for existence. 543 :param int timeout: 544 The server timeout, expressed in seconds. 545 :return: A boolean indicating whether the table exists. 546 :rtype: bool 547 ''' 548 _validate_not_none('table_name', table_name) 549 request = HTTPRequest() 550 request.method = 'GET' 551 request.host_locations = self._get_host_locations(secondary=True) 552 request.path = '/Tables' + "('" + table_name + "')" 553 request.headers = {'Accept': TablePayloadFormat.JSON_NO_METADATA} 554 request.query = {'timeout': _int_to_str(timeout)} 555 556 try: 557 self._perform_request(request) 558 return True 559 except AzureHttpError as ex: 560 _dont_fail_not_exist(ex) 561 return False 562 563 def delete_table(self, table_name, fail_not_exist=False, timeout=None): 564 ''' 565 Deletes the specified table and any data it contains. 566 567 When a table is successfully deleted, it is immediately marked for deletion 568 and is no longer accessible to clients. The table is later removed from 569 the Table service during garbage collection. 570 571 Note that deleting a table is likely to take at least 40 seconds to complete. 572 If an operation is attempted against the table while it was being deleted, 573 an :class:`AzureConflictHttpError` will be thrown. 574 575 :param str table_name: 576 The name of the table to delete. 577 :param bool fail_not_exist: 578 Specifies whether to throw an exception if the table doesn't exist. 579 :param int timeout: 580 The server timeout, expressed in seconds. 581 :return: 582 A boolean indicating whether the table was deleted. If fail_not_exist 583 was set to True, this will throw instead of returning false. 584 :rtype: bool 585 ''' 586 _validate_not_none('table_name', table_name) 587 request = HTTPRequest() 588 request.method = 'DELETE' 589 request.host_locations = self._get_host_locations() 590 request.path = '/Tables(\'' + _to_str(table_name) + '\')' 591 request.query = {'timeout': _int_to_str(timeout)} 592 request.headers = {_DEFAULT_ACCEPT_HEADER[0]: _DEFAULT_ACCEPT_HEADER[1]} 593 594 if not fail_not_exist: 595 try: 596 self._perform_request(request) 597 return True 598 except AzureHttpError as ex: 599 _dont_fail_not_exist(ex) 600 return False 601 else: 602 self._perform_request(request) 603 return True 604 605 def get_table_acl(self, table_name, timeout=None): 606 ''' 607 Returns details about any stored access policies specified on the 608 table that may be used with Shared Access Signatures. 609 610 :param str table_name: 611 The name of an existing table. 612 :param int timeout: 613 The server timeout, expressed in seconds. 614 :return: A dictionary of access policies associated with the table. 615 :rtype: dict(str, :class:`~azure.storage.common.models.AccessPolicy`) 616 ''' 617 _validate_not_none('table_name', table_name) 618 request = HTTPRequest() 619 request.method = 'GET' 620 request.host_locations = self._get_host_locations(secondary=True) 621 request.path = '/' + _to_str(table_name) 622 request.query = { 623 'comp': 'acl', 624 'timeout': _int_to_str(timeout), 625 } 626 627 return self._perform_request(request, _convert_xml_to_signed_identifiers) 628 629 def set_table_acl(self, table_name, signed_identifiers=None, timeout=None): 630 ''' 631 Sets stored access policies for the table that may be used with Shared 632 Access Signatures. 633 634 When you set permissions for a table, the existing permissions are replaced. 635 To update the table's permissions, call :func:`~get_table_acl` to fetch 636 all access policies associated with the table, modify the access policy 637 that you wish to change, and then call this function with the complete 638 set of data to perform the update. 639 640 When you establish a stored access policy on a table, it may take up to 641 30 seconds to take effect. During this interval, a shared access signature 642 that is associated with the stored access policy will throw an 643 :class:`AzureHttpError` until the access policy becomes active. 644 645 :param str table_name: 646 The name of an existing table. 647 :param signed_identifiers: 648 A dictionary of access policies to associate with the table. The 649 dictionary may contain up to 5 elements. An empty dictionary 650 will clear the access policies set on the service. 651 :type signed_identifiers: dict(str, :class:`~azure.storage.common.models.AccessPolicy`) 652 :param int timeout: 653 The server timeout, expressed in seconds. 654 ''' 655 _validate_not_none('table_name', table_name) 656 _validate_access_policies(signed_identifiers) 657 request = HTTPRequest() 658 request.method = 'PUT' 659 request.host_locations = self._get_host_locations() 660 request.path = '/' + _to_str(table_name) 661 request.query = { 662 'comp': 'acl', 663 'timeout': _int_to_str(timeout), 664 } 665 request.body = _get_request_body( 666 _convert_signed_identifiers_to_xml(signed_identifiers)) 667 668 self._perform_request(request) 669 670 def query_entities(self, table_name, filter=None, select=None, num_results=None, 671 marker=None, accept=TablePayloadFormat.JSON_MINIMAL_METADATA, 672 property_resolver=None, timeout=None): 673 ''' 674 Returns a generator to list the entities in the table specified. The 675 generator will lazily follow the continuation tokens returned by the 676 service and stop when all entities have been returned or num_results is 677 reached. 678 679 If num_results is specified and the account has more than that number of 680 entities, the generator will have a populated next_marker field once it 681 finishes. This marker can be used to create a new generator if more 682 results are desired. 683 684 :param str table_name: 685 The name of the table to query. 686 :param str filter: 687 Returns only entities that satisfy the specified filter. Note that 688 no more than 15 discrete comparisons are permitted within a $filter 689 string. See http://msdn.microsoft.com/en-us/library/windowsazure/dd894031.aspx 690 for more information on constructing filters. 691 :param str select: 692 Returns only the desired properties of an entity from the set. 693 :param int num_results: 694 The maximum number of entities to return. 695 :param marker: 696 An opaque continuation object. This value can be retrieved from the 697 next_marker field of a previous generator object if max_results was 698 specified and that generator has finished enumerating results. If 699 specified, this generator will begin returning results from the point 700 where the previous generator stopped. 701 :type marker: obj 702 :param str accept: 703 Specifies the accepted content type of the response payload. See 704 :class:`~azure.storage.table.models.TablePayloadFormat` for possible 705 values. 706 :param property_resolver: 707 A function which given the partition key, row key, property name, 708 property value, and the property EdmType if returned by the service, 709 returns the EdmType of the property. Generally used if accept is set 710 to JSON_NO_METADATA. 711 :type property_resolver: func(pk, rk, prop_name, prop_value, service_edm_type) 712 :param int timeout: 713 The server timeout, expressed in seconds. This function may make multiple 714 calls to the service in which case the timeout value specified will be 715 applied to each individual call. 716 :return: A generator which produces :class:`~azure.storage.table.models.Entity` objects. 717 :rtype: :class:`~azure.storage.common.models.ListGenerator` 718 ''' 719 720 operation_context = _OperationContext(location_lock=True) 721 if self.key_encryption_key is not None or self.key_resolver_function is not None: 722 # If query already requests all properties, no need to add the metadata columns 723 if select is not None and select != '*': 724 select += ',_ClientEncryptionMetadata1,_ClientEncryptionMetadata2' 725 726 args = (table_name,) 727 kwargs = {'filter': filter, 'select': select, 'max_results': num_results, 'marker': marker, 728 'accept': accept, 'property_resolver': property_resolver, 'timeout': timeout, 729 '_context': operation_context} 730 resp = self._query_entities(*args, **kwargs) 731 732 return ListGenerator(resp, self._query_entities, args, kwargs) 733 734 def _query_entities(self, table_name, filter=None, select=None, max_results=None, 735 marker=None, accept=TablePayloadFormat.JSON_MINIMAL_METADATA, 736 property_resolver=None, timeout=None, _context=None): 737 ''' 738 Returns a list of entities under the specified table. Makes a single list 739 request to the service. Used internally by the query_entities method. 740 741 :param str table_name: 742 The name of the table to query. 743 :param str filter: 744 Returns only entities that satisfy the specified filter. Note that 745 no more than 15 discrete comparisons are permitted within a $filter 746 string. See http://msdn.microsoft.com/en-us/library/windowsazure/dd894031.aspx 747 for more information on constructing filters. 748 :param str select: 749 Returns only the desired properties of an entity from the set. 750 :param int max_results: 751 The maximum number of entities to return. 752 :param obj marker: 753 A dictionary which identifies the portion of the query to be 754 returned with the next query operation. The operation returns a 755 next_marker element within the response body if the list returned 756 was not complete. This value may then be used as a query parameter 757 in a subsequent call to request the next portion of the list of 758 table. The marker value is opaque to the client. 759 :param str accept: 760 Specifies the accepted content type of the response payload. See 761 :class:`~azure.storage.table.models.TablePayloadFormat` for possible 762 values. 763 :param property_resolver: 764 A function which given the partition key, row key, property name, 765 property value, and the property EdmType if returned by the service, 766 returns the EdmType of the property. Generally used if accept is set 767 to JSON_NO_METADATA. 768 :type property_resolver: func(pk, rk, prop_name, prop_value, service_edm_type) 769 :param int timeout: 770 The server timeout, expressed in seconds. 771 :return: A list of entities, potentially with a next_marker property. 772 :rtype: list(:class:`~azure.storage.table.models.Entity`) 773 ''' 774 _validate_not_none('table_name', table_name) 775 _validate_not_none('accept', accept) 776 next_partition_key = None if marker is None else marker.get('nextpartitionkey') 777 next_row_key = None if marker is None else marker.get('nextrowkey') 778 779 request = HTTPRequest() 780 request.method = 'GET' 781 request.host_locations = self._get_host_locations(secondary=True) 782 request.path = '/' + _to_str(table_name) + '()' 783 request.headers = {'Accept': _to_str(accept)} 784 request.query = { 785 '$filter': _to_str(filter), 786 '$select': _to_str(select), 787 '$top': _int_to_str(max_results), 788 'NextPartitionKey': _to_str(next_partition_key), 789 'NextRowKey': _to_str(next_row_key), 790 'timeout': _int_to_str(timeout), 791 } 792 793 return self._perform_request(request, _convert_json_response_to_entities, 794 [property_resolver, self.require_encryption, 795 self.key_encryption_key, self.key_resolver_function], 796 operation_context=_context) 797 798 def commit_batch(self, table_name, batch, timeout=None): 799 ''' 800 Commits a :class:`~azure.storage.table.TableBatch` request. 801 802 :param str table_name: 803 The name of the table to commit the batch to. 804 :param TableBatch batch: 805 The batch to commit. 806 :param int timeout: 807 The server timeout, expressed in seconds. 808 :return: 809 A list of the batch responses corresponding to the requests in the batch. 810 The items could either be an etag, in case of success, or an error object in case of failure. 811 :rtype: list(:class:`~azure.storage.table.models.AzureBatchOperationError`, str) 812 ''' 813 _validate_not_none('table_name', table_name) 814 815 # Construct the batch request 816 request = HTTPRequest() 817 request.method = 'POST' 818 request.host_locations = self._get_host_locations() 819 request.path = '/' + '$batch' 820 request.query = {'timeout': _int_to_str(timeout)} 821 822 # Update the batch operation requests with table and client specific info 823 for row_key, batch_request in batch._requests: 824 if batch_request.method == 'POST': 825 batch_request.path = '/' + _to_str(table_name) 826 else: 827 batch_request.path = _get_entity_path(table_name, batch._partition_key, row_key) 828 if self.is_emulated: 829 batch_request.path = '/' + DEV_ACCOUNT_NAME + batch_request.path 830 _update_request(batch_request) 831 832 # Construct the batch body 833 request.body, boundary = _convert_batch_to_json(batch._requests) 834 request.headers = {'Content-Type': boundary} 835 836 # Perform the batch request and return the response 837 return self._perform_request(request, _parse_batch_response) 838 839 @contextmanager 840 def batch(self, table_name, timeout=None): 841 ''' 842 Creates a batch object which can be used as a context manager. Commits the batch on exit. 843 844 :param str table_name: 845 The name of the table to commit the batch to. 846 :param int timeout: 847 The server timeout, expressed in seconds. 848 ''' 849 batch = TableBatch(self.require_encryption, self.key_encryption_key, self.encryption_resolver_function) 850 yield batch 851 self.commit_batch(table_name, batch, timeout=timeout) 852 853 def get_entity(self, table_name, partition_key, row_key, select=None, 854 accept=TablePayloadFormat.JSON_MINIMAL_METADATA, 855 property_resolver=None, timeout=None): 856 ''' 857 Get an entity from the specified table. Throws if the entity does not exist. 858 859 :param str table_name: 860 The name of the table to get the entity from. 861 :param str partition_key: 862 The PartitionKey of the entity. 863 :param str row_key: 864 The RowKey of the entity. 865 :param str select: 866 Returns only the desired properties of an entity from the set. 867 :param str accept: 868 Specifies the accepted content type of the response payload. See 869 :class:`~azure.storage.table.models.TablePayloadFormat` for possible 870 values. 871 :param property_resolver: 872 A function which given the partition key, row key, property name, 873 property value, and the property EdmType if returned by the service, 874 returns the EdmType of the property. Generally used if accept is set 875 to JSON_NO_METADATA. 876 :type property_resolver: func(pk, rk, prop_name, prop_value, service_edm_type) 877 :param int timeout: 878 The server timeout, expressed in seconds. 879 :return: The retrieved entity. 880 :rtype: :class:`~azure.storage.table.models.Entity` 881 ''' 882 _validate_not_none('table_name', table_name) 883 request = _get_entity(partition_key, row_key, select, accept) 884 request.host_locations = self._get_host_locations(secondary=True) 885 request.path = _get_entity_path(table_name, partition_key, row_key) 886 request.query['timeout'] = _int_to_str(timeout) 887 888 return self._perform_request(request, _convert_json_response_to_entity, 889 [property_resolver, self.require_encryption, 890 self.key_encryption_key, self.key_resolver_function]) 891 892 def insert_entity(self, table_name, entity, timeout=None): 893 ''' 894 Inserts a new entity into the table. Throws if an entity with the same 895 PartitionKey and RowKey already exists. 896 897 When inserting an entity into a table, you must specify values for the 898 PartitionKey and RowKey system properties. Together, these properties 899 form the primary key and must be unique within the table. Both the 900 PartitionKey and RowKey values must be string values; each key value may 901 be up to 64 KB in size. If you are using an integer value for the key 902 value, you should convert the integer to a fixed-width string, because 903 they are canonically sorted. For example, you should convert the value 904 1 to 0000001 to ensure proper sorting. 905 906 :param str table_name: 907 The name of the table to insert the entity into. 908 :param entity: 909 The entity to insert. Could be a dict or an entity object. 910 Must contain a PartitionKey and a RowKey. 911 :type entity: dict or :class:`~azure.storage.table.models.Entity` 912 :param int timeout: 913 The server timeout, expressed in seconds. 914 :return: The etag of the inserted entity. 915 :rtype: str 916 ''' 917 _validate_not_none('table_name', table_name) 918 919 request = _insert_entity(entity, self.require_encryption, self.key_encryption_key, 920 self.encryption_resolver_function) 921 request.host_locations = self._get_host_locations() 922 request.path = '/' + _to_str(table_name) 923 request.query['timeout'] = _int_to_str(timeout) 924 925 return self._perform_request(request, _extract_etag) 926 927 def update_entity(self, table_name, entity, if_match='*', timeout=None): 928 ''' 929 Updates an existing entity in a table. Throws if the entity does not exist. 930 The update_entity operation replaces the entire entity and can be used to 931 remove properties. 932 933 :param str table_name: 934 The name of the table containing the entity to update. 935 :param entity: 936 The entity to update. Could be a dict or an entity object. 937 Must contain a PartitionKey and a RowKey. 938 :type entity: dict or :class:`~azure.storage.table.models.Entity` 939 :param str if_match: 940 The client may specify the ETag for the entity on the 941 request in order to compare to the ETag maintained by the service 942 for the purpose of optimistic concurrency. The update operation 943 will be performed only if the ETag sent by the client matches the 944 value maintained by the server, indicating that the entity has 945 not been modified since it was retrieved by the client. To force 946 an unconditional update, set If-Match to the wildcard character (*). 947 :param int timeout: 948 The server timeout, expressed in seconds. 949 :return: The etag of the entity. 950 :rtype: str 951 ''' 952 _validate_not_none('table_name', table_name) 953 request = _update_entity(entity, if_match, self.require_encryption, self.key_encryption_key, 954 self.encryption_resolver_function) 955 request.host_locations = self._get_host_locations() 956 request.path = _get_entity_path(table_name, entity['PartitionKey'], entity['RowKey']) 957 request.query['timeout'] = _int_to_str(timeout) 958 959 return self._perform_request(request, _extract_etag) 960 961 def merge_entity(self, table_name, entity, if_match='*', timeout=None): 962 ''' 963 Updates an existing entity by merging the entity's properties. Throws 964 if the entity does not exist. 965 966 This operation does not replace the existing entity as the update_entity 967 operation does. A property cannot be removed with merge_entity. 968 969 Any properties with null values are ignored. All other properties will be 970 updated or added. 971 972 :param str table_name: 973 The name of the table containing the entity to merge. 974 :param entity: 975 The entity to merge. Could be a dict or an entity object. 976 Must contain a PartitionKey and a RowKey. 977 :type entity: dict or :class:`~azure.storage.table.models.Entity` 978 :param str if_match: 979 The client may specify the ETag for the entity on the 980 request in order to compare to the ETag maintained by the service 981 for the purpose of optimistic concurrency. The merge operation 982 will be performed only if the ETag sent by the client matches the 983 value maintained by the server, indicating that the entity has 984 not been modified since it was retrieved by the client. To force 985 an unconditional merge, set If-Match to the wildcard character (*). 986 :param int timeout: 987 The server timeout, expressed in seconds. 988 :return: The etag of the entity. 989 :rtype: str 990 ''' 991 992 _validate_not_none('table_name', table_name) 993 994 request = _merge_entity(entity, if_match, self.require_encryption, 995 self.key_encryption_key) 996 request.host_locations = self._get_host_locations() 997 request.query['timeout'] = _int_to_str(timeout) 998 request.path = _get_entity_path(table_name, entity['PartitionKey'], entity['RowKey']) 999 1000 return self._perform_request(request, _extract_etag) 1001 1002 def delete_entity(self, table_name, partition_key, row_key, 1003 if_match='*', timeout=None): 1004 ''' 1005 Deletes an existing entity in a table. Throws if the entity does not exist. 1006 1007 When an entity is successfully deleted, the entity is immediately marked 1008 for deletion and is no longer accessible to clients. The entity is later 1009 removed from the Table service during garbage collection. 1010 1011 :param str table_name: 1012 The name of the table containing the entity to delete. 1013 :param str partition_key: 1014 The PartitionKey of the entity. 1015 :param str row_key: 1016 The RowKey of the entity. 1017 :param str if_match: 1018 The client may specify the ETag for the entity on the 1019 request in order to compare to the ETag maintained by the service 1020 for the purpose of optimistic concurrency. The delete operation 1021 will be performed only if the ETag sent by the client matches the 1022 value maintained by the server, indicating that the entity has 1023 not been modified since it was retrieved by the client. To force 1024 an unconditional delete, set If-Match to the wildcard character (*). 1025 :param int timeout: 1026 The server timeout, expressed in seconds. 1027 ''' 1028 _validate_not_none('table_name', table_name) 1029 request = _delete_entity(partition_key, row_key, if_match) 1030 request.host_locations = self._get_host_locations() 1031 request.query['timeout'] = _int_to_str(timeout) 1032 request.path = _get_entity_path(table_name, partition_key, row_key) 1033 1034 self._perform_request(request) 1035 1036 def insert_or_replace_entity(self, table_name, entity, timeout=None): 1037 ''' 1038 Replaces an existing entity or inserts a new entity if it does not 1039 exist in the table. Because this operation can insert or update an 1040 entity, it is also known as an "upsert" operation. 1041 1042 If insert_or_replace_entity is used to replace an entity, any properties 1043 from the previous entity will be removed if the new entity does not define 1044 them. 1045 1046 :param str table_name: 1047 The name of the table in which to insert or replace the entity. 1048 :param entity: 1049 The entity to insert or replace. Could be a dict or an entity object. 1050 Must contain a PartitionKey and a RowKey. 1051 :type entity: dict or :class:`~azure.storage.table.models.Entity` 1052 :param int timeout: 1053 The server timeout, expressed in seconds. 1054 :return: The etag of the entity. 1055 :rtype: str 1056 ''' 1057 _validate_not_none('table_name', table_name) 1058 request = _insert_or_replace_entity(entity, self.require_encryption, self.key_encryption_key, 1059 self.encryption_resolver_function) 1060 request.host_locations = self._get_host_locations() 1061 request.query['timeout'] = _int_to_str(timeout) 1062 request.path = _get_entity_path(table_name, entity['PartitionKey'], entity['RowKey']) 1063 1064 return self._perform_request(request, _extract_etag) 1065 1066 def insert_or_merge_entity(self, table_name, entity, timeout=None): 1067 ''' 1068 Merges an existing entity or inserts a new entity if it does not exist 1069 in the table. 1070 1071 If insert_or_merge_entity is used to merge an entity, any properties from 1072 the previous entity will be retained if the request does not define or 1073 include them. 1074 1075 :param str table_name: 1076 The name of the table in which to insert or merge the entity. 1077 :param entity: 1078 The entity to insert or merge. Could be a dict or an entity object. 1079 Must contain a PartitionKey and a RowKey. 1080 :type entity: dict or :class:`~azure.storage.table.models.Entity` 1081 :param int timeout: 1082 The server timeout, expressed in seconds. 1083 :return: The etag of the entity. 1084 :rtype: str 1085 ''' 1086 1087 _validate_not_none('table_name', table_name) 1088 request = _insert_or_merge_entity(entity, self.require_encryption, 1089 self.key_encryption_key) 1090 request.host_locations = self._get_host_locations() 1091 request.query['timeout'] = _int_to_str(timeout) 1092 request.path = _get_entity_path(table_name, entity['PartitionKey'], entity['RowKey']) 1093 1094 return self._perform_request(request, _extract_etag) 1095 1096 def _perform_request(self, request, parser=None, parser_args=None, operation_context=None): 1097 _update_storage_table_header(request) 1098 return super(TableService, self)._perform_request(request, parser, parser_args, operation_context) 1099