Skip to content

Create per-device ZTP firmware symlink from sonic_parameters.version#2427

Open
berendt wants to merge 1 commit into
mainfrom
add-sonic-ztp-firmware-version
Open

Create per-device ZTP firmware symlink from sonic_parameters.version#2427
berendt wants to merge 1 commit into
mainfrom
add-sonic-ztp-firmware-version

Conversation

@berendt

@berendt berendt commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

Adds support for per-device SONiC firmware selection to the sync sonic command, matching the ZTP dynamic-url switch in osism/ansible-collection-services#2131.

That PR changed the ZTP firmware install to a dynamic-url built from <prefix><identifier><suffix> (identifier serial-number), so each switch fetches sonic-broadcom-enterprise-base_<serial>.bin during ZTP. For that name to resolve to the right image, a per-serial link to the version-specific image must exist. The serial→version mapping lives in NetBox (sonic_parameters.version), and sync sonic already writes the per-serial artefacts into the export directory.

Changes

  • Settings (settings.py): new SONIC_FIRMWARE_DIR / _PREFIX / _SUFFIX / _IDENTIFIER, mirroring the ansible httpd role defaults. SONIC_FIRMWARE_DIR defaults to the same httpd-served directory as the config exports (/etc/sonic/export).
  • export_firmware_link() (exporter.py): creates <prefix><serial><suffix> as a relative symlink to <prefix><version><suffix>. Reconciled on every run (missing/stale/wrongly-pointed links are repaired), skips devices without a configured version, and raises on real failures so they surface as a non-zero task rc. The target image is provided out of band (e.g. downloaded by metalbox), so a dangling link until it arrives is expected.
  • sync_sonic (sync.py): reads sonic_parameters.version per device (like hwsku / config_version) and passes it to the exporter. config_db.json and the CONFIG DB VERSION are untouched.

For the example device (serial 732656X2419308, version: 4.4.2) this produces:

/etc/sonic/export/sonic-broadcom-enterprise-base_732656X2419308.bin
    -> sonic-broadcom-enterprise-base_4.4.2.bin

Tests

  • 13 tests in test_exporter.py (identifier/target naming, serial fallback, reconciliation, self-reference guard, error paths).
  • 3 tests in test_sync.py (version → firmware link, None when absent, rc=1 on failure); patch_sync_deps and make_device extended accordingly.

🤖 Generated with Claude Code

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The extraction of sonic_parameters.version in sync_sonic is quite verbose and duplicates the nested custom_fields access logic used for config_version; consider using a small helper (or .get chains) to centralise and simplify reading sonic_parameters fields.
  • In export_firmware_link, any SONIC_FIRMWARE_IDENTIFIER value other than serial-number silently falls back to hostname; it may be worth explicitly validating the setting (e.g. warning on unrecognised values) to make misconfiguration easier to spot.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The extraction of `sonic_parameters.version` in `sync_sonic` is quite verbose and duplicates the nested `custom_fields` access logic used for `config_version`; consider using a small helper (or `.get` chains) to centralise and simplify reading `sonic_parameters` fields.
- In `export_firmware_link`, any `SONIC_FIRMWARE_IDENTIFIER` value other than `serial-number` silently falls back to hostname; it may be worth explicitly validating the setting (e.g. warning on unrecognised values) to make misconfiguration easier to spot.

## Individual Comments

### Comment 1
<location path="osism/settings.py" line_range="80-83" />
<code_context>
+# sonic_parameters.version custom field. The defaults mirror the ansible
+# httpd role and place the links in the same httpd-served directory as the
+# config exports (SONIC_EXPORT_DIR).
+SONIC_FIRMWARE_DIR = os.getenv("SONIC_FIRMWARE_DIR", "/etc/sonic/export")
+SONIC_FIRMWARE_PREFIX = os.getenv(
+    "SONIC_FIRMWARE_PREFIX", "sonic-broadcom-enterprise-base_"
</code_context>
<issue_to_address>
**suggestion:** Avoid duplicating the export directory default by reusing the existing SONIC_EXPORT_DIR setting.

This adds a second path defaulting to `/etc/sonic/export`, while `SONIC_EXPORT_DIR` already represents the canonical export directory. To avoid config drift if only one env var is overridden, derive the firmware directory from the existing setting when the firmware-specific env var is unset:

```python
SONIC_FIRMWARE_DIR = os.getenv("SONIC_FIRMWARE_DIR", SONIC_EXPORT_DIR)
```

This keeps a single source of truth for the base export path unless a separate firmware directory is explicitly configured.

```suggestion
# sonic_parameters.version custom field. The defaults mirror the ansible
# httpd role and place the links in the same httpd-served directory as the
# config exports (SONIC_EXPORT_DIR).
SONIC_FIRMWARE_DIR = os.getenv("SONIC_FIRMWARE_DIR", SONIC_EXPORT_DIR)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread osism/settings.py Outdated
@berendt berendt force-pushed the add-sonic-ztp-firmware-version branch from 5d289f1 to d3af13e Compare July 2, 2026 12:38
@berendt berendt moved this from New to In progress in Human Board Jul 2, 2026
The ZTP firmware install now uses a dynamic-url built from
<prefix><identifier><suffix> (osism/ansible-collection-services#2131),
so each switch fetches sonic-broadcom-enterprise-base_<serial>.bin during
ZTP. Have sync_sonic create that per-device name as a relative symlink to
the version-specific image sonic-broadcom-enterprise-base_<version>.bin,
driven by the device's sonic_parameters.version custom field.

- Add SONIC_FIRMWARE_DIR/PREFIX/SUFFIX/IDENTIFIER settings mirroring the
  ansible httpd role defaults; SONIC_FIRMWARE_DIR defaults to the same
  httpd-served directory as the config exports.
- Add export_firmware_link(), reconciling the symlink on every run and
  skipping devices without a configured version. The target image is
  provided out of band, so a dangling link until it arrives is expected.
- Read sonic_parameters.version in sync_sonic and pass it to the exporter
  per device; config_db.json and the CONFIG DB VERSION are untouched.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
@berendt berendt force-pushed the add-sonic-ztp-firmware-version branch from d3af13e to 3512b96 Compare July 2, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants