diff --git a/plugins/modules/hcloud_server.py b/plugins/modules/hcloud_server.py index 521f2f4..65d3226 100644 --- a/plugins/modules/hcloud_server.py +++ b/plugins/modules/hcloud_server.py @@ -97,6 +97,12 @@ options: 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 + private_networks: + description: + - List of private networks the server is attached to (name or ID) + type: list + elements: str + default: [] force_upgrade: description: - Deprecated @@ -222,6 +228,16 @@ EXAMPLES = """ name: my-server placement_group: null state: present + +- 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 """ RETURN = """ @@ -260,6 +276,12 @@ hcloud_server: returned: always type: str sample: 2a01:4f8:1c1c:c140::/64 + 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'] location: description: Name of the location of the server returned: always @@ -345,6 +367,7 @@ class AnsibleHcloudServer(Hcloud): "name": to_native(self.hcloud_server.name), "ipv4_address": ipv4_address, "ipv6": ipv6, + "private_networks": [to_native(net.network.name) for net in self.hcloud_server.private_net], "image": image, "server_type": to_native(self.hcloud_server.server_type.name), "datacenter": to_native(self.hcloud_server.datacenter.name), @@ -401,6 +424,15 @@ class AnsibleHcloudServer(Hcloud): p = self.client.primary_ips.get_by_id(self.module.params.get("ipv6")) params["public_net"].ipv6 = p + 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 + if self.module.params.get("ssh_keys") is not None: params["ssh_keys"] = [ SSHKey(name=ssh_key_name) @@ -665,6 +697,34 @@ class AnsibleHcloudServer(Hcloud): 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() + if "private_networks" in self.module.params: + 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() + server_type = self.module.params.get("server_type") if server_type is not None and self.hcloud_server.server_type.name != server_type: self.stop_server_if_forced() @@ -811,6 +871,7 @@ class AnsibleHcloudServer(Hcloud): enable_ipv6={"type": "bool", "default": True}, ipv4={"type": "str"}, ipv6={"type": "str"}, + private_networks={"type": "list", "elements": "str", "default": []}, force={"type": "bool", "default": False}, force_upgrade={"type": "bool", "default": False}, allow_deprecated_image={"type": "bool", "default": False}, diff --git a/plugins/modules/hcloud_server_info.py b/plugins/modules/hcloud_server_info.py index 6242b45..908f188 100644 --- a/plugins/modules/hcloud_server_info.py +++ b/plugins/modules/hcloud_server_info.py @@ -87,6 +87,12 @@ hcloud_server_info: returned: always type: str sample: 2a01:4f8:1c1c:c140::/64 + private_networks: + description: List of private networks the server is attached to (name) + returned: always + type: list + elements: str + sample: ['my-network', 'another-network'] location: description: Name of the location of the server returned: always @@ -161,6 +167,7 @@ class AnsibleHcloudServerInfo(Hcloud): "name": to_native(server.name), "ipv4_address": ipv4_address, "ipv6": ipv6, + "private_networks": [to_native(net.network.name) for net in server.private_net], "image": image, "server_type": to_native(server.server_type.name), "datacenter": to_native(server.datacenter.name), diff --git a/tests/integration/targets/hcloud_server/defaults/main.yml b/tests/integration/targets/hcloud_server/defaults/main.yml index c5354c6..77b8c6b 100644 --- a/tests/integration/targets/hcloud_server/defaults/main.yml +++ b/tests/integration/targets/hcloud_server/defaults/main.yml @@ -5,3 +5,4 @@ hcloud_prefix: "tests" hcloud_server_name: "{{hcloud_prefix}}-i" hcloud_firewall_name: "{{hcloud_prefix}}-i" hcloud_primary_ip_name: "{{hcloud_prefix}}-i" +hcloud_network_name: "{{hcloud_prefix}}-i" diff --git a/tests/integration/targets/hcloud_server/tasks/main.yml b/tests/integration/targets/hcloud_server/tasks/main.yml index 9747812..54ae774 100644 --- a/tests/integration/targets/hcloud_server/tasks/main.yml +++ b/tests/integration/targets/hcloud_server/tasks/main.yml @@ -5,3 +5,4 @@ #- ansible.builtin.include_tasks: basic.yml #- ansible.builtin.include_tasks: firewalls.yml - ansible.builtin.include_tasks: primary_ips.yml +- ansible.builtin.include_tasks: private_network_only.yml diff --git a/tests/integration/targets/hcloud_server/tasks/private_network_only.yml b/tests/integration/targets/hcloud_server/tasks/private_network_only.yml new file mode 100644 index 0000000..40287e2 --- /dev/null +++ b/tests/integration/targets/hcloud_server/tasks/private_network_only.yml @@ -0,0 +1,120 @@ +# Copyright: (c) 2022, Hetzner Cloud GmbH +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup create network + hcloud_network: + name: "{{ hcloud_network_name }}-1" + ip_range: 192.168.0.0/23 + register: primaryNetwork + +- name: setup create network subnet 1 + hcloud_subnetwork: + network: "{{ hcloud_network_name }}-1" + ip_range: 192.168.0.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create network subnet 2 + hcloud_subnetwork: + network: "{{ hcloud_network_name }}-1" + ip_range: 192.168.1.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create secondary network + hcloud_network: + name: "{{ hcloud_network_name }}-2" + ip_range: 192.168.2.0/23 + register: secondaryNetwork + +- name: setup create secondary network subnet 1 + hcloud_subnetwork: + network: "{{ hcloud_network_name }}-2" + ip_range: 192.168.2.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create secondary network subnet 2 + hcloud_subnetwork: + network: "{{ hcloud_network_name }}-2" + ip_range: 192.168.3.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: test create server with primary network and no internet + hcloud_server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-20.04" + enable_ipv4: no + enable_ipv6: no + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + ssh_keys: + - ci@ansible.hetzner.cloud + state: stopped + register: result +- name: verify test create server with primary network + assert: + that: + - result is changed + +- name: test update server by adding secondary network + hcloud_server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-20.04" + enable_ipv4: no + enable_ipv6: no + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + ssh_keys: + - ci@ansible.hetzner.cloud + state: stopped + register: result +- name: verify test update server by adding secondary network + assert: + that: + - result is changed + +- name: test update server idem + hcloud_server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-20.04" + enable_ipv4: no + enable_ipv6: no + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + ssh_keys: + - ci@ansible.hetzner.cloud + state: stopped + register: result +- name: verify test update server idem + assert: + that: + - result is not changed + +- name: cleanup server + hcloud_server: + name: "{{ hcloud_server_name }}" + state: absent +- name: cleanup networks + hcloud_network: + name: "{{ item }}" + state: absent + with_items: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + until: result is not failed + retries: 5 + delay: 2