I'm trying to convert this known working SOAP call to SOAP::Lite in Perl and having difficulty doing so. The examples and tutorials I'm seeing are all for simpler use cases, and I haven't found what I need in the perldoc. So many options... I have a WSDL available to use, and the method to call seems to be RunLocator. This is an example of a request I would make in SoapUI:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envel
+ope/" xmlns:was="http://tempuri.org/AWEService/API/WASAPI">
<soapenv:Header/>
<soapenv:Body>
<was:RunLocator>
<was:userIdentification>
<was:WAS_userName>WS_Decom</was:WAS_userName>
<was:WAS_password>PASSWORD</was:WAS_password>
</was:userIdentification>
<was:locatorSearchFieldsIdentification>
<was:DEVICE_NAME>prodh20</was:DEVICE_NAME>
</was:locatorSearchFieldsIdentification>
</was:RunLocator>
</soapenv:Body>
</soapenv:Envelope>
Note that in SoapUI (or sending that request body with curl), I would fill out the authentication and DEVICE_NAME and leave the rest empty. It will send back a response in the format:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envel
+ope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://
+www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:RunLocatorResponse xmlns="urn:RunLocator" xmlns:ns1="http://tempu
+ri.org/AWEService/API/WASAPI" xmlns:xsd="http://www.w3.org/2001/XMLSc
+hema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:RunLocatorResult>
<ns1:DEVICE_NAME>prodh20</ns1:DEVICE_NAME>
<ns1:MANUFACTURER>HEWLETT PACKARD</ns1:MANUFACTURER>
<ns1:MODEL>PROLIANT DL380P GEN8</ns1:MODEL>
<ns1:ASSET_NUMBER></ns1:ASSET_NUMBER>
<ns1:BARCODE_NUMBER></ns1:BARCODE_NUMBER>
<ns1:SERIAL_NUMBER>(SERIAL)</ns1:SERIAL_NUMBER>
<ns1:BUILDING>(BUILDING)</ns1:BUILDING>
<ns1:FLOOR>(FLOOR)</ns1:FLOOR>
<ns1:SPACE_ID>(SPACE_ID)</ns1:SPACE_ID>
<ns1:GRID_LOCATION>(LOC)/ns1:GRID_LOCATION>
<ns1:RACK_NAME>(RACK)</ns1:RACK_NAME>
<ns1:RACK_MOUNT_SIDE>FRONT</ns1:RACK_MOUNT_SIDE>
<ns1:BOTTOM_RACK_UNIT>7</ns1:BOTTOM_RACK_UNIT>
<ns1:HEIGHT>3.5</ns1:HEIGHT>
</ns1:RunLocatorResult>
</ns1:RunLocatorResponse>
</soapenv:Body>
</soapenv:Envelope>
Parenthesized fields contain values which I've removed for posting here. I've tried many different techniques with SOAP::Lite adapted from examples online, the latest involving SOAP::Header and SOAP::Data. Can someone please point me in the right direction for how I should be approaching this? And I presume I'm going to need SOM to parse the result, if I can get that far.
Here is the WSDL, with the actual server name replaced by www.example.com:
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:
+soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.o
+rg/2001/XMLSchema" xmlns:s0="http://tempuri.org/AWEService/API/WASAPI
+" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm=
+"http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://sch
+emas.xmlsoap.org/wsdl/mime/" xmlns="http://schemas.xmlsoap.org/wsdl/"
+ targetNamespace="http://tempuri.org/AWEService/API/WASAPI">
<types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempu
+ri.org/AWEService/API/WASAPI">
<s:element name="RunLocator">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="userIdentification" type=
+"s0:UserIdentificationStructure"/>
<s:element minOccurs="1" maxOccurs="1" name="locatorSearchFieldsIdenti
+fication" type="s0:LocatorSearchFieldsStructure"/>
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="UserIdentificationStructure">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="WAS_userName" type="s:str
+ing"/>
<s:element minOccurs="0" maxOccurs="1" name="WAS_password" type="s:str
+ing"/>
</s:sequence>
</s:complexType>
<s:complexType name="LocatorSearchFieldsStructure">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="RACK_NAME" type="s:string
+"/>
<s:element minOccurs="0" maxOccurs="1" name="DEVICE_NAME" type="s:stri
+ng"/>
<s:element minOccurs="0" maxOccurs="1" name="MANUFACTURER" type="s:str
+ing"/>
<s:element minOccurs="0" maxOccurs="1" name="MODEL" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="ASSET_NUMBER" type="s:str
+ing"/>
<s:element minOccurs="0" maxOccurs="1" name="BARCODE_NUMBER" type="s:s
+tring"/>
<s:element minOccurs="0" maxOccurs="1" name="SERIAL_NUMBER" type="s:st
+ring"/>
<s:element minOccurs="0" maxOccurs="1" name="BUILDING" type="s:string"
+/>
<s:element minOccurs="0" maxOccurs="1" name="FLOOR" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="SPACE_ID" type="s:string"
+/>
<s:element minOccurs="0" maxOccurs="1" name="GRID_LOCATION" type="s:st
+ring"/>
</s:sequence>
</s:complexType>
<s:element name="RunLocatorResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="10000" name="RunLocatorResult" typ
+e="s0:LocatorResultInfo"/>
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="LocatorResultInfo">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="DEVICE_NAME" type="s:stri
+ng"/>
<s:element minOccurs="0" maxOccurs="1" name="MANUFACTURER" type="s:str
+ing"/>
<s:element minOccurs="0" maxOccurs="1" name="MODEL" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="ASSET_NUMBER" type="s:str
+ing"/>
<s:element minOccurs="0" maxOccurs="1" name="BARCODE_NUMBER" type="s:s
+tring"/>
<s:element minOccurs="0" maxOccurs="1" name="SERIAL_NUMBER" type="s:st
+ring"/>
<s:element minOccurs="0" maxOccurs="1" name="BUILDING" type="s:string"
+/>
<s:element minOccurs="0" maxOccurs="1" name="FLOOR" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="SPACE_ID" type="s:string"
+/>
<s:element minOccurs="0" maxOccurs="1" name="GRID_LOCATION" type="s:st
+ring"/>
<s:element minOccurs="0" maxOccurs="1" name="RACK_NAME" type="s:string
+"/>
<s:element minOccurs="0" maxOccurs="1" name="RACK_MOUNT_SIDE" type="s:
+string"/>
<s:element minOccurs="0" maxOccurs="1" name="BOTTOM_RACK_UNIT" type="s
+:double"/>
<s:element minOccurs="0" maxOccurs="1" name="HEIGHT" type="s:double"/>
</s:sequence>
</s:complexType>
</s:schema>
</types>
<message name="RunLocatorSoapIn">
<part name="parameters" element="s0:RunLocator"/>
</message>
<message name="RunLocatorSoapOut">
<part name="parameters" element="s0:RunLocatorResponse"/>
</message>
<portType name="RunLocatorSoap">
<operation name="RunLocator">
<documentation>
This function provides the XML interface to create a form
</documentation>
<input message="s0:RunLocatorSoapIn"/>
<output message="s0:RunLocatorSoapOut"/>
</operation>
</portType>
<binding name="RunLocatorSoap" type="s0:RunLocatorSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="
+document"/>
<operation name="RunLocator">
<soap:operation soapAction="http://tempuri.org/AWEService/API/WASAPI/R
+unLocator" style="document"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="RunLocator">
<port name="RunLocator" binding="s0:RunLocatorSoap">
<soap:address location="https://www.example.com/WAS/API/AWEService.asm
+x?w_v=1&w_s=VISTA&w_o=LocateDevice"/>
</port>
</service>
</definitions>
EDIT: I think a big part of what's tripping me up is the two separate element areas under the RunLocator element. The method requires "userIdentification" and "locatorSearchFieldsIdentification" separately.
This is the current iteration of what I'm attempting:
my $wsdl = 'https://www.example.com/VISTA/wsdl/LocateDevi
+ce_RunLocator.wsdl';
my $lite = SOAP::Lite
->service($wsdl)
->ns('was');
my @params = (SOAP::Data->name(WAS_userName => 'MY_USER'),
+SOAP::Data->name(WAS_password => 'MY_PASSWD'), SOAP::Data->name(DEVIC
+E_NAME => 'MY_SERVER'));
SOAP::Lite->import(+trace => 'all');
my $result = $lite->RunLocator(@params);
Here's the trace:
SOAP::Transport::HTTP::Client::new: ()
SOAP::Lite::call: ()
SOAP::Serializer::envelope: ()
SOAP::Serializer::envelope: RunLocator SOAP::Data=HASH(0x2063270) SOAP
+::Data=HASH(0x2063150) SOAP::Data=HASH(0x1b2a2a0)
SOAP::Data::new: ()
SOAP::Data::new: ()
SOAP::Data::new: ()
SOAP::Data::new: ()
SOAP::Data::new: ()
SOAP::Transport::HTTP::Client::send_receive: HTTP::Request=HASH(0x2046
+3b8)
SOAP::Transport::HTTP::Client::send_receive: POST https://www.example.
+com/WAS/API/AWEService.asmx?w_v=1&w_s=VISTA&w_o=LocateDevice HTTP/1.1
Accept: text/xml
Accept: multipart/*
Accept: application/soap
Content-Length: 832
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/AWEService/API/WASAPI/RunLocator"
<?xml version="1.0" encoding="UTF-8"?><soap:Envelope soap:encodingStyl
+e="http://schemas.xmlsoap.org/soap/encoding/" xmlns:http="http://sche
+mas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/ws
+dl/mime/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http:/
+/tempuri.org/AWEService/API/WASAPI" xmlns:soap="http://schemas.xmlsoa
+p.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/enco
+ding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:
+xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><s0:RunLoc
+ator><userIdentification xsi:nil="true" xsi:type="s0:UserIdentificati
+onStructure" /><locatorSearchFieldsIdentification xsi:nil="true" xsi:
+type="s0:LocatorSearchFieldsStructure" /><DEVICE_NAME xsi:type="xsd:s
+tring">prodh20</DEVICE_NAME></s0:RunLocator></soap:Body></soap:Envelo
+pe>
SOAP::Transport::HTTP::Client::send_receive: HTTP::Response=HASH(0x203
+6400)
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 500 Internal Ser
+ver Error
Cache-Control: private,no-cache, no-store, must-revalidate
Date: Sat, 20 May 2017 19:56:56 GMT
Server: Microsoft-IIS/7.5
Content-Length: 837
Content-Type: text/xml; charset=utf-8
...
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http:
+//schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/20
+01/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><
+soap:Header><soap12:Upgrade xmlns:soap12="http://www.w3.org/2003/05/s
+oap-envelope"><soap12:SupportedEnvelope qname="soap:Envelope" xmlns:s
+oap="http://schemas.xmlsoap.org/soap/envelope/" /><soap12:SupportedEn
+velope qname="soap12:Envelope" xmlns:soap12="http://www.w3.org/2003/0
+5/soap-envelope" /></soap12:Upgrade></soap:Header><soap:Body><soap:Fa
+ult><faultcode>soap:VersionMismatch</faultcode><faultstring>Possible
+SOAP version mismatch: Envelope namespace http://schemas.xmlsoap.org/
+wsdl/soap/ was unexpected. Expecting http://schemas.xmlsoap.org/soap/
+envelope/.</faultstring><detail /></soap:Fault></soap:Body></soap:Env
+elope>
SOAP::Deserializer::deserialize: ()
SOAP::Parser::decode: ()
SOAP::SOM::new: ()
SOAP::SOM::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Lite::DESTROY: ()
SOAP::Deserializer::DESTROY: ()
SOAP::Parser::DESTROY: ()
SOAP::Transport::DESTROY: ()
SOAP::Transport::HTTP::Client::DESTROY: ()
SOAP::Serializer::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
SOAP::Data::DESTROY: ()
Yes, I see the version mismatch messages, but I'm more concerned about the generated envelope not having the correct structure.
SOLVED thanks to poj. Code is below, slightly adapted for public posting:
#!/usr/bin/perl
use strict; use warnings;
use SOAP::Lite;
use v5.16;
my $client = SOAP::Lite
->proxy('https://www.example.com/WAS/API/AWEService.asmx?ENDPOINT'
+)
->on_action(sub { join '/', @_ })
->ns('http://tempuri.org/AWEService/API/WASAPI','was');
my %auth_parms = (
WAS_userName => 'USER',
WAS_password => 'PASSWORD',
);
my %search_parms = (
DEVICE_NAME => 'SERVER',
);
my @auth_data;
for (keys %auth_parms){
my $data = SOAP::Data->name(
$_ => $auth_parms{$_}
)->prefix('was');
push @auth_data, SOAP::Data->type('data' => $data);
}
my @search_data;
for (keys %search_parms){
my $data = SOAP::Data->name(
$_ => $search_parms{$_}
)->prefix('was');
push @search_data, SOAP::Data->type('data' => $data);
}
my $auth = SOAP::Data->name(
"userIdentification" => \@auth_data
)->prefix('was');
my $search = SOAP::Data->name(
"locatorSearchFieldsIdentification" => \@search_data
)->prefix('was');
my $header = SOAP::Data->type('xml' => '');
my $response = $client->call("RunLocator",
SOAP::Header->type('data' => $header),
SOAP::Data->type('data' => $auth),
SOAP::Data->type('data' => $search)
);
if ($response->valueof('//RunLocatorResult')) {
my $serial = $response->valueof('//SERIAL_NUMBER');
my $model = join(' ',
scalar $response->valueof('//MANUFACTURER'),
scalar $response->valueof('//MODEL')
);
my $location = join(' ',
scalar $response->valueof('//BUILDING'),
scalar $response->valueof('//FLOOR'),
scalar $response->valueof('//SPACE_ID'),
scalar $response->valueof('//GRID_LOCATION'),
scalar $response->valueof('//RACK_NAME')
);
return ($serial, $model, $location);
}