Skip to content

[bug] SubnetDivisionIndex foreign keys (subnet_id, ip_id) are saved as NULL during provisioning #1435

Description

@ApurveKaranwal

Describe the bug

When subnets and IP addresses are provisioned by a SubnetDivisionRule (either during the initial provisioning or when updating number_of_ips), the foreign keys (subnet_id and ip_id) on the corresponding SubnetDivisionIndex records are saved as NULL in the database.

This breaks two important pieces of functionality:

  1. Django Admin protections

    In subnet_division/admin.py, the change permission logic checks whether a subnet was automatically generated using:

    SubnetDivisionIndex.objects.filter(subnet_id=obj.id).exists()

    Since subnet_id is stored as NULL, this query always returns False. As a result, automatically generated subnets and IP addresses are treated as normal objects and can be edited or deleted manually from the Django Admin, leading to inconsistent or corrupted IPAM data.

  2. REST API queries and filters

    Several queries rely on the SubnetDivisionIndex relationships to resolve related configurations/devices or identify automatically generated resources. Since these foreign keys are missing, those queries fail to return the expected results.


Steps To Reproduce

  1. Create a SubnetDivisionRule (for example, a VPN Subnet Division Rule).
  2. Attach a device configuration or apply a template so provisioning is triggered.
  3. After provisioning completes, inspect the subnet_division_subnetdivisionindex table or query it from the Django shell.
  4. Observe that the generated records have subnet_id = NULL and/or ip_id = NULL.
  5. Open Django Admin → IPAM → Subnets.
  6. Open one of the automatically generated subnets.
  7. Notice that the subnet is editable, even though it was generated automatically and should be read-only.

Expected behavior

Generated SubnetDivisionIndex records should have valid subnet_id and ip_id foreign keys pointing to their corresponding generated objects.

Consequently:

  • Automatically generated subnets should be detected correctly by the Django Admin.
  • Automatically generated IP addresses should also be protected.
  • API queries relying on these relationships should work correctly.

Root Cause Analysis

The issue occurs because the code copies the primary key values (subnet.id / ip.id) before the objects are saved.

At that point, those IDs are still None.

Later, bulk_create() updates the model instances with their generated primary keys, but it does not update primitive values that were copied earlier.

tasks.py

generated_indexes.append(
    SubnetDivisionIndex(
        keyword=f"{division_rule.label}_subnet{subnet.id}_ip{ip_id}",
        subnet_id=subnet.id,
        ip_id=ip.id,             # ip.id is None here
        rule_id=division_rule.id,
        config_id=index.config_id,
    )
)

Here, ip.id is copied before bulk_create() assigns an ID, so ip_id remains NULL.


rule_types/base.py

generated_indexes.append(
    SubnetDivisionIndex(
        keyword=f"{division_rule.label}_subnet{subnet_id}",
        subnet_id=subnet_obj.id,  # subnet_obj.id is None here
        rule_id=division_rule.id,
        config=config,
    )
)

Similarly, subnet_obj.id is copied before the subnet has been saved, resulting in subnet_id = NULL.


Why this happens

Django's bulk_create() populates the primary keys on the model instances themselves after insertion.

For example:

subnet.id  # becomes populated after bulk_create()
ip.id      # becomes populated after bulk_create()

However, values that were copied earlier are not updated:

SubnetDivisionIndex(
    subnet_id=subnet.id,  # Copies None
)

The copied value remains None permanently unless explicitly updated afterward.


Minimal Reproduction

# Before bulk_create
ip.id == None

index = SubnetDivisionIndex(ip_id=ip.id)

# bulk_create assigns:
ip.id == 42

# But index.ip_id is still:
None

Proof of Bug

[Buggy Way] After bulk_create, index_buggy.ip_id is: None
Expected: 42
Actual:   None

[Fixed Way] After bulk_create and save, index_fixed.ip_id is: 42
Expected: 42
Actual:   42

Impact

This issue causes:

  • Django Admin protections to fail for generated subnets/IPs.
  • Automatically generated objects to become editable and deletable.
  • Broken REST API filtering and relationship resolution.
  • Missing referential integrity between SubnetDivisionIndex and generated IPAM objects.

System Information

  • OS:
    • Windows 11
    • Ubuntu 24.04 LTS
  • Python: 3.12
  • Django:
    • 5.2
    • 4.2

Metadata

Metadata

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions