Skip to content

Enhanced Interfaces: Add support for Firewall templates #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions linode_api4/groups/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
VLAN,
Base,
Firewall,
FirewallTemplate,
Instance,
IPAddress,
IPv6Pool,
Expand Down Expand Up @@ -94,6 +95,21 @@ def firewall_create(self, label, rules, **kwargs):
f = Firewall(self.client, result["id"], result)
return f

def firewall_templates(self, *filters):
"""
Returns a list of Firewall Templates available to the current user.

API Documentation: Not yet available.

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A list of Firewall Templates available to the current user.
:rtype: PaginatedList of FirewallTemplate
"""
return self.client._get_and_filter(FirewallTemplate, *filters)

def ips(self, *filters):
"""
Returns a list of IP addresses on this account, excluding private addresses.
Expand Down
14 changes: 14 additions & 0 deletions linode_api4/objects/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,20 @@ def device_create(self, id, type="linode", **kwargs):
return c


class FirewallTemplate(Base):
"""
Represents a single Linode Firewall template.

API documentation: Not yet available.
"""

api_endpoint = "/networking/firewalls/templates/{slug}"

id_attribute = "slug"

properties = {"slug": Property(identifier=True), "rules": Property()}


class NetworkTransferPrice(Base):
"""
An NetworkTransferPrice represents the structure of a valid network transfer price.
Expand Down
93 changes: 93 additions & 0 deletions test/fixtures/networking_firewalls_templates.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"data": [
{
"slug": "public",
"rules": {
"outbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"outbound_policy": "DROP",
"inbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"inbound_policy": "DROP"
}
},
{
"slug": "vpc",
"rules": {
"outbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"outbound_policy": "DROP",
"inbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"inbound_policy": "DROP"
}
}
],
"page": 1,
"pages": 1,
"results": 2
}
43 changes: 43 additions & 0 deletions test/fixtures/networking_firewalls_templates_public.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"slug": "public",
"rules": {
"outbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"outbound_policy": "DROP",
"inbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"inbound_policy": "DROP"
}
}
43 changes: 43 additions & 0 deletions test/fixtures/networking_firewalls_templates_vpc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"slug": "vpc",
"rules": {
"outbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"outbound_policy": "DROP",
"inbound": [
{
"action": "ACCEPT",
"addresses": {
"ipv4": [
"192.0.2.0/24",
"198.51.100.2/32"
],
"ipv6": [
"2001:DB8::/128"
]
},
"description": "test",
"label": "test-rule",
"ports": "22-24, 80, 443",
"protocol": "TCP"
}
],
"inbound_policy": "DROP"
}
}
33 changes: 33 additions & 0 deletions test/integration/models/firewall/test_firewall_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from linode_api4 import FirewallTemplate, MappedObject


def __assert_firewall_template_rules(rules: MappedObject):
# We can't confidently say that these rules will not be changed
# in the future, so we can just do basic assertions here.
assert isinstance(rules.inbound_policy, str)
assert len(rules.inbound_policy) > 0

assert isinstance(rules.outbound_policy, str)
assert len(rules.outbound_policy) > 0

assert isinstance(rules.outbound, list)
assert (rules.inbound, list)


def test_list_firewall_templates(test_linode_client):
templates = test_linode_client.networking.firewall_templates()
assert len(templates) > 0

for template in templates:
assert isinstance(template.slug, str)
assert len(template.slug) > 0

__assert_firewall_template_rules(template.rules)


def test_get_firewall_template(test_linode_client):
template = test_linode_client.load(FirewallTemplate, "vpc")

assert template.slug == "vpc"

__assert_firewall_template_rules(template.rules)
17 changes: 17 additions & 0 deletions test/unit/groups/networking_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from test.unit.base import ClientBaseCase
from test.unit.objects.firewall_test import FirewallTemplatesTest


class NetworkingGroupTest(ClientBaseCase):
"""
Tests methods under the NetworkingGroup class.
"""

def test_get_templates(self):
templates = self.client.networking.firewall_templates()

assert templates[0].slug == "public"
FirewallTemplatesTest.assert_rules(templates[0].rules)

assert templates[1].slug == "vpc"
FirewallTemplatesTest.assert_rules(templates[1].rules)
41 changes: 41 additions & 0 deletions test/unit/objects/firewall_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from test.unit.base import ClientBaseCase

from linode_api4 import FirewallTemplate, MappedObject
from linode_api4.objects import Firewall, FirewallDevice


Expand Down Expand Up @@ -81,3 +82,43 @@ def test_get_device(self):
self.assertEqual(device.entity.url, "/v4/linode/instances/123")

self.assertEqual(device._populated, True)


class FirewallTemplatesTest(ClientBaseCase):
@staticmethod
def assert_rules(rules: MappedObject):
assert rules.outbound_policy == "DROP"
assert len(rules.outbound) == 1

assert rules.inbound_policy == "DROP"
assert len(rules.inbound) == 1

outbound_rule = rules.outbound[0]
assert outbound_rule.action == "ACCEPT"
assert outbound_rule.addresses.ipv4[0] == "192.0.2.0/24"
assert outbound_rule.addresses.ipv4[1] == "198.51.100.2/32"
assert outbound_rule.addresses.ipv6[0] == "2001:DB8::/128"
assert outbound_rule.description == "test"
assert outbound_rule.label == "test-rule"
assert outbound_rule.ports == "22-24, 80, 443"
assert outbound_rule.protocol == "TCP"

inbound_rule = rules.outbound[0]
assert inbound_rule.action == "ACCEPT"
assert inbound_rule.addresses.ipv4[0] == "192.0.2.0/24"
assert inbound_rule.addresses.ipv4[1] == "198.51.100.2/32"
assert inbound_rule.addresses.ipv6[0] == "2001:DB8::/128"
assert inbound_rule.description == "test"
assert inbound_rule.label == "test-rule"
assert inbound_rule.ports == "22-24, 80, 443"
assert inbound_rule.protocol == "TCP"

def test_get_public(self):
template = self.client.load(FirewallTemplate, "public")
assert template.slug == "public"
self.assert_rules(template.rules)

def test_get_vpc(self):
template = self.client.load(FirewallTemplate, "vpc")
assert template.slug == "vpc"
self.assert_rules(template.rules)