perlquestion
lahfordie
<p> 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:</p>
<code>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 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>
</code>
<p> 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:</p>
<code>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 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>
<ns1:RunLocatorResponse xmlns="urn:RunLocator" xmlns:ns1="http://tempuri.org/AWEService/API/WASAPI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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>
</code>
<p> 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. </p>
<p>Here is the WSDL, with the actual server name replaced by www.example.com: </p>
<code>
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/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://schemas.xmlsoap.org/wsdl/mime/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://tempuri.org/AWEService/API/WASAPI">
<types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.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="locatorSearchFieldsIdentification" 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:string"/>
<s:element minOccurs="0" maxOccurs="1" name="WAS_password" type="s:string"/>
</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:string"/>
<s:element minOccurs="0" maxOccurs="1" name="MANUFACTURER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="MODEL" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="ASSET_NUMBER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="BARCODE_NUMBER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="SERIAL_NUMBER" type="s:string"/>
<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:string"/>
</s:sequence>
</s:complexType>
<s:element name="RunLocatorResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="10000" name="RunLocatorResult" type="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:string"/>
<s:element minOccurs="0" maxOccurs="1" name="MANUFACTURER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="MODEL" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="ASSET_NUMBER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="BARCODE_NUMBER" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="SERIAL_NUMBER" type="s:string"/>
<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:string"/>
<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/RunLocator" 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.asmx?w_v=1&w_s=VISTA&w_o=LocateDevice"/>
</port>
</service>
</definitions>
</code>
<p>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.</p>
<p>This is the current iteration of what I'm attempting:</p>
<code>
my $wsdl = 'https://www.example.com/VISTA/wsdl/LocateDevice_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(DEVICE_NAME => 'MY_SERVER'));
SOAP::Lite->import(+trace => 'all');
my $result = $lite->RunLocator(@params);
</code>
<p>Here's the trace:</p>
<code>
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(0x20463b8)
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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http://tempuri.org/AWEService/API/WASAPI" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><s0:RunLocator><userIdentification xsi:nil="true" xsi:type="s0:UserIdentificationStructure" /><locatorSearchFieldsIdentification xsi:nil="true" xsi:type="s0:LocatorSearchFieldsStructure" /><DEVICE_NAME xsi:type="xsd:string">prodh20</DEVICE_NAME></s0:RunLocator></soap:Body></soap:Envelope>
SOAP::Transport::HTTP::Client::send_receive: HTTP::Response=HASH(0x2036400)
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 500 Internal Server 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/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><soap12:Upgrade xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"><soap12:SupportedEnvelope qname="soap:Envelope" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" /><soap12:SupportedEnvelope qname="soap12:Envelope" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" /></soap12:Upgrade></soap:Header><soap:Body><soap:Fault><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:Envelope>
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: ()
</code>
<p>Yes, I see the version mismatch messages, but I'm more concerned about the generated envelope not having the correct structure.</p>
<p><strong>SOLVED</strong> thanks to <strong>poj</strong>. Code is below, slightly adapted for public posting:</p>
<code>
#!/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);
}
</code>