#!/usr/bin/python # Copyright: (c) 2025, Hetzner Cloud GmbH # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import annotations DOCUMENTATION = """ --- module: volume_attachment short_description: Manage the relationship between Hetzner Cloud Volumes and Servers description: - Attach and detach Volumes from Hetzner Cloud Servers. author: - Amirhossein Shaerpour (@shaerpour) options: volume: description: - Name or ID of the Hetzner Cloud Volume to attach/detach. type: str required: true server: description: - Name or ID of the Hetzner Cloud Server to attach/detach the Volume to. type: str required: true automount: description: - Automatically mount the Volume in the Server. type: bool default: False state: description: - State of the Volume. type: str default: present choices: [ present, absent ] extends_documentation_fragment: - hetzner.hcloud.hcloud """ EXAMPLES = """ - name: Attach my-volume to my-server hetzner.hcloud.volume_attachment: volume: my-volume server: my-server - name: Detach my-volume from my-server hetzner.hcloud.volume_attachment: volume: my-volume server: my-server state: absent - name: Attach my-volume using id to my-server with automount enabled hetzner.hcloud.volume_attachment: volume: 123456 server: my-server automount: true state: present """ RETURN = """ hcloud_volume_attachment: description: The relationship between a Server and a Volume returned: always type: complex contains: volume: description: Name of the Volume type: str returned: always sample: my-volume server: description: Name of the Server type: str returned: always sample: my-server """ from ansible.module_utils.basic import AnsibleModule from ..module_utils.hcloud import AnsibleHCloud from ..module_utils.vendor.hcloud import HCloudException from ..module_utils.vendor.hcloud.servers import BoundServer from ..module_utils.vendor.hcloud.volumes import BoundVolume class AnsibleHcloudVolumeAttachment(AnsibleHCloud): represent = "hcloud_volume_attachment" hcloud_volume: BoundVolume | None = None hcloud_server: BoundServer | None = None def _prepare_result(self): return { "volume": self.hcloud_volume.name, "server": self.hcloud_server.name, } def _get_server_and_volume(self): try: self.hcloud_volume = self._client_get_by_name_or_id( "volumes", self.module.params.get("volume"), ) self.hcloud_server = self._client_get_by_name_or_id( "servers", self.module.params.get("server"), ) except HCloudException as exception: self.fail_json_hcloud(exception) def attach_volume(self): try: self._get_server_and_volume() if self.hcloud_volume.server is not None: if self.hcloud_volume.server.id == self.hcloud_server.id: return if not self.module.check_mode: action = self.hcloud_volume.detach() action.wait_until_finished() self.hcloud_volume.server = None self._mark_as_changed() else: if not self.module.check_mode: action = self.hcloud_volume.attach( server=self.hcloud_server, automount=self.module.params.get("automount"), ) action.wait_until_finished() self.hcloud_volume.server = self.hcloud_server self._mark_as_changed() except HCloudException as exception: self.fail_json_hcloud(exception) def detach_volume(self): try: self._get_server_and_volume() if self.hcloud_volume.server is not None: if not self.module.check_mode: action = self.hcloud_volume.detach() action.wait_until_finished() self._mark_as_changed() except HCloudException as exception: self.fail_json_hcloud(exception) @classmethod def define_module(cls): return AnsibleModule( argument_spec=dict( volume={"type": "str", "required": True}, server={"type": "str", "required": True}, automount={"type": "bool", "default": False}, state={ "choices": ["present", "absent"], "default": "present", }, **super().base_module_arguments(), ), supports_check_mode=True, ) def main(): module = AnsibleHcloudVolumeAttachment.define_module() hcloud = AnsibleHcloudVolumeAttachment(module) state = module.params["state"] if state == "present": hcloud.attach_volume() elif state == "absent": hcloud.detach_volume() module.exit_json(**hcloud.get_result()) if __name__ == "__main__": main()