VMware Cloud Community
VM_
Contributor
Contributor
Jump to solution

Scripts run taking too long

Hi,

I'm hoping for some help with improving/reducing the amount of time it take for a script to run. I have provided the following script to our CMDB group but they are saying it take too long to run (whole day).

I am already using the get-view option to extract the details. Maybe someone can advise how they would improve this script. For the record we have 2000+ vm's in our environment.

Script is attached.

Any help is much appreciated.

0 Kudos
1 Solution

Accepted Solutions
mattboren
Expert
Expert
Jump to solution

Hello, VM_-

Well, nobody likes a slow script, that's for sure.  And, a whole day script?  Double-boo for that.

I had a look, and there were a few spots for improvement.  I have made them here:

Add-PSSnapin VMware.VimAutomation.Core

function Get-VMSerial {
 
## rewrote to take UUID; already being gotten in properties below
  # param([VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VirtualMachine)
  # $s = ($VirtualMachine.ExtensionData.Config.Uuid).Replace("-", "")
  param([string]$Uuid)
 
$s = $Uuid.Replace("-", "")
 
$Uuid = "VMware-"
 
for ($i = 0; $i -lt $s.Length; $i += 2)
  {
   
$Uuid += ("{0:x2}" -f [byte]("0x" + $s.Substring($i, 2)))
   
if ($Uuid.Length -eq 30) { $Uuid += "-" } else { $Uuid += " " }
  }

 
return $Uuid.TrimEnd()
}

# Connect to vcenter
Connect-VIServer vcenter

#Gathering VM settings
Write-Verbose -Verbose "Gathering VM statistics"
$VMReport = @()
$Count = 0

## no need to use Get-VM
#
Get-VM | % {
#
# moved outside of the Foreach-Object scriptblock, so this Get-View only gets called once, not 2000+ times (not once per VM, just once at all)
$filter = @{"Config.Template"="false"}
Get-View -ViewType VirtualMachine -Filter $filter -Property Name,Guest.HostName,summary.config.numcpu,summary.config.memorysizemb,summary.config.numEthernetCards,Summary.Config.NumVirtualDisks,Config.Uuid,Parent,Guest.GuestFamily,config.tools.toolsversion,guest.toolsstatus,config.Version,Config.ChangeTrackingEnabled,Datastore,AvailableField,Value | %{
   
## $vm is the View of the current VM
    $vm = $_
   
## not used -- removed it
    # $CustomDetails = Get-VM $_ | Select -ExpandProperty customfields
    ## rewrote to use Get-View or UpdateViewData()
    # $LunTierStringArray = Get-VM $_ | Get-Datastore
    $LunTierStringArray = Get-View -Property Name -Id $vm.Datastore
   
$LunTierString = $LunTierStringArray.Name -split "_"
   
$vms = "" | Select-Object VMName, Cluster, DnsName, TotalCPU, TotalMemory, TotalNics, Disks, DiskTier, SDF, UUID, Folder, OS, ToolsVersion, ToolsStatus, HardwareVersion, CBT, Serial
   
$vms.VMName = $vm.Name
   
## rewrote to use UpdateViewData()
    # $vms.Cluster = $(Get-vm $_ | Get-cluster).Name
    $vms.Cluster = &{$vm.UpdateViewData("Runtime.Host.Parent.Name"); $vm.Runtime.LinkedView.Host.LinkedView.Parent.Name}
   
$vms.DnsName = $vm.Guest.HostName
   
$vms.TotalCPU = $vm.summary.config.numcpu
   
$vms.TotalMemory = $vm.summary.config.memorysizemb
   
$vms.TotalNics = $vm.summary.config.numEthernetCards
   
$vms.Disks = $vm.Summary.Config.NumVirtualDisks
   
$vms.DiskTier = $LunTierString[0]
   
## getting this in some other way, from the
    # $vms.SDF = ($_ | Get-Annotation -CustomAttribute 'School/Division/Faculty').Value
    $vms.SDF = & {$intCustomAttributeKey = ($vm.AvailableField | ?{$_.Name -eq "School/Division/Faculty"}).Key; ($vm.Value | ?{$_.Key -eq $intCustomAttributeKey}).Value}
   
$vms.UUID = $vm.Config.Uuid
   
## add -Property Name, though, with " | Out-Null", does this even work?
    # $current = Get-View $vm.Parent | Out-Null
    $current = Get-View $vm.Parent -Property Name,Parent -ErrorAction:SilentlyContinue
   
$path = $vm.Name
   
do {
        
$parent = $current
        
if($parent.Name -ne "vm"){$path =  $parent.Name + "\" + $path}
        
## add -Property Name, though, with " | Out-Null", does this even work?
         $current = if ($null -ne $current.Parent) {Get-View $current.Parent -Property Name,Parent -ErrorAction:SilentlyContinue}
    }
while ($current.Parent -ne $null)
   
$vms.Folder = $path
   
$vms.OS = $vm.Guest.GuestFamily
   
$vms.ToolsVersion = $vm.config.tools.toolsversion
   
$vms.ToolsStatus = $vm.guest.toolsstatus
   
$vms.HardwareVersion = $vm.config.Version
   
$vms.CBT = $vm.Config.ChangeTrackingEnabled
   
$vms.Serial = Get-VMSerial -Uuid $vm.Config.Uuid
   
$VMReport += $vms
   
$Count++
   
## added Write-Verbose so that the pipeline does not get polluted with strings (for the day that this code is returning objects for further manipulation down the pipeline, instead of going straight to CSV)
  Write-Verbose -Verbose $Count
}
#Output
$VMReport | Export-Csv vcenter_vm_report.csv_tmp -NoTypeInformation -UseCulture
Copy-Item vcenter_vm_report.csv_tmp vcenter_vm_report.csv

I commented about the changes that I made (and why), and commented out the code that I replaced.  Give that a run, and see if it is any quicker for you / your CMDB crew.

Message was edited by Matt Boren on 24 Feb 2015: minor correction to the piece that gets the VM's inventory path (may have only returned one parent level previously)

View solution in original post

0 Kudos
3 Replies
mattboren
Expert
Expert
Jump to solution

Hello, VM_-

Well, nobody likes a slow script, that's for sure.  And, a whole day script?  Double-boo for that.

I had a look, and there were a few spots for improvement.  I have made them here:

Add-PSSnapin VMware.VimAutomation.Core

function Get-VMSerial {
 
## rewrote to take UUID; already being gotten in properties below
  # param([VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VirtualMachine)
  # $s = ($VirtualMachine.ExtensionData.Config.Uuid).Replace("-", "")
  param([string]$Uuid)
 
$s = $Uuid.Replace("-", "")
 
$Uuid = "VMware-"
 
for ($i = 0; $i -lt $s.Length; $i += 2)
  {
   
$Uuid += ("{0:x2}" -f [byte]("0x" + $s.Substring($i, 2)))
   
if ($Uuid.Length -eq 30) { $Uuid += "-" } else { $Uuid += " " }
  }

 
return $Uuid.TrimEnd()
}

# Connect to vcenter
Connect-VIServer vcenter

#Gathering VM settings
Write-Verbose -Verbose "Gathering VM statistics"
$VMReport = @()
$Count = 0

## no need to use Get-VM
#
Get-VM | % {
#
# moved outside of the Foreach-Object scriptblock, so this Get-View only gets called once, not 2000+ times (not once per VM, just once at all)
$filter = @{"Config.Template"="false"}
Get-View -ViewType VirtualMachine -Filter $filter -Property Name,Guest.HostName,summary.config.numcpu,summary.config.memorysizemb,summary.config.numEthernetCards,Summary.Config.NumVirtualDisks,Config.Uuid,Parent,Guest.GuestFamily,config.tools.toolsversion,guest.toolsstatus,config.Version,Config.ChangeTrackingEnabled,Datastore,AvailableField,Value | %{
   
## $vm is the View of the current VM
    $vm = $_
   
## not used -- removed it
    # $CustomDetails = Get-VM $_ | Select -ExpandProperty customfields
    ## rewrote to use Get-View or UpdateViewData()
    # $LunTierStringArray = Get-VM $_ | Get-Datastore
    $LunTierStringArray = Get-View -Property Name -Id $vm.Datastore
   
$LunTierString = $LunTierStringArray.Name -split "_"
   
$vms = "" | Select-Object VMName, Cluster, DnsName, TotalCPU, TotalMemory, TotalNics, Disks, DiskTier, SDF, UUID, Folder, OS, ToolsVersion, ToolsStatus, HardwareVersion, CBT, Serial
   
$vms.VMName = $vm.Name
   
## rewrote to use UpdateViewData()
    # $vms.Cluster = $(Get-vm $_ | Get-cluster).Name
    $vms.Cluster = &{$vm.UpdateViewData("Runtime.Host.Parent.Name"); $vm.Runtime.LinkedView.Host.LinkedView.Parent.Name}
   
$vms.DnsName = $vm.Guest.HostName
   
$vms.TotalCPU = $vm.summary.config.numcpu
   
$vms.TotalMemory = $vm.summary.config.memorysizemb
   
$vms.TotalNics = $vm.summary.config.numEthernetCards
   
$vms.Disks = $vm.Summary.Config.NumVirtualDisks
   
$vms.DiskTier = $LunTierString[0]
   
## getting this in some other way, from the
    # $vms.SDF = ($_ | Get-Annotation -CustomAttribute 'School/Division/Faculty').Value
    $vms.SDF = & {$intCustomAttributeKey = ($vm.AvailableField | ?{$_.Name -eq "School/Division/Faculty"}).Key; ($vm.Value | ?{$_.Key -eq $intCustomAttributeKey}).Value}
   
$vms.UUID = $vm.Config.Uuid
   
## add -Property Name, though, with " | Out-Null", does this even work?
    # $current = Get-View $vm.Parent | Out-Null
    $current = Get-View $vm.Parent -Property Name,Parent -ErrorAction:SilentlyContinue
   
$path = $vm.Name
   
do {
        
$parent = $current
        
if($parent.Name -ne "vm"){$path =  $parent.Name + "\" + $path}
        
## add -Property Name, though, with " | Out-Null", does this even work?
         $current = if ($null -ne $current.Parent) {Get-View $current.Parent -Property Name,Parent -ErrorAction:SilentlyContinue}
    }
while ($current.Parent -ne $null)
   
$vms.Folder = $path
   
$vms.OS = $vm.Guest.GuestFamily
   
$vms.ToolsVersion = $vm.config.tools.toolsversion
   
$vms.ToolsStatus = $vm.guest.toolsstatus
   
$vms.HardwareVersion = $vm.config.Version
   
$vms.CBT = $vm.Config.ChangeTrackingEnabled
   
$vms.Serial = Get-VMSerial -Uuid $vm.Config.Uuid
   
$VMReport += $vms
   
$Count++
   
## added Write-Verbose so that the pipeline does not get polluted with strings (for the day that this code is returning objects for further manipulation down the pipeline, instead of going straight to CSV)
  Write-Verbose -Verbose $Count
}
#Output
$VMReport | Export-Csv vcenter_vm_report.csv_tmp -NoTypeInformation -UseCulture
Copy-Item vcenter_vm_report.csv_tmp vcenter_vm_report.csv

I commented about the changes that I made (and why), and commented out the code that I replaced.  Give that a run, and see if it is any quicker for you / your CMDB crew.

Message was edited by Matt Boren on 24 Feb 2015: minor correction to the piece that gets the VM's inventory path (may have only returned one parent level previously)

0 Kudos
VM_
Contributor
Contributor
Jump to solution

Thanks mattboren.

I have tested and it completed in 35mins. That's a great improvement. Excellent comments too.

0 Kudos
mattboren
Expert
Expert
Jump to solution

You are welcome, glad to help. Enjoy!  Minor update:  there was a property missing for the Get-View calls that get the VM's inventory path. I updated the code above with what should correct that.

0 Kudos