From bedda1f0e02975c496c15d3043b56f8be6f06d85 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 9 Apr 2025 10:54:37 -0400 Subject: [PATCH 1/2] Implement endpoints & fields related to VPCs and non-interface networking --- linode_api4/groups/networking.py | 43 ++++++++- linode_api4/objects/networking.py | 48 +++++++++- linode_api4/objects/vpc.py | 1 + .../networking_firewalls_123_devices.json | 13 ++- .../networking_firewalls_123_devices_456.json | 11 +++ .../networking_firewalls_settings.json | 8 ++ test/fixtures/networking_ips_127.0.0.1.json | 1 + test/fixtures/regions.json | 33 ++++--- test/fixtures/vpcs_123456_subnets.json | 6 +- test/fixtures/vpcs_123456_subnets_789.json | 6 +- .../linode_client/test_linode_client.py | 6 +- .../models/networking/test_networking.py | 87 ++++++++++++++++++- test/unit/linode_client_test.py | 63 +++++++++++++- test/unit/objects/firewall_test.py | 38 +++++++- test/unit/objects/networking_test.py | 23 ++++- test/unit/objects/region_test.py | 4 +- test/unit/objects/vpc_test.py | 18 ++-- 17 files changed, 373 insertions(+), 36 deletions(-) create mode 100644 test/fixtures/networking_firewalls_123_devices_456.json create mode 100644 test/fixtures/networking_firewalls_settings.json diff --git a/linode_api4/groups/networking.py b/linode_api4/groups/networking.py index ba1e656bd..ae90b577a 100644 --- a/linode_api4/groups/networking.py +++ b/linode_api4/groups/networking.py @@ -1,9 +1,13 @@ +from typing import Any, Dict, Optional, Union + from linode_api4.errors import UnexpectedResponseError from linode_api4.groups import Group from linode_api4.objects import ( VLAN, Base, Firewall, + FirewallCreateDevicesOptions, + FirewallSettings, Instance, IPAddress, IPv6Pool, @@ -11,6 +15,8 @@ NetworkTransferPrice, Region, ) +from linode_api4.objects.base import _flatten_request_body_recursive +from linode_api4.util import drop_null_keys class NetworkingGroup(Group): @@ -33,7 +39,15 @@ def firewalls(self, *filters): """ return self.client._get_and_filter(Firewall, *filters) - def firewall_create(self, label, rules, **kwargs): + def firewall_create( + self, + label: str, + rules: Dict[str, Any], + devices: Optional[ + Union[FirewallCreateDevicesOptions, Dict[str, Any]] + ] = None, + **kwargs, + ): """ Creates a new Firewall, either in the given Region or attached to the given Instance. @@ -44,6 +58,8 @@ def firewall_create(self, label, rules, **kwargs): :type label: str :param rules: The rules to apply to the new Firewall. For more information on Firewall rules, see our `Firewalls Documentation`_. :type rules: dict + :param devices: Represents devices to create created alongside a Linode Firewall. + :type devices: Optional[Union[FirewallCreateDevicesOptions, Dict[str, Any]]] :returns: The new Firewall. :rtype: Firewall @@ -81,10 +97,14 @@ def firewall_create(self, label, rules, **kwargs): params = { "label": label, "rules": rules, + "devices": devices, } params.update(kwargs) - result = self.client.post("/networking/firewalls", data=params) + result = self.client.post( + "/networking/firewalls", + data=drop_null_keys(_flatten_request_body_recursive(params)), + ) if not "id" in result: raise UnexpectedResponseError( @@ -94,6 +114,25 @@ def firewall_create(self, label, rules, **kwargs): f = Firewall(self.client, result["id"], result) return f + def firewall_settings(self) -> FirewallSettings: + """ + Returns an object representing the Linode Firewall settings for the current user. + + API Documentation: Not yet available. + + :returns: An object representing the Linode Firewall settings for the current user. + :rtype: FirewallSettings + """ + result = self.client.get("/networking/firewalls/settings") + + if "default_firewall_ids" not in result: + raise UnexpectedResponseError( + "Unexpected response when getting firewall settings!", + json=result, + ) + + return FirewallSettings(self.client, None, result) + def ips(self, *filters): """ Returns a list of IP addresses on this account, excluding private addresses. diff --git a/linode_api4/objects/networking.py b/linode_api4/objects/networking.py index b7a16ae90..725475f33 100644 --- a/linode_api4/objects/networking.py +++ b/linode_api4/objects/networking.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass -from typing import Optional +from dataclasses import dataclass, field +from typing import List, Optional from linode_api4.common import Price, RegionPrice from linode_api4.errors import UnexpectedResponseError @@ -87,6 +87,7 @@ class IPAddress(Base): "public": Property(), "rdns": Property(mutable=True), "linode_id": Property(), + "interface_id": Property(), "region": Property(slug_relationship=Region), "vpc_nat_1_1": Property(json_object=InstanceIPNAT1To1), } @@ -99,6 +100,8 @@ def linode(self): self._set("_linode", Instance(self._client, self.linode_id)) return self._linode + # TODO (Enhanced Interfaces): Add `interface` property method + def to(self, linode): """ This is a helper method for ip-assign, and should not be used outside @@ -176,6 +179,47 @@ class VLAN(Base): } +@dataclass +class FirewallCreateDevicesOptions(JSONObject): + """ + Represents devices to create created alongside a Linode Firewall. + """ + + linodes: List[int] = field(default_factory=list) + nodebalancers: List[int] = field(default_factory=list) + interfaces: List[int] = field(default_factory=list) + + +@dataclass +class FirewallSettingsDefaultFirewallIDs(JSONObject): + """ + Contains the IDs of Linode Firewalls that should be used by default + when creating various interface types. + """ + + vpc_interface: Optional[int] = None + public_interface: Optional[int] = None + linode: Optional[int] = None + nodebalancer: Optional[int] = None + + +class FirewallSettings(Base): + """ + Represents the Firewall settings for the current user. + + API Documentation: Not yet available. + """ + + api_endpoint = "/networking/firewalls/settings" + + properties = { + "default_firewall_ids": Property( + json_object=FirewallSettingsDefaultFirewallIDs, + mutable=True, + ), + } + + class FirewallDevice(DerivedBase): """ An object representing the assignment between a Linode Firewall and another Linode resource. diff --git a/linode_api4/objects/vpc.py b/linode_api4/objects/vpc.py index 3c9a4aaba..94c0302f0 100644 --- a/linode_api4/objects/vpc.py +++ b/linode_api4/objects/vpc.py @@ -11,6 +11,7 @@ @dataclass class VPCSubnetLinodeInterface(JSONObject): id: int = 0 + config_id: Optional[int] = None active: bool = False diff --git a/test/fixtures/networking_firewalls_123_devices.json b/test/fixtures/networking_firewalls_123_devices.json index ae4efe2d0..e43e3725a 100644 --- a/test/fixtures/networking_firewalls_123_devices.json +++ b/test/fixtures/networking_firewalls_123_devices.json @@ -10,9 +10,20 @@ }, "id": 123, "updated": "2018-01-02T00:01:01" + }, + { + "created": "2018-01-01T00:01:01", + "entity": { + "id": 123, + "label": null, + "type": "interface", + "url": "/v4/linode/instances/123/interfaces/123" + }, + "id": 456, + "updated": "2018-01-02T00:01:01" } ], "page": 1, "pages": 1, - "results": 1 + "results": 2 } \ No newline at end of file diff --git a/test/fixtures/networking_firewalls_123_devices_456.json b/test/fixtures/networking_firewalls_123_devices_456.json new file mode 100644 index 000000000..aa76901ee --- /dev/null +++ b/test/fixtures/networking_firewalls_123_devices_456.json @@ -0,0 +1,11 @@ +{ + "created": "2018-01-01T00:01:01", + "entity": { + "id": 123, + "label": null, + "type": "interface", + "url": "/v4/linode/instances/123/interfaces/123" + }, + "id": 456, + "updated": "2018-01-02T00:01:01" +} \ No newline at end of file diff --git a/test/fixtures/networking_firewalls_settings.json b/test/fixtures/networking_firewalls_settings.json new file mode 100644 index 000000000..bfb7b2853 --- /dev/null +++ b/test/fixtures/networking_firewalls_settings.json @@ -0,0 +1,8 @@ +{ + "default_firewall_ids": { + "vpc_interface": 123, + "public_interface": 456, + "linode": 789, + "nodebalancer": 321 + } +} \ No newline at end of file diff --git a/test/fixtures/networking_ips_127.0.0.1.json b/test/fixtures/networking_ips_127.0.0.1.json index 9d3cfb449..7abb0fabd 100644 --- a/test/fixtures/networking_ips_127.0.0.1.json +++ b/test/fixtures/networking_ips_127.0.0.1.json @@ -2,6 +2,7 @@ "address": "127.0.0.1", "gateway": "127.0.0.1", "linode_id": 123, + "interface_id": 456, "prefix": 24, "public": true, "rdns": "test.example.org", diff --git a/test/fixtures/regions.json b/test/fixtures/regions.json index 5fe55e200..b58db045d 100644 --- a/test/fixtures/regions.json +++ b/test/fixtures/regions.json @@ -6,7 +6,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -26,7 +27,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -46,7 +48,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -62,7 +65,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -82,7 +86,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -102,7 +107,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -123,7 +129,8 @@ "Linodes", "NodeBalancers", "Block Storage", - "Object Storage" + "Object Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -143,7 +150,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -164,7 +172,8 @@ "Linodes", "NodeBalancers", "Block Storage", - "Object Storage" + "Object Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -185,7 +194,8 @@ "Linodes", "NodeBalancers", "Block Storage", - "Object Storage" + "Object Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { @@ -205,7 +215,8 @@ "capabilities": [ "Linodes", "NodeBalancers", - "Block Storage" + "Block Storage", + "Linode Interfaces" ], "status": "ok", "resolvers": { diff --git a/test/fixtures/vpcs_123456_subnets.json b/test/fixtures/vpcs_123456_subnets.json index f846399df..37537efb2 100644 --- a/test/fixtures/vpcs_123456_subnets.json +++ b/test/fixtures/vpcs_123456_subnets.json @@ -10,11 +10,13 @@ "interfaces": [ { "id": 678, - "active": true + "active": true, + "config_id": null }, { "id": 543, - "active": false + "active": false, + "config_id": null } ] } diff --git a/test/fixtures/vpcs_123456_subnets_789.json b/test/fixtures/vpcs_123456_subnets_789.json index ba6973472..7fac495c4 100644 --- a/test/fixtures/vpcs_123456_subnets_789.json +++ b/test/fixtures/vpcs_123456_subnets_789.json @@ -8,11 +8,13 @@ "interfaces": [ { "id": 678, - "active": true + "active": true, + "config_id": null }, { "id": 543, - "active": false + "active": false, + "config_id": null } ] } diff --git a/test/integration/linode_client/test_linode_client.py b/test/integration/linode_client/test_linode_client.py index eb1b06369..da7e93cef 100644 --- a/test/integration/linode_client/test_linode_client.py +++ b/test/integration/linode_client/test_linode_client.py @@ -6,7 +6,11 @@ import pytest from linode_api4 import ApiError -from linode_api4.objects import ConfigInterface, ObjectStorageKeys, Region +from linode_api4.objects import ( + ConfigInterface, + ObjectStorageKeys, + Region, +) @pytest.fixture(scope="session") diff --git a/test/integration/models/networking/test_networking.py b/test/integration/models/networking/test_networking.py index b92cdfadc..bb77d4f0a 100644 --- a/test/integration/models/networking/test_networking.py +++ b/test/integration/models/networking/test_networking.py @@ -15,7 +15,11 @@ from linode_api4 import Instance, LinodeClient from linode_api4.objects import Config, ConfigInterfaceIPv4, Firewall, IPAddress -from linode_api4.objects.networking import NetworkTransferPrice, Price +from linode_api4.objects.networking import ( + FirewallCreateDevicesOptions, + NetworkTransferPrice, + Price, +) TEST_REGION = get_region( LinodeClient( @@ -73,6 +77,47 @@ def test_get_networking_rules(test_linode_client, test_firewall): assert "outbound_policy" in str(rules) +@pytest.fixture +def create_linode_without_firewall(test_linode_client): + """ + WARNING: This is specifically reserved for Firewall testing. + Don't use this if the Linode will not be assigned to a firewall. + """ + + client = test_linode_client + region = get_region(client, {"Cloud Firewall"}, "core").id + + label = get_test_label() + + instance = client.linode.instance_create( + "g6-nanode-1", + region, + label=label, + ) + + yield client, instance + + instance.delete() + + +@pytest.fixture +def create_firewall_with_device(create_linode_without_firewall): + client, target_instance = create_linode_without_firewall + + firewall = client.networking.firewall_create( + get_test_label(), + rules={ + "inbound_policy": "DROP", + "outbound_policy": "DROP", + }, + devices=FirewallCreateDevicesOptions(linodes=[target_instance.id]), + ) + + yield firewall, target_instance + + firewall.delete() + + def test_get_networking_rule_versions(test_linode_client, test_firewall): firewall = test_linode_client.load(Firewall, test_firewall.id) @@ -263,3 +308,43 @@ def test_create_and_delete_vlan(test_linode_client, linode_for_vlan_tests): ) assert is_deleted is True + + +def test_create_firewall_with_linode_device(create_firewall_with_device): + firewall, target_instance = create_firewall_with_device + + devices = firewall.devices + + assert len(devices) == 1 + assert devices[0].entity.id == target_instance.id + + +# TODO (Enhanced Interfaces): Add test for interface device + + +def test_get_global_firewall_settings(test_linode_client): + settings = test_linode_client.networking.firewall_settings() + + assert settings.default_firewall_ids is not None + assert all( + k in {"vpc_interface", "public_interface", "linode", "nodebalancer"} + for k in vars(settings.default_firewall_ids).keys() + ) + + +def test_ip_info(test_linode_client, create_linode): + linode = create_linode + + ip_info = test_linode_client.load(IPAddress, linode.ipv4[0]) + + assert ip_info.address == linode.ipv4[0] + assert ip_info.gateway is not None + assert ip_info.linode_id == linode.id + assert ip_info.interface_id is None + assert ip_info.prefix == 24 + assert ip_info.public + assert ip_info.rdns is not None + assert ip_info.region.id == linode.region.id + assert ip_info.subnet_mask is not None + assert ip_info.type == "ipv4" + assert ip_info.vpc_nat_1_1 is None diff --git a/test/unit/linode_client_test.py b/test/unit/linode_client_test.py index c79c0a88d..7bd8d6c85 100644 --- a/test/unit/linode_client_test.py +++ b/test/unit/linode_client_test.py @@ -1,7 +1,7 @@ from datetime import datetime from test.unit.base import ClientBaseCase -from linode_api4 import LongviewSubscription +from linode_api4 import FirewallCreateDevicesOptions, LongviewSubscription from linode_api4.objects.beta import BetaProgram from linode_api4.objects.linode import Instance from linode_api4.objects.networking import IPAddress @@ -63,12 +63,18 @@ def test_get_regions(self): "NodeBalancers", "Block Storage", "Object Storage", + "Linode Interfaces", ], ) else: self.assertEqual( region.capabilities, - ["Linodes", "NodeBalancers", "Block Storage"], + [ + "Linodes", + "NodeBalancers", + "Block Storage", + "Linode Interfaces", + ], ) self.assertEqual(region.status, "ok") self.assertIsNotNone(region.resolvers) @@ -1185,7 +1191,12 @@ def test_firewall_create(self): } f = self.client.networking.firewall_create( - "test-firewall-1", rules, status="enabled" + "test-firewall-1", + rules, + devices=FirewallCreateDevicesOptions( + linodes=[123], nodebalancers=[456], interfaces=[789] + ), + status="enabled", ) self.assertIsNotNone(f) @@ -1200,6 +1211,11 @@ def test_firewall_create(self): "label": "test-firewall-1", "status": "enabled", "rules": rules, + "devices": { + "linodes": [123], + "nodebalancers": [456], + "interfaces": [789], + }, }, ) @@ -1214,6 +1230,47 @@ def test_get_firewalls(self): self.assertEqual(firewall.id, 123) + def test_get_firewall_settings(self): + """ + Tests that firewall settings can be retrieved + """ + settings = self.client.networking.firewall_settings() + + assert settings.default_firewall_ids.vpc_interface == 123 + assert settings.default_firewall_ids.public_interface == 456 + assert settings.default_firewall_ids.linode == 789 + assert settings.default_firewall_ids.nodebalancer == 321 + + settings.invalidate() + + assert settings.default_firewall_ids.vpc_interface == 123 + assert settings.default_firewall_ids.public_interface == 456 + assert settings.default_firewall_ids.linode == 789 + assert settings.default_firewall_ids.nodebalancer == 321 + + def test_update_firewall_settings(self): + """ + Tests that firewall settings can be updated + """ + settings = self.client.networking.firewall_settings() + + settings.default_firewall_ids.vpc_interface = 321 + settings.default_firewall_ids.public_interface = 654 + settings.default_firewall_ids.linode = 987 + settings.default_firewall_ids.nodebalancer = 123 + + with self.mock_put("networking/firewalls/settings") as m: + settings.save() + + assert m.call_data == { + "default_firewall_ids": { + "vpc_interface": 321, + "public_interface": 654, + "linode": 987, + "nodebalancer": 123, + } + } + def test_ip_addresses_share(self): """ Tests that you can submit a correct ip addresses share api request. diff --git a/test/unit/objects/firewall_test.py b/test/unit/objects/firewall_test.py index a46ea2750..aeb097d12 100644 --- a/test/unit/objects/firewall_test.py +++ b/test/unit/objects/firewall_test.py @@ -54,6 +54,21 @@ def test_update_rules(self): self.assertEqual(m.call_data, new_rules) + def test_create_device(self): + """ + Tests that firewall devices can be created successfully + """ + + firewall = Firewall(self.client, 123) + + with self.mock_post("networking/firewalls/123/devices/123") as m: + firewall.device_create(123, "linode") + assert m.call_data == {"id": 123, "type": "linode"} + + with self.mock_post("networking/firewalls/123/devices/456") as m: + firewall.device_create(123, "interface") + assert m.call_data == {"id": 123, "type": "interface"} + class FirewallDevicesTest(ClientBaseCase): """ @@ -65,7 +80,28 @@ def test_get_devices(self): Tests that devices can be pulled from a firewall """ firewall = Firewall(self.client, 123) - self.assertEqual(len(firewall.devices), 1) + assert len(firewall.devices) == 2 + + assert firewall.devices[0].created is not None + assert firewall.devices[0].id == 123 + assert firewall.devices[0].updated is not None + + assert firewall.devices[0].entity.id == 123 + assert firewall.devices[0].entity.label == "my-linode" + assert firewall.devices[0].entity.type == "linode" + assert firewall.devices[0].entity.url == "/v4/linode/instances/123" + + assert firewall.devices[1].created is not None + assert firewall.devices[1].id == 456 + assert firewall.devices[1].updated is not None + + assert firewall.devices[1].entity.id == 123 + assert firewall.devices[1].entity.label is None + assert firewall.devices[1].entity.type == "interface" + assert ( + firewall.devices[1].entity.url + == "/v4/linode/instances/123/interfaces/123" + ) def test_get_device(self): """ diff --git a/test/unit/objects/networking_test.py b/test/unit/objects/networking_test.py index f982dd6f7..583e06e64 100644 --- a/test/unit/objects/networking_test.py +++ b/test/unit/objects/networking_test.py @@ -121,16 +121,31 @@ def test_rdns_reset(self): self.assertEqual(m.call_data_raw, '{"rdns": null}') - def test_vpc_nat_1_1(self): + def test_get_ip(self): """ Tests that the vpc_nat_1_1 of an IP can be retrieved. """ ip = IPAddress(self.client, "127.0.0.1") - self.assertEqual(ip.vpc_nat_1_1.vpc_id, 242) - self.assertEqual(ip.vpc_nat_1_1.subnet_id, 194) - self.assertEqual(ip.vpc_nat_1_1.address, "139.144.244.36") + def __validate_ip(_ip: IPAddress): + assert _ip.address == "127.0.0.1" + assert _ip.gateway == "127.0.0.1" + assert _ip.linode_id == 123 + assert _ip.interface_id == 456 + assert _ip.prefix == 24 + assert _ip.public + assert _ip.rdns == "test.example.org" + assert _ip.region.id == "us-east" + assert _ip.subnet_mask == "255.255.255.0" + assert _ip.type == "ipv4" + assert _ip.vpc_nat_1_1.vpc_id == 242 + assert _ip.vpc_nat_1_1.subnet_id == 194 + assert _ip.vpc_nat_1_1.address == "139.144.244.36" + + __validate_ip(ip) + ip.invalidate() + __validate_ip(ip) def test_delete_ip(self): """ diff --git a/test/unit/objects/region_test.py b/test/unit/objects/region_test.py index 0bc1afa9e..6ae503098 100644 --- a/test/unit/objects/region_test.py +++ b/test/unit/objects/region_test.py @@ -15,7 +15,6 @@ def test_get_region(self): region = Region(self.client, "us-east") self.assertEqual(region.id, "us-east") - self.assertIsNotNone(region.capabilities) self.assertEqual(region.country, "us") self.assertEqual(region.label, "label7") self.assertEqual(region.status, "ok") @@ -28,6 +27,9 @@ def test_get_region(self): region.placement_group_limits.maximum_linodes_per_pg, 5 ) + self.assertIsNotNone(region.capabilities) + self.assertIn("Linode Interfaces", region.capabilities) + def test_region_availability(self): """ Tests that availability for a specific region can be listed and filtered on. diff --git a/test/unit/objects/vpc_test.py b/test/unit/objects/vpc_test.py index 5e7be1b69..7888bc101 100644 --- a/test/unit/objects/vpc_test.py +++ b/test/unit/objects/vpc_test.py @@ -118,11 +118,19 @@ def validate_vpc_subnet_789(self, subnet: VPCSubnet): "2018-01-01T00:01:01", DATE_FORMAT ) - self.assertEqual(subnet.label, "test-subnet") - self.assertEqual(subnet.ipv4, "10.0.0.0/24") - self.assertEqual(subnet.linodes[0].id, 12345) - self.assertEqual(subnet.created, expected_dt) - self.assertEqual(subnet.updated, expected_dt) + assert subnet.label == "test-subnet" + assert subnet.ipv4 == "10.0.0.0/24" + assert subnet.linodes[0].id == 12345 + assert subnet.created == expected_dt + assert subnet.updated == expected_dt + + assert subnet.linodes[0].interfaces[0].id == 678 + assert subnet.linodes[0].interfaces[0].active + assert subnet.linodes[0].interfaces[0].config_id is None + + assert subnet.linodes[0].interfaces[1].id == 543 + assert not subnet.linodes[0].interfaces[1].active + assert subnet.linodes[0].interfaces[1].config_id is None def test_list_vpc_ips(self): """ From c62f1603d55839087b2f76e8fc1c24c7a5f5006d Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 15 Apr 2025 09:23:15 -0400 Subject: [PATCH 2/2] Add LA notices --- linode_api4/groups/networking.py | 2 ++ linode_api4/objects/networking.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/linode_api4/groups/networking.py b/linode_api4/groups/networking.py index ae90b577a..16207665b 100644 --- a/linode_api4/groups/networking.py +++ b/linode_api4/groups/networking.py @@ -120,6 +120,8 @@ def firewall_settings(self) -> FirewallSettings: API Documentation: Not yet available. + NOTE: This feature may not currently be available to all users. + :returns: An object representing the Linode Firewall settings for the current user. :rtype: FirewallSettings """ diff --git a/linode_api4/objects/networking.py b/linode_api4/objects/networking.py index 725475f33..3ebf85a27 100644 --- a/linode_api4/objects/networking.py +++ b/linode_api4/objects/networking.py @@ -195,6 +195,8 @@ class FirewallSettingsDefaultFirewallIDs(JSONObject): """ Contains the IDs of Linode Firewalls that should be used by default when creating various interface types. + + NOTE: This feature may not currently be available to all users. """ vpc_interface: Optional[int] = None @@ -208,6 +210,8 @@ class FirewallSettings(Base): Represents the Firewall settings for the current user. API Documentation: Not yet available. + + NOTE: This feature may not currently be available to all users. """ api_endpoint = "/networking/firewalls/settings"