ovh / python-ovh

Thin wrapper around OVH's APIs. Handles all the hard work including credential creation and requests signing.
https://pypi.org/project/ovh/
Other
278 stars 76 forks source link

Sending data structure as parameter #45

Closed antoineco closed 7 years ago

antoineco commented 7 years ago

first of all "chapeau" for this module, it's really great 👍 🎩

The post/get/put/delete methods do not seem to translate data structures passed as parameters properly.

For example, the POST /cloud/project/{serviceName}/instance API endpoint accepts a networks parameter of type cloud.instance.NetworkParams[], which as Raw data translates to something like:

{
  "flavorId": "eeb4ccc9-faa0-4afb-955e-6a0224f93055",
  "imageId": "40b6d3ca-ab2a-48e3-b07d-c707f13a0115",
  "monthlyBilling": false,
  "name": "foo",
  "networks": [
    {
      "networkId": "ab_cdef_0"
    }
  ],
  "region": "GRA3"
}

However passing a list of dict as a parameter to the aforementioned method doesn't seem to do the trick, neither does a string:

networks = [ { 'networkId': 'ab_cdef_0' } ]
# or
networks = str([ { 'networkId': 'ab_cdef_0' } ])

c.post('/cloud/project/{}/instance'.format(service_name),
         name='foo',
         flavorId='eeb4ccc9-faa0-4afb-955e-6a0224f93055',
         imageId='40b6d3ca-ab2a-48e3-b07d-c707f13a0115',
         networks=networks,
         region='GRA3'
      )
400
Invalid JSON received 

edited 23/03 11:20

I played a little bit in the console and here are my findings:

from ovh.client import Client
c = Client()

data = { 'key1': 'value1', 'key2': [ { 'key': 'value' } ] }
c._canonicalize_kwargs(data)

>>> {'key1': 'value1', 'key2': [{'key': 'value'}]}

It's a effectively a dict which structure looks valid to me.

antoineco commented 7 years ago

I managed to pass my data in the Body using the call() method. I think it would be very useful to be able to pass data in the higher level methods as well.

yadutaf commented 7 years ago

Hmm, this looks strange. The "post" function uses _canonicalize_kwargs, not _prepare_query_string so that the first attempt with the list of duct should work. The example with str has no chance to work, you need to use the jdon module or an equivalent to produce a JSON :)

Can you give an example of working code ?

antoineco commented 7 years ago

You're right, sorry, _prepare_query_string() is not what I meant to test in this context. I just updated my original post.

The str() example was rubbish as well, because raw_call() invokes json.dumps() on the final data. Just ignore this offence :)

My working code is available here: main.py. Some variables used in the body are undefined in the code I committed (I want to grab them from the API later on), for my tests I used:

 flavorId = 'eeb4ccc9-faa0-4afb-955e-6a0224f93055' # s1-2
 imageId = '40b6d3ca-ab2a-48e3-b07d-c707f13a0115' # CoreOS stable
 region = 'GRA3'
antoineco commented 7 years ago

I suspect something was wrong with my instance data (cloud-init) in the first place, and not the network list, because if I get back to the example I posted yesterday it works just fine.

It went somehow unnoticed because I did not include it in the example, however after some further testing it appears that post() passes the following (valid) kwargs to call():

{'name': 'foo', 'flavorId': 'eeb4ccc9-faa0-4afb-955e-6a0224f93055', 'imageId': '40b6d3ca-ab2a-48e3-b07d-c707f13a0115', 'networks': [{'networkId': 'ab_cdef_0'}], 'region': 'GRA3', 'data': '#cloud-config\ncoreos:\n update:\n group: alpha\n'}

...and the instance is effectively built.

I'll close this for now and reopen if I manage to reproduce again.