linode_api4-python icon indicating copy to clipboard operation
linode_api4-python copied to clipboard

Linode Interfaces: Allow specifying ExplicitNullValue for LinodeInterfaceOptions firewall ID

Open lgarber-akamai opened this issue 7 months ago • 0 comments

📝 Description

This pull request adds support for users specifying an ExplicitNullValue for the firewall_id field when creating a Linode interface. This is necessary when specifying a Linode interface with no firewall because an unspecified/implicit null value will cause the API to use the account-wide default firewall or raise an error if no default firewall has been configured.

✔️ How to Test

The following test steps assume you have pulled down this PR locally and run make install.

Unit Testing

make test-unit

Integration Testing

make test-int TEST_COMMAND=models/linode

Manual Testing

  1. In a linode_api4-python sandbox environment (e.g. dx-devenv), run the following:
import os

from linode_api4 import (
    LinodeClient,
    LinodeInterfaceOptions,
    LinodeInterfacePublicOptions,
    ExplicitNullValue,
    LinodeInterfaceDefaultRouteOptions,
    ApiError,
)

linode_client = LinodeClient(
    os.getenv("LINODE_TOKEN"), base_url="https://api.linode.com/v4beta"
)

instance_index = 0


def provision_instance(interface_firewall_id):
    global instance_index

    result, _ = linode_client.linode.instance_create(
        ltype="g6-nanode-1",
        region="us-mia",
        image="linode/ubuntu24.04",
        label=f"pythonsdk-test-{instance_index}",
        booted=False,
        linode_interfaces=[
            LinodeInterfaceOptions(
                default_route=LinodeInterfaceDefaultRouteOptions(
                    ipv4=True,
                    ipv6=True,
                ),
                firewall_id=interface_firewall_id,
                public=LinodeInterfacePublicOptions(),
            )
        ],
    )

    instance_index += 1

    return result


firewall = linode_client.networking.firewall_create(
    "pythonsdk-test-firewall",
    rules={
        "outbound_policy": "DROP",
        "inbound_policy": "DROP",
        "inbound": [],
        "outbound": [],
    },
)

# Provision an instance with no attached firewall
no_firewalls = provision_instance(ExplicitNullValue)

# Provision an instance with an implicit firewall
try:
    implicit_firewall = provision_instance(None)
except ApiError as e:
    implicit_firewall = "N/A"

# Provision an instance with an explicit firewall
explicit_firewall = provision_instance(firewall.id)

print(f"Firewall: {firewall.id}")

print(f"No Firewalls: {no_firewalls}")
print(f"Implicit Firewall: {implicit_firewall}")
print(f"Explicit Firewall: {explicit_firewall}")
  1. Ensure the output matches to the following:
Firewall: 

No Firewalls: Instance: 
Implicit Firewall: Instance: 78999982
Explicit Firewall: Instance: 78999983

NOTE: These values may not be correct due to a potential API issue. The POST bodies can be validated manually by adding a print(body) here: https://github.com/linode/linode_api4-python/blob/7b7f6470c8d61f46f9561b1cdaa8fee7a090d607/linode_api4/linode_client.py#L281

lgarber-akamai avatar Jun 20 '25 19:06 lgarber-akamai