Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jooola committed Sep 25, 2023
1 parent 50ab05d commit 3a91aee
Show file tree
Hide file tree
Showing 11 changed files with 972 additions and 2 deletions.
88 changes: 88 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
## Follow API Schema

```yml
- name: Apply a firewall to resources
hetzner.hcloud.hcloud_firewall:
name: my-firewall
apply_to:
- type: label_selector
label_selector: env=prod
state: present

- name: Remove a firewall from resources
hetzner.hcloud.hcloud_firewall:
name: my-firewall
remove_from:
- type: server
server: 12345
state: present
```
## Map the data to new arguments
```yml
- name: Apply a firewall to resources
hetzner.hcloud.hcloud_firewall:
name: my-firewall
apply_to:
label_selectors:
- env=prod
state: present

- name: Remove a firewall from resources
hetzner.hcloud.hcloud_firewall:
name: my-firewall
remove_from:
servers:
- 12345
state: present
```
- Need to make the data returned by firewall and firewall_info consistent to this output to facilitate reusing the outputs
## Create new `hcloud_firewall_resource` module to manage a firewall resource

```yml
- name: Apply a firewall to resources
hetzner.hcloud.hcloud_firewall_resource:
name: my-firewall
server: 12345
state: present
- name: Apply a firewall to resources
hetzner.hcloud.hcloud_firewall_resource:
name: my-firewall
label_selector:
- env=prod
- key=value
server: 12345
state: present
- name: Remove a firewall from resources
hetzner.hcloud.hcloud_firewall_resource:
name: my-firewall
label_selector: env=prod
state: absent
- name: Remove a firewall from resources
hetzner.hcloud.hcloud_firewall_resource:
name: my-firewall
server:
- 12345
state: absent
# Example using firewall_info
- name: Get firewall
hetzner.hcloud.hcloud_firewall_info:
name: my-firewall
register: firewall
- name: Remove a firewall from resources
hetzner.hcloud.hcloud_firewall_resource:
id: "{{ firewall.hcloud_firewall_info.id }}"
server: "{{ firewall.hcloud_firewall_info.applied_to | json_query('[?type=="server"].server') }}"
state: absent
```

- Need to make the data returned by firewall and firewall_info consistent to this output to facilitate reusing the outputs
- No way to define applied_to rules on firewall creation
22 changes: 20 additions & 2 deletions plugins/modules/hcloud_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
module: hcloud_firewall
short_description: Create and manage firewalls on the Hetzner Cloud.
description:
- Create, update and manage firewalls on the Hetzner Cloud.
Expand Down Expand Up @@ -68,6 +66,26 @@
description:
- User defined description of this rule.
type: str
applied_to:
description:
- Resources the Firewall should be applied to.
- This will remove any additional resource and apply the missing resource.
type: list
elements: dict
suboptions:
type:
description:
- Type of the resource.
type: str
choices: [server, label_selector]
label_selector:
description:
- Label selector value, required if I(type) is `label_selector`.
type: str
server:
description:
- ID or name of the server, required if I(type) is `server`.
type: str
apply_to:
description:
- Resources the Firewall should be applied to.
Expand Down
244 changes: 244 additions & 0 deletions plugins/modules/hcloud_firewall_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#!/usr/bin/python

# Copyright: (c) 2023, Hetzner Cloud GmbH <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)


DOCUMENTATION = """
---
module: hcloud_firewall_info
short_description: Gather infos about your Hetzner Cloud Firewalls.
description:
- Gather infos about your Hetzner Cloud Firewalls.
author:
- Jonas Lammler (@jooola)
options:
id:
description:
- The ID of the Firewall you want to get.
- The module will fail if the provided ID is invalid.
type: int
name:
description:
- The name of the Firewall you want to get.
type: str
label_selector:
description:
- The label selector to filter the Firewalls you want to get.
type: str
extends_documentation_fragment:
- hetzner.hcloud.hcloud
"""

EXAMPLES = """
- name: Gather hcloud firewall infos
hetzner.hcloud.hcloud_firewall_info:
register: output
- name: Print the gathered infos
debug:
var: output
"""

RETURN = """
hcloud_firewall_info:
description: List of firewalls infos
returned: always
type: list
elements: dict
contains:
id:
description: Numeric identifier of the Firewall
returned: always
type: int
sample: 1937415
name:
description: Name of the Firewall
returned: always
type: str
sample: my-firewall
labels:
description: User-defined labels (key-value pairs)
returned: always
type: dict
rules:
description: List of Rules within this Firewall
returned: always
type: list
elements: dict
contains:
description:
description: User defined description of the Firewall Rule
type: str
returned: always
direction:
description: Direction of the Firewall Rule
type: str
returned: always
sample: in
protocol:
description: Protocol of the Firewall Rule
type: str
returned: always
sample: icmp
port:
description: Port or port range of the Firewall Rule, None if I(protocol) is icmp
type: str
returned: always
sample: 80
source_ips:
description: List of source IPs of the Firewall in CIDR notation
type: list
elements: str
returned: always
destination_ips:
description: List of destination IPs of the Firewall in CIDR notation
type: list
elements: str
returned: always
applied_to:
description: List of Resources the Firewall is applied to.
returned: always
type: list
elements: dict
contains:
type:
description: Type of the resource.
type: str
choices: [server, label_selector]
sample: label_selector
server:
description: ID of the server.
returned: if I(type) is server
type: int
sample: 12345
label_selector:
description: Label selector value.
if I(type) is label_selector
type: str
sample: key=value
applied_to_resources:
description: List of Resources the Firewall is applied to.
returned: if I(type) is label_selector
type: list
elements: dict
contains:
type:
description: Type of resource referenced.
type: str
choices: [server]
sample: server
server:
description: ID of the Server.
type: int
sample: 12345
"""
from typing import Dict, Any, List
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native

from ..module_utils.hcloud import AnsibleHCloud
from ..module_utils.vendor.hcloud import HCloudException
from ..module_utils.vendor.hcloud.firewalls import FirewallResource, FirewallRule


class AnsibleHCloudFirewallInfo(AnsibleHCloud):
def __init__(self, module):
super().__init__(module, "hcloud_firewall_info")
self.hcloud_firewall_info = None

def _prepare_result(self) -> List[Dict[str, Any]]:
tmp = []

for firewall in self.hcloud_firewall_info:
if firewall is None:
continue

tmp.append(
{
"id": to_native(firewall.id),
"name": to_native(firewall.name),
"labels": firewall.labels,
"rules": [self._prepare_result_rule(item) for item in firewall.rules],
"applied_to": [self._prepare_result_applied_to(item) for item in firewall.applied_to],
}
)

return tmp

def _prepare_result_rule(self, rule: FirewallRule) -> Dict[str, Any]:
return {
"direction": rule.direction,
"protocol": to_native(rule.protocol),
"port": to_native(rule.port) if rule.port is not None else None,
"source_ips": [to_native(cidr) for cidr in rule.source_ips],
"destination_ips": [to_native(cidr) for cidr in rule.destination_ips],
"description": to_native(rule.description) if rule.description is not None else None,
}

def _prepare_result_applied_to(self, resource: FirewallResource) -> Dict[str, Any]:
result = {
"type": resource.type,
"server": to_native(resource.server.id) if resource.server is not None else None,
"label_selector": resource.label_selector.selector if resource.label_selector is not None else None,
}
if resource.applied_to_resources is not None:
result["applied_to_resources"] = [
{
"type": item.type,
"server": item.server.id if item.server is not None else None,
}
for item in resource.applied_to_resources
]
return result

def get_firewalls(self):
try:
if self.module.params.get("id") is not None:
self.hcloud_firewall_info = [self.client.firewalls.get_by_id(self.module.params.get("id"))]
elif self.module.params.get("name") is not None:
self.hcloud_firewall_info = [self.client.firewalls.get_by_name(self.module.params.get("name"))]
else:
params = {}
label_selector = self.module.params.get("label_selector")
if label_selector:
params["label_selector"] = label_selector

self.hcloud_firewall_info = self.client.firewalls.get_all(**params)

except HCloudException as exception:
self.fail_json_hcloud(exception)

@classmethod
def define_module(cls):
return AnsibleModule(
argument_spec=dict(
id={"type": "int"},
name={"type": "str"},
label_selector={"type": "str"},
**super().base_module_arguments()
),
supports_check_mode=True,
)


def main():
module = AnsibleHCloudFirewallInfo.define_module()
hcloud = AnsibleHCloudFirewallInfo(module)

hcloud.get_firewalls()
result = hcloud.get_result()

ansible_info = {"hcloud_firewall_info": result["hcloud_firewall_info"]}
module.exit_json(**ansible_info)


if __name__ == "__main__":
main()
Loading

0 comments on commit 3a91aee

Please sign in to comment.