diff --git a/src/azure-cli/azure/cli/command_modules/storage/_help.py b/src/azure-cli/azure/cli/command_modules/storage/_help.py index 16c87066156..7fda6dd3a4c 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/_help.py +++ b/src/azure-cli/azure/cli/command_modules/storage/_help.py @@ -2253,7 +2253,7 @@ helps['storage share-rm delete'] = """ type: command -short-summary: Delete the specified Azure file share. +short-summary: Delete the specified Azure file share or share snapshot. examples: - name: Delete an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account name) in resource group 'MyResourceGroup'. text: az storage share-rm delete -g MyResourceGroup --storage-account mystorageaccount --name myfileshare @@ -2261,6 +2261,8 @@ text: az storage share-rm delete --storage-account mystorageaccount --name myfileshare - name: Delete an Azure file share by resource id. text: az storage share-rm delete --ids file-share-id + - name: Delete an Azure file share snapshot. + text: az storage share-rm delete --ids file-share-id --snapshot "2021-03-25T05:29:56.0000000Z" """ helps['storage share-rm exists'] = """ @@ -2299,14 +2301,16 @@ helps['storage share-rm show'] = """ type: command -short-summary: Show the properties for a specified Azure file share. +short-summary: Show the properties for a specified Azure file share or share snapshot. examples: - name: Show the properties for an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account name) in resource group 'MyResourceGroup'. text: az storage share-rm show -g MyResourceGroup --storage-account mystorageaccount --name myfileshare - name: Show the properties for an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account id). text: az storage share-rm show --storage-account mystorageaccount --name myfileshare - - name: Show the properties of an Azure file shares by resource id. + - name: Show the properties of an Azure file share by resource id. text: az storage share-rm show --ids file-share-id + - name: Show the properties of an Azure file share snapshot + text: az storage share-rm show --ids file-share-id --snapshot "2021-03-25T05:29:56.0000000Z" """ helps['storage share-rm stats'] = """ @@ -2329,6 +2333,14 @@ text: az storage share-rm update --ids file-share-id --quota 3 --metadata key1=value1 key2=value2 """ +helps['storage share-rm snapshot'] = """ +type: command +short-summary: Create a snapshot of an existing share under the specified account. +examples: + - name: Create a snapshot of an existing share under the specified account. + text: az storage share-rm snapshot -g MyResourceGroup --storage-account mystorageaccount --name myfileshare +""" + helps['storage share'] = """ type: group short-summary: Manage file shares. diff --git a/src/azure-cli/azure/cli/command_modules/storage/_params.py b/src/azure-cli/azure/cli/command_modules/storage/_params.py index 4631f26010f..02acf18e87e 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/_params.py +++ b/src/azure-cli/azure/cli/command_modules/storage/_params.py @@ -1170,10 +1170,14 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem c.argument('account_name', storage_account_type) c.argument('share_name', share_name_type, options_list=('--name', '-n'), id_part='child_name_2') c.argument('expand', default=None) + c.argument('x_ms_snapshot', options_list=['--snapshot'], is_preview=True, + help='The DateTime value that specifies the share snapshot to retrieve.') c.ignore('filter', 'maxpagesize') - c.ignore('x_ms_snapshot') # Ignore first before it is ready - for item in ['create', 'update']: + with self.argument_context('storage share-rm update', resource_type=ResourceType.MGMT_STORAGE) as c: + c.ignore('x_ms_snapshot') + + for item in ['create', 'update', 'snapshot']: with self.argument_context('storage share-rm {}'.format(item), resource_type=ResourceType.MGMT_STORAGE) as c: t_enabled_protocols, t_root_squash, t_access_tier = \ self.get_models('EnabledProtocols', 'RootSquashType', 'ShareAccessTier', diff --git a/src/azure-cli/azure/cli/command_modules/storage/_transformers.py b/src/azure-cli/azure/cli/command_modules/storage/_transformers.py index d4b0f514511..dd45caf077d 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/_transformers.py +++ b/src/azure-cli/azure/cli/command_modules/storage/_transformers.py @@ -255,3 +255,10 @@ def transform_response_with_bytearray(response): if response[item] and isinstance(response[item], (bytes, bytearray)): response[item] = Serializer.serialize_bytearray(response[item]) return response + + +def transform_share_rm_output(result): + if hasattr(result, 'snapshot_time') and result.snapshot_time: + snapshot = result.snapshot_time + result.snapshot_time = snapshot.strftime("%Y-%m-%dT%H:%M:%S.%f0Z") + return result diff --git a/src/azure-cli/azure/cli/command_modules/storage/commands.py b/src/azure-cli/azure/cli/command_modules/storage/commands.py index 2b749f33dcb..4064dfd3c5f 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/commands.py +++ b/src/azure-cli/azure/cli/command_modules/storage/commands.py @@ -501,15 +501,18 @@ def get_custom_sdk(custom_module, client_factory, resource_type=ResourceType.DAT cf_mgmt_file_shares, resource_type=ResourceType.MGMT_STORAGE), resource_type=ResourceType.MGMT_STORAGE, min_api='2019-04-01') as g: + from ._transformers import transform_share_rm_output g.custom_command('create', 'create_share_rm') g.command('delete', 'delete', confirmation=True) g.custom_command('exists', '_file_share_exists', transform=create_boolean_result_output_transformer('exists')) g.custom_command('list', 'list_share_rm') - g.show_command('show', 'get') + g.show_command('show', 'get', transform=transform_share_rm_output) g.generic_update_command('update', setter_name='update', setter_arg_name='file_share', custom_func_name='update_share_rm') g.custom_command('stats', 'get_stats', transform=lambda x: getattr(x, 'share_usage_bytes')) g.custom_command('restore', 'restore_share_rm') + g.custom_command('snapshot', 'snapshot_share_rm', min_api='2020-08-01-preview', is_preview=True, + transform=transform_share_rm_output) with self.command_group('storage share', command_type=file_sdk, custom_command_type=get_custom_sdk('file', file_data_service_factory)) as g: diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py index ed62e6118fc..d80a1ef93bd 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py @@ -21,9 +21,25 @@ def create_share_rm(cmd, client, resource_group_name, account_name, share_name, metadata=None, share_quota=None, enabled_protocols=None, root_squash=None, access_tier=None): + return _create_share_rm(cmd, client, resource_group_name, account_name, share_name, metadata=metadata, + share_quota=share_quota, enabled_protocols=enabled_protocols, root_squash=root_squash, + access_tier=access_tier, snapshot=False) + + +def snapshot_share_rm(cmd, client, resource_group_name, account_name, share_name, metadata=None, share_quota=None, + enabled_protocols=None, root_squash=None, access_tier=None): + + return _create_share_rm(cmd, client, resource_group_name, account_name, share_name, metadata=metadata, + share_quota=share_quota, enabled_protocols=enabled_protocols, root_squash=root_squash, + access_tier=access_tier, snapshot=True) + + +def _create_share_rm(cmd, client, resource_group_name, account_name, share_name, metadata=None, share_quota=None, + enabled_protocols=None, root_squash=None, access_tier=None, snapshot=None): FileShare = cmd.get_models('FileShare', resource_type=ResourceType.MGMT_STORAGE) file_share = FileShare() + expand = None if share_quota is not None: file_share.share_quota = share_quota if enabled_protocols is not None: @@ -34,9 +50,12 @@ def create_share_rm(cmd, client, resource_group_name, account_name, share_name, file_share.metadata = metadata if access_tier is not None: file_share.access_tier = access_tier + if snapshot: + PutSharesExpand = cmd.get_models('PutSharesExpand', resource_type=ResourceType.MGMT_STORAGE) + expand = PutSharesExpand.SNAPSHOTS return client.create(resource_group_name=resource_group_name, account_name=account_name, share_name=share_name, - file_share=file_share) + file_share=file_share, expand=expand) def get_stats(client, resource_group_name, account_name, share_name): diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/recordings/test_storage_share_rm_with_snapshot.yaml b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/recordings/test_storage_share_rm_with_snapshot.yaml new file mode 100644 index 00000000000..1b83099b13b --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/recordings/test_storage_share_rm_with_snapshot.yaml @@ -0,0 +1,244 @@ +interactions: +- request: + body: '{}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage share-rm create + Connection: + - keep-alive + Content-Length: + - '2' + Content-Type: + - application/json + ParameterSetName: + - --storage-account -g -n + User-Agent: + - AZURECLI/2.21.0 azsdk-python-azure-mgmt-storage/17.0.0 Python/3.7.9 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003?api-version=2021-01-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003","name":"share000003","type":"Microsoft.Storage/storageAccounts/fileServices/shares"}' + headers: + cache-control: + - no-cache + content-length: + - '370' + content-type: + - application/json + date: + - Thu, 25 Mar 2021 07:19:06 GMT + etag: + - '"0x8D8EF5E4703AE85"' + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-writes: + - '1199' + status: + code: 201 + message: Created +- request: + body: '{"properties": {"metadata": {"k1": "v1"}, "shareQuota": 10, "enabledProtocols": + "SMB", "rootSquash": "AllSquash", "accessTier": "Hot"}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage share-rm snapshot + Connection: + - keep-alive + Content-Length: + - '135' + Content-Type: + - application/json + ParameterSetName: + - --storage-account -g -n -q --metadata --access-tier --enabled-protocols --root-squash + User-Agent: + - AZURECLI/2.21.0 azsdk-python-azure-mgmt-storage/17.0.0 Python/3.7.9 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003?$expand=snapshots&api-version=2021-01-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003","name":"share000003","type":"Microsoft.Storage/storageAccounts/fileServices/shares","properties":{"accessTier":"Hot","snapshotTime":"2021-03-25T07:19:07.0000000Z","metadata":{"k1":"v1"},"shareQuota":10,"enabledProtocols":"SMB","rootSquash":"AllSquash"}}' + headers: + cache-control: + - no-cache + content-length: + - '539' + content-type: + - application/json + date: + - Thu, 25 Mar 2021 07:19:07 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-writes: + - '1198' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage share-rm show + Connection: + - keep-alive + ParameterSetName: + - --storage-account -g -n --snapshot + User-Agent: + - AZURECLI/2.21.0 azsdk-python-azure-mgmt-storage/17.0.0 Python/3.7.9 (Windows-10-10.0.19041-SP0) + x-ms-snapshot: + - '2021-03-25T07:19:07.0000000Z' + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003?api-version=2021-01-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003","name":"share000003","type":"Microsoft.Storage/storageAccounts/fileServices/shares","etag":"\"0x8D8EF5E4703AE85\"","properties":{"accessTier":"TransactionOptimized","snapshotTime":"2021-03-25T07:19:07.0000000Z","lastModifiedTime":"2021-03-25T07:19:07.0000000Z","shareQuota":5120}}' + headers: + cache-control: + - no-cache + content-length: + - '566' + content-type: + - application/json + date: + - Thu, 25 Mar 2021 07:19:08 GMT + etag: + - '"0x8D8EF5E4703AE85"' + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage share-rm delete + Connection: + - keep-alive + Content-Length: + - '0' + ParameterSetName: + - --storage-account -g -n --snapshot -y + User-Agent: + - AZURECLI/2.21.0 azsdk-python-azure-mgmt-storage/17.0.0 Python/3.7.9 (Windows-10-10.0.19041-SP0) + x-ms-snapshot: + - '2021-03-25T07:19:07.0000000Z' + method: DELETE + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003?api-version=2021-01-01 + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + content-type: + - text/plain; charset=utf-8 + date: + - Thu, 25 Mar 2021 07:19:09 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-deletes: + - '14999' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - storage share-rm show + Connection: + - keep-alive + ParameterSetName: + - --storage-account -g -n --snapshot + User-Agent: + - AZURECLI/2.21.0 azsdk-python-azure-mgmt-storage/17.0.0 Python/3.7.9 (Windows-10-10.0.19041-SP0) + x-ms-snapshot: + - '2021-03-25T07:19:07.0000000Z' + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_snapshot000001/providers/Microsoft.Storage/storageAccounts/snapshot000002/fileServices/default/shares/share000003?api-version=2021-01-01 + response: + body: + string: '{"error":{"code":"ShareNotFound","message":"The specified share does + not exist.\nRequestId:a668f789-b01a-0090-2f47-21ad08000000\nTime:2021-03-25T07:19:09.8757180Z"}}' + headers: + cache-control: + - no-cache + content-length: + - '165' + content-type: + - application/json + date: + - Thu, 25 Mar 2021 07:19:09 GMT + expires: + - '-1' + pragma: + - no-cache + server: + - Microsoft-Azure-Storage-Resource-Provider/1.0,Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 404 + message: Not Found +version: 1 diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_rm_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_rm_scenarios.py index 762b7a55610..d602a2ccf89 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_rm_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_rm_scenarios.py @@ -182,6 +182,38 @@ def test_storage_share_rm_with_NFS(self): JMESPathCheck('length(@)', 0) }) + @api_version_constraint(ResourceType.MGMT_STORAGE, min_api='2020-08-01-preview') + @ResourceGroupPreparer(name_prefix="cli_snapshot", location="eastus") + @StorageAccountPreparer(name_prefix="snapshot", location="eastus", kind='StorageV2') + def test_storage_share_rm_with_snapshot(self): + self.kwargs.update({ + 'share': self.create_random_name('share', 24), + }) + self.cmd('storage share-rm create --storage-account {sa} -g {rg} -n {share}') + + result = self.cmd('storage share-rm snapshot --storage-account {sa} -g {rg} -n {share} ' + '-q 10 --metadata k1=v1 --access-tier Hot --enabled-protocols SMB --root-squash AllSquash').get_output_in_json() + self.assertEqual(result['name'], self.kwargs['share']) + self.assertEqual(result['shareQuota'], 10) + self.assertEqual(result['metadata']['k1'], 'v1') + self.assertEqual(result['accessTier'], 'Hot') + self.assertEqual(result['enabledProtocols'], 'SMB') + self.assertEqual(result['rootSquash'], 'AllSquash') + self.assertIsNotNone(result['snapshotTime']) + + self.kwargs.update({ + 'snapshot': result['snapshotTime'] + }) + + self.cmd('storage share-rm show --storage-account {sa} -g {rg} -n {share} --snapshot {snapshot}', checks=[ + JMESPathCheck('name', self.kwargs['share']), + JMESPathCheck('snapshotTime', self.kwargs['snapshot']) + ]) + + self.cmd('storage share-rm delete --storage-account {sa} -g {rg} -n {share} --snapshot {snapshot} -y') + with self.assertRaisesRegexp(SystemExit, '3'): + self.cmd('storage share-rm show --storage-account {sa} -g {rg} -n {share} --snapshot {snapshot}') + @api_version_constraint(ResourceType.MGMT_STORAGE, min_api='2019-06-01') @ResourceGroupPreparer(name_prefix="cli_tier", location="eastus") @StorageAccountPreparer(name_prefix="tier", location="eastus", kind='StorageV2')