1- using System . Threading . Tasks ;
1+ using System . Text ;
2+ using System . Threading . Tasks ;
23using Azure ;
4+ using Azure . Core ;
35using Azure . ResourceManager . Compute ;
46using Azure . ResourceManager . Compute . Models ;
5- using Newtonsoft . Json ;
67
78namespace Microsoft . OneFuzz . Service ;
89
@@ -153,7 +154,7 @@ public async System.Threading.Tasks.Task DeleteVm(string name) {
153154
154155
155156 public async Task < OneFuzzResult < bool > > AddExtensions ( Vm vm , Dictionary < string , VirtualMachineExtensionData > extensions ) {
156- var status = new List < string > ( ) ;
157+ var statuses = new List < ( string extName , string state ) > ( ) ;
157158 var toCreate = new List < KeyValuePair < string , VirtualMachineExtensionData > > ( ) ;
158159 foreach ( var extensionConfig in extensions ) {
159160 var extensionName = extensionConfig . Key ;
@@ -164,7 +165,7 @@ public async Task<OneFuzzResult<bool>> AddExtensions(Vm vm, Dictionary<string, V
164165 _logTracer . Info (
165166 $ "vm extension state: { vm . Name : Tag:VmName} - { extensionName : Tag:ExtensionName} - { extension . ProvisioningState : Tag:ExtensionProvisioningState} "
166167 ) ;
167- status . Add ( extension . ProvisioningState ) ;
168+ statuses . Add ( ( extensionName , extension . ProvisioningState ) ) ;
168169 } else {
169170 toCreate . Add ( extensionConfig ) ;
170171 }
@@ -175,19 +176,15 @@ public async Task<OneFuzzResult<bool>> AddExtensions(Vm vm, Dictionary<string, V
175176 await CreateExtension ( vm . Name , config . Key , config . Value ) ;
176177 }
177178 } else {
178- if ( status . All ( s => string . Equals ( s , "Succeeded" , StringComparison . Ordinal ) ) ) {
179- return OneFuzzResult < bool > . Ok ( true ) ;
180- } else if ( status . Any ( s => string . Equals ( s , "Failed" , StringComparison . Ordinal ) ) ) {
181- return OneFuzzResult < bool > . Error (
182- ErrorCode . VM_CREATE_FAILED ,
183- "failed to launch extension"
184- ) ;
185- } else if ( ! ( status . Contains ( "Creating" ) || status . Contains ( "Updating" ) ) ) {
186- _logTracer . Error ( $ "vm agent - unknown state { vm . Name : Tag:VmName} : { JsonConvert . SerializeObject ( status ) : Tag:Status} ") ;
179+ if ( statuses . All ( s => s . state == "Succeeded" ) ) {
180+ return OneFuzzResult . Ok ( true ) ;
181+ } else if ( statuses . Any ( s => s . state == "Failed" ) ) {
182+ var errors = await GetExtensionErrors ( vm . Name , statuses . Where ( s => s . state == "Failed" ) . Select ( s => s . extName ) ) ;
183+ return OneFuzzResult < bool > . Error ( ErrorCode . VM_CREATE_FAILED , "failed to launch extension(s): " + errors ) ;
187184 }
188185 }
189186
190- return OneFuzzResult < bool > . Ok ( false ) ;
187+ return OneFuzzResult . Ok ( false ) ;
191188 }
192189
193190 public async Task < OneFuzzResultVoid > Create ( Vm vm ) {
@@ -223,16 +220,21 @@ public async Task<OneFuzzResultVoid> Create(Vm vm) {
223220 }
224221 }
225222
223+ private ResourceIdentifier GetVirtualMachineIdentifier ( string vmName )
224+ => VirtualMachineResource . CreateResourceIdentifier (
225+ _context . Creds . GetSubscription ( ) ,
226+ _context . Creds . GetBaseResourceGroup ( ) ,
227+ vmName ) ;
228+
226229 public async Async . Task CreateExtension ( string vmName , string extensionName , VirtualMachineExtensionData extension ) {
227230 _logTracer . Info ( $ "creating extension: { _context . Creds . GetBaseResourceGroup ( ) : Tag:ResourceGroup} - { vmName : Tag:VmName} - { extensionName : Tag:ExtensionName} ") ;
228- var vm = await _context . Creds . GetResourceGroupResource ( ) . GetVirtualMachineAsync ( vmName ) ;
229231
232+ var vm = _context . Creds . ArmClient . GetVirtualMachineResource ( GetVirtualMachineIdentifier ( vmName ) ) ;
230233 try {
231- _ = await vm . Value . GetVirtualMachineExtensions ( ) . CreateOrUpdateAsync (
234+ _ = await vm . GetVirtualMachineExtensions ( ) . CreateOrUpdateAsync (
232235 WaitUntil . Started ,
233236 extensionName ,
234- extension
235- ) ;
237+ extension ) ;
236238 } catch ( RequestFailedException ex ) when
237239 ( ex . Status == 409 &&
238240 ( ex . Message . Contains ( "VM is marked for deletion" ) || ex . Message . Contains ( "The request failed due to conflict with a concurrent request." ) ) ) {
@@ -241,6 +243,27 @@ public async Async.Task CreateExtension(string vmName, string extensionName, Vir
241243 return ;
242244 }
243245
246+ public async Async . Task < string > GetExtensionErrors ( string vmName , IEnumerable < string > extensionNames ) {
247+ var vmResource = _context . Creds . ArmClient . GetVirtualMachineResource ( GetVirtualMachineIdentifier ( vmName ) ) ;
248+ var vmData = await vmResource . GetAsync ( InstanceViewTypes . InstanceView ) ;
249+
250+ var result = new StringBuilder ( ) ;
251+ foreach ( var extensionName in extensionNames ) {
252+ result . Append ( $ "Errors for extension '{ extensionName } ':") ;
253+ var extensionData = vmData . Value . Data . InstanceView . Extensions . FirstOrDefault ( ext => ext . Name == extensionName ) ;
254+ if ( extensionData is null ) {
255+ result . Append ( $ " (cannot get errors - extension was not found on target VM)\n ") ;
256+ } else {
257+ result . Append ( '\n ' ) ;
258+ foreach ( var status in extensionData . Statuses ) {
259+ result . Append ( $ "{ status . Time } :{ status . Level } : { status . Code } ({ status . DisplayStatus } ) - { status . Message } \n ") ;
260+ }
261+ }
262+ }
263+
264+ return result . ToString ( ) ;
265+ }
266+
244267 async Task < OneFuzzResultVoid > CreateVm (
245268 string name ,
246269 Region location ,
0 commit comments