UpCloudLtd / upcloud-python-api

Python client for UpCloud's API
MIT License
53 stars 21 forks source link

Creating server from custom template results in "upcloud_api.errors.UpCloudAPIError: UNKNOWN_ATTRIBUTE The request body contains an unknown attribute zone." #84

Closed anton-molyboha closed 3 years ago

anton-molyboha commented 3 years ago

The call to storage.to_dict() in server.py line 332 results in a dictionary that includes a "zone" attribute. This dictionary is then used as a storage description for the server to be created.

This "zone" attribute inside the storage description seems to be the attribute that causes the error: removing it from the request causes the request to succeed. Note: I specified the server's zone separately.

The code that should reproduce the error:

import upcloud_api
import random

_manager = upcloud_api.CloudManager(login, password)
_manager.authenticate()
storage = _manager.get_storage(template_uuid)
srv = upcloud_api.Server(
    plan = "1xCPU-2GB",
    hostname = "testpackage{:06}".format(random.randrange(1000000)),
    zone = storage.zone,
    storage_devices = [storage],
)
_server = _manager.create_server(srv)
ajmyyra commented 3 years ago

Hi there,

Thank you for asking about this! It seems that you're trying to use a response object as a storage object in your request. While this can work by removing the extra field(s), it isn't something we can recommend as response objects can change in future versions and we don't consider these breaking changes (thus, can happen within minor version).

I'd suggest doing something like this instead in a more future-proof manner:

import upcloud_api
import random

_manager = upcloud_api.CloudManager(login, password)
_manager.authenticate()
my_template = _manager.get_storage(template_uuid)
srv = upcloud_api.Server(
    plan = "1xCPU-2GB",
    hostname = "testpackage{:06}".format(random.randrange(1000000)),
    zone = my_template.zone,
    storage_devices = [upcloud_api.Storage(os=my_template.uuid, size=my_template.size)],
)
_server = _manager.create_server(srv)
anton-molyboha commented 3 years ago

Thank you for the clarification! Indeed, seeing that the type returned by get_storage() and the type accepted by Server constructor are the same, I thought the get_storage() result can be given directly to the Server constructor.

Your suggestion works for me, I am going to use it. Thanks again for the detailed answer.