Code Monkey home page Code Monkey logo

hcloud-python's Introduction

Hetzner Cloud Python

Official Hetzner Cloud python library.

The library's documentation is available at hcloud-python.readthedocs.io, the public API documentation is available at docs.hetzner.cloud.

Usage

Install the hcloud library:

pip install hcloud

For more installation details, please see the installation docs.

Here is an example that creates a server and list them:

from hcloud import Client
from hcloud.images import Image
from hcloud.server_types import ServerType

client = Client(token="{YOUR_API_TOKEN}")  # Please paste your API token here

# Create a server named my-server
response = client.servers.create(
    name="my-server",
    server_type=ServerType(name="cx11"),
    image=Image(name="ubuntu-22.04"),
)
server = response.server
print(f"{server.id=} {server.name=} {server.status=}")
print(f"root password: {response.root_password}")

# List your servers
servers = client.servers.get_all()
for server in servers:
    print(f"{server.id=} {server.name=} {server.status=}")

For more details, please see the API reference.

You can find some more examples under the examples/ directory.

Supported Python versions

We support python versions until end-of-life.

Development

First, create a virtual environment and activate it:

make venv
source venv/bin/activate

You may setup pre-commit to run before you commit changes, this removes the need to run it manually afterwards:

pre-commit install

You can then run different tasks defined in the Makefile, below are the most important ones:

Build the documentation and open it in your browser:

make docs

Lint the code:

make lint

Run tests using the current python3 version:

make test

You may also run the tests for multiple python3 versions using tox:

tox .

Deprecations implementation

When deprecating a module or a function, you must:

  • Update the docstring with a deprecated notice:
"""Get image by name

.. deprecated:: 1.19
    Use :func:`hcloud.images.client.ImagesClient.get_by_name_and_architecture` instead.
"""
  • Raise a warning when the deprecated module or function is being used:
warnings.warn(
    "The 'hcloud.images.client.ImagesClient.get_by_name' method is deprecated, please use the "
    "'hcloud.images.client.ImagesClient.get_by_name_and_architecture' method instead.",
    DeprecationWarning,
    stacklevel=2,
)

License

The MIT License (MIT). Please see License File for more information.

hcloud-python's People

Contributors

83hd4d avatar adi146 avatar apricote avatar cenkalti avatar daroczig avatar decentral1se avatar doctorjohn avatar f7a5fcd1073c avatar fhofherr avatar hcloud-bot avatar jkreileder avatar jonasdlindner avatar jooola avatar ka7 avatar ld250 avatar lkaemmerling avatar lonvia avatar lotheac avatar mascheck avatar matias-pizarro avatar mgorny avatar navid2zp avatar renovate[bot] avatar s-m-e avatar s-t-e-v-e-n-k avatar skeen avatar usmannasir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hcloud-python's Issues

hcloud-python not recognized

Bug Report

I wanted to play around with Ansible+hcloud and pretty much copied the example from this tutorial. Executing the playbook gives an error (see below) that hcloud-python is missing even tough it is installed and pip3 show hcloud confirms it. Unfortunatly I'm not familiar with Python and don't know what the problem might be. Sorry if this is a stupid mistake by me.

Current Behavior

user:~$ ansible-playbook hcloud.yml
ERROR! The Hetzner Cloud dynamic inventory plugin requires hcloud-python.

Environment

  • OS: Fedora 31
  • Python Version: 3.7.6
  • Hcloud-Python Version: 1.6.3 (installed with pip3 install --user hcloud)

Additional context/Screenshots
Playbook content:

---
# hcloud.yml
- name: Create a Server and a Volume with server
  hosts: localhost
  connection: local
  gather_facts: False
  user: root
  vars:
    hcloud_token: "{{ hcloud_token_testing }}"
  tasks:
    - name: Create a basic server
      hcloud_server:
          api_token: "{{ hcloud_token }}"
          name: my-server
          server_type: cx11
          image: ubuntu-18.04
          state: present
      register: server
    - name: Create a volume
      hcloud_volume:
          api_token: "{{ hcloud_token }}"
          name: my-volume
          size: 10
          server: "{{ server.hcloud_server.name }}"
          state: present

ansible.cfg contains this:

[inventory]
enable_plugins = hcloud

Package `hcloud` for conda (forge)

Feature Request

In conda-based Python deployments (common in science etc.), it would be rather useful to have hcloud available as a conda / conda-forge package.

Technically, pip is also available in conda-based environments and sort of integrates with conda, but using it can cause non-deterministic behavior down the line. Using pip should therefore be avoided in this context if possible.

Because hcloud does not contain non-Python components as far as I can see, the packaging process should be trivial. Guidelines can be found here. If any help is required, I have done this before ;)

hcloud-python]$ make docs fails sphinx 4.x

Bug Report

Current Behavior
'make docs' fails with sphinx 4.0.2 (latest release). app.add_javascript() is removed.
'make docs' outputs a warning that app.add_javascript() is deprecated.

Input Code
Running Sphinx v3.5.4
making output directory... done
/home/tdoczkal/git/hcloud-python/docs/conf.py:166: RemovedInSphinx40Warning: The app.add_javascript() is deprecated. Please use app.add_js_file() instead.
app.add_javascript('js/open_links_in_new_tab.js')

hcloud-python]$ make docs

Expected behavior/code
build docs

Environment

  • Python Version: v3.8.8
  • Hcloud-Python Version: v.1.12.0 (latest release)

How to convert BoundServer to json for writing to MongoDB

This is more a cry for help rather than a bug report, but this seems to be the only relevant place to post the help request.

Bug Report

Unable to serialise BoundServer to json

Current Behavior
using json.dumps() results in AttributeError

Input Code

response = client.servers.create(....)
print(json.dumps(response.server.__dict__))

TypeError: Object of type ServersClient is not JSON serializable

Expected behavior/code
I don't have an in depth knowledge, so I know I am probably doing something wrong. However I need to write the server results returned from client.servers.create to mongodb.

Environment

  • Python Version: v3.9.1
  • Hcloud-Python Version: 1.16.0

update_service doesn't recognize changed certificates

Bug Report

Current Behavior
Hi!

We are facing an issue with the update_service method for loadbalancers. We are trying to add/delete certificates within the http-Object of a service. After adding or deleting a certificate inside the http-Object and calling the update_service method, the changes aren't made.

Input Code
Example:

created_cert = hcloud_client.certificates.create(
    name=f"{cert_name}_{datetime.strftime(datetime.now(), '%Y%m%d')}",
    certificate=cert["fullchain"],
    private_key=decrypted_privkey.decode('utf-8'),
)

hcloud_load_balancers = hcloud_client.load_balancers.get_all()

for load_balancer in hcloud_load_balancers:
    for service in load_balancer.services:
      service.http.certificates.append(created_cert)
      update_service_action  = load_balancer.update_service(service)
      update_service_action.wait_until_finished()

We already debugged the code, and especially looked into the service.http.certificates-object/list, before calling update_service, to observe, if the certificate is inside the service-object. Everything seems fine. The update_service_action.status also contains success after calling it.

Expected behavior/code
We would think, the above code should add a new certificate to the specified loadbalancers service.

  • Python Version: 3.8
  • Hcloud-Python Version: 1.12.0

Lack of persistent HTTP connections for API requests, high HTTPS connection overhead

Feature Request

The hcloud.Client should use a requests Session object for persistent HTTPS connections, avoiding a great deal of HTTPS connection latency/overhead when performing many API requests.

Using a rough time ... comparison, this change alone seems to reduce the runtime for an Ansible dynamic inventory script making lots of hetzner cloud API requests from ~32s -> ~18s.

Is your feature request related to a problem? Please describe.

Using logging.getLogger("urllib3").setLevel(logging.DEBUG) to show the individual API requests the library is making, I noticed that requests is establishing a new HTTPS connection for each API request:

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.hetzner.cloud:443
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/servers?label_selector=...&page=1&per_page=50 HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.hetzner.cloud:443
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/networks/... HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.hetzner.cloud:443
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/networks/... HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.hetzner.cloud:443
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/volumes/... HTTP/1.1" 200 None

Describe the solution you'd like

Changing the Client.request method to use a requests.Session avoids the HTTPS connection overhead:

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.hetzner.cloud:443
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/servers?label_selector=...&page=1&per_page=50 HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/networks/... HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/networks/... HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:https://api.hetzner.cloud:443 "GET /v1/volumes/... HTTP/1.1" 200 None

Describe alternatives you've considered
None

Teachability, Documentation, Adoption, Migration Strategy
This should be a completely transparent performance improvement for all users using a hcloud.Client(...) instance.

Show details of an APIException in the traceback

Feature Request

Is your feature request related to a problem? Please describe.
When I get an APIException, I would like to see error details in the console traceback

Describe the solution you'd like
add str method to APIException class

Don't force me to reset root password after creating a server

Feature Request

Is your feature request related to a problem? Please describe.

I'm spinning up and delegating workloads to servers in an automated fashion using hcloud-python. Since this is a fully automated setup, I don't want to deal with configuring SSH keys because, if everything goes as planned, I'll only ever connect to a given server once. Hence, I simply wanted to use the root password the API returns in its CreateServerResponse. However, once I connect via SSH I'm forced to reset the password – which I find completely unnecessary and which severely hinders automation because Python libraries like Paramiko, AsyncSSH, Plumbum, Fabric, etc. are not prepared for that.

Describe the solution you'd like

Provide an option for ServersClient.create() to disable the reset-password policy.

Describe alternatives you've considered

  • I suppose there might be a way to disable the policy using cloud-init but I haven't figured that out yet.
  • Generate an SSH key using some Python library, register it with the Hetzner Cloud API, invoke ServersClient.create() with that key, use server, tear down server, delete SSH key from project. Unfortunately, this adds an additional stack of dependencies just for generating a key which I want to use once.

PS: I realize that this request concerns the Cloud API much more than it concerns hcloud-python. However, I didn't know where else to post this and, as outlined above, I also think this is much more of an obstacle for full automation (in Python) than it is for, say, casual users of the Cloud CLI.

libcloud storage support

Ciao Hetzner,

We use your cloud and storage boxes. We use Apache libcloud to abstract from a specific implementation and found that your storage is not in the library (you can find Amazon, Google, etc).

https://github.com/apache/libcloud

We wrote a driver for libcloud that connects to your storage box via WebDAV. You could add it to Apache libcloud so people could find it and also find you as a cloud provider. I attached the source code below. If you decide to use it I can explain how to add to libcloud, etc and also provide testing code. License is open source.

webdavdrivers.py.zip

Cheers

pip install

Bug Report

Current Behavior
pip install libs differs from source even tho it has same version (1.6.3)

Environment

  • Python Version: 3.8.2
  • Hcloud-Python Version: 1.6.3 (installed from pip)

Additional context/Screenshots
https://i.imgur.com/FFHLVKZ.png
Image of servers/client.py from pip install hcloud
Notice that its missing the networks which will cause a TypeError
TypeError: create() got an unexpected keyword argument 'networks'

Use looser bounds for future dependency

Bug Report

Current Behavior

ERROR: hcloud 1.6.1 has requirement future==0.17.1, but you'll have future 0.18.0 which is incompatible.

With the following requirements file (reproduce with pip install -r requirements.txt):

ansible==2.8.5
molecule[hetznercloud]==2.22
pip==19.3
setuptools==41.4.0

Expected behavior/code

Easier bounds so that libraries don't conflict as easily.

Environment

  • Python 3.7
  • Hcloud 1.6.1

Possible Solution

Use <= and >= bounds.

Ansible) Creating loadbalancer) TypeError: __init__() got an unexpected keyword argument 'dns_ptr'

Bug Report

Current Behavior
When running the following ansible-script, i receive the error TypeError: __init__() got an unexpected keyword argument 'dns_ptr'. (full stack trace in buttom).

I can easily create servers and other ressources

Futhermore i cannot find any documentation saying anything about dns_ptr in the documentation for creating a loadbalancer, i have looked at the ansible-documentation, and also API-documentation, but did not find anything.

Input Code
Simple example created to illustrate problem;
lberror.yml

- hosts: all
  gather_facts: no

  vars_prompt:
    - name: "hetzner_api_token"
      prompt: "Enter api-token from Hetzner"
      private: no

  tasks:
    - name: Create a basic Load Balancer
      hetzner.hcloud.hcloud_load_balancer:
        api_token: "{{ hetzner_api_token }}"
        name: "testlb"
        load_balancer_type: "lb11"
        location: "fsn1"
        state: "present"

It is run with command

ansible-playbook lberror.yml -e "ansible_python_interpreter=/usr/bin/python3" -vvv

Expected behavior/code
This script has run before and i expected it to run again when i ran it yesterday - But it does not. I expected it to create a load-balancer in hetzner.

Environment

  • Python Version: 3.8.10
  • Hcloud-Python Version: Tried with 1.4.3 and also 1.6.0
  • Ansible: 2.10.8

Possible Solution

Possible relations
Since my error is dns_ptr on load balancers, it could be related to PR #144

Additional context/Screenshots

The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py", line 187, in _get_load_balancer
  File "/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py", line 337, in get_by_name
    return super(LoadBalancersClient, self).get_by_name(name)
  File "/home/mikkel/.local/lib/python3.8/site-packages/hcloud/core/client.py", line 75, in get_by_name
    response = self.get_list(name=name)
  File "/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py", line 310, in get_list
    ass_load_balancers = [
  File "/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py", line 311, in <listcomp>
    BoundLoadBalancer(self, load_balancer_data) for load_balancer_data in response["load_balancers"]
  File "/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py", line 29, in __init__
    ipv4_address = IPv4Address(**public_net['ipv4'])
TypeError: __init__() got an unexpected keyword argument 'dns_ptr'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py", line 102, in <module>
    _ansiballz_main()
  File "/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py", line 94, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py", line 40, in invoke_module
    runpy.run_module(mod_name='ansible_collections.hetzner.hcloud.plugins.modules.hcloud_load_balancer', init_globals=None, run_name='__main__', alter_sys=True)
  File "/usr/lib/python3.8/runpy.py", line 207, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.8/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py", line 324, in <module>
  File "/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py", line 318, in main
  File "/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py", line 269, in present_load_balancer
  File "/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py", line 191, in _get_load_balancer
AttributeError: 'TypeError' object has no attribute 'message'
fatal: [localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py\", line 187, in _get_load_balancer\n  File \"/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py\", line 337, in get_by_name\n    return super(LoadBalancersClient, self).get_by_name(name)\n  File \"/home/mikkel/.local/lib/python3.8/site-packages/hcloud/core/client.py\", line 75, in get_by_name\n    response = self.get_list(name=name)\n  File \"/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py\", line 310, in get_list\n    ass_load_balancers = [\n  File \"/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py\", line 311, in <listcomp>\n    BoundLoadBalancer(self, load_balancer_data) for load_balancer_data in response[\"load_balancers\"]\n  File \"/home/mikkel/.local/lib/python3.8/site-packages/hcloud/load_balancers/client.py\", line 29, in __init__\n    ipv4_address = IPv4Address(**public_net['ipv4'])\nTypeError: __init__() got an unexpected keyword argument 'dns_ptr'\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n  File \"/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/mikkel/.ansible/tmp/ansible-tmp-1632291019.7032492-1208449-116182518945343/AnsiballZ_hcloud_load_balancer.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.hetzner.hcloud.plugins.modules.hcloud_load_balancer', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python3.8/runpy.py\", line 207, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.8/runpy.py\", line 97, in _run_module_code\n    _run_code(code, mod_globals, init_globals,\n  File \"/usr/lib/python3.8/runpy.py\", line 87, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py\", line 324, in <module>\n  File \"/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py\", line 318, in main\n  File \"/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py\", line 269, in present_load_balancer\n  File \"/tmp/ansible_hetzner.hcloud.hcloud_load_balancer_payload_drs_0vnu/ansible_hetzner.hcloud.hcloud_load_balancer_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py\", line 191, in _get_load_balancer\nAttributeError: 'TypeError' object has no attribute 'message'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}


Consider moving to docstings for documenting API

Feature Request

Is your feature request related to a problem? Please describe.
I (as a library user) have an issue referring to the "readthedocs" every time I need to understand how I should use a method

I (as a library developer) have a problem with maintaining docs. I would be much easier for me to document my code inside a docstring.

Describe the solution you'd like
I would like to use a docstrings for documenting methods/objects and then run sphinx-apidoc to create documentation for me

Describe alternatives you've considered
The alternative is what we have right now. We document everything directly in *.rst files

Teachability, Documentation, Adoption, Migration Strategy
I would like to adopt requests library approach. They have a custom table of content, but they refer to autogenerated code inside documentation.
Example: https://github.com/kennethreitz/requests/blob/master/docs/api.rst

wait_until_status_is

Feature Request

hetznercloud-py has a method wait_until_status_is for actions and servers which is very convenient.

Are there any plans to implement something like this in hcloud-python?

In general it would be nice to have a way to synchronize actions, e. g.

  • create a volume
  • wait until it is available
  • attach volume to existing server

Make it easier to detach and delete volume

Feature Request

Is your feature request related to a problem? Please describe.

In one scenario, when I run volume.detach(), I want to run volume.delete() afterwards. However, because the detach job is async in the background, my script has to wait for an unknown amount of time before it can successfully call volume.delete()

Describe the solution you'd like

Alternative 1: Make a new function that covers my use case: volume.detach_and_delete()

Alternative 2: Add an option to volume.detach that makes it block until the detach action is actually completed in the backend. It could look like this, for example:

volume.detach(block_until_done=True)

Describe alternatives you've considered

I could write a custom for loop like this in my script, but that's not code I want to have in my script. I would really prefer that the hcloud takes care of this.

def is_volume_detached_from_server(volume, server):
    # Implement code that gets the updated volume object or server object
    # and checks whether or not the given volume is still attached to the server
    return True | False

server = ...
volume.detach()

for i in range(50):
    if i == 49:
        raise Exception("Timeout")
    if is_volume_detached_from_server(volume, server):
        break
    else:
        time.sleep(5)
        continue
    

volume.delete()

Teachability, Documentation, Adoption, Migration Strategy

Migration strategy: None needed, since the requested change(s) would be backwards-compatible

Creating a server throws an exception

Bug Report

Current Behavior
I get an exeption

  File "<snipped>scripts\provision_vms.py", line 31, in create_vm
    image=Image(name="ubuntu-18.04"),
  File "<snipped>\lib\site-packages\hcloud\servers\client.py", line 449, in create
    response = self._client.request(url="/servers", method="POST", json=data)
  File "<snipped>\lib\site-packages\hcloud\hcloud.py", line 192, in request
    self._raise_exception_from_json_content(json_content)
  File "<snipped>\lib\site-packages\hcloud\hcloud.py", line 156, in _raise_exception_from_json_content
    details=json_content['error']['details']
hcloud.hcloud.APIException: invalid input in field 'name'

Input Code

  • REPL or Repo link if applicable:

https://github.com/hetznercloud/hcloud-python/blob/master/examples/create_server.py#L6

    client = Client(token=HETZNER_CLOUD_API_TOKEN)
    response = client.servers.create(
        name="that_server_12",
        server_type=ServerType(name="cx11"),
        image=Image(name="ubuntu-18.04"),
    )

Expected behavior/code

I expected the server to be created, not get an exception about having an invalid image name.

Environment

  • Python Version: 3.6.6
  • Hcloud-Python Version: 1.4.0

Possible Solution

Additional context/Screenshots
Add any other context about the problem here. If applicable, add screenshots to help explain.

Loosen up the requirements

Right now, you specify exact versions of dependencies, like this:

    "future==0.17.1",
    "python-dateutil==2.7.5",
    "requests==2.20.0"

This may be incompatible with existing projects. It would be better to specify ranges of versions that are compatible with the hcloud package. For example, requests is a very stable package, and hcloud will probably work with a wide range of requests versions, e.g. requests>=2.20,<2.23

get volume by id failed

Bug Report

Tried to get volume by id failed:

sys.path.append('/home/user/hcloud-python/')
from hcloud import HcloudClient
client = HcloudClient(token=token)
client.volumes.get_by_id(vol_id)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/user/hcloud-python/hcloud/volumes/client.py", line 58, in get_by_id
    return BoundVolume(self, response['volume'])
  File "/home/user/hcloud-python/hcloud/volumes/client.py", line 17, in __init__
    super(BoundVolume, self).__init__(client, data, complete)
  File "/home/user/hcloud-python/hcloud/core/client.py", line 69, in __init__
    self.data_model = self.model(**data)
TypeError: __init__() got an unexpected keyword argument 'format'

Environment

  • OS: Ubuntu 18.04.2 LTS
  • Python Version: v3.6.7
  • Hcloud-Python Version: d80fedd

UserData not available at the server response object

Feature Request

Is your feature request related to a problem? Please describe.
I cannot ask your api for the initial used user_data script

Describe the solution you'd like
A user_data attribute on the responded server object would be fine

Describe alternatives you've considered
Just save a own database entity for each created server

Teachability, Documentation, Adoption, Migration Strategy

response = servers_client.get_by_id(...)
response.server.user_data

How to use server console websocket url?

BoundServer.request_console() returns an object containing a websocket url to connect to and a password.

Is there any documentation on how to use that websocket url? Browsers cannot open it directory; I can connect using wscat or websocat, but I don't speak fluent websocket, how do I get to a remote console from there?

Thanks in advance!

Cheers,
Kosta

Failed to import the required Python library (hcloud-python)

Bug Report

Current Behavior

When running an Ansible script, an error shows up and the script stops:
"Failed to import the required Python library (hcloud-python) on computer's Python /usr/bin/python3. Please read the module documentation and install it in the appropriate location."

Input Code

- name: Create a basic Hetzner hcloud server with ssh key
  hcloud_server:
    api_token: "{{ hcloud_token }}"
    image: "{{ hcloud_image }}"
    location:  "{{ hcloud_location }}"
    name: "{{ item }}"
    server_type: "{{ hcloud_type }}"
    ssh_keys:
      - "{{ hcloud_ssh }}"
    state: present
  with_inventory_hostnames:
    - webservers
  register: hcloud_info

Expected behavior/code

Virtual server creation

Environment

  • Linux Ubuntu Desktop [21.04]
  • Python Version: [3.9.5]
  • Hcloud-Python Version: [1.13.0]
  • Ansible version: [2.11.3]
  • Ansible Galaxy hcloud collection version: [1.4.4]

hcloud pip module for Python 3.9

Feature Request

Hello,

We are developing a new saltstack cloud driver for the Hetzner Public Cloud using hcloud-python. Saltstacks build chain in github required to have the dependencies available in pip for Python 3.9, but there is no hcloud available via pip install hcloud==1.6.3 for Python 3.9. Would it be possible to make this available?

Link to saltstack/salt - PR: saltstack/salt#57453
Link to saltstack/salt - specific check: https://jenkinsci.saltstack.com/job/pr-pre-commit/job/PR-57453/7/console

Best regards

Implement get_by_name - Method

We should implement an easy short method to get a specific server (volume, image, location, datacenter, ssh-key) by its name like we have in the hcloud-go https://github.com/hetznercloud/hcloud-go/blob/master/hcloud/server.go#L119

We can also implement a get_by_label(selector=) method servers, volumes, image, ssh-keys and floating-ips)

This would allow users don't make something like this:

        if module.params['id']:
            server = client.servers.get_by_id(module.params['id'])
        elif module.params['name']:
            server = client.servers.get_all(name=module.params['name'])[0]
        elif module.params['label_selector']:
            server = client.servers.get_all(label_selector=module.params['label_selector'])[0]

@thcyron @LD250 what do you think about this?

Cloud-init status

Is there a way to determine, if cloud-init is finished by using this library.

Allow Endpoint to be set

We should support the same Client Configuration as the hcloud-go.

So we should also allow the user to customize the endpoint, instead of hardcoding it.

firewalls.apply_to_resources makes no sense

Bug Report

Current Behavior
I'm not able to assign my server ressource with this kind of implementation.
Either the documentation does not fit or the implementation or both

Input Code

    def apply_to_resources(self, firewall, resources):
        # type: (Firewall, List[FirewallResource]) -> List[BoundAction]
        """Applies one Firewall to multiple resources.

        :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or  :class:`Firewall <hcloud.firewalls.domain.Firewall>`
        :param rules: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`]
        :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`]
        """
        data = {
            "apply_to": []
        }
        for resource in resources:
            data['apply_to'].append(resource.to_payload())
        response = self._client.request(
            url="/firewalls/{firewall_id}/actions/apply_to_resources".format(firewall_id=firewall.id),
            method="POST", json=data)
        return [BoundAction(self._client.actions, _) for _ in response['actions']]

Expected behavior/code
I like to assign the server-ressource to the firewall.

Environment

  • Python Version: doesn't matter
  • Hcloud-Python Version: current

Possible Solution

Firewall Rules Limit

Hello,
Why has the Rules List used for create and set_rules a limit from 50 FirewallRule Objects. How is it possible to add more then 50 Firewall Rules to a Firewall right now I don’t have found a other solution for this than a bulk update with all rules.

Drop Python 3.4

  • Remove from Travis CI

  • Remove all references in our documentation

hcloud-python]$ make docs throws exception

Bug Report

Current Behavior
hcloud-python]$ make docs
make -C docs clean
[...]
Running Sphinx v3.5.4
making output directory... done
hcloud-python/docs/conf.py:166: RemovedInSphinx40Warning: The app.add_javascript() is deprecated. Please use app.add_js_file() instead.
app.add_javascript('js/open_links_in_new_tab.js')
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 21 source files that are out of date
updating environment: [new config] 21 added, 0 changed, 0 removed
reading sources... [100%] samples
WARNING: autodoc: failed to import class 'CertificateClient' from module 'hcloud.certificates.client'; the following exception was raised:
Traceback (most recent call last):
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/util/inspect.py", line 403, in safe_getattr
return getattr(obj, name, *defargs)
AttributeError: module 'hcloud.certificates.client' has no attribute 'CertificateClient'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/importer.py", line 111, in import_object
obj = attrgetter(obj, mangled_name)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/init.py", line 320, in get_attr
return autodoc_attrgetter(self.env.app, obj, name, *defargs)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/init.py", line 2604, in autodoc_attrgetter
return safe_getattr(obj, name, *defargs)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/util/inspect.py", line 419, in safe_getattr
raise AttributeError(name) from exc
AttributeError: CertificateClient

hcloud-python/hcloud/certificates/client.py:docstring of hcloud.certificates.client.BoundCertificate.update:7: WARNING: Inline interpreted text or phrase reference start-string without end-string.
WARNING: error while formatting arguments for hcloud.firewalls.client.BoundFirewall.get_actions: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
WARNING: error while formatting arguments for hcloud.floating_ips.client.BoundFloatingIP.get_actions: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
hcloud-python/hcloud/floating_ips/client.py:docstring of hcloud.floating_ips.client.BoundFloatingIP.get_actions_list:5: WARNING: Definition list ends without a blank line; unexpected unindent.
hcloud-python/hcloud/images/client.py:docstring of hcloud.images.client.ImagesClient.get_by_id:4: WARNING: Inline interpreted text or phrase reference start-string without end-string.
WARNING: error while formatting arguments for hcloud.images.client.BoundImage.update: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
hcloud-python/docs/api.clients.load_balancer_types.rst:2: WARNING: Title underline too short.

LoadBalancerTypesClient

hcloud-python/hcloud/load_balancers/client.py:docstring of hcloud.load_balancers.client.LoadBalancersClient.get_by_id:4: WARNING: Inline interpreted text or phrase reference start-string without end-string.
WARNING: autodoc: failed to import class 'LoadBalancerHealthCheckHttp' from module 'hcloud.load_balancers.domain'; the following exception was raised:
Traceback (most recent call last):
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/util/inspect.py", line 403, in safe_getattr
return getattr(obj, name, *defargs)
AttributeError: module 'hcloud.load_balancers.domain' has no attribute 'LoadBalancerHealthCheckHttp'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/importer.py", line 111, in import_object
obj = attrgetter(obj, mangled_name)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/init.py", line 320, in get_attr
return autodoc_attrgetter(self.env.app, obj, name, *defargs)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/ext/autodoc/init.py", line 2604, in autodoc_attrgetter
return safe_getattr(obj, name, *defargs)
File "/app/hcloud-python/lib64/python3.8/site-packages/sphinx/util/inspect.py", line 419, in safe_getattr
raise AttributeError(name) from exc
AttributeError: LoadBalancerHealthCheckHttp

hcloud-python/hcloud/load_balancers/domain.py:docstring of hcloud.load_balancers.domain.LoadBalancerTarget:3: WARNING: Unexpected indentation.
hcloud-python/hcloud/load_balancers/domain.py:docstring of hcloud.load_balancers.domain.LoadBalancerTarget:4: WARNING: Block quote ends without a blank line; unexpected unindent.
hcloud-python/hcloud/load_balancers/domain.py:docstring of hcloud.load_balancers.domain.LoadBalancerTargetLabelSelector:3: WARNING: Unexpected indentation.
hcloud-python/hcloud/load_balancers/domain.py:docstring of hcloud.load_balancers.domain.LoadBalancerTargetIP:3: WARNING: Unexpected indentation.
hcloud-python/hcloud/load_balancers/domain.py:docstring of hcloud.load_balancers.domain.LoadBalancerAlgorithm:3: WARNING: Unexpected indentation.
hcloud-python/hcloud/networks/client.py:docstring of hcloud.networks.client.NetworksClient.get_by_id:4: WARNING: Inline interpreted text or phrase reference start-string without end-string.
hcloud-python/hcloud/ssh_keys/client.py:docstring of hcloud.ssh_keys.client.BoundSSHKey.update:7: WARNING: Inline interpreted text or phrase reference start-string without end-string.
WARNING: error while formatting arguments for hcloud.volumes.client.VolumesClient.get_actions: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
WARNING: error while formatting arguments for hcloud.volumes.client.VolumesClient.get_actions_list: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
hcloud-python/hcloud/volumes/client.py:docstring of hcloud.volumes.client.VolumesClient.get_actions:6: WARNING: Field list ends without a blank line; unexpected unindent.
hcloud-python/hcloud/volumes/client.py:docstring of hcloud.volumes.client.VolumesClient.get_actions:8: WARNING: Definition list ends without a blank line; unexpected unindent.
WARNING: error while formatting arguments for hcloud.volumes.client.BoundVolume.attach: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
WARNING: error while formatting arguments for hcloud.volumes.client.BoundVolume.get_actions: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
WARNING: error while formatting arguments for hcloud.volumes.client.BoundVolume.get_actions_list: Handler <function update_annotations_using_type_comments at 0x7f2220505b80> for event 'autodoc-before-process-signature' threw an exception (exception: list index out of range)
hcloud-python/hcloud/volumes/client.py:docstring of hcloud.volumes.client.BoundVolume.get_actions:5: WARNING: Field list ends without a blank line; unexpected unindent.
hcloud-python/hcloud/volumes/client.py:docstring of hcloud.volumes.client.BoundVolume.get_actions:7: WARNING: Definition list ends without a blank line; unexpected unindent.
../CONTRIBUTING.rst:122: WARNING: Literal block expected; none found.
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] samples
generating indices... genindex done
highlighting module code... [100%] hcloud.volumes.domain
writing additional pages... search done
copying static files... done
copying extra files... done
dumping search index in English (code: en)... done
dumping object inventory... done
build succeeded, 27 warnings.

Expected behavior/code
no build errors for 'make docs'

Environment

  • Python Version: v3.8.8
  • Hcloud-Python Version: v.1.12.0

New root password is not available after server rebuild

Bug Report

Current Behavior
When rebuilding a server from an image, and this server has no ssh key associated, a new root password is generated.
According to the API documentation this new password is returned in key root_password of the HTTP response. From the Python API, this new root password is not available, making rebuilt servers inaccessible.

Input Code
Something along these lines might reproduce the issue:

client = Client('<token>')
response = client.servers.create('testbox', ServerType(name='cx11'), Image(name='debian-10'))
print('root =', response.root_password)
time.sleep(60)
server = client.servers.get_by_name('testbox')
response = server.rebuild(Image(name='debian-10'))
print('root =', '???') # problem here - response is of type BoundAction not containing the password

Expected behavior/code
In servers/client.py, the root password trashed. It could for example be returned as second return argument, or added to the action's response, or be added to the server object. The wrapper method in line 199 might also need adaption to return the new root password.

Environment

  • Python Version: 3.6
  • Hcloud-Python Version: 1.10.0 (and current master branch)

Prepare for release

  • Polish Docs (we need to update the examples)
  • Add travis-ci
  • Publish on PyPi (Already prepared on tag)
  • Add badge for supported python versions #25

Failing to provide rules to firewall

Bug Report

Current Behavior

Creating a firewall with rules or adding a rule to an existing firewall fails (in different forms). I am not entirely sure if it is my fault, to be honest.

Input Code

I create a rule as follows:

rule_ssh = FirewallRule(
    direction = 'in',
    protocol = 'TCP',
    source_ips = ['0.0.0.0/0', '::/0'],
    port = '22',
    destination_ips = ['0.0.0.0/0', '::/0'],
)

Then I try to create a firewall:

firewall = client.firewalls.create(
    name = 'test',
    rules = [rule_ssh]
)

I get the following traceback:

---------------------------------------------------------------------------
APIException                              Traceback (most recent call last)
<ipython-input-66-c8e589768b69> in <module>
----> 1 firewall = client.firewalls.create(
      2     name = 'clusterfirewall',
      3     rules = [rule_ssh]
      4 )

~/env38/lib/python3.8/site-packages/hcloud/firewalls/client.py in create(self, name, rules, labels)
    262                 data['rules'].append(rule.to_payload())
    263 
--> 264         response = self._client.request(url="/firewalls", json=data, method="POST")
    265 
    266         action = None

~/env38/lib/python3.8/site-packages/hcloud/hcloud.py in request(self, method, url, tries, **kwargs)
    217                     return self.request(method, url, tries, **kwargs)
    218                 else:
--> 219                     self._raise_exception_from_json_content(json_content)
    220             else:
    221                 self._raise_exception_from_response(response)

~/env38/lib/python3.8/site-packages/hcloud/hcloud.py in _raise_exception_from_json_content(self, json_content)
    178 
    179     def _raise_exception_from_json_content(self, json_content):
--> 180         raise APIException(
    181             code=json_content['error']['code'],
    182             message=json_content['error']['message'],

APIException: invalid input in field 'rules'

Alternatively, I create a firewall without rules and try to add a rule in a second step:

firewall = client.firewalls.create(name = 'test')
client.firewalls.set_rules(firewall = firewall, rules = [rule_ssh])

Now the following exception occurs:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-71-c2cae1417130> in <module>
----> 1 client.firewalls.set_rules(firewall = firewall, rules = [rule_ssh])

~/env38/lib/python3.8/site-packages/hcloud/firewalls/client.py in set_rules(self, firewall, rules)
    321             data['rules'].append(rule.to_payload())
    322         response = self._client.request(
--> 323             url="/firewalls/{firewall_id}/actions/set_rules".format(firewall_id=firewall.id),
    324             method="POST", json=data)
    325         return [BoundAction(self._client.actions, _) for _ in response['actions']]

AttributeError: 'CreateFirewallResponse' object has no attribute 'id'

Expected behavior/code

No errors, but a firewall with rules.

Environment

  • Python Version: 3.8.7
  • Hcloud-Python Version: 1.11.0

Create Server with ssh_keys failed

Bug Report

Current Behavior
Tried to create a server from image with volume and ssh-keys failed:

sys.path.append('/home/user/hcloud-python/')
from hcloud import HcloudClient
client = HcloudClient(token=token)

volumes = client.volumes.get_by_id(id=vol_id)
ssh_keys = client.ssh_keys.get_all(name='user')

server = client.servers.create(name=vm, server_type=ServerType(name='cx11'), \
            image=Image(id=image_id), volumes=volumes, ssh_keys=ssh_keys)

Traceback (most recent call last):
  File "bin/hcloud-control.py", line 250, in <module>
    main(args)
  File "bin/hcloud-control.py", line 230, in main
    resume(client, args.vm)
  File "bin/hcloud-control.py", line 174, in resume
    image=Image(id=image_id), volumes=volumes, ssh_keys=ssh_keys)
  File "/home/user/hcloud-python/hcloud/servers/client.py", line 226, in create
    response = self._client.request(url="/servers", method="POST", json=data)
  File "/home/user/hcloud-python/hcloud/hcloud.py", line 109, in request
    self._raise_exception_from_json_content(json_content)
  File "/home/user/hcloud-python/hcloud/hcloud.py", line 84, in _raise_exception_from_json_content
    details=json_content['error']['details']
hcloud.hcloud.HcloudAPIException

if i put this in try...except block i got:
SSH key 'ssh_key_id not found

Environment
OS: Ubuntu 18.04.2 LTS
Python Version: v3.6.7
Hcloud-Python Version: 95120b9

Provide PEP 484 compatible type annotations

Feature Request

I'd like to type check code using this lib with mypy, but it doesn't type check sphinx type directives in docstrings. Stub files could provide type hints without having to change the current code, I'd be willing to submit a PR if that's desirable.

EDIT: Sorry, I looked into the code and it's already annotated. It seems like a hcloud/py.typed file is needed though, according to the mypy docs. I set up a local clone with a py.typed file and mypy can import hcloud now, though it still doesn't typecheck, the following gives me no warnings for instance:

foo: int = hcloud.Client(self.token).servers.get_by_name(234)

Also, it seems that some types are not set correcly:

hcloud-python/hcloud/certificates/client.py:81: error: Type signature has too few arguments
hcloud-python/hcloud/certificates/client.py:103: error: Type signature has too few arguments
hcloud-python/hcloud/floating_ips/client.py:41: error: Type signature has too few arguments
hcloud-python/hcloud/volumes/client.py:24: error: Type signature has too few arguments
hcloud-python/hcloud/volumes/client.py:40: error: Type signature has too few arguments
hcloud-python/hcloud/volumes/client.py:72: error: Type signature has too few arguments
hcloud-python/hcloud/volumes/client.py:236: error: Type signature has too few arguments
hcloud-python/hcloud/volumes/client.py:266: error: Type signature has too few arguments
hcloud-python/hcloud/images/client.py:51: error: Type signature has too few arguments
hcloud-python/hcloud/load_balancers/client.py:339: error: syntax error in type comment '(...) -> CreateLoadBalancerResponse:'
Found 10 errors in 5 files (checked 1 source file)

Include SSH fingerprint in CreateServerResponse

Feature Request

Is your feature request related to a problem? Please describe.

Right after creating a server, I want my Python script to connect to it via SSH. Unfortunately, I then have no other option than to trust the server's SSH fingerprint, resulting in a serious security issue. The same thing applies to the Cloud Console – it doesn't show the SSH fingerprint, either, and I will have to trust the server's fingerprint on first use.

With the Cloud Console the risk is much lower, though, because typically I create a server once, trust its fingerprint once and then use the server for an extended period of time. So the attack window is comparatively small. If however, I spin up and tear down servers using hcloud-python on the daily, the risk of an MITM attack increases tremendously. In fact, I'd go as far as saying that this almost completely defeats the purpose of having an API for automatically spinning up servers, especially if I need to transmit sensitive data (which in my case I do).

Describe the solution you'd like

Always have the Hetzner Cloud API return the server's SSH fingerprint in its CreateServerResponse, so that I don't have to trust the fingerprint when connecting later.

Even better, also provide a way to obtain the SSH fingerprint for existing servers.

Describe alternatives you've considered

A workaround is to set up the SSH daemon's keys using cloud-init. However, this is a) cumbersome and b) more work than should be necessary, given that I don't actually want/need to prescribe the exact fingerprint (which requires me to generate RSA keys from within Python, resulting in yet another dependency). More importantly, though, in light of the above security concerns I'd say the Cloud API should always give me a way to securely obtain a server's SSH fingerprint.

PS: I realize that this request concerns the Cloud API much more than it concerns hcloud-python. However, I didn't know where else to post this and, as outlined above, I also think this is much more of a security issue for automation than it is for, say, casual users of the CLI.

client.servers.get_all() returns one created date for all servers

When I create new server via hcloud API and then get all servers information - 'created' date is set one for all. When I then get one server information - date is different
Python 3.6.8 (default, Oct 7 2019, 12:59:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
#> from hcloud import Client
#> client = Client(token="sLgM*******N2wMh")
#> s = client.servers.get_all()
#> s[0].created
datetime.datetime(2019, 10, 11, 10, 36, 49, tzinfo=tzutc())
#> s[1].created
datetime.datetime(2019, 10, 11, 10, 36, 49, tzinfo=tzutc())
#> s1 = client.servers.get_by_id(3434180)
#> s1.created
datetime.datetime(2019, 10, 11, 10, 36, 49, tzinfo=tzutc())
#> s2 = client.servers.get_by_id(3434122)
#> s2.created
datetime.datetime(2019, 10, 11, 10, 27, 43, tzinfo=tzutc())

Too strict python-dateutil requirement

Bug Report

Current Behavior

In my project, I'm getting an error message like this when I install my dependencies:

hcloud 1.4.1 has requirement python-dateutil==2.7.5, but you'll have python-dateutil 2.8.0 which is incompatible.

Input Code
N/A

Expected behavior/code
I expect hcloud to support more than one version of python-dateutil. Depending on one single version makes it hard to fit hcloud in a python environment with many other packages.

Environment

  • Python Version: v3.7, but also applies to other versions
  • Hcloud-Python Version: 1.4.1, but also applies to 1.5 and 1.6

Possible Solution
Specify a wider version range for python-dateutil in setup.py

Additional context/Screenshots
Add any other context about the problem here. If applicable, add screenshots to help explain.

Drop Python 2.7 support

Python 2.7 will we EOL at mid-April 2020. We should drop the support for the version then too and remove everything which is only Python 2.x related (like #14).

This issue should only remind us and we should list everything here which can be improved with the drop.

cc @LD250

build system want to install the tests directory

Hello,

I was trying to package hcloud-python under gentoo using distutils. The issue resides in the fact that setup.py want to install the 'tests' directory under /usr/lib64/python2.7/site-packages/:

 * ERROR: dev-python/hcloud-python-1.2.1::x-portage failed (install phase):
 *   Package installs 'tests' package which is forbidden and likely a bug in the build system.

# ls -la /tmp/portage/dev-python/hcloud-python-1.2.1/image/_python2.7/usr/lib64/python2.7/site-packages/
total 0
drwxr-xr-x  5 root root 100 apr 26 14:30 .
drwxr-xr-x  3 root root  60 apr 26 14:30 ..
drwxr-xr-x 14 root root 460 apr 26 14:30 hcloud
drwxr-xr-x  2 root root 160 apr 26 14:30 hcloud-1.2.1-py2.7.egg-info
drwxr-xr-x  4 root root  80 apr 26 14:30 tests

For now, I'm dropping the tests directory, but would be great fix the issue upstream. Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.