Skip to content

Commit ce6b627

Browse files
authored
[Storage] az storage share/directory/file: support NFS FileShares, add az storage file hard-link create (#31050)
* update storage multiapi to 1.4.0, fileshare to api version 2025-05-05 * fix version in setup * add nfs file share support, add `az storage file hard-link create` * lint, style
1 parent ba5d6da commit ce6b627

File tree

11 files changed

+1291
-10
lines changed

11 files changed

+1291
-10
lines changed

src/azure-cli-core/azure/cli/core/profiles/_shared.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def default_api_version(self):
216216
ResourceType.DATA_STORAGE: '2018-11-09',
217217
ResourceType.DATA_STORAGE_BLOB: '2022-11-02',
218218
ResourceType.DATA_STORAGE_FILEDATALAKE: '2021-08-06',
219-
ResourceType.DATA_STORAGE_FILESHARE: '2024-08-04',
219+
ResourceType.DATA_STORAGE_FILESHARE: '2025-05-05',
220220
ResourceType.DATA_STORAGE_QUEUE: '2018-03-28',
221221
ResourceType.DATA_COSMOS_TABLE: '2017-04-17',
222222
ResourceType.MGMT_SERVICEBUS: '2022-10-01-preview',

src/azure-cli/azure/cli/command_modules/storage/_help.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,11 @@
18001800
helps['storage directory create'] = """
18011801
type: command
18021802
short-summary: Create a new directory under the specified share or parent directory.
1803+
examples:
1804+
- name: Create a new directory under the specified NFS share with file-mode, owner, group
1805+
text: |
1806+
az storage directory create --account-name mystorageaccount --name dir --share-name myshare
1807+
--file-mode rwxr--r-- --owner 1 --group 2
18031808
"""
18041809

18051810
helps['storage directory delete'] = """
@@ -1914,7 +1919,7 @@
19141919

19151920
helps['storage file'] = """
19161921
type: group
1917-
short-summary: Manage file shares that use the SMB 3.0 protocol.
1922+
short-summary: Manage file shares.
19181923
"""
19191924

19201925
helps['storage file copy'] = """
@@ -1947,6 +1952,12 @@
19471952
- name: Copy a file asynchronously from file snapshot to destination storage account with sas token.
19481953
text: |
19491954
az storage file copy start --source-account-name srcaccount --source-account-key 00000000 --source-path <srcpath-to-file> --source-share srcshare --file-snapshot "2020-03-02T13:51:54.0000000Z" --destination-path <destpath-to-file> --destination-share destshare --account-name destaccount --sas-token <destination-sas>
1955+
- name: Copy a file with the source file-mode, group, owner.
1956+
text: |
1957+
az storage file copy start --source-account-name srcaccount --source-path srcpath --source-share srcshare --destination-path dstpath --destination-share dstshare --owner-copy-mode Source --file-mode-copy-mode Source
1958+
- name: Copy a file with the overridden file-mode, group, owner.
1959+
text: |
1960+
az storage file copy start --source-account-name srcaccount --source-path srcpath --source-share srcshare --destination-path dstpath --destination-share dstshare --owner-copy-mode Override --file-mode-copy-mode Override --file-mode rw-rw-rw- --owner 4 --group 5
19501961
"""
19511962

19521963
helps['storage file copy start-batch'] = """
@@ -2165,12 +2176,12 @@
21652176

21662177
helps['storage file upload'] = """
21672178
type: command
2168-
short-summary: Upload a file to a share that uses the SMB 3.0 protocol.
2179+
short-summary: Upload a file to a share.
21692180
long-summary: Creates or updates an Azure file from a source path with automatic chunking and progress notifications.
21702181
examples:
21712182
- name: Upload to a local file to a share.
21722183
text: az storage file upload -s MyShare --source /path/to/file
2173-
- name: Upload a file to a share that uses the SMB 3.0 protocol. (autogenerated)
2184+
- name: Upload a file to a share.
21742185
text: |
21752186
az storage file upload --account-key 00000000 --account-name MyStorageAccount --path path/file.txt --share-name MyShare --source /path/to/file
21762187
crafted: true
@@ -2994,6 +3005,9 @@
29943005
text: |
29953006
az storage share create --account-name MyAccount --name MyFileShare
29963007
crafted: true
3008+
- name: Creates a new share under the specified account for NFS.
3009+
text: |
3010+
az storage share create --account-name MyAccount --name MyFileShare --protocol nfs
29973011
"""
29983012

29993013
helps['storage share exists'] = """
@@ -3161,3 +3175,17 @@
31613175
type: group
31623176
short-summary: Manage shared access policies of a storage table.
31633177
"""
3178+
3179+
helps['storage file hard-link'] = """
3180+
type: group
3181+
short-summary: Manage storage file hard-link.
3182+
"""
3183+
3184+
helps['storage file hard-link create'] = """
3185+
type: command
3186+
short-summary: NFS only. Create a hard link to the file specified by path.
3187+
examples:
3188+
- name: Create a hard link to an NFS file specified by path.
3189+
text: |
3190+
az storage file hard-link create --account-name MyAccount --share-name share --path link_path --target original_path
3191+
"""

src/azure-cli/azure/cli/command_modules/storage/_params.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,10 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
17781778
'default.')
17791779
c.argument('quota', type=int, help='Specifies the maximum size of the share, in gigabytes. Must be greater '
17801780
'than 0, and less than or equal to 5TB (5120).')
1781+
t_share_protocols_type = self.get_sdk('_models#ShareProtocols',
1782+
resource_type=ResourceType.DATA_STORAGE_FILESHARE)
1783+
c.extra('protocols', options_list=['--protocol'], arg_type=get_enum_type(t_share_protocols_type),
1784+
help='The protocol to enable for the share.')
17811785

17821786
with self.argument_context('storage share url') as c:
17831787
c.extra('unc', action='store_true', help='Output UNC network path.')
@@ -1941,6 +1945,16 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
19411945

19421946
with self.argument_context('storage directory create') as c:
19431947
c.argument('fail_on_exist', help='Throw an exception if the directory already exists.')
1948+
c.extra('file_mode',
1949+
help='Only applicable to NFS Directory. The mode permissions to be set on the directory. '
1950+
'Symbolic (rwxrw-rw-) is supported. The sticky bit is also supported and its represented '
1951+
'either by the letter t or T in the final character-place depending on whether the execution '
1952+
'bit for the others category is set or unset respectively, absence of t or T indicates sticky '
1953+
'bit not set."')
1954+
c.extra('owner', help='Only applicable to NFS Directory. The owner user identifier (UID) to be set on the '
1955+
'directory. The default value is 0 (root).')
1956+
c.extra('group', help='Only applicable to NFS Directory. The owner group identifier (GID) to be set on the '
1957+
'directory. The default value is 0 (root group).')
19441958

19451959
with self.argument_context('storage directory delete') as c:
19461960
c.argument('fail_not_exist', help='Throw an exception if the directory does not exist.')
@@ -1969,6 +1983,33 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
19691983
help='Metadata in space-separated key=value pairs. This overwrites any existing metadata.',
19701984
validator=validate_metadata)
19711985
c.extra('timeout', help='Request timeout in seconds. Applies to each call to the service.', type=int)
1986+
c.extra('file_mode',
1987+
help='The mode permissions to be set on the file. Only applicable to NFS Files. '
1988+
'Only work together with parameter `--file-mode-copy-mode Override`. '
1989+
'Symbolic (rwxrw-rw-) is supported. '
1990+
'The sticky bit is also supported and its represented '
1991+
'either by the letter t or T in the final character-place depending on whether the execution '
1992+
'bit for the others category is set or unset respectively, absence of t or T indicates sticky '
1993+
'bit not set."')
1994+
c.extra('owner', help='Only applicable to NFS Files. Only work together with parameter '
1995+
'`--owner-copy-mode Override`. The owner user identifier (UID) '
1996+
'to be set on the directory. The default value is 0 (root).')
1997+
c.extra('group', help='Only applicable to NFS Files. Only work together with parameter '
1998+
'`--owner-copy-mode Override`. The owner group identifier (GID) '
1999+
'to be set on the directory. The default value is 0 (root group).')
2000+
t_file_mode_copy_mode_type = self.get_sdk('_generated.models._azure_file_storage_enums#ModeCopyMode',
2001+
resource_type=ResourceType.DATA_STORAGE_FILESHARE)
2002+
c.extra('file_mode_copy_mode',
2003+
arg_type=get_enum_type(t_file_mode_copy_mode_type),
2004+
help='Only applicable to NFS Files. Applicable only when the copy source is a File. '
2005+
'Determines the copy behavior of the mode bits of the destination file. '
2006+
'If not populated, the destination file will have the default File Mode.')
2007+
t_owner_copy_mode_type = self.get_sdk('_generated.models._azure_file_storage_enums#OwnerCopyMode',
2008+
resource_type=ResourceType.DATA_STORAGE_FILESHARE)
2009+
c.extra('owner_copy_mode', arg_type=get_enum_type(t_owner_copy_mode_type),
2010+
help='Only applicable to NFS Files. Applicable only when the copy source is a File. '
2011+
'Determines the copy behavior of the owner and group of the destination file. '
2012+
'If not populated, the destination file will have the default Owner and Group.')
19722013

19732014
with self.argument_context('storage file copy cancel') as c:
19742015
c.register_path_argument(options_list=('--destination-path', '-p'))
@@ -2116,6 +2157,19 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
21162157
'already validate. Note that this MD5 hash is not stored with the file.')
21172158
c.extra('file_url', help='The full endpoint URL to the File, including SAS token if used.')
21182159

2160+
for cmd in ['file update', 'file upload']:
2161+
with self.argument_context(f'storage {cmd}') as c:
2162+
c.extra('file_mode',
2163+
help='Only applicable to NFS Files. The mode permissions to be set on the file. '
2164+
'Symbolic (rwxrw-rw-) is supported. The sticky bit is also supported and its represented '
2165+
'either by the letter t or T in the final character-place depending on whether the execution '
2166+
'bit for the others category is set or unset respectively, absence of t or T indicates sticky '
2167+
'bit not set."')
2168+
c.extra('owner', help='Only applicable to NFS Files. The owner user identifier (UID) to be set on the '
2169+
'file. The default value is 0 (root).')
2170+
c.extra('group', help='Only applicable to NFS Files. The owner group identifier (GID) to be set on the '
2171+
'file. The default value is 0 (root group).')
2172+
21192173
with self.argument_context('storage file url') as c:
21202174
c.register_path_argument(fileshare=True)
21212175
c.extra('share_name', share_name_type, required=True)
@@ -2701,3 +2755,13 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
27012755
c.extra('disallow_source_trailing_dot', arg_type=get_three_state_flag(), default=False,
27022756
options_list=["--disallow-source-trailing-dot", "--disallow-src-trailing"],
27032757
help="If true, the trailing dot will be trimmed from the source URI. Default to False")
2758+
2759+
with self.argument_context('storage file hard-link create') as c:
2760+
c.extra('share_name', share_name_type, required=True)
2761+
c.register_path_argument()
2762+
c.extra('target', required=True,
2763+
help='Specifies the path of the target file to which the link will be created, up to 2 KiB in length. '
2764+
'It should be the full path of the target starting from the root. The target file must be in the '
2765+
'same share and the same storage account.')
2766+
c.extra('lease',
2767+
help='Lease id, required if the file has an active lease.')

src/azure-cli/azure/cli/command_modules/storage/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ def get_custom_sdk(custom_module, client_factory, resource_type=ResourceType.DAT
723723
exception_handler=file_related_exception_handler,
724724
transform=transform_file_show_result)
725725
g.storage_custom_command('download-batch', 'storage_file_download_batch', client_factory=cf_share_client)
726+
g.storage_command_oauth('hard-link create', 'create_hardlink')
726727

727728
with self.command_group('storage cors', get_custom_sdk('cors', multi_service_properties_factory)) as g:
728729
from ._transformers import transform_cors_list_output

src/azure-cli/azure/cli/command_modules/storage/operations/file.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def list_share_files(cmd, client, directory_name=None, timeout=None, exclude_dir
110110

111111

112112
def storage_file_upload(client, local_file_path, content_settings=None,
113-
metadata=None, validate_content=False, progress_callback=None, max_connections=2, timeout=None):
113+
metadata=None, validate_content=False, progress_callback=None, max_connections=2, timeout=None,
114+
file_mode=None, owner=None, group=None):
114115
upload_args = {
115116
'content_settings': content_settings,
116117
'metadata': metadata,
@@ -130,6 +131,14 @@ def storage_file_upload(client, local_file_path, content_settings=None,
130131
if isinstance(response['content_md5'], bytearray):
131132
response['content_md5'] = ''.join(hex(x) for x in response['content_md5'])
132133

134+
if not (file_mode is None and owner is None and group is None):
135+
nfs_args = {
136+
'file_mode': file_mode,
137+
'owner': owner,
138+
'group': group
139+
}
140+
client.set_http_headers(content_settings, **nfs_args)
141+
133142
return response
134143

135144

0 commit comments

Comments
 (0)