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