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.
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
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
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
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!!
Sean.
There was an error in the attached script.
It's corrected now. Sorry about that.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi LucD,
I can't see the revised script, did you attach it?
Many thanks!
Sean.
Yes, I replaced the faulty script in my first reply.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
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
The script is far to complex for my skills (i can just about work my way through piping cmdlets into each other ), 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.
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
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.
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 ---> vmprod_nj_dr
vmdata-nj ---> 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
*
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
This works excellently and has saved our Bacon! Thanks again LucD!!!
Mmmmmm, bacon.
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.
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?
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]
no luck with that.
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.
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: ( , 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
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