PropertyCollector.RetrieveContents Failed

Si vous êtes comme nous, et que vous avez une utilisation massive des templates vSphere, et si vous être comme dans notre cas encore en vSphere 4.1 (pas encore migré sur vSphere 5 = todolist :p), vous avez pu rencontrer le problème suivant sur l’édition de paramètre d’un template (après convertion VM > Template) :

On se prend une erreur qui nous dis clairement qu’elle est pas contente :

Call « PropertyCollector.RetrieveContents » for object « propertyCollector » on vCenter Server « vcenter » failed.

VMware a publiée une KB à ce sujet le 2 aôut 2011 : KB1025367

Voici en détail le workaround :

  1. Power off the virtual machine.
  2. Note the ESX/ESXi host on which the virtual machine resides.
  3. Right-click the virtual machine and click Remove from Inventory.
  4. Browse the datastore and locate the virtual machine.
  5. Right-click the <vm name>.vmx file and click Add to Inventory.
  6. If you are unable to add the virtual machine, close the vSphere Client from vCenter Server.
  7. Log in directly to the ESX/ESXi host on which the virtual machine resides using the vSphere Client. To do this, you must know the root password.
  8. Browse the datastore and locate the virtual machine.
  9. Right-click the <vm name>.vmx file and click Add to Inventory.
  10. Log in to vCenter Server using the vSphere Client.

On peut le faire une fois, mais à un moment ça saoule, surtout que faire un unregister/register de la VM fait perdre toute la partie Custom Field^^

Donc on a fait un petit script PowerCLI (évidemment) pour faire le boulot à notre place !

En se basant sur les excellents scripts de @LucD22 http://www.lucd.info/2009/12/02/raiders-of-the-lost-vmx/ et de @hypervisor_fr http://www.hypervisor.fr/?p=2927, le script va sauvegarder toutes les informations de la VM qui seront perdues avec le Unregister (Custom Field, VM Folder, etc…), puis va effectuer un Unregister/Register pour finalement réinscrire les informations précédemment sauvegardées.

Function Fix-VMPropCol {
	PARAM (
		[switch]$IsVM = $false,
		[switch]$IsTemplate = $false,
		[Parameter(ValueFromPipeline=$true)]
		[string]$VM = $null
	)

	Function Get-Usage {
		Write-Host "Incorrect Parameters" -ForegroundColor red
		Write-Host "Fix-VMPropCol [-IsVM / -IsTemplate]"
		Write-Host "IsVM		: input object is virtual machine"
		Write-Host "IsTemplate	: input object is template"
	}

	Function Log-Console([string]$log) {
		[string]$logText = (Get-Date -Format "yyyy.MM.dd HH:mm:ss") + " - " + $log
		Write-Host -ForegroundColor green $logText
	}

	# Hiding Write-Progress bar of Set-VM cmdlet
	$ProgressPreference = "SilentlyContinue"

	if (($IsVM -and $IsTemplate) -or ((-not $IsVM) -and (-not $IsTemplate))) {
		Get-Usage
		Break
	}

	Log-Console("Gathering informations")
	$VM_View = Get-View -ViewType VirtualMachine -Filter @{"Name"="^$VM$"}
	$exp_VMName = $VM_View.Name
	$exp_VMHost = Get-View $VM_View.Runtime.Host
	$exp_VMXPath = $VM_View.Config.Files.vmPathName
	$exp_Folder = Get-View $VM_View.parent
	$tab_VM = "" | Select VMName
	$tab_VM.VMName = $exp_VMName

	# Backup informations
	# Backup custom fields
	Log-Console("Gathering Custom Fields")
	if ($VM_View.Summary.CustomValue) {
		foreach ($vmField in $VM_View.Summary.CustomValue) {
			$tab_VM | Add-Member -MemberType NoteProperty -Name ($VM_View.AvailableField | ?{$vmField.Key -eq $_.Key}).name -Value $vmField.value
		}
	}

	# Backup Notes
	Log-Console("Gathering Notes")
	if ($VM_View.Config.Annotation.length -gt 0) {
		$tab_VM | Add-Member -MemberType NoteProperty -Name "Annotation" -Value $VM_View.Config.Annotation
	}

	# Unregister VirtualMachine
	Log-Console("Unregister VirtualMachine")
	if (($tab_VM | Get-Member -MemberType NoteProperty | Measure-Object).count -gt "1") {
		if ($IsTemplate) {
			Get-Template -Name $VM | Remove-Template -DeletePermanently:$false -Confirm:$false
		} elseif ($IsVM) {
			Get-VM -Name $VM | Remove-VM -DeletePermanently:$false -Confirm:$false
		}
	}

	# Register VirtualMachine
	Log-Console("Register VirtualMachine")
	#$params = @($exp_VMXPath,$null,$true,$null,$exp_VMHost.MoRef)
	$params = @($exp_VMXPath,$null,(!$IsTemplate -eq $null),(get-view $exp_VMHost.Parent).ResourcePool,$exp_VMHost.MoRef)
	$taskMoRef = $exp_Folder.GetType().GetMethod("RegisterVM_Task").Invoke($exp_Folder, $params)

	# Waiting just a little bit to be sure object is here ...
	Log-Console("Waiting for task to finalize")
	While ( (Get-Task | ?{$_.id -match $taskMoRef.value}).state -match "Running") { Sleep 4 }

	# Convert into VirtualMachine
	if ($IsTemplate) {
		Log-Console("Convert Template to Virtual Machine in order to set up back Custom Fields and Notes")
		Set-Template -Template $VM -ToVM | Out-Null
	}

	# Reconfigure Custom fields
	Log-Console("Set up back Custom Fields and Notes")
	Foreach ($VMField in ($tab_VM | Get-Member -MemberType NoteProperty)) {
		if ($VMField.name -match "^Annotation$") {
			Set-VM -VM $VM -Description $tab_VM.Annotation -Confirm:$false | Out-Null
		} elseif ($VMField.name -notmatch "^VMName$") {
			Set-Annotation -Entity $VM -CustomAttribute $VMField.name -Value $tab_VM."$($VMField.name)" -Confirm:$false | Out-Null
		}
	}

	# Convert back into template
	if ($IsTemplate) {
		Log-Console("Convert back to Template")
		Set-VM -VM $VM -ToTemplate -Confirm:$false | Out-Null
	}
}

Cette fonction est à utiliser directement après un pipe d’un Get-VM ou d’un Get-Template et prend comme seul argument un switch afin de déterminer quel type d’objet sera utilisé (-IsVM pour une machine virtuelle et -IsTemplate pour un template) :

Get-VM -Name VM | Fix-VMPropCol -IsVM
Get-Template -Name TEMPLATE | Fix-VMPropCol -IsTemplate

Dans les évolutions prévues, il y aura la détection de l’InputObject afin d’éviter le besoin du switch -IsVM ou -IsTemplate ainsi que permettre de le lancer directement sans pipe.

4 comments

  1. Super pratique par contre je remplacerai
    la ligne 65 par $params = @($exp_VMXPath,$null,(!$IsTemplate -eq $null),(get-view $exp_VMHost.Parent).ResourcePool,$exp_VMHost.MoRef)
    la ligne 70 par While ((Get-Task|?{$_.id -match $taskMoRef.value}).state -match « Running »){Sleep 8}
    Comme ca, le asTemplate correspond à IsTemplate et le script ne continu que quand le RegisterVM est fini 🙂

  2. et la ligne 30 $VM_View = Get-View -ViewType VirtualMachine -Filter @{« Name »= »^$VM$ »}
    Pour eviter les erreurs avec les vm ayant une partie du nom en commun

  3. Pingback: Error while editing VM settings in vSphere | archy.net

Laisser un commentaire

Required fields are marked *.