tags:

views:

92

answers:

2

I try to talk to a load balancer (Zeus ZXTM) with python:

a = client.factory.create('StringArrayArray')
b = client.factory.create('StringArray')
b.value = ['node01:80',]
a.value = [b,]
client.service.addDrainingNodes(['my pool'], a)

But I get the following error:

suds.WebFault: Server raised fault: 'Not an ARRAY reference at /usr/local/zeus/zxtmadmin/lib/perl/Zeus/ZXTM/SOAPBase.pm line 772.

Extract of the schema definition:

    <types>
        <xsd:schema targetNamespace='http://soap.zeus.com/zxtm/1.0/'
            xmlns='http://www.w3.org/2001/XMLSchema'
            xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'
            xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'&gt;

            <xsd:complexType name="StringArray">
                <xsd:complexContent>
                    <xsd:restriction base='SOAP-ENC:Array'>
                        <xsd:attribute ref='SOAP-ENC:arrayType' wsdl:arrayType='xsd:string[]'/>
                    </xsd:restriction>
                </xsd:complexContent>
            </xsd:complexType>

            <xsd:complexType name="StringArrayArray">
                <xsd:complexContent>
                    <xsd:restriction base='SOAP-ENC:Array'>
                        <xsd:attribute ref='SOAP-ENC:arrayType' wsdl:arrayType='zeusns:StringArray[]'/>
                    </xsd:restriction>
                </xsd:complexContent>
            </xsd:complexType>
        </xsd:schema>
    </types>

    <message name="addDrainingNodesRequest">
        <part name="names" type="zeusns:StringArray" />
        <part name="values" type="zeusns:StringArrayArray" />
    </message>

    <message name="addDrainingNodesResponse"></message>

    <portType name="PoolPort">

        <operation name="addDrainingNodes">
            <documentation>
                Add nodes to the lists of draining nodes, for each of the named pools.
            </documentation>

            <input message="zeusns:addDrainingNodesRequest"/>
            <output message="zeusns:addDrainingNodesResponse"/>
        </operation>
    </portType>
</definitions>

I also tried like this: client.service.addDrainingNodes(['my pool'], [['node01:80']]) which worked in SOAPpy but now in suds I get:

suds.WebFault: Server raised fault: 'Value isn't an array'

Comparison between what SOAPpy and what suds sends:

SOAPpy (works):

<ns1:addDrainingNodes xmlns:ns1="http://soap.zeus.com/zxtm/1.0/Pool/" SOAP-ENC:root="1">
    <v1 SOAP-ENC:arrayType="xsd:string[1]" xsi:type="SOAP-ENC:Array">
        <item>my pool</item>
    </v1>
    <v2 SOAP-ENC:arrayType="xsd:list[1]" xsi:type="SOAP-ENC:Array">
        <item SOAP-ENC:arrayType="xsd:string[1]" xsi:type="SOAP-ENC:Array">
            <item>node01:80</item>
        </item>
    </v2>
</ns1:addDrainingNodes>

suds (doesn't work):

<ns4:addDrainingNodes>
    <names xsi:type="ns0:StringArray" ns3:arrayType="ns2:string[1]">
        <item xsi:type="ns2:string">my pool</item>
    </names>
    <values xsi:type="ns0:StringArrayArray" ns3:arrayType="ns0:StringArray[1]">
        <item xsi:type="ns2:string">node01:80</item>
    </values>
</ns4:addDrainingNodes>

Context:

  • I'm new to suds and Soap
  • there's only the SOAP interface to the ZXTM loadbalancer
  • using python2.6 and suds 0.3.9
  • we used to use ZSI's SOAPpy, but had issues using it under python 2.6

Edit: Added suds/SOAPpy payloads

A: 

What looks strange to me is you have to explicitly construct types like 'StringArrayArray' and 'StringArray' - a smart enough SOAP client in a language like python should be able to figure this out via reflection, examining the arguments you pass to the service call. I'm guessing they're declared in the wsdl as something like "SOAP-ENC:Array" - if so they're not intended to be objects. That would also make sense with the error message "Not an ARRAY reference". Have you tried just;

a = [ ['node01:80',], ]
client.service.addDrainingNodes(['my pool'], a)

Or failing that perhaps...

a = client.factory.create('StringArrayArray')
b = ['node01:80',]
a.value = [ b, ]
client.service.addDrainingNodes(['my pool'], a)

Good luck.

HarryF
hm, no luck tried both.. Indeed, suds should be able to construct the complex types, but that somewhere fails, also now also opened a trac ticket: https://fedorahosted.org/suds/ticket/340
Philipp Keller
A: 

After trying

  • zillions of different arguments to this function
  • wsdl2py from ZSI

I found out that suds 4.0 offers plugins, that solves this case by hacking, but nonetheless I think that's a suds bug:

class FixArrayPlugin(Plugin):
    def sending(self, context):
        command = context.envelope.getChild('Body').getChildren()[0].name
        if command == 'addDrainingNodes':
            context.envelope.addPrefix('xsd', 'http://www.w3.org/1999/XMLSchema')
            values = context.envelope.getChild('Body').getChild('addDrainingNodes').getChild('values')
            values.set('SOAP-ENC:arrayType', 'xsd:list[1]')
            values.set('xsi:type', 'SOAP-ENC:Array')
            item = values[0]
            item.set('SOAP-ENC:arrayType', 'xsd:list[1]')
            item.set('xsi:type', 'SOAP-ENC:Array')

client = Client(wsdl, location=location, plugins=[FixArrayPlugin()])
a = client.factory.create('StringArrayArray')
b = client.factory.create('StringArray')
b.item = ['node01:80']
a.item = [b,]
client.service.addDrainingNodes(['my pool'], a)

I'm looking forward for this issue to be fixed, IMO this should be a one liner I'm leaving this open as I'm still interested in better alternatives

Philipp Keller