Problems connecting to VI SDK using C++/gsoap-2.7

Version 2

    Hi

    I know this topic has been discussed quite a lot recently, but  unfortunately I haven't been able to find a solution to my problem.  I  am currently running ESX Server 3.5.  I have no problems accessing it  via the SDK using Java and Perl.  However, I am trying to get a simple  C++ program working that can connect to it and do something useful.   First of all, I changed the proxy.xml file on the ESX Server so that the  sdk can be accessed via both http and https.

     

    Second, on my client machine (64-bit AMD Athlon running Ubuntu 8.10) I  have installed gsoap-2.7 to convert the WSDL files into stubs necessary  to communicate with the server.  I use the various gsoap programs to  convert the vimService.wsdl file in the directory SDK/wsdl/vim/ as  follows:

     

     

    wsdl2h -x -s vimService.wsdl

     

    soapcpp2 -i -C -I/home/ashok/gsoap-2.7/gsoap/import vimService.h

     

     

    This generates soapH.h, soapStub.h, soapC.cpp, as well as soapVimBindingProxy.{h,cpp}.

     

     

    Here is my simple test.cpp driver program that tries to access the ESX  Server and create a snapshot of one of the VMs (note that I've specified  http and not https in the URL):

     

     


    #include "soapH.h"

    #include "VimBinding.nsmap"

    #include "soapVimBindingProxy.h"

     

    int main() {

     

    VimBindingProxy service;

     

     

    service.soap_endpoint = "http://<server ip address>:443/sdk";

     

     

    ns2__ManagedObjectReference mor;

     

    ns2__CreateSnapshotRequestType req;

     

    _ns2__CreateSnapshotResponse resp;

     

     

    req._USCOREthis = &mor;

     

    req.name = "ashok_linux_1";

     

    req.memory = false;

     

    req.quiesce = false;

     

     

    if (service.CreateSnapshot(&req, &resp) == SOAP_OK) {

     

    std::cout << "OK"<< std::endl;

     

    }

     

    else {

     

    std::cout << "BAD" << std::endl;

     

    service.soap_print_fault(stdout);

     

    }

     

    }

     


    I compile this and run as follows:

     

    g++ *.cpp -Wall -DWITH_OPENSSL -lgsoap++

     

     

    ashok@ashok-desktop: ./a.out

     

    BAD

     

    Error -1 fault: SOAP-ENV:Client

    no subcode

    "End of file or no input: Connection reset by peer"

    Detail: no detail

     

     

    The message "Error -1 fault: SOAP-ENV:Client

    no subcode"  is something that I always  get, regardless of the SDK function that I  invoke.  Has anyone figured out this problem, specifically using the C++  stub functions auto-generated by gsoap?  If so, please tell me what I'm  doing wrong as I'm close to pulling my hair out!

     

     

    Ashok

     


    I think I'm going to write up a tutorial for C++ & gsoap to get  people started.  I got a working setup that will build libraries and use  the advanced C++ proxy option.  This generates more OO like code that  (in my opinion) reads and is easier to code with.  I'll post it up in  this thread if I do get to it this week.

     

    First thing is you'll need to login first.  You won't be able to call most of the functions in the SDK until you do.

     

     

    You'll also need to keep the compile flags consistent for all the  objects.  You'll need at least -DWITH_COOKIES.  Without cookies you'll  end up with permission errors as you end up regenerating a new SessionID  for each call.

     

     

    Can you also post up all the steps in your compile process?  It wasn't clear if you compiled and linked in your soapC.

     

     

    Here's a very simple example you can work from.  I don't call a login in  this one, but you can use it to get a working program and start to  build on it.  It does use SSL so you can see how that's handled.

     

     

    #include "soapVimBindingProxy.h"
    #include "VimBinding.nsmap"
    #include <time.h>
    #include <iostream>
    #include <typeinfo>

    using namespace std;

    void sigpipe_handle(int x) { cout << "sigpipe: " << x << endl; }

    int main(int argc, char* argv[])
    {
         VimBinding vim;
         ns1__ManagedObjectReference ManagedObjectRef;
         ns1__RetrieveServiceContentRequestType RetrieveServiceContentReq;
         _ns1__RetrieveServiceContentResponse RetrieveServiceContentRes;
         ns1__ServiceContent *ServiceContent;
         ns1__AboutInfo *AboutInfo;
         
         if (argc != 2)
         {
              cout << "Usage: " << argv[0] << " <service url>" << endl;
              exit(1);
         }
         
         string service_url(argv[1]);     
         
         vim.endpoint = service_url.c_str();
         soap_ssl_init();
         
         
         if (soap_ssl_client_context(
              vim.soap,
              SOAP_SSL_NO_AUTHENTICATION,
              NULL,
              NULL,
              NULL,
              NULL,
              NULL     ))
         {
              cout << "SSL:";
              soap_print_fault(vim.soap, stderr);
              soap_done(vim.soap);
              soap_end(vim.soap);
              exit(1);
         }
         
         ManagedObjectRef.__item = "ServiceInstance";
         ManagedObjectRef.type = new string("ServiceInstance");
         RetrieveServiceContentReq._USCOREthis = &ManagedObjectRef;
         
         if ( vim.__ns1__RetrieveServiceContent(&RetrieveServiceContentReq, &RetrieveServiceContentRes) == SOAP_OK )
         {
              cout << "RetrieveServiceContent - OK" << endl;
         }
         else
         {
              delete ManagedObjectRef.type;
              soap_print_fault(vim.soap,stderr);
              soap_done(vim.soap);
              soap_end(vim.soap);
              exit(1);
         }
         
         ServiceContent = RetrieveServiceContentRes.returnval;
         
         if (ServiceContent && ServiceContent->about)
         {
              AboutInfo = ServiceContent->about;
              cout << "fullName: " << AboutInfo->fullName << endl;
              cout << "  name: " << AboutInfo->name << endl;
              cout << "  build: " << AboutInfo->build << endl;
              cout << "  version: " << AboutInfo->version << endl;
              cout << "  apiType: " << AboutInfo->apiType << endl;
              cout << "  productLineId: " << AboutInfo->productLineId << endl;
         }
         
         delete ManagedObjectRef.type;
         
         soap_done(vim.soap);
         soap_end(vim.soap);
         
         return 0;
         
    }

     


    Thank you very much for your response.  I am looking forward to reading  your C++/gsoap "Getting Started" tutorial.   I will try working with the  template code you provided, but I first tried adding login capability  to my original test program.  My program (test.cpp) now looks like this:

     


    #include <cstdlib>

    #include <iostream>

    #include <string>

    #include "soapH.h"

    #include "VimBinding.nsmap"

    #include "soapVimBindingProxy.h"

     

    int main() {

     

    VimBindingProxy service;

     

    ns2__ManagedObjectReference mor;

     

    service.soap_endpoint = "http://<ip address>:443/sdk";

    // Invoke Login API
    ns2__LoginRequestType login_request;
    _ns2__LoginResponse   login_response;
    login_request._USCOREthis = &mor;
    login_request.userName = "root";
    login_request.password = "<my root passwd>";

    if (service.Login(&login_request, &login_response) == SOAP_OK) {
    std::cout << "login ok" << std::endl;
    }
    else {
    std::cout << "login not ok" << std::endl;
    service.soap_print_fault(stdout);
    }

    // Invoke RetrieveServiceContent API
    ns2__RetrieveServiceContentRequestType service_request;
    _ns2__RetrieveServiceContentResponse service_response;
    service_request._USCOREthis = &mor;

    if (service.RetrieveServiceContent(&service_request, &service_response) == SOAP_OK) {
    std::cout << "retrieve ok" << std::endl;
    }
    else {
    std::cout << "retrieve not ok" << std::endl;
    service.soap_print_fault(stdout);
    }
    }


     

    This is how I compiled the code:

     

     

    ashok@ashok-desktop:g++  -DWITH_OPENSSL -DWITH_COOKIES test.cpp  soapC.cpp soapVimBindingProxy.cpp  /home/ashok/gsoap-2.7/gsoap/stdsoap2.cpp -lssl -lcrypto

     

     

    Unfortunately, I get both "not ok" messages when I run the executable.    Looking at this code, is it obvious to you what I'm not doing  correctly?

     

     

    Thanks

     

    Ashok

     


    You'll need your ServiceContent first.  You'll need it for most of the  method calls against the SDK and should get it first.  You also need to  do a bit more setup on the ManagedObject reference.

     

    You should setup the ManagedObjectReference with the "ServiceInstance"  string.  I updated it below.  Call this before the login, you'll need  the ServiceContent response for login.

     

     

    // Invoke RetrieveServiceContent API 
    ns2__RetrieveServiceContentRequestType service_request;
    _ns2__RetrieveServiceContentResponse service_response;

    mor.__item = "ServiceInstance";
    mor.type = new string("ServiceInstance");
    service_request._USCOREthis = &mor;

    if (service.RetrieveServiceContent(&service_request, &service_response) == SOAP_OK)
    {
        std::cout << "retrieve ok" << std::endl;
    }
    else
    {
       std::cout << "retrieve not ok" << std::endl;
       service.soap_print_fault(stdout);
    }

    delete mor.type;

     

     

    The login function is from the SessionManager object, so you'll need that to set up your Login call:

     

     


    ns2__ServiceContent *ServiceContent;
    ServiceContent = service_response.returnval;

    // Invoke Login API
    ns2__LoginRequestType login_request;
    _ns2__LoginResponse login_response;

    login_request._USCOREthis = ServiceContent->sessionManager;
    login_request.userName = "root";
    login_request.password = "<my root passwd>";

    if (service.Login(&login_request, &login_response) == SOAP_OK)
    {
        std::cout << "login ok" << std::endl;
    }
    else
    {
        std::cout << "login not ok" << std::endl;
        service.soap_print_fault(stdout);
    }

     


    I finally got your test program (from your prior post) to build and run  properly.  Thanks so much for your help! In case anyone is interested,  here are the exact sequence of steps I took to get things working.  Note  that I have installed both gsoap-2.7 and the VI SDK in my home  directory /home/ashok:

     

    1) Copy over the vim25 WSDL file:

     

    cp /home/ashok/vi_sdk/SDK/wsdl/vim25/vim.wsdl .

     

     

    2) Build .h and .cpp files from WSDL:

     

    wsdl2h -o vim25.h vim.wsdl

     

    soapcpp2 -x -C vim25.h -I/home/ashok/gsoap-2.7/gsoap/import

     

     

    3) Put test program in file test.cpp

     

     

    4) Build executable:

     

    g++ -DWITH_OPENSSL -DWITH_COOKIES test.cpp soapC.cpp soapClient.cpp /home/ashok/gsoap-2.7/gsoap/stdsoap2.cpp -lssl -lcrypto

     

     

    5) Run executable

     

     

    ashok@ashok-desktop:~/vi_gsoap$ ./a.out

    https://<ip addr>:443/sdk

     

    RetrieveServiceContent - OK

     

    fullName: VMware ESX Server 3.5.0 build-123630

     

    name: VMware ESX Server

     

    build: 123630

     

    version: 3.5.0

     

    apiType: HostAgent

     

    productLineId: esx

     


     

    I can use the property collector to get the ManagedObjectReference  corresponding to a specific VirtualMachine.  However, I'm having trouble  getting the MOR corresponding to a snapshot of the VM.  Given the name  of a VM and the name of a snapshot within the VM, could you please tell  me the general algorithm I need to follow to get a hold of the  ManagedObjectReference for this snapshot?

     

     

     

    Thanks

    Ashok


    If you're getting your VM ManagedObject from RetrieveProperties, you should also just get the snapshot

    property.  The currentSnapshot

    from this property will have a ManagedObjectReference to the snapshot.   You can use this to the snapshot task methods or you can  RetrieveProperties again to get the snapshot info.

     

    I might be able to get you some code examples in a bit.

     


    Stumpr

    Sorry to bother you with more questions.  So I have no problems using  the PropertyCollector to iterate over all the VirtualMachine objects.   For each VM object, I can easily grab the Snapshot MORs associated with  it as well as get info about all the virtual disk files that comprise  the VM.  What I would like to do now is to use the PropertyCollector to  iterate over all the Datastore objects and for each Datastore, find out  which VirtualMachines map to the datastore.  I took your original  traversal code and modified it slightly (I changed the datacenter  traversal path and propertyspec type).  My changes are marked with ***:


     

    FolderTraversalSelectionSpec.name = new string("FolderTraversalSpec");

     

    DataCenterVMTraversalSelectionSpec.name = new string("DataCenterVMTraversalSpec");

     

     

    DataCenterVMTraversalSpec.name = new string("DataCenterVMTraversalSpec");

     

    DataCenterVMTraversalSpec.type = "Datacenter";

     

    DataCenterVMTraversalSpec.path = "datastoreFolder";      ***

     

    DataCenterVMTraversalSpec.skip = &xsd_true;

     

     

    FolderTraversalSpec.name = new string("FolderTraversalSpec");

     

    FolderTraversalSpec.type = "Folder";

     

    FolderTraversalSpec.path = "childEntity";

     

    FolderTraversalSpec.skip = &xsd_true;

     

     

    DataCenterVMTraversalSpec.selectSet.push_back(&FolderTraversalSelectionSpec);

     

    FolderTraversalSpec.selectSet.push_back(&DataCenterVMTraversalSelectionSpec);

     

    FolderTraversalSpec.selectSet.push_back(&FolderTraversalSelectionSpec);

     

     

    PropertySpec.type = "Datastore";   ***

     

    PropertySpec.all = &xsd_true;        ***

     

     

    ObjectSpec.obj = ServiceContent->rootFolder;

     

    ObjectSpec.skip = &xsd_true;

     

     

    ObjectSpec.selectSet.push_back(&FolderTraversalSpec);

     

    ObjectSpec.selectSet.push_back(&DataCenterVMTraversalSpec);

     

     

    PropertyFilterSpec.propSet.push_back(&PropertySpec);

     

    PropertyFilterSpec.objectSet.push_back(&ObjectSpec);

     

     

    RetrievePropertiesReq._USCOREthis = ServiceContent->propertyCollector;

     

    RetrievePropertiesReq.specSet.push_back(&PropertyFilterSpec);

     


     

    When I invoke the RetrieveProperties function:

     

     

    if ( vim.__ns1__RetrieveProperties(&RetrievePropertiesReq, &RetrievePropertiesRes) == SOAP_OK )

     

     

    the ESX server responds with a fault:

     

     

    SOAP 1.1 fault: "":ServerFaultCode

    no subcode

    ""

    Detail:

    Is this something you have seen before?
    Thanks
    Ashok


    I don't think there is a folder for the Datastore.  You will probably  need to just do your call to get the Datacenter object(s) and then  enumerate their datastores...using the string "datastoreFolder" is  likely the cause of your serverFaultCode.

     

    FolderTraversalSelectionSpec.name = new string("FolderTraversalSpec");
    DataCenterVMTraversalSelectionSpec.name = new string("DataCenterVMTraversalSpec");

    DataCenterVMTraversalSpec.name = new string("DataCenterVMTraversalSpec");
    DataCenterVMTraversalSpec.type = "Datacenter";
    DataCenterVMTraversalSpec.path = "vmFolder"; // Change this back
    DataCenterVMTraversalSpec.skip = &xsd_true;

    FolderTraversalSpec.name = new string("FolderTraversalSpec");
    FolderTraversalSpec.type = "Folder";
    FolderTraversalSpec.path = "childEntity";
    FolderTraversalSpec.skip = &xsd_true;

    DataCenterVMTraversalSpec.selectSet.push_back(&FolderTraversalSelectionSpec);
    FolderTraversalSpec.selectSet.push_back(&DataCenterVMTraversalSelectionSpec);
    FolderTraversalSpec.selectSet.push_back(&FolderTraversalSelectionSpec);

    PropertySpec.type = "Datacenter"; // Change this to Datacenter
    PropertySpec.all = &xsd_false; // Change this to false, just get the Datastore property -- will be faster
    PropertySpec.pathSet.push_back("name"); // Get the name in case you have multiple datacenters in your inventory
    PropertySpec.pathSet.push_back("datastore"); // Get the datastore property

    ObjectSpec.obj = ServiceContent->rootFolder;
    ObjectSpec.skip = &xsd_true;

    ObjectSpec.selectSet.push_back(&FolderTraversalSpec);
    ObjectSpec.selectSet.push_back(&DataCenterVMTraversalSpec);

    PropertyFilterSpec.propSet.push_back(&PropertySpec);
    PropertyFilterSpec.objectSet.push_back(&ObjectSpec);

    RetrievePropertiesReq._USCOREthis = ServiceContent->propertyCollector;
    RetrievePropertiesReq.specSet.push_back(&PropertyFilterSpec);

     

    You can read up on TraversalSpecs in the VI SDK Programming guide.  I  sort of take the lazy method of just using a sort of 'generic all'  TraversalSpec, but you'll want to review it to understand what inventory  objects you can query that way.

    What will really start to drive you crazy is when you have to deal with  the DynamicProperty data type you get back from your calls.  You'll end  up with a lot of conditional code using either dynamic_cast or typeid  calls.  I've been thinking of creating a class, say Entity, that would  parse the properties data and build a hash_map similar to how the perl  toolkit get_view* and find_entity_view* calls work.


    Thank you very much.  I'll give this a try and let you know what  happens.  What I find confusing is that when I access the MOB for my ESX  server over the web, I see that the Datacenter object type has a  property called 'datastoreFolder' and the value of this property is  'ha-folder-datastore'.

     

    One other thing: I have created 2 VMs on two different ESX servers --  one runs ESX Server 3.5 and the other runs the 4.0 Beta.  I can  successfully issue a PowerOn command to the ESX 4.0 server using the  call: vim->__ns1__PowerOnVM(&powerOnReq, &powerOnRes)  (where  the powerOnReq object contains a pointer to the VM MOR corresponding to  the VM I created).  However, when I issue the same command to the ESX  3.5 server, I get the SOAP_FAULT error that I reported previously:

     

     

    SOAP 1.1 fault: "":ServerFaultCode no subcode

     

    ""

     

    Detail:

     

     

    I am certain I am accessing the VM MOR properly on the ESX 3.5 server  because I can print out the correct properties of this machine.  I just  can't power it on/off, take snapshots, etc.  Do you have any idea why  the 3.5 server won't allow me to issue these commands?  Could it be some  kind of permission issue?

     

     

    ashok

     


    Could be, do you have full admin rights on 3.5?

     


     

    Hi Ashok,

     

    Let me try to understand what you did with the ESX server, you call the  following line of code but supplied a MOR to your host. Am I right?

    vim->__ns1__PowerOnVM(&powerOnReq, &powerOnRes)

    If so, you for sure got the error message. Basically the PowerOnVM is  only applicable for the virtual machine managed object. If you want to  power on a host, you want to use... Wait a second, you cannot power on a  host using VI SDK at all. But if you want to power it off, then you can  use ShutdownHost_Task() method. Check out more @ http://pubs.vmware.com/vi-sdk/visdk250/ReferenceGuide/vim.HostSystem.html

    Final note, if you use higher level API like VI Java API, you will not  get this problem at all. The compile will enforce the type checking.  Find it more at http://vijava.sf.net.

    Steve JIN, VMware Engineering
    Creator of VMware Infrastructure Java(Jython) API.  VI Java API 2.0 --- 15 times faster than AXIS in loading, 4+ faster in  deserialization; only 1/4 of the size required by AXIS. More  importantly, the freedom to redistribute it with your applications. (Download, Samples, DocWiki, RSS Feed)
    Get Connected with Other Developers in the Community?

     


    I didn't get that impression.  He stated he had a moref from a VM he  created (and used it to populate his PowerOnReq object), then got a  fault only when trying to power on the VM on an ESX 3.5 server, his beta  4 worked properly.  If it isn't a permission issue Ashok, post up some  code.  Might be able to see if there is anything off in the call.

     


    I wasn't sure what he did... You are right, sharing more code definitely helps.

     

    Steve JIN, VMware Engineering

     

    Creator of

    VMware Infrastructure Java(Jython) API.  VI Java API 2.0 --- 15 times faster than AXIS in loading, 4+ faster in  deserialization; only 1/4 of the size required by AXIS. More  importantly, the freedom to redistribute it with your applications. (

    Download,

    Samples,

    DocWiki,

    RSS Feed)

     

    Get Connected with Other Developers in the Community?

     


    Steve and stumpr:

     

    (stumpr: what is your real name by the way?)

     

     

    I am attaching the small test program that I've been using to power on /  power off a specified VM.  To recap, I can use this program to  manipulate a VM on an ESX 4.0 beta server, but not a VM on an ESX 3.5  server.  I am pretty certain that this has nothing to do with  permissions on the 3.5 server.  The reason for this is that I can  powerOn/powerOff a VM on the ESX 3.5 machine using the Java sample  program VMpowerOps.java, but I cannot do this with my C++ program.  In  both programs, I login using the same username ("root") and password.

     

     

    Here is the command I issue to get the Java program working (note that I use the "--ignorecert" option):

     

     

    ashok@ashok-ubuntu-vm:~/vi_sdk/SDK/samples/Axis/java$ ./run.sh com.vmware.samples.vm.VMpowerOps --url

    https://<ip  address>:443/sdk/vimService --username root --password <my  password> --vmname ashok_linux_1 --operation poweroff --ignorecert

     

    ./run.sh: 48: pushd: not found

     

    RUNNING

     

    Sun Mar 22 00:20:26 PDT 2009 Begin Log.

     

    Started

     

    Powering off virtualmachine 'ashok_linux_1'

     

    Virtual Machine ashok_linux_1 powered off successfuly

     

    Ended VMpowerOps

     

    Why would the powerOn/powerOff operation work correctly from the Java application, but not the C++ app?


    It's sort of an issue with the gsoap code generation, but I found an answer after a comparing a few packet captures.

     

    So there are actually two functions created after parsing the VMware vim25 wsdl matching PowerOnVM.  One is the

    PowerOnVM function you used.  However, this will send the method name to the VIM instance as

    PowerOnVM which is the VIM service returns as unsupported.  This in turn generates the fault code you were seeing.

     

     

    There is another function,

    PowerOnVM_USCORETask.  You'll need to  use this version (and I suspect you'll need it for every _Task  function).  This will create the soap envelope with the function name as  the VIM service expects.

     

     

    I'm guessing that Beta 4 either is supports both (or just PowerOnVM).

     

     

    It's actually an easy fix.

     

     

    Update your powerVM function to match the following: (I put a "This line has changed" comment at the end of each line I changed)

     

     

    static void
    powerVM(VimBinding* vim, ns1__ServiceContent* serviceContent,
         string vm_name, bool powerOn) {

      ns1__ManagedObjectReference* vm_mor =
        getVMManagedObjectRef(vim, serviceContent, vm_name);

      if (powerOn) {
        // Power-on the Virtual Machine.
        ns1__PowerOnVMRequestType powerOnReq;
        _ns1__PowerOnVM_USCORETaskResponse powerOnRes; // This line is changed
       
        powerOnReq._USCOREthis = vm_mor;
        if (vim->__ns1__PowerOnVM_USCORETask(&powerOnReq, &powerOnRes) == SOAP_OK) { // This line is changed
          cout << "PowerOn of VM " << vm_name << " successful" << endl << endl;
        }
        else {
          cout << "PowerOn of VM " << vm_name << " unsuccessful" << endl << endl;
          soap_print_fault(vim->soap,stderr);
          soap_done(vim->soap);
          soap_end(vim->soap);
          exit(1);
        }
      }
      else {
        // Power-off the Virtual Machine.
        ns1__PowerOffVMRequestType powerOffReq;
        _ns1__PowerOffVM_USCORETaskResponse powerOffRes;  // This line is changed
       
        powerOffReq._USCOREthis = vm_mor;
        if (vim->__ns1__PowerOffVM_USCORETask(&powerOffReq, &powerOffRes) == SOAP_OK) { // This line is changed
          cout << "PowerOff of VM " << vm_name << " successful" << endl << endl;
        }
        else {
          cout << "PowerOff of VM " << vm_name << " unsuccessful" << endl << endl;
          soap_print_fault(vim->soap,stderr);
          soap_done(vim->soap);
          soap_end(vim->soap);
          exit(1);
        }
      }
    }

     


     

    I just verified that using the PowerOnVM_USCORETask function on the ESX  3.5 server works fine, thanks for your help!  I will let you know if I  run into any problems with the datastore object retrieval.

     

    I'm curious as  to why  gsoap generates both PowerOnVM and  PowerOnVM_USCORETask functions.  It seems that we should always use the  latter function.  When would one use the former?

    Ashok


    Well, both are defined in the WSDL.  In fact, the PowerOnVM worked for Beta 4 right?

     

    They may have it in there as a place holder or just not have gotten  around to implementing that version on the server side.  Don't know.   Does the PowerOnVM_Task work on Beta 4 as well?  I don't have a b4  system to test against.

     


    Stumpr is right that both methods are there in the WSDL. In the API ref,  you only see the *_Task version. Please stick with the *_Task() since  the * version may go away.

     

    Steve JIN, VMware Engineering

     

    Creator of

    VMware Infrastructure Java(Jython) API.  VI Java API 2.0 --- 15 times faster than AXIS in loading, 4+ faster in  deserialization; only 1/4 of the size required by AXIS. More  importantly, the freedom to redistribute it with your applications. (

    Download,

    Samples,

    DocWiki,

    RSS Feed)

     

    Get Connected with Other Developers in the Community?

     


    Hi,

     

    I have followed the same steps listed in this thread to traverse Managed  objects to get the list of VMs in my  C++ client with gsoap.

     

    Login happened successfully. But RetrieveProperties()  is returning zero objects.

     

     

    (I am using ESX server 3.5.)

     

     

    The following message appears in /var/log/vmware/hostd.log, whenever I run my client.

     

     

    2009-05-13 04:24:49.542 'ha-eventmgr' 29846448 info Event 10 : User root@127.0.0.1 logged in

     

    2009-05-13 04:24:49.554 'App' 14834608 error _FetchPropertyPath got unexpected error of type  N3Vim5Fault16NotAuthenticated9ExceptionE: vim.fault.NotAuthenticated,  for ref=ha-folder-root, path=childEntity

     

    2009-05-13 04:24:49.564 'Vmomi' 3076436896 info Activation

    N5Vmomi10ActivationE:0xae06200 : Invoke done

    logout on

    vim.SessionManager:ha-sessionmgr

    2009-05-13 04:24:49.565 'Vmomi' 3076436896 info Throw vim.fault.NotAuthenticated

     

    2009-05-13 04:24:49.565 'Vmomi' 3076436896 info Result:

     

    (vim.fault.NotAuthenticated) {

     

    dynamicType = <unset>,

     

    object = 'vim.SessionManager:ha-sessionmgr',

     

    privilegeId = "System.View",

     

    msg = ""

     

    }

     

     

    The client code is:

     

     

    #include <string>

     

    #include <iostream>

     

    #include <sstream>

     

    #include <vector>

     

    #include "visdkH.h"

     

    #include "visdkStub.h"

     

    #include "VimBinding.nsmap"

     

    #include <assert.h>

     

    #include <typeinfo>

     

    using namespace std;

     

     

    struct soap *soap;

     

    ostringstream server;

     

     

    void sigpipe_handle(int x) {

     

    cout << "sigpipe: " << x << endl;

     

    }

     

     

    int getCurrentVMMob(sduvisdk2__ServiceContent *ServiceContent) {

     

     

    sduvisdk2__TraversalSpec DatacenterToVmFolderTraversalSpec, FolderToChildEntityTraversalSpec;

     

    sduvisdk2__SelectionSpec DatacenterToVmFolderSelectionSpec, FolderToChildEntitySelectionSpec;

     

    sduvisdk2__PropertySpec propertySpec;
    sduvisdk2__ObjectSpec objectSpec;
    sduvisdk2__PropertyFilterSpec propertyFilterSpec;
    sduvisdk2__DynamicProperty dynamicProperty;

    sduvisdk2__RetrievePropertiesRequestType retrievePropertiesReq;
    _sduvisdk2__RetrievePropertiesResponse retrievePropertiesRes;
    sduvisdk2__ManagedObjectReference* vm_mor = NULL;

    bool xsd_true = 1;
    bool xsd_false = 0;

    FolderToChildEntitySelectionSpec.name = new string("DatacenterToVmFolderTraversalSpec");
    DatacenterToVmFolderSelectionSpec.name = new string("FolderToChildEntityTraversalSpec");

    DatacenterToVmFolderTraversalSpec.name = new string("DatacenterToVmFolderTraversalSpec");
    DatacenterToVmFolderTraversalSpec.type = "Datacenter";
    DatacenterToVmFolderTraversalSpec.path = "vmFolder";
    DatacenterToVmFolderTraversalSpec.skip = &xsd_true;

    FolderToChildEntityTraversalSpec.name = new string("FolderToChildEntityTraversalSpec");
    FolderToChildEntityTraversalSpec.type = "Folder";
    FolderToChildEntityTraversalSpec.path = "childEntity";
    FolderToChildEntityTraversalSpec.skip = &xsd_true;

    DatacenterToVmFolderTraversalSpec.selectSet.push_back(&FolderToChildEntitySelectionSpec);
    FolderToChildEntityTraversalSpec.selectSet.push_back(&DatacenterToVmFolderSelectionSpec);
    FolderToChildEntityTraversalSpec.selectSet.push_back(&FolderToChildEntitySelectionSpec);

    propertySpec.type = "VirtualMachine";
    propertySpec.all = &xsd_false;
    propertySpec.pathSet.push_back("name");

    objectSpec.obj = ServiceContent->rootFolder;
    objectSpec.skip = &xsd_true;

    objectSpec.selectSet.push_back(&FolderToChildEntityTraversalSpec);
    objectSpec.selectSet.push_back(&DatacenterToVmFolderTraversalSpec);

    propertyFilterSpec.propSet.push_back(&propertySpec);
    propertyFilterSpec.objectSet.push_back(&objectSpec);

    retrievePropertiesReq._USCOREthis = ServiceContent->propertyCollector;
    retrievePropertiesReq.specSet.push_back(&propertyFilterSpec);

    soap_call___sduvisdk2__RetrieveProperties(soap, server.str().c_str(), "",
    &retrievePropertiesReq, &retrievePropertiesRes);

    if (soap->error) {
    soap_print_fault(soap, stderr);
    goto end_of_function;
    }

    cout << "Retrieved Properties: " << retrievePropertiesRes.returnval.size() << "\n";

    for (unsigned int i = 0; i < retrievePropertiesRes.returnval.size(); i++) {
    dynamicProperty = *retrievePropertiesRes.returnval->propSet[0];

    assert(dynamicProperty.name == "name");
    xsd__string* name = (xsd__string*) dynamicProperty.val;

    cout << "VM name: " << name->__item << endl;     
    }
    end_of_function:
    delete FolderToChildEntitySelectionSpec.name;
    delete FolderToChildEntityTraversalSpec.name;
    delete DatacenterToVmFolderSelectionSpec.name;
    delete DatacenterToVmFolderTraversalSpec.name;

    return soap->error;
    }

    int main(int argc, char *argv[])
    {
    int ret = 0;
    sduvisdk2__RetrieveServiceContentRequestType SvcCont;
    _sduvisdk2__RetrieveServiceContentResponse Response;

    sduvisdk2__ManagedObjectReference *Mobj;
    sduvisdk2__ServiceContent *sc;

    sduvisdk2__LoginRequestType login;
    _sduvisdk2__LoginResponse loginResponse;

    sduvisdk2__LogoutRequestType logout;
    _sduvisdk2__LogoutResponse logoutResponse;

    sduvisdk2__AboutInfo *about; 
    sduvisdk2__UserSession *returnval = NULL;

    if (argc != 4) {
    cout << "Usage: " << argv[0] << " <ESX-server-IP> <user> <passwd>\n";
    exit(1);
    }      
    server << "https://" << argv[1] << ":443/sdk";

    soap = soap_new();
    soap_init(soap);
    soap_set_namespaces(soap, namespaces);

    Mobj = new sduvisdk2__ManagedObjectReference;

    soap_ssl_init();
    if (soap_ssl_client_context( soap, SOAP_SSL_NO_AUTHENTICATION, NULL, NULL, NULL, NULL, NULL)) {

    std::cout << "SSL:";
    soap_print_fault(soap, stderr);
    soap_done(soap);
    soap_end(soap);
    exit(1);
    }
    Mobj->__item = "ServiceInstance";
    Mobj->type = new string("ServiceInstance");
    SvcCont._USCOREthis = Mobj;

    ret = soap_call___sduvisdk2__RetrieveServiceContent(soap, server.str().c_str(), "", &SvcCont, &Response);
    if(SOAP_OK == ret) {
    cout << "Retrieve success\n";

    } else {

    cout << "Soap Errors " << soap->error
    << " " << soap->errnum;
    soap_print_fault(soap, stderr);
    goto end_of_function;
    }
    sc = Response.returnval;

    if( !sc || !sc->sessionManager ) {
    goto end_of_function;
    }

    about = sc->about; 
    cout << "---------------------------------------\n"
    << "About Info: \n"
    << "Name          :" << about->name          << endl
    << "FullName      :" << about->fullName      << endl
    << "Vendor        :" << about->vendor        << endl
    << "version       :" << about->version       << endl
    << "build         :" << about->build         << endl 
    << "osType        :" << about->osType        << endl
    << "productLineId :" << about->productLineId << endl
    << "apiType       :" << about->apiType       << endl
    << "apiVersion    :" << about->apiVersion    << endl      
    << "----------------------------------------\n";    

    login._USCOREthis = sc->sessionManager;
    login.userName = argv[2];
    login.password = argv[3];

    soap_call___sduvisdk2__Login(soap, server.str().c_str(), "", &login, &loginResponse);
    if(soap->error != SOAP_OK ) {
    soap_print_fault(soap, stderr);
    goto end_of_function;
    }
    returnval = loginResponse.returnval;

    cout <<  "Login success, the details are:\n"
    <<  "\tUser: "      << returnval->userName << endl
    <<  "\tFull Name: " << returnval->fullName << endl
    <<  "\tLocale: "    << returnval->locale << endl;

    ret = getCurrentVMMob(sc);
    if (ret != SOAP_OK)
    cout << "Failed to get CurrentVMMob\n"; 

    cout << "\nNow Logging out\n";
    logout._USCOREthis = sc->sessionManager;

    ret = soap_call___sduvisdk2__Logout(soap, server.str().c_str(), "", &logout, &logoutResponse);
    if( soap->error != SOAP_OK ) {
    soap_print_fault(soap, stderr);
    goto end_of_function;
    }
    cout << "Logout succesful\n";

    end_of_function:
    soap_done(soap);
    soap_end(soap);
    }

    When I run this code:

    $ ./DynamicClient 10.72.197.66 root XXXXX
    Retrieve success


    About Info:
    Name          :VMware ESX Server
    FullName      :VMware ESX Server 3.5.0 build-64607
    Vendor        :VMware, Inc.
    version       :3.5.0
    build         :64607
    osType        :vmnix-x86
    productLineId :esx
    apiType       :HostAgent
    apiVersion    :2.5.0


    Login success, the details are:
    User: root
    Full Name: root
    Locale: en
    Retrieved Properties: 0

    Now Logging out
    SOAP 1.1 fault: "":ServerFaultCode no subcode
    "The session is not authenticated."
    Detail:

    Why is Logout() call failing. Please let me know if I am doing anything wrong in my client code.

    Thanks!


    Did you compile all your sources with -DWITH_COOKIES?


    Hi stumpr,

    Yes. I complied my sources with -DWITH_COOKIES. I am hitting this issue with/without -DWITH_COOKIES.

    Thanks!


    You have to use WITH_COOKIES, or you will not be authenticated  on subsequent calls to the API.  Make sure you compiled all components,  not just your final executable.  I'll take another look at your code  and see if anything sticks out.


    Hi stumpr,

    I tried to generate the stubs once again and compiled the sources with -DWITH_COOKIES. Logout is working fine now.
    I see proper messages in /var/log/vmware/hostd.log

    2009-05-14 02:21:45.673 'ha-eventmgr' 3076436896 info Event 45 : User root@127.0.0.1 logged in
    2009-05-14 02:21:45.697 'ha-eventmgr' 14834608 info Event 46 : User root logged out

    When I run the client (attached with this post), no Object Contents are  found. Is there anything wrong in my client code (traversing the Mobs)

    bash-2.05b$ ./a.out 10.72.197.66 root XXXXX
    Retrieve success


    About Info:
    Name          :VMware ESX Server
    FullName      :VMware ESX Server 3.5.0 build-64607
    Vendor        :VMware, Inc.
    version       :3.5.0
    build         :64607
    osType        :vmnix-x86
    productLineId :esx
    apiType       :HostAgent
    apiVersion    :2.5.0


    Login success, the details are:
    User: root
    Full Name: root
    Locale: en
    Retrieved Properties: 0  <<<<<<<<<<<<<<<<<<<<<<<  No VMs found

    Now Logging out
    Logout succesful


    Thanks once again!!


    Hi stumpr,

    I made an error in following statements:

    FolderToChildEntitySelectionSpec.name = new string("DatacenterToVmFolderTraversalSpec");
    DatacenterToVmFolderSelectionSpec.name = new string("FolderToChildEntityTraversalSpec");


    The client code works fine after correcting them to,

    FolderToChildEntitySelectionSpec.name = new string("FolderToChildEntityTraversalSpec");
    DatacenterToVmFolderSelectionSpec.name = new string("DatacenterToVmFolderTraversalSpec");


    Sorry for troubling you guys.

    bash-2.05b$ ./a.out 10.72.197.66 root brooklyn
    Retrieve success


    About Info:
    Name          :VMware ESX Server
    FullName      :VMware ESX Server 3.5.0 build-64607
    Vendor        :VMware, Inc.
    version       :3.5.0
    build         :64607
    osType        :vmnix-x86
    productLineId :esx
    apiType       :HostAgent
    apiVersion    :2.5.0


    Login success, the details are:
    User: root
    Full Name: root
    Locale: en
    Retrieved Properties: 5
    VM name: rhel5
    VM name: Solaris_VM
    VM name: Pattar_rhel4
    VM name: winxp
    VM name: winserver2k8

    Now Logging out
    Logout succesful
    bash-2.05b$

    Thanks a lot!!


    I followed the same steps as listed in  this post but I am unable to get past the "vim.fault.NotAuthenticated,  for ref=ha-folder-root" error. I am working with ESX 4.0 and have no  issues accessing it via SDK using the Java samples. For my project I  need to build an application in C++ to manage the ESX hosts. I am able  to retrieve the ServiceContent and login to the host however my attempts  to retrieve the Virtual Machines on the host is failing with following  error on the host (hostd-0.log file):

    2009-06-2309:12:00.812 F6430B90 info 'ha-eventmgr' Event 123 : User root@127.0.0.1logged in
    --2009-06-23 09:12:00.817 F6430B90 error 'App'-- _FetchPropertyPath got  unexpected error of type N3Vim5Fault16NotAuthenticated9ExceptionE:  vim.fault.NotAuthenticated, for ref=ha-folder-root, path=childEntity


    All the  application components are built with -DWITH_OPENSSL and -DWITH_COOKIES  flags. I have attached the source file (retrieveVMs.cpp) for this  program.


    Am I doing something wrong in the client code or is this some  permissions issue I need to fix on the host? I have another C++ sample  program based on the SimpleClient.java sample form SDK and that results  in same error.


    Thanks!


    Probably is from permissions. I tested your code (with some modifications) and it seems is working. Look here http://www.randombugs.com/linux/vmware-webservice-gsoap.html to see a version of your code adapted for VImBinding add with some extra features.



    The issue I posted related to authenction was related to cookies. I was  building against libgsoapssl++ when I saw this issue. Building with the  gsoap source (stdsoap2.cpp) and -DWITH_COOKIES flag resolved the issue.

    Your post in randombugs is a good starting point for someone working with VMWare Webservices SDK.

    Thanks!


    Anyone make any progress on the gsoap with ssl certificate validation  on ESX 4.0?  I haven't dug in too deep but was playing with it a bit on  Friday as time permitted.


    Did you see that: http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc13.1

    soap_init(&soap);
    soap_ssl_server_context(&soap,  SOAP_SSL_DEFAULT,  "server.pem",     /*  keyfile: required when server must authenticate to clients (see SSL docs  on how to obtain this file) /
    *      "password",     /* password to read the key file /
    *       "cacert.pem",     /* optional cacert file to store trusted certificates /
    *      NULL,          /* optional capath to directory with trusted certificates /
    *      "dh512.pem",     /* DH file, if NULL use RSA /
    *      NULL,          /* if randfile!=NULL: use a file with random data to seed randomness /
    *       NULL          /* optional server identification to enable SSL session cache (must be a unique name) */
    ))

    I've seen it.  Have you run your code against ESX 4.0 servers?  If so, post up your gsoap, openssl and OS version please.


    Ok, I think I found the issue with the certificates.

    The program used to generate the certs (/sbin/generate-certificates) is  deleting what I believe is the intermediate certificate.  You can see  the error when you run openssl verify /etc/vmware/ssl/rui.crt.  However, I commented out the rm commands at the end /sbin/generate-certificates script and then combined the files /etc/vmware/ssl/ca.crt and /etc/vmware/ssl/rui.crt into a new file (after deleting my old rui.crt and rui.key files).  This is then cleanly verified with openssl verify /etc/vmware/ssl/combined.crt.   This certificate will work with VMware ESX 4.0 and gSoap.  This is  likely to come up with other applications that do not allow you to trust  a certificate with an intermediate trust or strictly checks for an  intermediary.

    I'd chalk this one upto a bug personally.  The ca.crt and rui.crt should  be concatenated into a single file before cleaning up.  This probably  won't affect anyone not doing server certificate validation and/or  clients that use keychains in the OS (which will probably let you  implicitly trust the certificate even without its intermediary).

    I just couldn't let this one go, even though I'm not using gsoap as much lately.

    This document was generated from the following thread: Problems connecting to VI SDK using C++/gsoap-2.7