Skip to content

Commit 11c5d20

Browse files
authored
Merge pull request #2899 from zhencui/dev
Implement Add/Remove Cmdlets for VMSS diagnostics extension, add rela…
2 parents 308e601 + 43cbf18 commit 11c5d20

File tree

15 files changed

+5401
-3
lines changed

15 files changed

+5401
-3
lines changed

src/ResourceManager/Compute/Commands.Compute.Test/Commands.Compute.Test.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@
232232
<Content Include="ConfigFiles\DiagnosticsExtensionConfig.json">
233233
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
234234
</Content>
235+
<Content Include="ConfigFiles\DiagnosticsExtensionPrivateConfig.json">
236+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
237+
</Content>
238+
<Content Include="ConfigFiles\DiagnosticsExtensionPublicConfig.json">
239+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
240+
</Content>
235241
<None Include="ScenarioTests\AddVhdTests.ps1">
236242
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
237243
</None>
@@ -332,6 +338,9 @@
332338
<None Include="SessionRecords\Microsoft.Azure.Commands.Compute.Test.ScenarioTests.DiagnosticsExtensionTests\TestDiagnosticsExtensionSupportJsonConfig.json">
333339
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
334340
</None>
341+
<None Include="SessionRecords\Microsoft.Azure.Commands.Compute.Test.ScenarioTests.DiagnosticsExtensionTests\TestVmssDiagnosticsExtension.json">
342+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
343+
</None>
335344
<None Include="SessionRecords\Microsoft.Azure.Commands.Compute.Test.ScenarioTests.DscExtensionTests\TestGetAzureRmVMDscExtension.json">
336345
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
337346
</None>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"storageAccountName": "definedinconfigstorage"
3+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"WadCfg": {
3+
"DiagnosticMonitorConfiguration": {
4+
"DiagnosticInfrastructureLogs": {
5+
"scheduledTransferLogLevelFilter": "Error"
6+
},
7+
"Directories": {
8+
"IISLogs": {
9+
"containerName": "wad-iis-logfiles"
10+
},
11+
"FailedRequestLogs": {
12+
"containerName": "wad-failedrequestlogs"
13+
},
14+
"scheduledTransferPeriod": "PT1M"
15+
},
16+
"PerformanceCounters": {
17+
"PerformanceCounterConfiguration": [
18+
{
19+
"annotation": [ ],
20+
"counterSpecifier": "\\Memory\\Available MBytes",
21+
"sampleRate": "PT3M"
22+
},
23+
{
24+
"annotation": [ ],
25+
"counterSpecifier": "\\Web Service(_Total)\\ISAPI Extension Requests/sec",
26+
"sampleRate": "PT3M"
27+
},
28+
{
29+
"annotation": [ ],
30+
"counterSpecifier": "\\Web Service(_Total)\\Bytes Total/Sec",
31+
"sampleRate": "PT3M"
32+
},
33+
{
34+
"annotation": [ ],
35+
"counterSpecifier": "\\ASP.NET Applications(__Total__)\\Requests/Sec",
36+
"sampleRate": "PT3M"
37+
},
38+
{
39+
"annotation": [ ],
40+
"counterSpecifier": "\\ASP.NET Applications(__Total__)\\Errors Total/Sec",
41+
"sampleRate": "PT3M"
42+
},
43+
{
44+
"annotation": [ ],
45+
"counterSpecifier": "\\ASP.NET\\Requests Queued",
46+
"sampleRate": "PT3M"
47+
},
48+
{
49+
"annotation": [ ],
50+
"counterSpecifier": "\\ASP.NET\\Requests Rejected",
51+
"sampleRate": "PT3M"
52+
},
53+
{
54+
"annotation": [ ],
55+
"counterSpecifier": "\\Processor(_Total)\\% Processor Time",
56+
"sampleRate": "PT3M"
57+
}
58+
],
59+
"scheduledTransferPeriod": "PT1M"
60+
},
61+
"WindowsEventLog": {
62+
"DataSource": [
63+
{
64+
"name": "Application!*[System[(Level=1 or Level=2 or Level=3)]]"
65+
},
66+
{
67+
"name": "Windows Azure!*[System[(Level=1 or Level=2 or Level=3 or Level=4)]]"
68+
}
69+
],
70+
"scheduledTransferPeriod": "PT1M"
71+
},
72+
"CrashDumps": {
73+
"CrashDumpConfiguration": [
74+
{
75+
"processName": "WaIISHost.exe"
76+
},
77+
{
78+
"processName": "WaWorkerHost.exe"
79+
},
80+
{
81+
"processName": "w3wp.exe"
82+
}
83+
]
84+
},
85+
"Logs": {
86+
"scheduledTransferLogLevelFilter": "Error",
87+
"scheduledTransferPeriod": "PT1M"
88+
},
89+
"Metrics": {
90+
"resourceId": "dummy",
91+
"MetricAggregation": [
92+
{
93+
"scheduledTransferPeriod": "PT1M"
94+
}
95+
]
96+
},
97+
"overallQuotaInMB": 4096
98+
}
99+
},
100+
"StorageAccount": "definedinconfigstorage"
101+
}

src/ResourceManager/Compute/Commands.Compute.Test/ScenarioTests/ComputeTestCommon.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,3 +576,8 @@ function Get-SubscriptionIdFromResourceGroup
576576
$last = $rgid.IndexOf('/', $first + 1);
577577
return $rgid.Substring($first + 1, $last - $first - 1);
578578
}
579+
580+
function Get-ComputeVmssLocation
581+
{
582+
Get-ResourceProviderLocation "Microsoft.Compute/virtualMachineScaleSets"
583+
}

src/ResourceManager/Compute/Commands.Compute.Test/ScenarioTests/DiagnosticsExtensionTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,12 @@ public void TestDiagnosticsExtensionSupportJsonConfig()
5151
{
5252
ComputeTestController.NewInstance.RunPsTest("Test-DiagnosticsExtensionSupportJsonConfig");
5353
}
54+
55+
[Fact]
56+
[Trait(Category.AcceptanceType, Category.CheckIn)]
57+
public void TestVmssDiagnosticsExtension()
58+
{
59+
ComputeTestController.NewInstance.RunPsTest("Test-VmssDiagnosticsExtension");
60+
}
5461
}
5562
}

src/ResourceManager/Compute/Commands.Compute.Test/ScenarioTests/DiagnosticsExtensionTests.ps1

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,114 @@ function Test-DiagnosticsExtensionSupportJsonConfig
183183
# Cleanup
184184
Clean-ResourceGroup $rgname
185185
}
186-
}
186+
}
187+
188+
<#
189+
.SYNOPSIS
190+
Test the basic usage of the Add/Remove VMSS diagnostics extension command
191+
#>
192+
function Test-VmssDiagnosticsExtension
193+
{
194+
$rgname = Get-ComputeTestResourceName
195+
$loc = Get-ComputeVmssLocation
196+
197+
try
198+
{
199+
# Common
200+
New-AzureRMResourceGroup -Name $rgname -Location $loc -Force;
201+
202+
# Create VMSS
203+
204+
# SRP
205+
$stoname = 'sto' + $rgname;
206+
$stotype = 'Standard_GRS';
207+
New-AzureRMStorageAccount -ResourceGroupName $rgname -Name $stoname -Location $loc -Type $stotype;
208+
$stoaccount = Get-AzureRMStorageAccount -ResourceGroupName $rgname -Name $stoname;
209+
210+
# NRP
211+
$subnet = New-AzureRMVirtualNetworkSubnetConfig -Name ('subnet' + $rgname) -AddressPrefix "10.0.0.0/24";
212+
$vnet = New-AzureRMVirtualNetwork -Force -Name ('vnet' + $rgname) -ResourceGroupName $rgname -Location $loc -AddressPrefix "10.0.0.0/16" -Subnet $subnet;
213+
$vnet = Get-AzureRMVirtualNetwork -Name ('vnet' + $rgname) -ResourceGroupName $rgname;
214+
$subnetId = $vnet.Subnets[0].Id;
215+
216+
# New VMSS Parameters
217+
$vmssName = 'vmss' + $rgname;
218+
$vmssType = 'Microsoft.Compute/virtualMachineScaleSets';
219+
220+
$adminUsername = 'Foo12';
221+
$adminPassword = "BaR@123" + $rgname;
222+
223+
$imgRef = Get-DefaultCRPImage -loc $loc;
224+
$vhdContainer = "https://" + $stoname + ".blob.core.windows.net/" + $vmssName;
225+
226+
$extname = 'diagextest';
227+
$diagExtPublisher = 'Microsoft.Azure.Diagnostics';
228+
$diagExtType = 'IaaSDiagnostics';
229+
230+
# This storage name will be used in command line directly when set diagnostics extension
231+
$storagename = 'definedinconfigstorage';
232+
$storagetype = 'Standard_GRS';
233+
New-AzureRmStorageAccount -ResourceGroupName $rgname -Name $storagename -Location $loc -Type $storagetype;
234+
235+
$ipCfg = New-AzureRmVmssIPConfig -Name 'test' -SubnetId $subnetId;
236+
$vmss = New-AzureRmVmssConfig -Location $loc -SkuCapacity 2 -SkuName 'Standard_A0' -UpgradePolicyMode 'automatic' -NetworkInterfaceConfiguration $netCfg `
237+
| Add-AzureRmVmssNetworkInterfaceConfiguration -Name 'test' -Primary $true -IPConfiguration $ipCfg `
238+
| Set-AzureRmVmssOSProfile -ComputerNamePrefix 'test' -AdminUsername $adminUsername -AdminPassword $adminPassword `
239+
| Set-AzureRmVmssStorageProfile -Name 'test' -OsDiskCreateOption 'FromImage' -OsDiskCaching 'None' `
240+
-ImageReferenceOffer $imgRef.Offer -ImageReferenceSku $imgRef.Skus -ImageReferenceVersion $imgRef.Version `
241+
-ImageReferencePublisher $imgRef.PublisherName -VhdContainer $vhdContainer;
242+
243+
# Full parameter test
244+
$version = '1.5';
245+
$publicSettingFilePath = "$TestOutputRoot\ConfigFiles\DiagnosticsExtensionPublicConfig.json";
246+
$privateSettingFilePath = "$TestOutputRoot\ConfigFiles\DiagnosticsExtensionPrivateConfig.json";
247+
$vmss = Add-AzureRmVmssDiagnosticsExtension -VirtualMachineScaleSet $vmss -Name $extname -SettingFilePath $publicSettingFilePath `
248+
-ProtectedSettingFilePath $privateSettingFilePath -TypeHandlerVersion $version -AutoUpgradeMinorVersion $false -Force;
249+
250+
$vmssDiagExtensions = $vmss.VirtualMachineProfile.ExtensionProfile.Extensions | Where-Object {$_.Publisher -eq $diagExtPublisher -and $_.Type -eq $diagExtType};
251+
Assert-AreEqual 1 $vmssDiagExtensions.Count;
252+
$vmssDiagExtension = $vmssDiagExtensions | Select-Object -first 1;
253+
254+
Assert-AreEqual $extname $vmssDiagExtension.Name;
255+
Assert-AreEqual $version $vmssDiagExtension.TypeHandlerVersion;
256+
Assert-AreEqual $false $vmssDiagExtension.AutoUpgradeMinorVersion;
257+
258+
# Storage account key should be filled up by the cmdlet.
259+
$storageAccountKey = $vmssDiagExtension.ProtectedSettings['storageAccountKey'];
260+
Assert-NotNull $storageAccountKey;
261+
Assert-AreNotEqual '' $storageAccountKey;
262+
263+
# Remove without specifying extension name, diagnostic extension is expected to be removed.
264+
$vmss = Remove-AzureRmVmssDiagnosticsExtension -VirtualMachineScaleSet $vmss;
265+
$vmssDiagExtensions = $vmss.VirtualMachineProfile.ExtensionProfile.Extensions | Where-Object {$_.Publisher -eq $diagExtPublisher -and $_.Type -eq $diagExtType};
266+
267+
Assert-Null $vmssDiagExtensions;
268+
269+
$vmss = $vmss | Add-AzureRmVmssDiagnosticsExtension -Name $extname -SettingFilePath $publicSettingFilePath `
270+
| New-AzureRmVmss -ResourceGroupName $rgname -Name $vmssName;
271+
272+
$vmss = Get-AzureRmVmss -ResourceGroupName $rgname -VMScaleSetName $vmssName;
273+
274+
$vmssDiagExtensions = $vmss.VirtualMachineProfile.ExtensionProfile.Extensions | Where-Object {$_.Publisher -eq $diagExtPublisher -and $_.Type -eq $diagExtType};
275+
Assert-AreEqual 1 $vmssDiagExtensions.Count;
276+
277+
$vmssDiagExtension = $vmssDiagExtensions | Select-Object -first 1;
278+
Assert-AreEqual $extname $vmssDiagExtension.Name;
279+
280+
$settings = $vmssDiagExtension.Settings;
281+
Assert-AreEqual $storagename $settings.storageAccount.Value;
282+
283+
Remove-AzureRmVmssDiagnosticsExtension -VirtualMachineScaleSet $vmss -Name $extname;
284+
Update-AzureRmVmss -ResourceGroupName $rgname -Name $vmssName -VirtualMachineScaleSet $vmss;
285+
286+
$vmss = Get-AzureRmVmss -ResourceGroupName $rgname -VMScaleSetName $vmssName;
287+
$vmssDiagExtensions = $vmss.VirtualMachineProfile.ExtensionProfile.Extensions | Where-Object {$_.Publisher -eq $diagExtPublisher -and $_.Type -eq $diagExtType};
288+
289+
Assert-Null $vmssDiagExtensions;
290+
}
291+
finally
292+
{
293+
# Cleanup
294+
Clean-ResourceGroup $rgname
295+
}
296+
}

src/ResourceManager/Compute/Commands.Compute.Test/SessionRecords/Microsoft.Azure.Commands.Compute.Test.ScenarioTests.DiagnosticsExtensionTests/TestVmssDiagnosticsExtension.json

Lines changed: 4737 additions & 0 deletions
Large diffs are not rendered by default.

src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,11 @@
247247
<Compile Include="Extension\CustomScript\RemoveAzureVMCustomScriptExtensionCommand.cs" />
248248
<Compile Include="Extension\CustomScript\SetAzureVMCustomScriptExtensionCommand.cs" />
249249
<Compile Include="Extension\CustomScript\VirtualMachineCustomScriptExtensionContext.cs" />
250+
<Compile Include="Extension\Diagnostics\AddAzureRmVmssDiagnosticsExtension.cs" />
250251
<Compile Include="Extension\Diagnostics\DiagnosticsExtensionConstants.cs" />
251252
<Compile Include="Extension\Diagnostics\GetAzureRmVMDiagnosticsExtension.cs" />
252253
<Compile Include="Extension\Diagnostics\RemoveAzureRmVMDiagnosticsExtension.cs" />
254+
<Compile Include="Extension\Diagnostics\RemoveAzureRmVmssDiagnosticsExtension.cs" />
253255
<Compile Include="Extension\Diagnostics\SetAzureRmVMDiagnosticsExtension.cs" />
254256
<Compile Include="Extension\Diagnostics\WADPrivateConfigSchema.cs" />
255257
<Compile Include="Extension\DSC\GetAzureVMDscExtensionStatusCommand.cs" />

src/ResourceManager/Compute/Commands.Compute/Common/ConstantStringTypes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ public static class ProfileNouns
116116
public const string VaultSecretGroup = "AzureRmVMSecret";
117117
public const string RemoteDesktopFile = "AzureRmRemoteDesktopFile";
118118

119+
public const string VirtualMachineScaleSetDiagnosticsExtension = "AzureRmVmssDiagnosticsExtension";
120+
119121
//DSC
120122
public const string VirtualMachineDscExtension = "AzureRmVMDscExtension";
121123
public const string VirtualMachineDscConfiguration = "AzureRmVMDscConfiguration";

src/ResourceManager/Compute/Commands.Compute/Common/DiagnosticsHelper.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using Microsoft.Azure.Commands.Common.Authentication.Models;
1616
using Microsoft.Azure.Commands.Management.Storage.Models;
17+
using Microsoft.Azure.Management.Compute.Models;
1718
using Microsoft.Azure.Management.Storage;
1819
using Microsoft.Azure.Management.Storage.Models;
1920
using Microsoft.WindowsAzure.Commands.Common.Storage;
@@ -195,8 +196,15 @@ private static Hashtable GetPublicConfigFromJsonFile(string configurationPath, s
195196
var wadCfgProperty = properties.FirstOrDefault(p => p.Equals(WadCfg, StringComparison.OrdinalIgnoreCase));
196197
var wadCfgBlobProperty = properties.FirstOrDefault(p => p.Equals(WadCfgBlob, StringComparison.OrdinalIgnoreCase));
197198
var xmlCfgProperty = properties.FirstOrDefault(p => p.Equals(EncodedXmlCfg, StringComparison.OrdinalIgnoreCase));
199+
var storageAccountProperty = properties.FirstOrDefault(p => p.Equals(StorageAccount, StringComparison.OrdinalIgnoreCase));
198200

199201
var hashTable = new Hashtable();
202+
203+
if (string.IsNullOrEmpty(storageAccountName) && storageAccountProperty != null)
204+
{
205+
storageAccountName = (string)publicConfig[storageAccountProperty];
206+
}
207+
200208
hashTable.Add(StorageAccount, storageAccountName);
201209

202210
if (wadCfgProperty != null && publicConfig[wadCfgProperty] is JObject)
@@ -578,5 +586,79 @@ private static string GetEndpointFromStorageContext(AzureStorageContext context)
578586
var scheme = context.BlobEndPoint.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ? "https://" : "http://";
579587
return scheme + context.EndPointSuffix;
580588
}
589+
590+
/// <summary>
591+
/// Parse public and private configurations, and automatically resolve storage key for private configuration.
592+
/// </summary>
593+
/// <param name="publicConfigPath">Public configuration file path</param>
594+
/// <param name="privateConfigPath">Private configuration file path, can be empty</param>
595+
/// <param name="storageClient">Storage client</param>
596+
/// <returns>A tuple with public configuration as first element and private configuration as second element</returns>
597+
public static Tuple<Hashtable, Hashtable> GetConfigurationsFromFiles(string publicConfigPath, string privateConfigPath, string resourceId, Cmdlet cmdlet, IStorageManagementClient storageClient)
598+
{
599+
var publicConfig = GetPublicConfigFromJsonFile(publicConfigPath, null, resourceId, cmdlet);
600+
var privateConfig = string.IsNullOrEmpty(privateConfigPath) ? new Hashtable() :
601+
JsonConvert.DeserializeObject<Hashtable>(File.ReadAllText(privateConfigPath));
602+
603+
// Resolve storage account name
604+
// Storage account name must be provided in public config
605+
var storageAccountName = publicConfig[StorageAccount] as string;
606+
if (string.IsNullOrEmpty(storageAccountName))
607+
{
608+
throw new ArgumentException(Properties.Resources.DiagnosticsExtensionNullStorageAccountName);
609+
}
610+
611+
privateConfig[StorageAccountNameTag] = storageAccountName;
612+
613+
// Resolve storage account key
614+
var storageAccountKey = InitializeStorageAccountKey(storageClient, storageAccountName, privateConfigPath);
615+
if (string.IsNullOrEmpty(storageAccountKey))
616+
{
617+
storageAccountKey = privateConfig[StorageAccountKeyTag] as string;
618+
619+
if (string.IsNullOrEmpty(storageAccountKey))
620+
{
621+
// Throw exception if no storage key provided in private config and cannot retrieve it from server
622+
throw new ArgumentException(Properties.Resources.DiagnosticsExtensionNullStorageAccountKey);
623+
}
624+
}
625+
else
626+
{
627+
// If storage key can be retrieved, use that one.
628+
privateConfig[StorageAccountKeyTag] = storageAccountKey;
629+
}
630+
631+
632+
// Resolve storage account endpoint
633+
var storageAccountEndpoint = InitializeStorageAccountEndpoint(storageAccountName, storageAccountKey, storageClient);
634+
if (string.IsNullOrEmpty(storageAccountEndpoint))
635+
{
636+
storageAccountEndpoint = privateConfig[StorageAccountEndPointTag] as string;
637+
638+
if (string.IsNullOrEmpty(storageAccountEndpoint))
639+
{
640+
// Throw exception if no storage endpoint provided in private config and cannot retrieve it from server
641+
throw new ArgumentNullException(Properties.Resources.DiagnosticsExtensionNullStorageAccountEndpoint);
642+
}
643+
}
644+
else
645+
{
646+
// If storage account endpoint can be retrieved, use that one.
647+
privateConfig[StorageAccountEndPointTag] = storageAccountEndpoint;
648+
}
649+
650+
return new Tuple<Hashtable, Hashtable>(publicConfig, privateConfig);
651+
}
652+
653+
/// <summary>
654+
/// Check if a VMSS extension is diagnostics extension.
655+
/// </summary>
656+
/// <param name="extension">VMSS extension</param>
657+
/// <returns>Whether the VMSS extension is diagnostics extension</returns>
658+
public static bool IsDiagnosticsExtension(VirtualMachineScaleSetExtension extension)
659+
{
660+
return extension.Publisher.Equals(DiagnosticsExtensionConstants.ExtensionPublisher, StringComparison.InvariantCultureIgnoreCase) &&
661+
extension.Type.Equals(DiagnosticsExtensionConstants.ExtensionType, StringComparison.InvariantCultureIgnoreCase);
662+
}
581663
}
582664
}

0 commit comments

Comments
 (0)