vmware-archive / vsphere-automation-sdk-.net

[DEPRECATED] Please see README. C# samples, language bindings, and API reference documentation for vSphere, VMC, and NSX-T using the VMware REST API
MIT License
68 stars 20 forks source link

Issue: Error returned by expat parser: not well-formed (invalid token) #17

Closed fkleuver closed 5 years ago

fkleuver commented 5 years ago

Similar issues with no (useful) answers:

Description

When trying to run any of the samples, I get a 500 server error:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<soapenv:Fault>
  <faultcode>ClientFaultCode</faultcode>
  <faultstring>
Error returned by expat parser: not well-formed (invalid token)

while parsing HTTP request before method was determined
at line 1, column 0</faultstring>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>

This happens regardless of which sample I run. It happens during authentication (session.Create()) as well as when directly invoking any of the services (after retrieving a session ID with the REST API).

I have been able to work with the same "endpoints" via both PowerCLI and the REST api. I noticed that the PowerCLI is calling the same endpoint (myhostname.com/sdk) as the .NET SDK, but PowerCLI sends XML whereas the .NET SDK sends JSON. Pairing that with the error message, it seems that the server simply doesn't accept JSON.

Environment

Steps or code snippet to reproduce

Actual behavior

The server errors out with a 500 code.

Expected behavior

Either for the sample to succeed by default, or for there to be some option in the SDK to switch to XML (if that is indeed the issue).

vinpai commented 5 years ago

Thats strange. In the link you shared, it was mentioned that the issue was happening due to special characters in the password. Did you check that ?

fkleuver commented 5 years ago

My password consists only of alphanumeric characters.

Considering the at line 1, column 0 part of the message, I get the strong impression that the server simply does not like the JSON. That particular piece of the payload would be a < if it's XML or a { if it's JSON. I don't see any other reason why it would reject the very first character of the payload.

The request header does specify Content-Type: application/json. The administrator of our vSphere server believes the SDK should be using SOAP. I think it at least should be configurable.

I should point out that I've manually extracted the VMware.Vim.dll from the PowerCLI and am using that in my .NET app right now, and it works. It sends XML as well. I wonder why this SDK deviates from that?

vinpai commented 5 years ago

The new REST based VM APIs do not use SOAP. So, thats not the issue. The older Vim APIs are SOAP based, hence you see SOAP in the request. Btw, the endpoint for the new VM apis is /api and not /sdk as mentioned by you above. Can you make sure you are using /api and try running sample ?

fkleuver commented 5 years ago

I assumed this SDK was talking to the old API because it exposes APIs that the REST API does not expose, such as cloning VM / creating from template. That is the API I need.

In any case, our VMware team has exposed the REST api on /rest. There is nothing on /api afaik.

Now take this snippet from a helper class that I wrote, based on the SDK example:

protected StubFactory StubFactory { get; }
protected StubConfiguration StubConfig { get; }
protected Session Session { get; }

public VMwareManager(string host, string username, string password)
{
    ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
    ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

    var connectionFactory = new ProtocolConnectionFactory();
    var connection = connectionFactory.GetConnection(Protocol.Http, $"https://{host}/rest", new CspParameters());
    var apiProvider = connection.GetApiProvider();
    StubFactory = new StubFactory(apiProvider);

    var userSecurityContext = new UserPassSecurityContext(username, password.ToCharArray());
    StubConfig = new StubConfiguration(userSecurityContext);
    var stubSession = CreateService<Session>();
    var sessionId = stubSession.Create();

    var sessionSecurityContext = new SessionSecurityContext(sessionId);
    StubConfig.SetSecurityContext(sessionSecurityContext);

    Session = CreateService<Session>();
}

public T CreateService<T>() where T : IService
{
    return StubFactory.CreateStub<T>(StubConfig);
}

This gives me a 404 on var sessionId = stubSession.Create(); The sample/SDK does not call the correct endpoint by default.

If I change the line that instantiates the connection to:

var connection = connectionFactory.GetConnection(Protocol.Http, $"https://{host}/rest/com/vmware/cis/session", new CspParameters());

Then I get a 401.

However, if I manually call https://{{vc}}/rest/com/vmware/cis/session via POSTman with the appropriate credentials, all works fine. I get a session ID and I can proceed to call other REST apis.

Either the SDK is somehow calling the wrong endpoints or our VMware administration team did something unusual with the endpoints.

The real question

The only reason I'm trying to use the SDK is because the REST endpoints don't appear to expose CloneVM_Task (and a whole bunch of other important things).

If you say the SDK uses the REST endpoints, and all of it is supposed to work, then that tells me there are some undocumented REST endpoints.

Are you telling me that you can do CloneVM_Task via the REST API? If that is the case, I don't need the SDK in the first place and I'll just call that directly. That would actually be fantastic since that's what I've been trying to do from the beginning!

vinpai commented 5 years ago

There are 2 SDK endpoints - The old management sdk use /sdk. This is for the clone operation you mentioned above and any other api which is currently not included in the new sdk.

The new sdk use the /api endpoint. They dont include all APIs at the moment as some of them are still being developed. The /api is not a rest endpoint, it is just an endpoint for the new SDK.

You can make plain rest calls for the new APIs at /rest as described above.

fkleuver commented 5 years ago

Right, so there are in fact 3 different endpoints for different purposes. That clears things up a bit. I don't think we have the /api endpoints enabled so I'll ask about that. And this /api endpoint is JSON?

vinpai commented 5 years ago

The /api endpoint is for making api calls via the new sdk. And yes, it uses json.