diff --git a/linode_api4/groups/networking.py b/linode_api4/groups/networking.py index 4820b706d..ba1e656bd 100644 --- a/linode_api4/groups/networking.py +++ b/linode_api4/groups/networking.py @@ -367,3 +367,30 @@ def transfer_prices(self, *filters): return self.client._get_and_filter( NetworkTransferPrice, *filters, endpoint="/network-transfer/prices" ) + + def delete_vlan(self, vlan, region): + """ + This operation deletes a VLAN. + You can't delete a VLAN if it's still attached to a Linode. + + API Documentation: https://techdocs.akamai.com/linode-api/reference/delete-vlan + + :param vlan: The label of the VLAN to be deleted. + :type vlan: str or VLAN + :param region: The VLAN's region. + :type region: str or Region + """ + if isinstance(region, Region): + region = region.id + + if isinstance(vlan, VLAN): + vlan = vlan.label + resp = self.client.delete( + "/networking/vlans/{}/{}".format(region, vlan), + model=self, + ) + + if "error" in resp: + return False + + return True diff --git a/test/integration/conftest.py b/test/integration/conftest.py index 34aba39db..8c7d44a57 100644 --- a/test/integration/conftest.py +++ b/test/integration/conftest.py @@ -502,3 +502,22 @@ def pytest_configure(config): "markers", "smoke: mark test as part of smoke test suite", ) + + +@pytest.fixture(scope="session") +def linode_for_vlan_tests(test_linode_client, e2e_test_firewall): + client = test_linode_client + region = get_region(client, {"Linodes", "Vlans"}, site_type="core") + label = get_test_label(length=8) + + linode_instance, password = client.linode.instance_create( + "g6-nanode-1", + region, + image="linode/debian12", + label=label, + firewall=e2e_test_firewall, + ) + + yield linode_instance + + linode_instance.delete() diff --git a/test/integration/models/networking/test_networking.py b/test/integration/models/networking/test_networking.py index 9bffb57e8..032436246 100644 --- a/test/integration/models/networking/test_networking.py +++ b/test/integration/models/networking/test_networking.py @@ -4,11 +4,15 @@ get_region, get_token, ) -from test.integration.helpers import get_test_label +from test.integration.helpers import ( + get_test_label, + retry_sending_request, + wait_for_condition, +) import pytest -from linode_api4 import LinodeClient +from linode_api4 import Instance, LinodeClient from linode_api4.objects import Config, ConfigInterfaceIPv4, Firewall, IPAddress from linode_api4.objects.networking import NetworkTransferPrice, Price @@ -163,3 +167,45 @@ def test_allocate_and_delete_ip(test_linode_client, create_linode): is_deleted = ip.delete() assert is_deleted is True + + +def get_status(linode: Instance, status: str): + return linode.status == status + + +def test_create_and_delete_vlan(test_linode_client, linode_for_vlan_tests): + linode = linode_for_vlan_tests + + config: Config = linode.configs[0] + + config.interfaces = [] + config.save() + + vlan_label = "testvlan" + interface = config.interface_create_vlan( + label=vlan_label, ipam_address="10.0.0.2/32" + ) + + config.invalidate() + + assert interface.id == config.interfaces[0].id + assert interface.purpose == "vlan" + assert interface.label == vlan_label + + # Remove the VLAN interface and reboot Linode + config.interfaces = [] + config.save() + + wait_for_condition(3, 100, get_status, linode, "running") + + retry_sending_request(3, linode.reboot) + + wait_for_condition(3, 100, get_status, linode, "rebooting") + assert linode.status == "rebooting" + + # Delete the VLAN + is_deleted = test_linode_client.networking.delete_vlan( + vlan_label, linode.region + ) + + assert is_deleted is True diff --git a/test/unit/objects/networking_test.py b/test/unit/objects/networking_test.py index 7192683ef..d12167d8c 100644 --- a/test/unit/objects/networking_test.py +++ b/test/unit/objects/networking_test.py @@ -1,6 +1,6 @@ from test.unit.base import ClientBaseCase -from linode_api4 import ExplicitNullValue, Instance +from linode_api4 import VLAN, ExplicitNullValue, Instance, Region from linode_api4.objects import Firewall, IPAddress, IPv6Range @@ -94,3 +94,17 @@ def test_delete_ip(self): ip.delete() self.assertEqual(m.call_url, "/linode/instances/123/ips/127.0.0.1") + + def test_delete_vlan(self): + """ + Tests that deleting a VLAN creates the correct api request + """ + with self.mock_delete() as m: + self.client.networking.delete_vlan( + VLAN(self.client, "vlan-test"), + Region(self.client, "us-southeast"), + ) + + self.assertEqual( + m.call_url, "/networking/vlans/us-southeast/vlan-test" + )