Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Storage]: Add a new command group az storage share-rm to use the Microsoft.Storage resource provider for Azure file share management operations. #11649

Merged
merged 9 commits into from
Jan 19, 2020
5 changes: 4 additions & 1 deletion src/azure-cli/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Release History

* Fix #6371: Support filename and environment variable completion in Bash

**Storage**

* Add a new command group `az storage share-rm` to use the Microsoft.Storage resource provider for Azure file share management operations.
Copy link
Contributor

@Juliehzl Juliehzl Jan 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about "Add a new command group az storage share-rm to support manage File share (create/delete/update/show/list) in mgmt plane"? #WontFix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI users should not care if it's mgmt plane or data plane


In reply to: 364247847 [](ancestors = 364247847)


2.0.80
++++++

Expand Down Expand Up @@ -97,7 +101,6 @@ Release History
* Update azure-mgmt-storage version to 7.0.0 to use api version 2019-06-01
* Add new parameters `--enable-delete-retention` and `--delete-retention-days` to support managing delete retention policy for storage account blob-service-properties.


2.0.78
++++++

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,9 @@ def cf_mgmt_blob_services(cli_ctx, _):
return storage_client_factory(cli_ctx).blob_services


def cf_mgmt_file_shares(cli_ctx, _):
return storage_client_factory(cli_ctx).file_shares


def cf_blob_data_gen_update(cli_ctx, kwargs):
return blob_data_service_factory(cli_ctx, kwargs.copy())
77 changes: 77 additions & 0 deletions src/azure-cli/azure/cli/command_modules/storage/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,83 @@
text: az storage remove -s MyShare --recursive
"""

helps['storage share-rm'] = """
type: group
short-summary: Manage Azure file shares using the Microsoft.Storage resource provider.
"""

helps['storage share-rm create'] = """
type: command
short-summary: Create a new Azure file share under the specified storage account.
examples:
- name: Create a new Azure file share 'myfileshare' with metadata and quota as 10 GB under the storage account 'mystorageaccount'(account name) in resource group 'MyResourceGroup'.
text: az storage share-rm create -g MyResourceGroup --storage-account mystorageaccount --name myfileshare --quota 10 --metadata key1=value1 key2=value2
- name: Create a new Azure file share 'myfileshare' with metadata and quota as 6000 GB under the storage account 'mystorageaccount'(account name) which enables large file share in resource group 'MyResourceGroup'.
text: |
az storage account update -g MyResourceGroup --name mystorageaccount --enable-large-file-share
az storage share-rm create -g MyResourceGroup --storage-account mystorageaccount --name myfileshare --quota 6000 --metadata key1=value1 key2=value2
- name: Create a new Azure file share 'myfileshare' with metadata and quota as 10 GB under the storage account 'mystorageaccount' (account id).
text: az storage share-rm create --storage-account mystorageaccount --name myfileshare --quota 10 --metadata key1=value1 key2=value2
"""

helps['storage share-rm delete'] = """
type: command
short-summary: Delete the specified Azure file share.
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
- name: Delete an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account id).
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
"""

helps['storage share-rm exists'] = """
type: command
short-summary: Check for the existence of an Azure file share.
examples:
- name: Check for the existence of an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account name) in resource group 'MyResourceGroup'.
text: az storage share-rm exists -g MyResourceGroup --storage-account mystorageaccount --name myfileshare
- name: Check for the existence of an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account id).
text: az storage share-rm exists --storage-account mystorageaccount --name myfileshare
- name: Check for the existence of an Azure file share by resource id.
text: az storage share-rm exists --ids file-share-id
"""

helps['storage share-rm list'] = """
type: command
short-summary: List the Azure file shares under the specified storage account.
examples:
- name: List the Azure file shares under the storage account 'mystorageaccount' (account name) in resource group 'MyResourceGroup'.
text: az storage share-rm list -g MyResourceGroup --storage-account mystorageaccount
- name: List the Azure file shares under the storage account 'mystorageaccount' (account id).
text: az storage share-rm list --storage-account mystorageaccount
"""

helps['storage share-rm show'] = """
type: command
short-summary: Show the properties for a specified Azure file share.
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.
text: az storage share-rm show --ids file-share-id
"""

helps['storage share-rm update'] = """
type: command
short-summary: Update the properties for an Azure file share.
examples:
- name: Update the properties for an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account name) in resource group 'MyResourceGroup'.
text: az storage share-rm update -g MyResourceGroup --storage-account mystorageaccount --name myfileshare --quota 3 --metadata key1=value1 key2=value2
- name: Update the properties for an Azure file share 'myfileshare' under the storage account 'mystorageaccount' (account id).
text: az storage share-rm update --storage-account mystorageaccount --name myfileshare --quota 3 --metadata key1=value1 key2=value2
- name: Update the properties for an Azure file shares by resource id.
text: az storage share-rm update --ids file-share-id --quota 3 --metadata key1=value1 key2=value2
"""

helps['storage share'] = """
type: group
short-summary: Manage file shares.
Expand Down
21 changes: 20 additions & 1 deletion src/azure-cli/azure/cli/command_modules/storage/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
storage_account_key_options, process_file_download_namespace, process_metric_update_namespace,
get_char_options_validator, validate_bypass, validate_encryption_source, validate_marker,
validate_storage_data_plane_list, validate_azcopy_upload_destination_url,
validate_azcopy_remove_arguments, as_user_validator)
validate_azcopy_remove_arguments, as_user_validator, parse_storage_account)


def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statements
Expand All @@ -34,6 +34,10 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
t_queue_service = self.get_sdk('queue#QueueService')
t_table_service = get_table_data_type(self.cli_ctx, 'table', 'TableService')

storage_account_type = CLIArgumentType(options_list='--storage-account',
help='The name or ID of the storage account.',
validator=parse_storage_account, id_part='name')

acct_name_type = CLIArgumentType(options_list=['--account-name', '-n'], help='The storage account name.',
id_part='name',
completer=get_resource_name_completion_list('Microsoft.Storage/storageAccounts'))
Expand Down Expand Up @@ -635,6 +639,21 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
with self.argument_context('storage share') as c:
c.argument('share_name', share_name_type, options_list=('--name', '-n'))

for item in ['create', 'delete', 'exists', 'list', 'show', 'update']:
with self.argument_context('storage share-rm {}'.format(item), resource_type=ResourceType.MGMT_STORAGE) as c:
c.argument('resource_group_name', required=False)
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('share_quota', type=int, options_list='--quota')
c.argument('metadata', nargs='+',
help='Metadata in space-separated key=value pairs that is associated with the share. '
'This overwrites any existing metadata',
validator=validate_metadata)
c.ignore('filter', 'maxpagesize', 'skip_token')

with self.argument_context('storage share-rm list', resource_type=ResourceType.MGMT_STORAGE) as c:
c.argument('account_name', storage_account_type, id_part=None)

with self.argument_context('storage share url') as c:
c.argument('unc', action='store_true', help='Output UNC network path.')
c.argument('protocol', arg_type=get_enum_type(['http', 'https'], 'https'), help='Protocol to use.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ def _cancel_timer_event_handler(_, **__):


# region PARAMETER VALIDATORS
def parse_storage_account(namespace):
"""Parse storage account which can be either account name or account id"""
from msrestazure.tools import parse_resource_id, is_valid_resource_id

if namespace.account_name and is_valid_resource_id(namespace.account_name):
namespace.resource_group_name = parse_resource_id(namespace.account_name)['resource_group']
namespace.account_name = parse_resource_id(namespace.account_name)['name']


def process_resource_group(cmd, namespace):
"""Processes the resource group parameter from the account name"""
Expand Down
20 changes: 19 additions & 1 deletion src/azure-cli/azure/cli/command_modules/storage/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
multi_service_properties_factory,
cf_mgmt_policy,
cf_blob_data_gen_update, cf_sa_for_keys,
cf_mgmt_blob_services)
cf_mgmt_blob_services, cf_mgmt_file_shares)
from azure.cli.command_modules.storage.sdkutil import cosmosdb_table_exists
from azure.cli.command_modules.storage._format import transform_immutability_policy
from azure.cli.core.commands import CliCommandType
Expand All @@ -31,6 +31,12 @@ def load_command_table(self, _): # pylint: disable=too-many-locals, too-many-st
resource_type=ResourceType.MGMT_STORAGE
)

file_shares_mgmt_sdk = CliCommandType(
operations_tmpl='azure.mgmt.storage.operations#FileSharesOperations.{}',
client_factory=cf_mgmt_file_shares,
resource_type=ResourceType.MGMT_STORAGE
)

storage_account_sdk_keys = CliCommandType(
operations_tmpl='azure.mgmt.storage.operations#StorageAccountsOperations.{}',
client_factory=cf_sa_for_keys,
Expand Down Expand Up @@ -320,6 +326,18 @@ def get_custom_sdk(custom_module, client_factory, resource_type=ResourceType.DAT
client_factory=file_data_service_factory,
resource_type=ResourceType.DATA_STORAGE)

with self.command_group('storage share-rm', command_type=file_shares_mgmt_sdk,
custom_command_type=get_custom_sdk('file',
cf_mgmt_file_shares,
resource_type=ResourceType.MGMT_STORAGE),
resource_type=ResourceType.MGMT_STORAGE, min_api='2019-04-01', is_preview=True) as g:
g.command('create', 'create')
g.command('delete', 'delete', confirmation=True)
g.custom_command('exists', '_file_share_exists', transform=create_boolean_result_output_transformer('exists'))
g.command('list', 'list')
g.show_command('show', 'get')
g.command('update', 'update')

with self.command_group('storage share', command_type=file_sdk,
custom_command_type=get_custom_sdk('file', file_data_service_factory)) as g:
from ._format import (transform_share_list,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,12 @@ def _make_directory_in_files_share(file_service, file_share, directory_path, exi

if existing_dirs:
existing_dirs.add(directory_path)


def _file_share_exists(client, resource_group_name, account_name, share_name):
from msrestazure.azure_exceptions import CloudError
try:
file_share = client.get(resource_group_name, account_name, share_name)
return file_share is not None
except CloudError:
return False

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import os
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, api_version_constraint)
from azure.cli.core.profiles import ResourceType
from ..storage_test_util import StorageScenarioMixin


@api_version_constraint(ResourceType.MGMT_STORAGE, min_api='2019-04-01')
class StorageFileShareUsingResourceProviderScenarios(StorageScenarioMixin, ScenarioTest):
@ResourceGroupPreparer()
@StorageAccountPreparer()
def test_storage_file_using_rm_main_scenario(self):
# 1. Test create command.

# Create file share with storage account name and resource group.
share_name_1 = self.create_random_name('share', 24)
initial_quota = 5
self.kwargs.update({
'share_name_1': share_name_1,
'initial_quota': initial_quota
})

result = self.cmd('storage share-rm create --storage-account {sa} -g {rg} -n {share_name_1} --quota {initial_quota} --metadata key1=value1').get_output_in_json()
self.assertEqual(result['name'], share_name_1)
self.assertEqual(result['shareQuota'], initial_quota)
self.assertEqual(result['metadata']['key1'], 'value1')

share_id_1 = result['id']
self.kwargs.update({
'share_id_1': share_id_1
})

# Create file share with storage account id.
share_name_2 = self.create_random_name('share', 24)
storage_account = self.cmd('storage account show -n {sa}').get_output_in_json()
storage_account_id = storage_account['id']
self.kwargs.update({
'share_name_2': share_name_2,
'storage_account_id': storage_account_id
})

result = self.cmd('storage share-rm create --storage-account {storage_account_id} -n {share_name_2} --quota {initial_quota} --metadata key1=value1').get_output_in_json()
self.assertEqual(result['name'], share_name_2)
self.assertEqual(result['shareQuota'], initial_quota)
self.assertEqual(result['metadata']['key1'], 'value1')

share_id_2 = result['id']
self.kwargs.update({
'share_id_2': share_id_2
})

# 2. Test exists command (the file share exists).

# Check existence with storage account name and resource group.
result = self.cmd('storage share-rm exists --storage-account {sa} -g {rg} -n {share_name_1}').get_output_in_json()
self.assertEqual(result['exists'], True)

# Check existence with storage account id.
result = self.cmd('storage share-rm exists --storage-account {storage_account_id} -n {share_name_1}').get_output_in_json()
self.assertEqual(result['exists'], True)

# Check existence by file share resource id.
result = self.cmd('storage share-rm exists --ids {share_id_1}').get_output_in_json()
self.assertEqual(result['exists'], True)

# 3. Test show command (the file share exists).

# Show properties of a file share with storage account name and resource group.
result = self.cmd('storage share-rm show --storage-account {sa} -g {rg} -n {share_name_1}').get_output_in_json()
self.assertEqual(result['name'], share_name_1)

# Show properties of a file share with storage account id.
result = self.cmd('storage share-rm show --storage-account {storage_account_id} -n {share_name_1}').get_output_in_json()
self.assertEqual(result['name'], share_name_1)

# Show properties by file share resource id.
result = self.cmd('storage share-rm show --ids {share_id_1}').get_output_in_json()
self.assertEqual(result['name'], share_name_1)

# 4. Test show command (the file share doesn't exist).
non_exist_share_name = self.create_random_name('share', 24)
self.kwargs.update({
'non_exist_share_name': non_exist_share_name
})
with self.assertRaisesRegexp(SystemExit, '3'):
self.cmd('storage share-rm show --storage-account {sa} -g {rg} -n {non_exist_share_name}')

# 5. Test update command.
updated_quota = 10
self.kwargs.update({
'updated_quota': updated_quota
})

# Update file share with storage account name and resource group.
result = self.cmd(
'storage share-rm update --storage-account {sa} -g {rg} -n {share_name_1} --quota {updated_quota} --metadata key2=value2').get_output_in_json()
self.assertEqual(result['shareQuota'], updated_quota)
self.assertEqual(result['metadata']['key2'], 'value2')
self.assertNotIn('key1', result['metadata'])

# Update file share with storage account id.
result = self.cmd(
'storage share-rm update --storage-account {storage_account_id} -n {share_name_2} --quota {updated_quota} --metadata key2=value2').get_output_in_json()
self.assertEqual(result['shareQuota'], updated_quota)
self.assertEqual(result['metadata']['key2'], 'value2')
self.assertNotIn('key1', result['metadata'])

# Update file share by resource id
result = self.cmd(
'storage share-rm update --ids {share_id_1} --quota {updated_quota} --metadata key2=value2').get_output_in_json()
self.assertEqual(result['shareQuota'], updated_quota)
self.assertEqual(result['metadata']['key2'], 'value2')
self.assertNotIn('key1', result['metadata'])

# 6. Test list command.

# List file shares with storage account name and resource group.
self.assertIn(share_name_1,
self.cmd('storage share-rm list --storage-account {sa} -g {rg} --query "[].name"').get_output_in_json())

# List file shares with storage account id.
self.assertIn(share_name_1,
self.cmd('storage share-rm list --storage-account {storage_account_id} --query "[].name"').get_output_in_json())

# 7. Test delete command.

# Delete file shares with storage account name and resource group.
self.cmd('storage share-rm delete --storage-account {sa} -g {rg} -n {share_name_1} -y')

# Delete file share by resource id.
self.cmd('storage share-rm delete --ids {share_id_2} -y')

# 8. Test exists command (the file share doesn't exist).
result = self.cmd('storage share-rm exists --storage-account {sa} -g {rg} -n {share_name_1}').get_output_in_json()
self.assertEqual(result['exists'], False)

result = self.cmd('storage share-rm exists --ids {share_id_2}').get_output_in_json()
self.assertEqual(result['exists'], False)