Skip to content

Commit 82a2772

Browse files
Enhanced Interfaces: Add support for Firewall templates (#529)
* Add support for Firewall Templates * oops * Add LA notices
1 parent cf04ca6 commit 82a2772

File tree

8 files changed

+304
-0
lines changed

8 files changed

+304
-0
lines changed

linode_api4/groups/networking.py

+18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
VLAN,
55
Base,
66
Firewall,
7+
FirewallTemplate,
78
Instance,
89
IPAddress,
910
IPv6Pool,
@@ -94,6 +95,23 @@ def firewall_create(self, label, rules, **kwargs):
9495
f = Firewall(self.client, result["id"], result)
9596
return f
9697

98+
def firewall_templates(self, *filters):
99+
"""
100+
Returns a list of Firewall Templates available to the current user.
101+
102+
API Documentation: Not yet available.
103+
104+
NOTE: This feature may not currently be available to all users.
105+
106+
:param filters: Any number of filters to apply to this query.
107+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
108+
for more details on filtering.
109+
110+
:returns: A list of Firewall Templates available to the current user.
111+
:rtype: PaginatedList of FirewallTemplate
112+
"""
113+
return self.client._get_and_filter(FirewallTemplate, *filters)
114+
97115
def ips(self, *filters):
98116
"""
99117
Returns a list of IP addresses on this account, excluding private addresses.

linode_api4/objects/networking.py

+16
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,22 @@ def device_create(self, id, type="linode", **kwargs):
307307
return c
308308

309309

310+
class FirewallTemplate(Base):
311+
"""
312+
Represents a single Linode Firewall template.
313+
314+
API documentation: Not yet available.
315+
316+
NOTE: This feature may not currently be available to all users.
317+
"""
318+
319+
api_endpoint = "/networking/firewalls/templates/{slug}"
320+
321+
id_attribute = "slug"
322+
323+
properties = {"slug": Property(identifier=True), "rules": Property()}
324+
325+
310326
class NetworkTransferPrice(Base):
311327
"""
312328
An NetworkTransferPrice represents the structure of a valid network transfer price.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"data": [
3+
{
4+
"slug": "public",
5+
"rules": {
6+
"outbound": [
7+
{
8+
"action": "ACCEPT",
9+
"addresses": {
10+
"ipv4": [
11+
"192.0.2.0/24",
12+
"198.51.100.2/32"
13+
],
14+
"ipv6": [
15+
"2001:DB8::/128"
16+
]
17+
},
18+
"description": "test",
19+
"label": "test-rule",
20+
"ports": "22-24, 80, 443",
21+
"protocol": "TCP"
22+
}
23+
],
24+
"outbound_policy": "DROP",
25+
"inbound": [
26+
{
27+
"action": "ACCEPT",
28+
"addresses": {
29+
"ipv4": [
30+
"192.0.2.0/24",
31+
"198.51.100.2/32"
32+
],
33+
"ipv6": [
34+
"2001:DB8::/128"
35+
]
36+
},
37+
"description": "test",
38+
"label": "test-rule",
39+
"ports": "22-24, 80, 443",
40+
"protocol": "TCP"
41+
}
42+
],
43+
"inbound_policy": "DROP"
44+
}
45+
},
46+
{
47+
"slug": "vpc",
48+
"rules": {
49+
"outbound": [
50+
{
51+
"action": "ACCEPT",
52+
"addresses": {
53+
"ipv4": [
54+
"192.0.2.0/24",
55+
"198.51.100.2/32"
56+
],
57+
"ipv6": [
58+
"2001:DB8::/128"
59+
]
60+
},
61+
"description": "test",
62+
"label": "test-rule",
63+
"ports": "22-24, 80, 443",
64+
"protocol": "TCP"
65+
}
66+
],
67+
"outbound_policy": "DROP",
68+
"inbound": [
69+
{
70+
"action": "ACCEPT",
71+
"addresses": {
72+
"ipv4": [
73+
"192.0.2.0/24",
74+
"198.51.100.2/32"
75+
],
76+
"ipv6": [
77+
"2001:DB8::/128"
78+
]
79+
},
80+
"description": "test",
81+
"label": "test-rule",
82+
"ports": "22-24, 80, 443",
83+
"protocol": "TCP"
84+
}
85+
],
86+
"inbound_policy": "DROP"
87+
}
88+
}
89+
],
90+
"page": 1,
91+
"pages": 1,
92+
"results": 2
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"slug": "public",
3+
"rules": {
4+
"outbound": [
5+
{
6+
"action": "ACCEPT",
7+
"addresses": {
8+
"ipv4": [
9+
"192.0.2.0/24",
10+
"198.51.100.2/32"
11+
],
12+
"ipv6": [
13+
"2001:DB8::/128"
14+
]
15+
},
16+
"description": "test",
17+
"label": "test-rule",
18+
"ports": "22-24, 80, 443",
19+
"protocol": "TCP"
20+
}
21+
],
22+
"outbound_policy": "DROP",
23+
"inbound": [
24+
{
25+
"action": "ACCEPT",
26+
"addresses": {
27+
"ipv4": [
28+
"192.0.2.0/24",
29+
"198.51.100.2/32"
30+
],
31+
"ipv6": [
32+
"2001:DB8::/128"
33+
]
34+
},
35+
"description": "test",
36+
"label": "test-rule",
37+
"ports": "22-24, 80, 443",
38+
"protocol": "TCP"
39+
}
40+
],
41+
"inbound_policy": "DROP"
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"slug": "vpc",
3+
"rules": {
4+
"outbound": [
5+
{
6+
"action": "ACCEPT",
7+
"addresses": {
8+
"ipv4": [
9+
"192.0.2.0/24",
10+
"198.51.100.2/32"
11+
],
12+
"ipv6": [
13+
"2001:DB8::/128"
14+
]
15+
},
16+
"description": "test",
17+
"label": "test-rule",
18+
"ports": "22-24, 80, 443",
19+
"protocol": "TCP"
20+
}
21+
],
22+
"outbound_policy": "DROP",
23+
"inbound": [
24+
{
25+
"action": "ACCEPT",
26+
"addresses": {
27+
"ipv4": [
28+
"192.0.2.0/24",
29+
"198.51.100.2/32"
30+
],
31+
"ipv6": [
32+
"2001:DB8::/128"
33+
]
34+
},
35+
"description": "test",
36+
"label": "test-rule",
37+
"ports": "22-24, 80, 443",
38+
"protocol": "TCP"
39+
}
40+
],
41+
"inbound_policy": "DROP"
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from linode_api4 import FirewallTemplate, MappedObject
2+
3+
4+
def __assert_firewall_template_rules(rules: MappedObject):
5+
# We can't confidently say that these rules will not be changed
6+
# in the future, so we can just do basic assertions here.
7+
assert isinstance(rules.inbound_policy, str)
8+
assert len(rules.inbound_policy) > 0
9+
10+
assert isinstance(rules.outbound_policy, str)
11+
assert len(rules.outbound_policy) > 0
12+
13+
assert isinstance(rules.outbound, list)
14+
assert isinstance(rules.inbound, list)
15+
16+
17+
def test_list_firewall_templates(test_linode_client):
18+
templates = test_linode_client.networking.firewall_templates()
19+
assert len(templates) > 0
20+
21+
for template in templates:
22+
assert isinstance(template.slug, str)
23+
assert len(template.slug) > 0
24+
25+
__assert_firewall_template_rules(template.rules)
26+
27+
28+
def test_get_firewall_template(test_linode_client):
29+
template = test_linode_client.load(FirewallTemplate, "vpc")
30+
31+
assert template.slug == "vpc"
32+
33+
__assert_firewall_template_rules(template.rules)

test/unit/groups/networking_test.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from test.unit.base import ClientBaseCase
2+
from test.unit.objects.firewall_test import FirewallTemplatesTest
3+
4+
5+
class NetworkingGroupTest(ClientBaseCase):
6+
"""
7+
Tests methods under the NetworkingGroup class.
8+
"""
9+
10+
def test_get_templates(self):
11+
templates = self.client.networking.firewall_templates()
12+
13+
assert templates[0].slug == "public"
14+
FirewallTemplatesTest.assert_rules(templates[0].rules)
15+
16+
assert templates[1].slug == "vpc"
17+
FirewallTemplatesTest.assert_rules(templates[1].rules)

test/unit/objects/firewall_test.py

+41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from test.unit.base import ClientBaseCase
22

3+
from linode_api4 import FirewallTemplate, MappedObject
34
from linode_api4.objects import Firewall, FirewallDevice
45

56

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

8384
self.assertEqual(device._populated, True)
85+
86+
87+
class FirewallTemplatesTest(ClientBaseCase):
88+
@staticmethod
89+
def assert_rules(rules: MappedObject):
90+
assert rules.outbound_policy == "DROP"
91+
assert len(rules.outbound) == 1
92+
93+
assert rules.inbound_policy == "DROP"
94+
assert len(rules.inbound) == 1
95+
96+
outbound_rule = rules.outbound[0]
97+
assert outbound_rule.action == "ACCEPT"
98+
assert outbound_rule.addresses.ipv4[0] == "192.0.2.0/24"
99+
assert outbound_rule.addresses.ipv4[1] == "198.51.100.2/32"
100+
assert outbound_rule.addresses.ipv6[0] == "2001:DB8::/128"
101+
assert outbound_rule.description == "test"
102+
assert outbound_rule.label == "test-rule"
103+
assert outbound_rule.ports == "22-24, 80, 443"
104+
assert outbound_rule.protocol == "TCP"
105+
106+
inbound_rule = rules.outbound[0]
107+
assert inbound_rule.action == "ACCEPT"
108+
assert inbound_rule.addresses.ipv4[0] == "192.0.2.0/24"
109+
assert inbound_rule.addresses.ipv4[1] == "198.51.100.2/32"
110+
assert inbound_rule.addresses.ipv6[0] == "2001:DB8::/128"
111+
assert inbound_rule.description == "test"
112+
assert inbound_rule.label == "test-rule"
113+
assert inbound_rule.ports == "22-24, 80, 443"
114+
assert inbound_rule.protocol == "TCP"
115+
116+
def test_get_public(self):
117+
template = self.client.load(FirewallTemplate, "public")
118+
assert template.slug == "public"
119+
self.assert_rules(template.rules)
120+
121+
def test_get_vpc(self):
122+
template = self.client.load(FirewallTemplate, "vpc")
123+
assert template.slug == "vpc"
124+
self.assert_rules(template.rules)

0 commit comments

Comments
 (0)