A Python 3 SDK for the new (and wonderful) Hetzner cloud service.
- Contributing
- Changelog
- Usage
Open source contributions are more than welcome to be submitted to this repository and every issue and pull request will be promptly reviewed and evaluated on its suitability to be merged into the main branches.
- Adds new server type constants
- Adds new image constants
- Parses the IP field from the created floating IP API calls
- Fixes the cloud-init documentation.
- Allows new servers to be created in any datacenter in a selected location. See pull request 16.
- Adds the new image type constants. See pull request 17.
- Fixes an issue with the 8GB server constants having the wrong type. See issue 15
- Updated the README to show supported Python version and this changelog section.
- Fixed an issue with status codes returned from server actions.
- Fixed an issue where the body is null when sending a POST request.
- Added the new Helsinki DC as a constant.
- Added a check to the shared API requestor that checks for the 429 rate limited status code and returns a suitable exception.
- Added an alias for datacenters, as my Britishness got the best of me when creating this library.
This library is organised into two components: the first, a set of actions that retrieve an actionable component. These actionable components (for example servers) each have their own methods that modify their own (and only their own) behaviour.
Install the library into your environment by executing the following command in your terminal:
pip install hetznercloud
In order to get started with the library, you first need an instance of it. This is very simple, and uses a number of hand-crafted helper functions to make configuration changes.
from hetznercloud import HetznerCloudClientConfiguration, HetznerCloudClient
configuration = HetznerCloudClientConfiguration().with_api_key("YOUR-API-KEY").with_api_version(1)
client = HetznerCloudClient(configuration)
The client is your entry point to the SDK.
Methods that modify server state (such as creating, imaging, snapshotting, deleting etc) generally return a tuple. The first value of this tuple is normally the object type modified (i.e. a server), whilst the second value is the action.
The HetznerCloudAction
object (second value in the tuple) can be used to wait for certain states of that invoked task
to be achieved before continuing. For more information, see the Actions section.
The SDK contains a number of helpful constants that save you having to look up things such as image names, server types and so on.
Please note: I endeavour to keep the constants up to date, but sometimes it is just not possible. Should this happen, feel free to use plain-ole strings (i.e. cx11 for the smallest cloud instance) in their place until I get around to deploying the update.
Constants that represent the different statuses a server can have.
SERVER_STATUS_RUNNING
- The server is running.SERVER_STATUS_INITIALIZING
- The server is running its initialisation cycle.SERVER_STATUS_STARTING
- The server is starting after being powered off.SERVER_STATUS_STOPPING
- The server is stopping (shutting down).SERVER_STATUS_OFF
- The server is turned off.SERVER_STATUS_DELETING
- The server is being deleted.SERVER_STATUS_MIGRATING
- The server is being migrated.SERVER_STATUS_REBUILDING
- The server is being rebuilt.SERVER_STATUS_UNKNOWN
- The status of the server is unknown.`
Constants that represent the different statuses an action can have.
ACTION_STATUS_RUNNING
- The action is currently running.ACTION_STATUS_SUCCESS
- The action completed successfully.ACTION_STATUS_ERROR
- The action terminated due to an error.
Constants that represent the server types available to users.
-
SERVER_TYPE_1CPU_2GB
- The smallest type with 1 CPU core, 2GB of RAM and a 20GB SSD disk. -
SERVER_TYPE_1CPU_2GB_CEPH
- The same as above but with a 20GB CEPH network-attached disk. -
SERVER_TYPE_2CPU_4GB
- Type with 2 CPU cores, 4GB of RAM and a 40GB SSD disk. -
SERVER_TYPE_2CPU_4GB_CEPH
- The same as above but with a 40GB CEPH network-attached disk. -
SERVER_TYPE_2CPU_8GB
- Type with 2 CPU cores, 8GB of RAM and a 80GB SSD disk. -
SERVER_TYPE_2CPU_8GB_CEPH
- The same as above but with a 80GB CEPH network-attached disk. -
SERVER_TYPE_4CPU_16GB
- Type with 4 CPU cores, 16GB of RAM and a 160GB SSD disk. -
SERVER_TYPE_4CPU_16GB_CEPH
- The same as above but with a 160GB CEPH network-attached disk. -
SERVER_TYPE_8CPU_32GB
- The largest type with 8 CPU cores, 32GB of RAM and a 240GB SSD disk. -
SERVER_TYPE_8CPU_32GB_CEPH
- The same as above but with a 240GB CEPH network-attached disk. -
SERVER_TYPE_2CPU_8GB_DVCPU
- 2 dedicated CPU cores, 8GB of RAM and 80GB SSD disk. -
SERVER_TYPE_4CPU_16GB_DVCPU
- 4 dedicated CPU cores, 16GB of RAM and 160GB SSD disk. -
SERVER_TYPE_8CPU_32GB_DVCPU
- 8 dedicated CPU cores, 32GB of RAM and 240GB SSD disk. -
SERVER_TYPE_16CPU_64GB_DVCPU
- 16 dedicated CPU cores, 64GB of RAM and 360GB SSD disk. -
SERVER_TYPE_32CPU_128GB_DVCPU
- 32 dedicated CPU cores, 128GB of RAM and 540GB SSD disk.
Constants that represent the standard images available to users.
IMAGE_UBUNTU_1604
- Ubuntu 16.04 LTSIMAGE_UBUNTU_1804
- Ubuntu 18.04 LTSIMAGE_DEBIAN_9
- Debian 9.3IMAGE_CENTOS_7
- CentOS 7.5IMAGE_FEDORA_27
- Fedora 27IMAGE_FEDORA_28
- Fedora 28
Constants that represent the datacentres available to users.
DATACENTER_FALKENSTEIN_1
- Falkenstein 1 DC 8DATACENTER_NUREMBERG_1
- Nuremberg 1 DC 3DATACENTER_HELSINKI_1
- Helsinki 1 DC 2
Constants that represent backup windows.
BACKUP_WINDOW_10PM_2AM
- Backup window between 10PM and 2AMBACKUP_WINDOW_2AM_6AM
- Backup window between 2AM and 6AMBACKUP_WINDOW_6AM_10AM
- Backup window between 6AM and 10AMBACKUP_WINDOW_10AM_2PM
- Backup window between 10AM and 2PMBACKUP_WINDOW_2PM_6PM
- Backup window between 2PM and 6PMBACKUP_WINDOW_6PM_10PM
- Backup window between 6PM and 10PM
Constants that represent floating IP types
FLOATING_IP_TYPE_IPv4
- IPv4 floating IPFLOATING_IP_TYPE_IPv6
- IPv6 floating IP
Constants that represent image types.
IMAGE_TYPE_BACKUP
- A manual backup of a server, which is then bound to the server it was created from.IMAGE_TYPE_SNAPSHOT
- A snapshot of a server that can be used to create other servers.
Constants that define the different ways that images can be sorted.
SORT_BY_ID_ASC
- Sorts the images by their numerical id in ascending order.SORT_BY_ID_DESC
- Sorts the images by their numerical id in descending order.SORT_BY_NAME_ASC
- Sorts the images by their name in ascending character order.SORT_BY_NAME_DESC
- Sorts the images by their name in descending character order.SORT_BY_CREATED_ASC
- Sorts the images by their created date in ascending order.SORT_BY_CREATED_DESC
- Sorts the images by their created date in descending order.
A number of standard exceptions can be thrown from the methods that interact with the Hetzner API.
HetznerAuthenticationException
- raised when the API returns a 401 Not Authorized or 403 Forbidden status code.HetznerInternalServerErrorException
- raised when the API returns a 500 status code.HetznerActionException
- raised when an action on something yields an error in the JSON response or the status code is not what was expected.HetznerInvalidArgumentException
- raised when a required argument of the method is not specified correctly. The exception will detail the failing parameter.
The datacenter top level action can be retrieved by calling the datacenters()
method on the HetznerCloudClient
instance.
To retrieve all of the datacenters available on the Hetzner Cloud service, simply call the get_all()
method, passing
in no parameters.
NOTE: This method returns a generator, so if you wish to get all of the results instantly, you should encapsulate the
call within the list()
function
all_dcs_generator = client.datacenters().get_all()
for dc in all_dcs_generator:
print(dc.id)
all_dcs_list = list(client.datacenters().get_all())
print(all_dcs_list)
To get all datacenters filtered by a name, call the get_all()
method with the name parameter populated.
all_dcs = list(client.datacenters().get_all(name="fsn1-dc8"))
print(all_dcs)
To get a datacenter by id, simply call the get()
method on the datacenter action, passing in the id of the datacenter
you wish to get information for.
datacenter = client.datacenters().get(1)
print(datacenter.name)
To create a floating IP, simply call the create()
method with the parameters detailed below.
NOTE: If you do not specify server
, then you must specify home_location
, or vice versa.
new_floating_ip = client.floating_ips().create(type=FLOATING_IP_TYPE_IPv4,
server=42,
description="My new floating IP")
" or...
new_floating_ip = client.floating_ips().create(type=FLOATING_IP_TYPE_IPv4,
home_location="fep1",
description="My new floating IP")
floating_ips = client.floating_ips().get_all()
for ip in floating_ips:
print(ip.id)
floating_ip = client.floating_ips().get(1)
print(floating_ip.id)
floating_ip = client.floating_ips().get(1)
action = floating_ip.assign_to_server(2)
action.wait_until_status_is(ACTION_STATUS_RUNNING)
floating_ip = client.floating_ips().get(1)
action = floating_ip.change_description("My new floating IP v2")
floating_ip = client.floating_ips().get(1)
action = floating_ip.change_reverse_dns_entry("192.168.1.1", "www.google.com")
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
floating_ip = client.floating_ips().get(1)
action = floating_ip.delete()
floating_ip = client.floating_ips().get(1)
action = floating_ip.unassign_from_server()
To retrieve all of the images available on the Hetzner Cloud service, simply call the get_all()
method, passing
in no parameters.
There are also a number of parameters on this method that allow you to filter and sort images.
images = client.images().get_all(sort=SORT_BY_ID_ASC)
for image in images:
print(image.id)
To get an image by its id, simply call the get()
method, passing in the id of the image you wish to retrieve.
image = client.images().get(1)
print(image.id)
To update an image's description or type, call the update()
method with the description of the image and/or the type
of the image, should you wish to update them. Both parameters are optional.
image = client.images().get(1)
image.update(description="my description", type=IMAGE_TYPE_SNAPSHOT)
To delete an image, call the delete()
method. NOTE: Only images of type 'snapshot' or 'backup' can be deleted (so,
you cannot delete the images provided by Hetzner!).
image = list(client.images().get_all(name="my-first-image"))[0]
image.delete()
The iso top level action can be retrieved by calling the isos()
method on the HetznerCloudClient
instance.
To retrieve all of the isos available on the Hetzner Cloud service, simply call the get_all()
method, passing
in no parameters.
NOTE: This method returns a generator, so if you wish to get all of the results instantly, you should encapsulate the
call within the list()
function
isos = client.isos().get_all()
for l in isos:
print(l.id)
isos = list(client.isos().get_all())
print(isos)
To get all isos filtered by a name, call the get_all()
method with the name parameter populated.
isos = list(client.isos().get_all(name="virtio-win-0.1.141.iso"))
print(isos)
To get an iso by id, simply call the get()
method on the datacenter action, passing in the id of the iso
you wish to get information for.
iso = client.isos().get(1)
print(iso.name)
The location top level action can be retrieved by calling the locations()
method on the HetznerCloudClient
instance.
To retrieve all of the locations available on the Hetzner Cloud service, simply call the get_all()
method, passing
in no parameters.
NOTE: This method returns a generator, so if you wish to get all of the results instantly, you should encapsulate the
call within the list()
function
all_locs_generator = client.locations().get_all()
for l in all_locs_generator:
print(l.id)
all_locs_list = list(client.locations().get_all())
print(all_locs_list)
To get all locations filtered by a name, call the get_all()
method with the name parameter populated.
all_locs = list(client.locations().get_all(name="fsn1"))
print(all_locs)
To get a location by id, simply call the get()
method on the datacenter action, passing in the id of the location
you wish to get information for.
location = client.locations().get(1)
print(location.name)
The servers top level action is accessible through the client.servers()
method. You must use one of the methods in
the object returned by this top level action in order to modify the state of individual servers.
All servers associated with the API key you provided can be retrieved by calling the get_all()
top level action method.
all_servers = client.servers().get_all() # gets all the servers as a generator
all_servers_list = list(client.servers().get_all()) # gets all the servers as a list
By calling the get_all(name="my-server-name")
method (with the optional name
parameter entered), you can bring back
the servers that have the name entered.
all_servers = client.servers().get_all(name="foo") # gets all the servers as a generator
all_servers_list = list(client.servers().get_all(name="foo")) # gets all the servers as a list
If you know the id of the server you wish to retrieve you can use this method to retrieve that specific server.
try:
server = client.servers().get(1)
except HetznerServerNotFoundException:
print("Woops, server not found!")
This method throws a HetznerServerNotFoundException
if the following conditions are satisfied:
- The id passed into the method is not an integer or is not greater than 0.
- The API returns a 404 indicating that the server could not be found.
To create a server, you can call the create
top level action method. This method accepts a number of parameters (some
are optional, some aren't).
server_a, create_action = client.servers().create(name="my-required-server-name", # REQUIRED
server_type=SERVER_TYPE_1CPU_2GB, # REQUIRED
image=IMAGE_UBUNTU_1604, # REQUIRED
datacenter=DATACENTER_FALKENSTEIN_1,
start_after_create=True,
ssh_keys=["my-ssh-key-1", "my-ssh-key-2"],
user_data='''#cloud-config
packages:
- screen
- git
''')
server_a.wait_until_status_is(SERVER_STATUS_RUNNING)
For more details on user_data please see https://cloudinit.readthedocs.io/en/latest/topics/examples.html
Once you have an instance of the server (retrieved by using one of the "Top level actions" above), you are able to perform different modifier actions on them.
To attach an ISO to the server, call the attach_iso()
method on the HetznerCloudServer
object, specifying either the
name or the ID of the ISO you wish to attach to the server (these can be retrieved by using the isos().get_all()
method on the client).
NOTE: Constants will not be provided for the ISOs, as they are too dynamic and likely to change.
server = client.servers().get(1)
iso_action = server.attach_iso("virtio-win-0.1.141.iso")
iso_action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To change the reverse DNS record associated with the server, call the change_reverse_dns_entry()
method on the
HetznerCloudServer
object, specifying the IP address you wish to add a record for and the reverse DNS record.
NOTE: If you leave the dns_pointer
parameter as None
, the reverse DNS record will be reverted back to what Hetzner
set it as when they created your server.
server = client.servers().get(1)
action = server.change_reverse_dns_entry("192.168.1.1", "www.google.com")
To change the server name, call the change_name()
method on the HetznerCloudServer
object, specifying a valid name
as the first and only parameter.
NOTE: This method does not return an action, so it is assumed that the update is processed immediately. You can verify this assumption by renaming the server, immediately retrieving it by its id and checking the name of the retrieved server is what you renamed it to.
server = client.servers().get(1)
server.change_name("my-new-server-name")
To change the server type (i.e. from a small, to a large instance), call the change_type()
method on the
HetznerCloudServer
object, specifying the new server type as the first parameter and whether to resize the
disk as the second parameter.
NOTE: Your server needs to be powered off in order for this to work.
NOTE: If you wish to downgrade the server type in the future, make sure you set the upgrade_disk
parameter to False.
Not doing this will result in an error being thrown should you try to downgrade in the future.
server = client.servers().get(1)
action = server.change_type(SERVER_TYPE_2CPU_4GB, False)
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To delete the server, call the delete()
method on the HetznerCloudServer
object.
server = client.servers().get(1)
action = server.delete()
# Wait until the delete action has completed.
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To detach the ISO from the server (if there is one present), call the detach_iso()
method on the HetznerCloudServer
object.
NOTE: Calling this method when no ISO is attached to the server will succeed and not throw an error.
server = client.servers.get(1)
action = server.detach_iso()
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To disable rescue mode on the server, simply call the disable_rescue_mode()
on the server object.
NOTE: Although the API documentation says an error will be returned if rescue mode is already disabled, we have found this is not the case.
server = client.servers().get(1)
disable_action = server.disable_rescue_mode()
disable_action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To enable server backups, simply call the enable_backups()
method on the server object with your chosen backup window.
server = client.servers().get(1)
action = server.enable_backups(BACKUP_WINDOW_2AM_6AM)
This method throws a HetznerActionException
if the backup window is not one of the valid choices (see:
https://docs.hetzner.cloud/#resources-server-actions-post-11).
To enable rescue mode, simply call the enable_rescue_mode()
method on the server object. You can specify a rescue
image and an array of SSH keys to load into it.
NOTE: If you use the FreeBSD rescue image, you will not be able to load in any SSH keys.
NOTE: If you want to use the rescue mode, you will need to reboot your server. This method will not automatically do that for you.
server = client.servers().get(1)
root_password, enable_action = server.enable_rescue_mode(rescue_type=RESCUE_TYPE_LINUX32, ssh_keys=["my-ssh-key"])
enable_action.wait_until_status_is(ACTION_STATUS_SUCCESS)
print("Your root password for the rescue mode is %s" % root_password)
To image a server (i.e. create a backup or a snapshot), call the image()
method on the server object. You can specify
the description of the server and the type of image you are creating. Possible image types are as follows:
backup
- An image that is bound to the server and deleted when the server is. Only available when server backups are enabled.snapshot
- An image created independent of the server and billed seperately.
server = client.servers().get(1)
image_id, image_action = server.image("My backup image", type=IMAGE_TYPE_BACKUP)
image_action.wait_until_status_is(ACTION_STATUS_SUCCESS)
print("The new backup image identifier is %s" % image_id)
To power a server on, simply call the power_on()
method on the server object.
To power a server off, simply call the power_off()
method on the server object.
To rebuild a server from an image, simply call the rebuild_from_image()
method on the server object, passing in the
image id or name you wish to overwrite the server with.
NOTE: This method will destroy all data on the server
server = client.servers().get(1)
action = server.rebuild_from_image(IMAGE_UBUNTU_1604)
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To reset a server (equivelent to pulling the power cord and plugging it back in), simply call the reset()
method on
the server object.
server = client.servers().get(1)
action = server.reset()
action.wait_until_status_is(ACTION_STATUS_SUCCESS)
To reset the password of the Root account on a server, simply call the reset_root_password()
method on the server
object.
server = client.servers().get(1)
root_password, reset_action = server.reset_root_password()
print("The new root password is %s" % root_password)
To shutdown a server gracefully, simply call the shutdown()
method on the server object.
NOTE: The OS on the server you are trying to shut down must support ACPI.
server = client.servers().get(1)
server.shutdown()
You can wait for a server to have a particular status by calling the wait_until_status_is(desired_status)
method on
the server object.
This method will loop a set number of times (defined by the attempts
parameter) and pause each time for one second
(or a timespan defined by modifying the wait_seconds parameter) until the number of attempts is exceeded
or the condition matches.
This is useful when you want to ensure your server is of a particular state before performing any actions on it.
server = client.servers().get(1)
try:
server.wait_until_status_is(SERVER_STATUS_OFF, attempts=50, wait_seconds=10)
except HetznerWaitAttemptsExceededException:
print("Server status was not updated in 50 attempts")
This method throws a HetznerWaitAttemptsExceededException
should the amount of attempts be exceeded with the condition
still being unmet.
The server types top level action can be retrieved by calling the server_types()
method on the HetznerCloudClient
instance.
To retrieve all of the server types available on the Hetzner Cloud service, simply call the get_all()
method, passing
in no parameters.
NOTE: This method returns a generator, so if you wish to get all of the results instantly, you should encapsulate the
call within the list()
function
types = client.server_types().get_all()
for l in types:
print(l.id)
types = list(client.server_types().get_all())
print(types)
To get all server types filtered by a name, call the get_all()
method with the name parameter populated.
server_types = list(client.server_types().get_all(name="cx11"))
print(server_types)
To get a server type by id, simply call the get()
method on the datacenter action, passing in the id of the server type
you wish to get information for.
stype = client.server_types().get(1)
print(stype.name)
To create an SSH key, call the create()
method on the HetznerCloudSSHKeysAction
with the name and the public key of
the SSH key you wish to add.
new_ssh_key = client.ssh_keys().create(name="My new SSH key", public_key="abcdef")
print(new_ssh_key.fingerprint)
To get all SSH keys, call the get_all()
method. NOTE: This object returned from this method is a generator.
ssh_keys = client.ssh_keys().get_all()
for ssh_key in ssh_keys:
print(ssh_key.id)
To get all SSH keys by their name, call the get_all()
method, but pass in the name of the SSH key you wish to search
for.
ssh_keys = list(client.ssh_keys().get_all(name="My SSH key"))
for ssh_key in ssh_keys:
print(ssh_key.id)
ssh_key = client.ssh_keys().get(1)
print(ssh_key.id)
To delete an SSH key, call the delete()
method on the SSH key object.
ssh_key = client.ssh_keys().get(1)
ssh_key.delete()
To update an SSH key's name, call the update()
method on the SSH key object.
ssh_key = client.ssh_keys().get(1)
ssh_key.update(name="Foo")