2020-03-09 13:36:01 +00:00
|
|
|
#!/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
|
|
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: hcloud_server
|
|
|
|
|
|
|
|
short_description: Create and manage cloud servers on the Hetzner Cloud.
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Only required if no server I(id) is given or a server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
server_type:
|
|
|
|
description:
|
|
|
|
- The Server Type of the Hetzner Cloud server to manage.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Required if server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
ssh_keys:
|
|
|
|
description:
|
|
|
|
- List of SSH key names
|
|
|
|
- The key names correspond to the SSH keys configured for your
|
|
|
|
Hetzner Cloud account access.
|
|
|
|
type: list
|
2020-06-29 11:53:01 +00:00
|
|
|
elements: str
|
2020-03-09 13:36:01 +00:00
|
|
|
volumes:
|
|
|
|
description:
|
|
|
|
- List of Volumes IDs that should be attached to the server on server creation.
|
|
|
|
type: list
|
2020-06-29 11:53:01 +00:00
|
|
|
elements: str
|
2021-03-11 10:07:41 +00:00
|
|
|
firewalls:
|
|
|
|
description:
|
|
|
|
- List of Firewall IDs that should be attached to the server on server creation.
|
|
|
|
type: list
|
|
|
|
elements: str
|
2020-03-09 13:36:01 +00:00
|
|
|
image:
|
|
|
|
description:
|
|
|
|
- Image the server should be created from.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Required if server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
location:
|
|
|
|
description:
|
|
|
|
- Location of Server.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Required if no I(datacenter) is given and server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
datacenter:
|
|
|
|
description:
|
|
|
|
- Datacenter of Server.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Required of no I(location) is given and server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
backups:
|
|
|
|
description:
|
|
|
|
- Enable or disable Backups for the given Server.
|
|
|
|
type: bool
|
|
|
|
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
|
2023-06-21 08:49:54 +00:00
|
|
|
default: false
|
2022-06-29 07:58:01 +00:00
|
|
|
enable_ipv4:
|
|
|
|
description:
|
|
|
|
- Enables the public ipv4 address
|
|
|
|
type: bool
|
2023-06-21 08:49:54 +00:00
|
|
|
default: true
|
2022-06-29 07:58:01 +00:00
|
|
|
enable_ipv6:
|
|
|
|
description:
|
|
|
|
- Enables the public ipv6 address
|
|
|
|
type: bool
|
2023-06-21 08:49:54 +00:00
|
|
|
default: true
|
2022-06-29 07:58:01 +00:00
|
|
|
ipv4:
|
|
|
|
description:
|
|
|
|
- ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a new ipv4 Primary IP will automatically be created
|
|
|
|
type: str
|
|
|
|
ipv6:
|
|
|
|
description:
|
|
|
|
- ID of the ipv6 Primary IP to use. If omitted and enable_ipv6 is true, a new ipv6 Primary IP will automatically be created.
|
|
|
|
type: str
|
2022-11-09 21:31:03 +00:00
|
|
|
private_networks:
|
|
|
|
description:
|
|
|
|
- List of private networks the server is attached to (name or ID)
|
2022-12-20 13:49:48 +00:00
|
|
|
- If None, private networks are left as they are (e.g. if previously added by hcloud_server_network),
|
|
|
|
if it has any other value (including []), only those networks are attached to the server.
|
2022-11-09 21:31:03 +00:00
|
|
|
type: list
|
|
|
|
elements: str
|
2020-03-09 13:36:01 +00:00
|
|
|
force_upgrade:
|
|
|
|
description:
|
2021-08-16 10:31:02 +00:00
|
|
|
- Deprecated
|
2020-03-09 13:36:01 +00:00
|
|
|
- Force the upgrade of the server.
|
|
|
|
- Power off the server if it is running on upgrade.
|
|
|
|
type: bool
|
2021-08-16 10:31:02 +00:00
|
|
|
force:
|
|
|
|
description:
|
|
|
|
- Force the update of the server.
|
|
|
|
- May power off the server if update.
|
|
|
|
type: bool
|
2023-06-21 08:49:54 +00:00
|
|
|
default: false
|
2021-04-07 06:15:10 +00:00
|
|
|
allow_deprecated_image:
|
|
|
|
description:
|
|
|
|
- Allows the creation of servers with deprecated images.
|
|
|
|
type: bool
|
2023-06-21 08:49:54 +00:00
|
|
|
default: false
|
2020-03-09 13:36:01 +00:00
|
|
|
user_data:
|
|
|
|
description:
|
|
|
|
- User Data to be passed to the server on creation.
|
2022-04-25 04:28:21 +00:00
|
|
|
- Only used if server does not exist.
|
2020-03-09 13:36:01 +00:00
|
|
|
type: str
|
|
|
|
rescue_mode:
|
|
|
|
description:
|
|
|
|
- Add the Hetzner rescue system type you want the server to be booted into.
|
|
|
|
type: str
|
|
|
|
labels:
|
|
|
|
description:
|
|
|
|
- User-defined labels (key-value pairs).
|
|
|
|
type: dict
|
|
|
|
delete_protection:
|
|
|
|
description:
|
|
|
|
- Protect the Server for deletion.
|
|
|
|
- Needs to be the same as I(rebuild_protection).
|
|
|
|
type: bool
|
|
|
|
rebuild_protection:
|
|
|
|
description:
|
|
|
|
- Protect the Server for rebuild.
|
|
|
|
- Needs to be the same as I(delete_protection).
|
|
|
|
type: bool
|
2021-08-16 10:31:02 +00:00
|
|
|
placement_group:
|
|
|
|
description:
|
|
|
|
- Placement Group of the server.
|
|
|
|
type: str
|
2020-03-09 13:36:01 +00:00
|
|
|
state:
|
|
|
|
description:
|
|
|
|
- State of the server.
|
|
|
|
default: present
|
|
|
|
choices: [ absent, present, restarted, started, stopped, rebuild ]
|
|
|
|
type: str
|
|
|
|
extends_documentation_fragment:
|
|
|
|
- hetzner.hcloud.hcloud
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = """
|
|
|
|
- name: Create a basic server
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
server_type: cx11
|
2023-06-20 09:38:51 +00:00
|
|
|
image: ubuntu-22.04
|
2020-03-09 13:36:01 +00:00
|
|
|
state: present
|
|
|
|
|
|
|
|
- name: Create a basic server with ssh key
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
server_type: cx11
|
2023-06-20 09:38:51 +00:00
|
|
|
image: ubuntu-22.04
|
2020-03-09 13:36:01 +00:00
|
|
|
location: fsn1
|
|
|
|
ssh_keys:
|
|
|
|
- me@myorganisation
|
|
|
|
state: present
|
|
|
|
|
|
|
|
- name: Resize an existing server
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
server_type: cx21
|
2023-06-21 08:49:54 +00:00
|
|
|
upgrade_disk: true
|
2020-03-09 13:36:01 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
- name: Ensure the server is will be booted in rescue mode and therefore restarted
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
rescue_mode: linux64
|
|
|
|
state: restarted
|
|
|
|
|
|
|
|
- name: Ensure the server is rebuild
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
2023-06-20 09:38:51 +00:00
|
|
|
image: ubuntu-22.04
|
2020-03-09 13:36:01 +00:00
|
|
|
state: rebuild
|
2021-08-16 10:31:02 +00:00
|
|
|
|
|
|
|
- name: Add server to placement group
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
placement_group: my-placement-group
|
|
|
|
force: True
|
|
|
|
state: present
|
|
|
|
|
|
|
|
- name: Remove server from placement group
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
placement_group: null
|
|
|
|
state: present
|
2022-11-09 21:31:03 +00:00
|
|
|
|
|
|
|
- name: Add server with private network only
|
|
|
|
hcloud_server:
|
|
|
|
name: my-server
|
|
|
|
enable_ipv4: false
|
|
|
|
enable_ipv6: false
|
|
|
|
private_networks:
|
|
|
|
- my-network
|
|
|
|
- 4711
|
|
|
|
state: present
|
2020-03-09 13:36:01 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
RETURN = """
|
|
|
|
hcloud_server:
|
|
|
|
description: The server instance
|
|
|
|
returned: Always
|
|
|
|
type: complex
|
|
|
|
contains:
|
|
|
|
id:
|
|
|
|
description: Numeric identifier of the server
|
|
|
|
returned: always
|
|
|
|
type: int
|
|
|
|
sample: 1937415
|
|
|
|
name:
|
|
|
|
description: Name of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: my-server
|
|
|
|
status:
|
|
|
|
description: Status of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: running
|
|
|
|
server_type:
|
|
|
|
description: Name of the server type of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: cx11
|
|
|
|
ipv4_address:
|
|
|
|
description: Public IPv4 address of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: 116.203.104.109
|
|
|
|
ipv6:
|
|
|
|
description: IPv6 network of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: 2a01:4f8:1c1c:c140::/64
|
2022-11-09 21:31:03 +00:00
|
|
|
private_networks:
|
|
|
|
description: List of private networks the server is attached to (name or ID)
|
|
|
|
returned: always
|
|
|
|
type: list
|
|
|
|
elements: str
|
|
|
|
sample: ['my-network', 'another-network', '4711']
|
2023-01-31 11:29:27 +00:00
|
|
|
private_networks_info:
|
|
|
|
description: List of private networks the server is attached to (dict with name and ip)
|
|
|
|
returned: always
|
|
|
|
type: list
|
|
|
|
elements: dict
|
|
|
|
sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}]
|
2020-03-09 13:36:01 +00:00
|
|
|
location:
|
|
|
|
description: Name of the location of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: fsn1
|
2021-08-16 10:31:02 +00:00
|
|
|
placement_group:
|
|
|
|
description: Placement Group of the server
|
|
|
|
type: str
|
|
|
|
returned: always
|
|
|
|
sample: 4711
|
|
|
|
version_added: "1.5.0"
|
2020-03-09 13:36:01 +00:00
|
|
|
datacenter:
|
|
|
|
description: Name of the datacenter of the server
|
|
|
|
returned: always
|
|
|
|
type: str
|
|
|
|
sample: fsn1-dc14
|
|
|
|
rescue_enabled:
|
|
|
|
description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot
|
|
|
|
returned: always
|
|
|
|
type: bool
|
|
|
|
sample: false
|
|
|
|
backup_window:
|
|
|
|
description: Time window (UTC) in which the backup will run, or null if the backups are not enabled
|
|
|
|
returned: always
|
|
|
|
type: bool
|
|
|
|
sample: 22-02
|
|
|
|
labels:
|
|
|
|
description: User-defined labels (key-value pairs)
|
|
|
|
returned: always
|
|
|
|
type: dict
|
|
|
|
delete_protection:
|
|
|
|
description: True if server is protected for deletion
|
|
|
|
type: bool
|
|
|
|
returned: always
|
|
|
|
sample: false
|
2020-06-29 13:41:30 +00:00
|
|
|
version_added: "0.1.0"
|
2020-03-09 13:36:01 +00:00
|
|
|
rebuild_protection:
|
|
|
|
description: True if server is protected for rebuild
|
|
|
|
type: bool
|
|
|
|
returned: always
|
|
|
|
sample: false
|
2020-06-29 13:41:30 +00:00
|
|
|
version_added: "0.1.0"
|
2020-03-09 13:36:01 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils._text import to_native
|
|
|
|
from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud
|
2023-06-23 12:43:42 +00:00
|
|
|
from datetime import timedelta, datetime, timezone
|
2020-03-09 13:36:01 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from hcloud.volumes.domain import Volume
|
|
|
|
from hcloud.ssh_keys.domain import SSHKey
|
2022-06-29 07:58:01 +00:00
|
|
|
from hcloud.servers.domain import Server, ServerCreatePublicNetwork
|
2023-03-31 07:01:22 +00:00
|
|
|
from hcloud.firewalls.domain import FirewallResource
|
2020-03-09 13:36:01 +00:00
|
|
|
except ImportError:
|
2020-11-24 05:39:21 +00:00
|
|
|
Volume = None
|
|
|
|
SSHKey = None
|
|
|
|
Server = None
|
2022-06-29 07:58:01 +00:00
|
|
|
ServerCreatePublicNetwork = None
|
2021-03-11 10:07:41 +00:00
|
|
|
FirewallResource = None
|
2020-03-09 13:36:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
class AnsibleHcloudServer(Hcloud):
|
|
|
|
def __init__(self, module):
|
|
|
|
Hcloud.__init__(self, module, "hcloud_server")
|
|
|
|
self.hcloud_server = None
|
|
|
|
|
|
|
|
def _prepare_result(self):
|
|
|
|
image = None if self.hcloud_server.image is None else to_native(self.hcloud_server.image.name)
|
2022-06-29 07:58:01 +00:00
|
|
|
placement_group = None if self.hcloud_server.placement_group is None else to_native(
|
|
|
|
self.hcloud_server.placement_group.name)
|
|
|
|
ipv4_address = None if self.hcloud_server.public_net.ipv4 is None else to_native(
|
|
|
|
self.hcloud_server.public_net.ipv4.ip)
|
|
|
|
ipv6 = None if self.hcloud_server.public_net.ipv6 is None else to_native(self.hcloud_server.public_net.ipv6.ip)
|
2022-11-07 13:49:19 +00:00
|
|
|
backup_window = None if self.hcloud_server.backup_window is None else to_native(self.hcloud_server.backup_window)
|
2020-03-09 13:36:01 +00:00
|
|
|
return {
|
|
|
|
"id": to_native(self.hcloud_server.id),
|
|
|
|
"name": to_native(self.hcloud_server.name),
|
2022-06-29 07:58:01 +00:00
|
|
|
"ipv4_address": ipv4_address,
|
|
|
|
"ipv6": ipv6,
|
2022-11-09 21:31:03 +00:00
|
|
|
"private_networks": [to_native(net.network.name) for net in self.hcloud_server.private_net],
|
2023-01-31 11:29:27 +00:00
|
|
|
"private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in self.hcloud_server.private_net],
|
2020-03-09 13:36:01 +00:00
|
|
|
"image": image,
|
|
|
|
"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),
|
2021-08-16 10:31:02 +00:00
|
|
|
"placement_group": placement_group,
|
2020-03-09 13:36:01 +00:00
|
|
|
"rescue_enabled": self.hcloud_server.rescue_enabled,
|
2022-11-07 13:49:19 +00:00
|
|
|
"backup_window": backup_window,
|
2020-03-09 13:36:01 +00:00
|
|
|
"labels": self.hcloud_server.labels,
|
|
|
|
"delete_protection": self.hcloud_server.protection["delete"],
|
|
|
|
"rebuild_protection": self.hcloud_server.protection["rebuild"],
|
|
|
|
"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")
|
|
|
|
)
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2020-03-09 13:36:01 +00:00
|
|
|
self.module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
def _create_server(self):
|
|
|
|
self.module.fail_on_missing_params(
|
|
|
|
required_params=["name", "server_type", "image"]
|
|
|
|
)
|
2020-05-06 05:44:05 +00:00
|
|
|
|
2023-04-12 08:43:19 +00:00
|
|
|
server_type = self._get_server_type()
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
params = {
|
|
|
|
"name": self.module.params.get("name"),
|
2023-04-12 08:43:19 +00:00
|
|
|
"server_type": server_type,
|
2020-03-09 13:36:01 +00:00
|
|
|
"user_data": self.module.params.get("user_data"),
|
|
|
|
"labels": self.module.params.get("labels"),
|
2023-04-12 08:43:19 +00:00
|
|
|
"image": self._get_image(server_type),
|
2021-08-16 10:31:02 +00:00
|
|
|
"placement_group": self._get_placement_group(),
|
2022-06-29 07:58:01 +00:00
|
|
|
"public_net": ServerCreatePublicNetwork(
|
|
|
|
enable_ipv4=self.module.params.get("enable_ipv4"),
|
|
|
|
enable_ipv6=self.module.params.get("enable_ipv6")
|
|
|
|
)
|
2020-03-09 13:36:01 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 07:58:01 +00:00
|
|
|
if self.module.params.get("ipv4") is not None:
|
|
|
|
p = self.client.primary_ips.get_by_name(self.module.params.get("ipv4"))
|
|
|
|
if not p:
|
|
|
|
p = self.client.primary_ips.get_by_id(self.module.params.get("ipv4"))
|
|
|
|
params["public_net"].ipv4 = p
|
|
|
|
|
|
|
|
if self.module.params.get("ipv6") is not None:
|
|
|
|
p = self.client.primary_ips.get_by_name(self.module.params.get("ipv6"))
|
|
|
|
if not p:
|
|
|
|
p = self.client.primary_ips.get_by_id(self.module.params.get("ipv6"))
|
|
|
|
params["public_net"].ipv6 = p
|
|
|
|
|
2022-11-09 21:31:03 +00:00
|
|
|
if self.module.params.get("private_networks") is not None:
|
|
|
|
_networks = []
|
|
|
|
for network_name_or_id in self.module.params.get("private_networks"):
|
|
|
|
_networks.append(
|
|
|
|
self.client.networks.get_by_name(network_name_or_id)
|
|
|
|
or self.client.networks.get_by_id(network_name_or_id)
|
|
|
|
)
|
|
|
|
params["networks"] = _networks
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
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")
|
|
|
|
]
|
2021-03-11 10:07:41 +00:00
|
|
|
if self.module.params.get("firewalls") is not None:
|
|
|
|
params["firewalls"] = []
|
|
|
|
for fw in self.module.params.get("firewalls"):
|
|
|
|
f = self.client.firewalls.get_by_name(fw)
|
|
|
|
if f is not None:
|
|
|
|
# When firewall name is not available look for id instead
|
|
|
|
params["firewalls"].append(f)
|
|
|
|
else:
|
|
|
|
params["firewalls"].append(self.client.firewalls.get_by_id(fw))
|
2020-03-09 13:36:01 +00:00
|
|
|
|
|
|
|
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")
|
|
|
|
)
|
|
|
|
|
2022-07-13 06:54:55 +00:00
|
|
|
if self.module.params.get("state") == "stopped":
|
|
|
|
params["start_after_create"] = False
|
2020-03-09 13:36:01 +00:00
|
|
|
if not self.module.check_mode:
|
2021-02-24 12:37:58 +00:00
|
|
|
try:
|
|
|
|
resp = self.client.servers.create(**params)
|
|
|
|
self.result["root_password"] = resp.root_password
|
|
|
|
resp.action.wait_until_finished(max_retries=1000)
|
|
|
|
[action.wait_until_finished() for action in resp.next_actions]
|
|
|
|
|
|
|
|
rescue_mode = self.module.params.get("rescue_mode")
|
|
|
|
if rescue_mode:
|
|
|
|
self._get_server()
|
|
|
|
self._set_rescue_mode(rescue_mode)
|
2020-03-09 13:36:01 +00:00
|
|
|
|
2021-02-24 12:37:58 +00:00
|
|
|
backups = self.module.params.get("backups")
|
|
|
|
if backups:
|
|
|
|
self._get_server()
|
|
|
|
self.hcloud_server.enable_backup().wait_until_finished()
|
2020-07-13 09:52:14 +00:00
|
|
|
|
2021-02-24 12:37:58 +00:00
|
|
|
delete_protection = self.module.params.get("delete_protection")
|
|
|
|
rebuild_protection = self.module.params.get("rebuild_protection")
|
|
|
|
if delete_protection is not None and rebuild_protection is not None:
|
|
|
|
self._get_server()
|
|
|
|
self.hcloud_server.change_protection(delete=delete_protection,
|
|
|
|
rebuild=rebuild_protection).wait_until_finished()
|
|
|
|
except Exception as e:
|
|
|
|
self.module.fail_json(msg=e.message)
|
2020-03-09 13:36:01 +00:00
|
|
|
self._mark_as_changed()
|
|
|
|
self._get_server()
|
|
|
|
|
2023-04-12 08:43:19 +00:00
|
|
|
def _get_image(self, server_type):
|
|
|
|
image_resp = self.client.images.get_list(name=self.module.params.get("image"), architecture=server_type.architecture, include_deprecated=True)
|
2021-04-07 06:15:10 +00:00
|
|
|
images = getattr(image_resp, 'images')
|
|
|
|
image = None
|
|
|
|
if images is not None and len(images) > 0:
|
|
|
|
# If image name is not available look for id instead
|
|
|
|
image = images[0]
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
image = self.client.images.get_by_id(self.module.params.get("image"))
|
2021-04-22 06:39:34 +00:00
|
|
|
except Exception:
|
|
|
|
self.module.fail_json(msg="Image %s was not found" % self.module.params.get('image'))
|
2021-04-07 06:15:10 +00:00
|
|
|
if image.deprecated is not None:
|
2021-04-22 06:39:34 +00:00
|
|
|
available_until = image.deprecated + timedelta(days=90)
|
2021-04-07 06:15:10 +00:00
|
|
|
if self.module.params.get("allow_deprecated_image"):
|
|
|
|
self.module.warn(
|
fix(server): TypeError when using deprecated image (#219)
When using a deprecated image with the "allow_deprecated_image" flag,
the script would crash because the string interpolation was not done
correctly. The brackes were in the wrong place.
Error would look like this:
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: unsupported operand type(s) for %: 'NoneType' and 'str'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n File \"/home/julian/.ansible/tmp/ansible-tmp-1686564668.6136558-165294-192879183382787/AnsiballZ_hcloud_server.py\", line 107, in <module>\n _ansiballz_main()\n File \"/home/julian/.ansible/tmp/ansible-tmp-1686564668.6136558-165294-192879183382787/AnsiballZ_hcloud_server.py\", line 99, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/home/julian/.ansible/tmp/ansible-tmp-1686564668.6136558-165294-192879183382787/AnsiballZ_hcloud_server.py\", line 47, in invoke_module\n runpy.run_module(mod_name='ansible_collections.hetzner.hcloud.plugins.modules.hcloud_server', init_globals=dict(_module_fqn='ansible_collections.hetzner.hcloud.plugins.modules.hcloud_server', _modlib_path=modlib_path),\n File \"/nix/store/95cxzy2hpizr23343b8bskl4yacf4b3l-python3-3.10.11/lib/python3.10/runpy.py\", line 224, in run_module\n return _run_module_code(code, init_globals, run_name, mod_spec)\n File \"/nix/store/95cxzy2hpizr23343b8bskl4yacf4b3l-python3-3.10.11/lib/python3.10/runpy.py\", line 96, in _run_module_code\n _run_code(code, mod_globals, init_globals,\n File \"/nix/store/95cxzy2hpizr23343b8bskl4yacf4b3l-python3-3.10.11/lib/python3.10/runpy.py\", line 86, in _run_code\n exec(code, run_globals)\n File \"/run/user/1000/ansible_hcloud_server_payload_1g2rf66q/ansible_hcloud_server_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py\", line 927, in <module>\n File \"/run/user/1000/ansible_hcloud_server_payload_1g2rf66q/ansible_hcloud_server_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py\", line 908, in main\n File \"/run/user/1000/ansible_hcloud_server_payload_1g2rf66q/ansible_hcloud_server_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py\", line 843, in present_server\n File \"/run/user/1000/ansible_hcloud_server_payload_1g2rf66q/ansible_hcloud_server_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py\", line 411, in _create_server\n File \"/run/user/1000/ansible_hcloud_server_payload_1g2rf66q/ansible_hcloud_server_payload.zip/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py\", line 518, in _get_image\nTypeError: unsupported operand type(s) for %: 'NoneType' and 'str'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
2023-06-19 13:17:07 +00:00
|
|
|
"You try to use a deprecated image. The image %s will continue to be available until %s." % (
|
|
|
|
image.name, available_until.strftime('%Y-%m-%d')))
|
2021-04-07 06:15:10 +00:00
|
|
|
else:
|
2021-04-22 06:39:34 +00:00
|
|
|
self.module.fail_json(
|
|
|
|
msg=("You try to use a deprecated image. The image %s will continue to be available until %s." +
|
2023-06-21 08:49:54 +00:00
|
|
|
" If you want to use this image use allow_deprecated_image=true."
|
2021-04-22 06:39:34 +00:00
|
|
|
) % (image.name, available_until.strftime('%Y-%m-%d')))
|
2021-04-07 06:15:10 +00:00
|
|
|
return image
|
|
|
|
|
2021-04-22 08:24:41 +00:00
|
|
|
def _get_server_type(self):
|
|
|
|
server_type = self.client.server_types.get_by_name(
|
|
|
|
self.module.params.get("server_type")
|
|
|
|
)
|
|
|
|
if server_type is None:
|
|
|
|
try:
|
|
|
|
server_type = self.client.server_types.get_by_id(self.module.params.get("server_type"))
|
|
|
|
except Exception:
|
|
|
|
self.module.fail_json(msg="server_type %s was not found" % self.module.params.get('server_type'))
|
|
|
|
|
2023-06-20 09:38:38 +00:00
|
|
|
self._check_and_warn_deprecated_server(server_type)
|
|
|
|
|
2021-04-22 08:24:41 +00:00
|
|
|
return server_type
|
|
|
|
|
2023-06-20 09:38:38 +00:00
|
|
|
def _check_and_warn_deprecated_server(self, server_type):
|
|
|
|
if server_type.deprecation is None:
|
|
|
|
return
|
|
|
|
|
2023-06-23 12:43:42 +00:00
|
|
|
if server_type.deprecation.unavailable_after < datetime.now(timezone.utc):
|
2023-06-20 09:38:38 +00:00
|
|
|
self.module.warn(
|
|
|
|
'Attention: The server plan %s is deprecated and can no longer be ordered. Existing servers of ' % server_type.name
|
|
|
|
+ 'that plan will continue to work as before and no action is required on your part. It is possible '
|
|
|
|
'to migrate this server to another server plan by setting the server_type parameter on the hetzner.hcloud.hcloud_server module.'
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
self.module.warn(
|
|
|
|
'Attention: The server plan % is deprecated and will no longer be available for order as of ' % server_type.name
|
|
|
|
+ '%s. Existing servers of that plan will continue to work as before ' % server_type.deprecation.unavailable_after.strftime("%Y-%m-%d")
|
|
|
|
+ 'and no action is required on your part. It is possible to migrate this server to another server plan by setting '
|
|
|
|
'the server_type parameter on the hetzner.hcloud.hcloud_server module.'
|
|
|
|
)
|
|
|
|
|
2021-08-16 10:31:02 +00:00
|
|
|
def _get_placement_group(self):
|
|
|
|
if self.module.params.get("placement_group") is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
placement_group = self.client.placement_groups.get_by_name(
|
|
|
|
self.module.params.get("placement_group")
|
|
|
|
)
|
|
|
|
if placement_group is None:
|
|
|
|
try:
|
|
|
|
placement_group = self.client.placement_groups.get_by_id(self.module.params.get("placement_group"))
|
|
|
|
except Exception:
|
2022-06-29 07:58:01 +00:00
|
|
|
self.module.fail_json(
|
|
|
|
msg="placement_group %s was not found" % self.module.params.get("placement_group"))
|
2021-08-16 10:31:02 +00:00
|
|
|
|
|
|
|
return placement_group
|
|
|
|
|
2022-06-29 07:58:01 +00:00
|
|
|
def _get_primary_ip(self, field):
|
|
|
|
if self.module.params.get(field) is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
primary_ip = self.client.primary_ips.get_by_name(
|
|
|
|
self.module.params.get(field)
|
|
|
|
)
|
|
|
|
if primary_ip is None:
|
|
|
|
try:
|
|
|
|
primary_ip = self.client.primary_ips.get_by_id(self.module.params.get(field))
|
|
|
|
except Exception as e:
|
|
|
|
self.module.fail_json(
|
|
|
|
msg="primary_ip %s was not found" % self.module.params.get(field))
|
|
|
|
|
|
|
|
return primary_ip
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
def _update_server(self):
|
2023-05-16 09:29:41 +00:00
|
|
|
if "force_upgrade" in self.module.params and self.module.params.get("force_upgrade") is not None:
|
2022-06-29 07:58:01 +00:00
|
|
|
self.module.warn("force_upgrade is deprecated, use force instead")
|
2021-08-16 10:31:02 +00:00
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
try:
|
2021-08-16 10:31:02 +00:00
|
|
|
previous_server_status = self.hcloud_server.status
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
rescue_mode = self.module.params.get("rescue_mode")
|
|
|
|
if rescue_mode and self.hcloud_server.rescue_enabled is False:
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self._set_rescue_mode(rescue_mode)
|
|
|
|
self._mark_as_changed()
|
|
|
|
elif not rescue_mode and self.hcloud_server.rescue_enabled is True:
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.disable_rescue().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
|
2023-04-03 10:36:33 +00:00
|
|
|
backups = self.module.params.get("backups")
|
|
|
|
if backups and self.hcloud_server.backup_window is None:
|
2020-03-09 13:36:01 +00:00
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.enable_backup().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
2023-04-03 10:36:33 +00:00
|
|
|
elif backups is not None and not backups and self.hcloud_server.backup_window is not None:
|
2020-03-09 13:36:01 +00:00
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.disable_backup().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
|
|
|
|
labels = self.module.params.get("labels")
|
|
|
|
if labels is not None and labels != self.hcloud_server.labels:
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.update(labels=labels)
|
|
|
|
self._mark_as_changed()
|
|
|
|
|
2021-04-06 09:28:38 +00:00
|
|
|
wanted_firewalls = self.module.params.get("firewalls")
|
|
|
|
if wanted_firewalls is not None:
|
|
|
|
# Removing existing but not wanted firewalls
|
|
|
|
for current_firewall in self.hcloud_server.public_net.firewalls:
|
|
|
|
if current_firewall.firewall.name not in wanted_firewalls:
|
|
|
|
self._mark_as_changed()
|
|
|
|
if not self.module.check_mode:
|
2021-03-11 10:07:41 +00:00
|
|
|
r = FirewallResource(type="server", server=self.hcloud_server)
|
2021-04-06 09:28:38 +00:00
|
|
|
actions = self.client.firewalls.remove_from_resources(current_firewall.firewall, [r])
|
2021-03-11 10:07:41 +00:00
|
|
|
for a in actions:
|
|
|
|
a.wait_until_finished()
|
|
|
|
|
2021-04-06 09:28:38 +00:00
|
|
|
# Adding wanted firewalls that doesn't exist yet
|
|
|
|
for fname in wanted_firewalls:
|
|
|
|
found = False
|
|
|
|
for f in self.hcloud_server.public_net.firewalls:
|
|
|
|
if f.firewall.name == fname:
|
|
|
|
found = True
|
|
|
|
break
|
|
|
|
|
|
|
|
if not found:
|
|
|
|
self._mark_as_changed()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
fw = self.client.firewalls.get_by_name(fname)
|
2021-07-16 09:37:33 +00:00
|
|
|
if fw is None:
|
|
|
|
self.module.fail_json(msg="firewall %s was not found" % fname)
|
2021-03-11 10:07:41 +00:00
|
|
|
r = FirewallResource(type="server", server=self.hcloud_server)
|
|
|
|
actions = self.client.firewalls.apply_to_resources(fw, [r])
|
|
|
|
for a in actions:
|
|
|
|
a.wait_until_finished()
|
|
|
|
|
2021-08-16 10:31:02 +00:00
|
|
|
if "placement_group" in self.module.params:
|
|
|
|
if self.module.params["placement_group"] is None and self.hcloud_server.placement_group is not None:
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.remove_from_placement_group().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
else:
|
|
|
|
placement_group = self._get_placement_group()
|
|
|
|
if (
|
2022-06-29 07:58:01 +00:00
|
|
|
placement_group is not None and
|
|
|
|
(
|
|
|
|
self.hcloud_server.placement_group is None or
|
|
|
|
self.hcloud_server.placement_group.id != placement_group.id
|
|
|
|
)
|
2021-08-16 10:31:02 +00:00
|
|
|
):
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
2023-02-08 09:07:46 +00:00
|
|
|
self.hcloud_server.add_to_placement_group(placement_group).wait_until_finished()
|
2021-08-16 10:31:02 +00:00
|
|
|
self._mark_as_changed()
|
|
|
|
|
2022-06-29 07:58:01 +00:00
|
|
|
if "ipv4" in self.module.params:
|
|
|
|
if (
|
|
|
|
self.module.params["ipv4"] is None and
|
|
|
|
self.hcloud_server.public_net.primary_ipv4 is not None and
|
|
|
|
not self.module.params.get("enable_ipv4")
|
|
|
|
):
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
else:
|
|
|
|
primary_ip = self._get_primary_ip("ipv4")
|
|
|
|
if (
|
|
|
|
primary_ip is not None and
|
|
|
|
(
|
|
|
|
self.hcloud_server.public_net.primary_ipv4 is None or
|
|
|
|
self.hcloud_server.public_net.primary_ipv4.id != primary_ip.id
|
|
|
|
)
|
|
|
|
):
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
if self.hcloud_server.public_net.primary_ipv4:
|
|
|
|
self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished()
|
|
|
|
primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
if "ipv6" in self.module.params:
|
|
|
|
if (
|
|
|
|
(self.module.params["ipv6"] is None or self.module.params["ipv6"] == "") and
|
|
|
|
self.hcloud_server.public_net.primary_ipv6 is not None and
|
|
|
|
not self.module.params.get("enable_ipv6")
|
|
|
|
):
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
else:
|
|
|
|
primary_ip = self._get_primary_ip("ipv6")
|
|
|
|
if (
|
|
|
|
primary_ip is not None and
|
|
|
|
(
|
|
|
|
self.hcloud_server.public_net.primary_ipv6 is None or
|
|
|
|
self.hcloud_server.public_net.primary_ipv6.id != primary_ip.id
|
|
|
|
)
|
|
|
|
):
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
if self.hcloud_server.public_net.primary_ipv6 is not None:
|
|
|
|
self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished()
|
|
|
|
primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
2022-12-20 13:49:48 +00:00
|
|
|
if "private_networks" in self.module.params and self.module.params["private_networks"] is not None:
|
2022-11-09 21:31:03 +00:00
|
|
|
if not bool(self.module.params["private_networks"]):
|
|
|
|
# This handles None, "" and []
|
|
|
|
networks_target = {}
|
|
|
|
else:
|
|
|
|
_networks = {}
|
|
|
|
for network_name_or_id in self.module.params.get("private_networks"):
|
|
|
|
_found_network = self.client.networks.get_by_name(network_name_or_id) \
|
|
|
|
or self.client.networks.get_by_id(network_name_or_id)
|
|
|
|
_networks.update(
|
|
|
|
{_found_network.id: _found_network}
|
|
|
|
)
|
|
|
|
networks_target = _networks
|
|
|
|
networks_is = dict()
|
|
|
|
for p_network in self.hcloud_server.private_net:
|
|
|
|
networks_is.update({p_network.network.id: p_network.network})
|
|
|
|
for network_id in set(list(networks_is) + list(networks_target)):
|
|
|
|
if network_id in networks_is and network_id not in networks_target:
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.detach_from_network(networks_is[network_id]).wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
elif network_id in networks_target and network_id not in networks_is:
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.attach_to_network(networks_target[network_id]).wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
server_type = self.module.params.get("server_type")
|
2023-06-20 09:38:38 +00:00
|
|
|
if server_type is not None:
|
|
|
|
if self.hcloud_server.server_type.name == server_type:
|
|
|
|
# Check if we should warn for using an deprecated server type
|
|
|
|
self._check_and_warn_deprecated_server(self.hcloud_server.server_type)
|
|
|
|
|
|
|
|
else:
|
|
|
|
# Server type should be changed
|
|
|
|
self.stop_server_if_forced()
|
|
|
|
|
|
|
|
timeout = 100
|
|
|
|
if self.module.params.get("upgrade_disk"):
|
|
|
|
timeout = (
|
|
|
|
1000
|
|
|
|
) # When we upgrade the disk to the resize progress takes some more time.
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.change_type(
|
|
|
|
server_type=self._get_server_type(),
|
|
|
|
upgrade_disk=self.module.params.get("upgrade_disk"),
|
|
|
|
).wait_until_finished(timeout)
|
|
|
|
self._mark_as_changed()
|
2020-03-09 13:36:01 +00:00
|
|
|
|
2021-08-16 10:31:02 +00:00
|
|
|
if (
|
2022-06-29 07:58:01 +00:00
|
|
|
not self.module.check_mode and
|
|
|
|
(
|
|
|
|
(
|
|
|
|
self.module.params.get("state") == "present" and
|
|
|
|
previous_server_status == Server.STATUS_RUNNING
|
|
|
|
) or
|
|
|
|
self.module.params.get("state") == "started"
|
|
|
|
)
|
2021-08-16 10:31:02 +00:00
|
|
|
):
|
|
|
|
self.start_server()
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
delete_protection = self.module.params.get("delete_protection")
|
|
|
|
rebuild_protection = self.module.params.get("rebuild_protection")
|
|
|
|
if (delete_protection is not None and rebuild_protection is not None) and (
|
|
|
|
delete_protection != self.hcloud_server.protection["delete"] or rebuild_protection !=
|
|
|
|
self.hcloud_server.protection["rebuild"]):
|
|
|
|
if not self.module.check_mode:
|
|
|
|
self.hcloud_server.change_protection(delete=delete_protection,
|
|
|
|
rebuild=rebuild_protection).wait_until_finished()
|
|
|
|
self._mark_as_changed()
|
|
|
|
self._get_server()
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2022-06-29 07:58:01 +00:00
|
|
|
self.module.fail_json(msg=e)
|
2020-03-09 13:36:01 +00:00
|
|
|
|
|
|
|
def _set_rescue_mode(self, rescue_mode):
|
|
|
|
if self.module.params.get("ssh_keys"):
|
|
|
|
resp = self.hcloud_server.enable_rescue(type=rescue_mode,
|
|
|
|
ssh_keys=[self.client.ssh_keys.get_by_name(ssh_key_name).id
|
|
|
|
for
|
|
|
|
ssh_key_name in
|
|
|
|
self.module.params.get("ssh_keys")])
|
|
|
|
else:
|
|
|
|
resp = self.hcloud_server.enable_rescue(type=rescue_mode)
|
|
|
|
resp.action.wait_until_finished()
|
|
|
|
self.result["root_password"] = resp.root_password
|
|
|
|
|
|
|
|
def start_server(self):
|
|
|
|
try:
|
2021-03-12 13:22:47 +00:00
|
|
|
if self.hcloud_server:
|
|
|
|
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()
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2020-03-09 13:36:01 +00:00
|
|
|
self.module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
def stop_server(self):
|
|
|
|
try:
|
2021-03-12 13:22:47 +00:00
|
|
|
if self.hcloud_server:
|
|
|
|
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()
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2020-03-09 13:36:01 +00:00
|
|
|
self.module.fail_json(msg=e.message)
|
|
|
|
|
2021-08-16 10:31:02 +00:00
|
|
|
def stop_server_if_forced(self):
|
|
|
|
previous_server_status = self.hcloud_server.status
|
|
|
|
if previous_server_status == Server.STATUS_RUNNING and not self.module.check_mode:
|
|
|
|
if (
|
2022-06-29 07:58:01 +00:00
|
|
|
self.module.params.get("force_upgrade") or
|
|
|
|
self.module.params.get("force") or
|
|
|
|
self.module.params.get("state") == "stopped"
|
2021-08-16 10:31:02 +00:00
|
|
|
):
|
|
|
|
self.stop_server() # Only stopped server can be upgraded
|
|
|
|
return previous_server_status
|
|
|
|
else:
|
|
|
|
self.module.warn(
|
2023-06-21 08:49:54 +00:00
|
|
|
"You can not upgrade a running instance %s. You need to stop the instance or use force=true."
|
2021-08-16 10:31:02 +00:00
|
|
|
% self.hcloud_server.name
|
|
|
|
)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
2020-03-09 13:36:01 +00:00
|
|
|
def rebuild_server(self):
|
|
|
|
self.module.fail_on_missing_params(
|
|
|
|
required_params=["image"]
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
if not self.module.check_mode:
|
2023-04-12 08:43:19 +00:00
|
|
|
image = self._get_image(self.hcloud_server.server_type)
|
2023-01-31 15:48:02 +00:00
|
|
|
self.client.servers.rebuild(self.hcloud_server, image).wait_until_finished(1000) # When we rebuild the server progress takes some more time.
|
2020-03-09 13:36:01 +00:00
|
|
|
self._mark_as_changed()
|
|
|
|
|
|
|
|
self._get_server()
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2020-03-09 13:36:01 +00:00
|
|
|
self.module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
def present_server(self):
|
|
|
|
self._get_server()
|
|
|
|
if self.hcloud_server is None:
|
|
|
|
self._create_server()
|
|
|
|
else:
|
|
|
|
self._update_server()
|
|
|
|
|
|
|
|
def delete_server(self):
|
|
|
|
try:
|
|
|
|
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
|
2021-02-24 12:37:58 +00:00
|
|
|
except Exception as e:
|
2020-03-09 13:36:01 +00:00
|
|
|
self.module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
@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"},
|
2021-03-26 09:02:52 +00:00
|
|
|
ssh_keys={"type": "list", "elements": "str", "no_log": False},
|
2020-06-29 11:53:01 +00:00
|
|
|
volumes={"type": "list", "elements": "str"},
|
2021-03-11 10:07:41 +00:00
|
|
|
firewalls={"type": "list", "elements": "str"},
|
2020-03-09 13:36:01 +00:00
|
|
|
labels={"type": "dict"},
|
2021-07-19 05:21:09 +00:00
|
|
|
backups={"type": "bool"},
|
2020-03-09 13:36:01 +00:00
|
|
|
upgrade_disk={"type": "bool", "default": False},
|
2022-06-29 07:58:01 +00:00
|
|
|
enable_ipv4={"type": "bool", "default": True},
|
|
|
|
enable_ipv6={"type": "bool", "default": True},
|
|
|
|
ipv4={"type": "str"},
|
|
|
|
ipv6={"type": "str"},
|
2022-12-20 13:49:48 +00:00
|
|
|
private_networks={"type": "list", "elements": "str", "default": None},
|
2021-08-16 10:31:02 +00:00
|
|
|
force={"type": "bool", "default": False},
|
2023-05-16 09:29:41 +00:00
|
|
|
force_upgrade={"type": "bool"},
|
2021-04-07 06:15:10 +00:00
|
|
|
allow_deprecated_image={"type": "bool", "default": False},
|
2020-03-09 13:36:01 +00:00
|
|
|
rescue_mode={"type": "str"},
|
|
|
|
delete_protection={"type": "bool"},
|
|
|
|
rebuild_protection={"type": "bool"},
|
2021-08-16 10:31:02 +00:00
|
|
|
placement_group={"type": "str"},
|
2020-03-09 13:36:01 +00:00
|
|
|
state={
|
|
|
|
"choices": ["absent", "present", "restarted", "started", "stopped", "rebuild"],
|
|
|
|
"default": "present",
|
|
|
|
},
|
|
|
|
**Hcloud.base_module_arguments()
|
|
|
|
),
|
|
|
|
required_one_of=[['id', 'name']],
|
|
|
|
mutually_exclusive=[["location", "datacenter"]],
|
|
|
|
required_together=[["delete_protection", "rebuild_protection"]],
|
|
|
|
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()
|
|
|
|
elif state == "rebuild":
|
|
|
|
hcloud.present_server()
|
|
|
|
hcloud.rebuild_server()
|
|
|
|
|
|
|
|
module.exit_json(**hcloud.get_result())
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|