[UPDATE 2014-08-11] See note below for a workaround on this.
vCAC provides access to its data model via an ODATA REST interface that is based on the Microsoft .NET Entity Framework. Access to this interface is encapsulated and abstracted via the EF DataService that is exposed via the ModelManagerEntities object. For more information you can check some of my earlier blog posts like Investigating the vCAC 5.2 mgmtContext.
When querying entities from this object you have the easy option to use a PowerShell expression like:
$Machine = $m.VirtualMachines |? VirtualMachineName -eq 'myServer1';
In the background the ModelManagerEntities object generates a corresponding REST call like this:
The result set is then filtered in the PowerShell runtime to only return the object with the ‘VirtualMachineName’ that is equal to ‘myServer1’. You can imagine that this will affect performance in environments that consist of more than a few objects.
Luckily the .NET DataServiceQuery provides helper functions that allow for server side filtering. This filtering is actually part of the ODATA protocol specification. See ODATA V3 Documentation. So to filter for only the first entity returned you code something like this:
$Machine = $m.VirtalMachines.AddQueryOption('$top',1);
There are more (useful) filtering options as described in the ODATA URI conventions. Especially interesing are the ‘filter’ and ‘expand’ query options. With it you can reduce the returned result set or expand on a returned entity (in a single call).
The problem with the current implementation of this ‘AddQueryOption()’ method is that it does not generate the underlying ODATA REST call in a correct way. So when you code something like this:
PS > $m.VirtalMachines.AddQueryOption('$expand','VirtualMachineProperties'); The following exception occurred while trying to enumerate the collection: "An error occurred while processing this request.". At line:1 char:1 + $m.VirtualMachines.AddQueryOption('$expand', 'VirtualMachineProperties') + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) , ExtendedTypeSystemException + FullyQualifiedErrorId : ExceptionInGetEnumerator
When you have a look at the HTTP data you will see a request/response pattern like this:
=== Request === GET https://vcac52.sharedop.org/Repository/Data/ManagementModelEntities.svc/VirtualMachines()?$expand='VirtualMachineProperties' HTTP/1.1 User-Agent: Microsoft ADO.NET Data Services DataServiceVersion: 1.0;NetFx MaxDataServiceVersion: 2.0;NetFx Accept: application/atom+xml,application/xml Accept-Charset: UTF-8 Authorization: Negotiate abcd Host: vcac52.sharedop.org === Response === HTTP/1.1 400 Bad Request Content-Length: 230 Content-Type: application/xml DataServiceVersion: 1.0; Persistent-Auth: true WWW-Authenticate: Negotiate Date: Fri, 17 Jan 2014 10:34:43 GMT <?xml version="1.0" encoding="utf-8" standalone="yes"?> <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code></code> <message xml:lang="en-US">Syntax error at position 26.</message> </error>
You notice that the Property table to expand (‘VirtualMachineProperties’) is actually enclosed in single quotes (“‘”), indicating to the ODATA REST service that this is actually not a property to expand but in fact a string (which is wrong).
VMware has confirmed this to be a bug in the current version of the product and a fix for this has not yet been committed.
A workaround to this is to create the REST call yourself (via [WebClient] or Invoke-WebRequest or something similar) and the use the resulting identity links via ‘TryGetEntity()’ like this:
PS > $vm = New-Object DynamicOps.ManagementModel.VirtualMachine; PS > $m.TryGetEntity($uri.AbsoluteUri, [ref] $vm); True # if $true, VM then contains a vCAC entity object of type VirtualMachine
A note regarding the ‘Expand()’ method: this method expects a LINQ expression with lamdba functions that cannot be used with PowerShell either. So this is also no viable approach / workaround for this problem.
Though it is a little bit out of date, I finally came to a workaround for this. Create a new class library with Visual Studio and use the ManagementModel as the service reference in your class library. That assembly then will generate the correct REST calls for you so that you can use ‘AddQueryOption(‘$filter’,”)’ options and even ‘Expand()’. You certainly have to update your model when vCAC is updated. But as it seems, vCAC is quite a ‘stable’ (or ‘frozen’) state that you not have too much worries with that…