Skip to content

New Golden Config Setting Enabled Per GC Setting (replaces 847 913 936) - #984

Open
jeffkala wants to merge 14 commits into
developfrom
gcs-aug25
Open

New Golden Config Setting Enabled Per GC Setting (replaces 847 913 936)#984
jeffkala wants to merge 14 commits into
developfrom
gcs-aug25

Conversation

@jeffkala

@jeffkala jeffkala commented Sep 2, 2025

Copy link
Copy Markdown
Contributor

Test Plan Executed

Backups

  • Both GCS are using the same git repos.
  1. 1 GC Setting Only Backup enabled.
    • 2 devices in scope.
  2. 2 GC settings 1 device in scope per setting
    • 2 devices backup successfully
    • disabled backup on 1 GCS but run GC backup on all devices
  3. 2 GC settings backup disabled for both
  4. 2 GC settings 1 devices in both Dynamic groups make sure higher weight GC setting for that device takes precedence.

Intended

  • Both GCS are using the same git repos.
  1. Make sure GC settings in the UI can't be saved if Intended is enabled and Sot agg query, Jinja repository and Jinja Template Path are not set.
  2. 1 GC Setting Only Intended enabled.
    • 2 devices in scope.
  3. 2 GC settings 1 device in scope per setting
    • 2 devices intended successfully
    • disabled intended on 1 GCS but run GC intended on all devices
  4. 2 GC settings intended disabled for both
  5. 2 GC settings 1 devices in both Dynamic groups make sure higher weight GC setting for that device takes precedence.

Compliance

  • Both GCS are using the same git repos.
  1. 1 GC Setting Only compliance enabled.
    • 2 devices in scope.
  2. 2 GC settings 1 device in scope per setting
    • 2 devices compliance successfully
    • disabled compliance on 1 GCS but run GC compliance on all devices
  3. 2 GC settings backup disabled for both
  4. 2 GC settings 1 devices in both Dynamic groups make sure higher weight GC setting for that device takes precedence.

All in One (Single)

All in One (Multiple)

Questions to Answer (ADRs)

  • If a device is in scope for two GC Settings and a feature is disabled on teh hightest weighted setting should it fallback to the other setting and still run? Or only use the highest weighted settings.
  • For the all in one jobs, what should happen if any of the jobs are not enabled? Run the ones that are? Don't run any of them and warn?

Extra Details

What do we need for each job related to repositories?

  • The best GoldenConfigSettings across all inscope devices. should be get_inscope_settings_from_device_qs from helpers.
  • Helps with quickly determining if a repo is needed for a job.
  • get_repo_types_for_job accompanied by inscope settings gets actual repos.
  • Device to settings map. This is a dict of device pk to the GoldenConfigSettings object.

From a device qs, we need to get the following:

  • The best GoldenConfigSettings across all inscope devices. should be get_inscope_settings_from_device_qs from helpers.
  • Device to settings map. This is a dict of device pk to the GoldenConfigSettings object.
  • Repos per inscope settings, if that specific setting is enabled based on the repos needed for the job (get_repo_types_for_job).

Other considerations for (only for compliance job, and all jobs)

  • If a setting is disabled, but a repo/repo path is set that means we need to do:
    • Still run ensure_git_repository on the repo.
    • Don't add repo to the list of repos to commit/push.

Backup job:

  • Repo types needed: backup_repository
  • Always sync repos if a device is in scope for the job that matches a GC setting.
  • If setting is enabled, and repo is set, and if a device is in scope for the job, then we need to commit/push only backup_repository.
  • We should log if a device is in the queryset, but the setting is disabled.

Intended job:

  • Repo types needed: jinja_repository, intended_repository
  • Always sync repos if a device is in scope for the job that matches a GC setting.
  • If setting is enabled, and repo is set, and if a device is in scope for the job, then we need to commit/push only intended_repository.
  • We should log if a device is in the queryset, but the setting is disabled.
    • Also if templates git repo is not set, but intended is set, and setting is enabled.

Compliance job:

  • Repo types needed: intended_repository, backup_repository
  • All job:
  • Repo types needed: backup_repository, jinja_repository, intended_repository

@jeffkala jeffkala changed the title New Golden Config Setting Enabled Per GC Setting (replaces #913 936) New Golden Config Setting Enabled Per GC Setting (replaces 847 913 936) Sep 2, 2025
@jeffkala

jeffkala commented Sep 2, 2025

Copy link
Copy Markdown
Contributor Author
Screenshot 2025-08-30 at 11 51 37 AM Screenshot 2025-08-30 at 11 34 07 AM

@jeffkala

jeffkala commented Sep 26, 2025

Copy link
Copy Markdown
Contributor Author

The ADRS have been discussed

  • If a device is in scope for two GC Settings and a feature is disabled on the highest weighted setting should it fallback to the other setting and still run? Or only use the highest weighted settings.
    • A: highest weight only, no fallback.
  • For the all in one jobs, what should happen if any of the jobs are not enabled? Run the ones that are? Don't run any of them and warn?
    • A: Run whichever are enabled.

jeffkala and others added 4 commits September 26, 2025 21:25
Each device routes through its highest-weighted GoldenConfigSetting and
honors that Setting's per-feature enable_* flag. All in-scope Settings'
repos still sync (so read-only consumers stay current); push is gated by
the enable_* flag on the winning Setting. Skipped devices log E3038
naming the winning Setting and weight; jobs with no eligible devices log
E3039 per feature. Plugin-level ENABLE_* settings stay as
backwards-compat defaults for the new model fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the new per-Setting enable_* flags to GoldenConfigSettingTable
(colored BooleanColumn rendering plus Meta.default_columns), the
filter-form sidebar, and the bulk-edit form. Centralizes E3039 emission
in _log_no_eligible_devices with three branches (no devices / single
device suppressed / many devices summarized) so single-device
AllGoldenConfig runs no longer log redundant "no devices found"
summaries. Suppresses the per-play "Device(s) with settings
enabled/disabled" debug logs when the job's queryset has only one
device. Picks up two pylint cleanups (E0712 on except tuple, W0108
redundant lambdas in test patches) plus a startswith fix on the
_e30xx test helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  nautobot_golden_config
  datasources.py
  forms.py
  jobs.py 63, 73-74, 90-99, 321-327, 329-332, 349, 353-356, 377-380, 401-404
  models.py
  navigation.py
  tables.py
  template_content.py 93-94
  views.py 209, 562-563, 581-582, 606-627, 647-648, 686-687
  nautobot_golden_config/api
  views.py 74-77
  nautobot_golden_config/nornir_plays
  config_backup.py
  config_deployment.py 38-44
  nautobot_golden_config/utilities
  config_postprocessing.py 135-136
  constant.py
  helper.py
Project Total  

This report was generated by python-coverage-comment-action

@jeffkala

Copy link
Copy Markdown
Contributor Author

@nickydavey @itdependsnetworks @nkallergis I believe this is finally ready. Better late then never, but manually tested all the test cases again from the PR description.

The two ADRS in the thread were implemented from #984 (comment).

The app as a whole settings from NautobotAppConfig are used as the defaults for all the new GC settings that get created, this is for backwards compatibility.

Comment thread docs/user/app_use_cases.md Outdated
Comment thread docs/user/app_use_cases.md Outdated
Comment thread nautobot_golden_config/nornir_plays/config_backup.py Outdated
Co-authored-by: Ken Celenza <ken@celenza.org>
@jeffkala

jeffkala commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Some new screenshots now that this is 3.x only.

Screenshot 2026-05-18 at 8 25 28 PM Screenshot 2026-05-18 at 8 25 03 PM Screenshot 2026-05-18 at 8 24 04 PM Screenshot 2026-05-18 at 8 23 43 PM Screenshot 2026-05-18 at 8 22 47 PM

@jeffkala

jeffkala commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Looks like Config Plans doesn't work as intended. Even when the feature is disabled, config plans are created. Will look at this.

Also deploys are the same, settings don't seem to take account.

@jeffkala

Copy link
Copy Markdown
Contributor Author

@itdependsnetworks thoughts on these two enable_sotagg and enable_postprocessing? Should these remain app level? Or settings level like the rest are moving towards?

Comment thread nautobot_golden_config/nornir_plays/config_backup.py Outdated
Comment thread nautobot_golden_config/utilities/config_postprocessing.py Outdated
)
gcs = {gc.id: gc for gc in models.GoldenConfigSetting.objects.all()}
return {device.id: gcs[device.gc_settings] for device in annotated_queryset}
return {device.id: gcs[device.gc_settings] for device in annotated_queryset if device.gc_settings}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why is this needed now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I had seen a instance where if I created a device and immediately ran a GC job it seemed like a race condition existed where members were updated in the middle of a run and gc_settings didn't exist yet for that device. This was a catch for that, but I think it was largely a issue with my local environment as I've never seen it again. Will get it removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Side note as well @itdependsnetworks curious your thoughts on:
A config plan can outlive its device leaving any GC setting's scope, so device.gc_settings legitimately can be None from that callsite. What do we want to happen if a config plan is built while a device is in-scope for a setting and if that device gets removed or DG changes and now they shouldn't be able to create a config plan

@itdependsnetworks

Copy link
Copy Markdown
Contributor

@jeffkala what assurance do you have that we are not introducing an N+1 issue here with settings here?

Comment thread development/development.env Outdated
Comment thread docs/user/app_use_cases.md Outdated
Comment thread docs/user/app_use_cases.md
Comment thread nautobot_golden_config/nornir_plays/config_intended.py Outdated
Comment thread nautobot_golden_config/jobs.py
Comment thread nautobot_golden_config/template_content.py Outdated
@jeffkala

Copy link
Copy Markdown
Contributor Author

@jeffkala what assurance do you have that we are not introducing an N+1 issue here with settings here?

Will do some research on this.

@jeffkala

Copy link
Copy Markdown
Contributor Author

With most recent commits the following UI test plan was done:

  • backup / intended / compliance — mixed (veos via gcs2 runs; demo-cisco-xe + vjunos E3038 via Default GCS) and
    all-off (all three devices E3038 + E3039)
  • all_multi — backup/intended run for veos, others E3038; compliance play emits E3039
  • all_single_run (veos) — all features run, no skips
  • all_single_skip (demo-cisco-xe) — E3038 per feature, E3039 suppressed (single device)
  • plans_create — config plan created for demo-cisco-xe (missing hostname/ntp); gated devices E3038

@jeffkala

Copy link
Copy Markdown
Contributor Author

@jeffkala what assurance do you have that we are not introducing an N+1 issue here with settings here?

Will do some research on this.

@itdependsnetworks from my research of what N+1 is I think we're safe here. I don't see multiple queries happening that would indicate this is a concern we should have.

@@ -0,0 +1,47 @@
# Generated by Django 5.2.14 on 2026-05-30 03:35

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Move to true removal of nautobot_config and just pull from there during data migration.

keep this migrations
new second migration to update from nautobot_config values

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

look for deprecation warning from SSoT to follow existing pattern.

.order_by("-weight")
# Match Meta.ordering (`-weight`, `name`) so a weight tie resolves deterministically
# to the lower-sorted name rather than an arbitrary DB row.
.order_by("-weight", "name")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe implement weight being unique? To avoid this "guess" of a winner.

verbose_name="Backup Path in Jinja Template Form",
help_text="The Jinja path representation of where the backup file will be found. The variable `obj` is available as the device instance object of a given device, as is the case for all Jinja templates. e.g. `{{obj.location.name|slugify}}/{{obj.name}}.cfg`",
)
enable_backup = models.BooleanField(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

change all these defaults from the plugin constant to just 'true"

Comment on lines -657 to -658
if ENABLE_SOTAGG and not self.sot_agg_query:
raise ValidationError("A GraphQL query must be defined when `ENABLE_SOTAGG` is True")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

add this back.

Also important going through all these iterations now that we don't have global + overrides in a GC setting

Comment on lines +146 to +149
# if GoldenConfigSetting.objects.filter(enable_compliance=True).exists():
extensions.append(ConfigComplianceDeviceCheck)
extensions.append(ConfigComplianceLocationCheck)
extensions.append(ConfigComplianceTenantCheck)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

don't do append add to actual extensions list

"golden_config": golden_config,
"template_type": "device-configs",
"config_features": CONFIG_FEATURES,
"config_features": {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

validate its using same structure as before and no html template changes are needed.

# Gate generation on each device's winning GoldenConfigSetting.enable_plan flag —
# devices whose winning Setting has enable_plan=False are dropped here (E3038 names
# the Setting and weight; E3039 fires if nothing remains).
self._device_qs = filter_devices_by_feature_enabled(self.logger, self._device_qs, "plan")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Provide better docs on what this is doing and where its doing it.

@jeffkala

Copy link
Copy Markdown
Contributor Author

double check filters are implemented for GCS list view.

@gsnider2195 gsnider2195 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me pending feedback added in today's review

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.

3 participants