I just had a co-worker ask for some help on automation with the Perl SDK around DistributedVirtualPortgroups.
What I quickly found out is that find_entity_view calls will not work. And for a very simple reason. TraversalSpecs for networkFolder and datastoreFolder were not added to the subroutine get_search_filter_spec in VICommon.pm.
It seems like an oversight to have skipped TraversalSpecs for these folders. Without modifying VICommon.pm or calling RetrieveProperties directly, you'll need to do some klunky enumeration of Datacenter(s) and Network properties.
Is this something that others have run into or at least something that is slated to be fixed in a future release of the VI Perl SDK?
I updated the subroutine get_search_filter_spec in VICommon.pm to the following and it works quite simply. Similar specs would have to be added for the datastoreFolder
sub get_search_filter_spec { my ($class, $mo_ref, $property_spec) = @_; my $resourcePoolTraversalSpec = TraversalSpec->new(name => 'resourcePoolTraversalSpec', type => 'ResourcePool', path => 'resourcePool', skip => 0, selectSet => [SelectionSpec->new(name => 'resourcePoolTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec')]); my $resourcePoolVmTraversalSpec = TraversalSpec->new(name => 'resourcePoolVmTraversalSpec', type => 'ResourcePool', path => 'vm', skip => 0); my $computeResourceRpTraversalSpec = TraversalSpec->new(name => 'computeResourceRpTraversalSpec', type => 'ComputeResource', path => 'resourcePool', skip => 0, selectSet => [SelectionSpec->new(name => 'resourcePoolTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec')]); my $computeResourceHostTraversalSpec = TraversalSpec->new(name => 'computeResourceHostTraversalSpec', type => 'ComputeResource', path => 'host', skip => 0); my $datacenterHostTraversalSpec = TraversalSpec->new(name => 'datacenterHostTraversalSpec', type => 'Datacenter', path => 'hostFolder', skip => 0, selectSet => ); my $datacenterVmTraversalSpec = TraversalSpec->new(name => 'datacenterVmTraversalSpec', type => 'Datacenter', path => 'vmFolder', skip => 0, selectSet => ); my $hostVmTraversalSpec = TraversalSpec->new(name => 'hostVmTraversalSpec', type => 'HostSystem', path => 'vm', skip => 0, selectSet => ); my $datacenterNetTraversalSpec = TraversalSpec->new(name => 'datacenterNetTraversalSpec', type => 'Datacenter', path => 'networkFolder', skip => 0, selectSet => ); my $folderTraversalSpec = TraversalSpec->new(name => 'folderTraversalSpec', type => 'Folder', path => 'childEntity', skip => 0, selectSet => [SelectionSpec->new(name => 'folderTraversalSpec'), SelectionSpec->new(name => 'datacenterHostTraversalSpec'), SelectionSpec->new(name => 'datacenterVmTraversalSpec',), SelectionSpec->new(name => 'computeResourceRpTraversalSpec'), SelectionSpec->new(name => 'computeResourceHostTraversalSpec'), SelectionSpec->new(name => 'hostVmTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec'), SelectionSpec->new(name => 'datacenterNetTraversalSpec'), ]); my $obj_spec = ObjectSpec->new(obj => $mo_ref, skip => 0, selectSet => [$folderTraversalSpec, $datacenterVmTraversalSpec, $datacenterHostTraversalSpec, $computeResourceHostTraversalSpec, $computeResourceRpTraversalSpec, $resourcePoolTraversalSpec, $hostVmTraversalSpec, $datacenterNetTraversalSpec, $resourcePoolVmTraversalSpec]); return PropertyFilterSpec->new(propSet => $property_spec, objectSet => [$obj_spec]); }
The following code is much simpler than having to do a few nested soap calls on network objects from each Datacenter in the inventory.
#!/usr/bin/perl use strict; use warnings; use VMware::VIRuntime; Opts::parse(); Opts::validate(); Util::connect(); my $dvpgs = Vim::find_entity_views(view_type => "DistributedVirtualPortgroup", properties => \['name']); foreach (@{$dvpgs}) { my $portgroup = $_->{'name'}; print "Distributed Virtual Portgroup: $portgroup\n"; }
Just a quick note in case someone else has run into this snag with the VI Perl SDK and needed a fix. Obviously this requires modifying the VICommon.pm module provided by the SDK. The other option would be to build PropertyFilterSpecs and call RetrieveProperties, skipping the utility function find_entity_view.
EDIT: Ugh, the forum code mangled the function, watch directly copying and pasting from this post! Look at datacenterNetTraversalSpec which I added to the subroutine to support the networkFolder with find_entity_view.
Great post Ruben!
Sounds more like a bug to me and something that VMware should fix
I had not dug into the VICommon.pm when I had to extrac vDS information and had to go through the ugly route of traversing datacenter/networks to get to the vDS and vDP
=========================================================================
William Lam
VMware vExpert 2009
VMware ESX/ESXi scripts and resources at:
VMware Code Central - Scripts/Sample code for Developers and Administrators
If you find this information useful, please award points for "correct" or "helpful".
William,
Funny thing is, my co-worker sent me a script from the client you obviously updated and touched -- updateVMPortgroupLS.pl. They just wanted to take out the need to have parameters with the uuid and dvPortgroup key value and use something simpler, like the Portgroup friendly name (as seen in vCenter).
So, being a bit persistent, I found a really slick solution as a quick fix to this. You really have to love Perl.
There is a simple source module called Sub::Override that allows you to override subroutines. A quick CPAN install later (you could probably copy the Sub::Override source file to your Perl path) and I have a working solution that doesn't require patching VICommon.pm.
#!/usr/bin/perl use strict; use warnings; use VMware::VIRuntime; use Sub::Override; Opts::parse(); Opts::validate(); Util::connect(); my $override = Sub::Override->new( 'EntityViewBase::get_search_filter_spec' => \&get_search_filter_spec2); my $dvpgs = Vim::find_entity_views(view_type => "DistributedVirtualPortgroup", properties => \['name']); foreach (@{$dvpgs}) { my $portgroup = $_->{'name'}; print "Distributed Virtual Portgroup: $portgroup\n"; } sub get_search_filter_spec2 { my ($class, $mo_ref, $property_spec) = @_; my $resourcePoolTraversalSpec = TraversalSpec->new(name => 'resourcePoolTraversalSpec', type => 'ResourcePool', path => 'resourcePool', skip => 0, selectSet => [SelectionSpec->new(name => 'resourcePoolTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec')]); my $resourcePoolVmTraversalSpec = TraversalSpec->new(name => 'resourcePoolVmTraversalSpec', type => 'ResourcePool', path => 'vm', skip => 0); my $computeResourceRpTraversalSpec = TraversalSpec->new(name => 'computeResourceRpTraversalSpec', type => 'ComputeResource', path => 'resourcePool', skip => 0, selectSet => [SelectionSpec->new(name => 'resourcePoolTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec')]); my $computeResourceHostTraversalSpec = TraversalSpec->new(name => 'computeResourceHostTraversalSpec', type => 'ComputeResource', path => 'host', skip => 0); my $datacenterHostTraversalSpec = TraversalSpec->new(name => 'datacenterHostTraversalSpec', type => 'Datacenter', path => 'hostFolder', skip => 0, selectSet => \[SelectionSpec->new(name => "folderTraversalSpec")]); my $datacenterVmTraversalSpec = TraversalSpec->new(name => 'datacenterVmTraversalSpec', type => 'Datacenter', path => 'vmFolder', skip => 0, selectSet => \[SelectionSpec->new(name => "folderTraversalSpec")]); my $datacenterNetTraversalSpec = TraversalSpec->new(name => 'datacenterNetTraversalSpec', type => 'Datacenter', path => 'networkFolder', skip => 0, selectSet => \[SelectionSpec->new(name => "folderTraversalSpec")]); my $hostVmTraversalSpec = TraversalSpec->new(name => 'hostVmTraversalSpec', type => 'HostSystem', path => 'vm', skip => 0, selectSet => \[SelectionSpec->new(name => "folderTraversalSpec")]); my $folderTraversalSpec = TraversalSpec->new(name => 'folderTraversalSpec', type => 'Folder', path => 'childEntity', skip => 0, selectSet => [SelectionSpec->new(name => 'folderTraversalSpec'), SelectionSpec->new(name => 'datacenterHostTraversalSpec'), SelectionSpec->new(name => 'datacenterVmTraversalSpec',), SelectionSpec->new(name => 'datacenterNetTraversalSpec',), SelectionSpec->new(name => 'computeResourceRpTraversalSpec'), SelectionSpec->new(name => 'computeResourceHostTraversalSpec'), SelectionSpec->new(name => 'hostVmTraversalSpec'), SelectionSpec->new(name => 'resourcePoolVmTraversalSpec'), ]); my $obj_spec = ObjectSpec->new(obj => $mo_ref, skip => 0, selectSet => [$folderTraversalSpec, $datacenterVmTraversalSpec, $datacenterNetTraversalSpec, $datacenterHostTraversalSpec, $computeResourceHostTraversalSpec, $computeResourceRpTraversalSpec, $resourcePoolTraversalSpec, $hostVmTraversalSpec, $resourcePoolVmTraversalSpec]); return PropertyFilterSpec->new(propSet => $property_spec, objectSet => \[$obj_spec]); }
Note you'll have to search and replace <backslash>[ with [, the forum mangles open-closed brackets on the same line. You can of course add in a TraversalSpec object for the datastoreFolder if you wanted those objects in your scripts. I just did networkFolder with datacenterNetTraversalSpec for this simple fix.
I'm sure VMware will update/fix it, but it'll have to go through testing to get officially released. This fix might fill the gap for some people until then without breaking their VI Perl SDK support by modifying the VMware core modules.
- Reuben
Very slick solution indeed, glad you shared with the community!
You really have to love Perl.
Agreed!
=========================================================================
William Lam
VMware vExpert 2009
VMware ESX/ESXi scripts and resources at:
VMware Code Central - Scripts/Sample code for Developers and Administrators
If you find this information useful, please award points for "correct" or "helpful".
William,
I updated your script a bit using the Override. The changes allow you to drop the dvSwitchUuid parameter all together and the portgroup parameter becomes the friendly name as seen in the vCenter UI (not the key property of dvPortgroup).
It also prevents you from specifying a dvPortgroup that isn't in the same datacenter as the VM specified by vmname. dvPortgroup friendly names can be reused across Datacenters and you aren't guaranteed a unique name in a single vCenter inventory tree with multiple Datacenters instances.
perl updateVMPortgroupLS2.pl --username=administrator --password=VMware1 --server=172.16.171.130 --vmname="TestVM" --vnic=1 --portgroup=dvPortGroup Reconfiguration of portgroup "dvPortGroup" successful for "TestVM".
For the attached script to work, Sub::Override will have to be installed using CPAN, PPM, or a manual install (copy Override.pm down from CPAN).
Hi Ruben and William,
Is this still the best way for updating distributed vswitch portgroup with vSphere API 5.0?
Thanks,
ymnick
You shouldn't need the sub override anymore, newer API versions have the correct traversal specs to get to the DistributedVirtual* objects.
Thanks. And beside that the same technique as in the updateVMPortgroupLS2.pl script above?
Yeah, should work. Haven't tried it, but nothing off the top of my head has changed. Just one version of the SDK didn't include traversal specs to the new network folder when it was first released (bug). It was fixed soon after.
Okay, thanks again. Then another question - it goes through Datacenter object to retrieve dvPortgroup, does this mean that the connection must be against vCenter and not ESXi for this to work?
That's correct. I don't think the Host API even understands DistributedVirtualSwitches.
Thank you.