From 100b56439ee0fb779f049c1cd61ebb305a6bb5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20K=C3=A4mmerling?= <4281581+LKaemmerling@users.noreply.github.com> Date: Sat, 17 Aug 2019 11:29:58 +0200 Subject: [PATCH] hcloud_server: Allow users to enable/disable the rescue mode (#60070) --- .../58746-hcloud_server_allow_rescue_mode.yml | 2 + .../modules/cloud/hcloud/hcloud_server.py | 114 +++++++++++++++--- .../targets/hcloud_server/tasks/main.yml | 112 ++++++++++++++++- 3 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 changelogs/fragments/58746-hcloud_server_allow_rescue_mode.yml diff --git a/changelogs/fragments/58746-hcloud_server_allow_rescue_mode.yml b/changelogs/fragments/58746-hcloud_server_allow_rescue_mode.yml new file mode 100644 index 00000000000..93954bedbbd --- /dev/null +++ b/changelogs/fragments/58746-hcloud_server_allow_rescue_mode.yml @@ -0,0 +1,2 @@ +minor_changes: + - Allow the users to enable or disable the rescue mode on Hetzner cloud servers diff --git a/lib/ansible/modules/cloud/hcloud/hcloud_server.py b/lib/ansible/modules/cloud/hcloud/hcloud_server.py index b3e062857cb..2dd0da4646f 100644 --- a/lib/ansible/modules/cloud/hcloud/hcloud_server.py +++ b/lib/ansible/modules/cloud/hcloud/hcloud_server.py @@ -91,6 +91,11 @@ options: - User Data to be passed to the server on creation. - Only used if server does not exists. type: str + rescue_mode: + description: + - Add the Hetzner rescue system type you want the server to be booted into. + type: str + version_added: 2.9 labels: description: - User-defined labels (key-value pairs). @@ -149,6 +154,12 @@ EXAMPLES = """ 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 @@ -160,21 +171,62 @@ 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" - } + 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 + location: + description: Name of the location of the server + returned: always + type: str + sample: fsn1 + 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 """ from ansible.module_utils.basic import AnsibleModule @@ -272,10 +324,26 @@ class AnsibleHcloudServer(Hcloud): 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) + self._mark_as_changed() self._get_server() def _update_server(self): + 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() + 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() @@ -320,6 +388,18 @@ class AnsibleHcloudServer(Hcloud): self._mark_as_changed() self._get_server() + 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): if self.hcloud_server.status != Server.STATUS_RUNNING: if not self.module.check_mode: @@ -339,7 +419,8 @@ class AnsibleHcloudServer(Hcloud): required_params=["image"] ) if not self.module.check_mode: - self.client.servers.rebuild(self.hcloud_server, self.client.images.get_by_name(self.module.params.get("image"))).wait_until_finished() + self.client.servers.rebuild(self.hcloud_server, self.client.images.get_by_name( + self.module.params.get("image"))).wait_until_finished() self._mark_as_changed() self._get_server() @@ -376,6 +457,7 @@ class AnsibleHcloudServer(Hcloud): backups={"type": "bool", "default": False}, upgrade_disk={"type": "bool", "default": False}, force_upgrade={"type": "bool", "default": False}, + rescue_mode={"type": "str"}, state={ "choices": ["absent", "present", "restarted", "started", "stopped", "rebuild"], "default": "present", diff --git a/test/integration/targets/hcloud_server/tasks/main.yml b/test/integration/targets/hcloud_server/tasks/main.yml index 92a5b4c7ab3..6910ce3a5b3 100644 --- a/test/integration/targets/hcloud_server/tasks/main.yml +++ b/test/integration/targets/hcloud_server/tasks/main.yml @@ -279,7 +279,36 @@ - ci@ansible.hetzner.cloud state: started register: main_server -- name: verify create server +- name: verify create server with ssh key + 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: 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 rescue_mode + hcloud_server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-18.04" + ssh_keys: + - ci@ansible.hetzner.cloud + rescue_mode: "linux64" + state: started + register: main_server +- name: verify create server with rescue_mode assert: that: - main_server is changed @@ -287,6 +316,86 @@ - main_server.hcloud_server.server_type == "cx11" - main_server.hcloud_server.status == "running" - main_server.root_password != "" + - main_server.hcloud_server.rescue_enabled is sameas true + +- name: absent server + hcloud_server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify absent server + assert: + that: + - result is success + +- name: setup server + hcloud_server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: ubuntu-18.04 + state: started + register: main_server +- name: verify setup 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 activate rescue mode with check_mode + hcloud_server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + ssh_keys: + - ci@ansible.hetzner.cloud + state: present + register: main_server + check_mode: true +- name: verify activate rescue mode + assert: + that: + - main_server is changed + +- name: test activate rescue mode + hcloud_server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + ssh_keys: + - ci@ansible.hetzner.cloud + state: present + register: main_server +- name: verify activate rescue mode + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas true + +- name: test disable rescue mode + hcloud_server: + name: "{{ hcloud_server_name }}" + ssh_keys: + - ci@ansible.hetzner.cloud + state: present + register: main_server +- name: verify activate rescue mode + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas false + +- name: test activate rescue mode without ssh keys + hcloud_server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + state: present + register: main_server +- name: verify activate rescue mode without ssh keys + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas true - name: cleanup hcloud_server: @@ -336,7 +445,6 @@ - main_server.hcloud_server.labels.key == "other" - main_server.hcloud_server.labels.mylabel == "val123" - - name: test update server with labels in other order hcloud_server: name: "{{ hcloud_server_name}}"