Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce IPAM Namespaces #3378

Merged
merged 85 commits into from
Jun 2, 2023
Merged

Conversation

jathanism
Copy link
Contributor

@jathanism jathanism commented Feb 28, 2023

Closes: #3337, #3429, #3681

What's Changed

This is the very rough around the edges prototype of IPAM Namespaces. It is not yet complete.

It has drifted somewhat from what was discussed in the design chats and what is scoped on #3337, due to the experimentation in the data design during prototyping.

The gist

  • VRFs can have prefixes assigned to them
  • VRFs can have devices assigned to them
  • VRFs can be used across devices
  • Any number of unique VRFs may be assigned to any number of devices, therefore allowing VRF names to be reused across devices
  • An RD is only significant upon assignment of a VRF to a device; therefore the same VRF can be assigned to multiple devices, each with a distinct (including null) RD if so desired
  • Any number of unique Prefixes may be assigned to any number of VRFs, so long as they are in the same namespace

Changes that drifted related to VRF/RD

  • VRF has FK to Namespace instead of deriving namespace through RD
  • VRF has m2m to Device, Prefix with through tables for each
  • VRF through table for Device is where RD is assigned; validation asserts that RD (if set) namespace matches VRF namespace
  • VRF through table for Device; validation asserts that if set, VRF namespace must match Prefix namespace

Caveats

  • This will likely not work in any API or UI views. It's rough.
  • This is tested at the ORM only at this time, expecting manual creation of objects to assert functionality.
  • There are fields commented out in serializers/views/forms/filters so that database migrations work without complaining

TODO

  • Explanation of Change(s)
  • Added change log fragment(s) (for more information see the documentation)
  • Attached Screenshots, Payload Example
  • Unit, Integration Tests
  • Documentation Updates (when adding/changing features)
  • Example Plugin Updates (when adding/changing features)
  • Outline Remaining Work, Constraints from Design

@jathanism jathanism marked this pull request as draft February 28, 2023 20:10
@jathanism jathanism changed the title Prototype/jathanism 3337 ipam namespace Prototype: IPAM Namespaces Feb 28, 2023
@bryanculver
Copy link
Member

@jathanism Can you describe the why we deviated from the original design? As far as I can tell this moves the validation into Python as opposed to schema/DB. Is this long-term desirable? What were the limitation with the original?

An RD is only significant upon assignment of a VRF to a device; therefore the same VRF can be assigned to multiple devices, each with a distinct (including null) RD if so desired

Is this, could this not be, done behind the scenes from the API interfaces? Where we get schema performant validation but simplified APIs for integrators? Is that the plan and this just a compromise for the prototype?

@glennmatthews
Copy link
Contributor

Could you add a class diagram similar to #3337 (comment) and #3245 (comment)? I'm having a hard time telling from the text description alone how this implementation differs from those and what the implications are.

@jathanism
Copy link
Contributor Author

Could you add a class diagram similar to #3337 (comment) and #3245 (comment)? I'm having a hard time telling from the text description alone how this implementation differs from those and what the implications are.

@jathanism Can you describe the why we deviated from the original design? As far as I can tell this moves the validation into Python as opposed to schema/DB. Is this long-term desirable? What were the limitation with the original?

An RD is only significant upon assignment of a VRF to a device; therefore the same VRF can be assigned to multiple devices, each with a distinct (including null) RD if so desired

Is this, could this not be, done behind the scenes from the API interfaces? Where we get schema performant validation but simplified APIs for integrators? Is that the plan and this just a compromise for the prototype?

Context, descriptions and some Mermaid diagrams have been provided in the issue: #3337 (comment)

@jathanism
Copy link
Contributor Author

Could you add a class diagram similar to #3337 (comment) and #3245 (comment)? I'm having a hard time telling from the text description alone how this implementation differs from those and what the implications are.

This is the output from:

nautobot-server graph_models -a -g -I Namespace,Prefix,IPAddress,RouteDistinguisher,VRF,VRFDeviceAssignment,VRFPrefixAssignment,Interface,Device` -o nautobot_ipam_namespace.png

The boxes for Device and Interface were cropped out of this render for conciseness, but you can see the relationship lines pointing to them at the top of the image.

image

@HanlinMiao
Copy link
Contributor

#3395
This is the implementation based on the original discussion we had. @jathanism and I collaborated on this.

@jathanism jathanism changed the title Prototype: IPAM Namespaces [Prototype] IPAM Namespaces modified from original design. Mar 7, 2023
@jathanism jathanism force-pushed the prototype/jathanism-3337-ipam-namespace branch 3 times, most recently from 1968fa4 to 5dbd1c9 Compare March 9, 2023 00:20
@jathanism
Copy link
Contributor Author

jathanism commented Mar 9, 2023

Latest changes:

Got UI/API working with bare minimum effort to keep things flowing.

Namespace

  • Went with default "Global" namespace for foreign keys FOR NOW just to keep things moving
  • Basic API/UI views

image

image

General

  • Had to remove vrf from select_related for Prefix, IPAddress in about 7,000 places

RouteDistinguisher

  • Basic API/UI views

image

image

image

image

VRF

  • VRF edit includes namespace/devices, prefixes limited by namespace
  • VRF detail includes devices/prefixes
  • Filter by namespace now possible

image

image

image

image

image

Prefix

  • Prefix.objects.annotate_tree now filters on namespace not VRF
  • Edit view includes namespace
  • List view groups by namepace
  • Filter by namespace now possible
  • Prefix.get_duplicates() changed to a noop (returns queryset.none())
  • Available prefixes now filtered by namespace instead of VRF

image

image

image

image

IPAddress

  • WIP: Not filtering on VRF, but also not updated for Prefix parenting yet

Generic

  • Had to patch ObjectEditView to catch ValidationError for PrefixEditView because of the unique_together constraint ValidationError/IntegrityError not being caught the form validation due to network and prefix_length fields being excluded from validation due to not being included in the form fields.

Not Done

  • Linking RD to VRF when assigned to Devices "somehow"
  • Show assigned VRFs in Device detail view
  • Assigning a VRF to an Interface
  • Showing assigned VRF in Interface detail view

@jathanism jathanism force-pushed the prototype/jathanism-3337-ipam-namespace branch from 5dbd1c9 to fccb6c5 Compare March 9, 2023 00:58
@bryanculver bryanculver linked an issue Mar 9, 2023 that may be closed by this pull request
6 tasks
@@ -44,6 +46,19 @@
)


class NamespaceFilterSet(NautobotFilterSet):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs LocatableModelFilterSetMixin.

@bryanculver bryanculver changed the title [Prototype] IPAM Namespaces modified from original design. Introduce IPAM Namespaces Mar 10, 2023
Copy link
Member

@bryanculver bryanculver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes:

  • I think there was unanimous agreement that there shouldn't be a VRF property on IPAddress so I've updated places that might have caused issues in testing in UI (instead of commenting out), there might be some utility in a helper method to get the parent prefix's vrf

@@ -1714,7 +1714,8 @@ class InterfaceView(generic.ObjectView):
def get_extra_context(self, request, instance):
# Get assigned IP addresses
ipaddress_table = InterfaceIPAddressTable(
data=instance.ip_addresses.restrict(request.user, "view").select_related("vrf", "tenant"),
# data=instance.ip_addresses.restrict(request.user, "view").select_related("vrf", "tenant"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VRF relationship moves to Interface so this becomes unnecessary.

@@ -1828,7 +1828,7 @@ def get_extra_context(self, request, instance):
"device_type",
)
ipaddress = instance.ip_addresses.select_related(
"vrf",
# "vrf",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VRF is either a property of Prefix or Interface, not needed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes:

  • I think there was unanimous agreement that there shouldn't be a VRF property on IPAddress so I've updated places that might have caused issues in testing in UI (instead of commenting out), there might be some utility in a helper method to get the parent prefix's vrf

Oh yeah, for sure, I stopped at the places that were causing utter breakage in other views. I didn't actually complete the necessary work for IPAddress.

Comment on lines 12 to 19
def ipam_object_saved(sender, instance, raw=False, **kwargs):
"""
Forcibly call `full_clean()` when a new IPAM object is manually saved to prevent creation of
invalid objects.
"""
if raw:
return
instance.full_clean()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to create some noise, as it's causing a bunch of tests to blow up everywhere. See: https://github.com/nautobot/nautobot/pull/3378/files#diff-00a7372c809230ca828aa70f90352848b6654faadb78e4762a8f55ee6c59ce4fR584-R585

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. This is there for now to force model validation in lieu of having formsets for m2m intermediate relationships for the prototype. I don't think it's worth creating the formset code right now if we're just going to rip it out and replace it for the v2 UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disabled it for Prefix and VRF.



@extras_features(
"custom_fields",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 Silly git

@bryanculver
Copy link
Member

I was reviewing the test changes in the prototype and the changes being made in #2403 and I still have some thoughts around the VRF-Prefix M2M:

  • Intuitively we will be adding an optional FK on (VM)Interface to VRF.
  • Considering IPAM Overhaul: Assign IP Address to Multiple Interfaces #2403 change, or even the old behavior where an Interface can have many IPs, it would be expected that if I have a VRF set on the Interface, I should only see the IPAddress selections from Prefixes that are a part of the same VRF
    • Hand-wavey: We should eventually as a part of UX make this easier for the user to assign the VRF/Prefixes to each other but call out that we are building it for them (think modal popup "add prefix to VRF" when editing Interface)
  • If a Prefix was allowed to be a part of multiple VRFs (via M2M) then is that address assignable because it's through a different VRF or is that not a separate network?
    - Interface(ETH0) is a member of VRF(Orange)
    - Interface(ETH1) is a member of VRF(Blue)
    - Prefix(10.0.0.0/24) is a member of VRF(Orange) and VRF(Blue)
    - I want to assign IPAddress(10.0.0.100) which has a (concrete relationship to Prefix(10.0.0.0/24)) to Interface(ETH0), this would work but then it's an assignment to Interface(ETH1) would be :sus: because
    - These are effectively the same network (and impose the same VRF) and I would get an address conflict, or at least would have to be a "backup" IP and have additional validation check
    - These are different networks and then I should not be using the same IPAddress object and that would require a different Namespace and VRF entirely

Also, Prefix-VRF was non-M2M before and this never came up before as a limitation, but it was a uniqueness constraint and it would create a unique object because it was implied to be a different network and on the off-chance you did want them to be the same you would have to manage the duplicates manually between these prefixes.

Though there's probably a single sentence that unravels my train of thought here...

jathanism and others added 8 commits March 14, 2023 13:58
- Signal to forcefully call `full_clean()` on assignment of device(s) to VRF or prefix(es) to VRF

Modify Interface IPAddress assignment to m2m

- Did not yet remove `assigned_object` or any of that jazz from IPAddress
- Committed signals (forgot in a previous commit)

Remove `IPAddress.vrf`

- Clarify unique constraints for VRFDeviceAssignment,
- add m2m signal for VRF <-> PRefix

Refactor to flatten migrations and implement VMInterface <-> IPAddress m2m

Linting

Default Namespace will always be created with name "Global" for use in default FK value for RouteDistinguisher, VRF, and Prefix.
- Also make Black happy. Yay!
Namespace

- Went with default "Global" namespace for foreign keys FOR NOW just to keep things moving

General

- Had to remove `vrf` from `select_related` for `Prefix`, `IPAddress` in about 7,000 places

VRF

- VRF edit includes namespace/devices, prefixes limited by namespace
- VRF detail includes devices/prefixes
- Filter by namespace now possible

Prefix

- `Prefix.objects.annotate_tree` now filters on namespace not VRF
- Edit view includes namespace
- List view groups by namepace
- Filter by namespace now possible
- `Prefix.get_duplicates()` changed to a noop (returns `queryset.none()`)
- Available prefixes now filtered by namespace instead of VRF

IPAddress

- WIP: Not filtering on VRF, but also not updated for Prefix parenting yet

Generic

- Had to patch `ObjectEditView` to catch `ValidationError` for `PrefixEditView` because of the `unique_together` constraint ValidationError/IntegrityError not being caught the form validation due to `network` and `prefix_length` fields being excluded from validation due to no
t being included in the form fields.
- Fixed lingering issues where `vrf` was still in `select_related` in some cases
- Refactored signal handlers to use @receiver decorator
- Added `VRFDeviceAssignmentTable` for use in displaying devices assigned to a VRF in RouteDistinguisher detail view.
- Added a custom detail view template for RouteDistinguisher
Based on today's demo review...

- Namespace
  - Added `description` field
  - Added `location` foreign key
  - Added `@extras_features`
  - Added assigned VRFs, Prefixes to detail view
- RouteDistinguisher
  - Removed entirely
  - Revered back to `VRF.rd` field
- VRF
  - Restored `rd` field
  - Added `namespace` to bulk edit
  - Added assigned Devices, Prefixes to detail view
- VRFDeviceAssignment
  - Added `rd` field
  - Added `name` field
  - If either `rd` or `name` are not set on assignment, they are inherited from the VRF
  - Added a new `vrf_device_associated` signal handler for `m2m_changed.post_add` to hack m2m validation until a formset is put into place for Device/VRF edit views
- Prefix
  - Added `namespace` to bulk edit
  - Added assigned VRFs to detail view
- Also improved UI error display slightly.
@@ -228,6 +228,7 @@ def test_extend_schema_null_field_choices(self):
self.assertIsInstance(getattr(schema, "resolve_mode"), types.FunctionType)


@skip("Something really funky is broken here. Setup fails due to content types not existing...")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix before merging?

@bryanculver bryanculver marked this pull request as ready for review June 2, 2023 19:20
@gsnider2195 gsnider2195 merged commit 03eaac7 into next Jun 2, 2023
19 checks passed
@gsnider2195 gsnider2195 deleted the prototype/jathanism-3337-ipam-namespace branch June 2, 2023 19:21
bryanculver added a commit that referenced this pull request Jun 5, 2023
Co-authored-by: Bryan Culver <bryan.culver@networktocode.com>
Co-authored-by: Glenn Matthews <glenn.matthews@networktocode.com>
Co-authored-by: Gary Snider <gary.snider@networktocode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants