1# ------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for 4# license information. 5# -------------------------------------------------------------------------- 6 7import uuid 8 9from typing import ( # pylint: disable=unused-import 10 Union, Optional, Any, TypeVar, TYPE_CHECKING 11) 12 13from azure.core.tracing.decorator import distributed_trace 14 15from ._shared.response_handlers import return_response_headers, process_storage_error 16from ._generated.models import StorageErrorException 17from ._generated.operations import FileOperations, ShareOperations 18 19if TYPE_CHECKING: 20 from datetime import datetime 21 ShareFileClient = TypeVar("ShareFileClient") 22 ShareClient = TypeVar("ShareClient") 23 24 25class ShareLeaseClient(object): 26 """Creates a new ShareLeaseClient. 27 28 This client provides lease operations on a ShareClient or ShareFileClient. 29 30 :ivar str id: 31 The ID of the lease currently being maintained. This will be `None` if no 32 lease has yet been acquired. 33 :ivar str etag: 34 The ETag of the lease currently being maintained. This will be `None` if no 35 lease has yet been acquired or modified. 36 :ivar ~datetime.datetime last_modified: 37 The last modified timestamp of the lease currently being maintained. 38 This will be `None` if no lease has yet been acquired or modified. 39 40 :param client: 41 The client of the file or share to lease. 42 :type client: ~azure.storage.fileshare.ShareFileClient or 43 ~azure.storage.fileshare.ShareClient 44 :param str lease_id: 45 A string representing the lease ID of an existing lease. This value does not 46 need to be specified in order to acquire a new lease, or break one. 47 """ 48 def __init__( 49 self, client, lease_id=None 50 ): # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs 51 # type: (Union[ShareFileClient, ShareClient], Optional[str]) -> None 52 self.id = lease_id or str(uuid.uuid4()) 53 self.last_modified = None 54 self.etag = None 55 if hasattr(client, 'file_name'): 56 self._client = client._client.file # type: ignore # pylint: disable=protected-access 57 self._snapshot = None 58 elif hasattr(client, 'share_name'): 59 self._client = client._client.share 60 self._snapshot = client.snapshot 61 else: 62 raise TypeError("Lease must use ShareFileClient or ShareClient.") 63 64 def __enter__(self): 65 return self 66 67 def __exit__(self, *args): 68 self.release() 69 70 @distributed_trace 71 def acquire(self, **kwargs): 72 # type: (**Any) -> None 73 """Requests a new lease. This operation establishes and manages a lock on a 74 file or share for write and delete operations. If the file or share does not have an active lease, 75 the File or Share service creates a lease on the file or share. If the file has an active lease, 76 you can only request a new lease using the active lease ID. 77 78 79 If the file or share does not have an active lease, the File or Share service creates a 80 lease on the file and returns a new lease ID. 81 82 :keyword int lease_duration: 83 Specifies the duration of the lease, in seconds, or negative one 84 (-1) for a lease that never expires. File leases never expire. A non-infinite share lease can be 85 between 15 and 60 seconds. A share lease duration cannot be changed 86 using renew or change. Default is -1 (infinite share lease). 87 88 :keyword int timeout: 89 The timeout parameter is expressed in seconds. 90 :rtype: None 91 """ 92 try: 93 lease_duration = kwargs.pop('lease_duration', -1) 94 if self._snapshot: 95 kwargs['sharesnapshot'] = self._snapshot 96 response = self._client.acquire_lease( 97 timeout=kwargs.pop('timeout', None), 98 duration=lease_duration, 99 proposed_lease_id=self.id, 100 cls=return_response_headers, 101 **kwargs) 102 except StorageErrorException as error: 103 process_storage_error(error) 104 self.id = response.get('lease_id') # type: str 105 self.last_modified = response.get('last_modified') # type: datetime 106 self.etag = response.get('etag') # type: str 107 108 @distributed_trace 109 def renew(self, **kwargs): 110 # type: (Any) -> None 111 """Renews the share lease. 112 113 The share lease can be renewed if the lease ID specified in the 114 lease client matches that associated with the share. Note that 115 the lease may be renewed even if it has expired as long as the share 116 has not been leased again since the expiration of that lease. When you 117 renew a lease, the lease duration clock resets. 118 119 .. versionadded:: 12.6.0 120 121 :keyword int timeout: 122 The timeout parameter is expressed in seconds. 123 :return: None 124 """ 125 if isinstance(self._client, FileOperations): 126 raise TypeError("Lease renewal operations are only valid for ShareClient.") 127 try: 128 response = self._client.renew_lease( 129 lease_id=self.id, 130 timeout=kwargs.pop('timeout', None), 131 sharesnapshot=self._snapshot, 132 cls=return_response_headers, 133 **kwargs) 134 except StorageErrorException as error: 135 process_storage_error(error) 136 self.etag = response.get('etag') # type: str 137 self.id = response.get('lease_id') # type: str 138 self.last_modified = response.get('last_modified') # type: datetime 139 140 @distributed_trace 141 def release(self, **kwargs): 142 # type: (Any) -> None 143 """Releases the lease. The lease may be released if the lease ID specified on the request matches 144 that associated with the share or file. Releasing the lease allows another client to immediately acquire 145 the lease for the share or file as soon as the release is complete. 146 147 :keyword int timeout: 148 The timeout parameter is expressed in seconds. 149 :return: None 150 """ 151 try: 152 if self._snapshot: 153 kwargs['sharesnapshot'] = self._snapshot 154 response = self._client.release_lease( 155 lease_id=self.id, 156 timeout=kwargs.pop('timeout', None), 157 cls=return_response_headers, 158 **kwargs) 159 except StorageErrorException as error: 160 process_storage_error(error) 161 self.etag = response.get('etag') # type: str 162 self.id = response.get('lease_id') # type: str 163 self.last_modified = response.get('last_modified') # type: datetime 164 165 @distributed_trace 166 def change(self, proposed_lease_id, **kwargs): 167 # type: (str, Any) -> None 168 """ Changes the lease ID of an active lease. A change must include the current lease ID in x-ms-lease-id and 169 a new lease ID in x-ms-proposed-lease-id. 170 171 :param str proposed_lease_id: 172 Proposed lease ID, in a GUID string format. The File or Share service will raise an error 173 (Invalid request) if the proposed lease ID is not in the correct format. 174 :keyword int timeout: 175 The timeout parameter is expressed in seconds. 176 :return: None 177 """ 178 try: 179 if self._snapshot: 180 kwargs['sharesnapshot'] = self._snapshot 181 response = self._client.change_lease( 182 lease_id=self.id, 183 proposed_lease_id=proposed_lease_id, 184 timeout=kwargs.pop('timeout', None), 185 cls=return_response_headers, 186 **kwargs) 187 except StorageErrorException as error: 188 process_storage_error(error) 189 self.etag = response.get('etag') # type: str 190 self.id = response.get('lease_id') # type: str 191 self.last_modified = response.get('last_modified') # type: datetime 192 193 @distributed_trace 194 def break_lease(self, **kwargs): 195 # type: (Any) -> int 196 """Force breaks the lease if the file or share has an active lease. Any authorized request can break the lease; 197 the request is not required to specify a matching lease ID. An infinite lease breaks immediately. 198 199 Once a lease is broken, it cannot be changed. Any authorized request can break the lease; 200 the request is not required to specify a matching lease ID. 201 When a lease is successfully broken, the response indicates the interval 202 in seconds until a new lease can be acquired. 203 204 :keyword int lease_break_period: 205 This is the proposed duration of seconds that the share lease 206 should continue before it is broken, between 0 and 60 seconds. This 207 break period is only used if it is shorter than the time remaining 208 on the share lease. If longer, the time remaining on the share lease is used. 209 A new share lease will not be available before the break period has 210 expired, but the share lease may be held for longer than the break 211 period. If this header does not appear with a break 212 operation, a fixed-duration share lease breaks after the remaining share lease 213 period elapses, and an infinite share lease breaks immediately. 214 215 .. versionadded:: 12.6.0 216 217 :keyword int timeout: 218 The timeout parameter is expressed in seconds. 219 :return: Approximate time remaining in the lease period, in seconds. 220 :rtype: int 221 """ 222 try: 223 lease_break_period = kwargs.pop('lease_break_period', None) 224 if self._snapshot: 225 kwargs['sharesnapshot'] = self._snapshot 226 if isinstance(self._client, ShareOperations): 227 kwargs['break_period'] = lease_break_period 228 if isinstance(self._client, FileOperations) and lease_break_period: 229 raise TypeError("Setting a lease break period is only applicable to Share leases.") 230 231 response = self._client.break_lease( 232 timeout=kwargs.pop('timeout', None), 233 cls=return_response_headers, 234 **kwargs) 235 except StorageErrorException as error: 236 process_storage_error(error) 237 return response.get('lease_time') # type: ignore 238