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

Device Redundancy Groups #2615

Merged
merged 47 commits into from
Nov 4, 2022
Merged

Device Redundancy Groups #2615

merged 47 commits into from
Nov 4, 2022

Conversation

bryanculver
Copy link
Member

@bryanculver bryanculver commented Oct 14, 2022

Closes: #1892

What's Changed

Starting RedundancyGroup implementation.

So far what's implemented:

  • DeviceRedundancyGroup model, supporting custom fields, tags, statuses, SecretGroups
    • failover_strategy is a choice, currently identified as Active/Active or Active/Passive
  • Device has been updated:
    • A FK to DeviceRedundancyGroup as device_redundancy_group
    • A PositiveSmallInt for optional priority as device_redundancy_group_priority
  • ConfigContext has been updated:
    • A ManyToManyField to dcim.DeviceRedundancyGroup as device_redundancy_groups for the purposes of applying a ConfigContext based upon a Devices DeviceRedundancyGroup membership
    • Necessary form, queryset, filter updates to make use of this change
    • Bug fixed on ConfigContextFilterSet: schema not a valid filter but exists on form, see https://demo.nautobot.com/extras/config-contexts/
    • schema filter form field on ConfigContextFilterForm wasn't compatible with proper filter field introduced above

Other changes due to changes in Factory fuzzing exposing test failures

  • Devices form test (DeviceTestCase) was flaky

    • A DeviceType from factory may not be found that has a Manufacturer with an associated Platforms that is full depth and 1U height
      • We just needed a DeviceType of non-zero U height
      • Tests updated if needed to be offset by a derived U height
  • BaseNetworkQuerySet and IPAddressQuerySet's parse_network_string function overhauled (used when performing incomplete searches for network addresses)

    • Searching for b2a (a valid abbreviated first hextet for an IPv6 address) would not return results. This was due to RE_HEXTET requiring a unabbreviated hextet for searching
      • RE_HEXTET updated to permit abbreviated forms of hextet
      • This change exacerbated the bug below
    • Searching for 10 would always result in only IPv4 searches, even though it also is a valid IPv6 beginning hextet. This is an ambiguous search string so should return results for both.
      • _is_ambiguous_network_string introduced to help determine if inputted string could be either a valid IPv4 beginning octet or a valid IPv6 beginning hextet
      • string_search updated to search for both IPv4 and IPv6 addresses if an ambiguous search string is given
    • To keep the output of parse_network_string consistent (both derived IP version and always a single netaddr.IPNetwork) and DRY the following changes were made:
      • _safe_parse_network_string introduced to forcibly try to parse inputted search string as either a IPv4 or IPv6 address or fragment
        • This calls internal parse_ipv4 and parse_ipv6 as parse_network_string did before
        • This keeps parse_network_string's original error handling for thrown netaddr.core.AddrFormatError and ensures always return a valid netaddr.IPNetwork object
      • _check_and_prep_ipv6 introduced to perform much of the "probably IPv6 address" logic originally done in parse_network_string
        • Will also prepare the string for parse_ipv6 by appending the necessary :s
      • parse_network_string simplified significantly by leveraging the discrete functions above
    • No tests were harmed in this rewrite however new ones were added to test the discrete functions added above
  • RouteTargets View test case (RouteTargetTestCase) was exposed to have a flaky csv_data structure

    • Sometimes Tenants name can include a comma so structure was updated to always escape this field
  • PrefixFactory may randomly decided to create a child of 2.2.2.2/32. This is because it checks the size of the prefix and attempts to create children if it's of a large enough size.

    • Weird behavior of prefix.size:
      • Prefix(2.2.2.2/31).size correctly returns 2 which can have two children in it, 2.2.2.2/32 and 2.2.2.3/32
      • Prefix(2.2.2.2/32).size correctly returns 1 but cannot have a child
    • Factory updated to not create children if prefix.size is 1
  • VirtualChassis Filter test case (VirtualChassisTestCase) had too narrow of a region lookup

    • Region.objects.filter(sites__isnull=False, children__isnull=True, parent__isnull=True) may not have returned the required 3 Regions. The tests below were improved in earlier commits to not require no parents and no children so updated to cls.regions = Region.objects.filter(sites__isnull=False)[:3]
  • Similar failure for DynamicFilterLookupExpressionTest Filter test as VirtualCassis filter test above.

  • RackGroup Model test (test_change_rackgroup_site) may have randomly chosen the same site to update the rackgroups existing membership to

    • site_b updated to be the last object returned from an exclusion of the existing membership site
  • Similar failure for Prefix Model test as RackGroup above.

  • Location View test case (LocationTestCase) was exposed to have a flaky csv_data structure

    • Sometimes names can include a comma so structure was updated to always escape these fields
  • RIR API View test case (RIRTest) may have had no deleteable objects to test

    • Aggregate has in inbound protected relationship to RIR which would prevent deletion. Instead of increasing random pool size, an ensured deletable object was created
  • test_slug_not_modified was updated to ensure no collision on new slug source value as well as changing lookup expressions from __contains to __exact in the case where auto-generated names have nested details (ex: Device names: Device A and Device A - PSU)

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)
  • NA Example Plugin Updates (when adding/changing features)
  • Outline Remaining Work, Constraints from Design

nautobot/dcim/api/views.py Outdated Show resolved Hide resolved
nautobot/dcim/choices.py Show resolved Hide resolved
nautobot/dcim/filters.py Outdated Show resolved Hide resolved
nautobot/dcim/forms.py Outdated Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/tables/devices.py Outdated Show resolved Hide resolved
nautobot/dcim/templates/dcim/redundancygroup_retrieve.html Outdated Show resolved Hide resolved
nautobot/dcim/views.py Outdated Show resolved Hide resolved
nautobot/dcim/views.py Outdated Show resolved Hide resolved
@bryanculver bryanculver linked an issue Oct 14, 2022 that may be closed by this pull request
1 task
@lampwins
Copy link
Member

lampwins commented Oct 14, 2022

Built-in validation for no two Devices with same priority?

I'd say no to this validation. There are valid real-world use cases where two devices would have the same priority. For instance, I might have a cluster of 4 firewalls with two 10 and two 20. End users could implement custom validators if they want this validation.

nautobot/core/forms.py Outdated Show resolved Hide resolved
nautobot/dcim/api/urls.py Outdated Show resolved Hide resolved
nautobot/dcim/api/serializers.py Outdated Show resolved Hide resolved
nautobot/dcim/api/serializers.py Show resolved Hide resolved
nautobot/dcim/homepage.py Outdated Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/navigation.py Outdated Show resolved Hide resolved
nautobot/dcim/models/devices.py Outdated Show resolved Hide resolved
nautobot/dcim/urls.py Show resolved Hide resolved
nautobot/dcim/api/views.py Outdated Show resolved Hide resolved
nautobot/dcim/filters.py Outdated Show resolved Hide resolved
nautobot/dcim/forms.py Show resolved Hide resolved
nautobot/dcim/models/devices.py Outdated Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/models/devices.py Show resolved Hide resolved
nautobot/dcim/views.py Outdated Show resolved Hide resolved
nautobot/dcim/views.py Outdated Show resolved Hide resolved
bryanculver and others added 3 commits October 14, 2022 13:28
Co-authored-by: Jathan McCollum <jathan@gmail.com>
Co-authored-by: HanlinMiao <46973263+HanlinMiao@users.noreply.github.com>
Co-authored-by: Glenn Matthews <glenn.matthews@networktocode.com>
Co-authored-by: Glenn Matthews <glenn.matthews@networktocode.com>
@jathanism jathanism mentioned this pull request Oct 17, 2022
7 tasks
@glennmatthews glennmatthews changed the title Redundancy Groups [WIP] Device Redundancy Groups Oct 18, 2022
@bryanculver bryanculver changed the title [WIP] Device Redundancy Groups Device Redundancy Groups Oct 28, 2022
@bryanculver bryanculver marked this pull request as ready for review October 28, 2022 03:15
@@ -1039,6 +1041,11 @@ class DeviceFilterSet(
field_name="virtual_chassis",
label="Is a virtual chassis member",
)
device_redundancy_group = NaturalKeyOrPKMultipleChoiceFilter(
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want a has_device_redundancy_group filter?

Copy link
Member Author

Choose a reason for hiding this comment

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

Does that hold up the merge?

Copy link
Contributor

Choose a reason for hiding this comment

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

no

nautobot/ipam/tests/test_api.py Show resolved Hide resolved
nautobot/tenancy/tests/test_views.py Show resolved Hide resolved
nautobot/utilities/testing/views.py Outdated Show resolved Hide resolved
Copy link
Contributor

@jathanism jathanism left a comment

Choose a reason for hiding this comment

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

Small requests here. Loving the changes to parse_network_string!

@@ -383,14 +383,18 @@ def test_slug_not_modified(self):
"""Ensure save method does not modify slug that is passed in."""
# This really should go on a models test page, but we don't have test structures for models.
if self.slug_source is not None:
new_slug_source_value = "kwyjibo"
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment on lines +391 to +392
filter_ = self.slug_source + "__exact"
self.model.objects.filter(**{filter_: self.slug_test_object}).update(
Copy link
Contributor

Choose a reason for hiding this comment

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

Not that it's a huge deal, but exact is the default. If we're switching to this, we might as well rip this out:

Suggested change
filter_ = self.slug_source + "__exact"
self.model.objects.filter(**{filter_: self.slug_test_object}).update(
self.model.objects.filter(**{self.slug_source: self.slug_test_object}).update(

Copy link
Member Author

Choose a reason for hiding this comment

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

Did you test that suggestion?



class DeviceRedundancyGroupViewSet(StatusViewSetMixin, NautobotModelViewSet):
queryset = DeviceRedundancyGroup.objects.select_related("status").prefetch_related("members")
Copy link
Contributor

Choose a reason for hiding this comment

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

We're safe to use select_related, it just won't be "optimized" by Cacheops. See: Cacheops caveats, notably:

Update of "selected_related" object does not invalidate cache for queryset. Use .prefetch_related() instead.

Comment on lines +1799 to +1801
field_name="secrets_group",
queryset=SecretsGroup.objects.all(),
to_field_name="slug",
Copy link
Contributor

Choose a reason for hiding this comment

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

Another minor nit, but pretty sure it's redundant to specify field_name and to_field_name here. First because field_name is only required if the related field differs from the name of the filter defined on the filterset, and to_field_name defaults to slug. :)

Suggested change
field_name="secrets_group",
queryset=SecretsGroup.objects.all(),
to_field_name="slug",
queryset=SecretsGroup.objects.all(),

@@ -1039,6 +1041,11 @@ class DeviceFilterSet(
field_name="virtual_chassis",
label="Is a virtual chassis member",
)
device_redundancy_group = NaturalKeyOrPKMultipleChoiceFilter(
field_name="device_redundancy_group",
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, field_name is likely redundant.

Suggested change
field_name="device_redundancy_group",

failover_strategy = CSVChoiceField(
choices=DeviceRedundancyGroupFailoverStrategyChoices, required=False, help_text="Failover Strategy"
)

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

nautobot/dcim/models/devices.py Outdated Show resolved Hide resolved
Documentation for DeviceRedundancyGroup model
@bryanculver bryanculver merged commit 7a61b1a into next Nov 4, 2022
@bryanculver bryanculver mentioned this pull request Nov 7, 2022
1 task
@bryanculver bryanculver deleted the bsc-1892-redundnacy-groups branch November 15, 2022 18:14
@bryanculver bryanculver mentioned this pull request Jun 14, 2023
17 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Device Cluster/HA Modeling
7 participants