nd_interface_ethernet_access module#222
Open
allenrobel wants to merge 21 commits intond_interface_loopbackfrom
Open
nd_interface_ethernet_access module#222allenrobel wants to merge 21 commits intond_interface_loopbackfrom
allenrobel wants to merge 21 commits intond_interface_loopbackfrom
Conversation
2f28392 to
f7efdf8
Compare
2d2895d to
4abc3c5
Compare
f7efdf8 to
9912dd3
Compare
4b2453f to
f5ee201
Compare
cab6652 to
7cb3d5d
Compare
8380710 to
3940b27
Compare
abc59bc to
78a5554
Compare
3940b27 to
7aa132e
Compare
7aa132e to
9620a8f
Compare
This was referenced Apr 22, 2026
74f70ae to
c05c9b4
Compare
313593f to
b810abf
Compare
b810abf to
ca0da57
Compare
Add Ansible module for managing ethernet accessHost interfaces on Nexus Dashboard, with fan-out playbook shape, port-channel membership enforcement, and bulk normalize-based delete for physical interfaces. New files: - Module: nd_interface_ethernet_access with expand_config() fan-out - Model: EthernetAccessInterfaceModel with 19 accessHost policy fields - Model: InterfaceDefaultConfig for int_trunk_host normalize payload - Orchestrator: EthernetBaseOrchestrator (shared ethernet CRUD logic) - Orchestrator: EthernetAccessInterfaceOrchestrator (thin subclass) - Endpoints: EpManageInterfacesDelete, EpManageInterfacesNormalize - Integration tests: merged, replaced, overridden, deleted states Physical ethernet interfaces cannot be deleted via interfaceActions/remove (silently does nothing) or DELETE (returns 500). The delete mechanism uses interfaceActions/normalize with the int_trunk_host template defaults, which resets interfaces to policyType: trunkHost, removing them from the accessHost query filter and enabling idempotent state: overridden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ntries The DELETE endpoint does not work for physical ethernet interfaces (returns HTTP 500). Updated docstring to reflect this and point to EpManageInterfacesNormalize. Added missing EpManageInterfacesDelete and EpManageInterfacesNormalize to the module-level endpoint list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed mention of “reset” and “default interface” CLI. While this endpoint can serve as the equivalent to “default interface” this is only so if the payload is constructured to do so.
Update docstring to clarify that EpManageInterfacesNormalize does not reset physical interfaces without an appropriate payload.
…elds Add plugins/module_utils/models/interfaces/enums.py with reusable enums derived from the int_access_host config template. Replace unconstrained Optional[str]/Optional[int] fields with enum types, Field(ge=/le=) for integer ranges, and Field(max_length=) for strings. Add 16 missing policy fields from the template (storm control, fec, debounce, etc.). Replace NDConstantMapping with AccessHostPolicyTypeEnum for policy_type handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Leverage NDBaseOrchestrator bulk infrastructure from PR #223 to reduce API calls for all ethernet interface types. create_bulk groups interfaces by switch and sends one POST per switch. delete_bulk queues all interfaces for deferred normalization and deployment via remove_pending/deploy_pending. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…estrator Reuse shared interface lifecycle methods (deploy, remove, fabric context, switch resolution) from NDBaseInterfaceOrchestrator instead of duplicating them. Add isinstance type narrowing in both interface modules, using explicit raise AssertionError to satisfy ansible-test sanity no-assert check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match base class Optional type for create_bulk_endpoint and delete_bulk_endpoint. Add pyright ignore for the guarded callsites where the @requires_bulk_support decorator ensures non-None at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ModelType is NDBaseModel which lacks interface-specific fields (switch_ip, interface_name, config_data). Concrete subclasses always bind ModelType to a model that provides these fields, so the accesses are safe at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same migration as fabric_context/base_interface/loopback_interface: replace self.sender.request() with self._request() inherited from NDBaseOrchestrator. Six call sites in ethernet_base.py: - _normalize_interfaces (bulk normalize via interfaceActions/normalize) - create / update / create_bulk (per-switch POST/PUT) - query_one (single interface GET) - query_all (was self.sender.query_obj; now uses _request with not_found_ok=True to preserve 404-as-empty semantics) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrate to builtin generics and union syntax in files introduced on this branch: Optional[X] -> X | None, List[X] -> list[X], Dict -> dict, Set -> set, Type -> type. Drop the now-unused imports from typing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cover the two branch-new source files with 105 tests and 100% real
line coverage:
- `tests/unit/module_utils/models/test_ethernet_access_interface.py`
exercises every nested Pydantic model (Policy, NetworkOS, ConfigData,
Interface) with field validation, range and enum constraints,
`normalize_policy_type`, `serialize_policy_type`, round-trip
to_payload / to_config / from_response / from_config, composite
identifier, diff, merge, and argument_spec shape.
- `tests/unit/module_utils/orchestrators/test_ethernet_access_interface.py`
wires a real `RestSend` with the file-based `Sender` from
`tests/unit/module_utils/sender_file.py` and `ResponseHandler` to
verify `model_class`, `supports_bulk_*` inheritance, and
`_managed_policy_types` returning `{"accessHost"}`. A `query_all`
smoke test exercises the inherited base-class logic end-to-end
across two switches, confirming non-accessHost interfaces are
filtered out and `switchIp` is injected on the kept interfaces.
Parametrize collapses all value-table tests (range checks, enum choice
checks, serializer modes, normalizer inputs) into single functions
with descriptive `ids=` labels so pytest output still names each case.
Adds an empty `tests/unit/module_utils/orchestrators/__init__.py` to
satisfy ansible-test's `empty-init` sanity check.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire setup_logging() into the module's main() and emit debug records at expand_config, manage_state begin/end, and on NDStateMachineError. Logging activates only when ND_LOGGING_CONFIG points at a logging.config JSON file; otherwise the calls are no-ops. ansible-test strips the controller's shell environment, so the integration test's main.yaml now wraps its include_tasks in a block that forwards nd_logging_config (set in inventory.networking [nd:vars]) into the module subprocess as ND_LOGGING_CONFIG. Added a header comment documenting the variable.
Replaces the inline `description: str | None` declaration with the shared `AsciiDescription` Annotated type introduced in nd_interface_loopback. Same `max_length=254` constraint via Field(...). Adds parametrized tests for em-dash, smart quotes, emoji, and latin-1 rejection alongside ASCII passthrough. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`FabricContext.validate_for_mutation` now also fetches `/deploymentFreeze` after the fabric summary, so the orchestrator's `query_all` consumes one extra response from the queue. Insert a `deploymentFreeze: false` fixture between the summary and switch list yields to keep the response generator aligned. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The OpenAPI intAccessHostTemplate declares stormControlBroadcastLevel, stormControlMulticastLevel, and stormControlUnicastLevel as number/float, but the model and argument spec had them as str. Switch to float | None with ge=0.0/le=100.0 so the model matches the API contract and the argument spec validates percentage input correctly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per-policy interface modules (one module = one policyType) must not expose policy_type as a user-facing argspec option. The nd_interface_ethernet_access module targets accessHost only, so policy_type was dead surface area. - Remove policy_type from argspec and DOCUMENTATION - Drop normalize_policy_type validator and serialize_policy_type field serializer - Hardcode AccessHostPolicyTypeEnum.ACCESS_HOST as the model default; field still serialized into payloads as policyType - Strip policy_type from integration test inputs - Update unit tests (96/96 pass) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace verbatim-repeated module argument blocks (check / normal / idempotent runs of the same call) with YAML anchors. Removes a class of typo where one of the duplicated blocks could drift from its siblings. Anchors are defined at first use within each section to keep them local to the reader.
Move the top-of-file SETUP block from merged.yaml into a dedicated tasks/setup.yaml and call it from main.yaml before the state-test blocks. Includes the post-cleanup DEBUG re-query probe alongside the cleanup since both are file-level observability rather than test-specific. Intra-test setup that's coupled to specific tests stays inline (e.g. the Ethernet1/48 cleanup after the no-deploy test in merged.yaml). The final CLEANUP at the bottom of deleted.yaml stays inline as well — it's post-test teardown for the last state file, not file-level pre-test prep.
af37a04 to
a63bc2a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
nd_interface_ethernet_accessAnsible module for managing ethernet accessHost interfaces on Nexus Dashboardinterface_nameslist with sharedconfig_data, expanded into flat items before the state machineEthernetBaseOrchestratorbase class for all future ethernet interface modules (trunk, routed, breakout, freeform)description,admin_state,extra_config)interfaceActions/normalizewith theint_trunk_hosttemplate defaultsInterfaceDefaultConfigmodel providing the fullint_trunk_hostnormalize payloadEpManageInterfacesDeleteandEpManageInterfacesNormalizeendpoint classessetup_logging()inmain(). Module-levelnd.nd_interface_ethernet_accesslogger emits debug records atexpand_config,manage_statebegin/end, and onNDStateMachineError(vialog.exceptionso tracebacks reach the log file regardless ofoutput_level). Logging activates only whenND_LOGGING_CONFIGpoints at alogging.config.dictConfigJSON file; otherwise the calls are no-ops.tasks/main.yamlwraps itsinclude_tasksin a block that forwardsnd_logging_config(set ininventory.networking [nd:vars]) into the module subprocess asND_LOGGING_CONFIG, sinceansible-teststrips the controller's shell environment.Delete mechanism for physical ethernet interfaces
/api/v1/manage/fabrics/{fabricName}/interfaceActions/normalizeis used with the fullint_trunk_hostconfig template defaults (mode: "trunk",policyType: "trunkHost", etc). This resets the interface to fabric defaults and changes itspolicyTypetotrunkHost, which naturally removes it from the accessHostquery_all()filter, enabling idempotentstate: overridden.Merge order
Merge & rebase note
This PR is part of a stack with a follow-on branch (
nd_interface_ethernet_trunk). Because the team uses squash merges, after this PR is merged, @allenrobel will rebase the remaining stack branch ontodevelopbefore the next PR can be opened/merged. Please ping him once this is squashed.Unit tests
Unit tests now cover the two branch-new source files with 105 tests and 100% real line coverage:
tests/unit/module_utils/models/test_ethernet_access_interface.pyexercises every nested Pydantic model (Policy,NetworkOS,ConfigData,Interface) with field validation, range and enum constraints,normalize_policy_type,serialize_policy_type, round-tripto_payload/to_config/from_response/from_config, composite identifier, diff, merge, and argument_spec shape.tests/unit/module_utils/orchestrators/test_ethernet_access_interface.pywires a realRestSendwith the file-basedSenderfromtests/unit/module_utils/sender_file.pyandResponseHandlerto verifymodel_class,supports_bulk_*inheritance, and_managed_policy_typesreturning{"accessHost"}. Aquery_allsmoke test exercises the inherited base-class logic end-to-end across two switches, confirming non-accessHost interfaces are filtered out andswitchIpis injected on the kept interfaces.Parametrize collapses all value-table tests (range checks, enum choice checks, serializer modes, normalizer inputs) into single functions with descriptive
ids=labels so pytest output still names each case.Test plan
state: merged(create, fan-out, idempotency, update, deploy:false)state: replaced(full replace, fan-out, idempotency)state: overridden(reduce, idempotency, swap, fan-out, non-accessHost filtering)state: deleted(single, fan-out, idempotency, non-existent)python -m pytest tests/unit/module_utils/models/test_ethernet_access_interface.py tests/unit/module_utils/orchestrators/test_ethernet_access_interface.py(105 tests, 100% line coverage of the two new files)nd_logging_config=/absolute/path/to/logging_config.jsonto[nd:vars]intests/integration/inventory.networking. An example config conforming tologging.config.dictConfigis documented inplugins/module_utils/common/log.py.🤖 Generated with Claude Code