VMware Cloud Community
sddunne
Contributor
Contributor
Jump to solution

How to script adding .VMX's from a Datastore (NFS) to Virtual Center

Hi Guys,

I'm hoping someone with a much better understanding of scripting than I will know of a simple way of doing this?

Basically, here is the scenario...

We use NFS Datastores running on Netapp Filers to house all of our Virtual Machines. This normally works great however we have had a few incidents recently with the filer which has meant us having to switch to our snapmirrored backups. Basically how this works is that each datastore, say for example Datastore1 normally runs on Filer1. This datastore is regulary 'snapmirrored' across to Filer2. this snapmirror is a readonly copy of the datastore. If we have a problem with Filer1, it is possible to reconfigure Filer2 to make the backup snapmirror writeable, effectively allowing us to see the vm's again.

Currently, if this happens, in order to get the Vm's back we need to remove all the vm's from Virtual Center Inventory, then add the snapmirrored datastore to all the hosts in Virtual Center, then Browse the datastore and manually add back all the vm's individually by doing the usual right click/add to inventory on each vm.

As i say we recently had an issue and had to invoke this for 10 datastores housing almost 250 vm's so as you can imagine was quite a slow and labour intensive task!!!

What i've been trying to work out is how i could script this in Power CLI? I've seen LuCD's excellent looking add .vmx script and looks very similar to what i'd like to do but can't work out how to modify it for my purposes.

What id' ideally love to get to would be a script that i can specify a Virtual Center instance, Datastore and Cluster as variables (say vcname$ = "ukvir0001" , cluster$ = "Datacentre1" and dsname$ = "datastore1") and then have the script read the datastore and add any .vmx's it finds to the specified Cluster.

I've been reading with interest the new Datastore Provider functionality in PowerCLI4 and that sounds like something that may provide an easy way of doing it i just can't work out how!

Any help would be very much appreciated as i have the daunting task of having to move almost 250 vm's back over to Filer1 now that the filer has been repaired and don't have the luxury of a several hour maintenance window to do it manually!

Many thanks,

Sean.

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

After some PMs between Sean and myself, we came up with a working version.

Note that this latest version of the script was tested in a vSphere environment against an ESXi v4 server.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
33 Replies
LucD
Leadership
Leadership
Jump to solution

Hi Sean,

I have adapted my earlier script a bit to able to work with the parameters you specified.

The function Register-Vmx can be called with the 3 parameters (VCname, ClusterName, DSName) as you preferred.


function Register-Vmx{
	param($vcname, $cluster ,$dsname)

	Connect-VIServer -Server $vcname
	$datacenter = (Get-View (Get-View -Id (Get-Cluster $cluster).parentid).parent).Name
	$ESXname = Get-Cluster $cluster | Get-VMHost | select -First 1

	$folder = Get-View (Get-Datacenter -Name $datacenter | Get-Folder -Name "vm").ID
	$pool = Get-View (Get-Cluster -Name $cluster | Get-ResourcePool -Name "Resources").ID

	$esxImpl = Get-VMHost -Name $ESXname
	$esx = Get-View $esxImpl.ID 
	$dsBrowser = Get-View $esx.DatastoreBrowser
	foreach($dsImpl in $dsBrowser.Datastore){
		$ds = Get-View $dsImpl
		$vms = @()
		foreach($vmImpl in $ds.Vm){
			$vm = Get-View $vmImpl
			$vms += $vm.Config.Files.VmPathName
		}
		$datastorepath = ""

		$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
		$searchSpec.matchpattern = "*.vmx"

		$taskMoRef = $dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec) 
		$task = Get-View $taskMoRef 
		while ($task.Info.State -eq "running"){$task = Get-View $taskMoRef}

		foreach ($file in $task.info.Result){
			$found = $FALSE
			foreach($vmx in $vms){
				if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
					$found = $TRUE
				}
			}
			if (-not $found -and $task.Info.Result[0].File -ne $null){
				$vmx = $file.FolderPath + $file.File[0].Path
				$params = @($vmx,$null,$FALSE,$pool.MoRef,$null)
				$folder.GetType().GetMethod("RegisterVM_Task").Invoke($folder, $params)

			}
		}
	}
	Disconnect-VIServer -Server $vcname
}

# Sample function call
Register-Vmx "MyVCName" "MyClusterName" "MyDatastoreName"

The RegisterVM_Task doesn't contain an ESX server pointer. It's DRS that will decide to which ESX server in the cluster the guest will be registered.

This should guarantee an good spread of the guests over the different ESX servers in the cluster.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
sddunne
Contributor
Contributor
Jump to solution

Hi LucD,

Thanks for the very speedy reply!!

I just tried the script using the following ;

Register-Vmx "ukvir09100" "IOC Management Cluster" "test"

The Datastore is called test!

And got the following error;

C:\Powershell\TestScripts> .\AddAllVMX.ps1

Name Port User

-


-


-


ukvir09100 443 UK\a-sdunne

Get-Cluster : 03/06/2009 13:21:12 Get-Cluster Cluster with name 'its'

not found, using the specified filter(s).

At C:\Powershell\TestScripts\AddAllVMX.ps1:5 char:52

+ $datacenter = (Get-View (Get-View -Id (Get-Cluster <<<< its).parentid).p

arent).Name

Get-View : The argument cannot be null or empty.

At C:\Powershell\TestScripts\AddAllVMX.ps1:5 char:39

+ $datacenter = (Get-View (Get-View -Id <<<< (Get-Cluster its).parentid).p

arent).Name

Get-Datacenter : The argument cannot be null or empty.

At C:\Powershell\TestScripts\AddAllVMX.ps1:8 char:42

+ $folder = Get-View (Get-Datacenter -Name <<<< $datacenter | Get-Folder -

Name "vm").ID

C:\Powershell\TestScripts>

The other thing i forgot to add to the mix is that the way Netapp NFS Datastores work is that they also produce a .snapshot folder in every datastore that holds all the snapshot backups for that datastore, i'm guessing the script would also need to exclude that folder somehow to stop all the backup snapshots from being added?

Thanks again for your help with this, lifesaving work here!! Smiley Happy

Sean.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

There was an error in the attached script.

It's corrected now. Sorry about that.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
sddunne
Contributor
Contributor
Jump to solution

Hi LucD,

I can't see the revised script, did you attach it?

Many thanks!

Sean.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Yes, I replaced the faulty script in my first reply.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
sddunne
Contributor
Contributor
Jump to solution

Hi LucD,

Doh! Can see it now! Just tried it again and getting;

C:\Powershell\TestScripts> .\AddAllVMX.ps1

Name Port User

-


-


-


ukvir09100 443 UK\a-sdunne

Get-View : The argument cannot be null or empty.

At C:\Powershell\TestScripts\AddAllVMX.ps1:5 char:39

+ $datacenter = (Get-View (Get-View -Id <<<< (Get-Cluster $cluster).parent

id).parent).Name

Get-Datacenter : The argument cannot be null or empty.

At C:\Powershell\TestScripts\AddAllVMX.ps1:8 char:42

+ $folder = Get-View (Get-Datacenter -Name <<<< $datacenter | Get-Folder -

Name "vm").ID

C:\Powershell\TestScripts>

C:\Powershell\TestScripts>

The script is far to complex for my skills (i can just about work my way through piping cmdlets into each other Smiley Happy ), any ideas? Also any idea on excluding the .snapshot folder? I was thinking if excluding a folder is hard it might possibly be easier to set the folder depth to 1? I.e. the .snapshot folder looks like this;

.snapshot

>>> hourly.0

>>> hourly.1

>>> nightly.0

>>> VM1

>>> VM1.vmx

VM2

>>> VM2.vmx

If you see what i mean?

Thanks again!

Sean.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I think I found the problem. A cluster is not always located directly under a datacenter.

The method to get at the datacenter's name has been changed.

Can you try this version?

function Register-Vmx{
	param($vcname, $cluster ,$dsname)

	$VCserver = Connect-VIServer -Server $vcname
	$datacenter = (Get-Datacenter -VMHost (Get-Cluster $cluster | Get-VMHost | select -First 1)).Name
	$ESXname = Get-Cluster $cluster | Get-VMHost | select -First 1

	$folder = Get-View (Get-Datacenter -Name $datacenter | Get-Folder -Name "vm").ID
	$pool = Get-View (Get-Cluster -Name $cluster | Get-ResourcePool -Name "Resources").ID

	$esxImpl = Get-VMHost -Name $ESXname
	$esx = Get-View $esxImpl.ID 
	$dsBrowser = Get-View $esx.DatastoreBrowser
	foreach($dsImpl in $dsBrowser.Datastore){
		$ds = Get-View $dsImpl
		if($ds.Summary.Name -ne $dsname){continue}
		$vms = @()
		foreach($vmImpl in $ds.Vm){
			$vm = Get-View $vmImpl
			$vms += $vm.Config.Files.VmPathName
		}
		$datastorepath = ""

		$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
		$searchSpec.matchpattern = "*.vmx"

		$task = Get-View ($dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec))
		while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){
			$task.UpdateViewData("Info.State")
			sleep 5
		}
		foreach ($file in $task.info.Result){
			$found = $FALSE
			foreach($vmx in $vms){
				if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
					$found = $TRUE
				}
			}
			if (-not $found -and $task.Info.Result[0].File -ne $null){
				$vmx = $file.FolderPath + $file.File[0].Path
				$params = @($vmx,$null,$FALSE,$pool.MoRef,$null)
				$folder.GetType().GetMethod("RegisterVM_Task").Invoke($folder, $params)

			}
		}
	}
	Disconnect-VIServer -Server $VCserver -Confirm:$false
}

# Sample function call
Register-Vmx "ukvir09100" "IOC Management Cluster" "test"

Note: new attachment on this reply.

Are you sure the .Snapshot folder is visible to the datastore ?

Can you check with the Datastore Browser in the VIC.

Could perhaps include a screenshot of the Datastore Browser so I can see which folders should be used to get at the .vmx files ?

If the .Snapshot folder is visible it could be excluded while handling the results.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
sddunne
Contributor
Contributor
Jump to solution

Morning LucD,

Alas its not working still, coming up with;

PS C:\> .\addallvmxtest.ps1

Get-Datacenter : A parameter cannot be found that matches parameter name 'VMHos

t'.

At C:\addallvmxtest.ps1:5 char:39

+ $datacenter = (Get-Datacenter -VMHost <<<< (Get-Cluster $cluster | Get-V

MHost | select -First 1)).Name

Get-Datacenter : The argument cannot be null or empty.

At C:\addallvmxtest.ps1:8 char:42

+ $folder = Get-View (Get-Datacenter -Name <<<< $datacenter | Get-Folder -

Name "vm").ID

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

Exception calling "UpdateViewData" with "1" argument(s): "Object reference not

set to an instance of an object."

At C:\addallvmxtest.ps1:29 char:24

+ $task.UpdateViewData( <<<< "Info.State")

PS C:\>

I've attached a couple of screenshots showing the .snapshot folder in the Datastore;

Thanks again for your help with this, must be becoming a bit of a pain now!

Cheers,

Sean.

Reply
0 Kudos
jahallen01
Contributor
Contributor
Jump to solution

We have the same requirements as the original poster. The only difference is that we have two different datastores for each VM. Your original script works great however when we failover the NetApp and run the script it registers the first volume fine (because its on the same volume as the vmx) however the second datastore stays the same.

vmx is on vmprod-nj so it registers and sets the datastore to vmprod1-nj (which contains the VM's C and D drive)

The E drive is held on vmdata-nj so when we run the script everything works fine, when we failover the system:

vmprod-nj ---&gt; vmprod_nj_dr

vmdata-nj ---&gt; vmdata_nj_dr

Now when we run the script the VM registers the vmx fine on vmprod_nj_dr however the E drive is still registered as vmdata-nj instead of vmdata_nj_dr.

Is there a way to change the datastore setting for the E drive on each VM?

We change the script so that it looks for a single datastore during DR because we really only want to look for the mirrored volume and not all of the other datastores attached to the ESX host (we have a lot of NFS mounts)

$folder = Get-View (Get-Datacenter -Name "North America" | Get-Folder -Name "dr").ID

$pool = Get-View (Get-Folder "NYC" | Get-Cluster -Name "Servers" | Get-ResourcePool -Name "Production").ID

$esxImpl = Get-VMHost -Name ny-esxprd001.comp.com

$esx = Get-View $esxImpl.ID

$dsBrowser = Get-View $esx.DatastoreBrowser

foreach($dsImpl in $dsBrowser.Datastore){

$ds = Get-View $dsImpl

$vms = @()

foreach($vmImpl in $ds.Vm){

$vm = Get-View $vmImpl

$vms += $vm.Config.Files.VmPathName

}

$datastorerr = ""

if ($datastorerr -eq "[vmprod_nj_dr]"){

$datastorepath = $datastorerr

$datastorepath

$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec

$searchSpec.matchpattern = "*.vmx"

$taskMoRef = $dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec)

$task = Get-View $taskMoRef

while ($task.Info.State -eq "running"){$task = Get-View $taskMoRef}

foreach ($file in $task.info.Result){

$found = $FALSE

foreach($vmx in $vms){

if(($file.FolderPath + $file.File[0].Path) -eq $vmx){

$found = $TRUE

}

}

if (-not $found -and $task.Info.Result[0].File -ne $null){

$vmx = $file.FolderPath + $file.File[0].Path

$params = @($vmx,$null,$FALSE,$pool.MoRef,$null)

$folder.GetType().GetMethod("RegisterVM_Task").Invoke($folder, $params)

}

}}

}

*

Disconnect-VIServer -Confirm:$False

*

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

After some PMs between Sean and myself, we came up with a working version.

Note that this latest version of the script was tested in a vSphere environment against an ESXi v4 server.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
sddunne
Contributor
Contributor
Jump to solution

This works excellently and has saved our Bacon! Thanks again LucD!!!

Reply
0 Kudos
admin
Immortal
Immortal
Jump to solution

Mmmmmm, bacon.

Reply
0 Kudos
bulletprooffool
Champion
Champion
Jump to solution

I'll be reviving this thread . .

I intend to use it as the second half of a VM failover script that I intend to try write.

Our scenario is 2 clusters in different physical datacentres (same VCentre and datacentre in VC though) that have NetApps replicating the vmdks etc.

The intention is to be able to release the i[ps on vms, shut them down and remove friom inventory on the source cluster, manually do the netapp failover, then re-add them to the inventory on the remote end.

One day I will virtualise myself . . .
Reply
0 Kudos
Daune_Mattoon
Contributor
Contributor
Jump to solution

I have an issue where 2 out of 3 of my esx hosts will not return newly mounted NFS stores to the databrowser object. Anyone else have this problem?

Reply
0 Kudos
admin
Immortal
Immortal
Jump to solution

For the missing NFS, you might try running Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs

=====

Carter Shanklin

Read the PowerCLI Blog
[Follow me on Twitter|http://twitter.com/cshanklin]

Reply
0 Kudos
Daune_Mattoon
Contributor
Contributor
Jump to solution

no luck with that.

Reply
0 Kudos
bulletprooffool
Champion
Champion
Jump to solution

Thanks LucD

Your script was the last piece in the puzzle for the script that I mentioned before.

In addition, for those who are lazy, I have added some bad code to LucD's beautiful code, to do the import based on user prompt, rather than editing the code each time that you run it.

One day I will virtualise myself . . .
Reply
0 Kudos
beaunewcomb
Contributor
Contributor
Jump to solution

I have NFS storage and am trying to run this script but I get this error:

Get-View : Cannot validate argument on parameter 'Id'. The argument is null or empty. Supply an argument that is not null or empty and then try the c

ommand again.

At C:\Scripts\DR\DR-Register-VM-NFS.ps1:92 char:16

+ $ds = Get-View <<<< $dsImpl

+ CategoryInfo : InvalidData: (Smiley Happy , ParameterBindingValidationException

+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.Commands.DotNetInterop.GetVIView

Can anyone shed some light on this? I have a DR test on Saturday and need to get this working ASAP!

many thanks

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Could you provide a bit more info ?

What ESX/ESXi server version build ? How many datastores ?

Are you running the script while connected to the vCenter or a specific ESX/ESXi host ?

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos