Skip to content

Commit 2841667

Browse files
committed
[Storage] support file handle
1 parent 9e4376e commit 2841667

12 files changed

+659
-10
lines changed

src/Storage/Storage.Management/Az.Storage.psd1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ CmdletsToExport = 'Get-AzStorageAccount', 'Get-AzStorageAccountKey',
154154
'Update-AzStorageBlobServiceProperty',
155155
'Get-AzStorageBlobServiceProperty',
156156
'Enable-AzStorageBlobDeleteRetentionPolicy',
157-
'Disable-AzStorageBlobDeleteRetentionPolicy'
157+
'Disable-AzStorageBlobDeleteRetentionPolicy',
158+
'Get-AzStorageFileHandle','Close-AzStorageFileHandle'
158159

159160
# Variables to export from this module
160161
# VariablesToExport = @()

src/Storage/Storage.Management/ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
-->
2020
## Upcoming Release
2121
* Show more error information when cmdlet failed with StorageException
22+
* Support list or close file handles of a file share, file directory or a file
23+
- Get-AzStorageFileHandle
24+
- Close-AzStorageFileHandle
2225

2326
## Version 1.4.0
2427
* Support Kind FileStorage and SkuName Premium_ZRS when create Storage account

src/Storage/Storage.Management/Storage.Management.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
</PropertyGroup>
1616
<ItemGroup>
1717
<PackageReference Include="Microsoft.Azure.Management.Storage" Version="11.0.0" />
18-
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="10.0.1" />
19-
<PackageReference Include="Microsoft.Azure.Storage.File" Version="10.0.1" />
20-
<PackageReference Include="Microsoft.Azure.Storage.Queue" Version="10.0.1" />
18+
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="10.0.3" />
19+
<PackageReference Include="Microsoft.Azure.Storage.File" Version="10.0.3" />
20+
<PackageReference Include="Microsoft.Azure.Storage.Queue" Version="10.0.3" />
2121
</ItemGroup>
2222
<ItemGroup>
2323
<Compile Include="$(StorageToolsPath)Adapters\ARM.Storage.6\ARMStorageProvider.cs" Link="Common\ARMStorageProvider.cs" />

src/Storage/Storage.Test/Storage.Test.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
<ItemGroup>
1616
<PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="0.10.1-preview" />
17-
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="10.0.1" />
18-
<PackageReference Include="Microsoft.Azure.Storage.File" Version="10.0.1" />
19-
<PackageReference Include="Microsoft.Azure.Storage.Queue" Version="10.0.1" />
17+
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="10.0.3" />
18+
<PackageReference Include="Microsoft.Azure.Storage.File" Version="10.0.3" />
19+
<PackageReference Include="Microsoft.Azure.Storage.Queue" Version="10.0.3" />
2020
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" />
2121
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" />
2222
</ItemGroup>

src/Storage/Storage/Common/StorageCloudCmdletBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ public class StorageCloudCmdletBase<T> : AzureDataCmdlet
5151
public virtual IStorageContext Context { get; set; }
5252

5353
[Parameter(HelpMessage = "The server time out for each request in seconds.")]
54+
[Alias("ServerTimeoutPerRequestInSeconds")]
5455
public virtual int? ServerTimeoutPerRequest { get; set; }
5556

5657
[Parameter(HelpMessage = "The client side maximum execution time for each request in seconds.")]
58+
[Alias("ClientTimeoutPerRequestInSeconds")]
5759
public virtual int? ClientTimeoutPerRequest { get; set; }
5860

5961
/// <summary>

src/Storage/Storage/File/AzureStorageFileCmdletBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ protected override IStorageFileManagement CreateChannel()
4646
{
4747
this.Channel = new StorageFileManagement(
4848
this.ParameterSetName == Constants.ShareNameParameterSetName ||
49+
this.ParameterSetName.StartsWith("ShareName") ||
4950
this.ParameterSetName == Constants.MatchingPrefixParameterSetName ||
5051
this.ParameterSetName == Constants.SpecificParameterSetName ?
5152
this.GetCmdletStorageContext() :
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
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+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.WindowsAzure.Commands.Storage.File.Cmdlet
16+
{
17+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
18+
using Microsoft.Azure.Storage;
19+
using Microsoft.Azure.Storage.File;
20+
using Microsoft.WindowsAzure.Commands.Storage.Model.ResourceModel;
21+
using System.Globalization;
22+
using System.Management.Automation;
23+
using System.Net;
24+
25+
[Cmdlet("Close", Azure.Commands.ResourceManager.Common.AzureRMConstants.AzurePrefix + "StorageFileHandle", SupportsShouldProcess = true, DefaultParameterSetName = ShareNameCloseAllParameterSetName)]
26+
[OutputType(typeof(int))]
27+
public class CloseAzureStorageFileHandle : AzureStorageFileCmdletBase
28+
{
29+
/// <summary>
30+
/// Parameter set name for ShareName.
31+
/// </summary>
32+
public const string ShareNameCloseAllParameterSetName = "ShareNameCloseAll";
33+
34+
/// <summary>
35+
/// Parameter set name for directory.
36+
/// </summary>
37+
public const string DirectoryCloseAllParameterSetName = "DirectoryCloseAll";
38+
39+
/// <summary>
40+
/// Parameter set name for Share CloseAll.
41+
/// </summary>
42+
public const string ShareCloseAllParameterSetName = "ShareCloseAll";
43+
44+
/// <summary>
45+
/// Parameter set name for File CloseAll.
46+
/// </summary>
47+
public const string FileCloseAllParameterSetName = "FileCloseAll";
48+
49+
/// <summary>
50+
/// Parameter set name for ShareName.
51+
/// </summary>
52+
public const string ShareNameCloseSingleParameterSetName = "ShareNameCloseSingle";
53+
54+
///// <summary>
55+
///// Parameter set name for directory.
56+
///// </summary>
57+
//public const string DirectoryCloseSingleParameterSetName = "DirectoryCloseSingle";
58+
59+
/// <summary>
60+
/// Parameter set name for Share CloseAll.
61+
/// </summary>
62+
public const string ShareCloseSingleParameterSetName = "ShareCloseSingle";
63+
64+
///// <summary>
65+
///// Parameter set name for File CloseAll.
66+
///// </summary>
67+
//public const string FileCloseSingleParameterSetName = "FileCloseSingle";
68+
69+
70+
[Parameter(
71+
Position = 0,
72+
Mandatory = true,
73+
ParameterSetName = ShareNameCloseAllParameterSetName,
74+
HelpMessage = "Name of the file share which contains the files/directories to closed handle.")]
75+
[Parameter(
76+
Position = 0,
77+
Mandatory = true,
78+
ParameterSetName = ShareNameCloseSingleParameterSetName,
79+
HelpMessage = "Name of the file share which contains the files/directories to closed handle.")]
80+
[ValidateNotNullOrEmpty]
81+
public string ShareName { get; set; }
82+
83+
[Parameter(
84+
Position = 0,
85+
Mandatory = true,
86+
ValueFromPipeline = true,
87+
ParameterSetName = ShareCloseAllParameterSetName,
88+
HelpMessage = "CloudFileShare object indicated the share which contains the files/directories to closed handle.")]
89+
[Parameter(
90+
Position = 0,
91+
Mandatory = true,
92+
ValueFromPipeline = true,
93+
ParameterSetName = ShareCloseSingleParameterSetName,
94+
HelpMessage = "CloudFileShare object indicated the share which contains the files/directories to closed handle.")]
95+
[ValidateNotNull]
96+
public CloudFileShare Share { get; set; }
97+
98+
[Parameter(
99+
Position = 0,
100+
Mandatory = true,
101+
ValueFromPipeline = true,
102+
ParameterSetName = DirectoryCloseAllParameterSetName,
103+
HelpMessage = "CloudFileDirectory object indicated the base folder which contains the files/directories to closed handle.")]
104+
[ValidateNotNull]
105+
public CloudFileDirectory Directory { get; set; }
106+
107+
[Parameter(
108+
Position = 0,
109+
Mandatory = true,
110+
ValueFromPipeline = true,
111+
ParameterSetName = FileCloseAllParameterSetName,
112+
HelpMessage = "CloudFile object indicated the file to close handle.")]
113+
[ValidateNotNull]
114+
public CloudFile File { get; set; }
115+
116+
[Parameter(
117+
Position = 1,
118+
Mandatory = false,
119+
ParameterSetName = ShareNameCloseAllParameterSetName,
120+
HelpMessage = "Path to an existing file/directory.")]
121+
[Parameter(
122+
Position = 1,
123+
Mandatory = false,
124+
ParameterSetName = ShareCloseAllParameterSetName,
125+
HelpMessage = "Path to an existing file/directory.")]
126+
[Parameter(
127+
Position = 1,
128+
Mandatory = false,
129+
ParameterSetName = DirectoryCloseAllParameterSetName,
130+
HelpMessage = "Path to an existing file/directory.")]
131+
public string Path { get; set; }
132+
133+
[Parameter(Mandatory = true, ParameterSetName = ShareNameCloseSingleParameterSetName, ValueFromPipeline = true, HelpMessage = "The File Handle to close.")]
134+
[Parameter(Mandatory = true, ParameterSetName = ShareCloseSingleParameterSetName, ValueFromPipeline = true, HelpMessage = "The File Handle to close.")]
135+
[ValidateNotNull]
136+
public PSFileHandle FileHandle { get; set; }
137+
138+
[Parameter(Mandatory = false, ParameterSetName = ShareNameCloseAllParameterSetName, HelpMessage = "Closed handles Recursively. Only works on File Directory.")]
139+
[Parameter(Mandatory = false, ParameterSetName = ShareCloseAllParameterSetName, HelpMessage = "Closed handles Recursively. Only works on File Directory.")]
140+
[Parameter(Mandatory = false, ParameterSetName = DirectoryCloseAllParameterSetName, HelpMessage = "Closed handles Recursively. Only works on File Directory.")]
141+
public SwitchParameter Recursive { get; set; }
142+
143+
[Parameter(Mandatory = true, ParameterSetName = ShareNameCloseAllParameterSetName, HelpMessage = "Force close all File handles.")]
144+
[Parameter(Mandatory = true, ParameterSetName = ShareCloseAllParameterSetName, HelpMessage = "Force close all File handles.")]
145+
[Parameter(Mandatory = true, ParameterSetName = DirectoryCloseAllParameterSetName, HelpMessage = "Force close all File handles.")]
146+
[Parameter(Mandatory = true, ParameterSetName = FileCloseAllParameterSetName, HelpMessage = "Force close all File handles.")]
147+
public SwitchParameter CloseAll { get; set; }
148+
149+
[Parameter(
150+
ValueFromPipeline = true,
151+
ValueFromPipelineByPropertyName = true,
152+
ParameterSetName = ShareNameCloseSingleParameterSetName,
153+
HelpMessage = "Azure Storage Context Object")]
154+
[Parameter(
155+
ValueFromPipeline = true,
156+
ValueFromPipelineByPropertyName = true,
157+
ParameterSetName = ShareNameCloseAllParameterSetName,
158+
HelpMessage = "Azure Storage Context Object")]
159+
public override IStorageContext Context { get; set; }
160+
161+
[Parameter(Mandatory = false, HelpMessage = "Return whether the specified blob is successfully removed")]
162+
public SwitchParameter PassThru { get; set; }
163+
164+
[Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")]
165+
public virtual SwitchParameter AsJob { get; set; }
166+
167+
public override void ExecuteCmdlet()
168+
{
169+
if (this.ShouldProcess(string.Format("Close File Handles for File or FileDirectory on Path: {0}", Path), "This operation will force the provided file handle(s) closed, which may cause data loss or corruption for active applications/users.", null))
170+
{
171+
CloudFileDirectory baseDirectory = null;
172+
switch (this.ParameterSetName)
173+
{
174+
case DirectoryCloseAllParameterSetName:
175+
baseDirectory = this.Directory;
176+
break;
177+
178+
case ShareNameCloseSingleParameterSetName:
179+
case ShareNameCloseAllParameterSetName:
180+
baseDirectory = this.BuildFileShareObjectFromName(this.ShareName).GetRootDirectoryReference();
181+
break;
182+
183+
case ShareCloseSingleParameterSetName:
184+
case ShareCloseAllParameterSetName:
185+
baseDirectory = this.Share.GetRootDirectoryReference();
186+
break;
187+
case FileCloseAllParameterSetName:
188+
// Don't need to set baseDirectory when input is a CloudFile
189+
break;
190+
191+
default:
192+
throw new PSArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", this.ParameterSetName));
193+
}
194+
195+
if(ParameterSetName == ShareNameCloseSingleParameterSetName || ParameterSetName == ShareCloseSingleParameterSetName)
196+
{
197+
this.Path = FileHandle.Path;
198+
}
199+
200+
// When not input path/File, the list handle target must be a Dir
201+
bool foundAFolder = true;
202+
CloudFileDirectory targetDir = baseDirectory;
203+
CloudFile targetFile = null;
204+
if (this.File != null)
205+
{
206+
targetFile = this.File;
207+
foundAFolder = false;
208+
}
209+
else
210+
{
211+
if (!string.IsNullOrEmpty(this.Path))
212+
{
213+
string[] subfolders = NamingUtil.ValidatePath(this.Path);
214+
targetDir = baseDirectory.GetDirectoryReferenceByPath(subfolders);
215+
216+
if (!targetDir.Exists())
217+
{
218+
foundAFolder = false;
219+
}
220+
221+
if (!foundAFolder)
222+
{
223+
//Get file
224+
string[] filePath = NamingUtil.ValidatePath(this.Path, true);
225+
targetFile = baseDirectory.GetFileReferenceByPath(filePath);
226+
227+
this.Channel.FetchFileAttributesAsync(
228+
targetFile,
229+
null,
230+
this.RequestOptions,
231+
this.OperationContext,
232+
this.CmdletCancellationToken).ConfigureAwait(false);
233+
}
234+
}
235+
}
236+
237+
// Recursive only take effect on File Dir
238+
if (!foundAFolder && Recursive.IsPresent)
239+
{
240+
WriteWarning("The target object of the 'Path' is an Azure File, the parameter '-Recursive' won't take effect.");
241+
}
242+
243+
//Close handle
244+
CloseFileHandleResultSegment closeResult = null;
245+
FileContinuationToken continuationToken = null;
246+
int numHandlesClosed = 0;
247+
do
248+
{
249+
if (foundAFolder)
250+
{
251+
if (FileHandle != null)
252+
{
253+
// close single handle on fileDir
254+
if (this.FileHandle.HandleId == null)
255+
{
256+
throw new System.ArgumentException(string.Format("The HandleId of the FileHandle on path {0} should not be null.", this.FileHandle.Path), "FileHandle");
257+
}
258+
closeResult = targetDir.CloseHandleSegmented(this.FileHandle.HandleId.ToString(), continuationToken, Recursive, null, this.RequestOptions, this.OperationContext);
259+
}
260+
else
261+
{
262+
// close all handle on fileDir
263+
closeResult = targetDir.CloseAllHandlesSegmented(continuationToken, Recursive, null, this.RequestOptions, this.OperationContext);
264+
}
265+
}
266+
else
267+
{
268+
if (FileHandle != null)
269+
{
270+
// close single handle on file
271+
if (this.FileHandle.HandleId == null)
272+
{
273+
throw new System.ArgumentException(string.Format("The HandleId of the FileHandle on path {0} should not be null.", this.FileHandle.Path), "FileHandle");
274+
}
275+
closeResult = targetFile.CloseHandleSegmented(this.FileHandle.HandleId.ToString(), continuationToken, null, this.RequestOptions, this.OperationContext);
276+
}
277+
else
278+
{
279+
// close all handle on file
280+
closeResult = targetFile.CloseAllHandlesSegmented(continuationToken, null, this.RequestOptions, this.OperationContext);
281+
}
282+
}
283+
numHandlesClosed += closeResult.NumHandlesClosed;
284+
continuationToken = closeResult.ContinuationToken;
285+
} while (continuationToken != null && continuationToken.NextMarker != null);
286+
287+
if (PassThru)
288+
{
289+
WriteObject(numHandlesClosed);
290+
}
291+
}
292+
}
293+
}
294+
}

0 commit comments

Comments
 (0)