Pages

Sunday, December 2, 2012

Querying vCenter Linked Mode with the vSphere API

One question I'm asked now and then is how to retrieve the list of Linked Mode vCenter Servers from the vSphere API.  Unfortunately, the Linked Mode configuration is not provided through the published vSphere SDK.

VMware products that are aware of Linked Mode, such as the vSphere Client, use an undocumented API method called QueryServiceEndpointList.  We can leverage the same API method to discover Linked Mode vCenter server instances for use in custom automation tools.

Linked Mode does not provide a federated inventory structure across multiple vCenter instances.  For example, when the vSphere Client connects to a Linked Mode vCenter Server it retrieves the list of all Linked Mode endpoints and connects to each individually to retrieve vCenter managed entities.  You can see this behavior when accepting self-signed certificates for each vCenter instance for the first time.

Note: The QueryServiceEndpointList API method is undocumented and may change without notification in future vSphere API versions.

The structure of the QueryServiceEndpointList API method is simple and can be invoked prior to authenticating against a vSphere API instance.  The SOAP envelope has the following structure:
<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
    <QueryServiceEndpointList xmlns="urn:internalvim25">
      <_this xsi:type="ManagedObjectReference" type="ServiceDirectory">ServiceDirectory</_this>
    </QueryServiceEndpointList>
  </soap:Body>
</soap:Envelope>

The vSphere API will respond with QueryServiceEndpointListResponse, which provides an array of returnval.  The returnval object contains a set of properties describing the API service endpoints provided by each vCenter Server.

The properties provided by the returnval object are:
  • key - The endpoint key composed of the vCenter instanceUuid and endpoint protocol, e.g., 08118895-075D-4DB4-A9A8-B566E4007DF6/VIMAPI
  • instanceUuid - The vCenter instanceUuid
  • instanceName- The vCenter instance name (FQDN hostname)
  • protocol- The endpoint protocol, i.e., vimWebServices or vimApi
  • sslThumbprint- The SSL thumbprint of the endpoint service
  • url- The endpoint URI of the vCenter web service
  • certificate- The PEM format of the vCenter web service certificate

In vSphere 5.1, you should see three web service endpoints for each vCenter Server instance in the Linked Mode configuration: the VIM API web service, the Inventory Service, and the Management Web Service (CIM).

The following VI SDK for Perl script, LinkedModeExample.pl, provides an example of how to leverage the QueryServiceEndpointList API method. The script imports the SOAP::Lite module to make the initial call against one of the Linked Mode vCenter Servers to retrieve the list of vCenter API endpoints, connecting to each endpoint directly. Finally, the script prints out the clusters of each vCenter Server.

The automation example integrates with the VI Perl SDK runtime, so it supports the same input options used in other vSphere Perl SDK scripts.  The --server or --url parameters are used to do the initial QueryServiceEndpointList call, and the login credentials supplied to the script are used to connect to each vCenter Server VIM API instance in the Linked Mode configuration.

LinkedModeExample.pl

#!/usr/bin/perl

## LinkedModeExample.pl
## Created by Reuben Stump (http://www.virtuin.com)
##
## Demonstrates the use of the undocumented API method 'QueryServiceEndpointList'
## to retrieve a list of Linked Mode vCenter Servers and connect to each vCenter API 
## instance directly.

use strict;
use warnings;

use VMware::VIRuntime;
use SOAP::Lite;

Opts::parse();
Opts::validate();

my (@serviceUrls, @serviceInstances);

@serviceUrls = getLinkedModeUrls(service_url => Opts::construct_service_url());

# Connect to each VIM instance in the Linked Mode configuration
foreach my $serviceUrl (@serviceUrls) {
 my $vim = Util::connect($serviceUrl);
 $Vim::vim_global = undef; # Clear Vim::vim_global
 push @serviceInstances, $vim;
}

# Enumerate each VIM instance and print Cluster names
foreach my $vim (@serviceInstances) {
 my ($clusters, $instanceUuid, $serviceUrl, @clusterNames);
 
 $clusters = $vim->find_entity_views(view_type => 'ClusterComputeResource', 
          properties => ['name']);
 $instanceUuid = $vim->get_service_content()->{'about'}->{'instanceUuid'};
 $serviceUrl = $vim->get_service_url();
 
 @clusterNames = map($_->{'name'}, @{$clusters});
 
 print "vCenter $serviceUrl (instanceUuid=$instanceUuid):\n";
 print "  Clusters: " . join(",", @clusterNames) . "\n";
}

sub getLinkedModeUrls {
 my (%args) = @_;
 my (@serviceUrls, $service_url, %supportedApiVersions, $apiVersion, $soap, $som, $retval);
 
 $service_url = delete($args{'service_url'}) || 
  Util::fail("Missing argument service_url in getLinkedModeUrls()");
 
 # Use Perl VI SDK method to get current SDK API version
 %supportedApiVersions = Vim::query_api_supported($service_url);
 $apiVersion = $supportedApiVersions{'version'};
 
 # The VI SDK internal method QueryServiceEndpointList doesn't require authentication,
 # so use SOAP::Lite to construct the request and parse the response
 $soap = SOAP::Lite->new(proxy => $service_url, 
      on_action => sub { return "urn:vim25/$apiVersion" });
 
 # Call QueryServiceEndpointList with a ManagedObjectReference to ServiceDirectory
 $som = $soap->call('QueryServiceEndpointList',
  SOAP::Data->name('_this' => SOAP::Data->new(
   attr => { 'type' => 'ServiceDirectory', 'xsi:type' => 'ManagedObjectReference' },
   value => 'ServiceDirectory')));
 Util::fail($som->fault->{'faultstring'}) if ($som->fault);
 $retval = $som->body->{'QueryServiceEndpointListResponse'}->{'returnval'};
 
 # Enumerate the QueryServiceEndpointListResponse return value, extracting VIMAPI
 # endpoint URLs: protocol == 'vimApi'
 @serviceUrls = map($_->{'url'}, 
     grep { $_->{'protocol'} =~ m/^vimApi$/i } @{$retval});
   
 return @serviceUrls;
}

BEGIN {
 $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
}
Note:  The VIM global is set to undefined on line 26 of the script.  When connecting to a vCenter Instance, the VI SDK for Perl by default uses a global variable for the current VIM instance.  However, using the global instance can result in errors when using multiple VIM instances.  Just be sure to use $vim->method() instead of Vim::method() when calling VI SDK VIM class methods.

You can also download a version of the LinkedModeExample.pl script here.

If you're curious how I discovered this and other undocumented functions, I used Wireshark SSL packet decryption using the vCenter Server private key.  You can learn more about configuring Wireshark to do SSL packet captures here.

8 comments:

  1. Nice post, thanks! Do you know if there is official support for this in the 5.5 API?

    ReplyDelete
  2. Any chance you have an example of how to make this call in the C# code?

    ReplyDelete
    Replies
    1. No, I don't use C# generally. However, it's a simple SOAP request. It should be easy to construct it using a SOAP library or just raw XML construction.

      Delete
  3. Looks like it doesn't work for 6.0 , there is not difference between endpoints received from linked VC and not linked VC

    ReplyDelete
  4. I don't have a linked mode setup with 6.0 to validate, but it runs without errors. Is it not returning the linked vCs in 6.0? I used the vSphere 6.0 Perl SDK (clean install on Ubuntu 14).

    perl LinkedModeExample.pl --username=administrator --password=* --server=172.16.0.10
    vCenter https://172.16.0.10:443/sdk/vimService.wsdl (instanceUuid=49f3ec64-4306-4b93-b3de-27a7c736ef02):
    Clusters: CLU-01

    ReplyDelete
    Replies
    1. yes, looks like iit doesn't return linked VCs, here is list of endpoints (toString in Java)
      ServiceEndpoint [key=xxxx/VIMAPI, instanceUuid=xxxx, protocol=vimApi, url=https://vc-dc12.corp.local.com:443/sdk]
      ServiceEndpoint [key=yyyy/VIMWEBSVC, instanceUuid=yyyy, protocol=vimWebServices, url=http://localhost:10080/invsvc]

      right now I'm looking at 'config.vpxd.sso.sts.uri' property , on linked VCs it points to master VC

      Delete
  5. I worked out a python script to list linked mode vCenter Servers 6.0 or other Service Endpoints from PSC https://gist.github.com/jessehu/2e28b2d8ab454e67712cd943a8500512

    ReplyDelete
  6. Can any one suggest how we can use QueryServiceEndpointListResponse using Powershell to obtain linked server details.

    ReplyDelete