Bug 1120502

Summary: return TypeError when use sdk to get storages of a host
Product: [Retired] oVirt Reporter: 马立克 <like.ma>
Component: ovirt-engine-sdkAssignee: Juan Hernández <juan.hernandez>
Status: CLOSED CURRENTRELEASE QA Contact: Antonin Pagac <apagac>
Severity: high Docs Contact:
Priority: unspecified    
Version: 3.5CC: ecohen, gklein, iheim, rbalakri, yeylon
Target Milestone: ---   
Target Release: 3.5.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard: infra
Fixed In Version: ovirt-engine-sdk-python-3.5.0.3-1 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2014-10-17 12:24:02 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: Infra RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description 马立克 2014-07-17 05:26:36 UTC
Description of problem:
When i use the python sdk to get storage of a host, it returns a TypeError:
__init__() got multiple values for keyword argument 'context'

Version-Release number of selected component (if applicable):


How reproducible:
Always

Steps to Reproduce:
1.Make sure there is a Up host in the datacenter
2.Supposed the uuid of the host is "11111111-1111-1111-1111-111111111111"
3.Use following codes to get storage of the host:
from ovirtsdk.xml import params
from ovirtsdk.api import API

api = API(url='127.0.0.1/api',username='admin@internal',password='password',insecure=True,persistent_auth=False)
storage = api.hosts.get(id='11111111-1111-1111-1111-111111111111').storage.list()

Actual results:
it returns TypeError: __init__() got multiple values for keyword argument 'context'

Expected results:
it returns storage of the host

Additional info:
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    storages = api.hosts.get(id='11111111-1111-1111-1111-111111111111').storage.list()
  File "/root/sdkforbuild/src/ovirtsdk/infrastructure/brokers.py", line 10400, in list
    context=self.context
  File "/root/sdkforbuild/src/ovirtsdk/utils/parsehelper.py", line 148, in toSubCollection
    new_coll.append(ParseHelper.toSubType(fromItem, toType, parent, **kwargs))
  File "/root/sdkforbuild/src/ovirtsdk/utils/parsehelper.py", line 128, in toSubType
    return toType(parent, fromItem, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'context'

Comment 1 Juan Hernández 2014-07-17 09:21:37 UTC
This happens because we have a sub collection of hosts that doesn't follow the convention of having names for collections in plural and names for entities in singular:

  /hosts/{host:id}/storage

Note that the name is *storage* instead of *storages*.

The generator of the SDK assumes this convention, and in this particular case it generates a HostStorage class for the entity that overwrites the same HostStorage class for the collection. As a result when the SDK tries to create the instance for the collection it is actually creating an instance of the entity, thus the constructor (the __init__() method of the HostStorage class) receives wrong arguments.

The ideal fix for this would be to rename that collection to "storages", but we can't do that as it would break backwards compatibility. Instead of that we will need to introduce an exception in the code generator, which isn't trivial.

Comment 2 马立克 2014-07-17 10:08:19 UTC
(In reply to Juan Hernández from comment #1)
> This happens because we have a sub collection of hosts that doesn't follow
> the convention of having names for collections in plural and names for
> entities in singular:
> 
>   /hosts/{host:id}/storage
> 
> Note that the name is *storage* instead of *storages*.
> 
> The generator of the SDK assumes this convention, and in this particular
> case it generates a HostStorage class for the entity that overwrites the
> same HostStorage class for the collection. As a result when the SDK tries to
> create the instance for the collection it is actually creating an instance
> of the entity, thus the constructor (the __init__() method of the
> HostStorage class) receives wrong arguments.
> 
> The ideal fix for this would be to rename that collection to "storages", but
> we can't do that as it would break backwards compatibility. Instead of that
> we will need to introduce an exception in the code generator, which isn't
> trivial.

OK, i see. Thank you for your reply.

Comment 3 Juan Hernández 2014-07-17 10:16:51 UTC
The proposed patch should fix the issue in the code generator. Once merged the SDK needs to be regenerated. I see that you are running the SDK from the source, so you may want to apply the patch and re-generate yourself. You can also apply the following patch to the brokers.py file:

diff --git a/src/ovirtsdk/infrastructure/brokers.py b/src/ovirtsdk/infrastructure/brokers.py
index e8fb278..3a4462e 100644
--- a/src/ovirtsdk/infrastructure/brokers.py
+++ b/src/ovirtsdk/infrastructure/brokers.py
@@ -7910,7 +7910,7 @@ class Host(params.Host, Base):
         self.numanodes = HostNumaNodes(self, context)
         self.permissions = HostPermissions(self, context)
         self.statistics = HostStatistics(self, context)
-        self.storage = HostStorage(self, context)
+        self.storage = HostStorages(self, context)
         self.tags = HostTags(self, context)
 
     def __new__(cls, host, context):
@@ -9318,7 +9318,29 @@ class HostStatistics(Base):
             context=self.context
         )
 
-class HostStorage(Base):
+class HostStorage(params.HostStorage, Base):
+    def __init__(self, host, storage, context):
+        Base.__init__(self, context)
+        self.parentclass = host
+        self.superclass  =  storage
+
+        #SUB_COLLECTIONS
+    def __new__(cls, host, storage, context):
+        if storage is None: return None
+        obj = object.__new__(cls)
+        obj.__init__(host, storage, context)
+        return obj
+
+    def __getProxy(self):
+        proxy = context.manager[self.context].get('proxy')
+        if proxy:
+            return proxy
+        #This may happen only if sdk was explicitly disconnected
+        #using .disconnect() method, but resource instance ref. is
+        #still available at client's code.
+        raise DisconnectedError
+
+class HostStorages(Base):
 
     def __init__(self, host , context):
         Base.__init__(self, context)

Comment 4 马立克 2014-07-17 10:28:37 UTC
(In reply to Juan Hernández from comment #3)
> The proposed patch should fix the issue in the code generator. Once merged
> the SDK needs to be regenerated. I see that you are running the SDK from the
> source, so you may want to apply the patch and re-generate yourself. You can
> also apply the following patch to the brokers.py file:
> 
> diff --git a/src/ovirtsdk/infrastructure/brokers.py
> b/src/ovirtsdk/infrastructure/brokers.py
> index e8fb278..3a4462e 100644
> --- a/src/ovirtsdk/infrastructure/brokers.py
> +++ b/src/ovirtsdk/infrastructure/brokers.py
> @@ -7910,7 +7910,7 @@ class Host(params.Host, Base):
>          self.numanodes = HostNumaNodes(self, context)
>          self.permissions = HostPermissions(self, context)
>          self.statistics = HostStatistics(self, context)
> -        self.storage = HostStorage(self, context)
> +        self.storage = HostStorages(self, context)
>          self.tags = HostTags(self, context)
>  
>      def __new__(cls, host, context):
> @@ -9318,7 +9318,29 @@ class HostStatistics(Base):
>              context=self.context
>          )
>  
> -class HostStorage(Base):
> +class HostStorage(params.HostStorage, Base):
> +    def __init__(self, host, storage, context):
> +        Base.__init__(self, context)
> +        self.parentclass = host
> +        self.superclass  =  storage
> +
> +        #SUB_COLLECTIONS
> +    def __new__(cls, host, storage, context):
> +        if storage is None: return None
> +        obj = object.__new__(cls)
> +        obj.__init__(host, storage, context)
> +        return obj
> +
> +    def __getProxy(self):
> +        proxy = context.manager[self.context].get('proxy')
> +        if proxy:
> +            return proxy
> +        #This may happen only if sdk was explicitly disconnected
> +        #using .disconnect() method, but resource instance ref. is
> +        #still available at client's code.
> +        raise DisconnectedError
> +
> +class HostStorages(Base):
>  
>      def __init__(self, host , context):
>          Base.__init__(self, context)

Thank you very much. The patch works well.

Comment 5 Juan Hernández 2014-07-18 07:43:27 UTC
The fix for the code generator has been merged, now the SDK needs to be regenerated.

Comment 6 Antonin Pagac 2014-09-03 12:34:33 UTC
Verified.

sdk: ovirt-engine-sdk-python-3.5.0.5
ovirt: oVirt Engine Version: 3.5.0-0.0.master.20140821064931.gitb794d66.el6

Comment 7 Sandro Bonazzola 2014-10-17 12:24:02 UTC
oVirt 3.5 has been released and should include the fix for this issue.