Skip to content

Fix silent and crashing bugs in the netsnmp and netntp states#69794

Open
ggiesen wants to merge 2 commits into
saltstack:3006.xfrom
ggiesen:fix-netsnmp-netntp-states
Open

Fix silent and crashing bugs in the netsnmp and netntp states#69794
ggiesen wants to merge 2 commits into
saltstack:3006.xfrom
ggiesen:fix-netsnmp-netntp-states

Conversation

@ggiesen

@ggiesen ggiesen commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes five bugs in the netsnmp and netntp NAPALM network-config states -- two
of them silent (report success while doing the wrong thing), which is why they
went unnoticed.

netsnmp:

  1. _expand_config did defaults.update(config), crashing with
    AttributeError: 'NoneType' object has no attribute 'update' whenever the
    state declares no defaults (the common case) -- the same pattern as [BUG] Unable to add users on junos via napalm #62170's
    netusers bug.
  2. _clear_community_details had community_details.get["mode"] = ..., a typo
    that subscripts the bound .get method and raises TypeError for every
    community given in the (documented, recommended) dict form.
  3. _create_diff's third branch was elif not fun(curr), which is unreachable
    once the first two branches are past -- so a valid->valid change (e.g.
    location "OldTown" -> "NewTown") matched no branch, was dropped from the
    diff, and the state reported success without pushing the change.

netntp:
4. _check resolved domain names into ip_only_peers and then did
peers = ip_only_peers, rebinding a local -- the resolved addresses were
discarded, so domain-name peers never converged (the device reports IPs, the
desired list kept the names, so the diff never emptied). It now rewrites the
caller's list in place, and keeps an unresolvable entry (no DNS resolver
available) instead of silently dropping it.
5. managed masked a device-retrieval failure as success: a failed
ntp.peers/ntp.servers produced no changes, which fell through to the
"no changes -> Device configured properly" branch and returned result=True.
The retrieval-failure path now flags successfully_changed=False, and
managed returns result=False before that branch.

Validation on real hardware

Validated against a live Juniper EX3400 (Junos 23.4R2) over NETCONF, test=True
(which exercises the full diff path without touching the device templates):

  • netsnmp.managed with community: {splab: {mode: read-only}} -> before: the
    AttributeError crash; after: normalizes the mode to ro and returns a
    correct diff.
  • netntp.managed servers=[...] -> a correct add/remove diff against the
    device's real NTP servers.

What issues does this PR fix or reference?

Found by an audit of the NAPALM state modules; part of the same family as
#62170. (netsnmp's end-to-end apply additionally needs napalm's py2 SNMP
templates fixed upstream -- napalm-automation/napalm#2326 -- but the state logic
fixed here is independent and was the crashing/silent layer.)

Merge requirements satisfied?

  • Docs
  • Changelog
  • Tests written/updated

New tests/pytests/unit/states/test_netsnmp.py and test_netntp.py (neither
module had any tests): the None-defaults crash, community-mode normalization,
the valid->valid "updated" diff, _check's in-place resolution, and the
masked-retrieval-failure -- each fails against the current code, plus the
behaviour-preserving inverse cases.

Commits signed with GPG?

No

netsnmp:
- _expand_config did ``defaults.update(config)`` and crashed with
  ``AttributeError: 'NoneType' object has no attribute 'update'`` whenever the
  state declared no ``defaults`` (the common case; the netusers twin of saltstack#62170).
- _clear_community_details had ``community_details.get["mode"] = ...``, a typo
  subscripting the bound ``.get`` method that raised ``TypeError`` for every
  community given in the documented dict form; ``mode`` is also defaulted now.
- _create_diff's third branch was ``elif not fun(curr)``, unreachable once the
  first two are past, so a valid->valid change (e.g. location "A" -> "B") matched
  no branch, was dropped from the diff, and the state reported success without
  pushing the change.

netntp:
- _check resolved names into ``ip_only_peers`` then did ``peers = ip_only_peers``
  (rebinding a local), so the resolved addresses were discarded and name-based
  peers never converged; it now rewrites the list in place, keeps an
  unresolvable entry (no resolver) instead of dropping it, and tolerates any
  ``dns.exception.DNSException`` rather than only ``NoAnswer``.
- managed masked a device-retrieve failure (which produces no changes) as
  "Device configured properly."; it now reports result=False for a failure with
  nothing staged, while still letting the commit path handle a partial change.

Both state modules had no unit tests; add tests/pytests/unit/states/test_netsnmp.py
and test_netntp.py covering each fix and the behaviour-preserving cases.
@dwoz dwoz added the test:full Run the full test suite label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants