Add hcloud server module (#53062)
This commit is contained in:
parent
bdfa99af9f
commit
66beeaf032
9 changed files with 832 additions and 0 deletions
63
lib/ansible/module_utils/hcloud.py
Normal file
63
lib/ansible/module_utils/hcloud.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
|
||||||
|
|
||||||
|
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
from ansible.module_utils.ansible_release import __version__
|
||||||
|
from ansible.module_utils.basic import env_fallback, missing_required_lib
|
||||||
|
|
||||||
|
try:
|
||||||
|
import hcloud
|
||||||
|
|
||||||
|
HAS_HCLOUD = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_HCLOUD = False
|
||||||
|
|
||||||
|
|
||||||
|
class Hcloud(object):
|
||||||
|
def __init__(self, module, represent):
|
||||||
|
self.module = module
|
||||||
|
self.represent = represent
|
||||||
|
self.result = {"changed": False, self.represent: None}
|
||||||
|
if not HAS_HCLOUD:
|
||||||
|
module.fail_json(msg=missing_required_lib("hcloud-python"))
|
||||||
|
self._build_client()
|
||||||
|
|
||||||
|
def _build_client(self):
|
||||||
|
self.client = hcloud.Client(
|
||||||
|
token=self.module.params["api_token"],
|
||||||
|
api_endpoint=self.module.params["endpoint"],
|
||||||
|
application_name="ansible-module",
|
||||||
|
application_version=__version__,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mark_as_changed(self):
|
||||||
|
self.result["changed"] = True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def base_module_arguments():
|
||||||
|
return {
|
||||||
|
"api_token": {
|
||||||
|
"type": "str",
|
||||||
|
"required": True,
|
||||||
|
"fallback": (env_fallback, ["HCLOUD_TOKEN"]),
|
||||||
|
"no_log": True,
|
||||||
|
},
|
||||||
|
"endpoint": {"type": "str", "default": "https://api.hetzner.cloud/v1"},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _prepare_result(self):
|
||||||
|
"""Prepare the result for every module
|
||||||
|
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_result(self):
|
||||||
|
if getattr(self, self.represent) is not None:
|
||||||
|
self.result[self.represent] = self._prepare_result()
|
||||||
|
return self.result
|
0
lib/ansible/modules/cloud/hcloud/__init__.py
Normal file
0
lib/ansible/modules/cloud/hcloud/__init__.py
Normal file
380
lib/ansible/modules/cloud/hcloud/hcloud_server.py
Normal file
380
lib/ansible/modules/cloud/hcloud/hcloud_server.py
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
|
"metadata_version": "1.1",
|
||||||
|
"status": ["preview"],
|
||||||
|
"supported_by": "community",
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
---
|
||||||
|
module: hcloud_server
|
||||||
|
|
||||||
|
short_description: Create and manage cloud servers on the Hetzner Cloud.
|
||||||
|
|
||||||
|
version_added: "2.8"
|
||||||
|
|
||||||
|
description:
|
||||||
|
- Create, update and manage cloud servers on the Hetzner Cloud.
|
||||||
|
|
||||||
|
author:
|
||||||
|
- Lukas Kaemmerling (@lkaemmerling)
|
||||||
|
|
||||||
|
options:
|
||||||
|
id:
|
||||||
|
description:
|
||||||
|
- The ID of the Hetzner Cloud server to manage.
|
||||||
|
- Only required if no server I(name) is given
|
||||||
|
type: int
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The Name of the Hetzner Cloud server to manage.
|
||||||
|
- Only required if no server I(id) is given or a server does not exists.
|
||||||
|
type: str
|
||||||
|
server_type:
|
||||||
|
description:
|
||||||
|
- The Server Type of the Hetzner Cloud server to manage.
|
||||||
|
- Required if server does not exists.
|
||||||
|
type: str
|
||||||
|
ssh_keys:
|
||||||
|
description:
|
||||||
|
- List of SSH Keys Names
|
||||||
|
type: list
|
||||||
|
volumes:
|
||||||
|
description:
|
||||||
|
- List of Volumes IDs that should be attached to the server on server creation.
|
||||||
|
type: list
|
||||||
|
image:
|
||||||
|
description:
|
||||||
|
- Image the server should be created from.
|
||||||
|
- Required if server does not exists.
|
||||||
|
type: str
|
||||||
|
location:
|
||||||
|
description:
|
||||||
|
- Location of Server.
|
||||||
|
- Required if no I(datacenter) is given and server does not exists.
|
||||||
|
type: str
|
||||||
|
datacenter:
|
||||||
|
description:
|
||||||
|
- Datacenter of Server.
|
||||||
|
- Required of no I(location) is given and server does not exists.
|
||||||
|
type: str
|
||||||
|
backups:
|
||||||
|
description:
|
||||||
|
- Enable or disable Backups for the given Server.
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
upgrade_disk:
|
||||||
|
description:
|
||||||
|
- Resize the disk size, when resizing a server.
|
||||||
|
- If you want to downgrade the server later, this value should be False.
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
force_upgrade:
|
||||||
|
description:
|
||||||
|
- Force the upgrade of the server.
|
||||||
|
- Power off the server if it is running on upgrade.
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
user_data:
|
||||||
|
description:
|
||||||
|
- User Data to be passed to the server on creation.
|
||||||
|
- Only used if server does not exists.
|
||||||
|
type: str
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- State of the server.
|
||||||
|
default: present
|
||||||
|
choices: [ absent, present, restarted, started, stopped ]
|
||||||
|
type: str
|
||||||
|
extends_documentation_fragment: hcloud
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = """
|
||||||
|
- name: Create a basic server
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
server_type: cx11
|
||||||
|
image: ubuntu-18.04
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create a basic server with ssh key
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
server_type: cx11
|
||||||
|
image: ubuntu-18.04
|
||||||
|
location: fsn1
|
||||||
|
ssh-key: my-ssh-key
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Resize an existing server
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
server_type: cx21
|
||||||
|
keep_disk: yes
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure the server is absent (remove if needed)
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Ensure the server is started
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: Ensure the server is stopped
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
state: stopped
|
||||||
|
|
||||||
|
- name: Ensure the server is restarted
|
||||||
|
hcloud_server:
|
||||||
|
name: my-server
|
||||||
|
state: restarted
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
hcloud_server:
|
||||||
|
description: The server instance
|
||||||
|
returned: Always
|
||||||
|
type: dict
|
||||||
|
sample: {
|
||||||
|
"backup_window": null,
|
||||||
|
"datacenter": "nbg1-dc3",
|
||||||
|
"id": 1937415,
|
||||||
|
"image": "ubuntu-18.04",
|
||||||
|
"ipv4_address": "116.203.104.109",
|
||||||
|
"ipv6": "2a01:4f8:1c1c:c140::/64",
|
||||||
|
"labels": {},
|
||||||
|
"location": "nbg1",
|
||||||
|
"name": "mein-server-2",
|
||||||
|
"rescue_enabled": false,
|
||||||
|
"server_type": "cx11",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils._text import to_native
|
||||||
|
from ansible.module_utils.hcloud import Hcloud
|
||||||
|
|
||||||
|
try:
|
||||||
|
from hcloud.volumes.domain import Volume
|
||||||
|
from hcloud.ssh_keys.domain import SSHKey
|
||||||
|
from hcloud.servers.domain import Server
|
||||||
|
from hcloud import APIException
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleHcloudServer(Hcloud):
|
||||||
|
def __init__(self, module):
|
||||||
|
Hcloud.__init__(self, module, "hcloud_server")
|
||||||
|
self.hcloud_server = None
|
||||||
|
|
||||||
|
def _prepare_result(self):
|
||||||
|
return {
|
||||||
|
"id": to_native(self.hcloud_server.id),
|
||||||
|
"name": to_native(self.hcloud_server.name),
|
||||||
|
"ipv4_address": to_native(self.hcloud_server.public_net.ipv4.ip),
|
||||||
|
"ipv6": to_native(self.hcloud_server.public_net.ipv6.ip),
|
||||||
|
"image": to_native(self.hcloud_server.image.name),
|
||||||
|
"server_type": to_native(self.hcloud_server.server_type.name),
|
||||||
|
"datacenter": to_native(self.hcloud_server.datacenter.name),
|
||||||
|
"location": to_native(self.hcloud_server.datacenter.location.name),
|
||||||
|
"rescue_enabled": self.hcloud_server.rescue_enabled,
|
||||||
|
"backup_window": to_native(self.hcloud_server.backup_window),
|
||||||
|
"labels": self.hcloud_server.labels,
|
||||||
|
"status": to_native(self.hcloud_server.status),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_server(self):
|
||||||
|
try:
|
||||||
|
if self.module.params.get("id") is not None:
|
||||||
|
self.hcloud_server = self.client.servers.get_by_id(
|
||||||
|
self.module.params.get("id")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.hcloud_server = self.client.servers.get_by_name(
|
||||||
|
self.module.params.get("name")
|
||||||
|
)
|
||||||
|
except APIException as e:
|
||||||
|
self.module.fail_json(msg=e.message)
|
||||||
|
|
||||||
|
def _create_server(self):
|
||||||
|
|
||||||
|
self.module.fail_on_missing_params(
|
||||||
|
required_params=["name", "server_type", "image"]
|
||||||
|
)
|
||||||
|
params = {
|
||||||
|
"name": self.module.params.get("name"),
|
||||||
|
"server_type": self.client.server_types.get_by_name(
|
||||||
|
self.module.params.get("server_type")
|
||||||
|
),
|
||||||
|
"image": self.client.images.get_by_name(self.module.params.get("image")),
|
||||||
|
"user_data": self.module.params.get("user_data"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.module.params.get("ssh_keys") is not None:
|
||||||
|
params["ssh_keys"] = [
|
||||||
|
SSHKey(name=ssh_key_name)
|
||||||
|
for ssh_key_name in self.module.params.get("ssh_keys")
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.module.params.get("volumes") is not None:
|
||||||
|
params["volumes"] = [
|
||||||
|
Volume(id=volume_id) for volume_id in self.module.params.get("volumes")
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.module.params.get("location") is None and self.module.params.get("datacenter") is None:
|
||||||
|
# When not given, the API will choose the location.
|
||||||
|
params["location"] = None
|
||||||
|
params["datacenter"] = None
|
||||||
|
elif self.module.params.get("location") is not None and self.module.params.get("datacenter") is None:
|
||||||
|
params["location"] = self.client.locations.get_by_name(
|
||||||
|
self.module.params.get("location")
|
||||||
|
)
|
||||||
|
elif self.module.params.get("location") is None and self.module.params.get("datacenter") is not None:
|
||||||
|
params["datacenter"] = self.client.datacenters.get_by_name(
|
||||||
|
self.module.params.get("datacenter")
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
resp = self.client.servers.create(**params)
|
||||||
|
self.result["root_password"] = resp.root_password
|
||||||
|
resp.action.wait_until_finished()
|
||||||
|
[action.wait_until_finished() for action in resp.next_actions]
|
||||||
|
self._mark_as_changed()
|
||||||
|
self._get_server()
|
||||||
|
|
||||||
|
def _update_server(self):
|
||||||
|
if self.module.params.get("backups") and self.hcloud_server.backup_window is None:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.hcloud_server.enable_backup().wait_until_finished()
|
||||||
|
self._mark_as_changed()
|
||||||
|
elif not self.module.params.get("backups") and self.hcloud_server.backup_window is not None:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.hcloud_server.disable_backup().wait_until_finished()
|
||||||
|
self._mark_as_changed()
|
||||||
|
|
||||||
|
server_type = self.module.params.get("server_type")
|
||||||
|
if server_type is not None and self.hcloud_server.server_type.name != server_type:
|
||||||
|
previous_server_status = self.hcloud_server.status
|
||||||
|
state = self.module.params.get("state")
|
||||||
|
if previous_server_status == Server.STATUS_RUNNING:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
if self.module.params.get("force_upgrade") or state == "stopped":
|
||||||
|
self.stop_server() # Only stopped server can be upgraded
|
||||||
|
else:
|
||||||
|
self.module.warn(
|
||||||
|
"You can not upgrade a running instance %s. You need to stop the instance or use force_upgrade=yes."
|
||||||
|
% self.hcloud_server.name
|
||||||
|
)
|
||||||
|
timeout = 100
|
||||||
|
if self.module.params.get("upgrade_disk"):
|
||||||
|
timeout = (
|
||||||
|
500
|
||||||
|
) # When we upgrade the disk too the resize progress takes some more time.
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.hcloud_server.change_type(
|
||||||
|
server_type=self.client.server_types.get_by_name(server_type),
|
||||||
|
upgrade_disk=self.module.params.get("upgrade_disk"),
|
||||||
|
).wait_until_finished(timeout)
|
||||||
|
if state == "present" and previous_server_status == Server.STATUS_RUNNING or state == "started":
|
||||||
|
self.start_server()
|
||||||
|
|
||||||
|
self._mark_as_changed()
|
||||||
|
self._get_server()
|
||||||
|
|
||||||
|
def start_server(self):
|
||||||
|
if self.hcloud_server.status != Server.STATUS_RUNNING:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.client.servers.power_on(self.hcloud_server).wait_until_finished()
|
||||||
|
self._mark_as_changed()
|
||||||
|
self._get_server()
|
||||||
|
|
||||||
|
def stop_server(self):
|
||||||
|
if self.hcloud_server.status != Server.STATUS_OFF:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.client.servers.power_off(self.hcloud_server).wait_until_finished()
|
||||||
|
self._mark_as_changed()
|
||||||
|
self._get_server()
|
||||||
|
|
||||||
|
def present_server(self):
|
||||||
|
self._get_server()
|
||||||
|
if self.hcloud_server is None:
|
||||||
|
self._create_server()
|
||||||
|
else:
|
||||||
|
self._update_server()
|
||||||
|
|
||||||
|
def delete_server(self):
|
||||||
|
self._get_server()
|
||||||
|
if self.hcloud_server is not None:
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.client.servers.delete(self.hcloud_server).wait_until_finished()
|
||||||
|
self._mark_as_changed()
|
||||||
|
self.hcloud_server = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def define_module():
|
||||||
|
return AnsibleModule(
|
||||||
|
argument_spec=dict(
|
||||||
|
id={"type": "int"},
|
||||||
|
name={"type": "str"},
|
||||||
|
image={"type": "str"},
|
||||||
|
server_type={"type": "str"},
|
||||||
|
location={"type": "str"},
|
||||||
|
datacenter={"type": "str"},
|
||||||
|
user_data={"type": "str"},
|
||||||
|
ssh_keys={"type": "list"},
|
||||||
|
volumes={"type": "list"},
|
||||||
|
backups={"type": "bool", "default": False},
|
||||||
|
upgrade_disk={"type": "bool", "default": False},
|
||||||
|
force_upgrade={"type": "bool", "default": False},
|
||||||
|
state={
|
||||||
|
"choices": ["absent", "present", "restarted", "started", "stopped"],
|
||||||
|
"default": "present",
|
||||||
|
},
|
||||||
|
**Hcloud.base_module_arguments()
|
||||||
|
),
|
||||||
|
required_one_of=[['id', 'name']],
|
||||||
|
mutually_exclusive=[["location", "datacenter"]],
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AnsibleHcloudServer.define_module()
|
||||||
|
|
||||||
|
hcloud = AnsibleHcloudServer(module)
|
||||||
|
state = module.params.get("state")
|
||||||
|
if state == "absent":
|
||||||
|
hcloud.delete_server()
|
||||||
|
elif state == "present":
|
||||||
|
hcloud.present_server()
|
||||||
|
elif state == "started":
|
||||||
|
hcloud.present_server()
|
||||||
|
hcloud.start_server()
|
||||||
|
elif state == "stopped":
|
||||||
|
hcloud.present_server()
|
||||||
|
hcloud.stop_server()
|
||||||
|
elif state == "restarted":
|
||||||
|
hcloud.present_server()
|
||||||
|
hcloud.stop_server()
|
||||||
|
hcloud.start_server()
|
||||||
|
|
||||||
|
module.exit_json(**hcloud.get_result())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
26
lib/ansible/plugins/doc_fragments/hcloud.py
Normal file
26
lib/ansible/plugins/doc_fragments/hcloud.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleDocFragment(object):
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
options:
|
||||||
|
api_token:
|
||||||
|
description:
|
||||||
|
- This is the API Token for the Hetzner Cloud.
|
||||||
|
required: True
|
||||||
|
type: str
|
||||||
|
endpoint:
|
||||||
|
description:
|
||||||
|
- This is the API Endpoint for the Hetzner Cloud.
|
||||||
|
default: https://api.hetzner.cloud/v1
|
||||||
|
type: str
|
||||||
|
requirements:
|
||||||
|
- hcloud-python >= 1.0.0
|
||||||
|
seealso:
|
||||||
|
- name: Documentation for Hetzner Cloud API
|
||||||
|
description: Complete reference for the Hetzner Cloud API.
|
||||||
|
link: https://docs.hetzner.cloud/
|
||||||
|
'''
|
12
test/integration/cloud-config-hcloud.ini.template
Normal file
12
test/integration/cloud-config-hcloud.ini.template
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# This is the configuration template for ansible-test Hetzner Cloud integration tests.
|
||||||
|
#
|
||||||
|
# You do not need this template if you are:
|
||||||
|
#
|
||||||
|
# 1) Running integration tests without using ansible-test.
|
||||||
|
#
|
||||||
|
# If you want to test against the Hetzner Cloud public API,
|
||||||
|
# fill in the values below and save this file without the .template extension.
|
||||||
|
# This will cause ansible-test to use the given configuration.
|
||||||
|
|
||||||
|
[default]
|
||||||
|
hcloud_api_token= @TOKEN
|
2
test/integration/targets/hcloud_server/aliases
Normal file
2
test/integration/targets/hcloud_server/aliases
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
cloud/hcloud
|
||||||
|
unsupported
|
5
test/integration/targets/hcloud_server/defaults/main.yml
Normal file
5
test/integration/targets/hcloud_server/defaults/main.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
---
|
||||||
|
hcloud_prefix: "tests"
|
||||||
|
hcloud_server_name: "{{hcloud_prefix}}-integration"
|
275
test/integration/targets/hcloud_server/tasks/main.yml
Normal file
275
test/integration/targets/hcloud_server/tasks/main.yml
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
---
|
||||||
|
- name: setup
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
- name: verify setup
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is success
|
||||||
|
- name: test missing required parameters on create server
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
register: result
|
||||||
|
ignore_errors: yes
|
||||||
|
- name: verify fail test missing required parameters on create server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
||||||
|
- 'result.msg == "missing required arguments: server_type, image"'
|
||||||
|
|
||||||
|
- name: test create server with check mode
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: cx11
|
||||||
|
image: ubuntu-18.04
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
check_mode: yes
|
||||||
|
- name: test create server server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: test create server
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name}}"
|
||||||
|
server_type: cx11
|
||||||
|
image: ubuntu-18.04
|
||||||
|
state: started
|
||||||
|
register: main_server
|
||||||
|
- name: verify create server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- main_server is changed
|
||||||
|
- main_server.hcloud_server.name == "{{ hcloud_server_name }}"
|
||||||
|
- main_server.hcloud_server.server_type == "cx11"
|
||||||
|
- main_server.hcloud_server.status == "running"
|
||||||
|
- main_server.root_password != ""
|
||||||
|
|
||||||
|
- name: test create server idempotence
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: started
|
||||||
|
register: result
|
||||||
|
- name: verify create server idempotence
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: test stop server with check mode
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
check_mode: yes
|
||||||
|
- name: verify stop server with check mode
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.status == "running"
|
||||||
|
|
||||||
|
- name: test stop server
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify stop server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.status == "off"
|
||||||
|
|
||||||
|
- name: test start server with check mode
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: started
|
||||||
|
register: result
|
||||||
|
check_mode: true
|
||||||
|
- name: verify start server with check mode
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: test start server
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: started
|
||||||
|
register: result
|
||||||
|
- name: verify start server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.status == "running"
|
||||||
|
|
||||||
|
- name: test start server idempotence
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: started
|
||||||
|
register: result
|
||||||
|
- name: verify start server idempotence
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
- result.hcloud_server.status == "running"
|
||||||
|
|
||||||
|
- name: test stop server by its id
|
||||||
|
hcloud_server:
|
||||||
|
id: "{{ main_server.hcloud_server.id }}"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify stop server by its id
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.status == "off"
|
||||||
|
|
||||||
|
- name: test resize server running without force
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx21"
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
check_mode: true
|
||||||
|
- name: verify test resize server running without force
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.server_type == "cx11"
|
||||||
|
|
||||||
|
- name: test resize server with check mode
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx21"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
check_mode: true
|
||||||
|
- name: verify resize server with check mode
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: test resize server without disk
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx21"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify resize server without disk
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.server_type == "cx21"
|
||||||
|
|
||||||
|
- name: test resize server idempotence
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx21"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify resize server idempotence
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: test resize server to smaller plan
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx11"
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify resize server to smaller plan
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.server_type == "cx11"
|
||||||
|
|
||||||
|
- name: test resize server with disk
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
server_type: "cx21"
|
||||||
|
upgrade_disk: true
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify resize server with disk
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.server_type == "cx21"
|
||||||
|
|
||||||
|
- name: test enable backups with check mode
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
backups: true
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
check_mode: true
|
||||||
|
- name: verify enable backups with check mode
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: test enable backups
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
backups: true
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify enable backups
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.hcloud_server.backup_window != ""
|
||||||
|
|
||||||
|
- name: test enable backups idempotence
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
backups: true
|
||||||
|
state: stopped
|
||||||
|
register: result
|
||||||
|
- name: verify enable backups idempotence
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
- result.hcloud_server.backup_window != ""
|
||||||
|
|
||||||
|
- name: absent server
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
- name: verify absent server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is success
|
||||||
|
|
||||||
|
- name: test create server with ssh key
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name}}"
|
||||||
|
server_type: cx11
|
||||||
|
image: "ubuntu-18.04"
|
||||||
|
ssh_keys:
|
||||||
|
- ci@ansible.hetzner.cloud
|
||||||
|
state: started
|
||||||
|
register: main_server
|
||||||
|
- name: verify create server
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- main_server is changed
|
||||||
|
- main_server.hcloud_server.name == "{{ hcloud_server_name }}"
|
||||||
|
- main_server.hcloud_server.server_type == "cx11"
|
||||||
|
- main_server.hcloud_server.status == "running"
|
||||||
|
- main_server.root_password != ""
|
||||||
|
|
||||||
|
- name: cleanup
|
||||||
|
hcloud_server:
|
||||||
|
name: "{{ hcloud_server_name }}"
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
- name: verify cleanup
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is success
|
69
test/runner/lib/cloud/hcloud.py
Normal file
69
test/runner/lib/cloud/hcloud.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
"""Hetzner Cloud plugin for integration tests."""
|
||||||
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
|
from os.path import isfile
|
||||||
|
|
||||||
|
from lib.cloud import (
|
||||||
|
CloudProvider,
|
||||||
|
CloudEnvironment,
|
||||||
|
CloudEnvironmentConfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
from lib.util import ConfigParser, display
|
||||||
|
|
||||||
|
|
||||||
|
class HcloudCloudProvider(CloudProvider):
|
||||||
|
"""Hetzner Cloud provider plugin. Sets up cloud resources before
|
||||||
|
delegation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
"""
|
||||||
|
:type args: TestConfig
|
||||||
|
"""
|
||||||
|
super(HcloudCloudProvider, self).__init__(args)
|
||||||
|
|
||||||
|
def filter(self, targets, exclude):
|
||||||
|
"""Filter out the cloud tests when the necessary config and resources are not available.
|
||||||
|
:type targets: tuple[TestTarget]
|
||||||
|
:type exclude: list[str]
|
||||||
|
"""
|
||||||
|
if isfile(self.config_static_path):
|
||||||
|
return
|
||||||
|
|
||||||
|
super(HcloudCloudProvider, self).filter(targets, exclude)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""Setup the cloud resource before delegation and register a cleanup callback."""
|
||||||
|
super(HcloudCloudProvider, self).setup()
|
||||||
|
|
||||||
|
if isfile(self.config_static_path):
|
||||||
|
self.config_path = self.config_static_path
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class HcloudCloudEnvironment(CloudEnvironment):
|
||||||
|
"""Hetzner Cloud cloud environment plugin. Updates integration test environment
|
||||||
|
after delegation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_environment_config(self):
|
||||||
|
parser = ConfigParser()
|
||||||
|
parser.read(self.config_path)
|
||||||
|
|
||||||
|
env_vars = dict(
|
||||||
|
HCLOUD_TOKEN=parser.get('default', 'hcloud_api_token'),
|
||||||
|
)
|
||||||
|
|
||||||
|
ansible_vars = dict(
|
||||||
|
hcloud_prefix=self.resource_prefix,
|
||||||
|
)
|
||||||
|
|
||||||
|
ansible_vars.update(dict((key.lower(), value) for key, value in env_vars.items()))
|
||||||
|
|
||||||
|
return CloudEnvironmentConfig(
|
||||||
|
env_vars=env_vars,
|
||||||
|
ansible_vars=ansible_vars,
|
||||||
|
)
|
Loading…
Reference in a new issue