@@ -229,6 +229,16 @@ func (a *AzureInfoer) getPricingWithRetailPricesAPI() (map[string]map[string]typ
229229 baseURL := "https://prices.azure.com/api/retail/prices?$filter=" + url .QueryEscape (filter )
230230 // ex: https://prices.azure.com/api/retail/prices?$filter=serviceName%20eq%20%27Virtual%20Machines%27%20and%20priceType%20eq%20%27Consumption%27%20and%20armSkuName%20eq%20%27Standard_D8as_v5%27%20and%20armRegionName%20eq%20%27australiaeast%27
231231
232+ startTime := time .Now ()
233+ a .log .Info ("RetailPricesAPI: starting fetch" )
234+
235+ pageCount := 0
236+ totalItems := 0
237+ totalSkippedWindows := 0
238+ totalSkippedNonVM := 0
239+ totalOnDemandSet := 0
240+ totalSpotSet := 0
241+
232242 url := baseURL
233243 for url != "" {
234244 client := & http.Client {}
@@ -237,12 +247,23 @@ func (a *AzureInfoer) getPricingWithRetailPricesAPI() (map[string]map[string]typ
237247
238248 resp , err := client .Do (req )
239249 if err != nil {
250+ a .log .Error ("RetailPricesAPI: request failed" , map [string ]interface {}{
251+ "page" : pageCount ,
252+ "elapsedMins" : time .Since (startTime ).Minutes (),
253+ "error" : err .Error (),
254+ })
240255 return allPrices , fmt .Errorf ("failed to call retail API: %w" , err )
241256 }
242257 defer resp .Body .Close ()
243258
244259 if resp .StatusCode != http .StatusOK {
245260 body , _ := io .ReadAll (resp .Body )
261+ a .log .Error ("RetailPricesAPI: non-200 status" , map [string ]interface {}{
262+ "statusCode" : resp .StatusCode ,
263+ "page" : pageCount ,
264+ "elapsedMins" : time .Since (startTime ).Minutes (),
265+ "body" : string (body ),
266+ })
246267 return allPrices , fmt .Errorf ("unexpected status code %d: %s" , resp .StatusCode , string (body ))
247268 }
248269
@@ -256,14 +277,19 @@ func (a *AzureInfoer) getPricingWithRetailPricesAPI() (map[string]map[string]typ
256277 return nil , fmt .Errorf ("failed to unmarshal retail API response: %w" , err )
257278 }
258279
280+ pageCount ++
281+ totalItems += len (data .Items )
282+
259283 for _ , item := range data .Items {
260284 // Ignoring the windows pricing similar to below
261285 if strings .Contains (strings .ToLower (item .ProductName ), "windows" ) {
286+ totalSkippedWindows ++
262287 continue
263288 }
264289
265290 // Ignoring the product if it do not contain virtual machines
266291 if ! strings .Contains (strings .ToLower (item .ProductName ), "virtual machines" ) {
292+ totalSkippedNonVM ++
267293 continue
268294 }
269295
@@ -278,10 +304,12 @@ func (a *AzureInfoer) getPricingWithRetailPricesAPI() (map[string]map[string]typ
278304 spotPrice := make (types.SpotPriceInfo )
279305 spotPrice [region ] = item .Price
280306 price .SpotPrice = spotPrice
307+ totalSpotSet ++
281308 } else if strings .Contains (strings .ToLower (item .MeterName ), "low priority" ) {
282309 // ignore this as this is old spot type pricing
283310 } else {
284311 price .OnDemandPrice = item .Price
312+ totalOnDemandSet ++
285313 }
286314
287315 allPrices [region ][item.ArmSkuName ] = price
@@ -290,6 +318,24 @@ func (a *AzureInfoer) getPricingWithRetailPricesAPI() (map[string]map[string]typ
290318 url = data .NextPageURL
291319 }
292320
321+ // Count unique VMs per region for summary
322+ totalUniqueVMs := 0
323+ for _ , regionPrices := range allPrices {
324+ totalUniqueVMs += len (regionPrices )
325+ }
326+
327+ a .log .Info ("RetailPricesAPI: fetch complete" , map [string ]interface {}{
328+ "totalPages" : pageCount ,
329+ "totalItems" : totalItems ,
330+ "skippedWindows" : totalSkippedWindows ,
331+ "skippedNonVM" : totalSkippedNonVM ,
332+ "onDemandPricesSet" : totalOnDemandSet ,
333+ "spotPricesSet" : totalSpotSet ,
334+ "regionsWithPrices" : len (allPrices ),
335+ "totalUniqueVMPrices" : totalUniqueVMs ,
336+ "elapsedMins" : time .Since (startTime ).Minutes (),
337+ })
338+
293339 return allPrices , nil
294340}
295341
@@ -302,6 +348,17 @@ func (a *AzureInfoer) Initialize() (map[string]map[string]types.Price, error) {
302348 allPrices = make (map [string ]map [string ]types.Price )
303349 }
304350
351+ // Count prices after Retail API
352+ retailVMCount := 0
353+ retailRegionCount := len (allPrices )
354+ for _ , regionPrices := range allPrices {
355+ retailVMCount += len (regionPrices )
356+ }
357+ a .log .Info ("Initialize: after RetailPricesAPI" , map [string ]interface {}{
358+ "retailRegions" : retailRegionCount ,
359+ "retailVMPrices" : retailVMCount ,
360+ })
361+
305362 regions , err := a .GetRegions ("compute" )
306363 if err != nil {
307364 return nil , err
@@ -310,64 +367,127 @@ func (a *AzureInfoer) Initialize() (map[string]map[string]types.Price, error) {
310367 rateCardFilter := "OfferDurableId eq 'MS-AZR-0003p' and Currency eq 'USD' and Locale eq 'en-US' and RegionInfo eq 'US'"
311368 // ResourceRateCardInfo is a huge object, it takes around 3-5 minutes to fetch this
312369 startTime := time .Now ()
313- a .log .Info ("Fetching Azure ResourceRateCardInfo " )
370+ a .log .Info ("RateCardAPI: starting fetch " )
314371 result , err := a .rateCardClient .Get (context .TODO (), rateCardFilter )
315- a .log .Info ("Fetched Azure ResourceRateCardInfo " , map [string ]interface {}{"minutesTaken " : time .Since (startTime ).Minutes ()})
372+ a .log .Info ("RateCardAPI: fetch complete " , map [string ]interface {}{"elapsedMins " : time .Since (startTime ).Minutes ()})
316373 if err != nil {
374+ a .log .Error ("RateCardAPI: fetch failed" , map [string ]interface {}{"error" : err .Error ()})
317375 return nil , err
318376 }
319377
378+ totalMeters := len (* result .Meters )
379+ vmMeters := 0
380+ skippedWindows := 0
381+ skippedNoRegion := 0
382+ skippedTags := 0
383+ rateCardOnDemandSet := 0
384+ rateCardOnDemandSkippedRetailExists := 0
385+ rateCardSpotSet := 0
386+ rateCardVariantsSet := 0
387+
320388 var missingRegions []string
321389 for _ , v := range * result .Meters {
322- if * v .MeterCategory == "Virtual Machines" && len (* v .MeterTags ) == 0 && * v .MeterRegion != "" {
323- if ! strings .Contains (* v .MeterSubCategory , "Windows" ) {
324- region , err := a .toRegionID (* v .MeterRegion , regions )
325- if err != nil {
326- missingRegions = appendIfMissing (missingRegions , * v .MeterRegion )
327- continue
328- }
390+ if * v .MeterCategory != "Virtual Machines" {
391+ continue
392+ }
393+ if len (* v .MeterTags ) != 0 {
394+ skippedTags ++
395+ continue
396+ }
397+ if * v .MeterRegion == "" {
398+ skippedNoRegion ++
399+ continue
400+ }
401+ if strings .Contains (* v .MeterSubCategory , "Windows" ) {
402+ skippedWindows ++
403+ continue
404+ }
329405
330- instanceTypes := a .machineType (* v .MeterName , * v .MeterSubCategory )
406+ vmMeters ++
407+ region , err := a .toRegionID (* v .MeterRegion , regions )
408+ if err != nil {
409+ missingRegions = appendIfMissing (missingRegions , * v .MeterRegion )
410+ continue
411+ }
331412
332- var priceInUsd float64
413+ instanceTypes := a . machineType ( * v . MeterName , * v . MeterSubCategory )
333414
334- if len (v .MeterRates ) < 1 {
335- a .log .Debug ("missing rate info" , map [string ]interface {}{"MeterSubCategory" : * v .MeterSubCategory , "region" : region })
336- continue
337- }
338- for _ , rate := range v .MeterRates {
339- priceInUsd += * rate
415+ var priceInUsd float64
416+
417+ if len (v .MeterRates ) < 1 {
418+ a .log .Warn ("missing rate info" , map [string ]interface {}{"MeterSubCategory" : * v .MeterSubCategory , "region" : region })
419+ continue
420+ }
421+ for _ , rate := range v .MeterRates {
422+ priceInUsd += * rate
423+ }
424+ if allPrices [region ] == nil {
425+ allPrices [region ] = make (map [string ]types.Price )
426+ }
427+ for _ , instanceType := range instanceTypes {
428+ price := allPrices [region ][instanceType ]
429+ if ! strings .Contains (* v .MeterName , "Low Priority" ) {
430+ if price .OnDemandPrice == 0 {
431+ price .OnDemandPrice = priceInUsd
432+ rateCardOnDemandSet ++
433+ } else {
434+ rateCardOnDemandSkippedRetailExists ++
340435 }
341- if allPrices [region ] == nil {
342- allPrices [region ] = make (map [string ]types.Price )
436+ } else {
437+ if price .SpotPrice == nil {
438+ spotPrice := make (types.SpotPriceInfo )
439+ spotPrice [region ] = priceInUsd
440+ price .SpotPrice = spotPrice
441+ metrics .ReportAzureSpotPrice (region , instanceType , priceInUsd )
442+ rateCardSpotSet ++
343443 }
344- for _ , instanceType := range instanceTypes {
345- price := allPrices [region ][instanceType ]
346- if ! strings .Contains (* v .MeterName , "Low Priority" ) {
347- if price .OnDemandPrice == 0 {
348- price .OnDemandPrice = priceInUsd
349- }
350- } else {
351- if price .SpotPrice == nil {
352- spotPrice := make (types.SpotPriceInfo )
353- spotPrice [region ] = priceInUsd
354- price .SpotPrice = spotPrice
355- metrics .ReportAzureSpotPrice (region , instanceType , priceInUsd )
356- }
357- }
444+ }
358445
359- allPrices [region ][instanceType ] = price
446+ allPrices [region ][instanceType ] = price
360447
361- mts := a .getMachineTypeVariants (instanceType )
362- for _ , mt := range mts {
363- allPrices [region ][mt ] = price
364- }
365- }
448+ mts := a .getMachineTypeVariants (instanceType )
449+ for _ , mt := range mts {
450+ allPrices [region ][mt ] = price
451+ rateCardVariantsSet ++
366452 }
367453 }
368454 }
369455 a .log .Debug ("couldn't find regions" , map [string ]interface {}{"missingRegions" : missingRegions })
370456
457+ a .log .Info ("RateCardAPI: processing complete" , map [string ]interface {}{
458+ "totalMeters" : totalMeters ,
459+ "vmMeters" : vmMeters ,
460+ "skippedWindows" : skippedWindows ,
461+ "skippedNoRegion" : skippedNoRegion ,
462+ "skippedTags" : skippedTags ,
463+ "missingRegions" : len (missingRegions ),
464+ "rateCardOnDemandSet" : rateCardOnDemandSet ,
465+ "rateCardOnDemandSkippedRetail" : rateCardOnDemandSkippedRetailExists ,
466+ "rateCardSpotSet" : rateCardSpotSet ,
467+ "rateCardVariantsSet" : rateCardVariantsSet ,
468+ })
469+
470+ // Final summary after merging both APIs
471+ finalVMCount := 0
472+ finalRegionCount := len (allPrices )
473+ zeroOnDemandCount := 0
474+ for _ , regionPrices := range allPrices {
475+ for _ , p := range regionPrices {
476+ finalVMCount ++
477+ if p .OnDemandPrice == 0 {
478+ zeroOnDemandCount ++
479+ }
480+ }
481+ }
482+ a .log .Info ("Initialize: final merged prices" , map [string ]interface {}{
483+ "finalRegions" : finalRegionCount ,
484+ "finalVMPrices" : finalVMCount ,
485+ "zeroOnDemandVMs" : zeroOnDemandCount ,
486+ "retailContrib" : retailVMCount ,
487+ "rateCardContrib" : rateCardOnDemandSet ,
488+ "variantsContrib" : rateCardVariantsSet ,
489+ })
490+
371491 a .log .Debug ("finished initializing price info" )
372492 return allPrices , nil
373493}
@@ -524,8 +644,25 @@ func (a *AzureInfoer) GetProducts(vms []types.VMInfo, service, regionId string)
524644 return nil , emperror .Wrap (err , "failed to get products" )
525645 }
526646 }
647+
527648 switch service {
528649 case svcAks :
650+ dasCount := 0
651+ dasSample := make ([]string , 0 )
652+ for _ , vm := range vmList {
653+ if strings .Contains (strings .ToLower (vm .Type ), "as_v" ) {
654+ dasCount ++
655+ if len (dasSample ) < 10 {
656+ dasSample = append (dasSample , vm .Type )
657+ }
658+ }
659+ }
660+ a .log .Info ("GetProducts for AKS - VM summary" , map [string ]interface {}{
661+ "region" : regionId ,
662+ "totalVMs" : len (vmList ),
663+ "dasFamilyCount" : dasCount ,
664+ "dasSample" : fmt .Sprintf ("%v" , dasSample ),
665+ })
529666 return vmList , nil
530667 case "compute" :
531668 return vmList , nil
0 commit comments