From 77df7e847804486e12708ec80176ba068adc1207 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Fri, 27 Mar 2026 12:12:19 -0400 Subject: [PATCH 01/27] Fabric modules for ibgp,ebgp,external fabrics --- .../endpoints/v1/manage/manage_fabrics.py | 525 +++++++ .../models/manage_fabric/enums.py | 251 +++ .../manage_fabric/manage_fabric_ebgp.py | 838 ++++++++++ .../manage_fabric/manage_fabric_external.py | 833 ++++++++++ .../manage_fabric/manage_fabric_ibgp.py | 1317 ++++++++++++++++ .../orchestrators/manage_fabric_ebgp.py | 46 + .../orchestrators/manage_fabric_external.py | 46 + .../orchestrators/manage_fabric_ibgp.py | 47 + plugins/modules/nd_manage_fabric_ebgp.py | 1179 ++++++++++++++ plugins/modules/nd_manage_fabric_external.py | 524 +++++++ plugins/modules/nd_manage_fabric_ibgp.py | 1393 +++++++++++++++++ .../nd_manage_fabric/tasks/fabric_ebgp.yaml | 1209 ++++++++++++++ .../tasks/fabric_external.yaml | 700 +++++++++ .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 1172 ++++++++++++++ .../targets/nd_manage_fabric/tasks/main.yaml | 9 + .../targets/nd_manage_fabric/vars/main.yaml | 209 +++ 16 files changed, 10298 insertions(+) create mode 100644 plugins/module_utils/endpoints/v1/manage/manage_fabrics.py create mode 100644 plugins/module_utils/models/manage_fabric/enums.py create mode 100644 plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py create mode 100644 plugins/module_utils/models/manage_fabric/manage_fabric_external.py create mode 100644 plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py create mode 100644 plugins/module_utils/orchestrators/manage_fabric_ebgp.py create mode 100644 plugins/module_utils/orchestrators/manage_fabric_external.py create mode 100644 plugins/module_utils/orchestrators/manage_fabric_ibgp.py create mode 100644 plugins/modules/nd_manage_fabric_ebgp.py create mode 100644 plugins/modules/nd_manage_fabric_external.py create mode 100644 plugins/modules/nd_manage_fabric_ibgp.py create mode 100644 tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml create mode 100644 tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml create mode 100644 tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml create mode 100644 tests/integration/targets/nd_manage_fabric/tasks/main.yaml create mode 100644 tests/integration/targets/nd_manage_fabric/vars/main.yaml diff --git a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py new file mode 100644 index 00000000..5cb08213 --- /dev/null +++ b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py @@ -0,0 +1,525 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +ND Manage Fabrics endpoint models. + +This module contains endpoint definitions for fabric-related operations +in the ND Manage API. + +## Endpoints + +- `EpApiV1ManageFabricsGet` - Get a specific fabric by name + (GET /api/v1/manage/fabrics/{fabric_name}) +- `EpApiV1ManageFabricsListGet` - List all fabrics with optional filtering + (GET /api/v1/manage/fabrics) +- `EpApiV1ManageFabricsPost` - Create a new fabric + (POST /api/v1/manage/fabrics) +- `EpApiV1ManageFabricsPut` - Update a specific fabric + (PUT /api/v1/manage/fabrics/{fabric_name}) +- `EpApiV1ManageFabricsDelete` - Delete a specific fabric + (DELETE /api/v1/manage/fabrics/{fabric_name}) +- `EpApiV1ManageFabricsSummaryGet` - Get summary for a specific fabric + (GET /api/v1/manage/fabrics/{fabric_name}/summary) +""" + +from __future__ import absolute_import, annotations, division, print_function + +# from plugins.module_utils.endpoints.base import NDBaseEndpoint + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=inFinal, valid-name + +from typing import ClassVar, Literal, Optional, Final + +from ansible_collections.cisco.nd.plugins.module_utils.enums import HttpVerbEnum +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.base_path import BasePath +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.mixins import FabricNameMixin +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.query_params import EndpointQueryParams +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import BaseModel, ConfigDict, Field +from ansible_collections.cisco.nd.plugins.module_utils.types import IdentifierKey + + +class FabricsEndpointParams(EndpointQueryParams): + """ + # Summary + + Endpoint-specific query parameters for the fabrics endpoint. + + ## Parameters + + - cluster_name: Name of the target Nexus Dashboard cluster to execute this API, + in a multi-cluster deployment (optional) + + ## Usage + + ```python + params = FabricsEndpointParams(cluster_name="cluster1") + query_string = params.to_query_string() + # Returns: "clusterName=cluster1" + ``` + """ + + cluster_name: Optional[str] = Field( + default=None, + min_length=1, + description="Name of the target Nexus Dashboard cluster to execute this API, in a multi-cluster deployment", + ) + + +class _EpManageFabricsBase(FabricNameMixin, NDEndpointBaseModel): + """ + Base class for ND Manage Fabrics endpoints. + + Provides common functionality for all HTTP methods on the + /api/v1/manage/fabrics endpoint. + + Subclasses may override: + - ``_require_fabric_name``: set to ``False`` for collection-level endpoints + (list, create) that do not include a fabric name in the path. + - ``_path_suffix``: set to a non-empty string to append an extra segment + after the fabric name (e.g. ``"summary"``). Only used when + ``_require_fabric_name`` is ``True``. + """ + + _require_fabric_name: ClassVar[bool] = True + _path_suffix: ClassVar[Optional[str]] = None + + endpoint_params: EndpointQueryParams = Field( + default_factory=EndpointQueryParams, description="Endpoint-specific query parameters" + ) + + def set_identifiers(self, identifier: IdentifierKey = None): + self.fabric_name = identifier + + @property + def path(self) -> str: + """ + # Summary + + Build the endpoint path with optional fabric name, path suffix, and + query string. + + ## Returns + + - Complete endpoint path string + + ## Raises + + - `ValueError` if `fabric_name` is required but not set + """ + if self._require_fabric_name and self.fabric_name is None: + raise ValueError( + f"{type(self).__name__}.path: fabric_name must be set before accessing path." + ) + segments = ["fabrics"] + if self.fabric_name is not None: + segments.append(self.fabric_name) + if self._path_suffix: + segments.append(self._path_suffix) + base_path = BasePath.path(*segments) + query_string = self.endpoint_params.to_query_string() + if query_string: + return f"{base_path}?{query_string}" + return base_path + +class EpManageFabricsGet(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics GET Endpoint + + ## Description + + Endpoint to retrieve details for a specific named fabric from the ND Manage service. + The fabric name is a required path parameter. Optionally filter by cluster name + using the clusterName query parameter in multi-cluster deployments. + + ## Path + + - /api/v1/manage/fabrics/{fabric_name} + - /api/v1/manage/fabrics/{fabric_name}?clusterName=cluster1 + + ## Verb + + - GET + + ## Raises + + - `ValueError` if `fabric_name` is not set when accessing `path` + + ## Usage + + ```python + # Get details for a specific fabric + request = EpApiV1ManageFabricsGet() + request.fabric_name = "my-fabric" + path = request.path + verb = request.verb + # Path will be: /api/v1/manage/fabrics/my-fabric + + # Get fabric details targeting a specific cluster in a multi-cluster deployment + request = EpApiV1ManageFabricsGet() + request.fabric_name = "my-fabric" + request.endpoint_params.cluster_name = "cluster1" + path = request.path + verb = request.verb + # Path will be: /api/v1/manage/fabrics/my-fabric?clusterName=cluster1 + ``` + """ + + class_name: Literal["EpApiV1ManageFabricsGet"] = Field( + default="EpApiV1ManageFabricsGet", description="Class name for backward compatibility" + ) + + endpoint_params: FabricsEndpointParams = Field( + default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.GET + + +class FabricsListEndpointParams(EndpointQueryParams): + """ + # Summary + + Query parameters for the ``GET /api/v1/manage/fabrics`` list endpoint. + + ## Parameters + + - cluster_name: Name of the target Nexus Dashboard cluster (multi-cluster deployments) + - category: Filter by fabric category (``"fabric"`` or ``"fabricGroup"``) + - filter: Lucene-format filter string + - max: Maximum number of records to return + - offset: Number of records to skip for pagination + - sort: Sort field with optional ``:desc`` suffix + + ## Usage + + ```python + params = FabricsListEndpointParams(category="fabric", max=10, offset=0) + query_string = params.to_query_string() + # Returns: "category=fabric&max=10&offset=0" + ``` + """ + + cluster_name: Optional[str] = Field( + default=None, + min_length=1, + description="Name of the target Nexus Dashboard cluster to execute this API, in a multi-cluster deployment", + ) + + category: Optional[str] = Field( + default=None, + description="Filter by category of fabric (fabric or fabricGroup)", + ) + + filter: Optional[str] = Field( + default=None, + description="Lucene format filter - Filter the response based on this filter field", + ) + + max: Optional[int] = Field( + default=None, + ge=1, + description="Number of records to return", + ) + + offset: Optional[int] = Field( + default=None, + ge=0, + description="Number of records to skip for pagination", + ) + + sort: Optional[str] = Field( + default=None, + description="Sort the records by the declared fields in either ascending (default) or descending (:desc) order", + ) + + +class EpManageFabricsListGet(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics List GET Endpoint + + ## Description + + Endpoint to list all fabrics from the ND Manage service. + Supports optional query parameters for filtering, pagination, and sorting. + + ## Path + + - ``/api/v1/manage/fabrics`` + - ``/api/v1/manage/fabrics?category=fabric&max=10`` + + ## Verb + + - GET + + ## Raises + + - None + + ## Usage + + ```python + # List all fabrics + ep = EpApiV1ManageFabricsListGet() + path = ep.path + verb = ep.verb + # Path: /api/v1/manage/fabrics + + # List fabrics with filtering and pagination + ep = EpApiV1ManageFabricsListGet() + ep.endpoint_params.category = "fabric" + ep.endpoint_params.max = 10 + path = ep.path + # Path: /api/v1/manage/fabrics?category=fabric&max=10 + ``` + """ + + _require_fabric_name: ClassVar[bool] = False + + class_name: Literal["EpApiV1ManageFabricsListGet"] = Field( + default="EpApiV1ManageFabricsListGet", description="Class name for backward compatibility" + ) + + endpoint_params: FabricsListEndpointParams = Field( + default_factory=FabricsListEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.GET + + +class EpManageFabricsPost(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics POST Endpoint + + ## Description + + Endpoint to create a new fabric via the ND Manage service. + The request body must conform to the ``baseFabric`` schema (discriminated + by ``category``). For standard fabrics the category is ``"fabric"`` and + the body includes ``name`` plus fabric-specific properties such as + ``location``, ``licenseTier``, ``telemetryCollection``, etc. + + ## Path + + - ``/api/v1/manage/fabrics`` + - ``/api/v1/manage/fabrics?clusterName=cluster1`` + + ## Verb + + - POST + + ## Request Body (application/json) + + ``baseFabric`` schema — for a standard fabric use ``category: "fabric"`` + with at minimum: + + - ``name`` (str, required): Name of the fabric + - ``category`` (str, required): ``"fabric"`` + + ## Raises + + - None + + ## Usage + + ```python + ep = EpApiV1ManageFabricsPost() + rest_send.path = ep.path + rest_send.verb = ep.verb + rest_send.payload = { + "name": "my-fabric", + "category": "fabric", + "telemetryCollection": True, + "telemetryCollectionType": "inBand", + } + ``` + """ + + _require_fabric_name: ClassVar[bool] = False + + class_name: Literal["EpApiV1ManageFabricsPost"] = Field( + default="EpApiV1ManageFabricsPost", description="Class name for backward compatibility" + ) + + endpoint_params: FabricsEndpointParams = Field( + default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.POST + + +class EpManageFabricsPut(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics PUT Endpoint + + ## Description + + Endpoint to update an existing fabric via the ND Manage service. + The fabric name is a required path parameter. The request body must + conform to the ``baseFabric`` schema (same shape as POST/create). + + ## Path + + - ``/api/v1/manage/fabrics/{fabric_name}`` + - ``/api/v1/manage/fabrics/{fabric_name}?clusterName=cluster1`` + + ## Verb + + - PUT + + ## Request Body (application/json) + + ``baseFabric`` schema — same as create (POST). + + ## Raises + + - `ValueError` if `fabric_name` is not set when accessing `path` + + ## Usage + + ```python + ep = EpApiV1ManageFabricsPut() + ep.fabric_name = "my-fabric" + rest_send.path = ep.path + rest_send.verb = ep.verb + rest_send.payload = { + "name": "my-fabric", + "category": "fabric", + "telemetryCollection": False, + } + ``` + """ + + class_name: Literal["EpApiV1ManageFabricsPut"] = Field( + default="EpApiV1ManageFabricsPut", description="Class name for backward compatibility" + ) + + endpoint_params: FabricsEndpointParams = Field( + default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.PUT + + +class EpManageFabricsDelete(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics DELETE Endpoint + + ## Description + + Endpoint to delete a specific fabric from the ND Manage service. + The fabric name is a required path parameter. + + ## Path + + - ``/api/v1/manage/fabrics/{fabric_name}`` + - ``/api/v1/manage/fabrics/{fabric_name}?clusterName=cluster1`` + + ## Verb + + - DELETE + + ## Raises + + - `ValueError` if `fabric_name` is not set when accessing `path` + + ## Usage + + ```python + ep = EpApiV1ManageFabricsDelete() + ep.fabric_name = "my-fabric" + rest_send.path = ep.path + rest_send.verb = ep.verb + ``` + """ + + class_name: Literal["EpApiV1ManageFabricsDelete"] = Field( + default="EpApiV1ManageFabricsDelete", description="Class name for backward compatibility" + ) + + endpoint_params: FabricsEndpointParams = Field( + default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.DELETE + + +class EpManageFabricsSummaryGet(_EpManageFabricsBase): + """ + # Summary + + ND Manage Fabrics Summary GET Endpoint + + ## Description + + Endpoint to retrieve summary information for a specific fabric from + the ND Manage service. The fabric name is a required path parameter. + + ## Path + + - ``/api/v1/manage/fabrics/{fabric_name}/summary`` + - ``/api/v1/manage/fabrics/{fabric_name}/summary?clusterName=cluster1`` + + ## Verb + + - GET + + ## Raises + + - `ValueError` if `fabric_name` is not set when accessing `path` + + ## Usage + + ```python + ep = EpApiV1ManageFabricsSummaryGet() + ep.fabric_name = "my-fabric" + path = ep.path + verb = ep.verb + # Path: /api/v1/manage/fabrics/my-fabric/summary + ``` + """ + + class_name: Literal["EpApiV1ManageFabricsSummaryGet"] = Field( + default="EpApiV1ManageFabricsSummaryGet", description="Class name for backward compatibility" + ) + + _path_suffix: ClassVar[Optional[str]] = "summary" + + endpoint_params: FabricsEndpointParams = Field( + default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" + ) + + @property + def verb(self) -> HttpVerbEnum: + """Return the HTTP verb for this endpoint.""" + return HttpVerbEnum.GET diff --git a/plugins/module_utils/models/manage_fabric/enums.py b/plugins/module_utils/models/manage_fabric/enums.py new file mode 100644 index 00000000..5d36756c --- /dev/null +++ b/plugins/module_utils/models/manage_fabric/enums.py @@ -0,0 +1,251 @@ +# -*- coding: utf-8 -*- +# pylint: disable=wrong-import-position +# pylint: disable=missing-module-docstring +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +# Summary + +Enum definitions for Nexus Dashboard Ansible modules. + +## Enums + +- HttpVerbEnum: Enum for HTTP verb values used in endpoints. +- OperationType: Enum for operation types used by Results to determine if changes have occurred. +""" + +from __future__ import absolute_import, annotations, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +from enum import Enum + +class FabricTypeEnum(str, Enum): + """ + # Summary + + Enumeration of supported fabric types for discriminated union. + + ## Values + + - `VXLAN_IBGP` - VXLAN fabric with iBGP overlay + - `VXLAN_EBGP` - VXLAN fabric with eBGP overlay + """ + + VXLAN_IBGP = "vxlanIbgp" + VXLAN_EBGP = "vxlanEbgp" + EXTERNAL_CONNECTIVITY = "externalConnectivity" + + +class AlertSuspendEnum(str, Enum): + """ + # Summary + + Enumeration for alert suspension states. + + ## Values + + - `ENABLED` - Alerts are enabled + - `DISABLED` - Alerts are disabled + """ + + ENABLED = "enabled" + DISABLED = "disabled" + + +class LicenseTierEnum(str, Enum): + """ + # Summary + + Enumeration for license tier options. + + ## Values + + - `ESSENTIALS` - Essentials license tier + - `PREMIER` - Premier license tier + """ + + ESSENTIALS = "essentials" + PREMIER = "premier" + + +class ReplicationModeEnum(str, Enum): + """ + # Summary + + Enumeration for replication modes. + + ## Values + + - `MULTICAST` - Multicast replication + - `INGRESS` - Ingress replication + """ + + MULTICAST = "multicast" + INGRESS = "ingress" + + +class OverlayModeEnum(str, Enum): + """ + # Summary + + Enumeration for overlay modes. + + ## Values + + - `CLI` - CLI based configuration + - `CONFIG_PROFILE` - Configuration profile based + """ + + CLI = "cli" + CONFIG_PROFILE = "config-profile" + + +class LinkStateRoutingProtocolEnum(str, Enum): + """ + # Summary + + Enumeration for underlay routing protocols. + + ## Values + + - `OSPF` - Open Shortest Path First + - `ISIS` - Intermediate System to Intermediate System + """ + + OSPF = "ospf" + ISIS = "isis" + + +class CoppPolicyEnum(str, Enum): + """ + # Summary + + Enumeration for CoPP policy options. + """ + + DENSE = "dense" + LENIENT = "lenient" + MODERATE = "moderate" + STRICT = "strict" + MANUAL = "manual" + + +class FabricInterfaceTypeEnum(str, Enum): + """ + # Summary + + Enumeration for fabric interface types. + """ + + P2P = "p2p" + UNNUMBERED = "unNumbered" + + +class GreenfieldDebugFlagEnum(str, Enum): + """ + # Summary + + Enumeration for greenfield debug flag. + """ + + ENABLE = "enable" + DISABLE = "disable" + + +class IsisLevelEnum(str, Enum): + """ + # Summary + + Enumeration for IS-IS levels. + """ + + LEVEL_1 = "level-1" + LEVEL_2 = "level-2" + + +class SecurityGroupStatusEnum(str, Enum): + """ + # Summary + + Enumeration for security group status. + """ + + ENABLED = "enabled" + ENABLED_STRICT = "enabledStrict" + ENABLED_LOOSE = "enabledLoose" + ENABLE_PENDING = "enablePending" + ENABLE_PENDING_STRICT = "enablePendingStrict" + ENABLE_PENDING_LOOSE = "enablePendingLoose" + DISABLE_PENDING = "disablePending" + DISABLED = "disabled" + + +class StpRootOptionEnum(str, Enum): + """ + # Summary + + Enumeration for STP root options. + """ + + RPVST_PLUS = "rpvst+" + MST = "mst" + UNMANAGED = "unmanaged" + + +class VpcPeerKeepAliveOptionEnum(str, Enum): + """ + # Summary + + Enumeration for vPC peer keep-alive options. + """ + + LOOPBACK = "loopback" + MANAGEMENT = "management" + + +class DhcpProtocolVersionEnum(str, Enum): + """ + # Summary + + Enumeration for DHCP protocol version options. + """ + + DHCPV4 = "dhcpv4" + DHCPV6 = "dhcpv6" + + +class PowerRedundancyModeEnum(str, Enum): + """ + # Summary + + Enumeration for power redundancy mode options. + """ + + REDUNDANT = "redundant" + COMBINED = "combined" + INPUT_SRC_REDUNDANT = "inputSrcRedundant" + + +class BgpAsModeEnum(str, Enum): + """ + # Summary + + Enumeration for eBGP BGP AS mode options. + """ + + MULTI_AS = "multiAS" + SAME_TIER_AS = "sameTierAS" + + +class FirstHopRedundancyProtocolEnum(str, Enum): + """ + # Summary + + Enumeration for first-hop redundancy protocol options. + """ + + HSRP = "hsrp" + VRRP = "vrrp" diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py new file mode 100644 index 00000000..8894941c --- /dev/null +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -0,0 +1,838 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +import re +from typing import List, Dict, Any, Optional, ClassVar, Literal + +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( + BaseModel, + ConfigDict, + Field, + field_validator, + model_validator, +) +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.enums import ( + FabricTypeEnum, + AlertSuspendEnum, + LicenseTierEnum, + OverlayModeEnum, + ReplicationModeEnum, + CoppPolicyEnum, + GreenfieldDebugFlagEnum, + VpcPeerKeepAliveOptionEnum, + BgpAsModeEnum, + FirstHopRedundancyProtocolEnum, +) +# Re-use shared nested models from the iBGP module +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import ( + LocationModel, + NetflowExporterModel, + NetflowRecordModel, + NetflowMonitorModel, + NetflowSettingsModel, + BootstrapSubnetModel, + TelemetryFlowCollectionModel, + TelemetryMicroburstModel, + TelemetryAnalysisSettingsModel, + TelemetryEnergyManagementModel, + TelemetryNasExportSettingsModel, + TelemetryNasModel, + TelemetrySettingsModel, + ExternalStreamingSettingsModel, +) + + +""" +# Comprehensive Pydantic models for eBGP VXLAN fabric management via Nexus Dashboard + +This module provides Pydantic models for creating, updating, and deleting +eBGP VXLAN fabrics through the Nexus Dashboard Fabric Controller (NDFC) API. + +## Models Overview + +- `VxlanEbgpManagementModel` - eBGP VXLAN specific management settings +- `FabricEbgpModel` - Complete fabric creation model for eBGP fabrics +- `FabricEbgpDeleteModel` - Fabric deletion model + +## Usage + +```python +# Create a new eBGP VXLAN fabric +fabric_data = { + "name": "MyEbgpFabric", + "management": { + "type": "vxlanEbgp", + "bgpAsnAutoAllocation": True, + "bgpAsnRange": "65000-65535" + } +} +fabric = FabricEbgpModel(**fabric_data) +``` +""" + +# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and +# dotted four-byte ASN notation (1-65535).(0-65535) +_BGP_ASN_RE = re.compile( + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" +) + + +class VxlanEbgpManagementModel(NDNestedModel): + """ + # Summary + + Comprehensive eBGP VXLAN fabric management configuration. + + This model contains all settings specific to eBGP VXLAN fabric types including + overlay configuration, BGP AS allocation, multicast settings, and advanced features. + + ## Raises + + - `ValueError` - If BGP ASN, VLAN ranges, or IP ranges are invalid + - `TypeError` - If required string fields are not provided + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + # Fabric Type (required for discriminated union) + type: Literal[FabricTypeEnum.VXLAN_EBGP] = Field(description="Fabric management type", default=FabricTypeEnum.VXLAN_EBGP) + + # Core eBGP Configuration + bgp_asn: Optional[str] = Field( + alias="bgpAsn", + description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]. Optional when bgpAsnAutoAllocation is True.", + default=None + ) + site_id: Optional[str] = Field(alias="siteId", description="Site identifier for the fabric. Defaults to Fabric ASN.", default="") + bgp_as_mode: BgpAsModeEnum = Field( + alias="bgpAsMode", + description="BGP AS mode: multiAS assigns unique AS per leaf tier, sameTierAS assigns same AS within a tier", + default=BgpAsModeEnum.MULTI_AS + ) + bgp_asn_auto_allocation: bool = Field( + alias="bgpAsnAutoAllocation", + description="Enable automatic BGP ASN allocation from bgpAsnRange", + default=True + ) + bgp_asn_range: Optional[str] = Field( + alias="bgpAsnRange", + description="BGP ASN range for automatic allocation (e.g., '65000-65535')", + default=None + ) + bgp_allow_as_in_num: int = Field( + alias="bgpAllowAsInNum", + description="Number of times BGP allows AS-path that contains local AS", + default=1 + ) + bgp_max_path: int = Field(alias="bgpMaxPath", description="Maximum number of BGP equal-cost paths", default=4) + bgp_underlay_failure_protect: bool = Field( + alias="bgpUnderlayFailureProtect", + description="Enable BGP underlay failure protection", + default=False + ) + auto_configure_ebgp_evpn_peering: bool = Field( + alias="autoConfigureEbgpEvpnPeering", + description="Automatically configure eBGP EVPN peering between spine and leaf", + default=True + ) + allow_leaf_same_as: bool = Field( + alias="allowLeafSameAs", + description="Allow leaf switches to have the same BGP AS number", + default=False + ) + assign_ipv4_to_loopback0: bool = Field( + alias="assignIpv4ToLoopback0", + description="Assign IPv4 address to loopback0 interface", + default=True + ) + evpn: bool = Field(description="Enable EVPN control plane", default=True) + route_map_tag: int = Field(alias="routeMapTag", description="Route map tag for redistribution", default=12345) + disable_route_map_tag: bool = Field( + alias="disableRouteMapTag", + description="Disable route map tag usage", + default=False + ) + leaf_bgp_as: Optional[str] = Field( + alias="leafBgpAs", + description="BGP AS number for leaf switches (used with sameTierAS mode)", + default=None + ) + border_bgp_as: Optional[str] = Field( + alias="borderBgpAs", + description="BGP AS number for border switches", + default=None + ) + super_spine_bgp_as: Optional[str] = Field( + alias="superSpineBgpAs", + description="BGP AS number for super-spine switches", + default=None + ) + + # Propagated from FabricEbgpModel + name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") + + # Network Addressing + bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="BGP loopback interface ID", ge=0, le=1023, default=0) + bgp_loopback_ip_range: str = Field(alias="bgpLoopbackIpRange", description="BGP loopback IP range", default="10.2.0.0/22") + bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="BGP loopback IPv6 range", default="fd00::a02:0/119") + nve_loopback_id: int = Field(alias="nveLoopbackId", description="NVE loopback interface ID", ge=0, le=1023, default=1) + nve_loopback_ip_range: str = Field(alias="nveLoopbackIpRange", description="NVE loopback IP range", default="10.3.0.0/22") + nve_loopback_ipv6_range: str = Field(alias="nveLoopbackIpv6Range", description="NVE loopback IPv6 range", default="fd00::a03:0/118") + anycast_loopback_id: int = Field(alias="anycastLoopbackId", description="Anycast loopback ID", default=10) + anycast_rendezvous_point_ip_range: str = Field( + alias="anycastRendezvousPointIpRange", + description="Anycast RP IP range", + default="10.254.254.0/24" + ) + ipv6_anycast_rendezvous_point_ip_range: str = Field( + alias="ipv6AnycastRendezvousPointIpRange", + description="IPv6 anycast RP IP range", + default="fd00::254:254:0/118" + ) + intra_fabric_subnet_range: str = Field( + alias="intraFabricSubnetRange", + description="Intra-fabric subnet range", + default="10.4.0.0/16" + ) + + # VLAN and VNI Ranges + l2_vni_range: str = Field(alias="l2VniRange", description="Layer 2 VNI range", default="30000-49000") + l3_vni_range: str = Field(alias="l3VniRange", description="Layer 3 VNI range", default="50000-59000") + network_vlan_range: str = Field(alias="networkVlanRange", description="Network VLAN range", default="2300-2999") + vrf_vlan_range: str = Field(alias="vrfVlanRange", description="VRF VLAN range", default="2000-2299") + + # Overlay Configuration + overlay_mode: OverlayModeEnum = Field(alias="overlayMode", description="Overlay configuration mode", default=OverlayModeEnum.CLI) + replication_mode: ReplicationModeEnum = Field( + alias="replicationMode", + description="Multicast replication mode", + default=ReplicationModeEnum.MULTICAST + ) + multicast_group_subnet: str = Field(alias="multicastGroupSubnet", description="Multicast group subnet", default="239.1.1.0/25") + auto_generate_multicast_group_address: bool = Field( + alias="autoGenerateMulticastGroupAddress", + description="Auto-generate multicast group addresses", + default=False + ) + underlay_multicast_group_address_limit: int = Field( + alias="underlayMulticastGroupAddressLimit", + description="Underlay multicast group address limit", + ge=1, + le=255, + default=128 + ) + tenant_routed_multicast: bool = Field(alias="tenantRoutedMulticast", description="Enable tenant routed multicast", default=False) + tenant_routed_multicast_ipv6: bool = Field( + alias="tenantRoutedMulticastIpv6", + description="Enable tenant routed multicast IPv6", + default=False + ) + first_hop_redundancy_protocol: FirstHopRedundancyProtocolEnum = Field( + alias="firstHopRedundancyProtocol", + description="First-hop redundancy protocol for tenant networks", + default=FirstHopRedundancyProtocolEnum.HSRP + ) + + # Multicast / Rendezvous Point + rendezvous_point_count: int = Field( + alias="rendezvousPointCount", + description="Number of spines acting as Rendezvous-Points", + default=2 + ) + rendezvous_point_loopback_id: int = Field(alias="rendezvousPointLoopbackId", description="RP loopback ID", default=254) + rendezvous_point_mode: str = Field(alias="rendezvousPointMode", description="Multicast RP mode", default="asm") + phantom_rendezvous_point_loopback_id1: int = Field(alias="phantomRendezvousPointLoopbackId1", description="Phantom RP loopback ID 1", default=2) + phantom_rendezvous_point_loopback_id2: int = Field(alias="phantomRendezvousPointLoopbackId2", description="Phantom RP loopback ID 2", default=3) + phantom_rendezvous_point_loopback_id3: int = Field(alias="phantomRendezvousPointLoopbackId3", description="Phantom RP loopback ID 3", default=4) + phantom_rendezvous_point_loopback_id4: int = Field(alias="phantomRendezvousPointLoopbackId4", description="Phantom RP loopback ID 4", default=5) + l3vni_multicast_group: str = Field(alias="l3vniMulticastGroup", description="Default L3 VNI multicast group IPv4 address", default="239.1.1.0") + l3_vni_ipv6_multicast_group: str = Field(alias="l3VniIpv6MulticastGroup", description="Default L3 VNI multicast group IPv6 address", default="ff1e::") + ipv6_multicast_group_subnet: str = Field(alias="ipv6MulticastGroupSubnet", description="IPv6 multicast group subnet", default="ff1e::/121") + mvpn_vrf_route_import_id: bool = Field(alias="mvpnVrfRouteImportId", description="Enable MVPN VRF route import ID", default=True) + mvpn_vrf_route_import_id_range: Optional[str] = Field( + alias="mvpnVrfRouteImportIdRange", + description="MVPN VRF route import ID range", + default=None + ) + vrf_route_import_id_reallocation: bool = Field( + alias="vrfRouteImportIdReallocation", + description="Enable VRF route import ID reallocation", + default=False + ) + + # Advanced Features + anycast_gateway_mac: str = Field( + alias="anycastGatewayMac", + description="Anycast gateway MAC address", + default="2020.0000.00aa" + ) + target_subnet_mask: int = Field(alias="targetSubnetMask", description="Target subnet mask", ge=24, le=31, default=30) + fabric_mtu: int = Field(alias="fabricMtu", description="Fabric MTU size", ge=1500, le=9216, default=9216) + l2_host_interface_mtu: int = Field(alias="l2HostInterfaceMtu", description="L2 host interface MTU", ge=1500, le=9216, default=9216) + l3_vni_no_vlan_default_option: bool = Field( + alias="l3VniNoVlanDefaultOption", + description="L3 VNI configuration without VLAN", + default=False + ) + underlay_ipv6: bool = Field(alias="underlayIpv6", description="Enable IPv6 underlay", default=False) + static_underlay_ip_allocation: bool = Field( + alias="staticUnderlayIpAllocation", + description="Disable dynamic underlay IP address allocation", + default=False + ) + anycast_border_gateway_advertise_physical_ip: bool = Field( + alias="anycastBorderGatewayAdvertisePhysicalIp", + description="Advertise Anycast Border Gateway PIP as VTEP", + default=False + ) + + # VPC Configuration + vpc_domain_id_range: str = Field(alias="vpcDomainIdRange", description="vPC domain ID range", default="1-1000") + vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="vPC peer link VLAN", default="3600") + vpc_peer_link_enable_native_vlan: bool = Field( + alias="vpcPeerLinkEnableNativeVlan", + description="Enable native VLAN on vPC peer link", + default=False + ) + vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( + alias="vpcPeerKeepAliveOption", + description="vPC peer keep-alive option", + default=VpcPeerKeepAliveOptionEnum.MANAGEMENT + ) + vpc_auto_recovery_timer: int = Field( + alias="vpcAutoRecoveryTimer", + description="vPC auto recovery timer", + ge=240, + le=3600, + default=360 + ) + vpc_delay_restore_timer: int = Field( + alias="vpcDelayRestoreTimer", + description="vPC delay restore timer", + ge=1, + le=3600, + default=150 + ) + vpc_peer_link_port_channel_id: str = Field(alias="vpcPeerLinkPortChannelId", description="vPC peer link port-channel ID", default="500") + vpc_ipv6_neighbor_discovery_sync: bool = Field( + alias="vpcIpv6NeighborDiscoverySync", + description="Enable vPC IPv6 ND sync", + default=True + ) + vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable vPC layer-3 peer router", default=True) + vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC TOR delay restore timer", default=30) + fabric_vpc_domain_id: bool = Field(alias="fabricVpcDomainId", description="Enable fabric vPC domain ID", default=False) + shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="Shared vPC domain ID", default=1) + fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Enable fabric vPC QoS", default=False) + fabric_vpc_qos_policy_name: str = Field( + alias="fabricVpcQosPolicyName", + description="Fabric vPC QoS policy name", + default="spine_qos_for_fabric_vpc_peering" + ) + enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable vPC peer-switch feature on ToR switches", default=False) + + # Per-VRF Loopback + per_vrf_loopback_auto_provision: bool = Field( + alias="perVrfLoopbackAutoProvision", + description="Auto provision IPv4 loopback on VRF attachment", + default=False + ) + per_vrf_loopback_ip_range: str = Field( + alias="perVrfLoopbackIpRange", + description="Per-VRF loopback IPv4 prefix pool", + default="10.5.0.0/22" + ) + per_vrf_loopback_auto_provision_ipv6: bool = Field( + alias="perVrfLoopbackAutoProvisionIpv6", + description="Auto provision IPv6 loopback on VRF attachment", + default=False + ) + per_vrf_loopback_ipv6_range: str = Field( + alias="perVrfLoopbackIpv6Range", + description="Per-VRF loopback IPv6 prefix pool", + default="fd00::a05:0/112" + ) + + # Templates + vrf_template: str = Field(alias="vrfTemplate", description="VRF template", default="Default_VRF_Universal") + network_template: str = Field(alias="networkTemplate", description="Network template", default="Default_Network_Universal") + vrf_extension_template: str = Field( + alias="vrfExtensionTemplate", + description="VRF extension template", + default="Default_VRF_Extension_Universal" + ) + network_extension_template: str = Field( + alias="networkExtensionTemplate", + description="Network extension template", + default="Default_Network_Extension_Universal" + ) + + # Optional Advanced Settings + performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) + tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable tenant DHCP", default=True) + advertise_physical_ip: bool = Field(alias="advertisePhysicalIp", description="Advertise physical IP as VTEP", default=False) + advertise_physical_ip_on_border: bool = Field( + alias="advertisePhysicalIpOnBorder", + description="Advertise physical IP on border switches only", + default=True + ) + + # Protocol Settings — BGP + bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enable BGP authentication", default=False) + bgp_authentication_key_type: str = Field( + alias="bgpAuthenticationKeyType", + description="BGP authentication key type", + default="3des" + ) + bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="BGP authentication key", default="") + + # Protocol Settings — BFD + bfd: bool = Field(description="Enable BFD", default=False) + bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD for iBGP", default=False) + bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD authentication", default=False) + bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD authentication key ID", default=100) + bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="BFD authentication key", default="") + + # Protocol Settings — PIM + pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Enable PIM hello authentication", default=False) + pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="PIM hello authentication key", default="") + + # Management Settings + nxapi: bool = Field(description="Enable NX-API", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) + + # Bootstrap / Day-0 / DHCP + day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) + bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( + alias="bootstrapSubnetCollection", + description="Bootstrap subnet collection", + default_factory=list + ) + local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) + dhcp_protocol_version: str = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default="dhcpv4") + dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") + dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") + management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") + management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) + management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) + + # Netflow Settings + netflow_settings: NetflowSettingsModel = Field( + alias="netflowSettings", + description="Netflow configuration", + default_factory=NetflowSettingsModel + ) + + # Backup / Restore + real_time_backup: Optional[bool] = Field(alias="realTimeBackup", description="Enable real-time backup", default=None) + scheduled_backup: Optional[bool] = Field(alias="scheduledBackup", description="Enable scheduled backup", default=None) + scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + + # VRF Lite / Sub-Interface + sub_interface_dot1q_range: str = Field(alias="subInterfaceDot1qRange", description="Sub-interface 802.1q range", default="2-511") + vrf_lite_auto_config: str = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default="manual") + vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="VRF lite subnet range", default="10.33.0.0/16") + vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF lite subnet target mask", default=30) + auto_unique_vrf_lite_ip_prefix: bool = Field( + alias="autoUniqueVrfLiteIpPrefix", + description="Auto unique VRF lite IP prefix", + default=False + ) + + # Leaf / TOR + leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Enable leaf/TOR ID range", default=False) + leaf_tor_vpc_port_channel_id_range: str = Field( + alias="leafTorVpcPortChannelIdRange", + description="Leaf/TOR vPC port-channel ID range", + default="1-499" + ) + allow_vlan_on_leaf_tor_pairing: str = Field( + alias="allowVlanOnLeafTorPairing", + description="Set trunk allowed VLAN on leaf-TOR pairing port-channels", + default="none" + ) + + # DNS / NTP / Syslog Collections + ntp_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerCollection") + ntp_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerVrfCollection") + dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection") + dns_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="dnsVrfCollection") + syslog_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerCollection") + syslog_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerVrfCollection") + syslog_severity_collection: List[int] = Field(default_factory=lambda: [7], alias="syslogSeverityCollection") + + # Extra Config / Pre-Interface Config / AAA / Banner + banner: str = Field(description="Fabric banner text", default="") + extra_config_leaf: str = Field(alias="extraConfigLeaf", description="Extra leaf config", default="") + extra_config_spine: str = Field(alias="extraConfigSpine", description="Extra spine config", default="") + extra_config_tor: str = Field(alias="extraConfigTor", description="Extra TOR config", default="") + extra_config_intra_fabric_links: str = Field( + alias="extraConfigIntraFabricLinks", + description="Extra intra-fabric links config", + default="" + ) + extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") + extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") + aaa: bool = Field(description="Enable AAA", default=False) + pre_interface_config_leaf: str = Field(alias="preInterfaceConfigLeaf", description="Pre-interface leaf config", default="") + pre_interface_config_spine: str = Field(alias="preInterfaceConfigSpine", description="Pre-interface spine config", default="") + pre_interface_config_tor: str = Field(alias="preInterfaceConfigTor", description="Pre-interface TOR config", default="") + + # System / Compliance / OAM / Misc + greenfield_debug_flag: GreenfieldDebugFlagEnum = Field( + alias="greenfieldDebugFlag", + description="Greenfield debug flag", + default=GreenfieldDebugFlagEnum.DISABLE + ) + interface_statistics_load_interval: int = Field( + alias="interfaceStatisticsLoadInterval", + description="Interface statistics load interval in seconds", + default=10 + ) + nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE source interface hold-down timer in seconds", default=180) + next_generation_oam: bool = Field(alias="nextGenerationOAM", description="Enable next-generation OAM", default=True) + ngoam_south_bound_loop_detect: bool = Field( + alias="ngoamSouthBoundLoopDetect", + description="Enable NGOAM south bound loop detection", + default=False + ) + ngoam_south_bound_loop_detect_probe_interval: int = Field( + alias="ngoamSouthBoundLoopDetectProbeInterval", + description="NGOAM south bound loop detect probe interval in seconds", + default=300 + ) + ngoam_south_bound_loop_detect_recovery_interval: int = Field( + alias="ngoamSouthBoundLoopDetectRecoveryInterval", + description="NGOAM south bound loop detect recovery interval in seconds", + default=600 + ) + strict_config_compliance_mode: bool = Field( + alias="strictConfigComplianceMode", + description="Enable strict config compliance mode", + default=False + ) + advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) + copp_policy: CoppPolicyEnum = Field(alias="coppPolicy", description="CoPP policy", default=CoppPolicyEnum.STRICT) + power_redundancy_mode: str = Field(alias="powerRedundancyMode", description="Power redundancy mode", default="redundant") + heartbeat_interval: int = Field(alias="heartbeatInterval", description="XConnect heartbeat interval", default=190) + snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) + cdp: bool = Field(description="Enable CDP", default=False) + real_time_interface_statistics_collection: bool = Field( + alias="realTimeInterfaceStatisticsCollection", + description="Enable real-time interface statistics collection", + default=False + ) + tcam_allocation: bool = Field(alias="tcamAllocation", description="Enable TCAM allocation", default=True) + allow_smart_switch_onboarding: bool = Field( + alias="allowSmartSwitchOnboarding", + description="Allow smart switch onboarding", + default=False + ) + + # Queuing / QoS + default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable default queuing policy", default=False) + default_queuing_policy_cloudscale: str = Field( + alias="defaultQueuingPolicyCloudscale", + description="Default queuing policy for cloudscale switches", + default="queuing_policy_default_8q_cloudscale" + ) + default_queuing_policy_r_series: str = Field( + alias="defaultQueuingPolicyRSeries", + description="Default queuing policy for R-Series switches", + default="queuing_policy_default_r_series" + ) + default_queuing_policy_other: str = Field( + alias="defaultQueuingPolicyOther", + description="Default queuing policy for other switches", + default="queuing_policy_default_other" + ) + aiml_qos: bool = Field(alias="aimlQos", description="Enable AI/ML QoS", default=False) + aiml_qos_policy: str = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default="400G") + roce_v2: str = Field(alias="roceV2", description="RoCEv2 DSCP value", default="26") + cnp: str = Field(description="CNP DSCP value", default="48") + wred_min: int = Field(alias="wredMin", description="WRED minimum threshold in kbytes", default=950) + wred_max: int = Field(alias="wredMax", description="WRED maximum threshold in kbytes", default=3000) + wred_drop_probability: int = Field(alias="wredDropProbability", description="WRED drop probability %", default=7) + wred_weight: int = Field(alias="wredWeight", description="WRED weight", default=0) + bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Bandwidth remaining % for AI traffic queues", default=50) + dlb: bool = Field(description="Enable dynamic load balancing", default=False) + dlb_mode: str = Field(alias="dlbMode", description="DLB mode", default="flowlet") + dlb_mixed_mode_default: str = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default="ecmp") + flowlet_aging: Optional[int] = Field(alias="flowletAging", description="Flowlet aging timer in microseconds", default=None) + flowlet_dscp: str = Field(alias="flowletDscp", description="Flowlet DSCP value", default="") + per_packet_dscp: str = Field(alias="perPacketDscp", description="Per-packet DSCP value", default="") + ai_load_sharing: bool = Field(alias="aiLoadSharing", description="Enable AI load sharing", default=False) + priority_flow_control_watch_interval: Optional[int] = Field( + alias="priorityFlowControlWatchInterval", + description="Priority flow control watch interval in milliseconds", + default=None + ) + + # PTP + ptp: bool = Field(description="Enable PTP", default=False) + ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) + ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) + + # Private VLAN + private_vlan: bool = Field(alias="privateVlan", description="Enable private VLAN", default=False) + default_private_vlan_secondary_network_template: str = Field( + alias="defaultPrivateVlanSecondaryNetworkTemplate", + description="Default private VLAN secondary network template", + default="Pvlan_Secondary_Network" + ) + + # MACsec + macsec: bool = Field(description="Enable MACsec", default=False) + macsec_cipher_suite: str = Field( + alias="macsecCipherSuite", + description="MACsec cipher suite", + default="GCM-AES-XPN-256" + ) + macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec primary key string", default="") + macsec_algorithm: str = Field(alias="macsecAlgorithm", description="MACsec primary cryptographic algorithm", default="AES_128_CMAC") + macsec_fallback_key_string: str = Field(alias="macsecFallbackKeyString", description="MACsec fallback key string", default="") + macsec_fallback_algorithm: str = Field( + alias="macsecFallbackAlgorithm", + description="MACsec fallback cryptographic algorithm", + default="AES_128_CMAC" + ) + macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec report timer in minutes", default=5) + + # Hypershield / Connectivity + connectivity_domain_name: Optional[str] = Field( + alias="connectivityDomainName", + description="Domain name to connect to Hypershield", + default=None + ) + hypershield_connectivity_proxy_server: Optional[str] = Field( + alias="hypershieldConnectivityProxyServer", + description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", + default=None + ) + hypershield_connectivity_proxy_server_port: Optional[int] = Field( + alias="hypershieldConnectivityProxyServerPort", + description="Proxy port number for communication with Hypershield", + default=None + ) + hypershield_connectivity_source_intf: Optional[str] = Field( + alias="hypershieldConnectivitySourceIntf", + description="Loopback interface on smart switch for communication with Hypershield", + default=None + ) + + @field_validator("bgp_asn") + @classmethod + def validate_bgp_asn(cls, value: Optional[str]) -> Optional[str]: + """ + # Summary + + Validate BGP ASN format and range when provided. + + ## Raises + + - `ValueError` - If value does not match the expected ASN format + """ + if value is None: + return value + if not _BGP_ASN_RE.match(value): + raise ValueError( + f"Invalid BGP ASN '{value}'. " + "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." + ) + return value + + @field_validator("site_id") + @classmethod + def validate_site_id(cls, value: str) -> str: + """ + # Summary + + Validate site ID format. + + ## Raises + + - `ValueError` - If site ID is not numeric or outside valid range + """ + if value == "": + return value + if not value.isdigit(): + raise ValueError(f"Site ID must be numeric, got: {value}") + site_id_int = int(value) + if not (1 <= site_id_int <= 281474976710655): + raise ValueError(f"Site ID must be between 1 and 281474976710655, got: {site_id_int}") + return value + + @field_validator("anycast_gateway_mac") + @classmethod + def validate_mac_address(cls, value: str) -> str: + """ + # Summary + + Validate MAC address format. + + ## Raises + + - `ValueError` - If MAC address format is invalid + """ + mac_pattern = re.compile(r'^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$') + if not mac_pattern.match(value): + raise ValueError(f"Invalid MAC address format, expected xxxx.xxxx.xxxx, got: {value}") + return value.lower() + + +class FabricEbgpModel(NDBaseModel): + """ + # Summary + + Complete model for creating a new eBGP VXLAN fabric. + + ## Raises + + - `ValueError` - If required fields are missing or invalid + - `TypeError` - If field types don't match expected types + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" + + # Basic Fabric Properties + category: Literal["fabric"] = Field(description="Resource category", default="fabric") + name: str = Field(description="Fabric name", min_length=1, max_length=64) + location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) + + # License and Operations + license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) + alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) + telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) + telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") + telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") + telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") + telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") + security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") + + # Core Management Configuration + management: Optional[VxlanEbgpManagementModel] = Field(description="eBGP VXLAN management configuration", default=None) + + # Optional Advanced Settings + telemetry_settings: Optional[TelemetrySettingsModel] = Field( + alias="telemetrySettings", + description="Telemetry configuration", + default=None + ) + external_streaming_settings: ExternalStreamingSettingsModel = Field( + alias="externalStreamingSettings", + description="External streaming settings", + default_factory=ExternalStreamingSettingsModel + ) + + @field_validator("name") + @classmethod + def validate_fabric_name(cls, value: str) -> str: + """ + # Summary + + Validate fabric name format and characters. + + ## Raises + + - `ValueError` - If name contains invalid characters or format + """ + if not re.match(r'^[a-zA-Z0-9_-]+$', value): + raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") + return value + + @model_validator(mode='after') + def validate_fabric_consistency(self) -> 'FabricEbgpModel': + """ + # Summary + + Validate consistency between fabric settings and management configuration. + + ## Raises + + - `ValueError` - If fabric settings are inconsistent + """ + if self.management is not None and self.management.type != FabricTypeEnum.VXLAN_EBGP: + raise ValueError(f"Management type must be {FabricTypeEnum.VXLAN_EBGP}") + + # Propagate fabric name to management model + if self.management is not None: + self.management.name = self.name + + # Propagate BGP ASN to site_id if both are set and site_id is empty + if self.management is not None and self.management.site_id == "" and self.management.bgp_asn is not None: + bgp_asn = self.management.bgp_asn + if "." in bgp_asn: + high, low = bgp_asn.split(".") + self.management.site_id = str(int(high) * 65536 + int(low)) + else: + self.management.site_id = bgp_asn + + # Auto-create default telemetry settings if collection is enabled + if self.telemetry_collection and self.telemetry_settings is None: + self.telemetry_settings = TelemetrySettingsModel() + + return self + + def to_diff_dict(self, **kwargs) -> Dict[str, Any]: + """Export for diff comparison, excluding fields that ND overrides for eBGP fabrics.""" + d = super().to_diff_dict(**kwargs) + # ND always returns nxapiHttp=True for eBGP fabrics regardless of the configured value, + # so exclude it from diff comparison to prevent a persistent false-positive diff. + if "management" in d: + d["management"].pop("nxapiHttp", None) + return d + + @classmethod + def get_argument_spec(cls) -> Dict: + return dict( + state={ + "type": "str", + "default": "merged", + "choices": ["merged", "replaced", "deleted", "overridden", "query"], + }, + config={"required": False, "type": "list", "elements": "dict"}, + ) + + +# Export all models for external use +__all__ = [ + "VxlanEbgpManagementModel", + "FabricEbgpModel", + "FabricEbgpDeleteModel", + "FabricTypeEnum", + "AlertSuspendEnum", + "LicenseTierEnum", + "ReplicationModeEnum", + "OverlayModeEnum", + "BgpAsModeEnum", + "FirstHopRedundancyProtocolEnum", + "VpcPeerKeepAliveOptionEnum", + "CoppPolicyEnum", + "GreenfieldDebugFlagEnum", +] diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py new file mode 100644 index 00000000..9210a8a7 --- /dev/null +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -0,0 +1,833 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +import re +from enum import Enum +from typing import List, Dict, Any, Optional, ClassVar, Literal + +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( + BaseModel, + ConfigDict, + Field, + field_validator, + model_validator, +) +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.enums import ( + FabricTypeEnum, + AlertSuspendEnum, + LicenseTierEnum, + CoppPolicyEnum, + DhcpProtocolVersionEnum, + PowerRedundancyModeEnum, +) + + +""" +# Comprehensive Pydantic models for External Connectivity fabric management via Nexus Dashboard + +This module provides comprehensive Pydantic models for creating, updating, and deleting +External Connectivity fabrics through the Nexus Dashboard Fabric Controller (NDFC) API. + +## Models Overview + +- `LocationModel` - Geographic location coordinates +- `NetflowExporterModel` - Netflow exporter configuration +- `NetflowRecordModel` - Netflow record configuration +- `NetflowMonitorModel` - Netflow monitor configuration +- `NetflowSettingsModel` - Complete netflow settings +- `BootstrapSubnetModel` - Bootstrap subnet configuration +- `TelemetryFlowCollectionModel` - Telemetry flow collection settings +- `TelemetrySettingsModel` - Complete telemetry configuration +- `ExternalStreamingSettingsModel` - External streaming configuration +- `ExternalConnectivityManagementModel` - External Connectivity specific management settings +- `FabricExternalConnectivityModel` - Complete fabric creation model + +## Usage + +```python +# Create a new External Connectivity fabric +fabric_data = { + "name": "MyExtFabric", + "location": {"latitude": 37.7749, "longitude": -122.4194}, + "management": { + "type": "externalConnectivity", + "bgp_asn": "65001", + } +} +fabric = FabricExternalConnectivityModel(**fabric_data) +``` +""" + +# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and +# dotted four-byte ASN notation (1-65535).(0-65535) +_BGP_ASN_RE = re.compile( + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" +) + + +class LocationModel(NDNestedModel): + """ + # Summary + + Geographic location coordinates for the fabric. + + ## Raises + + - `ValueError` - If latitude or longitude are outside valid ranges + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + latitude: float = Field( + description="Latitude coordinate (-90 to 90)", + ge=-90.0, + le=90.0 + ) + longitude: float = Field( + description="Longitude coordinate (-180 to 180)", + ge=-180.0, + le=180.0 + ) + + +class NetflowExporterModel(NDNestedModel): + """ + # Summary + + Netflow exporter configuration for telemetry. + + ## Raises + + - `ValueError` - If UDP port is outside valid range or IP address is invalid + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") + exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") + vrf: str = Field(description="VRF name for the exporter", default="management") + source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") + udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) + + +class NetflowRecordModel(NDNestedModel): + """ + # Summary + + Netflow record configuration defining flow record templates. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + record_name: str = Field(alias="recordName", description="Name of the netflow record") + record_template: str = Field(alias="recordTemplate", description="Template type for the record") + layer2_record: bool = Field(alias="layer2Record", description="Enable layer 2 record fields", default=False) + + +class NetflowMonitorModel(NDNestedModel): + """ + # Summary + + Netflow monitor configuration linking records to exporters. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") + record_name: str = Field(alias="recordName", description="Associated record name") + exporter1_name: str = Field(alias="exporter1Name", description="Primary exporter name") + exporter2_name: str = Field(alias="exporter2Name", description="Secondary exporter name", default="") + + +class NetflowSettingsModel(NDNestedModel): + """ + # Summary + + Complete netflow configuration including exporters, records, and monitors. + + ## Raises + + - `ValueError` - If netflow lists are inconsistent with netflow enabled state + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + netflow: bool = Field(description="Enable netflow collection", default=False) + netflow_exporter_collection: List[NetflowExporterModel] = Field( + alias="netflowExporterCollection", + description="List of netflow exporters", + default_factory=list + ) + netflow_record_collection: List[NetflowRecordModel] = Field( + alias="netflowRecordCollection", + description="List of netflow records", + default_factory=list + ) + netflow_monitor_collection: List[NetflowMonitorModel] = Field( + alias="netflowMonitorCollection", + description="List of netflow monitors", + default_factory=list + ) + + +class BootstrapSubnetModel(NDNestedModel): + """ + # Summary + + Bootstrap subnet configuration for fabric initialization. + + ## Raises + + - `ValueError` - If IP addresses or subnet prefix are invalid + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") + end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") + default_gateway: str = Field(alias="defaultGateway", description="Default gateway for bootstrap subnet") + subnet_prefix: int = Field(alias="subnetPrefix", description="Subnet prefix length", ge=8, le=30) + + +class TelemetryFlowCollectionModel(NDNestedModel): + """ + # Summary + + Telemetry flow collection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") + traffic_analytics_scope: str = Field( + alias="trafficAnalyticsScope", + description="Traffic analytics scope", + default="intraFabric" + ) + operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") + udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") + + +class TelemetryMicroburstModel(NDNestedModel): + """ + # Summary + + Microburst detection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + microburst: bool = Field(description="Enable microburst detection", default=False) + sensitivity: str = Field(description="Microburst sensitivity level", default="low") + + +class TelemetryAnalysisSettingsModel(NDNestedModel): + """ + # Summary + + Telemetry analysis configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) + + +class TelemetryEnergyManagementModel(NDNestedModel): + """ + # Summary + + Energy management telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + cost: float = Field(description="Energy cost per unit", default=1.2) + + +class TelemetryNasExportSettingsModel(NDNestedModel): + """ + # Summary + + NAS export settings for telemetry. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + export_type: str = Field(alias="exportType", description="Export type", default="full") + export_format: str = Field(alias="exportFormat", description="Export format", default="json") + + +class TelemetryNasModel(NDNestedModel): + """ + # Summary + + NAS (Network Attached Storage) telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + server: str = Field(description="NAS server address", default="") + export_settings: TelemetryNasExportSettingsModel = Field( + alias="exportSettings", + description="NAS export settings", + default_factory=TelemetryNasExportSettingsModel + ) + + +class TelemetrySettingsModel(NDNestedModel): + """ + # Summary + + Complete telemetry configuration for the fabric. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + flow_collection: TelemetryFlowCollectionModel = Field( + alias="flowCollection", + description="Flow collection settings", + default_factory=TelemetryFlowCollectionModel + ) + microburst: TelemetryMicroburstModel = Field( + description="Microburst detection settings", + default_factory=TelemetryMicroburstModel + ) + analysis_settings: TelemetryAnalysisSettingsModel = Field( + alias="analysisSettings", + description="Analysis settings", + default_factory=TelemetryAnalysisSettingsModel + ) + nas: TelemetryNasModel = Field( + description="NAS telemetry configuration", + default_factory=TelemetryNasModel + ) + energy_management: TelemetryEnergyManagementModel = Field( + alias="energyManagement", + description="Energy management settings", + default_factory=TelemetryEnergyManagementModel + ) + + +class ExternalStreamingSettingsModel(NDNestedModel): + """ + # Summary + + External streaming configuration for events and data export. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) + message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) + syslog: Dict[str, Any] = Field( + description="Syslog streaming configuration", + default_factory=lambda: { + "collectionSettings": {"anomalies": []}, + "facility": "", + "servers": [] + } + ) + webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) + + +class ExternalConnectivityManagementModel(NDNestedModel): + """ + # Summary + + Comprehensive External Connectivity fabric management configuration. + + This model contains all settings specific to External Connectivity fabric types including + BGP configuration, bootstrap settings, and advanced features. + + ## Raises + + - `ValueError` - If BGP ASN or IP ranges are invalid + - `TypeError` - If required string fields are not provided + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + # Fabric Type (required for discriminated union) + type: Literal[FabricTypeEnum.EXTERNAL_CONNECTIVITY] = Field( + description="Fabric management type", + default=FabricTypeEnum.EXTERNAL_CONNECTIVITY + ) + + # Core Configuration + bgp_asn: str = Field(alias="bgpAsn", description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]") + + # Name under management section is optional for backward compatibility + name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") + + # AAA + aaa: bool = Field(description="Enable AAA", default=False) + + # SSH + advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) + + # Loopback + allow_same_loopback_ip_on_switches: bool = Field( + alias="allowSameLoopbackIpOnSwitches", + description="Allow same loopback IP on switches", + default=False + ) + + # Smart Switch + allow_smart_switch_onboarding: bool = Field( + alias="allowSmartSwitchOnboarding", + description="Allow smart switch onboarding", + default=False + ) + + # Bootstrap Subnet Collection + bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( + alias="bootstrapSubnetCollection", + description="Bootstrap subnet collection", + default_factory=list + ) + + # CDP + cdp: bool = Field(description="Enable CDP", default=False) + + # CoPP Policy + copp_policy: CoppPolicyEnum = Field( + alias="coppPolicy", + description="CoPP policy", + default=CoppPolicyEnum.MANUAL + ) + + # BGP Configuration + create_bgp_config: bool = Field( + alias="createBgpConfig", + description="Create BGP configuration", + default=True + ) + + # Bootstrap Settings + day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) + day0_plug_and_play: bool = Field(alias="day0PlugAndPlay", description="Enable day-0 plug and play", default=False) + + # DHCP + dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") + dhcp_protocol_version: DhcpProtocolVersionEnum = Field( + alias="dhcpProtocolVersion", + description="DHCP protocol version", + default=DhcpProtocolVersionEnum.DHCPV4 + ) + dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") + + # DNS + dns_collection: List[str] = Field(alias="dnsCollection", description="DNS server collection", default_factory=list) + dns_vrf_collection: List[str] = Field(alias="dnsVrfCollection", description="DNS VRF collection", default_factory=list) + + # Domain + domain_name: str = Field(alias="domainName", description="Domain name", default="") + + # DPU Pinning + enable_dpu_pinning: bool = Field(alias="enableDpuPinning", description="Enable DPU pinning", default=False) + + # Extra Config + extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") + extra_config_fabric: str = Field(alias="extraConfigFabric", description="Extra fabric config", default="") + extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") + extra_config_xe_bootstrap: str = Field(alias="extraConfigXeBootstrap", description="Extra XE bootstrap config", default="") + + # Inband Management + inband_day0_bootstrap: bool = Field(alias="inbandDay0Bootstrap", description="Enable inband day-0 bootstrap", default=False) + inband_management: bool = Field(alias="inbandManagement", description="Enable in-band management", default=False) + + # Interface Statistics + interface_statistics_load_interval: int = Field( + alias="interfaceStatisticsLoadInterval", + description="Interface statistics load interval", + default=10 + ) + + # Local DHCP Server + local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) + + # Management + management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") + management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) + management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) + + # Monitored Mode + monitored_mode: bool = Field(alias="monitoredMode", description="Enable monitored mode", default=False) + + # MPLS Handoff + mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS handoff", default=False) + mpls_loopback_identifier: Optional[int] = Field( + alias="mplsLoopbackIdentifier", + description="MPLS loopback identifier", + default=None + ) + mpls_loopback_ip_range: str = Field( + alias="mplsLoopbackIpRange", + description="MPLS loopback IP range", + default="10.102.0.0/25" + ) + + # Netflow Settings + netflow_settings: NetflowSettingsModel = Field( + alias="netflowSettings", + description="Netflow configuration", + default_factory=NetflowSettingsModel + ) + + # NX-API Settings + nxapi: bool = Field(description="Enable NX-API", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) + + # Performance Monitoring + performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) + + # Power Redundancy + power_redundancy_mode: PowerRedundancyModeEnum = Field( + alias="powerRedundancyMode", + description="Power redundancy mode", + default=PowerRedundancyModeEnum.REDUNDANT + ) + + # PTP + ptp: bool = Field(description="Enable PTP", default=False) + ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) + ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) + + # Backup / Restore + real_time_backup: Optional[bool] = Field(alias="realTimeBackup", description="Enable real-time backup", default=None) + + # Interface Statistics Collection + real_time_interface_statistics_collection: bool = Field( + alias="realTimeInterfaceStatisticsCollection", + description="Enable real-time interface statistics", + default=False + ) + + # Scheduled Backup + scheduled_backup: Optional[bool] = Field(alias="scheduledBackup", description="Enable scheduled backup", default=None) + scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + + # SNMP + snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) + + # Sub-Interface + sub_interface_dot1q_range: str = Field( + alias="subInterfaceDot1qRange", + description="Sub-interface 802.1q range", + default="2-511" + ) + + # Hypershield / Connectivity + connectivity_domain_name: Optional[str] = Field( + alias="connectivityDomainName", + description="Domain name to connect to Hypershield", + default=None + ) + hypershield_connectivity_proxy_server: Optional[str] = Field( + alias="hypershieldConnectivityProxyServer", + description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", + default=None + ) + hypershield_connectivity_proxy_server_port: Optional[int] = Field( + alias="hypershieldConnectivityProxyServerPort", + description="Proxy port number for communication with Hypershield", + default=None + ) + hypershield_connectivity_source_intf: Optional[str] = Field( + alias="hypershieldConnectivitySourceIntf", + description="Loopback interface on smart switch for communication with Hypershield", + default=None + ) + + @field_validator("bgp_asn") + @classmethod + def validate_bgp_asn(cls, value: str) -> str: + """ + # Summary + + Validate BGP ASN format and range. + + ## Description + + Accepts either a plain integer ASN (1-4294967295) or dotted four-byte + ASN notation in the form ``MMMM.NNNN`` where both parts are in the + range 1-65535 / 0-65535 respectively. + + ## Raises + + - `ValueError` - If the value does not match the expected ASN format + """ + if not _BGP_ASN_RE.match(value): + raise ValueError( + f"Invalid BGP ASN '{value}'. " + "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." + ) + return value + + +class FabricExternalConnectivityModel(NDBaseModel): + """ + # Summary + + Complete model for creating a new External Connectivity fabric. + + This model combines all necessary components for fabric creation including + basic fabric properties, management settings, telemetry, and streaming configuration. + + ## Raises + + - `ValueError` - If required fields are missing or invalid + - `TypeError` - If field types don't match expected types + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" # Allow extra fields from API responses + ) + + identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" + + # Basic Fabric Properties + category: Literal["fabric"] = Field(description="Resource category", default="fabric") + name: str = Field(description="Fabric name", min_length=1, max_length=64) + location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) + + # License and Operations + license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) + alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) + telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) + telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") + telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") + telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") + telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") + security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") + + # Core Management Configuration + management: Optional[ExternalConnectivityManagementModel] = Field( + description="External Connectivity management configuration", + default=None + ) + + # Optional Advanced Settings + telemetry_settings: Optional[TelemetrySettingsModel] = Field( + alias="telemetrySettings", + description="Telemetry configuration", + default=None + ) + external_streaming_settings: ExternalStreamingSettingsModel = Field( + alias="externalStreamingSettings", + description="External streaming settings", + default_factory=ExternalStreamingSettingsModel + ) + + @field_validator("name") + @classmethod + def validate_fabric_name(cls, value: str) -> str: + """ + # Summary + + Validate fabric name format and characters. + + ## Raises + + - `ValueError` - If name contains invalid characters or format + """ + if not re.match(r'^[a-zA-Z0-9_-]+$', value): + raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") + + return value + + @model_validator(mode='after') + def validate_fabric_consistency(self) -> 'FabricExternalConnectivityModel': + """ + # Summary + + Validate consistency between fabric settings and management configuration. + + ## Raises + + - `ValueError` - If fabric settings are inconsistent + """ + # Ensure management type matches model type + if self.management is not None and self.management.type != FabricTypeEnum.EXTERNAL_CONNECTIVITY: + raise ValueError(f"Management type must be {FabricTypeEnum.EXTERNAL_CONNECTIVITY}") + + # Propagate fabric name to management model + if self.management is not None: + self.management.name = self.name + + # Validate telemetry consistency + if self.telemetry_collection and self.telemetry_settings is None: + # Auto-create default telemetry settings if collection is enabled + self.telemetry_settings = TelemetrySettingsModel() + + return self + + # TODO: to generate from Fields (low priority) + @classmethod + def get_argument_spec(cls) -> Dict: + return dict( + state={ + "type": "str", + "default": "merged", + "choices": ["merged", "replaced", "deleted", "overridden", "query"], + }, + config={"required": False, "type": "list", "elements": "dict"}, + ) + + +# Export all models for external use +__all__ = [ + "LocationModel", + "NetflowExporterModel", + "NetflowRecordModel", + "NetflowMonitorModel", + "NetflowSettingsModel", + "BootstrapSubnetModel", + "TelemetryFlowCollectionModel", + "TelemetryMicroburstModel", + "TelemetryAnalysisSettingsModel", + "TelemetryEnergyManagementModel", + "TelemetrySettingsModel", + "ExternalStreamingSettingsModel", + "ExternalConnectivityManagementModel", + "FabricExternalConnectivityModel", + "FabricTypeEnum", + "AlertSuspendEnum", + "LicenseTierEnum", + "CoppPolicyEnum", + "DhcpProtocolVersionEnum", + "PowerRedundancyModeEnum", +] diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py new file mode 100644 index 00000000..5e8169de --- /dev/null +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -0,0 +1,1317 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +import re +# from datetime import datetime +from enum import Enum +from typing import List, Dict, Any, Optional, ClassVar, Literal + +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( + BaseModel, + ConfigDict, + Field, + field_validator, + model_validator, +) +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.enums import ( + FabricTypeEnum, + AlertSuspendEnum, + LicenseTierEnum, + OverlayModeEnum, + ReplicationModeEnum, + LinkStateRoutingProtocolEnum, + CoppPolicyEnum, + FabricInterfaceTypeEnum, + GreenfieldDebugFlagEnum, + IsisLevelEnum, + SecurityGroupStatusEnum, + StpRootOptionEnum, + VpcPeerKeepAliveOptionEnum, +) + + +""" +# Comprehensive Pydantic models for iBGP VXLAN fabric management via Nexus Dashboard + +This module provides comprehensive Pydantic models for creating, updating, and deleting +iBGP VXLAN fabrics through the Nexus Dashboard Fabric Controller (NDFC) API. + +## Models Overview + +- `LocationModel` - Geographic location coordinates +- `NetflowExporterModel` - Netflow exporter configuration +- `NetflowRecordModel` - Netflow record configuration +- `NetflowMonitorModel` - Netflow monitor configuration +- `NetflowSettingsModel` - Complete netflow settings +- `BootstrapSubnetModel` - Bootstrap subnet configuration +- `TelemetryFlowCollectionModel` - Telemetry flow collection settings +- `TelemetrySettingsModel` - Complete telemetry configuration +- `ExternalStreamingSettingsModel` - External streaming configuration +- `VxlanIbgpManagementModel` - iBGP VXLAN specific management settings +- `FabricModel` - Complete fabric creation model +- `FabricDeleteModel` - Fabric deletion model + +## Usage + +```python +# Create a new iBGP VXLAN fabric +fabric_data = { + "name": "MyFabric", + "location": {"latitude": 37.7749, "longitude": -122.4194}, + "management": { + "type": "vxlanIbgp", + "bgp_asn": "65001", + "site_id": "65001" + } +} +fabric = FabricModel(**fabric_data) +``` +""" + +# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and +# dotted four-byte ASN notation (1-65535).(0-65535) +_BGP_ASN_RE = re.compile( + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" +) + + +class LocationModel(NDNestedModel): + """ + # Summary + + Geographic location coordinates for the fabric. + + ## Raises + + - `ValueError` - If latitude or longitude are outside valid ranges + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + latitude: float = Field( + description="Latitude coordinate (-90 to 90)", + ge=-90.0, + le=90.0 + ) + longitude: float = Field( + description="Longitude coordinate (-180 to 180)", + ge=-180.0, + le=180.0 + ) + + +class NetflowExporterModel(NDNestedModel): + """ + # Summary + + Netflow exporter configuration for telemetry. + + ## Raises + + - `ValueError` - If UDP port is outside valid range or IP address is invalid + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") + exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") + vrf: str = Field(description="VRF name for the exporter", default="management") + source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") + udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) + + +class NetflowRecordModel(NDNestedModel): + """ + # Summary + + Netflow record configuration defining flow record templates. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + record_name: str = Field(alias="recordName", description="Name of the netflow record") + record_template: str = Field(alias="recordTemplate", description="Template type for the record") + layer2_record: bool = Field(alias="layer2Record", description="Enable layer 2 record fields", default=False) + + +class NetflowMonitorModel(NDNestedModel): + """ + # Summary + + Netflow monitor configuration linking records to exporters. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") + record_name: str = Field(alias="recordName", description="Associated record name") + exporter1_name: str = Field(alias="exporter1Name", description="Primary exporter name") + exporter2_name: str = Field(alias="exporter2Name", description="Secondary exporter name", default="") + + +class NetflowSettingsModel(NDNestedModel): + """ + # Summary + + Complete netflow configuration including exporters, records, and monitors. + + ## Raises + + - `ValueError` - If netflow lists are inconsistent with netflow enabled state + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + netflow: bool = Field(description="Enable netflow collection", default=False) + netflow_exporter_collection: List[NetflowExporterModel] = Field( + alias="netflowExporterCollection", + description="List of netflow exporters", + default_factory=list + ) + netflow_record_collection: List[NetflowRecordModel] = Field( + alias="netflowRecordCollection", + description="List of netflow records", + default_factory=list + ) + netflow_monitor_collection: List[NetflowMonitorModel] = Field( + alias="netflowMonitorCollection", + description="List of netflow monitors", + default_factory=list + ) + + +class BootstrapSubnetModel(NDNestedModel): + """ + # Summary + + Bootstrap subnet configuration for fabric initialization. + + ## Raises + + - `ValueError` - If IP addresses or subnet prefix are invalid + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") + end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") + default_gateway: str = Field(alias="defaultGateway", description="Default gateway for bootstrap subnet") + subnet_prefix: int = Field(alias="subnetPrefix", description="Subnet prefix length", ge=8, le=30) + + +class TelemetryFlowCollectionModel(NDNestedModel): + """ + # Summary + + Telemetry flow collection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") + traffic_analytics_scope: str = Field( + alias="trafficAnalyticsScope", + description="Traffic analytics scope", + default="intraFabric" + ) + operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") + udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") + + +class TelemetryMicroburstModel(NDNestedModel): + """ + # Summary + + Microburst detection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + microburst: bool = Field(description="Enable microburst detection", default=False) + sensitivity: str = Field(description="Microburst sensitivity level", default="low") + + +class TelemetryAnalysisSettingsModel(NDNestedModel): + """ + # Summary + + Telemetry analysis configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) + + +class TelemetryEnergyManagementModel(NDNestedModel): + """ + # Summary + + Energy management telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + cost: float = Field(description="Energy cost per unit", default=1.2) + + +class TelemetryNasExportSettingsModel(NDNestedModel): + """ + # Summary + + NAS export settings for telemetry. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + export_type: str = Field(alias="exportType", description="Export type", default="full") + export_format: str = Field(alias="exportFormat", description="Export format", default="json") + + +class TelemetryNasModel(NDNestedModel): + """ + # Summary + + NAS (Network Attached Storage) telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + server: str = Field(description="NAS server address", default="") + export_settings: TelemetryNasExportSettingsModel = Field( + alias="exportSettings", + description="NAS export settings", + default_factory=TelemetryNasExportSettingsModel + ) + + +class TelemetrySettingsModel(NDNestedModel): + """ + # Summary + + Complete telemetry configuration for the fabric. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + flow_collection: TelemetryFlowCollectionModel = Field( + alias="flowCollection", + description="Flow collection settings", + default_factory=TelemetryFlowCollectionModel + ) + microburst: TelemetryMicroburstModel = Field( + description="Microburst detection settings", + default_factory=TelemetryMicroburstModel + ) + analysis_settings: TelemetryAnalysisSettingsModel = Field( + alias="analysisSettings", + description="Analysis settings", + default_factory=TelemetryAnalysisSettingsModel + ) + nas: TelemetryNasModel = Field( + description="NAS telemetry configuration", + default_factory=TelemetryNasModel + ) + energy_management: TelemetryEnergyManagementModel = Field( + alias="energyManagement", + description="Energy management settings", + default_factory=TelemetryEnergyManagementModel + ) + + +class ExternalStreamingSettingsModel(NDNestedModel): + """ + # Summary + + External streaming configuration for events and data export. + + ## Raises + + None + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) + message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) + syslog: Dict[str, Any] = Field( + description="Syslog streaming configuration", + default_factory=lambda: { + "collectionSettings": {"anomalies": []}, + "facility": "", + "servers": [] + } + ) + webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) + + +class VxlanIbgpManagementModel(NDNestedModel): + """ + # Summary + + Comprehensive iBGP VXLAN fabric management configuration. + + This model contains all settings specific to iBGP VXLAN fabric types including + overlay configuration, underlay routing, multicast settings, and advanced features. + + ## Raises + + - `ValueError` - If BGP ASN, VLAN ranges, or IP ranges are invalid + - `TypeError` - If required string fields are not provided + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" + ) + + # Fabric Type (required for discriminated union) + type: Literal[FabricTypeEnum.VXLAN_IBGP] = Field(description="Fabric management type", default=FabricTypeEnum.VXLAN_IBGP) + + # Core iBGP Configuration + bgp_asn: str = Field(alias="bgpAsn", description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]") + site_id: Optional[str] = Field(alias="siteId", description="Site identifier for the fabric", default="") + + # Name under management section is optional for backward compatibility, but if provided must be non-empty string + name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") + # border_count: Optional[int] = Field(alias="borderCount", description="Number of border switches", ge=0, le=32, default=0) + # breakout_spine_interfaces: Optional[bool] = Field(alias="breakoutSpineInterfaces", description="Enable breakout spine interfaces", default=False) + # designer_use_robot_password: Optional[bool] = Field(alias="designerUseRobotPassword", description="Use robot password for designer", default=False) + # leaf_count: Optional[int] = Field(alias="leafCount", description="Number of leaf switches", ge=1, le=128, default=1) + # spine_count: Optional[int] = Field(alias="spineCount", description="Number of spine switches", ge=1, le=32, default=1) + # vrf_lite_ipv6_subnet_range: Optional[str] = Field(alias="vrfLiteIpv6SubnetRange", description="VRF Lite IPv6 subnet range", default="fd00::a33:0/112") + # vrf_lite_ipv6_subnet_target_mask: Optional[int] = Field(alias="vrfLiteIpv6SubnetTargetMask", description="VRF Lite IPv6 subnet target mask", ge=112, le=128, default=126) + + + # Network Addressing + bgp_loopback_ip_range: str = Field( + alias="bgpLoopbackIpRange", + description="BGP loopback IP range", + default="10.2.0.0/22" + ) + nve_loopback_ip_range: str = Field( + alias="nveLoopbackIpRange", + description="NVE loopback IP range", + default="10.3.0.0/22" + ) + anycast_rendezvous_point_ip_range: str = Field( + alias="anycastRendezvousPointIpRange", + description="Anycast RP IP range", + default="10.254.254.0/24" + ) + intra_fabric_subnet_range: str = Field( + alias="intraFabricSubnetRange", + description="Intra-fabric subnet range", + default="10.4.0.0/16" + ) + + # VLAN and VNI Ranges + l2_vni_range: str = Field(alias="l2VniRange", description="Layer 2 VNI range", default="30000-49000") + l3_vni_range: str = Field(alias="l3VniRange", description="Layer 3 VNI range", default="50000-59000") + network_vlan_range: str = Field(alias="networkVlanRange", description="Network VLAN range", default="2300-2999") + vrf_vlan_range: str = Field(alias="vrfVlanRange", description="VRF VLAN range", default="2000-2299") + + # Overlay Configuration + overlay_mode: OverlayModeEnum = Field(alias="overlayMode", description="Overlay configuration mode", default=OverlayModeEnum.CLI) + replication_mode: ReplicationModeEnum = Field( + alias="replicationMode", + description="Multicast replication mode", + default=ReplicationModeEnum.MULTICAST + ) + multicast_group_subnet: str = Field( + alias="multicastGroupSubnet", + description="Multicast group subnet", + default="239.1.1.0/25" + ) + auto_generate_multicast_group_address: bool = Field( + alias="autoGenerateMulticastGroupAddress", + description="Auto-generate multicast group addresses", + default=False + ) + underlay_multicast_group_address_limit: int = Field( + alias="underlayMulticastGroupAddressLimit", + description="Underlay multicast group address limit", + ge=1, + le=255, + default=128 + ) + tenant_routed_multicast: bool = Field( + alias="tenantRoutedMulticast", + description="Enable tenant routed multicast", + default=False + ) + + # Underlay Configuration + link_state_routing_protocol: LinkStateRoutingProtocolEnum = Field( + alias="linkStateRoutingProtocol", + description="Underlay routing protocol", + default=LinkStateRoutingProtocolEnum.OSPF + ) + ospf_area_id: str = Field(alias="ospfAreaId", description="OSPF area ID", default="0.0.0.0") + fabric_interface_type: FabricInterfaceTypeEnum = Field(alias="fabricInterfaceType", description="Fabric interface type", default=FabricInterfaceTypeEnum.P2P) + + # Advanced Features + target_subnet_mask: int = Field(alias="targetSubnetMask", description="Target subnet mask", ge=24, le=31, default=30) + anycast_gateway_mac: str = Field( + alias="anycastGatewayMac", + description="Anycast gateway MAC address", + default="2020.0000.00aa" + ) + fabric_mtu: int = Field(alias="fabricMtu", description="Fabric MTU size", ge=1500, le=9216, default=9216) + l2_host_interface_mtu: int = Field( + alias="l2HostInterfaceMtu", + description="L2 host interface MTU", + ge=1500, + le=9216, + default=9216 + ) + + # VPC Configuration + vpc_domain_id_range: str = Field(alias="vpcDomainIdRange", description="vPC domain ID range", default="1-1000") + vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="vPC peer link VLAN", default="3600") + vpc_peer_link_enable_native_vlan: bool = Field( + alias="vpcPeerLinkEnableNativeVlan", + description="Enable native VLAN on vPC peer link", + default=False + ) + vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( + alias="vpcPeerKeepAliveOption", + description="vPC peer keep-alive option", + default=VpcPeerKeepAliveOptionEnum.MANAGEMENT + ) + vpc_auto_recovery_timer: int = Field( + alias="vpcAutoRecoveryTimer", + description="vPC auto recovery timer", + ge=240, + le=3600, + default=360 + ) + vpc_delay_restore_timer: int = Field( + alias="vpcDelayRestoreTimer", + description="vPC delay restore timer", + ge=1, + le=3600, + default=150 + ) + + # Loopback Configuration + bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="BGP loopback interface ID", ge=0, le=1023, default=0) + nve_loopback_id: int = Field(alias="nveLoopbackId", description="NVE loopback interface ID", ge=0, le=1023, default=1) + route_reflector_count: int = Field( + alias="routeReflectorCount", + description="Number of route reflectors", + ge=1, + le=4, + default=2 + ) + + # Templates + vrf_template: str = Field(alias="vrfTemplate", description="VRF template", default="Default_VRF_Universal") + network_template: str = Field(alias="networkTemplate", description="Network template", default="Default_Network_Universal") + vrf_extension_template: str = Field( + alias="vrfExtensionTemplate", + description="VRF extension template", + default="Default_VRF_Extension_Universal" + ) + network_extension_template: str = Field( + alias="networkExtensionTemplate", + description="Network extension template", + default="Default_Network_Extension_Universal" + ) + + # Optional Advanced Settings + performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) + tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable tenant DHCP", default=True) + advertise_physical_ip: bool = Field(alias="advertisePhysicalIp", description="Advertise physical IP", default=False) + advertise_physical_ip_on_border: bool = Field( + alias="advertisePhysicalIpOnBorder", + description="Advertise physical IP on border", + default=True + ) + + # Protocol Settings + bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enable BGP authentication", default=False) + bgp_authentication_key_type: str = Field( + alias="bgpAuthenticationKeyType", + description="BGP authentication key type", + default="3des" + ) + bfd: bool = Field(description="Enable BFD", default=False) + bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD for iBGP", default=False) + + # Management Settings + nxapi: bool = Field(description="Enable NX-API", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) + + # Bootstrap Settings + day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) + bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( + alias="bootstrapSubnetCollection", + description="Bootstrap subnet collection", + default_factory=list + ) + + # Netflow Settings + netflow_settings: NetflowSettingsModel = Field( + alias="netflowSettings", + description="Netflow configuration", + default_factory=NetflowSettingsModel + ) + + # Multicast Settings + rendezvous_point_count: int = Field( + alias="rendezvousPointCount", + description="Number of rendezvous points", + ge=1, + le=4, + default=2 + ) + rendezvous_point_loopback_id: int = Field( + alias="rendezvousPointLoopbackId", + description="RP loopback interface ID", + ge=0, + le=1023, + default=254 + ) + + # System Settings + snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) + cdp: bool = Field(description="Enable CDP", default=False) + real_time_interface_statistics_collection: bool = Field( + alias="realTimeInterfaceStatisticsCollection", + description="Enable real-time interface statistics", + default=False + ) + tcam_allocation: bool = Field(alias="tcamAllocation", description="Enable TCAM allocation", default=True) + + # VPC Extended Configuration + vpc_peer_link_port_channel_id: str = Field(alias="vpcPeerLinkPortChannelId", description="vPC peer link port-channel ID", default="500") + vpc_ipv6_neighbor_discovery_sync: bool = Field( + alias="vpcIpv6NeighborDiscoverySync", description="Enable vPC IPv6 ND sync", default=True + ) + vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable vPC layer-3 peer router", default=True) + vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC TOR delay restore timer", default=30) + fabric_vpc_domain_id: bool = Field(alias="fabricVpcDomainId", description="Enable fabric vPC domain ID", default=False) + shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="Shared vPC domain ID", default=1) + fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Enable fabric vPC QoS", default=False) + fabric_vpc_qos_policy_name: str = Field( + alias="fabricVpcQosPolicyName", description="Fabric vPC QoS policy name", default="spine_qos_for_fabric_vpc_peering" + ) + enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable peer switch", default=False) + + # Bootstrap / Day-0 / DHCP + local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) + dhcp_protocol_version: str = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default="dhcpv4") + dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") + dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") + management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") + management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) + management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) + extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") + un_numbered_bootstrap_loopback_id: int = Field( + alias="unNumberedBootstrapLoopbackId", description="Unnumbered bootstrap loopback ID", default=253 + ) + un_numbered_dhcp_start_address: str = Field(alias="unNumberedDhcpStartAddress", description="Unnumbered DHCP start address", default="") + un_numbered_dhcp_end_address: str = Field(alias="unNumberedDhcpEndAddress", description="Unnumbered DHCP end address", default="") + inband_management: bool = Field(alias="inbandManagement", description="Enable in-band management", default=False) + inband_dhcp_servers: List[str] = Field(alias="inbandDhcpServers", description="In-band DHCP servers", default_factory=list) + seed_switch_core_interfaces: List[str] = Field( + alias="seedSwitchCoreInterfaces", description="Seed switch core interfaces", default_factory=list + ) + spine_switch_core_interfaces: List[str] = Field( + alias="spineSwitchCoreInterfaces", description="Spine switch core interfaces", default_factory=list + ) + + # Backup / Restore + real_time_backup: bool = Field(alias="realTimeBackup", description="Enable real-time backup", default=False) + scheduled_backup: bool = Field(alias="scheduledBackup", description="Enable scheduled backup", default=False) + scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + + # IPv6 / Dual-Stack + underlay_ipv6: bool = Field(alias="underlayIpv6", description="Enable IPv6 underlay", default=False) + ipv6_multicast_group_subnet: str = Field( + alias="ipv6MulticastGroupSubnet", description="IPv6 multicast group subnet", default="ff1e::/121" + ) + tenant_routed_multicast_ipv6: bool = Field( + alias="tenantRoutedMulticastIpv6", description="Enable tenant routed multicast IPv6", default=False + ) + ipv6_link_local: bool = Field(alias="ipv6LinkLocal", description="Enable IPv6 link-local", default=True) + ipv6_subnet_target_mask: int = Field(alias="ipv6SubnetTargetMask", description="IPv6 subnet target mask", default=126) + ipv6_subnet_range: str = Field(alias="ipv6SubnetRange", description="IPv6 subnet range", default="fd00::a04:0/112") + bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="BGP loopback IPv6 range", default="fd00::a02:0/119") + nve_loopback_ipv6_range: str = Field(alias="nveLoopbackIpv6Range", description="NVE loopback IPv6 range", default="fd00::a03:0/118") + ipv6_anycast_rendezvous_point_ip_range: str = Field( + alias="ipv6AnycastRendezvousPointIpRange", description="IPv6 anycast RP IP range", default="fd00::254:254:0/118" + ) + + # Multicast / Rendezvous Point Extended + mvpn_vrf_route_import_id: bool = Field(alias="mvpnVrfRouteImportId", description="Enable MVPN VRF route import ID", default=True) + mvpn_vrf_route_import_id_range: str = Field( + alias="mvpnVrfRouteImportIdRange", description="MVPN VRF route import ID range", default="" + ) + vrf_route_import_id_reallocation: bool = Field( + alias="vrfRouteImportIdReallocation", description="Enable VRF route import ID reallocation", default=False + ) + l3vni_multicast_group: str = Field(alias="l3vniMulticastGroup", description="L3 VNI multicast group", default="239.1.1.0") + l3_vni_ipv6_multicast_group: str = Field(alias="l3VniIpv6MulticastGroup", description="L3 VNI IPv6 multicast group", default="ff1e::") + rendezvous_point_mode: str = Field(alias="rendezvousPointMode", description="Rendezvous point mode", default="asm") + phantom_rendezvous_point_loopback_id1: int = Field( + alias="phantomRendezvousPointLoopbackId1", description="Phantom RP loopback ID 1", default=2 + ) + phantom_rendezvous_point_loopback_id2: int = Field( + alias="phantomRendezvousPointLoopbackId2", description="Phantom RP loopback ID 2", default=3 + ) + phantom_rendezvous_point_loopback_id3: int = Field( + alias="phantomRendezvousPointLoopbackId3", description="Phantom RP loopback ID 3", default=4 + ) + phantom_rendezvous_point_loopback_id4: int = Field( + alias="phantomRendezvousPointLoopbackId4", description="Phantom RP loopback ID 4", default=5 + ) + anycast_loopback_id: int = Field(alias="anycastLoopbackId", description="Anycast loopback ID", default=10) + + # VRF Lite / Sub-Interface + sub_interface_dot1q_range: str = Field(alias="subInterfaceDot1qRange", description="Sub-interface 802.1q range", default="2-511") + vrf_lite_auto_config: str = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default="manual") + vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="VRF lite subnet range", default="10.33.0.0/16") + vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF lite subnet target mask", default=30) + auto_unique_vrf_lite_ip_prefix: bool = Field( + alias="autoUniqueVrfLiteIpPrefix", description="Auto unique VRF lite IP prefix", default=False + ) + auto_symmetric_vrf_lite: bool = Field(alias="autoSymmetricVrfLite", description="Auto symmetric VRF lite", default=False) + auto_vrf_lite_default_vrf: bool = Field(alias="autoVrfLiteDefaultVrf", description="Auto VRF lite default VRF", default=False) + auto_symmetric_default_vrf: bool = Field(alias="autoSymmetricDefaultVrf", description="Auto symmetric default VRF", default=False) + default_vrf_redistribution_bgp_route_map: str = Field( + alias="defaultVrfRedistributionBgpRouteMap", description="Default VRF redistribution BGP route map", default="extcon-rmap-filter" + ) + + # Per-VRF Loopback + per_vrf_loopback_auto_provision: bool = Field( + alias="perVrfLoopbackAutoProvision", description="Per-VRF loopback auto-provision", default=False + ) + per_vrf_loopback_ip_range: str = Field( + alias="perVrfLoopbackIpRange", description="Per-VRF loopback IP range", default="10.5.0.0/22" + ) + per_vrf_loopback_auto_provision_ipv6: bool = Field( + alias="perVrfLoopbackAutoProvisionIpv6", description="Per-VRF loopback auto-provision IPv6", default=False + ) + per_vrf_loopback_ipv6_range: str = Field( + alias="perVrfLoopbackIpv6Range", description="Per-VRF loopback IPv6 range", default="fd00::a05:0/112" + ) + per_vrf_unique_loopback_auto_provision: bool = Field( + alias="perVrfUniqueLoopbackAutoProvision", description="Per-VRF unique loopback auto-provision", default=False + ) + per_vrf_unique_loopback_ip_range: str = Field( + alias="perVrfUniqueLoopbackIpRange", description="Per-VRF unique loopback IP range", default="10.6.0.0/22" + ) + per_vrf_unique_loopback_auto_provision_v6: bool = Field( + alias="perVrfUniqueLoopbackAutoProvisionV6", description="Per-VRF unique loopback auto-provision IPv6", default=False + ) + per_vrf_unique_loopback_ipv6_range: str = Field( + alias="perVrfUniqueLoopbackIpv6Range", description="Per-VRF unique loopback IPv6 range", default="fd00::a06:0/112" + ) + + # Authentication — BGP Extended + bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="BGP authentication key", default="") + + # Authentication — PIM + pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Enable PIM hello authentication", default=False) + pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="PIM hello authentication key", default="") + + # Authentication — BFD + bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD authentication", default=False) + bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD authentication key ID", default=100) + bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="BFD authentication key", default="") + bfd_ospf: bool = Field(alias="bfdOspf", description="Enable BFD for OSPF", default=False) + bfd_isis: bool = Field(alias="bfdIsis", description="Enable BFD for IS-IS", default=False) + bfd_pim: bool = Field(alias="bfdPim", description="Enable BFD for PIM", default=False) + + # Authentication — OSPF + ospf_authentication: bool = Field(alias="ospfAuthentication", description="Enable OSPF authentication", default=False) + ospf_authentication_key_id: int = Field(alias="ospfAuthenticationKeyId", description="OSPF authentication key ID", default=127) + ospf_authentication_key: str = Field(alias="ospfAuthenticationKey", description="OSPF authentication key", default="") + + # IS-IS + isis_level: IsisLevelEnum = Field(alias="isisLevel", description="IS-IS level", default=IsisLevelEnum.LEVEL_2) + isis_area_number: str = Field(alias="isisAreaNumber", description="IS-IS area number", default="0001") + isis_point_to_point: bool = Field(alias="isisPointToPoint", description="IS-IS point-to-point", default=True) + isis_authentication: bool = Field(alias="isisAuthentication", description="Enable IS-IS authentication", default=False) + isis_authentication_keychain_name: str = Field( + alias="isisAuthenticationKeychainName", description="IS-IS authentication keychain name", default="" + ) + isis_authentication_keychain_key_id: int = Field( + alias="isisAuthenticationKeychainKeyId", description="IS-IS authentication keychain key ID", default=127 + ) + isis_authentication_key: str = Field(alias="isisAuthenticationKey", description="IS-IS authentication key", default="") + isis_overload: bool = Field(alias="isisOverload", description="Enable IS-IS overload bit", default=True) + isis_overload_elapse_time: int = Field(alias="isisOverloadElapseTime", description="IS-IS overload elapse time", default=60) + + # MACsec + macsec: bool = Field(description="Enable MACsec", default=False) + macsec_cipher_suite: str = Field(alias="macsecCipherSuite", description="MACsec cipher suite", default="GCM-AES-XPN-256") + macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec key string", default="") + macsec_algorithm: str = Field(alias="macsecAlgorithm", description="MACsec algorithm", default="AES_128_CMAC") + macsec_fallback_key_string: str = Field(alias="macsecFallbackKeyString", description="MACsec fallback key string", default="") + macsec_fallback_algorithm: str = Field(alias="macsecFallbackAlgorithm", description="MACsec fallback algorithm", default="AES_128_CMAC") + macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec report timer", default=5) + + # VRF Lite MACsec + vrf_lite_macsec: bool = Field(alias="vrfLiteMacsec", description="Enable VRF lite MACsec", default=False) + vrf_lite_macsec_cipher_suite: str = Field( + alias="vrfLiteMacsecCipherSuite", description="VRF lite MACsec cipher suite", default="GCM-AES-XPN-256" + ) + vrf_lite_macsec_key_string: str = Field(alias="vrfLiteMacsecKeyString", description="VRF lite MACsec key string", default="") + vrf_lite_macsec_algorithm: str = Field( + alias="vrfLiteMacsecAlgorithm", description="VRF lite MACsec algorithm", default="AES_128_CMAC" + ) + vrf_lite_macsec_fallback_key_string: str = Field( + alias="vrfLiteMacsecFallbackKeyString", description="VRF lite MACsec fallback key string", default="" + ) + vrf_lite_macsec_fallback_algorithm: str = Field( + alias="vrfLiteMacsecFallbackAlgorithm", description="VRF lite MACsec fallback algorithm", default="AES_128_CMAC" + ) + + # Quantum Key Distribution / Trustpoint + quantum_key_distribution: bool = Field(alias="quantumKeyDistribution", description="Enable quantum key distribution", default=False) + quantum_key_distribution_profile_name: str = Field( + alias="quantumKeyDistributionProfileName", description="Quantum key distribution profile name", default="" + ) + key_management_entity_server_ip: str = Field( + alias="keyManagementEntityServerIp", description="Key management entity server IP", default="" + ) + key_management_entity_server_port: int = Field( + alias="keyManagementEntityServerPort", description="Key management entity server port", default=0 + ) + trustpoint_label: str = Field(alias="trustpointLabel", description="Trustpoint label", default="") + skip_certificate_verification: bool = Field( + alias="skipCertificateVerification", description="Skip certificate verification", default=False + ) + + # BGP / Routing Enhancements + auto_bgp_neighbor_description: bool = Field( + alias="autoBgpNeighborDescription", description="Auto BGP neighbor description", default=True + ) + ibgp_peer_template: str = Field(alias="ibgpPeerTemplate", description="iBGP peer template", default="") + leaf_ibgp_peer_template: str = Field(alias="leafIbgpPeerTemplate", description="Leaf iBGP peer template", default="") + link_state_routing_tag: str = Field(alias="linkStateRoutingTag", description="Link state routing tag", default="UNDERLAY") + static_underlay_ip_allocation: bool = Field( + alias="staticUnderlayIpAllocation", description="Static underlay IP allocation", default=False + ) + router_id_range: str = Field(alias="routerIdRange", description="Router ID range", default="10.2.0.0/23") + + # Security Group Tags (SGT) + security_group_tag: bool = Field(alias="securityGroupTag", description="Enable security group tag", default=False) + security_group_tag_prefix: str = Field(alias="securityGroupTagPrefix", description="SGT prefix", default="SG_") + security_group_tag_mac_segmentation: bool = Field( + alias="securityGroupTagMacSegmentation", description="Enable SGT MAC segmentation", default=False + ) + security_group_tag_id_range: str = Field( + alias="securityGroupTagIdRange", description="SGT ID range", default="10000-14000" + ) + security_group_tag_preprovision: bool = Field( + alias="securityGroupTagPreprovision", description="Enable SGT preprovision", default=False + ) + security_group_status: SecurityGroupStatusEnum = Field(alias="securityGroupStatus", description="Security group status", default=SecurityGroupStatusEnum.DISABLED) + + # Queuing / QoS + default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable default queuing policy", default=False) + default_queuing_policy_cloudscale: str = Field( + alias="defaultQueuingPolicyCloudscale", description="Default queuing policy cloudscale", default="queuing_policy_default_8q_cloudscale" + ) + default_queuing_policy_r_series: str = Field( + alias="defaultQueuingPolicyRSeries", description="Default queuing policy R-Series", default="queuing_policy_default_r_series" + ) + default_queuing_policy_other: str = Field( + alias="defaultQueuingPolicyOther", description="Default queuing policy other", default="queuing_policy_default_other" + ) + aiml_qos: bool = Field(alias="aimlQos", description="Enable AI/ML QoS", default=False) + aiml_qos_policy: str = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default="400G") + roce_v2: str = Field(alias="roceV2", description="RoCEv2 DSCP value", default="26") + cnp: str = Field(description="CNP value", default="48") + wred_min: int = Field(alias="wredMin", description="WRED minimum threshold", default=950) + wred_max: int = Field(alias="wredMax", description="WRED maximum threshold", default=3000) + wred_drop_probability: int = Field(alias="wredDropProbability", description="WRED drop probability", default=7) + wred_weight: int = Field(alias="wredWeight", description="WRED weight", default=0) + bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Bandwidth remaining percentage", default=50) + dlb: bool = Field(description="Enable dynamic load balancing", default=False) + dlb_mode: str = Field(alias="dlbMode", description="DLB mode", default="flowlet") + dlb_mixed_mode_default: str = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default="ecmp") + flowlet_aging: int = Field(alias="flowletAging", description="Flowlet aging interval", default=1) + flowlet_dscp: str = Field(alias="flowletDscp", description="Flowlet DSCP value", default="") + per_packet_dscp: str = Field(alias="perPacketDscp", description="Per-packet DSCP value", default="") + ai_load_sharing: bool = Field(alias="aiLoadSharing", description="Enable AI load sharing", default=False) + priority_flow_control_watch_interval: int = Field( + alias="priorityFlowControlWatchInterval", description="Priority flow control watch interval", default=101 + ) + + # PTP + ptp: bool = Field(description="Enable PTP", default=False) + ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) + ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) + ptp_vlan_id: int = Field(alias="ptpVlanId", description="PTP VLAN ID", default=2) + + # STP + stp_root_option: StpRootOptionEnum = Field(alias="stpRootOption", description="STP root option", default=StpRootOptionEnum.UNMANAGED) + stp_vlan_range: str = Field(alias="stpVlanRange", description="STP VLAN range", default="1-3967") + mst_instance_range: str = Field(alias="mstInstanceRange", description="MST instance range", default="0") + stp_bridge_priority: int = Field(alias="stpBridgePriority", description="STP bridge priority", default=0) + + # MPLS Handoff + mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS handoff", default=False) + mpls_loopback_identifier: int = Field(alias="mplsLoopbackIdentifier", description="MPLS loopback identifier", default=101) + mpls_isis_area_number: str = Field(alias="mplsIsisAreaNumber", description="MPLS IS-IS area number", default="0001") + mpls_loopback_ip_range: str = Field(alias="mplsLoopbackIpRange", description="MPLS loopback IP range", default="10.101.0.0/25") + + # Private VLAN + private_vlan: bool = Field(alias="privateVlan", description="Enable private VLAN", default=False) + default_private_vlan_secondary_network_template: str = Field( + alias="defaultPrivateVlanSecondaryNetworkTemplate", + description="Default private VLAN secondary network template", + default="Pvlan_Secondary_Network" + ) + allow_vlan_on_leaf_tor_pairing: str = Field( + alias="allowVlanOnLeafTorPairing", description="Allow VLAN on leaf/TOR pairing", default="none" + ) + + # Leaf / TOR + leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Enable leaf/TOR ID range", default=False) + leaf_tor_vpc_port_channel_id_range: str = Field( + alias="leafTorVpcPortChannelIdRange", description="Leaf/TOR vPC port-channel ID range", default="1-499" + ) + + # Resource ID Ranges + l3_vni_no_vlan_default_option: bool = Field( + alias="l3VniNoVlanDefaultOption", description="L3 VNI no-VLAN default option", default=False + ) + ip_service_level_agreement_id_range: str = Field( + alias="ipServiceLevelAgreementIdRange", description="IP SLA ID range", default="10000-19999" + ) + object_tracking_number_range: str = Field( + alias="objectTrackingNumberRange", description="Object tracking number range", default="100-299" + ) + service_network_vlan_range: str = Field( + alias="serviceNetworkVlanRange", description="Service network VLAN range", default="3000-3199" + ) + route_map_sequence_number_range: str = Field( + alias="routeMapSequenceNumberRange", description="Route map sequence number range", default="1-65534" + ) + + # DNS / NTP / Syslog Collections + ntp_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerCollection") + ntp_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerVrfCollection") + dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection") + dns_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="dnsVrfCollection") + syslog_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerCollection") + syslog_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerVrfCollection") + syslog_severity_collection: List[int] = Field(default_factory=lambda: [7], alias="syslogSeverityCollection", description="Syslog severity levels (0-7)") + + # Extra Config / Pre-Interface Config / AAA / Banner + banner: str = Field(description="Fabric banner text", default="") + extra_config_leaf: str = Field(alias="extraConfigLeaf", description="Extra leaf config", default="") + extra_config_spine: str = Field(alias="extraConfigSpine", description="Extra spine config", default="") + extra_config_tor: str = Field(alias="extraConfigTor", description="Extra TOR config", default="") + extra_config_intra_fabric_links: str = Field( + alias="extraConfigIntraFabricLinks", description="Extra intra-fabric links config", default="" + ) + extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") + aaa: bool = Field(description="Enable AAA", default=False) + pre_interface_config_leaf: str = Field(alias="preInterfaceConfigLeaf", description="Pre-interface leaf config", default="") + pre_interface_config_spine: str = Field(alias="preInterfaceConfigSpine", description="Pre-interface spine config", default="") + pre_interface_config_tor: str = Field(alias="preInterfaceConfigTor", description="Pre-interface TOR config", default="") + + # System / Compliance / OAM / Misc + anycast_border_gateway_advertise_physical_ip: bool = Field( + alias="anycastBorderGatewayAdvertisePhysicalIp", description="Anycast border gateway advertise physical IP", default=False + ) + greenfield_debug_flag: GreenfieldDebugFlagEnum = Field(alias="greenfieldDebugFlag", description="Greenfield debug flag", default=GreenfieldDebugFlagEnum.DISABLE) + interface_statistics_load_interval: int = Field( + alias="interfaceStatisticsLoadInterval", description="Interface statistics load interval", default=10 + ) + nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE hold-down timer", default=180) + next_generation_oam: bool = Field(alias="nextGenerationOAM", description="Enable next-generation OAM", default=True) + ngoam_south_bound_loop_detect: bool = Field( + alias="ngoamSouthBoundLoopDetect", description="Enable NGOAM south bound loop detect", default=False + ) + ngoam_south_bound_loop_detect_probe_interval: int = Field( + alias="ngoamSouthBoundLoopDetectProbeInterval", description="NGOAM south bound loop detect probe interval", default=300 + ) + ngoam_south_bound_loop_detect_recovery_interval: int = Field( + alias="ngoamSouthBoundLoopDetectRecoveryInterval", description="NGOAM south bound loop detect recovery interval", default=600 + ) + strict_config_compliance_mode: bool = Field( + alias="strictConfigComplianceMode", description="Enable strict config compliance mode", default=False + ) + advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) + copp_policy: CoppPolicyEnum = Field(alias="coppPolicy", description="CoPP policy", default=CoppPolicyEnum.STRICT) + power_redundancy_mode: str = Field(alias="powerRedundancyMode", description="Power redundancy mode", default="redundant") + host_interface_admin_state: bool = Field( + alias="hostInterfaceAdminState", description="Host interface admin state", default=True + ) + heartbeat_interval: int = Field(alias="heartbeatInterval", description="Heartbeat interval", default=190) + policy_based_routing: bool = Field(alias="policyBasedRouting", description="Enable policy-based routing", default=False) + brownfield_network_name_format: str = Field( + alias="brownfieldNetworkNameFormat", description="Brownfield network name format", default="Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + ) + brownfield_skip_overlay_network_attachments: bool = Field( + alias="brownfieldSkipOverlayNetworkAttachments", description="Skip brownfield overlay network attachments", default=False + ) + allow_smart_switch_onboarding: bool = Field( + alias="allowSmartSwitchOnboarding", description="Allow smart switch onboarding", default=False + ) + + # Hypershield / Connectivity + connectivity_domain_name: Optional[str] = Field( + alias="connectivityDomainName", description="Domain name to connect to Hypershield", default=None + ) + hypershield_connectivity_proxy_server: Optional[str] = Field( + alias="hypershieldConnectivityProxyServer", + description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", + default=None + ) + hypershield_connectivity_proxy_server_port: Optional[int] = Field( + alias="hypershieldConnectivityProxyServerPort", + description="Proxy port number for communication with Hypershield", + default=None + ) + hypershield_connectivity_source_intf: Optional[str] = Field( + alias="hypershieldConnectivitySourceIntf", + description="Loopback interface on smart switch for communication with Hypershield", + default=None + ) + + @field_validator("bgp_asn") + @classmethod + def validate_bgp_asn(cls, value: str) -> str: + """ + # Summary + + Validate BGP ASN format and range. + + ## Description + + Accepts either a plain integer ASN (1-4294967295) or dotted four-byte + ASN notation in the form ``MMMM.NNNN`` where both parts are in the + range 1-65535 / 0-65535 respectively. + + ## Raises + + - `ValueError` - If the value does not match the expected ASN format + """ + if not _BGP_ASN_RE.match(value): + raise ValueError( + f"Invalid BGP ASN '{value}'. " + "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." + ) + return value + + @field_validator("site_id") + @classmethod + def validate_site_id(cls, value: str) -> str: + """ + # Summary + + Validate site ID format. + + ## Raises + + - `ValueError` - If site ID is not numeric or outside valid range + """ + + # If value is empty string (default), skip validation (will be set to BGP ASN later if still empty) + if value == "": + return value + + if not value.isdigit(): + raise ValueError(f"Site ID must be numeric, got: {value}") + + site_id_int = int(value) + if not (1 <= site_id_int <= 281474976710655): + raise ValueError(f"Site ID must be between 1 and 281474976710655, got: {site_id_int}") + + return value + + @field_validator("anycast_gateway_mac") + @classmethod + def validate_mac_address(cls, value: str) -> str: + """ + # Summary + + Validate MAC address format. + + ## Raises + + - `ValueError` - If MAC address format is invalid + """ + mac_pattern = re.compile(r'^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$') + if not mac_pattern.match(value): + raise ValueError(f"Invalid MAC address format, expected xxxx.xxxx.xxxx, got: {value}") + + return value.lower() + + +class FabricIbgpModel(NDBaseModel): + """ + # Summary + + Complete model for creating a new iBGP VXLAN fabric. + + This model combines all necessary components for fabric creation including + basic fabric properties, management settings, telemetry, and streaming configuration. + + ## Raises + + - `ValueError` - If required fields are missing or invalid + - `TypeError` - If field types don't match expected types + """ + + model_config = ConfigDict( + str_strip_whitespace=True, + validate_assignment=True, + populate_by_name=True, + extra="allow" # Allow extra fields from API responses + ) + + identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" + + # Basic Fabric Properties + category: Literal["fabric"] = Field(description="Resource category", default="fabric") + name: str = Field(description="Fabric name", min_length=1, max_length=64) + location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) + + # License and Operations + license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) + alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) + telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) + telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") + telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") + telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") + telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") + security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") + + # Core Management Configuration + management: Optional[VxlanIbgpManagementModel] = Field(description="iBGP VXLAN management configuration", default=None) + + # Optional Advanced Settings + telemetry_settings: Optional[TelemetrySettingsModel] = Field( + alias="telemetrySettings", + description="Telemetry configuration", + default=None + ) + external_streaming_settings: ExternalStreamingSettingsModel = Field( + alias="externalStreamingSettings", + description="External streaming settings", + default_factory=ExternalStreamingSettingsModel + ) + + @field_validator("name") + @classmethod + def validate_fabric_name(cls, value: str) -> str: + """ + # Summary + + Validate fabric name format and characters. + + ## Raises + + - `ValueError` - If name contains invalid characters or format + """ + if not re.match(r'^[a-zA-Z0-9_-]+$', value): + raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") + + return value + + @model_validator(mode='after') + def validate_fabric_consistency(self) -> 'FabricModel': + """ + # Summary + + Validate consistency between fabric settings and management configuration. + + ## Raises + + - `ValueError` - If fabric settings are inconsistent + """ + # Ensure management type matches model type + if self.management is not None and self.management.type != FabricTypeEnum.VXLAN_IBGP: + raise ValueError(f"Management type must be {FabricTypeEnum.VXLAN_IBGP}") + + # Propagate fabric name to management model + if self.management is not None: + self.management.name = self.name + + # Propagate BGP ASN to Site ID management model if not set + if self.management is not None and self.management.site_id == "": + bgp_asn = self.management.bgp_asn + if "." in bgp_asn: + # asdot notation (High.Low) → convert to asplain decimal: (High × 65536) + Low + high, low = bgp_asn.split(".") + self.management.site_id = str(int(high) * 65536 + int(low)) + else: + # Already plain decimal + self.management.site_id = bgp_asn + + # Validate telemetry consistency + if self.telemetry_collection and self.telemetry_settings is None: + # Auto-create default telemetry settings if collection is enabled + self.telemetry_settings = TelemetrySettingsModel() + + return self + + # TODO: to generate from Fields (low priority) + @classmethod + def get_argument_spec(cls) -> Dict: + return dict( + state={ + "type": "str", + "default": "merged", + "choices": ["merged", "replaced", "deleted", "overridden", "query"], + }, + config={"required": False, "type": "list", "elements": "dict"}, + ) + + +# Export all models for external use +__all__ = [ + "LocationModel", + "NetflowExporterModel", + "NetflowRecordModel", + "NetflowMonitorModel", + "NetflowSettingsModel", + "BootstrapSubnetModel", + "TelemetryFlowCollectionModel", + "TelemetryMicroburstModel", + "TelemetryAnalysisSettingsModel", + "TelemetryEnergyManagementModel", + "TelemetrySettingsModel", + "ExternalStreamingSettingsModel", + "VxlanIbgpManagementModel", + "FabricModel", + "FabricDeleteModel", + "FabricTypeEnum", + "AlertSuspendEnum", + "LicenseTierEnum", + "ReplicationModeEnum", + "OverlayModeEnum", + "LinkStateRoutingProtocolEnum" +] \ No newline at end of file diff --git a/plugins/module_utils/orchestrators/manage_fabric_ebgp.py b/plugins/module_utils/orchestrators/manage_fabric_ebgp.py new file mode 100644 index 00000000..45df1acd --- /dev/null +++ b/plugins/module_utils/orchestrators/manage_fabric_ebgp.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from typing import Type +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.base import NDBaseOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import FabricIbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ebgp import FabricEbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.types import ResponseType +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.manage_fabrics import ( + EpManageFabricsGet, + EpManageFabricsListGet, + EpManageFabricsPost, + EpManageFabricsPut, + EpManageFabricsDelete, +) + +class ManageEbgpFabricOrchestrator(NDBaseOrchestrator): + model_class: Type[NDBaseModel] = FabricEbgpModel + + create_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPost + update_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPut + delete_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsDelete + query_one_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsGet + query_all_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsListGet + + def query_all(self) -> ResponseType: + """ + Custom query_all action to extract 'fabrics' from response, + filtered to only vxlanEbgp fabric types. + """ + try: + api_endpoint = self.query_all_endpoint() + result = self.sender.query_obj(api_endpoint.path) + fabrics = result.get("fabrics", []) or [] + return [f for f in fabrics if f.get("management", {}).get("type") == "vxlanEbgp"] + except Exception as e: + raise Exception(f"Query all failed: {e}") from e diff --git a/plugins/module_utils/orchestrators/manage_fabric_external.py b/plugins/module_utils/orchestrators/manage_fabric_external.py new file mode 100644 index 00000000..d370315a --- /dev/null +++ b/plugins/module_utils/orchestrators/manage_fabric_external.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from typing import Type +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.base import NDBaseOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_external import FabricExternalConnectivityModel +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.types import ResponseType +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.manage_fabrics import ( + EpManageFabricsGet, + EpManageFabricsListGet, + EpManageFabricsPost, + EpManageFabricsPut, + EpManageFabricsDelete, +) + + +class ManageExternalFabricOrchestrator(NDBaseOrchestrator): + model_class: Type[NDBaseModel] = FabricExternalConnectivityModel + + create_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPost + update_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPut + delete_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsDelete + query_one_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsGet + query_all_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsListGet + + def query_all(self) -> ResponseType: + """ + Custom query_all action to extract 'fabrics' from response, + filtered to only externalConnectivity fabric types. + """ + try: + api_endpoint = self.query_all_endpoint() + result = self.sender.query_obj(api_endpoint.path) + fabrics = result.get("fabrics", []) or [] + return [f for f in fabrics if f.get("management", {}).get("type") == "externalConnectivity"] + except Exception as e: + raise Exception(f"Query all failed: {e}") from e diff --git a/plugins/module_utils/orchestrators/manage_fabric_ibgp.py b/plugins/module_utils/orchestrators/manage_fabric_ibgp.py new file mode 100644 index 00000000..e2082b57 --- /dev/null +++ b/plugins/module_utils/orchestrators/manage_fabric_ibgp.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from typing import Type +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.base import NDBaseOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import FabricIbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ebgp import FabricEbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.types import ResponseType +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.manage_fabrics import ( + EpManageFabricsGet, + EpManageFabricsListGet, + EpManageFabricsPost, + EpManageFabricsPut, + EpManageFabricsDelete, +) + + +class ManageIbgpFabricOrchestrator(NDBaseOrchestrator): + model_class: Type[NDBaseModel] = FabricIbgpModel + + create_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPost + update_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsPut + delete_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsDelete + query_one_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsGet + query_all_endpoint: Type[NDEndpointBaseModel] = EpManageFabricsListGet + + def query_all(self) -> ResponseType: + """ + Custom query_all action to extract 'fabrics' from response, + filtered to only vxlanIbgp fabric types. + """ + try: + api_endpoint = self.query_all_endpoint() + result = self.sender.query_obj(api_endpoint.path) + fabrics = result.get("fabrics", []) or [] + return [f for f in fabrics if f.get("management", {}).get("type") == "vxlanIbgp"] + except Exception as e: + raise Exception(f"Query all failed: {e}") from e diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py new file mode 100644 index 00000000..04a4ab72 --- /dev/null +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -0,0 +1,1179 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: nd_manage_fabric_ebgp +version_added: "1.4.0" +short_description: Manage eBGP VXLAN fabrics on Cisco Nexus Dashboard +description: +- Manage eBGP VXLAN fabrics on Cisco Nexus Dashboard (ND). +- It supports creating, updating, replacing, and deleting eBGP VXLAN fabrics. +author: +- Mike Wiebe (@mwiebe) +options: + config: + description: + - The list of eBGP VXLAN fabrics to configure. + type: list + elements: dict + suboptions: + name: + description: + - The name of the fabric. + - Only letters, numbers, underscores, and hyphens are allowed. + - The O(config.name) must be defined when creating, updating or deleting a fabric. + type: str + required: true + category: + description: + - The resource category. + type: str + default: fabric + location: + description: + - The geographic location of the fabric. + type: dict + suboptions: + latitude: + description: + - Latitude coordinate of the fabric location (-90 to 90). + type: float + required: true + longitude: + description: + - Longitude coordinate of the fabric location (-180 to 180). + type: float + required: true + license_tier: + description: + - The license tier for the fabric. + type: str + default: premier + choices: [ essentials, premier ] + alert_suspend: + description: + - The alert suspension state for the fabric. + type: str + default: disabled + choices: [ enabled, disabled ] + telemetry_collection: + description: + - Enable telemetry collection for the fabric. + type: bool + default: false + telemetry_collection_type: + description: + - The telemetry collection type. + type: str + default: outOfBand + telemetry_streaming_protocol: + description: + - The telemetry streaming protocol. + type: str + default: ipv4 + telemetry_source_interface: + description: + - The telemetry source interface. + type: str + default: "" + telemetry_source_vrf: + description: + - The telemetry source VRF. + type: str + default: "" + security_domain: + description: + - The security domain associated with the fabric. + type: str + default: all + management: + description: + - The eBGP VXLAN management configuration for the fabric. + type: dict + suboptions: + type: + description: + - The fabric management type. Must be C(vxlanEbgp) for eBGP VXLAN fabrics. + type: str + default: vxlanEbgp + choices: [ vxlanEbgp ] + bgp_asn: + description: + - The BGP Autonomous System Number for the fabric. + - Must be a numeric value between 1 and 4294967295, or dotted notation (1-65535.0-65535). + - Optional when O(config.management.bgp_asn_auto_allocation) is C(true). + type: str + bgp_asn_auto_allocation: + description: + - Enable automatic BGP ASN allocation from the O(config.management.bgp_asn_range) pool. + type: bool + default: true + bgp_asn_range: + description: + - The BGP ASN range to use for automatic ASN allocation (e.g. C(65000-65535)). + - Required when O(config.management.bgp_asn_auto_allocation) is C(true). + type: str + bgp_as_mode: + description: + - The BGP AS mode for the fabric. + - C(multiAS) assigns a unique AS number to each leaf tier. + - C(sameTierAS) assigns the same AS number within a tier. + type: str + default: multiAS + choices: [ multiAS, sameTierAS ] + bgp_allow_as_in_num: + description: + - The number of times BGP allows an AS-path containing the local AS number. + type: int + default: 1 + bgp_max_path: + description: + - The maximum number of BGP equal-cost paths. + type: int + default: 4 + bgp_underlay_failure_protect: + description: + - Enable BGP underlay failure protection. + type: bool + default: false + auto_configure_ebgp_evpn_peering: + description: + - Automatically configure eBGP EVPN peering between spine and leaf switches. + type: bool + default: true + allow_leaf_same_as: + description: + - Allow leaf switches to share the same BGP AS number. + type: bool + default: false + assign_ipv4_to_loopback0: + description: + - Assign an IPv4 address to the loopback0 interface. + type: bool + default: true + evpn: + description: + - Enable the EVPN control plane. + type: bool + default: true + route_map_tag: + description: + - The route map tag used for redistribution. + type: int + default: 12345 + disable_route_map_tag: + description: + - Disable route map tag usage. + type: bool + default: false + leaf_bgp_as: + description: + - The BGP AS number for leaf switches (used with C(sameTierAS) mode). + type: str + border_bgp_as: + description: + - The BGP AS number for border switches. + type: str + super_spine_bgp_as: + description: + - The BGP AS number for super-spine switches. + type: str + site_id: + description: + - The site identifier for the fabric. + - Defaults to the value of O(config.management.bgp_asn) if not provided. + type: str + default: "" + target_subnet_mask: + description: + - The target subnet mask for intra-fabric links (24-31). + type: int + default: 30 + anycast_gateway_mac: + description: + - The anycast gateway MAC address in xxxx.xxxx.xxxx format. + type: str + default: 2020.0000.00aa + replication_mode: + description: + - The multicast replication mode. + type: str + default: multicast + choices: [ multicast, ingress ] + multicast_group_subnet: + description: + - The multicast group subnet. + type: str + default: "239.1.1.0/25" + auto_generate_multicast_group_address: + description: + - Automatically generate multicast group addresses. + type: bool + default: false + underlay_multicast_group_address_limit: + description: + - The underlay multicast group address limit (1-255). + type: int + default: 128 + tenant_routed_multicast: + description: + - Enable tenant routed multicast. + type: bool + default: false + tenant_routed_multicast_ipv6: + description: + - Enable tenant routed multicast for IPv6. + type: bool + default: false + first_hop_redundancy_protocol: + description: + - The first-hop redundancy protocol for tenant networks. + type: str + default: hsrp + choices: [ hsrp, vrrp ] + rendezvous_point_count: + description: + - The number of rendezvous points (1-4). + type: int + default: 2 + rendezvous_point_loopback_id: + description: + - The rendezvous point loopback interface ID (0-1023). + type: int + default: 254 + overlay_mode: + description: + - The overlay configuration mode. + type: str + default: cli + choices: [ cli, config-profile ] + bgp_loopback_id: + description: + - The BGP loopback interface ID (0-1023). + type: int + default: 0 + nve_loopback_id: + description: + - The NVE loopback interface ID (0-1023). + type: int + default: 1 + anycast_loopback_id: + description: + - The anycast loopback interface ID. + type: int + default: 10 + bgp_loopback_ip_range: + description: + - The BGP loopback IP address pool. + type: str + default: "10.2.0.0/22" + bgp_loopback_ipv6_range: + description: + - The BGP loopback IPv6 address pool. + type: str + default: "fd00::a02:0/119" + nve_loopback_ip_range: + description: + - The NVE loopback IP address pool. + type: str + default: "10.3.0.0/22" + nve_loopback_ipv6_range: + description: + - The NVE loopback IPv6 address pool. + type: str + default: "fd00::a03:0/118" + anycast_rendezvous_point_ip_range: + description: + - The anycast rendezvous point IP address pool. + type: str + default: "10.254.254.0/24" + ipv6_anycast_rendezvous_point_ip_range: + description: + - The IPv6 anycast rendezvous point IP address pool. + type: str + default: "fd00::254:254:0/118" + intra_fabric_subnet_range: + description: + - The intra-fabric subnet IP address pool. + type: str + default: "10.4.0.0/16" + l2_vni_range: + description: + - The Layer 2 VNI range. + type: str + default: "30000-49000" + l3_vni_range: + description: + - The Layer 3 VNI range. + type: str + default: "50000-59000" + network_vlan_range: + description: + - The network VLAN range. + type: str + default: "2300-2999" + vrf_vlan_range: + description: + - The VRF VLAN range. + type: str + default: "2000-2299" + sub_interface_dot1q_range: + description: + - The sub-interface 802.1q range. + type: str + default: "2-511" + l3_vni_no_vlan_default_option: + description: + - Enable L3 VNI no-VLAN default option. + type: bool + default: false + fabric_mtu: + description: + - The fabric MTU size (1500-9216). + type: int + default: 9216 + l2_host_interface_mtu: + description: + - The L2 host interface MTU size (1500-9216). + type: int + default: 9216 + underlay_ipv6: + description: + - Enable IPv6 underlay. + type: bool + default: false + static_underlay_ip_allocation: + description: + - Disable dynamic underlay IP address allocation. + type: bool + default: false + vpc_domain_id_range: + description: + - The vPC domain ID range. + type: str + default: "1-1000" + vpc_peer_link_vlan: + description: + - The vPC peer link VLAN ID. + type: str + default: "3600" + vpc_peer_link_enable_native_vlan: + description: + - Enable native VLAN on the vPC peer link. + type: bool + default: false + vpc_peer_keep_alive_option: + description: + - The vPC peer keep-alive option. + type: str + default: management + choices: [ loopback, management ] + vpc_auto_recovery_timer: + description: + - The vPC auto recovery timer in seconds (240-3600). + type: int + default: 360 + vpc_delay_restore_timer: + description: + - The vPC delay restore timer in seconds (1-3600). + type: int + default: 150 + vpc_peer_link_port_channel_id: + description: + - The vPC peer link port-channel ID. + type: str + default: "500" + vpc_ipv6_neighbor_discovery_sync: + description: + - Enable vPC IPv6 neighbor discovery synchronization. + type: bool + default: true + vpc_layer3_peer_router: + description: + - Enable vPC layer-3 peer router. + type: bool + default: true + vpc_tor_delay_restore_timer: + description: + - The vPC TOR delay restore timer. + type: int + default: 30 + fabric_vpc_domain_id: + description: + - Enable fabric vPC domain ID. + type: bool + default: false + shared_vpc_domain_id: + description: + - The shared vPC domain ID. + type: int + default: 1 + fabric_vpc_qos: + description: + - Enable fabric vPC QoS. + type: bool + default: false + fabric_vpc_qos_policy_name: + description: + - The fabric vPC QoS policy name. + type: str + default: spine_qos_for_fabric_vpc_peering + enable_peer_switch: + description: + - Enable peer switch. + type: bool + default: false + per_vrf_loopback_auto_provision: + description: + - Enable per-VRF loopback auto-provisioning. + type: bool + default: false + per_vrf_loopback_ip_range: + description: + - The per-VRF loopback IP address pool. + type: str + default: "10.5.0.0/22" + per_vrf_loopback_auto_provision_ipv6: + description: + - Enable per-VRF loopback auto-provisioning for IPv6. + type: bool + default: false + per_vrf_loopback_ipv6_range: + description: + - The per-VRF loopback IPv6 address pool. + type: str + default: "fd00::a05:0/112" + vrf_template: + description: + - The VRF template name. + type: str + default: Default_VRF_Universal + network_template: + description: + - The network template name. + type: str + default: Default_Network_Universal + vrf_extension_template: + description: + - The VRF extension template name. + type: str + default: Default_VRF_Extension_Universal + network_extension_template: + description: + - The network extension template name. + type: str + default: Default_Network_Extension_Universal + performance_monitoring: + description: + - Enable performance monitoring. + type: bool + default: false + tenant_dhcp: + description: + - Enable tenant DHCP. + type: bool + default: true + advertise_physical_ip: + description: + - Advertise physical IP address for NVE loopback. + type: bool + default: false + advertise_physical_ip_on_border: + description: + - Advertise physical IP address on border switches. + type: bool + default: true + anycast_border_gateway_advertise_physical_ip: + description: + - Enable anycast border gateway to advertise physical IP. + type: bool + default: false + snmp_trap: + description: + - Enable SNMP traps. + type: bool + default: true + cdp: + description: + - Enable CDP. + type: bool + default: false + tcam_allocation: + description: + - Enable TCAM allocation. + type: bool + default: true + real_time_interface_statistics_collection: + description: + - Enable real-time interface statistics collection. + type: bool + default: false + interface_statistics_load_interval: + description: + - The interface statistics load interval in seconds. + type: int + default: 10 + greenfield_debug_flag: + description: + - The greenfield debug flag. + type: str + default: disable + choices: [ enable, disable ] + nxapi: + description: + - Enable NX-API (HTTPS). + type: bool + default: false + nxapi_https_port: + description: + - The NX-API HTTPS port (1-65535). + type: int + default: 443 + nxapi_http: + description: + - Enable NX-API HTTP. + type: bool + default: false + nxapi_http_port: + description: + - The NX-API HTTP port (1-65535). + type: int + default: 80 + bgp_authentication: + description: + - Enable BGP authentication. + type: bool + default: false + bgp_authentication_key_type: + description: + - The BGP authentication key type. + type: str + default: 3des + bgp_authentication_key: + description: + - The BGP authentication key. + type: str + default: "" + bfd: + description: + - Enable BFD globally. + type: bool + default: false + bfd_ibgp: + description: + - Enable BFD for iBGP sessions. + type: bool + default: false + bfd_authentication: + description: + - Enable BFD authentication. + type: bool + default: false + bfd_authentication_key_id: + description: + - The BFD authentication key ID. + type: int + default: 100 + bfd_authentication_key: + description: + - The BFD authentication key. + type: str + default: "" + pim_hello_authentication: + description: + - Enable PIM hello authentication. + type: bool + default: false + pim_hello_authentication_key: + description: + - The PIM hello authentication key. + type: str + default: "" + macsec: + description: + - Enable MACsec on intra-fabric links. + type: bool + default: false + macsec_cipher_suite: + description: + - The MACsec cipher suite. + type: str + default: GCM-AES-XPN-256 + macsec_key_string: + description: + - The MACsec primary key string. + type: str + default: "" + macsec_algorithm: + description: + - The MACsec algorithm. + type: str + default: AES_128_CMAC + macsec_fallback_key_string: + description: + - The MACsec fallback key string. + type: str + default: "" + macsec_fallback_algorithm: + description: + - The MACsec fallback algorithm. + type: str + default: AES_128_CMAC + macsec_report_timer: + description: + - The MACsec report timer in minutes. + type: int + default: 5 + vrf_lite_auto_config: + description: + - The VRF lite auto-configuration mode. + type: str + default: manual + vrf_lite_subnet_range: + description: + - The VRF lite subnet IP address pool. + type: str + default: "10.33.0.0/16" + vrf_lite_subnet_target_mask: + description: + - The VRF lite subnet target mask. + type: int + default: 30 + auto_unique_vrf_lite_ip_prefix: + description: + - Enable auto unique VRF lite IP prefix. + type: bool + default: false + default_queuing_policy: + description: + - Enable default queuing policy. + type: bool + default: false + aiml_qos: + description: + - Enable AI/ML QoS. + type: bool + default: false + aiml_qos_policy: + description: + - The AI/ML QoS policy. + type: str + default: 400G + dlb: + description: + - Enable dynamic load balancing. + type: bool + default: false + dlb_mode: + description: + - The DLB mode. + type: str + default: flowlet + ptp: + description: + - Enable Precision Time Protocol (PTP). + type: bool + default: false + ptp_loopback_id: + description: + - The PTP loopback ID. + type: int + default: 0 + ptp_domain_id: + description: + - The PTP domain ID. + type: int + default: 0 + private_vlan: + description: + - Enable private VLAN support. + type: bool + default: false + day0_bootstrap: + description: + - Enable day-0 bootstrap (POAP). + type: bool + default: false + local_dhcp_server: + description: + - Enable local DHCP server for bootstrap. + type: bool + default: false + dhcp_protocol_version: + description: + - The DHCP protocol version for bootstrap. + type: str + default: dhcpv4 + dhcp_start_address: + description: + - The DHCP start address for bootstrap. + type: str + default: "" + dhcp_end_address: + description: + - The DHCP end address for bootstrap. + type: str + default: "" + management_gateway: + description: + - The management gateway for bootstrap. + type: str + default: "" + management_ipv4_prefix: + description: + - The management IPv4 prefix length for bootstrap. + type: int + default: 24 + management_ipv6_prefix: + description: + - The management IPv6 prefix length for bootstrap. + type: int + default: 64 + real_time_backup: + description: + - Enable real-time backup. + type: bool + scheduled_backup: + description: + - Enable scheduled backup. + type: bool + scheduled_backup_time: + description: + - The scheduled backup time. + type: str + default: "" + nve_hold_down_timer: + description: + - The NVE hold-down timer in seconds. + type: int + default: 180 + next_generation_oam: + description: + - Enable next-generation OAM. + type: bool + default: true + strict_config_compliance_mode: + description: + - Enable strict configuration compliance mode. + type: bool + default: false + copp_policy: + description: + - The CoPP policy. + type: str + default: strict + power_redundancy_mode: + description: + - The power redundancy mode. + type: str + default: redundant + heartbeat_interval: + description: + - The heartbeat interval. + type: int + default: 190 + allow_smart_switch_onboarding: + description: + - Allow smart switch onboarding. + type: bool + default: false + aaa: + description: + - Enable AAA. + type: bool + default: false + extra_config_leaf: + description: + - Extra freeform configuration applied to leaf switches. + type: str + default: "" + extra_config_spine: + description: + - Extra freeform configuration applied to spine switches. + type: str + default: "" + extra_config_tor: + description: + - Extra freeform configuration applied to TOR switches. + type: str + default: "" + extra_config_intra_fabric_links: + description: + - Extra freeform configuration applied to intra-fabric links. + type: str + default: "" + extra_config_aaa: + description: + - Extra freeform AAA configuration. + type: str + default: "" + banner: + description: + - The fabric banner text displayed on switch login. + type: str + default: "" + ntp_server_collection: + description: + - The list of NTP server IP addresses. + type: list + elements: str + dns_collection: + description: + - The list of DNS server IP addresses. + type: list + elements: str + syslog_server_collection: + description: + - The list of syslog server IP addresses. + type: list + elements: str + syslog_server_vrf_collection: + description: + - The list of VRFs for syslog servers. + type: list + elements: str + syslog_severity_collection: + description: + - The list of syslog severity levels (0-7). + type: list + elements: int + state: + description: + - The desired state of the fabric resources on the Cisco Nexus Dashboard. + - Use O(state=merged) to create new fabrics and update existing ones as defined in the configuration. + Resources on ND that are not specified in the configuration will be left unchanged. + - Use O(state=replaced) to replace the fabric configuration specified in the configuration. + Any settings not explicitly provided will revert to their defaults. + - Use O(state=overridden) to enforce the configuration as the single source of truth. + Any fabric existing on ND but not present in the configuration will be deleted. Use with extra caution. + - Use O(state=deleted) to remove the fabrics specified in the configuration from the Cisco Nexus Dashboard. + type: str + default: merged + choices: [ merged, replaced, overridden, deleted ] +extends_documentation_fragment: +- cisco.nd.modules +- cisco.nd.check_mode +notes: +- This module is only supported on Nexus Dashboard having version 4.1.0 or higher. +- Only eBGP VXLAN fabric type (C(vxlanEbgp)) is supported by this module. +- When using O(state=replaced) with only required fields, all optional management settings revert to their defaults. +- The O(config.management.bgp_asn) field is optional when O(config.management.bgp_asn_auto_allocation) is C(true). +- The O(config.management.bgp_asn) field is required when O(config.management.bgp_asn_auto_allocation) is C(false). +- O(config.management.site_id) defaults to the value of O(config.management.bgp_asn) if not provided. +- The default O(config.management.vpc_peer_keep_alive_option) for eBGP fabrics is C(management), unlike iBGP fabrics. +""" + +EXAMPLES = r""" +- name: Create an eBGP VXLAN fabric using state merged (with auto ASN allocation) + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: my_ebgp_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65535" + bgp_as_mode: multiAS + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00aa" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: false + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: disable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: result + +- name: Create an eBGP VXLAN fabric with a static BGP ASN + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: my_ebgp_fabric_static + category: fabric + management: + type: vxlanEbgp + bgp_asn: "65001" + bgp_asn_auto_allocation: false + site_id: "65001" + bgp_as_mode: multiAS + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00aa" + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + register: result + +- name: Update specific fields on an existing eBGP fabric using state merged (partial update) + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: my_ebgp_fabric + category: fabric + management: + bgp_asn_range: "65100-65199" + anycast_gateway_mac: "2020.0000.00bb" + performance_monitoring: true + register: result + +- name: Create or fully replace an eBGP VXLAN fabric using state replaced + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: my_ebgp_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65004" + bgp_asn_auto_allocation: false + site_id: "65004" + bgp_as_mode: multiAS + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" + performance_monitoring: true + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" + rendezvous_point_count: 3 + rendezvous_point_loopback_id: 253 + vpc_peer_link_vlan: "3700" + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 300 + vpc_delay_restore_timer: 120 + vpc_peer_link_port_channel_id: "600" + advertise_physical_ip: true + vpc_domain_id_range: "1-800" + fabric_mtu: 9000 + l2_host_interface_mtu: 9000 + tenant_dhcp: false + snmp_trap: false + anycast_border_gateway_advertise_physical_ip: true + greenfield_debug_flag: disable + tcam_allocation: false + real_time_interface_statistics_collection: true + interface_statistics_load_interval: 30 + bgp_loopback_ip_range: "10.22.0.0/22" + nve_loopback_ip_range: "10.23.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.252.0/24" + intra_fabric_subnet_range: "10.24.0.0/16" + l2_vni_range: "40000-59000" + l3_vni_range: "60000-69000" + network_vlan_range: "2400-3099" + vrf_vlan_range: "2100-2399" + banner: "^ Managed by Ansible ^" + register: result + +- name: Replace fabric with only required fields (all optional settings revert to defaults) + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: my_ebgp_fabric + category: fabric + management: + type: vxlanEbgp + bgp_asn: "65004" + bgp_asn_auto_allocation: false + site_id: "65004" + banner: "^ Managed by Ansible ^" + register: result + +- name: Enforce exact fabric inventory using state overridden (deletes unlisted fabrics) + cisco.nd.nd_manage_fabric_ebgp: + state: overridden + config: + - name: fabric_east + category: fabric + location: + latitude: 40.7128 + longitude: -74.0060 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65010" + bgp_asn_auto_allocation: false + site_id: "65010" + bgp_as_mode: multiAS + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0010" + replication_mode: multicast + multicast_group_subnet: "239.1.10.0/25" + bgp_loopback_ip_range: "10.10.0.0/22" + nve_loopback_ip_range: "10.11.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.10.0/24" + intra_fabric_subnet_range: "10.12.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + - name: fabric_west + category: fabric + location: + latitude: 34.0522 + longitude: -118.2437 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65020" + bgp_asn_auto_allocation: false + site_id: "65020" + bgp_as_mode: multiAS + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0020" + replication_mode: multicast + multicast_group_subnet: "239.1.20.0/25" + bgp_loopback_ip_range: "10.20.0.0/22" + nve_loopback_ip_range: "10.21.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.20.0/24" + intra_fabric_subnet_range: "10.22.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + register: result + +- name: Delete a specific eBGP fabric using state deleted + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: my_ebgp_fabric + register: result + +- name: Delete multiple eBGP fabrics in a single task + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: fabric_east + - name: fabric_west + - name: fabric_old + register: result +""" + +RETURN = r""" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.nd.plugins.module_utils.nd import nd_argument_spec +from ansible_collections.cisco.nd.plugins.module_utils.nd_state_machine import NDStateMachine +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ebgp import FabricEbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.manage_fabric_ebgp import ManageEbgpFabricOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.common.exceptions import NDStateMachineError + + +def main(): + argument_spec = nd_argument_spec() + argument_spec.update(FabricEbgpModel.get_argument_spec()) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + try: + # Initialize StateMachine + nd_state_machine = NDStateMachine( + module=module, + model_orchestrator=ManageEbgpFabricOrchestrator, + ) + + # Manage state + nd_state_machine.manage_state() + + module.exit_json(**nd_state_machine.output.format()) + + except NDStateMachineError as e: + module.fail_json(msg=str(e)) + except Exception as e: + module.fail_json(msg=f"Module execution failed: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py new file mode 100644 index 00000000..a2ab33df --- /dev/null +++ b/plugins/modules/nd_manage_fabric_external.py @@ -0,0 +1,524 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: nd_manage_fabric_external +version_added: "1.4.0" +short_description: Manage External Connectivity fabrics on Cisco Nexus Dashboard +description: +- Manage External Connectivity fabrics on Cisco Nexus Dashboard (ND). +- It supports creating, updating, replacing, and deleting External Connectivity fabrics. +author: +- Mike Wiebe (@mwiebe) +options: + config: + description: + - The list of External Connectivity fabrics to configure. + type: list + elements: dict + suboptions: + name: + description: + - The name of the fabric. + - Only letters, numbers, underscores, and hyphens are allowed. + - The O(config.name) must be defined when creating, updating or deleting a fabric. + type: str + required: true + category: + description: + - The resource category. + type: str + default: fabric + location: + description: + - The geographic location of the fabric. + type: dict + suboptions: + latitude: + description: + - Latitude coordinate of the fabric location (-90 to 90). + type: float + required: true + longitude: + description: + - Longitude coordinate of the fabric location (-180 to 180). + type: float + required: true + license_tier: + description: + - The license tier for the fabric. + type: str + default: premier + choices: [ essentials, premier ] + alert_suspend: + description: + - The alert suspension state for the fabric. + type: str + default: disabled + choices: [ enabled, disabled ] + telemetry_collection: + description: + - Enable telemetry collection for the fabric. + type: bool + default: false + telemetry_collection_type: + description: + - The telemetry collection type. + type: str + default: outOfBand + telemetry_streaming_protocol: + description: + - The telemetry streaming protocol. + type: str + default: ipv4 + telemetry_source_interface: + description: + - The telemetry source interface. + type: str + default: "" + telemetry_source_vrf: + description: + - The telemetry source VRF. + type: str + default: "" + security_domain: + description: + - The security domain associated with the fabric. + type: str + default: all + management: + description: + - The External Connectivity management configuration for the fabric. + type: dict + suboptions: + type: + description: + - The fabric management type. Must be C(externalConnectivity) for External Connectivity fabrics. + type: str + default: externalConnectivity + choices: [ externalConnectivity ] + bgp_asn: + description: + - The BGP Autonomous System Number for the fabric. + - Must be a numeric value between 1 and 4294967295 or dotted notation 1-65535.0-65535. + type: str + required: true + aaa: + description: + - Enable AAA. + type: bool + default: false + advanced_ssh_option: + description: + - Enable advanced SSH option. + type: bool + default: false + allow_same_loopback_ip_on_switches: + description: + - Allow same loopback IP on switches. + type: bool + default: false + allow_smart_switch_onboarding: + description: + - Allow smart switch onboarding. + type: bool + default: false + cdp: + description: + - Enable CDP. + type: bool + default: false + copp_policy: + description: + - The CoPP policy. + type: str + default: manual + choices: [ dense, lenient, moderate, strict, manual ] + create_bgp_config: + description: + - Create BGP configuration. + type: bool + default: true + day0_bootstrap: + description: + - Enable day-0 bootstrap (POAP). + type: bool + default: false + day0_plug_and_play: + description: + - Enable day-0 plug and play. + type: bool + default: false + dhcp_end_address: + description: + - The DHCP end address for bootstrap. + type: str + default: "" + dhcp_protocol_version: + description: + - The DHCP protocol version for bootstrap. + type: str + default: dhcpv4 + choices: [ dhcpv4, dhcpv6 ] + dhcp_start_address: + description: + - The DHCP start address for bootstrap. + type: str + default: "" + dns_collection: + description: + - The list of DNS server IP addresses. + type: list + elements: str + dns_vrf_collection: + description: + - The list of VRFs for DNS servers. + type: list + elements: str + domain_name: + description: + - The domain name. + type: str + default: "" + enable_dpu_pinning: + description: + - Enable DPU pinning. + type: bool + default: false + extra_config_aaa: + description: + - Extra freeform AAA configuration. + type: str + default: "" + extra_config_fabric: + description: + - Extra freeform fabric configuration. + type: str + default: "" + extra_config_nxos_bootstrap: + description: + - Extra NX-OS bootstrap configuration. + type: str + default: "" + extra_config_xe_bootstrap: + description: + - Extra XE bootstrap configuration. + type: str + default: "" + inband_day0_bootstrap: + description: + - Enable inband day-0 bootstrap. + type: bool + default: false + inband_management: + description: + - Enable in-band management. + type: bool + default: false + interface_statistics_load_interval: + description: + - The interface statistics load interval in seconds. + type: int + default: 10 + local_dhcp_server: + description: + - Enable local DHCP server for bootstrap. + type: bool + default: false + management_gateway: + description: + - The management gateway for bootstrap. + type: str + default: "" + management_ipv4_prefix: + description: + - The management IPv4 prefix length for bootstrap. + type: int + default: 24 + management_ipv6_prefix: + description: + - The management IPv6 prefix length for bootstrap. + type: int + default: 64 + monitored_mode: + description: + - Enable monitored mode. + type: bool + default: false + mpls_handoff: + description: + - Enable MPLS handoff. + type: bool + default: false + mpls_loopback_identifier: + description: + - The MPLS loopback identifier. + type: int + mpls_loopback_ip_range: + description: + - The MPLS loopback IP address pool. + type: str + default: "10.102.0.0/25" + nxapi: + description: + - Enable NX-API (HTTPS). + type: bool + default: false + nxapi_http: + description: + - Enable NX-API HTTP. + type: bool + default: false + nxapi_http_port: + description: + - The NX-API HTTP port (1-65535). + type: int + default: 80 + nxapi_https_port: + description: + - The NX-API HTTPS port (1-65535). + type: int + default: 443 + performance_monitoring: + description: + - Enable performance monitoring. + type: bool + default: false + power_redundancy_mode: + description: + - The power redundancy mode. + type: str + default: redundant + choices: [ redundant, combined, inputSrcRedundant ] + ptp: + description: + - Enable Precision Time Protocol (PTP). + type: bool + default: false + ptp_domain_id: + description: + - The PTP domain ID. + type: int + default: 0 + ptp_loopback_id: + description: + - The PTP loopback ID. + type: int + default: 0 + real_time_backup: + description: + - Enable real-time backup. + type: bool + real_time_interface_statistics_collection: + description: + - Enable real-time interface statistics collection. + type: bool + default: false + scheduled_backup: + description: + - Enable scheduled backup. + type: bool + scheduled_backup_time: + description: + - The scheduled backup time. + type: str + default: "" + snmp_trap: + description: + - Enable SNMP traps. + type: bool + default: true + sub_interface_dot1q_range: + description: + - The sub-interface 802.1q range. + type: str + default: "2-511" + state: + description: + - The desired state of the fabric resources on the Cisco Nexus Dashboard. + - Use O(state=merged) to create new fabrics and update existing ones as defined in the configuration. + Resources on ND that are not specified in the configuration will be left unchanged. + - Use O(state=replaced) to replace the fabric configuration specified in the configuration. + Any settings not explicitly provided will revert to their defaults. + - Use O(state=overridden) to enforce the configuration as the single source of truth. + Any fabric existing on ND but not present in the configuration will be deleted. Use with extra caution. + - Use O(state=deleted) to remove the fabrics specified in the configuration from the Cisco Nexus Dashboard. + type: str + default: merged + choices: [ merged, replaced, overridden, deleted ] +extends_documentation_fragment: +- cisco.nd.modules +- cisco.nd.check_mode +notes: +- This module is only supported on Nexus Dashboard having version 4.1.0 or higher. +- Only External Connectivity fabric type (C(externalConnectivity)) is supported by this module. +- When using O(state=replaced) with only required fields, all optional management settings revert to their defaults. +- The O(config.management.bgp_asn) field is required when creating a fabric. +""" + +EXAMPLES = r""" +- name: Create an External Connectivity fabric using state merged + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - name: my_ext_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65001" + copp_policy: manual + create_bgp_config: true + cdp: false + snmp_trap: true + nxapi: false + nxapi_http: false + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: false + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + sub_interface_dot1q_range: "2-511" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: result + +- name: Update specific fields on an existing fabric using state merged (partial update) + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - name: my_ext_fabric + category: fabric + management: + bgp_asn: "65002" + performance_monitoring: true + snmp_trap: false + register: result + +- name: Create or fully replace an External Connectivity fabric using state replaced + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: my_ext_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65004" + copp_policy: strict + create_bgp_config: true + cdp: true + snmp_trap: false + nxapi: true + nxapi_http: true + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: true + real_time_interface_statistics_collection: true + interface_statistics_load_interval: 30 + sub_interface_dot1q_range: "2-511" + power_redundancy_mode: combined + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: result + +- name: Replace fabric with only required fields (all optional settings revert to defaults) + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: my_ext_fabric + category: fabric + management: + type: externalConnectivity + bgp_asn: "65004" + register: result + +- name: Delete a specific fabric using state deleted + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: my_ext_fabric + register: result + +- name: Delete multiple fabrics in a single task + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: ext_fabric_east + - name: ext_fabric_west + register: result +""" + +RETURN = r""" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.nd.plugins.module_utils.nd import nd_argument_spec +from ansible_collections.cisco.nd.plugins.module_utils.nd_state_machine import NDStateMachine +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_external import FabricExternalConnectivityModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.manage_fabric_external import ManageExternalFabricOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.common.exceptions import NDStateMachineError + + +def main(): + argument_spec = nd_argument_spec() + argument_spec.update(FabricExternalConnectivityModel.get_argument_spec()) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + try: + # Initialize StateMachine + nd_state_machine = NDStateMachine( + module=module, + model_orchestrator=ManageExternalFabricOrchestrator, + ) + + # Manage state + nd_state_machine.manage_state() + + module.exit_json(**nd_state_machine.output.format()) + + except NDStateMachineError as e: + module.fail_json(msg=str(e)) + except Exception as e: + module.fail_json(msg=f"Module execution failed: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py new file mode 100644 index 00000000..9d857fc6 --- /dev/null +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -0,0 +1,1393 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: nd_manage_fabric_ibgp +version_added: "1.4.0" +short_description: Manage iBGP VXLAN fabrics on Cisco Nexus Dashboard +description: +- Manage iBGP VXLAN fabrics on Cisco Nexus Dashboard (ND). +- It supports creating, updating, replacing, and deleting iBGP VXLAN fabrics. +author: +- Mike Wiebe (@mwiebe) +options: + config: + description: + - The list of iBGP VXLAN fabrics to configure. + type: list + elements: dict + suboptions: + name: + description: + - The name of the fabric. + - Only letters, numbers, underscores, and hyphens are allowed. + - The O(config.name) must be defined when creating, updating or deleting a fabric. + type: str + required: true + category: + description: + - The resource category. + type: str + default: fabric + location: + description: + - The geographic location of the fabric. + type: dict + suboptions: + latitude: + description: + - Latitude coordinate of the fabric location (-90 to 90). + type: float + required: true + longitude: + description: + - Longitude coordinate of the fabric location (-180 to 180). + type: float + required: true + license_tier: + description: + - The license tier for the fabric. + type: str + default: premier + choices: [ essentials, premier ] + alert_suspend: + description: + - The alert suspension state for the fabric. + type: str + default: disabled + choices: [ enabled, disabled ] + telemetry_collection: + description: + - Enable telemetry collection for the fabric. + type: bool + default: false + telemetry_collection_type: + description: + - The telemetry collection type. + type: str + default: outOfBand + telemetry_streaming_protocol: + description: + - The telemetry streaming protocol. + type: str + default: ipv4 + telemetry_source_interface: + description: + - The telemetry source interface. + type: str + default: "" + telemetry_source_vrf: + description: + - The telemetry source VRF. + type: str + default: "" + security_domain: + description: + - The security domain associated with the fabric. + type: str + default: all + management: + description: + - The iBGP VXLAN management configuration for the fabric. + type: dict + suboptions: + type: + description: + - The fabric management type. Must be C(vxlanIbgp) for iBGP VXLAN fabrics. + type: str + default: vxlanIbgp + choices: [ vxlanIbgp ] + bgp_asn: + description: + - The BGP Autonomous System Number for the fabric. + - Must be a numeric value between 1 and 4294967295. + type: str + required: true + site_id: + description: + - The site identifier for the fabric. + - Must be a numeric value between 1 and 65535. + - Defaults to the value of O(config.management.bgp_asn) if not provided. + type: str + default: "" + target_subnet_mask: + description: + - The target subnet mask for intra-fabric links. + type: int + default: 30 + anycast_gateway_mac: + description: + - The anycast gateway MAC address in xxxx.xxxx.xxxx format. + type: str + default: 2020.0000.00aa + replication_mode: + description: + - The multicast replication mode. + type: str + default: multicast + choices: [ multicast, ingress ] + multicast_group_subnet: + description: + - The multicast group subnet. + type: str + default: "239.1.1.0/25" + auto_generate_multicast_group_address: + description: + - Automatically generate multicast group addresses. + type: bool + default: false + underlay_multicast_group_address_limit: + description: + - The underlay multicast group address limit (1-255). + type: int + default: 128 + tenant_routed_multicast: + description: + - Enable tenant routed multicast. + type: bool + default: false + rendezvous_point_count: + description: + - The number of rendezvous points (1-4). + type: int + default: 2 + rendezvous_point_loopback_id: + description: + - The rendezvous point loopback interface ID (0-1023). + type: int + default: 254 + overlay_mode: + description: + - The overlay configuration mode. + type: str + default: cli + choices: [ cli, config-profile ] + link_state_routing_protocol: + description: + - The underlay link-state routing protocol. + type: str + default: ospf + choices: [ ospf, isis ] + ospf_area_id: + description: + - The OSPF area ID. + type: str + default: "0.0.0.0" + fabric_interface_type: + description: + - The fabric interface type. + type: str + default: p2p + bgp_loopback_id: + description: + - The BGP loopback interface ID (0-1023). + type: int + default: 0 + nve_loopback_id: + description: + - The NVE loopback interface ID (0-1023). + type: int + default: 1 + route_reflector_count: + description: + - The number of BGP route reflectors (1-4). + type: int + default: 2 + bgp_loopback_ip_range: + description: + - The BGP loopback IP address pool. + type: str + default: "10.2.0.0/22" + nve_loopback_ip_range: + description: + - The NVE loopback IP address pool. + type: str + default: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: + description: + - The anycast rendezvous point IP address pool. + type: str + default: "10.254.254.0/24" + intra_fabric_subnet_range: + description: + - The intra-fabric subnet IP address pool. + type: str + default: "10.4.0.0/16" + router_id_range: + description: + - The router ID IP address pool. + type: str + default: "10.2.0.0/23" + l2_vni_range: + description: + - The Layer 2 VNI range. + type: str + default: "30000-49000" + l3_vni_range: + description: + - The Layer 3 VNI range. + type: str + default: "50000-59000" + network_vlan_range: + description: + - The network VLAN range. + type: str + default: "2300-2999" + vrf_vlan_range: + description: + - The VRF VLAN range. + type: str + default: "2000-2299" + sub_interface_dot1q_range: + description: + - The sub-interface 802.1q range. + type: str + default: "2-511" + service_network_vlan_range: + description: + - The service network VLAN range. + type: str + default: "3000-3199" + l3_vni_no_vlan_default_option: + description: + - Enable L3 VNI no-VLAN default option. + type: bool + default: false + fabric_mtu: + description: + - The fabric MTU size (1500-9216). + type: int + default: 9216 + l2_host_interface_mtu: + description: + - The L2 host interface MTU size (1500-9216). + type: int + default: 9216 + vpc_domain_id_range: + description: + - The vPC domain ID range. + type: str + default: "1-1000" + vpc_peer_link_vlan: + description: + - The vPC peer link VLAN ID. + type: str + default: "3600" + vpc_peer_link_enable_native_vlan: + description: + - Enable native VLAN on the vPC peer link. + type: bool + default: false + vpc_peer_keep_alive_option: + description: + - The vPC peer keep-alive option. + type: str + default: loopback + vpc_auto_recovery_timer: + description: + - The vPC auto recovery timer in seconds (240-3600). + type: int + default: 360 + vpc_delay_restore_timer: + description: + - The vPC delay restore timer in seconds (1-3600). + type: int + default: 150 + vpc_peer_link_port_channel_id: + description: + - The vPC peer link port-channel ID. + type: str + default: "500" + vpc_ipv6_neighbor_discovery_sync: + description: + - Enable vPC IPv6 neighbor discovery synchronization. + type: bool + default: true + vpc_layer3_peer_router: + description: + - Enable vPC layer-3 peer router. + type: bool + default: true + vpc_tor_delay_restore_timer: + description: + - The vPC TOR delay restore timer. + type: int + default: 30 + fabric_vpc_domain_id: + description: + - Enable fabric vPC domain ID. + type: bool + default: false + shared_vpc_domain_id: + description: + - The shared vPC domain ID. + type: int + default: 1 + fabric_vpc_qos: + description: + - Enable fabric vPC QoS. + type: bool + default: false + fabric_vpc_qos_policy_name: + description: + - The fabric vPC QoS policy name. + type: str + default: spine_qos_for_fabric_vpc_peering + enable_peer_switch: + description: + - Enable peer switch. + type: bool + default: false + vrf_template: + description: + - The VRF template name. + type: str + default: Default_VRF_Universal + network_template: + description: + - The network template name. + type: str + default: Default_Network_Universal + vrf_extension_template: + description: + - The VRF extension template name. + type: str + default: Default_VRF_Extension_Universal + network_extension_template: + description: + - The network extension template name. + type: str + default: Default_Network_Extension_Universal + performance_monitoring: + description: + - Enable performance monitoring. + type: bool + default: false + tenant_dhcp: + description: + - Enable tenant DHCP. + type: bool + default: true + advertise_physical_ip: + description: + - Advertise physical IP address for NVE loopback. + type: bool + default: false + advertise_physical_ip_on_border: + description: + - Advertise physical IP address on border switches. + type: bool + default: true + anycast_border_gateway_advertise_physical_ip: + description: + - Enable anycast border gateway to advertise physical IP. + type: bool + default: false + snmp_trap: + description: + - Enable SNMP traps. + type: bool + default: true + cdp: + description: + - Enable CDP. + type: bool + default: false + tcam_allocation: + description: + - Enable TCAM allocation. + type: bool + default: true + real_time_interface_statistics_collection: + description: + - Enable real-time interface statistics collection. + type: bool + default: false + interface_statistics_load_interval: + description: + - The interface statistics load interval in seconds. + type: int + default: 10 + greenfield_debug_flag: + description: + - The greenfield debug flag. + type: str + default: enable + nxapi: + description: + - Enable NX-API (HTTPS). + type: bool + default: false + nxapi_https_port: + description: + - The NX-API HTTPS port (1-65535). + type: int + default: 443 + nxapi_http: + description: + - Enable NX-API HTTP. + type: bool + default: true + nxapi_http_port: + description: + - The NX-API HTTP port (1-65535). + type: int + default: 80 + bgp_authentication: + description: + - Enable BGP authentication. + type: bool + default: false + bgp_authentication_key_type: + description: + - The BGP authentication key type. + type: str + default: 3des + bgp_authentication_key: + description: + - The BGP authentication key. + type: str + default: "" + bfd: + description: + - Enable BFD globally. + type: bool + default: false + bfd_ibgp: + description: + - Enable BFD for iBGP sessions. + type: bool + default: false + bfd_ospf: + description: + - Enable BFD for OSPF. + type: bool + default: false + bfd_isis: + description: + - Enable BFD for IS-IS. + type: bool + default: false + bfd_pim: + description: + - Enable BFD for PIM. + type: bool + default: false + bfd_authentication: + description: + - Enable BFD authentication. + type: bool + default: false + bfd_authentication_key_id: + description: + - The BFD authentication key ID. + type: int + default: 100 + bfd_authentication_key: + description: + - The BFD authentication key. + type: str + default: "" + ospf_authentication: + description: + - Enable OSPF authentication. + type: bool + default: false + ospf_authentication_key_id: + description: + - The OSPF authentication key ID. + type: int + default: 127 + ospf_authentication_key: + description: + - The OSPF authentication key. + type: str + default: "" + pim_hello_authentication: + description: + - Enable PIM hello authentication. + type: bool + default: false + pim_hello_authentication_key: + description: + - The PIM hello authentication key. + type: str + default: "" + isis_level: + description: + - The IS-IS level. + type: str + default: level-2 + isis_area_number: + description: + - The IS-IS area number. + type: str + default: "0001" + isis_point_to_point: + description: + - Enable IS-IS point-to-point. + type: bool + default: true + isis_authentication: + description: + - Enable IS-IS authentication. + type: bool + default: false + isis_authentication_keychain_name: + description: + - The IS-IS authentication keychain name. + type: str + default: "" + isis_authentication_keychain_key_id: + description: + - The IS-IS authentication keychain key ID. + type: int + default: 127 + isis_authentication_key: + description: + - The IS-IS authentication key. + type: str + default: "" + isis_overload: + description: + - Enable IS-IS overload bit. + type: bool + default: true + isis_overload_elapse_time: + description: + - The IS-IS overload elapse time in seconds. + type: int + default: 60 + macsec: + description: + - Enable MACsec on intra-fabric links. + type: bool + default: false + macsec_cipher_suite: + description: + - The MACsec cipher suite. + type: str + default: GCM-AES-XPN-256 + macsec_key_string: + description: + - The MACsec primary key string. + type: str + default: "" + macsec_algorithm: + description: + - The MACsec algorithm. + type: str + default: AES_128_CMAC + macsec_fallback_key_string: + description: + - The MACsec fallback key string. + type: str + default: "" + macsec_fallback_algorithm: + description: + - The MACsec fallback algorithm. + type: str + default: AES_128_CMAC + macsec_report_timer: + description: + - The MACsec report timer. + type: int + default: 5 + vrf_lite_macsec: + description: + - Enable MACsec on VRF lite links. + type: bool + default: false + quantum_key_distribution: + description: + - Enable quantum key distribution. + type: bool + default: false + quantum_key_distribution_profile_name: + description: + - The quantum key distribution profile name. + type: str + default: "" + key_management_entity_server_ip: + description: + - The key management entity server IP address. + type: str + default: "" + key_management_entity_server_port: + description: + - The key management entity server port. + type: int + default: 0 + trustpoint_label: + description: + - The trustpoint label. + type: str + default: "" + vrf_lite_auto_config: + description: + - The VRF lite auto-configuration mode. + type: str + default: manual + vrf_lite_subnet_range: + description: + - The VRF lite subnet IP address pool. + type: str + default: "10.33.0.0/16" + vrf_lite_subnet_target_mask: + description: + - The VRF lite subnet target mask. + type: int + default: 30 + vrf_lite_ipv6_subnet_range: + description: + - The VRF lite IPv6 subnet range. + type: str + default: "fd00::a33:0/112" + vrf_lite_ipv6_subnet_target_mask: + description: + - The VRF lite IPv6 subnet target mask (112-128). + type: int + default: 126 + auto_unique_vrf_lite_ip_prefix: + description: + - Enable auto unique VRF lite IP prefix. + type: bool + default: false + auto_symmetric_vrf_lite: + description: + - Enable auto symmetric VRF lite. + type: bool + default: false + auto_vrf_lite_default_vrf: + description: + - Enable auto VRF lite for the default VRF. + type: bool + default: false + auto_symmetric_default_vrf: + description: + - Enable auto symmetric default VRF. + type: bool + default: false + per_vrf_loopback_auto_provision: + description: + - Enable per-VRF loopback auto-provisioning. + type: bool + default: false + per_vrf_loopback_ip_range: + description: + - The per-VRF loopback IP address pool. + type: str + default: "10.5.0.0/22" + per_vrf_loopback_auto_provision_ipv6: + description: + - Enable per-VRF loopback auto-provisioning for IPv6. + type: bool + default: false + per_vrf_loopback_ipv6_range: + description: + - The per-VRF loopback IPv6 address pool. + type: str + default: "fd00::a05:0/112" + underlay_ipv6: + description: + - Enable IPv6 underlay. + type: bool + default: false + ipv6_multicast_group_subnet: + description: + - The IPv6 multicast group subnet. + type: str + default: "ff1e::/121" + tenant_routed_multicast_ipv6: + description: + - Enable tenant routed multicast for IPv6. + type: bool + default: false + ipv6_link_local: + description: + - Enable IPv6 link-local addressing. + type: bool + default: true + ipv6_subnet_target_mask: + description: + - The IPv6 subnet target mask. + type: int + default: 126 + ipv6_subnet_range: + description: + - The IPv6 subnet range. + type: str + default: "fd00::a04:0/112" + bgp_loopback_ipv6_range: + description: + - The BGP loopback IPv6 address pool. + type: str + default: "fd00::a02:0/119" + nve_loopback_ipv6_range: + description: + - The NVE loopback IPv6 address pool. + type: str + default: "fd00::a03:0/118" + ipv6_anycast_rendezvous_point_ip_range: + description: + - The IPv6 anycast rendezvous point IP address pool. + type: str + default: "fd00::254:254:0/118" + auto_bgp_neighbor_description: + description: + - Enable automatic BGP neighbor description. + type: bool + default: true + ibgp_peer_template: + description: + - The iBGP peer template name. + type: str + default: "" + leaf_ibgp_peer_template: + description: + - The leaf iBGP peer template name. + type: str + default: "" + link_state_routing_tag: + description: + - The link state routing tag. + type: str + default: UNDERLAY + static_underlay_ip_allocation: + description: + - Enable static underlay IP allocation. + type: bool + default: false + security_group_tag: + description: + - Enable Security Group Tag (SGT) support. + type: bool + default: false + security_group_tag_prefix: + description: + - The SGT prefix. + type: str + default: SG_ + security_group_tag_mac_segmentation: + description: + - Enable SGT MAC segmentation. + type: bool + default: false + security_group_tag_id_range: + description: + - The SGT ID range. + type: str + default: "10000-14000" + security_group_tag_preprovision: + description: + - Enable SGT pre-provisioning. + type: bool + default: false + security_group_status: + description: + - The security group status. + type: str + default: enabled + default_queuing_policy: + description: + - Enable default queuing policy. + type: bool + default: false + aiml_qos: + description: + - Enable AI/ML QoS. + type: bool + default: false + aiml_qos_policy: + description: + - The AI/ML QoS policy. + type: str + default: 400G + dlb: + description: + - Enable dynamic load balancing. + type: bool + default: false + dlb_mode: + description: + - The DLB mode. + type: str + default: flowlet + ptp: + description: + - Enable Precision Time Protocol (PTP). + type: bool + default: false + ptp_loopback_id: + description: + - The PTP loopback ID. + type: int + default: 0 + ptp_domain_id: + description: + - The PTP domain ID. + type: int + default: 0 + stp_root_option: + description: + - The STP root option. + type: str + default: mst + stp_vlan_range: + description: + - The STP VLAN range. + type: str + default: "" + mst_instance_range: + description: + - The MST instance range. + type: str + default: "0-3,5,7-9" + stp_bridge_priority: + description: + - The STP bridge priority. + type: int + default: 0 + mpls_handoff: + description: + - Enable MPLS handoff. + type: bool + default: false + mpls_loopback_identifier: + description: + - The MPLS loopback identifier. + type: int + default: 101 + mpls_loopback_ip_range: + description: + - The MPLS loopback IP address pool. + type: str + default: "10.101.0.0/25" + private_vlan: + description: + - Enable private VLAN support. + type: bool + default: false + ip_service_level_agreement_id_range: + description: + - The IP SLA ID range. + type: str + default: "10000-19999" + object_tracking_number_range: + description: + - The object tracking number range. + type: str + default: "100-299" + route_map_sequence_number_range: + description: + - The route map sequence number range. + type: str + default: "1-65534" + day0_bootstrap: + description: + - Enable day-0 bootstrap (POAP). + type: bool + default: false + local_dhcp_server: + description: + - Enable local DHCP server for bootstrap. + type: bool + default: false + dhcp_protocol_version: + description: + - The DHCP protocol version for bootstrap. + type: str + default: dhcpv4 + dhcp_start_address: + description: + - The DHCP start address for bootstrap. + type: str + default: "" + dhcp_end_address: + description: + - The DHCP end address for bootstrap. + type: str + default: "" + management_gateway: + description: + - The management gateway for bootstrap. + type: str + default: "" + management_ipv4_prefix: + description: + - The management IPv4 prefix length for bootstrap. + type: int + default: 24 + management_ipv6_prefix: + description: + - The management IPv6 prefix length for bootstrap. + type: int + default: 64 + real_time_backup: + description: + - Enable real-time backup. + type: bool + default: false + scheduled_backup: + description: + - Enable scheduled backup. + type: bool + default: false + scheduled_backup_time: + description: + - The scheduled backup time. + type: str + default: "" + nve_hold_down_timer: + description: + - The NVE hold-down timer in seconds. + type: int + default: 180 + next_generation_oam: + description: + - Enable next-generation OAM. + type: bool + default: true + strict_config_compliance_mode: + description: + - Enable strict configuration compliance mode. + type: bool + default: false + copp_policy: + description: + - The CoPP policy. + type: str + default: dense + power_redundancy_mode: + description: + - The power redundancy mode. + type: str + default: redundant + host_interface_admin_state: + description: + - Enable host interface admin state. + type: bool + default: true + heartbeat_interval: + description: + - The heartbeat interval. + type: int + default: 190 + policy_based_routing: + description: + - Enable policy-based routing. + type: bool + default: false + brownfield_network_name_format: + description: + - The brownfield network name format. + type: str + default: "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + brownfield_skip_overlay_network_attachments: + description: + - Skip brownfield overlay network attachments. + type: bool + default: false + allow_smart_switch_onboarding: + description: + - Allow smart switch onboarding. + type: bool + default: false + aaa: + description: + - Enable AAA. + type: bool + default: false + extra_config_leaf: + description: + - Extra freeform configuration applied to leaf switches. + type: str + default: "" + extra_config_spine: + description: + - Extra freeform configuration applied to spine switches. + type: str + default: "" + extra_config_tor: + description: + - Extra freeform configuration applied to TOR switches. + type: str + default: "" + extra_config_intra_fabric_links: + description: + - Extra freeform configuration applied to intra-fabric links. + type: str + default: "" + extra_config_aaa: + description: + - Extra freeform AAA configuration. + type: str + default: "" + banner: + description: + - The fabric banner text displayed on switch login. + type: str + default: "" + ntp_server_collection: + description: + - The list of NTP server IP addresses. + type: list + elements: str + dns_collection: + description: + - The list of DNS server IP addresses. + type: list + elements: str + syslog_server_collection: + description: + - The list of syslog server IP addresses. + type: list + elements: str + syslog_server_vrf_collection: + description: + - The list of VRFs for syslog servers. + type: list + elements: str + syslog_severity_collection: + description: + - The list of syslog severity levels (0-7). + type: list + elements: int + state: + description: + - The desired state of the fabric resources on the Cisco Nexus Dashboard. + - Use O(state=merged) to create new fabrics and update existing ones as defined in the configuration. + Resources on ND that are not specified in the configuration will be left unchanged. + - Use O(state=replaced) to replace the fabric configuration specified in the configuration. + Any settings not explicitly provided will revert to their defaults. + - Use O(state=overridden) to enforce the configuration as the single source of truth. + Any fabric existing on ND but not present in the configuration will be deleted. Use with extra caution. + - Use O(state=deleted) to remove the fabrics specified in the configuration from the Cisco Nexus Dashboard. + type: str + default: merged + choices: [ merged, replaced, overridden, deleted ] +extends_documentation_fragment: +- cisco.nd.modules +- cisco.nd.check_mode +notes: +- This module is only supported on Nexus Dashboard having version 4.1.0 or higher. +- Only iBGP VXLAN fabric type (C(vxlanIbgp)) is supported by this module. +- When using O(state=replaced) with only required fields, all optional management settings revert to their defaults. +- The O(config.management.bgp_asn) field is required when creating a fabric. +- O(config.management.site_id) defaults to the value of O(config.management.bgp_asn) if not provided. +""" + +EXAMPLES = r""" +- name: Create an iBGP VXLAN fabric using state merged + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - name: my_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65001" + site_id: "65001" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00aa" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: enable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: result + +- name: Update specific fields on an existing fabric using state merged (partial update) + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - name: my_fabric + category: fabric + management: + bgp_asn: "65002" + site_id: "65002" + anycast_gateway_mac: "2020.0000.00bb" + performance_monitoring: true + register: result + +- name: Create or fully replace an iBGP VXLAN fabric using state replaced + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: my_fabric + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65004" + site_id: "65004" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" + performance_monitoring: true + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 3 + rendezvous_point_loopback_id: 253 + vpc_peer_link_vlan: "3700" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 300 + vpc_delay_restore_timer: 120 + vpc_peer_link_port_channel_id: "600" + vpc_ipv6_neighbor_discovery_sync: false + advertise_physical_ip: true + vpc_domain_id_range: "1-800" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9000 + l2_host_interface_mtu: 9000 + tenant_dhcp: false + nxapi: false + nxapi_https_port: 443 + nxapi_http: true + nxapi_http_port: 80 + snmp_trap: false + anycast_border_gateway_advertise_physical_ip: true + greenfield_debug_flag: disable + tcam_allocation: false + real_time_interface_statistics_collection: true + interface_statistics_load_interval: 30 + bgp_loopback_ip_range: "10.22.0.0/22" + nve_loopback_ip_range: "10.23.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.252.0/24" + intra_fabric_subnet_range: "10.24.0.0/16" + l2_vni_range: "40000-59000" + l3_vni_range: "60000-69000" + network_vlan_range: "2400-3099" + vrf_vlan_range: "2100-2399" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.53.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.25.0.0/22" + per_vrf_loopback_auto_provision_ipv6: true + per_vrf_loopback_ipv6_range: "fd00::a25:0/112" + banner: "^ Managed by Ansible ^" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: result + +- name: Replace fabric with only required fields (all optional settings revert to defaults) + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: my_fabric + category: fabric + management: + type: vxlanIbgp + bgp_asn: "65004" + site_id: "65004" + banner: "^ Managed by Ansible ^" + register: result + +- name: Enforce exact fabric inventory using state overridden (deletes unlisted fabrics) + cisco.nd.nd_manage_fabric_ibgp: + state: overridden + config: + - name: fabric_east + category: fabric + location: + latitude: 40.7128 + longitude: -74.0060 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65010" + site_id: "65010" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0010" + replication_mode: multicast + multicast_group_subnet: "239.1.10.0/25" + bgp_loopback_ip_range: "10.10.0.0/22" + nve_loopback_ip_range: "10.11.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.10.0/24" + intra_fabric_subnet_range: "10.12.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + - name: fabric_west + category: fabric + location: + latitude: 34.0522 + longitude: -118.2437 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65020" + site_id: "65020" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0020" + replication_mode: multicast + multicast_group_subnet: "239.1.20.0/25" + bgp_loopback_ip_range: "10.20.0.0/22" + nve_loopback_ip_range: "10.21.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.20.0/24" + intra_fabric_subnet_range: "10.22.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + register: result + +- name: Delete a specific fabric using state deleted + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: my_fabric + register: result + +- name: Delete multiple fabrics in a single task + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: fabric_east + - name: fabric_west + - name: fabric_old + register: result +""" + +RETURN = r""" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.nd.plugins.module_utils.nd import nd_argument_spec +from ansible_collections.cisco.nd.plugins.module_utils.nd_state_machine import NDStateMachine +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import FabricIbgpModel +from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.manage_fabric_ibgp import ManageIbgpFabricOrchestrator +from ansible_collections.cisco.nd.plugins.module_utils.common.exceptions import NDStateMachineError + + +def main(): + argument_spec = nd_argument_spec() + argument_spec.update(FabricIbgpModel.get_argument_spec()) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + try: + # Initialize StateMachine + nd_state_machine = NDStateMachine( + module=module, + model_orchestrator=ManageIbgpFabricOrchestrator, + ) + + # Manage state + nd_state_machine.manage_state() + + module.exit_json(**nd_state_machine.output.format()) + + except NDStateMachineError as e: + module.fail_json(msg=str(e)) + except Exception as e: + module.fail_json(msg=f"Module execution failed: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml new file mode 100644 index 00000000..f8cf517e --- /dev/null +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml @@ -0,0 +1,1209 @@ +--- +# Test code for the ND modules +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have a Nexus Dashboard host, username and password + ansible.builtin.fail: + msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' + when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined + +############################################################################# +# CLEANUP - Ensure clean state before tests +############################################################################# +- name: Clean up any existing test fabrics before starting tests + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: "{{ ebgp_test_fabric_merged }}" + - name: "{{ ebgp_test_fabric_replaced }}" + - name: "{{ ebgp_test_fabric_deleted }}" + tags: always + +############################################################################# +# TEST 1: STATE MERGED - Create fabric using merged state +############################################################################# +- name: "TEST 1a: Create eBGP fabric using state merged (first run)" + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - "{{ {'name': ebgp_test_fabric_merged} | combine(common_ebgp_fabric_config) }}" + register: ebgp_merged_result_1 + tags: [test_merged, test_merged_create] + +- name: "TEST 1a: Verify eBGP fabric was created using merged state" + assert: + that: + - ebgp_merged_result_1 is changed + - ebgp_merged_result_1 is not failed + fail_msg: "eBGP fabric creation with state merged failed" + success_msg: "eBGP fabric successfully created with state merged" + tags: [test_merged, test_merged_create] + +- name: "TEST 1b: Create eBGP fabric using state merged (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - "{{ {'name': ebgp_test_fabric_merged} | combine(common_ebgp_fabric_config) }}" + register: ebgp_merged_result_2 + tags: [test_merged, test_merged_idempotent] + +- name: "TEST 1b: Verify merged state is idempotent" + assert: + that: + - ebgp_merged_result_2 is not changed + - ebgp_merged_result_2 is not failed + fail_msg: "Merged state is not idempotent - should not change when run twice with same config" + success_msg: "Merged state is idempotent - no changes on second run" + tags: [test_merged, test_merged_idempotent] + +- name: "TEST 1c: Update eBGP fabric using state merged (modify existing)" + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: "{{ ebgp_test_fabric_merged }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65002" # Changed from 65001 + bgp_asn_auto_allocation: false + site_id: "65002" # Changed from 65001 + bgp_as_mode: multiAS + bgp_allow_as_in_num: 1 + bgp_max_path: 4 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00bb" # Changed from 00aa + performance_monitoring: true # Changed from false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: false + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: disable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: ebgp_merged_result_3 + tags: [test_merged, test_merged_update] + +- name: "TEST 1c: Verify eBGP fabric was updated using merged state" + assert: + that: + - ebgp_merged_result_3 is changed + - ebgp_merged_result_3 is not failed + fail_msg: "eBGP fabric update with state merged failed" + success_msg: "eBGP fabric successfully updated with state merged" + tags: [test_merged, test_merged_update] + +############################################################################# +# VALIDATION: Query ebgp_test_fabric_merged and validate expected changes +############################################################################# +- name: "VALIDATION 1: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +- name: "VALIDATION 1: Query ebgp_test_fabric_merged configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ ebgp_test_fabric_merged }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: ebgp_merged_fabric_query + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +- name: "VALIDATION 1: Parse eBGP fabric configuration response" + set_fact: + ebgp_merged_fabric_config: "{{ ebgp_merged_fabric_query.json }}" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify BGP ASN was updated to 65002" + assert: + that: + - ebgp_merged_fabric_config.management.bgpAsn == "65002" + fail_msg: "BGP ASN validation failed. Expected: 65002, Actual: {{ ebgp_merged_fabric_config.management.bgpAsn }}" + success_msg: "✓ BGP ASN correctly updated to 65002" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Site ID was updated to 65002" + assert: + that: + - ebgp_merged_fabric_config.management.siteId == "65002" + fail_msg: "Site ID validation failed. Expected: 65002, Actual: {{ ebgp_merged_fabric_config.management.siteId }}" + success_msg: "✓ Site ID correctly updated to 65002" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Anycast Gateway MAC was updated to 2020.0000.00bb" + assert: + that: + - ebgp_merged_fabric_config.management.anycastGatewayMac == "2020.0000.00bb" + fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00bb, Actual: {{ ebgp_merged_fabric_config.management.anycastGatewayMac }}" + success_msg: "✓ Anycast Gateway MAC correctly updated to 2020.0000.00bb" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Performance Monitoring was enabled" + assert: + that: + - ebgp_merged_fabric_config.management.performanceMonitoring == true + fail_msg: "Performance Monitoring validation failed. Expected: true, Actual: {{ ebgp_merged_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly enabled" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify BGP AS Mode is multiAS" + assert: + that: + - ebgp_merged_fabric_config.management.bgpAsMode == "multiAS" + fail_msg: "BGP AS Mode validation failed. Expected: multiAS, Actual: {{ ebgp_merged_fabric_config.management.bgpAsMode }}" + success_msg: "✓ BGP AS Mode correctly set to multiAS" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Display successful validation summary for ebgp_test_fabric_merged" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for ebgp_test_fabric_merged: + ======================================== + ✓ BGP ASN: {{ ebgp_merged_fabric_config.management.bgpAsn }} + ✓ Site ID: {{ ebgp_merged_fabric_config.management.siteId }} + ✓ Anycast Gateway MAC: {{ ebgp_merged_fabric_config.management.anycastGatewayMac }} + ✓ Performance Monitoring: {{ ebgp_merged_fabric_config.management.performanceMonitoring }} + ✓ BGP AS Mode: {{ ebgp_merged_fabric_config.management.bgpAsMode }} + + All 5 expected changes validated successfully! + ======================================== + tags: [test_merged, test_merged_validation] + +############################################################################# +# TEST 2: STATE REPLACED - Create and manage fabric using replaced state +############################################################################# +- name: "TEST 2a: Create eBGP fabric using state replaced (first run)" + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: "{{ ebgp_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65004" # Different from default ASN + bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65100" + site_id: "65004" # Different from default site_id + bgp_as_mode: multiAS # Different from default multiAS + bgp_allow_as_in_num: 2 # Different from default 1 + bgp_max_path: 8 # Different from default 4 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" # Different from default MAC + performance_monitoring: true # Different from default false + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" # Different from default subnet + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 3 # Different from default 2 + rendezvous_point_loopback_id: 253 # Different from default 254 + vpc_peer_link_vlan: "3700" # Different from default 3600 + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 300 # Different from default 360 + vpc_delay_restore_timer: 120 # Different from default 150 + vpc_peer_link_port_channel_id: "600" # Different from default 500 + vpc_ipv6_neighbor_discovery_sync: false # Different from default true + advertise_physical_ip: true # Different from default false + vpc_domain_id_range: "1-800" # Different from default 1-1000 + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9000 # Different from default 9216 + l2_host_interface_mtu: 9000 # Different from default 9216 + tenant_dhcp: false # Different from default true + nxapi: false + nxapi_https_port: 443 + nxapi_http: true # Different from default false + nxapi_http_port: 80 + snmp_trap: false # Different from default true + anycast_border_gateway_advertise_physical_ip: true # Different from default false + greenfield_debug_flag: enable # Different from default disable + tcam_allocation: false # Different from default true + real_time_interface_statistics_collection: true # Different from default false + interface_statistics_load_interval: 30 # Different from default 10 + bgp_loopback_ip_range: "10.22.0.0/22" # Different from default range + nve_loopback_ip_range: "10.23.0.0/22" # Different from default range + anycast_rendezvous_point_ip_range: "10.254.252.0/24" # Different from default range + intra_fabric_subnet_range: "10.24.0.0/16" # Different from default range + l2_vni_range: "40000-59000" # Different from default range + l3_vni_range: "60000-69000" # Different from default range + network_vlan_range: "2400-3099" # Different from default range + vrf_vlan_range: "2100-2399" # Different from default range + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.53.0.0/16" # Different from default range + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.25.0.0/22" # Different from default range + per_vrf_loopback_auto_provision_ipv6: true + per_vrf_loopback_ipv6_range: "fd00::a25:0/112" # Different from default range + banner: "^ Updated via replaced state ^" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: ebgp_replaced_result_1 + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2a: Verify eBGP fabric was created using replaced state" + assert: + that: + - ebgp_replaced_result_1 is changed + - ebgp_replaced_result_1 is not failed + fail_msg: "eBGP fabric creation with state replaced failed" + success_msg: "eBGP fabric successfully created with state replaced" + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2b: Create eBGP fabric using state replaced (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: "{{ ebgp_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65004" # Different from default ASN + bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65100" + site_id: "65004" + bgp_as_mode: multiAS # Different from default multiAS + bgp_allow_as_in_num: 2 + bgp_max_path: 8 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" + performance_monitoring: true + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 3 + rendezvous_point_loopback_id: 253 + vpc_peer_link_vlan: "3700" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 300 + vpc_delay_restore_timer: 120 + vpc_peer_link_port_channel_id: "600" + vpc_ipv6_neighbor_discovery_sync: false + advertise_physical_ip: true + vpc_domain_id_range: "1-800" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9000 + l2_host_interface_mtu: 9000 + tenant_dhcp: false + nxapi: false + nxapi_https_port: 443 + nxapi_http: true + nxapi_http_port: 80 + snmp_trap: false + anycast_border_gateway_advertise_physical_ip: true + greenfield_debug_flag: enable + tcam_allocation: false + real_time_interface_statistics_collection: true + interface_statistics_load_interval: 30 + bgp_loopback_ip_range: "10.22.0.0/22" + nve_loopback_ip_range: "10.23.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.252.0/24" + intra_fabric_subnet_range: "10.24.0.0/16" + l2_vni_range: "40000-59000" + l3_vni_range: "60000-69000" + network_vlan_range: "2400-3099" + vrf_vlan_range: "2100-2399" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.53.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.25.0.0/22" + per_vrf_loopback_auto_provision_ipv6: true + per_vrf_loopback_ipv6_range: "fd00::a25:0/112" + banner: "^ Updated via replaced state ^" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: ebgp_replaced_result_2 + tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2b: Verify replaced state is idempotent" + assert: + that: + - ebgp_replaced_result_2 is not changed + - ebgp_replaced_result_2 is not failed + fail_msg: "Replaced state is not idempotent - should not change when run twice with same config" + success_msg: "Replaced state is idempotent - no changes on second run" + tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2c: Update eBGP fabric using state replaced (complete replacement with minimal config)" + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: "{{ ebgp_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65004" # Different from default ASN + bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65100" + site_id: "65004" + banner: "^ Updated via replaced state ^" + register: ebgp_replaced_result_3 + tags: [test_replaced, test_replaced_update] + +- name: "TEST 2c: Verify eBGP fabric was completely replaced (defaults restored)" + assert: + that: + - ebgp_replaced_result_3 is changed + - ebgp_replaced_result_3 is not failed + fail_msg: "eBGP fabric replacement with state replaced failed" + success_msg: "eBGP fabric successfully replaced with state replaced" + tags: [test_replaced, test_replaced_update] + +############################################################################# +# VALIDATION: Query ebgp_test_fabric_replaced and validate defaults are restored +############################################################################# +- name: "VALIDATION 2: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response_2 + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Query ebgp_test_fabric_replaced configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ ebgp_test_fabric_replaced }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response_2.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: ebgp_replaced_fabric_query + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Parse eBGP fabric configuration response" + set_fact: + ebgp_replaced_fabric_config: "{{ ebgp_replaced_fabric_query.json }}" + tags: [test_replaced, test_replaced_validation] + +# Network Range Validations - verify defaults were restored +- name: "VALIDATION 2: Verify L3 VNI Range was standardized to 50000-59000" + assert: + that: + - ebgp_replaced_fabric_config.management.l3VniRange == "50000-59000" + fail_msg: "L3 VNI Range validation failed. Expected: 50000-59000, Actual: {{ ebgp_replaced_fabric_config.management.l3VniRange }}" + success_msg: "✓ L3 VNI Range correctly standardized to 50000-59000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify L2 VNI Range was standardized to 30000-49000" + assert: + that: + - ebgp_replaced_fabric_config.management.l2VniRange == "30000-49000" + fail_msg: "L2 VNI Range validation failed. Expected: 30000-49000, Actual: {{ ebgp_replaced_fabric_config.management.l2VniRange }}" + success_msg: "✓ L2 VNI Range correctly standardized to 30000-49000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify BGP Loopback IP Range was standardized to 10.2.0.0/22" + assert: + that: + - ebgp_replaced_fabric_config.management.bgpLoopbackIpRange == "10.2.0.0/22" + fail_msg: "BGP Loopback IP Range validation failed. Expected: 10.2.0.0/22, Actual: {{ ebgp_replaced_fabric_config.management.bgpLoopbackIpRange }}" + success_msg: "✓ BGP Loopback IP Range correctly standardized to 10.2.0.0/22" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NVE Loopback IP Range was standardized to 10.3.0.0/22" + assert: + that: + - ebgp_replaced_fabric_config.management.nveLoopbackIpRange == "10.3.0.0/22" + fail_msg: "NVE Loopback IP Range validation failed. Expected: 10.3.0.0/22, Actual: {{ ebgp_replaced_fabric_config.management.nveLoopbackIpRange }}" + success_msg: "✓ NVE Loopback IP Range correctly standardized to 10.3.0.0/22" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Intra-Fabric Subnet Range was standardized to 10.4.0.0/16" + assert: + that: + - ebgp_replaced_fabric_config.management.intraFabricSubnetRange == "10.4.0.0/16" + fail_msg: "Intra-Fabric Subnet Range validation failed. Expected: 10.4.0.0/16, Actual: {{ ebgp_replaced_fabric_config.management.intraFabricSubnetRange }}" + success_msg: "✓ Intra-Fabric Subnet Range correctly standardized to 10.4.0.0/16" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VRF Lite Subnet Range was standardized to 10.33.0.0/16" + assert: + that: + - ebgp_replaced_fabric_config.management.vrfLiteSubnetRange == "10.33.0.0/16" + fail_msg: "VRF Lite Subnet Range validation failed. Expected: 10.33.0.0/16, Actual: {{ ebgp_replaced_fabric_config.management.vrfLiteSubnetRange }}" + success_msg: "✓ VRF Lite Subnet Range correctly standardized to 10.33.0.0/16" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Anycast RP IP Range was standardized to 10.254.254.0/24" + assert: + that: + - ebgp_replaced_fabric_config.management.anycastRendezvousPointIpRange == "10.254.254.0/24" + fail_msg: "Anycast RP IP Range validation failed. Expected: 10.254.254.0/24, Actual: {{ ebgp_replaced_fabric_config.management.anycastRendezvousPointIpRange }}" + success_msg: "✓ Anycast RP IP Range correctly standardized to 10.254.254.0/24" + tags: [test_replaced, test_replaced_validation] + +# VLAN Range Validations +- name: "VALIDATION 2: Verify Network VLAN Range was standardized to 2300-2999" + assert: + that: + - ebgp_replaced_fabric_config.management.networkVlanRange == "2300-2999" + fail_msg: "Network VLAN Range validation failed. Expected: 2300-2999, Actual: {{ ebgp_replaced_fabric_config.management.networkVlanRange }}" + success_msg: "✓ Network VLAN Range correctly standardized to 2300-2999" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VRF VLAN Range was standardized to 2000-2299" + assert: + that: + - ebgp_replaced_fabric_config.management.vrfVlanRange == "2000-2299" + fail_msg: "VRF VLAN Range validation failed. Expected: 2000-2299, Actual: {{ ebgp_replaced_fabric_config.management.vrfVlanRange }}" + success_msg: "✓ VRF VLAN Range correctly standardized to 2000-2299" + tags: [test_replaced, test_replaced_validation] + +# MTU Validations +- name: "VALIDATION 2: Verify Fabric MTU was restored to 9216" + assert: + that: + - ebgp_replaced_fabric_config.management.fabricMtu == 9216 + fail_msg: "Fabric MTU validation failed. Expected: 9216, Actual: {{ ebgp_replaced_fabric_config.management.fabricMtu }}" + success_msg: "✓ Fabric MTU correctly restored to 9216" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify L2 Host Interface MTU was restored to 9216" + assert: + that: + - ebgp_replaced_fabric_config.management.l2HostInterfaceMtu == 9216 + fail_msg: "L2 Host Interface MTU validation failed. Expected: 9216, Actual: {{ ebgp_replaced_fabric_config.management.l2HostInterfaceMtu }}" + success_msg: "✓ L2 Host Interface MTU correctly restored to 9216" + tags: [test_replaced, test_replaced_validation] + +# Gateway and Multicast Validations +- name: "VALIDATION 2: Verify Anycast Gateway MAC was standardized to 2020.0000.00aa" + assert: + that: + - ebgp_replaced_fabric_config.management.anycastGatewayMac == "2020.0000.00aa" + fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00aa, Actual: {{ ebgp_replaced_fabric_config.management.anycastGatewayMac }}" + success_msg: "✓ Anycast Gateway MAC correctly standardized to 2020.0000.00aa" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Multicast Group Subnet was standardized to 239.1.1.0/25" + assert: + that: + - ebgp_replaced_fabric_config.management.multicastGroupSubnet == "239.1.1.0/25" + fail_msg: "Multicast Group Subnet validation failed. Expected: 239.1.1.0/25, Actual: {{ ebgp_replaced_fabric_config.management.multicastGroupSubnet }}" + success_msg: "✓ Multicast Group Subnet correctly standardized to 239.1.1.0/25" + tags: [test_replaced, test_replaced_validation] + +# VPC Configuration Validations +- name: "VALIDATION 2: Verify VPC Auto Recovery Timer was standardized to 360" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcAutoRecoveryTimer == 360 + fail_msg: "VPC Auto Recovery Timer validation failed. Expected: 360, Actual: {{ ebgp_replaced_fabric_config.management.vpcAutoRecoveryTimer }}" + success_msg: "✓ VPC Auto Recovery Timer correctly standardized to 360" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Delay Restore Timer was standardized to 150" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcDelayRestoreTimer == 150 + fail_msg: "VPC Delay Restore Timer validation failed. Expected: 150, Actual: {{ ebgp_replaced_fabric_config.management.vpcDelayRestoreTimer }}" + success_msg: "✓ VPC Delay Restore Timer correctly standardized to 150" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Peer Link Port Channel ID was standardized to 500" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcPeerLinkPortChannelId == "500" + fail_msg: "VPC Peer Link Port Channel ID validation failed. Expected: 500, Actual: {{ ebgp_replaced_fabric_config.management.vpcPeerLinkPortChannelId }}" + success_msg: "✓ VPC Peer Link Port Channel ID correctly standardized to 500" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Peer Link VLAN was standardized to 3600" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcPeerLinkVlan == "3600" + fail_msg: "VPC Peer Link VLAN validation failed. Expected: 3600, Actual: {{ ebgp_replaced_fabric_config.management.vpcPeerLinkVlan }}" + success_msg: "✓ VPC Peer Link VLAN correctly standardized to 3600" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Domain ID Range was standardized to 1-1000" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcDomainIdRange == "1-1000" + fail_msg: "VPC Domain ID Range validation failed. Expected: 1-1000, Actual: {{ ebgp_replaced_fabric_config.management.vpcDomainIdRange }}" + success_msg: "✓ VPC Domain ID Range correctly standardized to 1-1000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC IPv6 Neighbor Discovery Sync was enabled" + assert: + that: + - ebgp_replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync == true + fail_msg: "VPC IPv6 Neighbor Discovery Sync validation failed. Expected: true, Actual: {{ ebgp_replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }}" + success_msg: "✓ VPC IPv6 Neighbor Discovery Sync correctly enabled" + tags: [test_replaced, test_replaced_validation] + +# Multicast Settings Validations +- name: "VALIDATION 2: Verify Rendezvous Point Count was standardized to 2" + assert: + that: + - ebgp_replaced_fabric_config.management.rendezvousPointCount == 2 + fail_msg: "Rendezvous Point Count validation failed. Expected: 2, Actual: {{ ebgp_replaced_fabric_config.management.rendezvousPointCount }}" + success_msg: "✓ Rendezvous Point Count correctly standardized to 2" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Rendezvous Point Loopback ID was standardized to 254" + assert: + that: + - ebgp_replaced_fabric_config.management.rendezvousPointLoopbackId == 254 + fail_msg: "Rendezvous Point Loopback ID validation failed. Expected: 254, Actual: {{ ebgp_replaced_fabric_config.management.rendezvousPointLoopbackId }}" + success_msg: "✓ Rendezvous Point Loopback ID correctly standardized to 254" + tags: [test_replaced, test_replaced_validation] + +# eBGP-specific Validations +- name: "VALIDATION 2: Verify BGP AS Mode was standardized to multiAS" + assert: + that: + - ebgp_replaced_fabric_config.management.bgpAsMode == "multiAS" + fail_msg: "BGP AS Mode validation failed. Expected: multiAS, Actual: {{ ebgp_replaced_fabric_config.management.bgpAsMode }}" + success_msg: "✓ BGP AS Mode correctly standardized to multiAS" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify BGP Allow AS In Num was standardized to 1" + assert: + that: + - ebgp_replaced_fabric_config.management.bgpAllowAsInNum == 1 + fail_msg: "BGP Allow AS In Num validation failed. Expected: 1, Actual: {{ ebgp_replaced_fabric_config.management.bgpAllowAsInNum }}" + success_msg: "✓ BGP Allow AS In Num correctly standardized to 1" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify BGP Max Path was standardized to 4" + assert: + that: + - ebgp_replaced_fabric_config.management.bgpMaxPath == 4 + fail_msg: "BGP Max Path validation failed. Expected: 4, Actual: {{ ebgp_replaced_fabric_config.management.bgpMaxPath }}" + success_msg: "✓ BGP Max Path correctly standardized to 4" + tags: [test_replaced, test_replaced_validation] + +# Feature Flag Validations +- name: "VALIDATION 2: Verify TCAM Allocation was re-enabled" + assert: + that: + - ebgp_replaced_fabric_config.management.tcamAllocation == true + fail_msg: "TCAM Allocation validation failed. Expected: true, Actual: {{ ebgp_replaced_fabric_config.management.tcamAllocation }}" + success_msg: "✓ TCAM Allocation correctly re-enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Real Time Interface Statistics Collection was disabled" + assert: + that: + - ebgp_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection == false + fail_msg: "Real Time Interface Statistics Collection validation failed. Expected: false, Actual: {{ ebgp_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }}" + success_msg: "✓ Real Time Interface Statistics Collection correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Performance Monitoring was disabled" + assert: + that: + - ebgp_replaced_fabric_config.management.performanceMonitoring == false + fail_msg: "Performance Monitoring validation failed. Expected: false, Actual: {{ ebgp_replaced_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Tenant DHCP was re-enabled" + assert: + that: + - ebgp_replaced_fabric_config.management.tenantDhcp == true + fail_msg: "Tenant DHCP validation failed. Expected: true, Actual: {{ ebgp_replaced_fabric_config.management.tenantDhcp }}" + success_msg: "✓ Tenant DHCP correctly re-enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify SNMP Trap was re-enabled" + assert: + that: + - ebgp_replaced_fabric_config.management.snmpTrap == true + fail_msg: "SNMP Trap validation failed. Expected: true, Actual: {{ ebgp_replaced_fabric_config.management.snmpTrap }}" + success_msg: "✓ SNMP Trap correctly re-enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Greenfield Debug Flag was set to disable (eBGP default)" + assert: + that: + - ebgp_replaced_fabric_config.management.greenfieldDebugFlag == "disable" + fail_msg: "Greenfield Debug Flag validation failed. Expected: disable, Actual: {{ ebgp_replaced_fabric_config.management.greenfieldDebugFlag }}" + success_msg: "✓ Greenfield Debug Flag correctly set to disable (eBGP default)" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI HTTP is always true for eBGP (ND enforced behavior)" + assert: + that: + - ebgp_replaced_fabric_config.management.nxapiHttp == true + fail_msg: "NXAPI HTTP validation failed. ND enforces nxapiHttp=true for eBGP fabrics, Actual: {{ ebgp_replaced_fabric_config.management.nxapiHttp }}" + success_msg: "✓ NXAPI HTTP is true (ND enforces this for eBGP fabrics regardless of configured value)" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI was disabled" + assert: + that: + - ebgp_replaced_fabric_config.management.nxapi == false + fail_msg: "NXAPI validation failed. Expected: false, Actual: {{ ebgp_replaced_fabric_config.management.nxapi }}" + success_msg: "✓ NXAPI correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision was disabled" + assert: + that: + - ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvision == false + fail_msg: "Per VRF Loopback Auto Provision validation failed. Expected: false, Actual: {{ ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvision }}" + success_msg: "✓ Per VRF Loopback Auto Provision correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision IPv6 was disabled" + assert: + that: + - ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 == false + fail_msg: "Per VRF Loopback Auto Provision IPv6 validation failed. Expected: false, Actual: {{ ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }}" + success_msg: "✓ Per VRF Loopback Auto Provision IPv6 correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Banner was preserved" + assert: + that: + - ebgp_replaced_fabric_config.management.banner == "^ Updated via replaced state ^" + fail_msg: "Banner validation failed. Expected: '^ Updated via replaced state ^', Actual: {{ ebgp_replaced_fabric_config.management.banner }}" + success_msg: "✓ Banner correctly preserved: '{{ ebgp_replaced_fabric_config.management.banner }}'" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Display successful validation summary for ebgp_test_fabric_replaced" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for ebgp_test_fabric_replaced: + ======================================== + Network Ranges (restored to defaults): + ✓ L3 VNI Range: {{ ebgp_replaced_fabric_config.management.l3VniRange }} + ✓ L2 VNI Range: {{ ebgp_replaced_fabric_config.management.l2VniRange }} + ✓ BGP Loopback IP Range: {{ ebgp_replaced_fabric_config.management.bgpLoopbackIpRange }} + ✓ NVE Loopback IP Range: {{ ebgp_replaced_fabric_config.management.nveLoopbackIpRange }} + ✓ Intra-Fabric Subnet Range: {{ ebgp_replaced_fabric_config.management.intraFabricSubnetRange }} + ✓ VRF Lite Subnet Range: {{ ebgp_replaced_fabric_config.management.vrfLiteSubnetRange }} + ✓ Anycast RP IP Range: {{ ebgp_replaced_fabric_config.management.anycastRendezvousPointIpRange }} + + VLAN Ranges: + ✓ Network VLAN Range: {{ ebgp_replaced_fabric_config.management.networkVlanRange }} + ✓ VRF VLAN Range: {{ ebgp_replaced_fabric_config.management.vrfVlanRange }} + + MTU Settings: + ✓ Fabric MTU: {{ ebgp_replaced_fabric_config.management.fabricMtu }} + ✓ L2 Host Interface MTU: {{ ebgp_replaced_fabric_config.management.l2HostInterfaceMtu }} + + VPC Configuration: + ✓ VPC Auto Recovery Timer: {{ ebgp_replaced_fabric_config.management.vpcAutoRecoveryTimer }} + ✓ VPC Delay Restore Timer: {{ ebgp_replaced_fabric_config.management.vpcDelayRestoreTimer }} + ✓ VPC Peer Link Port Channel ID: {{ ebgp_replaced_fabric_config.management.vpcPeerLinkPortChannelId }} + ✓ VPC Peer Link VLAN: {{ ebgp_replaced_fabric_config.management.vpcPeerLinkVlan }} + ✓ VPC Domain ID Range: {{ ebgp_replaced_fabric_config.management.vpcDomainIdRange }} + ✓ VPC IPv6 Neighbor Discovery Sync: {{ ebgp_replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }} + + Gateway & Multicast: + ✓ Anycast Gateway MAC: {{ ebgp_replaced_fabric_config.management.anycastGatewayMac }} + ✓ Multicast Group Subnet: {{ ebgp_replaced_fabric_config.management.multicastGroupSubnet }} + ✓ Rendezvous Point Count: {{ ebgp_replaced_fabric_config.management.rendezvousPointCount }} + ✓ Rendezvous Point Loopback ID: {{ ebgp_replaced_fabric_config.management.rendezvousPointLoopbackId }} + + eBGP-specific: + ✓ BGP AS Mode: {{ ebgp_replaced_fabric_config.management.bgpAsMode }} + ✓ BGP Allow AS In Num: {{ ebgp_replaced_fabric_config.management.bgpAllowAsInNum }} + ✓ BGP Max Path: {{ ebgp_replaced_fabric_config.management.bgpMaxPath }} + + Feature Flags: + ✓ TCAM Allocation: {{ ebgp_replaced_fabric_config.management.tcamAllocation }} + ✓ Real Time Interface Statistics Collection: {{ ebgp_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }} + ✓ Performance Monitoring: {{ ebgp_replaced_fabric_config.management.performanceMonitoring }} + ✓ Tenant DHCP: {{ ebgp_replaced_fabric_config.management.tenantDhcp }} + ✓ SNMP Trap: {{ ebgp_replaced_fabric_config.management.snmpTrap }} + ✓ Greenfield Debug Flag (eBGP default): {{ ebgp_replaced_fabric_config.management.greenfieldDebugFlag }} + ✓ NXAPI HTTP (ND enforces true for eBGP): {{ ebgp_replaced_fabric_config.management.nxapiHttp }} + ✓ NXAPI: {{ ebgp_replaced_fabric_config.management.nxapi }} + + Auto-Provisioning: + ✓ Per VRF Loopback Auto Provision: {{ ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvision }} + ✓ Per VRF Loopback Auto Provision IPv6: {{ ebgp_replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }} + + Preserved Settings: + ✓ Banner: "{{ ebgp_replaced_fabric_config.management.banner }}" + + All 35+ expected changes validated successfully! + ======================================== + tags: [test_replaced, test_replaced_validation] + +############################################################################# +# TEST 3: Demonstrate difference between merged and replaced states +############################################################################# +- name: "TEST 3: Create eBGP fabric for merged vs replaced comparison" + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - "{{ {'name': ebgp_test_fabric_deleted} | combine(common_ebgp_fabric_config) }}" + register: ebgp_comparison_fabric_creation + tags: [test_comparison] + +- name: "TEST 3a: Partial update using merged state (should merge changes)" + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: "{{ ebgp_test_fabric_deleted }}" + category: fabric + management: + bgp_asn: "65004" # Different from default ASN + # bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65100" + fabric_mtu: 8000 # Only updating MTU + register: ebgp_merged_partial_result + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3a: Verify merged state preserves existing configuration" + assert: + that: + - ebgp_merged_partial_result is changed + - ebgp_merged_partial_result is not failed + fail_msg: "Partial update with merged state failed" + success_msg: "Merged state successfully performed partial update" + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3b: Partial update using replaced state (should replace entire config)" + cisco.nd.nd_manage_fabric_ebgp: + state: replaced + config: + - name: "{{ ebgp_test_fabric_deleted }}" + category: fabric + management: + type: vxlanEbgp + bgp_asn: "65100" + bgp_asn_auto_allocation: true + bgp_asn_range: "65000-65100" + target_subnet_mask: 30 + register: ebgp_replaced_partial_result + tags: [test_comparison, test_replaced_partial] + +- name: "TEST 3b: Verify replaced state performs complete replacement" + assert: + that: + - ebgp_replaced_partial_result is changed + - ebgp_replaced_partial_result is not failed + fail_msg: "Partial replacement with replaced state failed" + success_msg: "Replaced state successfully performed complete replacement" + tags: [test_comparison, test_replaced_partial] + +############################################################################# +# TEST 4: STATE DELETED - Delete fabrics +############################################################################# +- name: "TEST 4a: Delete eBGP fabric using state deleted" + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: "{{ ebgp_test_fabric_deleted }}" + register: ebgp_deleted_result_1 + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4a: Verify eBGP fabric was deleted" + assert: + that: + - ebgp_deleted_result_1 is changed + - ebgp_deleted_result_1 is not failed + fail_msg: "eBGP fabric deletion with state deleted failed" + success_msg: "eBGP fabric successfully deleted with state deleted" + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4b: Delete eBGP fabric using state deleted (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: "{{ ebgp_test_fabric_deleted }}" + register: ebgp_deleted_result_2 + tags: [test_deleted, test_deleted_idempotent] + +- name: "TEST 4b: Verify deleted state is idempotent" + assert: + that: + - ebgp_deleted_result_2 is not changed + - ebgp_deleted_result_2 is not failed + fail_msg: "Deleted state is not idempotent - should not change when deleting non-existent fabric" + success_msg: "Deleted state is idempotent - no changes when deleting non-existent fabric" + tags: [test_deleted, test_deleted_idempotent] + +############################################################################# +# TEST 5: Multiple fabric operations in single task +############################################################################# +- name: "TEST 5: Multiple eBGP fabric operations in single task" + cisco.nd.nd_manage_fabric_ebgp: + state: merged + config: + - name: "multi_ebgp_fabric_1" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65101" + bgp_asn_auto_allocation: false + site_id: "65101" + bgp_as_mode: sameTierAS + bgp_allow_as_in_num: 1 + bgp_max_path: 4 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0001" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: false + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: disable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.101.0.0/22" + nve_loopback_ip_range: "10.103.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.101.0/24" + intra_fabric_subnet_range: "10.104.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.133.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.105.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + - name: "multi_ebgp_fabric_2" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65102" + bgp_asn_auto_allocation: false + site_id: "65102" + bgp_as_mode: sameTierAS + bgp_allow_as_in_num: 1 + bgp_max_path: 4 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0002" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: false + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: disable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.102.0.0/22" + nve_loopback_ip_range: "10.103.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.102.0/24" + intra_fabric_subnet_range: "10.104.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.134.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.106.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: ebgp_multi_fabric_result + tags: [test_multi, test_multi_create] + +- name: "TEST 5: Verify multiple eBGP fabrics were created" + assert: + that: + - ebgp_multi_fabric_result is changed + - ebgp_multi_fabric_result is not failed + fail_msg: "Multiple eBGP fabric creation failed" + success_msg: "Multiple eBGP fabrics successfully created" + tags: [test_multi, test_multi_create] + +############################################################################# +# FINAL CLEANUP - Clean up all test fabrics +############################################################################# +- name: "CLEANUP: Delete all test eBGP fabrics" + cisco.nd.nd_manage_fabric_ebgp: + state: deleted + config: + - name: "{{ ebgp_test_fabric_merged }}" + - name: "{{ ebgp_test_fabric_replaced }}" + - name: "{{ ebgp_test_fabric_deleted }}" + - name: "multi_ebgp_fabric_1" + - name: "multi_ebgp_fabric_2" + ignore_errors: true + tags: [cleanup, always] + +############################################################################# +# TEST SUMMARY +############################################################################# +- name: "TEST SUMMARY: Display eBGP test results" + debug: + msg: | + ======================================================== + TEST SUMMARY for cisco.nd.nd_manage_fabric_ebgp module: + ======================================================== + ✓ TEST 1: STATE MERGED + - Create fabric: {{ 'PASSED' if ebgp_merged_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ebgp_merged_result_2 is not changed else 'FAILED' }} + - Update fabric: {{ 'PASSED' if ebgp_merged_result_3 is changed else 'FAILED' }} + + ✓ TEST 2: STATE REPLACED + - Create fabric: {{ 'PASSED' if ebgp_replaced_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ebgp_replaced_result_2 is not changed else 'FAILED' }} + - Replace fabric: {{ 'PASSED' if ebgp_replaced_result_3 is changed else 'FAILED' }} + + ✓ TEST 3: MERGED vs REPLACED Comparison + - Merged partial: {{ 'PASSED' if ebgp_merged_partial_result is changed else 'FAILED' }} + - Replaced partial: {{ 'PASSED' if ebgp_replaced_partial_result is changed else 'FAILED' }} + + ✓ TEST 4: STATE DELETED + - Delete fabric: {{ 'PASSED' if ebgp_deleted_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ebgp_deleted_result_2 is not changed else 'FAILED' }} + + ✓ TEST 5: MULTIPLE FABRICS + - Multi-create: {{ 'PASSED' if ebgp_multi_fabric_result is changed else 'FAILED' }} + + All tests validate: + - State merged: Creates and updates eBGP fabrics by merging changes + - State replaced: Creates and completely replaces eBGP fabric configuration + - State deleted: Removes eBGP fabrics + - Idempotency: All operations are idempotent when run multiple times + - Difference: Merged preserves existing config, replaced overwrites completely + - eBGP-specific: bgpAsMode, bgpAllowAsInNum, bgpMaxPath defaults validated + ======================================== + tags: [summary, always] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml new file mode 100644 index 00000000..17b292f1 --- /dev/null +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml @@ -0,0 +1,700 @@ +--- +# Test code for the ND modules +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have a Nexus Dashboard host, username and password + ansible.builtin.fail: + msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' + when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined + +############################################################################# +# CLEANUP - Ensure clean state before tests +############################################################################# +- name: Clean up any existing test fabrics before starting tests + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: "{{ ext_test_fabric_merged }}" + - name: "{{ ext_test_fabric_replaced }}" + - name: "{{ ext_test_fabric_deleted }}" + tags: always + +############################################################################# +# TEST 1: STATE MERGED - Create fabric using merged state +############################################################################# +- name: "TEST 1a: Create fabric using state merged (first run)" + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - "{{ {'name': ext_test_fabric_merged} | combine(common_external_fabric_config) }}" + register: ext_merged_result_1 + tags: [test_merged, test_merged_create] + +- name: "TEST 1a: Verify fabric was created using merged state" + assert: + that: + - ext_merged_result_1 is changed + - ext_merged_result_1 is not failed + fail_msg: "Fabric creation with state merged failed" + success_msg: "Fabric successfully created with state merged" + tags: [test_merged, test_merged_create] + +- name: "TEST 1b: Create fabric using state merged (second run - idempotency test)" + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - "{{ {'name': ext_test_fabric_merged} | combine(common_external_fabric_config) }}" + register: ext_merged_result_2 + tags: [test_merged, test_merged_idempotent] + +- name: "TEST 1b: Verify merged state is idempotent" + assert: + that: + - ext_merged_result_2 is not changed + - ext_merged_result_2 is not failed + fail_msg: "Merged state is not idempotent - should not change when run twice with same config" + success_msg: "Merged state is idempotent - no changes on second run" + tags: [test_merged, test_merged_idempotent] + +- name: "TEST 1c: Update fabric using state merged (modify existing)" + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - name: "{{ ext_test_fabric_merged }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65002" # Changed from 65001 + copp_policy: strict # Changed from manual + create_bgp_config: true + cdp: true # Changed from false + snmp_trap: false # Changed from true + nxapi: true # Changed from false + nxapi_http: true # Changed from false + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: true # Changed from false + real_time_interface_statistics_collection: true # Changed from false + interface_statistics_load_interval: 30 # Changed from 10 + sub_interface_dot1q_range: "2-511" + power_redundancy_mode: combined # Changed from redundant + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: ext_merged_result_3 + tags: [test_merged, test_merged_update] + +- name: "TEST 1c: Verify fabric was updated using merged state" + assert: + that: + - ext_merged_result_3 is changed + - ext_merged_result_3 is not failed + fail_msg: "Fabric update with state merged failed" + success_msg: "Fabric successfully updated with state merged" + tags: [test_merged, test_merged_update] + +############################################################################# +# VALIDATION: Query ext_test_fabric_merged and validate expected changes +############################################################################# +- name: "VALIDATION 1: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +- name: "VALIDATION 1: Query ext_test_fabric_merged configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ ext_test_fabric_merged }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: ext_merged_fabric_query + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +- name: "VALIDATION 1: Parse fabric configuration response" + set_fact: + ext_merged_fabric_config: "{{ ext_merged_fabric_query.json }}" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify BGP ASN was updated to 65002" + assert: + that: + - ext_merged_fabric_config.management.bgpAsn == "65002" + fail_msg: "BGP ASN validation failed. Expected: 65002, Actual: {{ ext_merged_fabric_config.management.bgpAsn }}" + success_msg: "✓ BGP ASN correctly updated to 65002" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify CoPP Policy was updated to strict" + assert: + that: + - ext_merged_fabric_config.management.coppPolicy == "strict" + fail_msg: "CoPP Policy validation failed. Expected: strict, Actual: {{ ext_merged_fabric_config.management.coppPolicy }}" + success_msg: "✓ CoPP Policy correctly updated to strict" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Performance Monitoring was enabled" + assert: + that: + - ext_merged_fabric_config.management.performanceMonitoring == true + fail_msg: "Performance Monitoring validation failed. Expected: true, Actual: {{ ext_merged_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly enabled" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify CDP was enabled" + assert: + that: + - ext_merged_fabric_config.management.cdp == true + fail_msg: "CDP validation failed. Expected: true, Actual: {{ ext_merged_fabric_config.management.cdp }}" + success_msg: "✓ CDP correctly enabled" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Display successful validation summary for ext_test_fabric_merged" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for ext_test_fabric_merged: + ======================================== + ✓ BGP ASN: {{ ext_merged_fabric_config.management.bgpAsn }} + ✓ CoPP Policy: {{ ext_merged_fabric_config.management.coppPolicy }} + ✓ Performance Monitoring: {{ ext_merged_fabric_config.management.performanceMonitoring }} + ✓ CDP: {{ ext_merged_fabric_config.management.cdp }} + + All 4 expected changes validated successfully! + ======================================== + tags: [test_merged, test_merged_validation] + +############################################################################# +# TEST 2: STATE REPLACED - Create and manage fabric using replaced state +############################################################################# +- name: "TEST 2a: Create fabric using state replaced (first run)" + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: "{{ ext_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65004" + copp_policy: strict # Different from default + create_bgp_config: true + cdp: true # Different from default + snmp_trap: false # Different from default + nxapi: true # Different from default + nxapi_http: true # Different from default + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: true # Different from default + real_time_interface_statistics_collection: true # Different from default + interface_statistics_load_interval: 30 # Different from default + sub_interface_dot1q_range: "2-511" + power_redundancy_mode: combined # Different from default + ptp: true # Different from default + ptp_domain_id: 10 # Different from default + ptp_loopback_id: 5 # Different from default + mpls_handoff: false + mpls_loopback_ip_range: "10.102.0.0/25" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + extra_config_aaa: "" + extra_config_fabric: "" + register: ext_replaced_result_1 + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2a: Verify fabric was created using replaced state" + assert: + that: + - ext_replaced_result_1 is changed + - ext_replaced_result_1 is not failed + fail_msg: "Fabric creation with state replaced failed" + success_msg: "Fabric successfully created with state replaced" + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2b: Create fabric using state replaced (second run - idempotency test)" + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: "{{ ext_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65004" + copp_policy: strict + create_bgp_config: true + cdp: true + snmp_trap: false + nxapi: true + nxapi_http: true + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: true + real_time_interface_statistics_collection: true + interface_statistics_load_interval: 30 + sub_interface_dot1q_range: "2-511" + power_redundancy_mode: combined + ptp: true + ptp_domain_id: 10 + ptp_loopback_id: 5 + mpls_handoff: false + mpls_loopback_ip_range: "10.102.0.0/25" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + extra_config_aaa: "" + extra_config_fabric: "" + register: ext_replaced_result_2 + tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2b: Verify replaced state is idempotent" + assert: + that: + - ext_replaced_result_2 is not changed + - ext_replaced_result_2 is not failed + fail_msg: "Replaced state is not idempotent - should not change when run twice with same config" + success_msg: "Replaced state is idempotent - no changes on second run" + tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2c: Update fabric using state replaced (complete replacement with minimal config)" + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: "{{ ext_test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65004" + register: ext_replaced_result_3 + tags: [test_replaced, test_replaced_update] + +- name: "TEST 2c: Verify fabric was completely replaced" + assert: + that: + - ext_replaced_result_3 is changed + - ext_replaced_result_3 is not failed + fail_msg: "Fabric replacement with state replaced failed" + success_msg: "Fabric successfully replaced with state replaced" + tags: [test_replaced, test_replaced_update] + +############################################################################# +# VALIDATION: Query ext_test_fabric_replaced and validate defaults restored +############################################################################# +- name: "VALIDATION 2: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response_2 + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Query ext_test_fabric_replaced configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ ext_test_fabric_replaced }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response_2.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: ext_replaced_fabric_query + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Parse fabric configuration response" + set_fact: + ext_replaced_fabric_config: "{{ ext_replaced_fabric_query.json }}" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify CoPP Policy was standardized to manual (default)" + assert: + that: + - ext_replaced_fabric_config.management.coppPolicy == "manual" + fail_msg: "CoPP Policy validation failed. Expected: manual, Actual: {{ ext_replaced_fabric_config.management.coppPolicy }}" + success_msg: "✓ CoPP Policy correctly standardized to manual" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify SNMP Trap was restored to default (true)" + assert: + that: + - ext_replaced_fabric_config.management.snmpTrap == true + fail_msg: "SNMP Trap validation failed. Expected: true, Actual: {{ ext_replaced_fabric_config.management.snmpTrap }}" + success_msg: "✓ SNMP Trap correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify CDP was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.cdp == false + fail_msg: "CDP validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.cdp }}" + success_msg: "✓ CDP correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.nxapi == false + fail_msg: "NXAPI validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.nxapi }}" + success_msg: "✓ NXAPI correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI HTTP was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.nxapiHttp == false + fail_msg: "NXAPI HTTP validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.nxapiHttp }}" + success_msg: "✓ NXAPI HTTP correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Performance Monitoring was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.performanceMonitoring == false + fail_msg: "Performance Monitoring validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Real Time Interface Statistics Collection was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection == false + fail_msg: "Real Time Interface Statistics Collection validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }}" + success_msg: "✓ Real Time Interface Statistics Collection correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Power Redundancy Mode was restored to default (redundant)" + assert: + that: + - ext_replaced_fabric_config.management.powerRedundancyMode == "redundant" + fail_msg: "Power Redundancy Mode validation failed. Expected: redundant, Actual: {{ ext_replaced_fabric_config.management.powerRedundancyMode }}" + success_msg: "✓ Power Redundancy Mode correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify PTP was restored to default (false)" + assert: + that: + - ext_replaced_fabric_config.management.ptp == false + fail_msg: "PTP validation failed. Expected: false, Actual: {{ ext_replaced_fabric_config.management.ptp }}" + success_msg: "✓ PTP correctly restored to default" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Display successful validation summary for ext_test_fabric_replaced" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for ext_test_fabric_replaced: + ======================================== + ✓ CoPP Policy: {{ ext_replaced_fabric_config.management.coppPolicy }} + ✓ SNMP Trap: {{ ext_replaced_fabric_config.management.snmpTrap }} + ✓ CDP: {{ ext_replaced_fabric_config.management.cdp }} + ✓ NXAPI: {{ ext_replaced_fabric_config.management.nxapi }} + ✓ NXAPI HTTP: {{ ext_replaced_fabric_config.management.nxapiHttp }} + ✓ Performance Monitoring: {{ ext_replaced_fabric_config.management.performanceMonitoring }} + ✓ Real Time Interface Statistics: {{ ext_replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }} + ✓ Power Redundancy Mode: {{ ext_replaced_fabric_config.management.powerRedundancyMode }} + ✓ PTP: {{ ext_replaced_fabric_config.management.ptp }} + + All defaults correctly restored after replaced with minimal config! + ======================================== + tags: [test_replaced, test_replaced_validation] + +############################################################################# +# TEST 3: Demonstrate difference between merged and replaced states +############################################################################# +- name: "TEST 3: Create fabric for merged vs replaced comparison" + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - "{{ {'name': ext_test_fabric_deleted} | combine(common_external_fabric_config) }}" + register: ext_comparison_fabric_creation + tags: [test_comparison] + +- name: "TEST 3a: Partial update using merged state (should merge changes)" + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - name: "{{ ext_test_fabric_deleted }}" + category: fabric + management: + bgp_asn: "65099" # Only updating ASN + copp_policy: strict # Only updating CoPP policy + register: ext_merged_partial_result + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3a: Verify merged state preserves existing configuration" + assert: + that: + - ext_merged_partial_result is changed + - ext_merged_partial_result is not failed + fail_msg: "Partial update with merged state failed" + success_msg: "Merged state successfully performed partial update" + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3b: Partial update using replaced state (should replace entire config)" + cisco.nd.nd_manage_fabric_external: + state: replaced + config: + - name: "{{ ext_test_fabric_deleted }}" + category: fabric + management: + type: externalConnectivity + bgp_asn: "65100" # Only specifying minimal config for replaced + register: ext_replaced_partial_result + tags: [test_comparison, test_replaced_partial] + +- name: "TEST 3b: Verify replaced state performs complete replacement" + assert: + that: + - ext_replaced_partial_result is changed + - ext_replaced_partial_result is not failed + fail_msg: "Partial replacement with replaced state failed" + success_msg: "Replaced state successfully performed complete replacement" + tags: [test_comparison, test_replaced_partial] + +############################################################################# +# TEST 4: STATE DELETED - Delete fabrics +############################################################################# +- name: "TEST 4a: Delete fabric using state deleted" + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: "{{ ext_test_fabric_deleted }}" + register: ext_deleted_result_1 + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4a: Verify fabric was deleted" + assert: + that: + - ext_deleted_result_1 is changed + - ext_deleted_result_1 is not failed + fail_msg: "Fabric deletion with state deleted failed" + success_msg: "Fabric successfully deleted with state deleted" + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4b: Delete fabric using state deleted (second run - idempotency test)" + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: "{{ ext_test_fabric_deleted }}" + register: ext_deleted_result_2 + tags: [test_deleted, test_deleted_idempotent] + +- name: "TEST 4b: Verify deleted state is idempotent" + assert: + that: + - ext_deleted_result_2 is not changed + - ext_deleted_result_2 is not failed + fail_msg: "Deleted state is not idempotent - should not change when deleting non-existent fabric" + success_msg: "Deleted state is idempotent - no changes when deleting non-existent fabric" + tags: [test_deleted, test_deleted_idempotent] + +############################################################################# +# TEST 5: Multiple fabric operations in single task +############################################################################# +- name: "TEST 5: Multiple fabric operations in single task" + cisco.nd.nd_manage_fabric_external: + state: merged + config: + - name: "ext_multi_fabric_1" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65101" + copp_policy: manual + create_bgp_config: true + cdp: false + snmp_trap: true + nxapi: false + nxapi_http: false + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: false + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + sub_interface_dot1q_range: "2-511" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + - name: "ext_multi_fabric_2" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65102" + copp_policy: manual + create_bgp_config: true + cdp: false + snmp_trap: true + nxapi: false + nxapi_http: false + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: false + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + sub_interface_dot1q_range: "2-511" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: ext_multi_fabric_result + tags: [test_multi, test_multi_create] + +- name: "TEST 5: Verify multiple fabrics were created" + assert: + that: + - ext_multi_fabric_result is changed + - ext_multi_fabric_result is not failed + fail_msg: "Multiple fabric creation failed" + success_msg: "Multiple fabrics successfully created" + tags: [test_multi, test_multi_create] + +############################################################################# +# FINAL CLEANUP - Clean up all test fabrics +############################################################################# +- name: "CLEANUP: Delete all test fabrics" + cisco.nd.nd_manage_fabric_external: + state: deleted + config: + - name: "{{ ext_test_fabric_merged }}" + - name: "{{ ext_test_fabric_replaced }}" + - name: "{{ ext_test_fabric_deleted }}" + - name: "ext_multi_fabric_1" + - name: "ext_multi_fabric_2" + ignore_errors: true + tags: [cleanup, always] + +############################################################################# +# TEST SUMMARY +############################################################################# +- name: "TEST SUMMARY: Display test results" + debug: + msg: | + ======================================================== + TEST SUMMARY for cisco.nd.nd_manage_fabric_external module: + ======================================================== + ✓ TEST 1: STATE MERGED + - Create fabric: {{ 'PASSED' if ext_merged_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ext_merged_result_2 is not changed else 'FAILED' }} + - Update fabric: {{ 'PASSED' if ext_merged_result_3 is changed else 'FAILED' }} + + ✓ TEST 2: STATE REPLACED + - Create fabric: {{ 'PASSED' if ext_replaced_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ext_replaced_result_2 is not changed else 'FAILED' }} + - Replace fabric: {{ 'PASSED' if ext_replaced_result_3 is changed else 'FAILED' }} + + ✓ TEST 3: MERGED vs REPLACED Comparison + - Merged partial: {{ 'PASSED' if ext_merged_partial_result is changed else 'FAILED' }} + - Replaced partial: {{ 'PASSED' if ext_replaced_partial_result is changed else 'FAILED' }} + + ✓ TEST 4: STATE DELETED + - Delete fabric: {{ 'PASSED' if ext_deleted_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if ext_deleted_result_2 is not changed else 'FAILED' }} + + ✓ TEST 5: MULTIPLE FABRICS + - Multi-create: {{ 'PASSED' if ext_multi_fabric_result is changed else 'FAILED' }} + + All tests validate: + - State merged: Creates and updates fabrics by merging changes + - State replaced: Creates and completely replaces fabric configuration + - State deleted: Removes fabrics + - Idempotency: All operations are idempotent when run multiple times + - Difference: Merged preserves existing config, replaced overwrites completely + ======================================== + tags: [summary, always] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml new file mode 100644 index 00000000..30b77c59 --- /dev/null +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -0,0 +1,1172 @@ +--- +# Test code for the ND modules +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have a Nexus Dashboard host, username and password + ansible.builtin.fail: + msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' + when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined + +############################################################################# +# CLEANUP - Ensure clean state before tests +############################################################################# +- name: Clean up any existing test fabrics before starting tests + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: "{{ test_fabric_merged }}" + - name: "{{ test_fabric_replaced }}" + - name: "{{ test_fabric_deleted }}" + tags: always + +############################################################################# +# TEST 1: STATE MERGED - Create fabric using merged state +############################################################################# +- name: "TEST 1a: Create fabric using state merged (first run)" + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - "{{ {'name': test_fabric_merged} | combine(common_fabric_config) }}" + register: merged_result_1 + tags: [test_merged, test_merged_create] + +- name: "TEST 1a: Verify fabric was created using merged state" + assert: + that: + - merged_result_1 is changed + - merged_result_1 is not failed + fail_msg: "Fabric creation with state merged failed" + success_msg: "Fabric successfully created with state merged" + tags: [test_merged, test_merged_create] + +- name: "TEST 1b: Create fabric using state merged (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - "{{ {'name': test_fabric_merged} | combine(common_fabric_config) }}" + register: merged_result_2 + tags: [test_merged, test_merged_idempotent] + +- name: "TEST 1b: Verify merged state is idempotent" + assert: + that: + - merged_result_2 is not changed + - merged_result_2 is not failed + fail_msg: "Merged state is not idempotent - should not change when run twice with same config" + success_msg: "Merged state is idempotent - no changes on second run" + tags: [test_merged, test_merged_idempotent] + +# - name: "PAUSE: Review TEST 1b results before continuing" +# ansible.builtin.pause: +# prompt: "TEST 1b complete. Review results and press Enter to continue, or Ctrl+C then A to abort" +# tags: [test_merged, test_merged_update] + +- name: "TEST 1c: Update fabric using state merged (modify existing)" + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - name: "{{ test_fabric_merged }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65002" # Changed from 65001 + site_id: "65002" # Changed from 65001 + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00bb" # Changed from 00aa + performance_monitoring: true # Changed from false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: enable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + # per_vrf_loopback_auto_provision_ipv6: false + # per_vrf_loopback_ipv6_range: "fd00::a05:0/112" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + register: merged_result_3 + tags: [test_merged, test_merged_update] + +- name: "TEST 1c: Verify fabric was updated using merged state" + assert: + that: + - merged_result_3 is changed + - merged_result_3 is not failed + fail_msg: "Fabric update with state merged failed" + success_msg: "Fabric successfully updated with state merged" + tags: [test_merged, test_merged_update] + +############################################################################# +# VALIDATION: Query test_fabric_merged and validate expected changes +############################################################################# +# Get authentication token first +- name: "VALIDATION 1: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +- name: "VALIDATION 1: Query test_fabric_merged configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ test_fabric_merged }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: merged_fabric_query + tags: [test_merged, test_merged_validation] + delegate_to: localhost + +# - debug: msg="{{ merged_fabric_query }}" +# - meta: end_play + +- name: "VALIDATION 1: Parse fabric configuration response" + set_fact: + merged_fabric_config: "{{ merged_fabric_query.json }}" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify BGP ASN was updated to 65002" + assert: + that: + - merged_fabric_config.management.bgpAsn == "65002" + fail_msg: "BGP ASN validation failed. Expected: 65002, Actual: {{ merged_fabric_config.management.bgpAsn }}" + success_msg: "✓ BGP ASN correctly updated to 65002" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Site ID was updated to 65002" + assert: + that: + - merged_fabric_config.management.siteId == "65002" + fail_msg: "Site ID validation failed. Expected: 65002, Actual: {{ merged_fabric_config.management.siteId }}" + success_msg: "✓ Site ID correctly updated to 65002" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Anycast Gateway MAC was updated to 2020.0000.00bb" + assert: + that: + - merged_fabric_config.management.anycastGatewayMac == "2020.0000.00bb" + fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00bb, Actual: {{ merged_fabric_config.management.anycastGatewayMac }}" + success_msg: "✓ Anycast Gateway MAC correctly updated to 2020.0000.00bb" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Verify Performance Monitoring was enabled" + assert: + that: + - merged_fabric_config.management.performanceMonitoring == true + fail_msg: "Performance Monitoring validation failed. Expected: true, Actual: {{ merged_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly enabled" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Display successful validation summary for test_fabric_merged" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for test_fabric_merged: + ======================================== + ✓ BGP ASN: {{ merged_fabric_config.management.bgpAsn }} + ✓ Site ID: {{ merged_fabric_config.management.siteId }} + ✓ Anycast Gateway MAC: {{ merged_fabric_config.management.anycastGatewayMac }} + ✓ Performance Monitoring: {{ merged_fabric_config.management.performanceMonitoring }} + + All 4 expected changes validated successfully! + ======================================== + tags: [test_merged, test_merged_validation] + +# - name: "PAUSE: Review TEST 1c results before continuing" +# ansible.builtin.pause: +# prompt: "TEST 1c complete. Review results and press Enter to continue, or Ctrl+C then A to abort" +# tags: [test_merged, test_merged_update] + +############################################################################# +# TEST 2: STATE REPLACED - Create and manage fabric using replaced state +############################################################################# +- name: "TEST 2a: Create fabric using state replaced (first run)" + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: "{{ test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65004" # DIfferent from default ASN + site_id: "65004" # DIfferent from default site_id + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" # DIfferent from default MAC + performance_monitoring: true # DIfferent from default to true + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" # DIfferent from default subnet + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 3 # DIfferent from default count + rendezvous_point_loopback_id: 253 # DIfferent from default loopback + vpc_peer_link_vlan: "3700" # DIfferent from default VLAN + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 300 # DIfferent from default timer + vpc_delay_restore_timer: 120 # DIfferent from default timer + vpc_peer_link_port_channel_id: "600" # DIfferent from default port channel + vpc_ipv6_neighbor_discovery_sync: false # DIfferent from default to false + advertise_physical_ip: true # DIfferent from default to true + vpc_domain_id_range: "1-800" # DIfferent from default range + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9000 # DIfferent from default MTU + l2_host_interface_mtu: 9000 # DIfferent from default MTU + tenant_dhcp: false # DIfferent from default to false + nxapi: false # DIfferent from default to false + nxapi_https_port: 443 + nxapi_http: true # DIfferent from default to true + nxapi_http_port: 80 + snmp_trap: false # DIfferent from default to false + anycast_border_gateway_advertise_physical_ip: true # DIfferent from default to true + greenfield_debug_flag: disable # DIfferent from default to disable + tcam_allocation: false # DIfferent from default to false + real_time_interface_statistics_collection: true # DIfferent from default to true + interface_statistics_load_interval: 30 # DIfferent from default interval + bgp_loopback_ip_range: "10.22.0.0/22" # DIfferent from default range + nve_loopback_ip_range: "10.23.0.0/22" # DIfferent from default range + anycast_rendezvous_point_ip_range: "10.254.252.0/24" # DIfferent from default range + intra_fabric_subnet_range: "10.24.0.0/16" # DIfferent from default range + l2_vni_range: "40000-59000" # DIfferent from default range + l3_vni_range: "60000-69000" # DIfferent from default range + network_vlan_range: "2400-3099" # DIfferent from default range + vrf_vlan_range: "2100-2399" # DIfferent from default range + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.53.0.0/16" # DIfferent from default range + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.25.0.0/22" # DIfferent from default range + per_vrf_loopback_auto_provision_ipv6: true + per_vrf_loopback_ipv6_range: "fd00::a25:0/112" # DIfferent from default range + banner: "^ Updated via replaced state ^" # Added banner + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: replaced_result_1 + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2a: Verify fabric was created using replaced state" + assert: + that: + - replaced_result_1 is changed + - replaced_result_1 is not failed + fail_msg: "Fabric creation with state replaced failed" + success_msg: "Fabric successfully created with state replaced" + tags: [test_replaced, test_replaced_create] + +- name: "TEST 2b: Create fabric using state replaced (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: "{{ test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65004" # DIfferent from default ASN + site_id: "65004" # DIfferent from default site_id + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00dd" # DIfferent from default MAC + performance_monitoring: true # DIfferent from default to true + replication_mode: multicast + multicast_group_subnet: "239.1.3.0/25" # DIfferent from default subnet + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 3 # DIfferent from default count + rendezvous_point_loopback_id: 253 # DIfferent from default loopback + vpc_peer_link_vlan: "3700" # DIfferent from default VLAN + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 300 # DIfferent from default timer + vpc_delay_restore_timer: 120 # DIfferent from default timer + vpc_peer_link_port_channel_id: "600" # DIfferent from default port channel + vpc_ipv6_neighbor_discovery_sync: false # DIfferent from default to false + advertise_physical_ip: true # DIfferent from default to true + vpc_domain_id_range: "1-800" # DIfferent from default range + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9000 # DIfferent from default MTU + l2_host_interface_mtu: 9000 # DIfferent from default MTU + tenant_dhcp: false # DIfferent from default to false + nxapi: false # DIfferent from default to false + nxapi_https_port: 443 + nxapi_http: true # DIfferent from default to true + nxapi_http_port: 80 + snmp_trap: false # DIfferent from default to false + anycast_border_gateway_advertise_physical_ip: true # DIfferent from default to true + greenfield_debug_flag: disable # DIfferent from default to disable + tcam_allocation: false # DIfferent from default to false + real_time_interface_statistics_collection: true # DIfferent from default to true + interface_statistics_load_interval: 30 # DIfferent from default interval + bgp_loopback_ip_range: "10.22.0.0/22" # DIfferent from default range + nve_loopback_ip_range: "10.23.0.0/22" # DIfferent from default range + anycast_rendezvous_point_ip_range: "10.254.252.0/24" # DIfferent from default range + intra_fabric_subnet_range: "10.24.0.0/16" # DIfferent from default range + l2_vni_range: "40000-59000" # DIfferent from default range + l3_vni_range: "60000-69000" # DIfferent from default range + network_vlan_range: "2400-3099" # DIfferent from default range + vrf_vlan_range: "2100-2399" # DIfferent from default range + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.53.0.0/16" # DIfferent from default range + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.25.0.0/22" # DIfferent from default range + per_vrf_loopback_auto_provision_ipv6: true + per_vrf_loopback_ipv6_range: "fd00::a25:0/112" # DIfferent from default range + banner: "^ Updated via replaced state ^" # Added banner + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + management_ipv6_prefix: 64 + register: replaced_result_2 + tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2b: Verify replaced state is idempotent" + assert: + that: + - replaced_result_2 is not changed + - replaced_result_2 is not failed + fail_msg: "Replaced state is not idempotent - should not change when run twice with same config" + success_msg: "Replaced state is idempotent - no changes on second run" + tags: [test_replaced, test_replaced_idempotent] + +# - name: "PAUSE: Review TEST 2b results before continuing" +# ansible.builtin.pause: +# prompt: "TEST 2b complete. Review results and press Enter to continue, or Ctrl+C then A to abort" +# tags: [test_replaced, test_replaced_idempotent] + +- name: "TEST 2c: Update fabric using state replaced (complete replacement)" + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: "{{ test_fabric_replaced }}" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65004" # Changed ASN + site_id: "65004" # Changed site_id + banner: "^ Updated via replaced state ^" # Added banner + register: replaced_result_3 + tags: [test_replaced, test_replaced_update] + +- name: "TEST 2c: Verify fabric was completely replaced" + assert: + that: + - replaced_result_3 is changed + - replaced_result_3 is not failed + fail_msg: "Fabric replacement with state replaced failed" + success_msg: "Fabric successfully replaced with state replaced" + tags: [test_replaced, test_replaced_update] + +# ############################################################################# +# # VALIDATION: Query test_fabric_replaced and validate expected changes +# ############################################################################# +# Get authentication token first +- name: "VALIDATION 2: Authenticate with ND to get token" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/login" + method: POST + headers: + Content-Type: "application/json" + body_format: json + body: + domain: "{{ ansible_httpapi_login_domain | default('local') }}" + userName: "{{ ansible_user }}" + userPasswd: "{{ ansible_password }}" + validate_certs: false + return_content: true + status_code: + - 200 + register: nd_auth_response_2 + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Query test_fabric_replaced configuration from ND" + ansible.builtin.uri: + url: "https://{{ ansible_host }}:{{ ansible_httpapi_port | default(443) }}/api/v1/manage/fabrics/{{ test_fabric_replaced }}" + method: GET + headers: + Authorization: "Bearer {{ nd_auth_response_2.json.jwttoken }}" + Content-Type: "application/json" + validate_certs: false + return_content: true + status_code: + - 200 + - 404 + register: replaced_fabric_query + tags: [test_replaced, test_replaced_validation] + delegate_to: localhost + +- name: "VALIDATION 2: Parse fabric configuration response" + set_fact: + replaced_fabric_config: "{{ replaced_fabric_query.json }}" + tags: [test_replaced, test_replaced_validation] + +# Network Range Validations +- name: "VALIDATION 2: Verify L3 VNI Range was standardized to 50000-59000" + assert: + that: + - replaced_fabric_config.management.l3VniRange == "50000-59000" + fail_msg: "L3 VNI Range validation failed. Expected: 50000-59000, Actual: {{ replaced_fabric_config.management.l3VniRange }}" + success_msg: "✓ L3 VNI Range correctly standardized to 50000-59000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify L2 VNI Range was standardized to 30000-49000" + assert: + that: + - replaced_fabric_config.management.l2VniRange == "30000-49000" + fail_msg: "L2 VNI Range validation failed. Expected: 30000-49000, Actual: {{ replaced_fabric_config.management.l2VniRange }}" + success_msg: "✓ L2 VNI Range correctly standardized to 30000-49000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify BGP Loopback IP Range was standardized to 10.2.0.0/22" + assert: + that: + - replaced_fabric_config.management.bgpLoopbackIpRange == "10.2.0.0/22" + fail_msg: "BGP Loopback IP Range validation failed. Expected: 10.2.0.0/22, Actual: {{ replaced_fabric_config.management.bgpLoopbackIpRange }}" + success_msg: "✓ BGP Loopback IP Range correctly standardized to 10.2.0.0/22" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NVE Loopback IP Range was standardized to 10.3.0.0/22" + assert: + that: + - replaced_fabric_config.management.nveLoopbackIpRange == "10.3.0.0/22" + fail_msg: "NVE Loopback IP Range validation failed. Expected: 10.3.0.0/22, Actual: {{ replaced_fabric_config.management.nveLoopbackIpRange }}" + success_msg: "✓ NVE Loopback IP Range correctly standardized to 10.3.0.0/22" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Intra-Fabric Subnet Range was standardized to 10.4.0.0/16" + assert: + that: + - replaced_fabric_config.management.intraFabricSubnetRange == "10.4.0.0/16" + fail_msg: "Intra-Fabric Subnet Range validation failed. Expected: 10.4.0.0/16, Actual: {{ replaced_fabric_config.management.intraFabricSubnetRange }}" + success_msg: "✓ Intra-Fabric Subnet Range correctly standardized to 10.4.0.0/16" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VRF Lite Subnet Range was standardized to 10.33.0.0/16" + assert: + that: + - replaced_fabric_config.management.vrfLiteSubnetRange == "10.33.0.0/16" + fail_msg: "VRF Lite Subnet Range validation failed. Expected: 10.33.0.0/16, Actual: {{ replaced_fabric_config.management.vrfLiteSubnetRange }}" + success_msg: "✓ VRF Lite Subnet Range correctly standardized to 10.33.0.0/16" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Anycast RP IP Range was standardized to 10.254.254.0/24" + assert: + that: + - replaced_fabric_config.management.anycastRendezvousPointIpRange == "10.254.254.0/24" + fail_msg: "Anycast RP IP Range validation failed. Expected: 10.254.254.0/24, Actual: {{ replaced_fabric_config.management.anycastRendezvousPointIpRange }}" + success_msg: "✓ Anycast RP IP Range correctly standardized to 10.254.254.0/24" + tags: [test_replaced, test_replaced_validation] + +# VLAN Range Validations +- name: "VALIDATION 2: Verify Network VLAN Range was standardized to 2300-2999" + assert: + that: + - replaced_fabric_config.management.networkVlanRange == "2300-2999" + fail_msg: "Network VLAN Range validation failed. Expected: 2300-2999, Actual: {{ replaced_fabric_config.management.networkVlanRange }}" + success_msg: "✓ Network VLAN Range correctly standardized to 2300-2999" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VRF VLAN Range was standardized to 2000-2299" + assert: + that: + - replaced_fabric_config.management.vrfVlanRange == "2000-2299" + fail_msg: "VRF VLAN Range validation failed. Expected: 2000-2299, Actual: {{ replaced_fabric_config.management.vrfVlanRange }}" + success_msg: "✓ VRF VLAN Range correctly standardized to 2000-2299" + tags: [test_replaced, test_replaced_validation] + +# MTU Validations +- name: "VALIDATION 2: Verify Fabric MTU was increased to 9216" + assert: + that: + - replaced_fabric_config.management.fabricMtu == 9216 + fail_msg: "Fabric MTU validation failed. Expected: 9216, Actual: {{ replaced_fabric_config.management.fabricMtu }}" + success_msg: "✓ Fabric MTU correctly increased to 9216" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify L2 Host Interface MTU was increased to 9216" + assert: + that: + - replaced_fabric_config.management.l2HostInterfaceMtu == 9216 + fail_msg: "L2 Host Interface MTU validation failed. Expected: 9216, Actual: {{ replaced_fabric_config.management.l2HostInterfaceMtu }}" + success_msg: "✓ L2 Host Interface MTU correctly increased to 9216" + tags: [test_replaced, test_replaced_validation] + +# Gateway and Multicast Validations +- name: "VALIDATION 2: Verify Anycast Gateway MAC was standardized to 2020.0000.00aa" + assert: + that: + - replaced_fabric_config.management.anycastGatewayMac == "2020.0000.00aa" + fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00aa, Actual: {{ replaced_fabric_config.management.anycastGatewayMac }}" + success_msg: "✓ Anycast Gateway MAC correctly standardized to 2020.0000.00aa" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Multicast Group Subnet was standardized to 239.1.1.0/25" + assert: + that: + - replaced_fabric_config.management.multicastGroupSubnet == "239.1.1.0/25" + fail_msg: "Multicast Group Subnet validation failed. Expected: 239.1.1.0/25, Actual: {{ replaced_fabric_config.management.multicastGroupSubnet }}" + success_msg: "✓ Multicast Group Subnet correctly standardized to 239.1.1.0/25" + tags: [test_replaced, test_replaced_validation] + +# VPC Configuration Validations +- name: "VALIDATION 2: Verify VPC Auto Recovery Timer was standardized to 360" + assert: + that: + - replaced_fabric_config.management.vpcAutoRecoveryTimer == 360 + fail_msg: "VPC Auto Recovery Timer validation failed. Expected: 360, Actual: {{ replaced_fabric_config.management.vpcAutoRecoveryTimer }}" + success_msg: "✓ VPC Auto Recovery Timer correctly standardized to 360" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Delay Restore Timer was standardized to 150" + assert: + that: + - replaced_fabric_config.management.vpcDelayRestoreTimer == 150 + fail_msg: "VPC Delay Restore Timer validation failed. Expected: 150, Actual: {{ replaced_fabric_config.management.vpcDelayRestoreTimer }}" + success_msg: "✓ VPC Delay Restore Timer correctly standardized to 150" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Peer Link Port Channel ID was standardized to 500" + assert: + that: + - replaced_fabric_config.management.vpcPeerLinkPortChannelId == "500" + fail_msg: "VPC Peer Link Port Channel ID validation failed. Expected: 500, Actual: {{ replaced_fabric_config.management.vpcPeerLinkPortChannelId }}" + success_msg: "✓ VPC Peer Link Port Channel ID correctly standardized to 500" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Peer Link VLAN was standardized to 3600" + assert: + that: + - replaced_fabric_config.management.vpcPeerLinkVlan == "3600" + fail_msg: "VPC Peer Link VLAN validation failed. Expected: 3600, Actual: {{ replaced_fabric_config.management.vpcPeerLinkVlan }}" + success_msg: "✓ VPC Peer Link VLAN correctly standardized to 3600" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC Domain ID Range was standardized to 1-1000" + assert: + that: + - replaced_fabric_config.management.vpcDomainIdRange == "1-1000" + fail_msg: "VPC Domain ID Range validation failed. Expected: 1-1000, Actual: {{ replaced_fabric_config.management.vpcDomainIdRange }}" + success_msg: "✓ VPC Domain ID Range correctly standardized to 1-1000" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify VPC IPv6 Neighbor Discovery Sync was enabled" + assert: + that: + - replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync == true + fail_msg: "VPC IPv6 Neighbor Discovery Sync validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }}" + success_msg: "✓ VPC IPv6 Neighbor Discovery Sync correctly enabled" + tags: [test_replaced, test_replaced_validation] + +# Multicast Settings Validations +- name: "VALIDATION 2: Verify Rendezvous Point Count was standardized to 2" + assert: + that: + - replaced_fabric_config.management.rendezvousPointCount == 2 + fail_msg: "Rendezvous Point Count validation failed. Expected: 2, Actual: {{ replaced_fabric_config.management.rendezvousPointCount }}" + success_msg: "✓ Rendezvous Point Count correctly standardized to 2" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Rendezvous Point Loopback ID was standardized to 254" + assert: + that: + - replaced_fabric_config.management.rendezvousPointLoopbackId == 254 + fail_msg: "Rendezvous Point Loopback ID validation failed. Expected: 254, Actual: {{ replaced_fabric_config.management.rendezvousPointLoopbackId }}" + success_msg: "✓ Rendezvous Point Loopback ID correctly standardized to 254" + tags: [test_replaced, test_replaced_validation] + +# Feature Flag Validations +- name: "VALIDATION 2: Verify TCAM Allocation was enabled" + assert: + that: + - replaced_fabric_config.management.tcamAllocation == true + fail_msg: "TCAM Allocation validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.tcamAllocation }}" + success_msg: "✓ TCAM Allocation correctly enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Real Time Interface Statistics Collection was disabled" + assert: + that: + - replaced_fabric_config.management.realTimeInterfaceStatisticsCollection == false + fail_msg: "Real Time Interface Statistics Collection validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }}" + success_msg: "✓ Real Time Interface Statistics Collection correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Performance Monitoring was disabled" + assert: + that: + - replaced_fabric_config.management.performanceMonitoring == false + fail_msg: "Performance Monitoring validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.performanceMonitoring }}" + success_msg: "✓ Performance Monitoring correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Tenant DHCP was enabled" + assert: + that: + - replaced_fabric_config.management.tenantDhcp == true + fail_msg: "Tenant DHCP validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.tenantDhcp }}" + success_msg: "✓ Tenant DHCP correctly enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify SNMP Trap was enabled" + assert: + that: + - replaced_fabric_config.management.snmpTrap == true + fail_msg: "SNMP Trap validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.snmpTrap }}" + success_msg: "✓ SNMP Trap correctly enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Greenfield Debug Flag was disabled" + assert: + that: + - replaced_fabric_config.management.greenfieldDebugFlag == "disable" + fail_msg: "Greenfield Debug Flag validation failed. Expected: disable, Actual: {{ replaced_fabric_config.management.greenfieldDebugFlag }}" + success_msg: "✓ Greenfield Debug Flag correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI HTTP was enabled" + assert: + that: + - replaced_fabric_config.management.nxapiHttp == true + fail_msg: "NXAPI HTTP validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.nxapiHttp }}" + success_msg: "✓ NXAPI HTTP correctly enabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify NXAPI was disabled" + assert: + that: + - replaced_fabric_config.management.nxapi == false + fail_msg: "NXAPI validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.nxapi }}" + success_msg: "✓ NXAPI correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision was disabled" + assert: + that: + - replaced_fabric_config.management.perVrfLoopbackAutoProvision == false + fail_msg: "Per VRF Loopback Auto Provision validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvision }}" + success_msg: "✓ Per VRF Loopback Auto Provision correctly disabled" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision IPv6 was disabled" + assert: + that: + - replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 == false + fail_msg: "Per VRF Loopback Auto Provision IPv6 validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }}" + success_msg: "✓ Per VRF Loopback Auto Provision IPv6 correctly disabled" + tags: [test_replaced, test_replaced_validation] + +# Verify banner was preserved +- name: "VALIDATION 2: Verify Banner was preserved" + assert: + that: + - replaced_fabric_config.management.banner == "^ Updated via replaced state ^" + fail_msg: "Banner validation failed. Expected: '^ Updated via replaced state ^', Actual: {{ replaced_fabric_config.management.banner }}" + success_msg: "✓ Banner correctly preserved: '{{ replaced_fabric_config.management.banner }}'" + tags: [test_replaced, test_replaced_validation] + +- name: "VALIDATION 2: Display successful validation summary for test_fabric_replaced" + debug: + msg: | + ======================================== + VALIDATION SUMMARY for test_fabric_replaced: + ======================================== + Network Ranges: + ✓ L3 VNI Range: {{ replaced_fabric_config.management.l3VniRange }} + ✓ L2 VNI Range: {{ replaced_fabric_config.management.l2VniRange }} + ✓ BGP Loopback IP Range: {{ replaced_fabric_config.management.bgpLoopbackIpRange }} + ✓ NVE Loopback IP Range: {{ replaced_fabric_config.management.nveLoopbackIpRange }} + ✓ Intra-Fabric Subnet Range: {{ replaced_fabric_config.management.intraFabricSubnetRange }} + ✓ VRF Lite Subnet Range: {{ replaced_fabric_config.management.vrfLiteSubnetRange }} + ✓ Anycast RP IP Range: {{ replaced_fabric_config.management.anycastRendezvousPointIpRange }} + + VLAN Ranges: + ✓ Network VLAN Range: {{ replaced_fabric_config.management.networkVlanRange }} + ✓ VRF VLAN Range: {{ replaced_fabric_config.management.vrfVlanRange }} + + MTU Settings: + ✓ Fabric MTU: {{ replaced_fabric_config.management.fabricMtu }} + ✓ L2 Host Interface MTU: {{ replaced_fabric_config.management.l2HostInterfaceMtu }} + + VPC Configuration: + ✓ VPC Auto Recovery Timer: {{ replaced_fabric_config.management.vpcAutoRecoveryTimer }} + ✓ VPC Delay Restore Timer: {{ replaced_fabric_config.management.vpcDelayRestoreTimer }} + ✓ VPC Peer Link Port Channel ID: {{ replaced_fabric_config.management.vpcPeerLinkPortChannelId }} + ✓ VPC Peer Link VLAN: {{ replaced_fabric_config.management.vpcPeerLinkVlan }} + ✓ VPC Domain ID Range: {{ replaced_fabric_config.management.vpcDomainIdRange }} + ✓ VPC IPv6 Neighbor Discovery Sync: {{ replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }} + + Gateway & Multicast: + ✓ Anycast Gateway MAC: {{ replaced_fabric_config.management.anycastGatewayMac }} + ✓ Multicast Group Subnet: {{ replaced_fabric_config.management.multicastGroupSubnet }} + ✓ Rendezvous Point Count: {{ replaced_fabric_config.management.rendezvousPointCount }} + ✓ Rendezvous Point Loopback ID: {{ replaced_fabric_config.management.rendezvousPointLoopbackId }} + + Feature Flags: + ✓ TCAM Allocation: {{ replaced_fabric_config.management.tcamAllocation }} + ✓ Real Time Interface Statistics Collection: {{ replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }} + ✓ Performance Monitoring: {{ replaced_fabric_config.management.performanceMonitoring }} + ✓ Tenant DHCP: {{ replaced_fabric_config.management.tenantDhcp }} + ✓ SNMP Trap: {{ replaced_fabric_config.management.snmpTrap }} + ✓ Greenfield Debug Flag: {{ replaced_fabric_config.management.greenfieldDebugFlag }} + ✓ NXAPI HTTP: {{ replaced_fabric_config.management.nxapiHttp }} + ✓ NXAPI: {{ replaced_fabric_config.management.nxapi }} + + Auto-Provisioning: + ✓ Per VRF Loopback Auto Provision: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvision }} + ✓ Per VRF Loopback Auto Provision IPv6: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }} + + Preserved Settings: + ✓ Banner: "{{ replaced_fabric_config.management.banner }}" + + All 30+ expected changes validated successfully! + ======================================== + tags: [test_replaced, test_replaced_validation] + +# - name: "PAUSE: Review TEST 2c results before continuing" +# ansible.builtin.pause: +# prompt: "TEST 2c complete. Review results and press Enter to continue, or Ctrl+C then A to abort" +# tags: [test_replaced, test_replaced_idempotent] + +############################################################################# +# TEST 3: Demonstrate difference between merged and replaced states +############################################################################# +- name: "TEST 3: Create fabric for merged vs replaced comparison" + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - "{{ {'name': test_fabric_deleted} | combine(common_fabric_config) }}" + register: comparison_fabric_creation + tags: [test_comparison] + +- name: "TEST 3a: Partial update using merged state (should merge changes)" + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - name: "{{ test_fabric_deleted }}" + category: fabric + management: + bgp_asn: "65099" # Only updating ASN + fabric_mtu: 8000 # Only updating MTU + register: merged_partial_result + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3a: Verify merged state preserves existing configuration" + assert: + that: + - merged_partial_result is changed + - merged_partial_result is not failed + fail_msg: "Partial update with merged state failed" + success_msg: "Merged state successfully performed partial update" + tags: [test_comparison, test_merged_partial] + +- name: "TEST 3b: Partial update using replaced state (should replace entire config)" + cisco.nd.nd_manage_fabric_ibgp: + state: replaced + config: + - name: "{{ test_fabric_deleted }}" + category: fabric + management: + type: vxlanIbgp + bgp_asn: "65100" # Only specifying minimal config for replaced + target_subnet_mask: 30 + register: replaced_partial_result + tags: [test_comparison, test_replaced_partial] + +- name: "TEST 3b: Verify replaced state performs complete replacement" + assert: + that: + - replaced_partial_result is changed + - replaced_partial_result is not failed + fail_msg: "Partial replacement with replaced state failed" + success_msg: "Replaced state successfully performed complete replacement" + tags: [test_comparison, test_replaced_partial] + +############################################################################# +# TEST 4: STATE DELETED - Delete fabrics +############################################################################# +- name: "TEST 4a: Delete fabric using state deleted" + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: "{{ test_fabric_deleted }}" + register: deleted_result_1 + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4a: Verify fabric was deleted" + assert: + that: + - deleted_result_1 is changed + - deleted_result_1 is not failed + fail_msg: "Fabric deletion with state deleted failed" + success_msg: "Fabric successfully deleted with state deleted" + tags: [test_deleted, test_deleted_delete] + +- name: "TEST 4b: Delete fabric using state deleted (second run - idempotency test)" + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: "{{ test_fabric_deleted }}" + register: deleted_result_2 + tags: [test_deleted, test_deleted_idempotent] + +- name: "TEST 4b: Verify deleted state is idempotent" + assert: + that: + - deleted_result_2 is not changed + - deleted_result_2 is not failed + fail_msg: "Deleted state is not idempotent - should not change when deleting non-existent fabric" + success_msg: "Deleted state is idempotent - no changes when deleting non-existent fabric" + tags: [test_deleted, test_deleted_idempotent] + +############################################################################# +# TEST 5: Multiple fabric operations in single task +############################################################################# +- name: "TEST 5: Multiple fabric operations in single task" + cisco.nd.nd_manage_fabric_ibgp: + state: merged + config: + - name: "multi_fabric_1" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65101" + site_id: "65101" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0001" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + # vpc_ipv6_neighbor_discovery_sync: true + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: enable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.101.0.0/22" + nve_loopback_ip_range: "10.103.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.101.0/24" + intra_fabric_subnet_range: "10.104.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.133.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.105.0.0/22" + # per_vrf_loopback_auto_provision_ipv6: false + # per_vrf_loopback_ipv6_range: "fd00::a105:0/112" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + # management_ipv6_prefix: 64 + - name: "multi_fabric_2" + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65102" + site_id: "65102" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.0002" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + # vpc_ipv6_neighbor_discovery_sync: true + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: enable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.102.0.0/22" + nve_loopback_ip_range: "10.103.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.102.0/24" + intra_fabric_subnet_range: "10.104.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.134.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.106.0.0/22" + # per_vrf_loopback_auto_provision_ipv6: false + # per_vrf_loopback_ipv6_range: "fd00::a106:0/112" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + # management_ipv6_prefix: 64 + register: multi_fabric_result + tags: [test_multi, test_multi_create] + +- name: "TEST 5: Verify multiple fabrics were created" + assert: + that: + - multi_fabric_result is changed + - multi_fabric_result is not failed + fail_msg: "Multiple fabric creation failed" + success_msg: "Multiple fabrics successfully created" + tags: [test_multi, test_multi_create] + +############################################################################# +# FINAL CLEANUP - Clean up all test fabrics +############################################################################# +- name: "CLEANUP: Delete all test fabrics" + cisco.nd.nd_manage_fabric_ibgp: + state: deleted + config: + - name: "{{ test_fabric_merged }}" + - name: "{{ test_fabric_replaced }}" + - name: "{{ test_fabric_deleted }}" + - name: "multi_fabric_1" + - name: "multi_fabric_2" + ignore_errors: true + tags: [cleanup, always] + +############################################################################# +# TEST SUMMARY +############################################################################# +- name: "TEST SUMMARY: Display test results" + debug: + msg: | + ======================================================== + TEST SUMMARY for cisco.nd.nd_manage_fabric_ibgp module: + ======================================================== + ✓ TEST 1: STATE MERGED + - Create fabric: {{ 'PASSED' if merged_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if merged_result_2 is not changed else 'FAILED' }} + - Update fabric: {{ 'PASSED' if merged_result_3 is changed else 'FAILED' }} + + ✓ TEST 2: STATE REPLACED + - Create fabric: {{ 'PASSED' if replaced_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if replaced_result_2 is not changed else 'FAILED' }} + - Replace fabric: {{ 'PASSED' if replaced_result_3 is changed else 'FAILED' }} + + ✓ TEST 3: MERGED vs REPLACED Comparison + - Merged partial: {{ 'PASSED' if merged_partial_result is changed else 'FAILED' }} + - Replaced partial: {{ 'PASSED' if replaced_partial_result is changed else 'FAILED' }} + + ✓ TEST 4: STATE DELETED + - Delete fabric: {{ 'PASSED' if deleted_result_1 is changed else 'FAILED' }} + - Idempotency: {{ 'PASSED' if deleted_result_2 is not changed else 'FAILED' }} + + ✓ TEST 5: MULTIPLE FABRICS + - Multi-create: {{ 'PASSED' if multi_fabric_result is changed else 'FAILED' }} + + All tests validate: + - State merged: Creates and updates fabrics by merging changes + - State replaced: Creates and completely replaces fabric configuration + - State deleted: Removes fabrics + - Idempotency: All operations are idempotent when run multiple times + - Difference: Merged preserves existing config, replaced overwrites completely + ======================================== + tags: [summary, always] \ No newline at end of file diff --git a/tests/integration/targets/nd_manage_fabric/tasks/main.yaml b/tests/integration/targets/nd_manage_fabric/tasks/main.yaml new file mode 100644 index 00000000..eacc3be3 --- /dev/null +++ b/tests/integration/targets/nd_manage_fabric/tasks/main.yaml @@ -0,0 +1,9 @@ +--- +- name: Run nd_manage_fabric iBGP tests + ansible.builtin.include_tasks: fabric_ibgp.yaml + +- name: Run nd_manage_fabric eBGP tests + ansible.builtin.include_tasks: fabric_ebgp.yaml + +- name: Run nd_manage_fabric External Connectivity tests + ansible.builtin.include_tasks: fabric_external.yaml diff --git a/tests/integration/targets/nd_manage_fabric/vars/main.yaml b/tests/integration/targets/nd_manage_fabric/vars/main.yaml new file mode 100644 index 00000000..d15748d5 --- /dev/null +++ b/tests/integration/targets/nd_manage_fabric/vars/main.yaml @@ -0,0 +1,209 @@ +--- + +test_fabric_merged: "ibgp_test_fabric_merged" +test_fabric_replaced: "ibgp_test_fabric_replaced" +test_fabric_deleted: "ibgp_test_fabric_deleted" + +ebgp_test_fabric_merged: "ebgp_test_fabric_merged" +ebgp_test_fabric_replaced: "ebgp_test_fabric_replaced" +ebgp_test_fabric_deleted: "ebgp_test_fabric_deleted" + +ext_test_fabric_merged: "ext_test_fabric_merged" +ext_test_fabric_replaced: "ext_test_fabric_replaced" +ext_test_fabric_deleted: "ext_test_fabric_deleted" + +# Common fabric configuration for all tests +common_fabric_config: + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanIbgp + bgp_asn: "65001.55" + site_id: "65001" + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00aa" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: loopback + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: enable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + +# Common External Connectivity fabric configuration for all External tests +common_external_fabric_config: + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: externalConnectivity + bgp_asn: "65001" + copp_policy: manual + create_bgp_config: true + cdp: false + snmp_trap: true + nxapi: false + nxapi_http: false + nxapi_https_port: 443 + nxapi_http_port: 80 + performance_monitoring: false + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + sub_interface_dot1q_range: "2-511" + power_redundancy_mode: redundant + ptp: false + ptp_domain_id: 0 + ptp_loopback_id: 0 + mpls_handoff: false + mpls_loopback_ip_range: "10.102.0.0/25" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 + +# Common eBGP fabric configuration for all eBGP tests +common_ebgp_fabric_config: + category: fabric + location: + latitude: 37.7749 + longitude: -122.4194 + license_tier: premier + alert_suspend: disabled + security_domain: all + telemetry_collection: false + management: + type: vxlanEbgp + bgp_asn: "65001" + bgp_asn_auto_allocation: false + site_id: "65001" + bgp_as_mode: multiAS + bgp_allow_as_in_num: 1 + bgp_max_path: 4 + auto_configure_ebgp_evpn_peering: true + target_subnet_mask: 30 + anycast_gateway_mac: "2020.0000.00aa" + performance_monitoring: false + replication_mode: multicast + multicast_group_subnet: "239.1.1.0/25" + auto_generate_multicast_group_address: false + underlay_multicast_group_address_limit: 128 + tenant_routed_multicast: false + rendezvous_point_count: 2 + rendezvous_point_loopback_id: 254 + vpc_peer_link_vlan: "3600" + vpc_peer_link_enable_native_vlan: false + vpc_peer_keep_alive_option: management + vpc_auto_recovery_timer: 360 + vpc_delay_restore_timer: 150 + vpc_peer_link_port_channel_id: "500" + advertise_physical_ip: false + vpc_domain_id_range: "1-1000" + bgp_loopback_id: 0 + nve_loopback_id: 1 + vrf_template: Default_VRF_Universal + network_template: Default_Network_Universal + vrf_extension_template: Default_VRF_Extension_Universal + network_extension_template: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: false + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 + tenant_dhcp: true + nxapi: false + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + anycast_border_gateway_advertise_physical_ip: false + greenfield_debug_flag: disable + tcam_allocation: true + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + l2_vni_range: "30000-49000" + l3_vni_range: "50000-59000" + network_vlan_range: "2300-2999" + vrf_vlan_range: "2000-2299" + sub_interface_dot1q_range: "2-511" + vrf_lite_auto_config: manual + vrf_lite_subnet_range: "10.33.0.0/16" + vrf_lite_subnet_target_mask: 30 + auto_unique_vrf_lite_ip_prefix: false + per_vrf_loopback_auto_provision: true + per_vrf_loopback_ip_range: "10.5.0.0/22" + banner: "" + day0_bootstrap: false + local_dhcp_server: false + dhcp_protocol_version: dhcpv4 + dhcp_start_address: "" + dhcp_end_address: "" + management_gateway: "" + management_ipv4_prefix: 24 From f8b924bdcd3aec321ef46046c1cfd07a8017762a Mon Sep 17 00:00:00 2001 From: mwiebe Date: Fri, 27 Mar 2026 15:36:32 -0400 Subject: [PATCH 02/27] Update ibgp model enums --- .../models/manage_fabric/enums.py | 144 ++++++++++++++++++ .../manage_fabric/manage_fabric_ibgp.py | 72 +++++---- .../nd_manage_fabric/tasks/fabric_ebgp.yaml | 4 +- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 4 +- 4 files changed, 188 insertions(+), 36 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/enums.py b/plugins/module_utils/models/manage_fabric/enums.py index 5d36756c..2386db54 100644 --- a/plugins/module_utils/models/manage_fabric/enums.py +++ b/plugins/module_utils/models/manage_fabric/enums.py @@ -64,10 +64,12 @@ class LicenseTierEnum(str, Enum): ## Values - `ESSENTIALS` - Essentials license tier + - `ADVANTAGE` - Advantage license tier - `PREMIER` - Premier license tier """ ESSENTIALS = "essentials" + ADVANTAGE = "advantage" PREMIER = "premier" @@ -249,3 +251,145 @@ class FirstHopRedundancyProtocolEnum(str, Enum): HSRP = "hsrp" VRRP = "vrrp" + + +class AimlQosPolicyEnum(str, Enum): + """ + # Summary + + Enumeration for AI/ML QoS policy options based on fabric link speed. + """ + + V_800G = "800G" + V_400G = "400G" + V_100G = "100G" + V_25G = "25G" + USER_DEFINED = "User-defined" + + +class AllowVlanOnLeafTorPairingEnum(str, Enum): + """ + # Summary + + Enumeration for allowed VLAN on leaf-TOR pairing port-channels. + """ + + NONE = "none" + ALL = "all" + + +class BgpAuthenticationKeyTypeEnum(str, Enum): + """ + # Summary + + Enumeration for BGP authentication key encryption types. + """ + + THREE_DES = "3des" + TYPE6 = "type6" + TYPE7 = "type7" + + +class DlbMixedModeDefaultEnum(str, Enum): + """ + # Summary + + Enumeration for DLB mixed mode default options. + """ + + ECMP = "ecmp" + FLOWLET = "flowlet" + PER_PACKET = "per-packet" + + +class DlbModeEnum(str, Enum): + """ + # Summary + + Enumeration for DLB mode options. + """ + + FLOWLET = "flowlet" + PER_PACKET = "per-packet" + POLICY_DRIVEN_FLOWLET = "policy-driven-flowlet" + POLICY_DRIVEN_PER_PACKET = "policy-driven-per-packet" + POLICY_DRIVEN_MIXED_MODE = "policy-driven-mixed-mode" + + +class MacsecAlgorithmEnum(str, Enum): + """ + # Summary + + Enumeration for MACsec cryptographic algorithm options. + """ + + AES_128_CMAC = "AES_128_CMAC" + AES_256_CMAC = "AES_256_CMAC" + + +class MacsecCipherSuiteEnum(str, Enum): + """ + # Summary + + Enumeration for MACsec cipher suite options. + """ + + GCM_AES_128 = "GCM-AES-128" + GCM_AES_256 = "GCM-AES-256" + GCM_AES_XPN_128 = "GCM-AES-XPN-128" + GCM_AES_XPN_256 = "GCM-AES-XPN-256" + + +class RendezvousPointCountEnum(int, Enum): + """ + # Summary + + Enumeration for number of spines acting as Rendezvous-Points. + """ + + TWO = 2 + FOUR = 4 + + +class RendezvousPointModeEnum(str, Enum): + """ + # Summary + + Enumeration for multicast rendezvous point mode. + """ + + ASM = "asm" + BIDIR = "bidir" + + +class RouteReflectorCountEnum(int, Enum): + """ + # Summary + + Enumeration for number of spines acting as Route-Reflectors. + """ + + TWO = 2 + FOUR = 4 + + +class UnderlayMulticastGroupAddressLimitEnum(int, Enum): + """ + # Summary + + Enumeration for underlay multicast group address limit. + """ + + V_128 = 128 + V_512 = 512 + + +class VrfLiteAutoConfigEnum(str, Enum): + """ + # Summary + + Enumeration for VRF Lite auto-config deployment options. + """ + + MANUAL = "manual" + BACK2BACK_AND_TO_EXTERNAL = "back2BackAndToExternal" diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index 5e8169de..b68aeaf6 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -38,6 +38,20 @@ SecurityGroupStatusEnum, StpRootOptionEnum, VpcPeerKeepAliveOptionEnum, + AimlQosPolicyEnum, + AllowVlanOnLeafTorPairingEnum, + BgpAuthenticationKeyTypeEnum, + DhcpProtocolVersionEnum, + DlbMixedModeDefaultEnum, + DlbModeEnum, + MacsecAlgorithmEnum, + MacsecCipherSuiteEnum, + PowerRedundancyModeEnum, + RendezvousPointCountEnum, + RendezvousPointModeEnum, + RouteReflectorCountEnum, + UnderlayMulticastGroupAddressLimitEnum, + VrfLiteAutoConfigEnum, ) @@ -547,12 +561,10 @@ class VxlanIbgpManagementModel(NDNestedModel): description="Auto-generate multicast group addresses", default=False ) - underlay_multicast_group_address_limit: int = Field( + underlay_multicast_group_address_limit: UnderlayMulticastGroupAddressLimitEnum = Field( alias="underlayMulticastGroupAddressLimit", description="Underlay multicast group address limit", - ge=1, - le=255, - default=128 + default=UnderlayMulticastGroupAddressLimitEnum.V_128 ) tenant_routed_multicast: bool = Field( alias="tenantRoutedMulticast", @@ -616,12 +628,10 @@ class VxlanIbgpManagementModel(NDNestedModel): # Loopback Configuration bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="BGP loopback interface ID", ge=0, le=1023, default=0) nve_loopback_id: int = Field(alias="nveLoopbackId", description="NVE loopback interface ID", ge=0, le=1023, default=1) - route_reflector_count: int = Field( + route_reflector_count: RouteReflectorCountEnum = Field( alias="routeReflectorCount", description="Number of route reflectors", - ge=1, - le=4, - default=2 + default=RouteReflectorCountEnum.TWO ) # Templates @@ -650,10 +660,10 @@ class VxlanIbgpManagementModel(NDNestedModel): # Protocol Settings bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enable BGP authentication", default=False) - bgp_authentication_key_type: str = Field( + bgp_authentication_key_type: BgpAuthenticationKeyTypeEnum = Field( alias="bgpAuthenticationKeyType", description="BGP authentication key type", - default="3des" + default=BgpAuthenticationKeyTypeEnum.THREE_DES ) bfd: bool = Field(description="Enable BFD", default=False) bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD for iBGP", default=False) @@ -680,12 +690,10 @@ class VxlanIbgpManagementModel(NDNestedModel): ) # Multicast Settings - rendezvous_point_count: int = Field( + rendezvous_point_count: RendezvousPointCountEnum = Field( alias="rendezvousPointCount", description="Number of rendezvous points", - ge=1, - le=4, - default=2 + default=RendezvousPointCountEnum.TWO ) rendezvous_point_loopback_id: int = Field( alias="rendezvousPointLoopbackId", @@ -722,7 +730,7 @@ class VxlanIbgpManagementModel(NDNestedModel): # Bootstrap / Day-0 / DHCP local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) - dhcp_protocol_version: str = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default="dhcpv4") + dhcp_protocol_version: DhcpProtocolVersionEnum = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default=DhcpProtocolVersionEnum.DHCPV4) dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") @@ -775,7 +783,7 @@ class VxlanIbgpManagementModel(NDNestedModel): ) l3vni_multicast_group: str = Field(alias="l3vniMulticastGroup", description="L3 VNI multicast group", default="239.1.1.0") l3_vni_ipv6_multicast_group: str = Field(alias="l3VniIpv6MulticastGroup", description="L3 VNI IPv6 multicast group", default="ff1e::") - rendezvous_point_mode: str = Field(alias="rendezvousPointMode", description="Rendezvous point mode", default="asm") + rendezvous_point_mode: RendezvousPointModeEnum = Field(alias="rendezvousPointMode", description="Rendezvous point mode", default=RendezvousPointModeEnum.ASM) phantom_rendezvous_point_loopback_id1: int = Field( alias="phantomRendezvousPointLoopbackId1", description="Phantom RP loopback ID 1", default=2 ) @@ -792,7 +800,7 @@ class VxlanIbgpManagementModel(NDNestedModel): # VRF Lite / Sub-Interface sub_interface_dot1q_range: str = Field(alias="subInterfaceDot1qRange", description="Sub-interface 802.1q range", default="2-511") - vrf_lite_auto_config: str = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default="manual") + vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default=VrfLiteAutoConfigEnum.MANUAL) vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="VRF lite subnet range", default="10.33.0.0/16") vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF lite subnet target mask", default=30) auto_unique_vrf_lite_ip_prefix: bool = Field( @@ -868,27 +876,27 @@ class VxlanIbgpManagementModel(NDNestedModel): # MACsec macsec: bool = Field(description="Enable MACsec", default=False) - macsec_cipher_suite: str = Field(alias="macsecCipherSuite", description="MACsec cipher suite", default="GCM-AES-XPN-256") + macsec_cipher_suite: MacsecCipherSuiteEnum = Field(alias="macsecCipherSuite", description="MACsec cipher suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256) macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec key string", default="") - macsec_algorithm: str = Field(alias="macsecAlgorithm", description="MACsec algorithm", default="AES_128_CMAC") + macsec_algorithm: MacsecAlgorithmEnum = Field(alias="macsecAlgorithm", description="MACsec algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC) macsec_fallback_key_string: str = Field(alias="macsecFallbackKeyString", description="MACsec fallback key string", default="") - macsec_fallback_algorithm: str = Field(alias="macsecFallbackAlgorithm", description="MACsec fallback algorithm", default="AES_128_CMAC") + macsec_fallback_algorithm: MacsecAlgorithmEnum = Field(alias="macsecFallbackAlgorithm", description="MACsec fallback algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC) macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec report timer", default=5) # VRF Lite MACsec vrf_lite_macsec: bool = Field(alias="vrfLiteMacsec", description="Enable VRF lite MACsec", default=False) - vrf_lite_macsec_cipher_suite: str = Field( - alias="vrfLiteMacsecCipherSuite", description="VRF lite MACsec cipher suite", default="GCM-AES-XPN-256" + vrf_lite_macsec_cipher_suite: MacsecCipherSuiteEnum = Field( + alias="vrfLiteMacsecCipherSuite", description="VRF lite MACsec cipher suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 ) vrf_lite_macsec_key_string: str = Field(alias="vrfLiteMacsecKeyString", description="VRF lite MACsec key string", default="") - vrf_lite_macsec_algorithm: str = Field( - alias="vrfLiteMacsecAlgorithm", description="VRF lite MACsec algorithm", default="AES_128_CMAC" + vrf_lite_macsec_algorithm: MacsecAlgorithmEnum = Field( + alias="vrfLiteMacsecAlgorithm", description="VRF lite MACsec algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC ) vrf_lite_macsec_fallback_key_string: str = Field( alias="vrfLiteMacsecFallbackKeyString", description="VRF lite MACsec fallback key string", default="" ) - vrf_lite_macsec_fallback_algorithm: str = Field( - alias="vrfLiteMacsecFallbackAlgorithm", description="VRF lite MACsec fallback algorithm", default="AES_128_CMAC" + vrf_lite_macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( + alias="vrfLiteMacsecFallbackAlgorithm", description="VRF lite MACsec fallback algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC ) # Quantum Key Distribution / Trustpoint @@ -945,7 +953,7 @@ class VxlanIbgpManagementModel(NDNestedModel): alias="defaultQueuingPolicyOther", description="Default queuing policy other", default="queuing_policy_default_other" ) aiml_qos: bool = Field(alias="aimlQos", description="Enable AI/ML QoS", default=False) - aiml_qos_policy: str = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default="400G") + aiml_qos_policy: AimlQosPolicyEnum = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default=AimlQosPolicyEnum.V_400G) roce_v2: str = Field(alias="roceV2", description="RoCEv2 DSCP value", default="26") cnp: str = Field(description="CNP value", default="48") wred_min: int = Field(alias="wredMin", description="WRED minimum threshold", default=950) @@ -954,8 +962,8 @@ class VxlanIbgpManagementModel(NDNestedModel): wred_weight: int = Field(alias="wredWeight", description="WRED weight", default=0) bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Bandwidth remaining percentage", default=50) dlb: bool = Field(description="Enable dynamic load balancing", default=False) - dlb_mode: str = Field(alias="dlbMode", description="DLB mode", default="flowlet") - dlb_mixed_mode_default: str = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default="ecmp") + dlb_mode: DlbModeEnum = Field(alias="dlbMode", description="DLB mode", default=DlbModeEnum.FLOWLET) + dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default=DlbMixedModeDefaultEnum.ECMP) flowlet_aging: int = Field(alias="flowletAging", description="Flowlet aging interval", default=1) flowlet_dscp: str = Field(alias="flowletDscp", description="Flowlet DSCP value", default="") per_packet_dscp: str = Field(alias="perPacketDscp", description="Per-packet DSCP value", default="") @@ -989,8 +997,8 @@ class VxlanIbgpManagementModel(NDNestedModel): description="Default private VLAN secondary network template", default="Pvlan_Secondary_Network" ) - allow_vlan_on_leaf_tor_pairing: str = Field( - alias="allowVlanOnLeafTorPairing", description="Allow VLAN on leaf/TOR pairing", default="none" + allow_vlan_on_leaf_tor_pairing: AllowVlanOnLeafTorPairingEnum = Field( + alias="allowVlanOnLeafTorPairing", description="Allow VLAN on leaf/TOR pairing", default=AllowVlanOnLeafTorPairingEnum.NONE ) # Leaf / TOR @@ -1063,7 +1071,7 @@ class VxlanIbgpManagementModel(NDNestedModel): ) advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) copp_policy: CoppPolicyEnum = Field(alias="coppPolicy", description="CoPP policy", default=CoppPolicyEnum.STRICT) - power_redundancy_mode: str = Field(alias="powerRedundancyMode", description="Power redundancy mode", default="redundant") + power_redundancy_mode: PowerRedundancyModeEnum = Field(alias="powerRedundancyMode", description="Power redundancy mode", default=PowerRedundancyModeEnum.REDUNDANT) host_interface_admin_state: bool = Field( alias="hostInterfaceAdminState", description="Host interface admin state", default=True ) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml index f8cf517e..1d22adb2 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml @@ -286,7 +286,7 @@ auto_generate_multicast_group_address: false underlay_multicast_group_address_limit: 128 tenant_routed_multicast: false - rendezvous_point_count: 3 # Different from default 2 + rendezvous_point_count: 4 # Different from default 2 rendezvous_point_loopback_id: 253 # Different from default 254 vpc_peer_link_vlan: "3700" # Different from default 3600 vpc_peer_link_enable_native_vlan: false @@ -386,7 +386,7 @@ auto_generate_multicast_group_address: false underlay_multicast_group_address_limit: 128 tenant_routed_multicast: false - rendezvous_point_count: 3 + rendezvous_point_count: 4 rendezvous_point_loopback_id: 253 vpc_peer_link_vlan: "3700" vpc_peer_link_enable_native_vlan: false diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index 30b77c59..b4385dc2 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -282,7 +282,7 @@ auto_generate_multicast_group_address: false underlay_multicast_group_address_limit: 128 tenant_routed_multicast: false - rendezvous_point_count: 3 # DIfferent from default count + rendezvous_point_count: 4 # DIfferent from default count rendezvous_point_loopback_id: 253 # DIfferent from default loopback vpc_peer_link_vlan: "3700" # DIfferent from default VLAN vpc_peer_link_enable_native_vlan: false @@ -376,7 +376,7 @@ auto_generate_multicast_group_address: false underlay_multicast_group_address_limit: 128 tenant_routed_multicast: false - rendezvous_point_count: 3 # DIfferent from default count + rendezvous_point_count: 4 # DIfferent from default count rendezvous_point_loopback_id: 253 # DIfferent from default loopback vpc_peer_link_vlan: "3700" # DIfferent from default VLAN vpc_peer_link_enable_native_vlan: false From a4f7f9189f65c65a935f4d0f665186ede6391a11 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Fri, 27 Mar 2026 17:37:46 -0400 Subject: [PATCH 03/27] Update pydantic model and module docstrings for ibgp --- .../manage_fabric/manage_fabric_ibgp.py | 1343 +++++++++++++---- plugins/modules/nd_manage_fabric_ibgp.py | 583 ++++++- 2 files changed, 1610 insertions(+), 316 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index b68aeaf6..4275dbd9 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -464,7 +464,11 @@ class ExternalStreamingSettingsModel(NDNestedModel): ) email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) - message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) + message_bus: List[Dict[str, Any]] = Field( + alias="messageBus", + description="Message bus configuration", + default_factory=list + ) syslog: Dict[str, Any] = Field( description="Syslog streaming configuration", default_factory=lambda: { @@ -499,11 +503,18 @@ class VxlanIbgpManagementModel(NDNestedModel): ) # Fabric Type (required for discriminated union) - type: Literal[FabricTypeEnum.VXLAN_IBGP] = Field(description="Fabric management type", default=FabricTypeEnum.VXLAN_IBGP) + type: Literal[FabricTypeEnum.VXLAN_IBGP] = Field( + description="Type of the fabric", + default=FabricTypeEnum.VXLAN_IBGP + ) # Core iBGP Configuration - bgp_asn: str = Field(alias="bgpAsn", description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]") - site_id: Optional[str] = Field(alias="siteId", description="Site identifier for the fabric", default="") + bgp_asn: str = Field(alias="bgpAsn", description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]") + site_id: Optional[str] = Field( + alias="siteId", + description="For EVPN Multi-Site Support. Defaults to Fabric ASN", + default="" + ) # Name under management section is optional for backward compatibility, but if provided must be non-empty string name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") @@ -519,509 +530,1191 @@ class VxlanIbgpManagementModel(NDNestedModel): # Network Addressing bgp_loopback_ip_range: str = Field( alias="bgpLoopbackIpRange", - description="BGP loopback IP range", + description="Typically Loopback0 IP Address Range", default="10.2.0.0/22" ) nve_loopback_ip_range: str = Field( alias="nveLoopbackIpRange", - description="NVE loopback IP range", + description="Typically Loopback1 IP Address Range", default="10.3.0.0/22" ) anycast_rendezvous_point_ip_range: str = Field( alias="anycastRendezvousPointIpRange", - description="Anycast RP IP range", + description="Anycast or Phantom RP IP Address Range", default="10.254.254.0/24" ) intra_fabric_subnet_range: str = Field( alias="intraFabricSubnetRange", - description="Intra-fabric subnet range", + description="Address range to assign numbered and peer link SVI IPs", default="10.4.0.0/16" ) # VLAN and VNI Ranges - l2_vni_range: str = Field(alias="l2VniRange", description="Layer 2 VNI range", default="30000-49000") - l3_vni_range: str = Field(alias="l3VniRange", description="Layer 3 VNI range", default="50000-59000") - network_vlan_range: str = Field(alias="networkVlanRange", description="Network VLAN range", default="2300-2999") - vrf_vlan_range: str = Field(alias="vrfVlanRange", description="VRF VLAN range", default="2000-2299") + l2_vni_range: str = Field( + alias="l2VniRange", + description="Overlay network identifier range (minimum: 1, maximum: 16777214)", + default="30000-49000" + ) + l3_vni_range: str = Field( + alias="l3VniRange", + description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", + default="50000-59000" + ) + network_vlan_range: str = Field( + alias="networkVlanRange", + description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", + default="2300-2999" + ) + vrf_vlan_range: str = Field( + alias="vrfVlanRange", + description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", + default="2000-2299" + ) # Overlay Configuration - overlay_mode: OverlayModeEnum = Field(alias="overlayMode", description="Overlay configuration mode", default=OverlayModeEnum.CLI) + overlay_mode: OverlayModeEnum = Field( + alias="overlayMode", + description="Overlay Mode. VRF/Network configuration using config-profile or CLI", + default=OverlayModeEnum.CLI + ) replication_mode: ReplicationModeEnum = Field( alias="replicationMode", - description="Multicast replication mode", + description="Replication Mode for BUM Traffic", default=ReplicationModeEnum.MULTICAST ) multicast_group_subnet: str = Field( alias="multicastGroupSubnet", - description="Multicast group subnet", + description=( + "Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool is used for BUM traffic for " + "each overlay network." + ), default="239.1.1.0/25" ) auto_generate_multicast_group_address: bool = Field( alias="autoGenerateMulticastGroupAddress", - description="Auto-generate multicast group addresses", + description="Generate a new multicast group address from the multicast pool using a round-robin approach", default=False ) underlay_multicast_group_address_limit: UnderlayMulticastGroupAddressLimitEnum = Field( alias="underlayMulticastGroupAddressLimit", - description="Underlay multicast group address limit", + description=( + "The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " + "and 512 for versions above 10.2(1)" + ), default=UnderlayMulticastGroupAddressLimitEnum.V_128 ) tenant_routed_multicast: bool = Field( alias="tenantRoutedMulticast", - description="Enable tenant routed multicast", + description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", default=False ) # Underlay Configuration link_state_routing_protocol: LinkStateRoutingProtocolEnum = Field( alias="linkStateRoutingProtocol", - description="Underlay routing protocol", + description="Underlay Routing Protocol. Used for Spine-Leaf Connectivity", default=LinkStateRoutingProtocolEnum.OSPF ) - ospf_area_id: str = Field(alias="ospfAreaId", description="OSPF area ID", default="0.0.0.0") - fabric_interface_type: FabricInterfaceTypeEnum = Field(alias="fabricInterfaceType", description="Fabric interface type", default=FabricInterfaceTypeEnum.P2P) + ospf_area_id: str = Field(alias="ospfAreaId", description="OSPF Area Id in IP address format", default="0.0.0.0") + fabric_interface_type: FabricInterfaceTypeEnum = Field( + alias="fabricInterfaceType", + description="Numbered(Point-to-Point) or unNumbered", + default=FabricInterfaceTypeEnum.P2P + ) # Advanced Features - target_subnet_mask: int = Field(alias="targetSubnetMask", description="Target subnet mask", ge=24, le=31, default=30) + target_subnet_mask: int = Field( + alias="targetSubnetMask", + description="Mask for underlay subnet IP range", + ge=24, + le=31, + default=30 + ) anycast_gateway_mac: str = Field( alias="anycastGatewayMac", - description="Anycast gateway MAC address", + description="Shared anycast gateway MAC address for all VTEPs", default="2020.0000.00aa" ) - fabric_mtu: int = Field(alias="fabricMtu", description="Fabric MTU size", ge=1500, le=9216, default=9216) + fabric_mtu: int = Field( + alias="fabricMtu", + description="Intra Fabric Interface MTU. Must be an even number", + ge=1500, + le=9216, + default=9216 + ) l2_host_interface_mtu: int = Field( alias="l2HostInterfaceMtu", - description="L2 host interface MTU", + description="Layer 2 host interface MTU. Must be an even number", ge=1500, le=9216, default=9216 ) # VPC Configuration - vpc_domain_id_range: str = Field(alias="vpcDomainIdRange", description="vPC domain ID range", default="1-1000") - vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="vPC peer link VLAN", default="3600") + vpc_domain_id_range: str = Field( + alias="vpcDomainIdRange", + description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", + default="1-1000" + ) + vpc_peer_link_vlan: str = Field( + alias="vpcPeerLinkVlan", + description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", + default="3600" + ) vpc_peer_link_enable_native_vlan: bool = Field( alias="vpcPeerLinkEnableNativeVlan", - description="Enable native VLAN on vPC peer link", + description="Enable VpcPeer Link for Native Vlan", default=False ) vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( alias="vpcPeerKeepAliveOption", - description="vPC peer keep-alive option", + description="Use vPC Peer Keep Alive with Loopback or Management", default=VpcPeerKeepAliveOptionEnum.MANAGEMENT ) vpc_auto_recovery_timer: int = Field( alias="vpcAutoRecoveryTimer", - description="vPC auto recovery timer", + description="vPC auto recovery timer (in seconds)", ge=240, le=3600, default=360 ) vpc_delay_restore_timer: int = Field( alias="vpcDelayRestoreTimer", - description="vPC delay restore timer", + description="vPC delay restore timer (in seconds)", ge=1, le=3600, default=150 ) # Loopback Configuration - bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="BGP loopback interface ID", ge=0, le=1023, default=0) - nve_loopback_id: int = Field(alias="nveLoopbackId", description="NVE loopback interface ID", ge=0, le=1023, default=1) + bgp_loopback_id: int = Field( + alias="bgpLoopbackId", + description="Underlay Routing Loopback Id", + ge=0, + le=1023, + default=0 + ) + nve_loopback_id: int = Field( + alias="nveLoopbackId", + description="Underlay VTEP loopback Id associated with the Network Virtualization Edge (nve) interface", + ge=0, + le=1023, + default=1 + ) route_reflector_count: RouteReflectorCountEnum = Field( alias="routeReflectorCount", - description="Number of route reflectors", + description="Number of spines acting as Route-Reflectors", default=RouteReflectorCountEnum.TWO ) # Templates - vrf_template: str = Field(alias="vrfTemplate", description="VRF template", default="Default_VRF_Universal") - network_template: str = Field(alias="networkTemplate", description="Network template", default="Default_Network_Universal") + vrf_template: str = Field( + alias="vrfTemplate", + description="Default overlay VRF template for leafs", + default="Default_VRF_Universal" + ) + network_template: str = Field( + alias="networkTemplate", + description="Default overlay network template for leafs", + default="Default_Network_Universal" + ) vrf_extension_template: str = Field( alias="vrfExtensionTemplate", - description="VRF extension template", + description="Default overlay VRF template for borders", default="Default_VRF_Extension_Universal" ) network_extension_template: str = Field( alias="networkExtensionTemplate", - description="Network extension template", + description="Default overlay network template for borders", default="Default_Network_Extension_Universal" ) # Optional Advanced Settings - performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) - tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable tenant DHCP", default=True) - advertise_physical_ip: bool = Field(alias="advertisePhysicalIp", description="Advertise physical IP", default=False) + performance_monitoring: bool = Field( + alias="performanceMonitoring", + description=( + "If enabled, switch metrics are collected through periodic SNMP polling. " + "Alternative to real-time telemetry" + ), + default=False + ) + tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable Tenant DHCP", default=True) + advertise_physical_ip: bool = Field( + alias="advertisePhysicalIp", + description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", + default=False + ) advertise_physical_ip_on_border: bool = Field( alias="advertisePhysicalIpOnBorder", - description="Advertise physical IP on border", + description=( + "Enable advertise-pip on vPC borders and border gateways only. Applicable only when vPC advertise-pip is " + "not enabled" + ), default=True ) # Protocol Settings - bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enable BGP authentication", default=False) + bgp_authentication: bool = Field( + alias="bgpAuthentication", + description="Enables or disables the BGP authentication", + default=False + ) bgp_authentication_key_type: BgpAuthenticationKeyTypeEnum = Field( alias="bgpAuthenticationKeyType", - description="BGP authentication key type", + description="BGP key encryption type: 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7", default=BgpAuthenticationKeyTypeEnum.THREE_DES ) - bfd: bool = Field(description="Enable BFD", default=False) - bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD for iBGP", default=False) + bfd: bool = Field(description="Enable BFD. Valid for IPv4 Underlay only", default=False) + bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD For iBGP", default=False) # Management Settings - nxapi: bool = Field(description="Enable NX-API", default=False) - nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) - nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) - nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) + nxapi: bool = Field(description="Enable NX-API over HTTPS", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API over HTTP", default=False) + nxapi_https_port: int = Field( + alias="nxapiHttpsPort", + description="HTTPS port for NX-API", + ge=1, + le=65535, + default=443 + ) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="HTTP port for NX-API", ge=1, le=65535, default=80) # Bootstrap Settings - day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) + day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Automatic IP Assignment For POAP", default=False) bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( alias="bootstrapSubnetCollection", - description="Bootstrap subnet collection", + description="List of IPv4 or IPv6 subnets to be used for bootstrap", default_factory=list ) # Netflow Settings netflow_settings: NetflowSettingsModel = Field( alias="netflowSettings", - description="Netflow configuration", + description="Settings associated with netflow", default_factory=NetflowSettingsModel ) # Multicast Settings rendezvous_point_count: RendezvousPointCountEnum = Field( alias="rendezvousPointCount", - description="Number of rendezvous points", + description="Number of spines acting as Rendezvous-Points (RPs)", default=RendezvousPointCountEnum.TWO ) rendezvous_point_loopback_id: int = Field( alias="rendezvousPointLoopbackId", - description="RP loopback interface ID", + description="Rendezvous point loopback Id", ge=0, le=1023, default=254 ) # System Settings - snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) - cdp: bool = Field(description="Enable CDP", default=False) + snmp_trap: bool = Field(alias="snmpTrap", description="Configure ND as a receiver for SNMP traps", default=True) + cdp: bool = Field(description="Enable CDP on management interface", default=False) real_time_interface_statistics_collection: bool = Field( alias="realTimeInterfaceStatisticsCollection", - description="Enable real-time interface statistics", + description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", default=False ) - tcam_allocation: bool = Field(alias="tcamAllocation", description="Enable TCAM allocation", default=True) + tcam_allocation: bool = Field( + alias="tcamAllocation", + description="TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled", + default=True + ) # VPC Extended Configuration - vpc_peer_link_port_channel_id: str = Field(alias="vpcPeerLinkPortChannelId", description="vPC peer link port-channel ID", default="500") + vpc_peer_link_port_channel_id: str = Field( + alias="vpcPeerLinkPortChannelId", + description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", + default="500" + ) vpc_ipv6_neighbor_discovery_sync: bool = Field( - alias="vpcIpv6NeighborDiscoverySync", description="Enable vPC IPv6 ND sync", default=True + alias="vpcIpv6NeighborDiscoverySync", + description="Enable IPv6 ND synchronization between vPC peers", + default=True + ) + vpc_layer3_peer_router: bool = Field( + alias="vpcLayer3PeerRouter", + description="Enable Layer-3 Peer-Router on all Leaf switches", + default=True + ) + vpc_tor_delay_restore_timer: int = Field( + alias="vpcTorDelayRestoreTimer", + description="vPC delay restore timer for ToR switches (in seconds)", + default=30 + ) + fabric_vpc_domain_id: bool = Field( + alias="fabricVpcDomainId", + description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", + default=False + ) + shared_vpc_domain_id: int = Field( + alias="sharedVpcDomainId", + description="vPC Domain Id to be used on all vPC pairs", + default=1 + ) + fabric_vpc_qos: bool = Field( + alias="fabricVpcQos", + description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", + default=False ) - vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable vPC layer-3 peer router", default=True) - vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC TOR delay restore timer", default=30) - fabric_vpc_domain_id: bool = Field(alias="fabricVpcDomainId", description="Enable fabric vPC domain ID", default=False) - shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="Shared vPC domain ID", default=1) - fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Enable fabric vPC QoS", default=False) fabric_vpc_qos_policy_name: str = Field( - alias="fabricVpcQosPolicyName", description="Fabric vPC QoS policy name", default="spine_qos_for_fabric_vpc_peering" + alias="fabricVpcQosPolicyName", + description="Qos Policy name should be same on all spines", + default="spine_qos_for_fabric_vpc_peering" + ) + enable_peer_switch: bool = Field( + alias="enablePeerSwitch", + description="Enable the vPC peer-switch feature on ToR switches", + default=False ) - enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable peer switch", default=False) # Bootstrap / Day-0 / DHCP - local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) - dhcp_protocol_version: DhcpProtocolVersionEnum = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default=DhcpProtocolVersionEnum.DHCPV4) - dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") - dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") - management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") - management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) - management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) - extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") + local_dhcp_server: bool = Field( + alias="localDhcpServer", + description="Automatic IP Assignment For POAP From Local DHCP Server", + default=False + ) + dhcp_protocol_version: DhcpProtocolVersionEnum = Field( + alias="dhcpProtocolVersion", + description="IP protocol version for Local DHCP Server", + default=DhcpProtocolVersionEnum.DHCPV4 + ) + dhcp_start_address: str = Field( + alias="dhcpStartAddress", + description="DHCP Scope Start Address For Switch POAP", + default="" + ) + dhcp_end_address: str = Field( + alias="dhcpEndAddress", + description="DHCP Scope End Address For Switch POAP", + default="" + ) + management_gateway: str = Field( + alias="managementGateway", + description="Default Gateway For Management VRF On The Switch", + default="" + ) + management_ipv4_prefix: int = Field( + alias="managementIpv4Prefix", + description="Switch Mgmt IP Subnet Prefix if ipv4", + default=24 + ) + management_ipv6_prefix: int = Field( + alias="managementIpv6Prefix", + description="Switch Management IP Subnet Prefix if ipv6", + default=64 + ) + extra_config_nxos_bootstrap: str = Field( + alias="extraConfigNxosBootstrap", + description="Additional CLIs required during device bootup/login e.g. AAA/Radius", + default="" + ) un_numbered_bootstrap_loopback_id: int = Field( - alias="unNumberedBootstrapLoopbackId", description="Unnumbered bootstrap loopback ID", default=253 + alias="unNumberedBootstrapLoopbackId", description="Bootstrap Seed Switch Loopback Interface ID", default=253 + ) + un_numbered_dhcp_start_address: str = Field( + alias="unNumberedDhcpStartAddress", + description="Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool", + default="" + ) + un_numbered_dhcp_end_address: str = Field( + alias="unNumberedDhcpEndAddress", + description="Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool", + default="" + ) + inband_management: bool = Field( + alias="inbandManagement", + description="Manage switches with only Inband connectivity", + default=False + ) + inband_dhcp_servers: List[str] = Field( + alias="inbandDhcpServers", + description="List of external DHCP server IP addresses (Max 3)", + default_factory=list ) - un_numbered_dhcp_start_address: str = Field(alias="unNumberedDhcpStartAddress", description="Unnumbered DHCP start address", default="") - un_numbered_dhcp_end_address: str = Field(alias="unNumberedDhcpEndAddress", description="Unnumbered DHCP end address", default="") - inband_management: bool = Field(alias="inbandManagement", description="Enable in-band management", default=False) - inband_dhcp_servers: List[str] = Field(alias="inbandDhcpServers", description="In-band DHCP servers", default_factory=list) seed_switch_core_interfaces: List[str] = Field( - alias="seedSwitchCoreInterfaces", description="Seed switch core interfaces", default_factory=list + alias="seedSwitchCoreInterfaces", + description="Seed switch fabric interfaces. Core-facing interface list on seed switch", + default_factory=list ) spine_switch_core_interfaces: List[str] = Field( - alias="spineSwitchCoreInterfaces", description="Spine switch core interfaces", default_factory=list + alias="spineSwitchCoreInterfaces", + description="Spine switch fabric interfaces. Core-facing interface list on all spines", + default_factory=list ) # Backup / Restore - real_time_backup: bool = Field(alias="realTimeBackup", description="Enable real-time backup", default=False) - scheduled_backup: bool = Field(alias="scheduledBackup", description="Enable scheduled backup", default=False) - scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + real_time_backup: bool = Field( + alias="realTimeBackup", + description="Backup hourly only if there is any config deployment since last backup", + default=False + ) + scheduled_backup: bool = Field( + alias="scheduledBackup", + description="Enable backup at the specified time daily", + default=False + ) + scheduled_backup_time: str = Field( + alias="scheduledBackupTime", + description="Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)", + default="" + ) # IPv6 / Dual-Stack - underlay_ipv6: bool = Field(alias="underlayIpv6", description="Enable IPv6 underlay", default=False) + underlay_ipv6: bool = Field( + alias="underlayIpv6", + description="If not enabled, IPv4 underlay is used", + default=False + ) ipv6_multicast_group_subnet: str = Field( - alias="ipv6MulticastGroupSubnet", description="IPv6 multicast group subnet", default="ff1e::/121" + alias="ipv6MulticastGroupSubnet", + description="IPv6 Multicast address with prefix 112 to 128", + default="ff1e::/121" ) tenant_routed_multicast_ipv6: bool = Field( - alias="tenantRoutedMulticastIpv6", description="Enable tenant routed multicast IPv6", default=False + alias="tenantRoutedMulticastIpv6", + description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", + default=False + ) + ipv6_link_local: bool = Field( + alias="ipv6LinkLocal", + description="If not enabled, Spine-Leaf interfaces will use global IPv6 addresses", + default=True + ) + ipv6_subnet_target_mask: int = Field( + alias="ipv6SubnetTargetMask", + description="Mask for Underlay Subnet IPv6 Range", + default=126 + ) + ipv6_subnet_range: str = Field( + alias="ipv6SubnetRange", + description="Underlay Subnet ipv6 range to assign Numbered and Peer Link SVI IPs", + default="fd00::a04:0/112" + ) + bgp_loopback_ipv6_range: str = Field( + alias="bgpLoopbackIpv6Range", + description="Typically Loopback0 IPv6 Address Range", + default="fd00::a02:0/119" + ) + nve_loopback_ipv6_range: str = Field( + alias="nveLoopbackIpv6Range", + description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", + default="fd00::a03:0/118" ) - ipv6_link_local: bool = Field(alias="ipv6LinkLocal", description="Enable IPv6 link-local", default=True) - ipv6_subnet_target_mask: int = Field(alias="ipv6SubnetTargetMask", description="IPv6 subnet target mask", default=126) - ipv6_subnet_range: str = Field(alias="ipv6SubnetRange", description="IPv6 subnet range", default="fd00::a04:0/112") - bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="BGP loopback IPv6 range", default="fd00::a02:0/119") - nve_loopback_ipv6_range: str = Field(alias="nveLoopbackIpv6Range", description="NVE loopback IPv6 range", default="fd00::a03:0/118") ipv6_anycast_rendezvous_point_ip_range: str = Field( - alias="ipv6AnycastRendezvousPointIpRange", description="IPv6 anycast RP IP range", default="fd00::254:254:0/118" + alias="ipv6AnycastRendezvousPointIpRange", + description="Anycast RP IPv6 Address Range", + default="fd00::254:254:0/118" ) # Multicast / Rendezvous Point Extended - mvpn_vrf_route_import_id: bool = Field(alias="mvpnVrfRouteImportId", description="Enable MVPN VRF route import ID", default=True) + mvpn_vrf_route_import_id: bool = Field( + alias="mvpnVrfRouteImportId", + description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", + default=True + ) mvpn_vrf_route_import_id_range: str = Field( - alias="mvpnVrfRouteImportIdRange", description="MVPN VRF route import ID range", default="" + alias="mvpnVrfRouteImportIdRange", + description=( + "MVPN VRI ID (minimum: 1, maximum: 65535) for vPC, applicable when TRM enabled with IPv6 underlay, or " + "mvpnVrfRouteImportId enabled with IPv4 underlay" + ), + default="" ) vrf_route_import_id_reallocation: bool = Field( - alias="vrfRouteImportIdReallocation", description="Enable VRF route import ID reallocation", default=False + alias="vrfRouteImportIdReallocation", + description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", + default=False + ) + l3vni_multicast_group: str = Field( + alias="l3vniMulticastGroup", + description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", + default="239.1.1.0" + ) + l3_vni_ipv6_multicast_group: str = Field( + alias="l3VniIpv6MulticastGroup", + description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", + default="ff1e::" + ) + rendezvous_point_mode: RendezvousPointModeEnum = Field( + alias="rendezvousPointMode", + description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", + default=RendezvousPointModeEnum.ASM ) - l3vni_multicast_group: str = Field(alias="l3vniMulticastGroup", description="L3 VNI multicast group", default="239.1.1.0") - l3_vni_ipv6_multicast_group: str = Field(alias="l3VniIpv6MulticastGroup", description="L3 VNI IPv6 multicast group", default="ff1e::") - rendezvous_point_mode: RendezvousPointModeEnum = Field(alias="rendezvousPointMode", description="Rendezvous point mode", default=RendezvousPointModeEnum.ASM) phantom_rendezvous_point_loopback_id1: int = Field( - alias="phantomRendezvousPointLoopbackId1", description="Phantom RP loopback ID 1", default=2 + alias="phantomRendezvousPointLoopbackId1", + description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", + default=2 ) phantom_rendezvous_point_loopback_id2: int = Field( - alias="phantomRendezvousPointLoopbackId2", description="Phantom RP loopback ID 2", default=3 + alias="phantomRendezvousPointLoopbackId2", + description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", + default=3 ) phantom_rendezvous_point_loopback_id3: int = Field( - alias="phantomRendezvousPointLoopbackId3", description="Phantom RP loopback ID 3", default=4 + alias="phantomRendezvousPointLoopbackId3", + description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", + default=4 ) phantom_rendezvous_point_loopback_id4: int = Field( - alias="phantomRendezvousPointLoopbackId4", description="Phantom RP loopback ID 4", default=5 + alias="phantomRendezvousPointLoopbackId4", + description="Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments", + default=5 + ) + anycast_loopback_id: int = Field( + alias="anycastLoopbackId", + description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", + default=10 ) - anycast_loopback_id: int = Field(alias="anycastLoopbackId", description="Anycast loopback ID", default=10) # VRF Lite / Sub-Interface - sub_interface_dot1q_range: str = Field(alias="subInterfaceDot1qRange", description="Sub-interface 802.1q range", default="2-511") - vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default=VrfLiteAutoConfigEnum.MANUAL) - vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="VRF lite subnet range", default="10.33.0.0/16") - vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF lite subnet target mask", default=30) + sub_interface_dot1q_range: str = Field( + alias="subInterfaceDot1qRange", + description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", + default="2-511" + ) + vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field( + alias="vrfLiteAutoConfig", + description=( + "VRF Lite Inter-Fabric Connection Deployment Options. If 'back2BackAndToExternal' is selected, VRF Lite " + "IFCs are auto created between border devices of two Easy Fabrics, and between border devices in Easy " + "Fabric and edge routers in External Fabric. The IP address is taken from the 'VRF Lite Subnet IP Range' " + "pool." + ), + default=VrfLiteAutoConfigEnum.MANUAL + ) + vrf_lite_subnet_range: str = Field( + alias="vrfLiteSubnetRange", + description="Address range to assign P2P Interfabric Connections", + default="10.33.0.0/16" + ) + vrf_lite_subnet_target_mask: int = Field( + alias="vrfLiteSubnetTargetMask", + description="VRF Lite Subnet Mask", + default=30 + ) auto_unique_vrf_lite_ip_prefix: bool = Field( - alias="autoUniqueVrfLiteIpPrefix", description="Auto unique VRF lite IP prefix", default=False + alias="autoUniqueVrfLiteIpPrefix", + description=( + "When enabled, IP prefix allocated to the VRF LITE IFC is not reused on VRF extension over VRF LITE IFC. " + "Instead, unique IP Subnet is allocated for each VRF extension over VRF LITE IFC." + ), + default=False + ) + auto_symmetric_vrf_lite: bool = Field( + alias="autoSymmetricVrfLite", + description=( + "Whether to auto generate VRF LITE sub-interface and BGP peering configuration on managed " + "neighbor devices. If set, auto created VRF Lite IFC links will have " + "'Auto Deploy for Peer' enabled." + ), + default=False + ) + auto_vrf_lite_default_vrf: bool = Field( + alias="autoVrfLiteDefaultVrf", + description=( + "For ipv4 underlay, whether to auto generate BGP peering in Default VRF for VRF Lite IFC auto deployment " + "option. If set, will auto create VRF Lite Inter-Fabric links with 'Auto Deploy Default VRF' knob enabled" + ), + default=False + ) + auto_symmetric_default_vrf: bool = Field( + alias="autoSymmetricDefaultVrf", + description=( + "Whether to auto generate Default VRF interface and BGP peering configuration on managed neighbor devices. " + "If set, auto created VRF Lite IFC links will have 'Auto Deploy Default VRF for Peer' enabled." + ), + default=False ) - auto_symmetric_vrf_lite: bool = Field(alias="autoSymmetricVrfLite", description="Auto symmetric VRF lite", default=False) - auto_vrf_lite_default_vrf: bool = Field(alias="autoVrfLiteDefaultVrf", description="Auto VRF lite default VRF", default=False) - auto_symmetric_default_vrf: bool = Field(alias="autoSymmetricDefaultVrf", description="Auto symmetric default VRF", default=False) default_vrf_redistribution_bgp_route_map: str = Field( - alias="defaultVrfRedistributionBgpRouteMap", description="Default VRF redistribution BGP route map", default="extcon-rmap-filter" + alias="defaultVrfRedistributionBgpRouteMap", + description=( + "Route Map used to redistribute BGP routes to IGP in default vrf " + "in auto created VRF Lite IFC links" + ), + default="extcon-rmap-filter" ) # Per-VRF Loopback per_vrf_loopback_auto_provision: bool = Field( - alias="perVrfLoopbackAutoProvision", description="Per-VRF loopback auto-provision", default=False + alias="perVrfLoopbackAutoProvision", + description=( + "Auto provision an IPv4 loopback on a VTEP on VRF attachment. Note: Enabling this option auto-provisions " + "loopback on existing VRF attachments and also when Edit, QuickAttach, or Multiattach actions are " + "performed. Provisioned loopbacks cannot be deleted until VRFs are unattached." + ), + default=False ) per_vrf_loopback_ip_range: str = Field( - alias="perVrfLoopbackIpRange", description="Per-VRF loopback IP range", default="10.5.0.0/22" + alias="perVrfLoopbackIpRange", + description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", + default="10.5.0.0/22" ) per_vrf_loopback_auto_provision_ipv6: bool = Field( - alias="perVrfLoopbackAutoProvisionIpv6", description="Per-VRF loopback auto-provision IPv6", default=False + alias="perVrfLoopbackAutoProvisionIpv6", + description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", + default=False ) per_vrf_loopback_ipv6_range: str = Field( - alias="perVrfLoopbackIpv6Range", description="Per-VRF loopback IPv6 range", default="fd00::a05:0/112" + alias="perVrfLoopbackIpv6Range", + description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", + default="fd00::a05:0/112" ) per_vrf_unique_loopback_auto_provision: bool = Field( - alias="perVrfUniqueLoopbackAutoProvision", description="Per-VRF unique loopback auto-provision", default=False + alias="perVrfUniqueLoopbackAutoProvision", + description=( + "Auto provision a unique IPV4 loopback on a VTEP on VRF attachment. Note: Enabling this option " + "auto-provisions unique loopback in the fabric per request. This option and per VRF per VTEP loopback " + "auto-provisioning are mutually exclusive. Provisioned unique loopbacks will be released upon VRF " + "unattachment or per request." + ), + default=False ) per_vrf_unique_loopback_ip_range: str = Field( - alias="perVrfUniqueLoopbackIpRange", description="Per-VRF unique loopback IP range", default="10.6.0.0/22" + alias="perVrfUniqueLoopbackIpRange", + description="Prefix pool to assign unique IPv4 addresses to loopbacks on VTEPs on a per VRF basis", + default="10.6.0.0/22" ) per_vrf_unique_loopback_auto_provision_v6: bool = Field( - alias="perVrfUniqueLoopbackAutoProvisionV6", description="Per-VRF unique loopback auto-provision IPv6", default=False + alias="perVrfUniqueLoopbackAutoProvisionV6", + description="Auto provision a unique IPV6 loopback on a VTEP on VRF attachment.", + default=False ) per_vrf_unique_loopback_ipv6_range: str = Field( - alias="perVrfUniqueLoopbackIpv6Range", description="Per-VRF unique loopback IPv6 range", default="fd00::a06:0/112" + alias="perVrfUniqueLoopbackIpv6Range", + description="Prefix pool to assign unique IPv6 addresses to loopbacks on VTEPs on a per VRF basis", + default="fd00::a06:0/112" ) # Authentication — BGP Extended - bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="BGP authentication key", default="") + bgp_authentication_key: str = Field( + alias="bgpAuthenticationKey", + description="Encrypted BGP authentication key based on type", + default="" + ) # Authentication — PIM - pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Enable PIM hello authentication", default=False) - pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="PIM hello authentication key", default="") + pim_hello_authentication: bool = Field( + alias="pimHelloAuthentication", + description="Valid for IPv4 Underlay only", + default=False + ) + pim_hello_authentication_key: str = Field( + alias="pimHelloAuthenticationKey", + description="3DES Encrypted", + default="" + ) # Authentication — BFD - bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD authentication", default=False) - bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD authentication key ID", default=100) - bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="BFD authentication key", default="") - bfd_ospf: bool = Field(alias="bfdOspf", description="Enable BFD for OSPF", default=False) - bfd_isis: bool = Field(alias="bfdIsis", description="Enable BFD for IS-IS", default=False) - bfd_pim: bool = Field(alias="bfdPim", description="Enable BFD for PIM", default=False) + bfd_authentication: bool = Field( + alias="bfdAuthentication", + description="Enable BFD Authentication. Valid for P2P Interfaces only", + default=False + ) + bfd_authentication_key_id: int = Field( + alias="bfdAuthenticationKeyId", + description="BFD Authentication Key ID", + default=100 + ) + bfd_authentication_key: str = Field( + alias="bfdAuthenticationKey", + description="Encrypted SHA1 secret value", + default="" + ) + bfd_ospf: bool = Field(alias="bfdOspf", description="Enable BFD For OSPF", default=False) + bfd_isis: bool = Field(alias="bfdIsis", description="Enable BFD For ISIS", default=False) + bfd_pim: bool = Field(alias="bfdPim", description="Enable BFD For PIM", default=False) # Authentication — OSPF - ospf_authentication: bool = Field(alias="ospfAuthentication", description="Enable OSPF authentication", default=False) - ospf_authentication_key_id: int = Field(alias="ospfAuthenticationKeyId", description="OSPF authentication key ID", default=127) - ospf_authentication_key: str = Field(alias="ospfAuthenticationKey", description="OSPF authentication key", default="") + ospf_authentication: bool = Field( + alias="ospfAuthentication", + description="Enable OSPF Authentication", + default=False + ) + ospf_authentication_key_id: int = Field( + alias="ospfAuthenticationKeyId", + description="(Min:0, Max:255)", + default=127 + ) + ospf_authentication_key: str = Field( + alias="ospfAuthenticationKey", + description="OSPF Authentication Key. 3DES Encrypted", + default="" + ) # IS-IS - isis_level: IsisLevelEnum = Field(alias="isisLevel", description="IS-IS level", default=IsisLevelEnum.LEVEL_2) - isis_area_number: str = Field(alias="isisAreaNumber", description="IS-IS area number", default="0001") - isis_point_to_point: bool = Field(alias="isisPointToPoint", description="IS-IS point-to-point", default=True) - isis_authentication: bool = Field(alias="isisAuthentication", description="Enable IS-IS authentication", default=False) + isis_level: IsisLevelEnum = Field(alias="isisLevel", description="IS-IS Level", default=IsisLevelEnum.LEVEL_2) + isis_area_number: str = Field( + alias="isisAreaNumber", + description=( + "NET in form of XX.<4-hex-digit Custom Area Number>.XXXX.XXXX.XXXX.00, default Area Number " + "is 0001. If area number in existing NETs matches the previous area number set in fabric " + "settings and is different from the " + "current area number, these NETs will be updated by Recalculate and Deploy." + ), + default="0001" + ) + isis_point_to_point: bool = Field( + alias="isisPointToPoint", + description="This will enable network point-to-point on fabric interfaces which are numbered", + default=True + ) + isis_authentication: bool = Field( + alias="isisAuthentication", + description="Enable IS-IS Authentication", + default=False + ) isis_authentication_keychain_name: str = Field( - alias="isisAuthenticationKeychainName", description="IS-IS authentication keychain name", default="" + alias="isisAuthenticationKeychainName", description="IS-IS Authentication Keychain Name", default="" ) isis_authentication_keychain_key_id: int = Field( - alias="isisAuthenticationKeychainKeyId", description="IS-IS authentication keychain key ID", default=127 + alias="isisAuthenticationKeychainKeyId", description="IS-IS Authentication Key ID", default=127 + ) + isis_authentication_key: str = Field( + alias="isisAuthenticationKey", + description="IS-IS Authentication Key. Cisco Type 7 Encrypted", + default="" + ) + isis_overload: bool = Field( + alias="isisOverload", + description="Set IS-IS Overload Bit. When enabled, set the overload bit for an elapsed time after a reload", + default=True + ) + isis_overload_elapse_time: int = Field( + alias="isisOverloadElapseTime", + description="IS-IS Overload Bit Elapsed Time. Clear the overload bit after an elapsed time in seconds", + default=60 ) - isis_authentication_key: str = Field(alias="isisAuthenticationKey", description="IS-IS authentication key", default="") - isis_overload: bool = Field(alias="isisOverload", description="Enable IS-IS overload bit", default=True) - isis_overload_elapse_time: int = Field(alias="isisOverloadElapseTime", description="IS-IS overload elapse time", default=60) # MACsec - macsec: bool = Field(description="Enable MACsec", default=False) - macsec_cipher_suite: MacsecCipherSuiteEnum = Field(alias="macsecCipherSuite", description="MACsec cipher suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256) - macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec key string", default="") - macsec_algorithm: MacsecAlgorithmEnum = Field(alias="macsecAlgorithm", description="MACsec algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC) - macsec_fallback_key_string: str = Field(alias="macsecFallbackKeyString", description="MACsec fallback key string", default="") - macsec_fallback_algorithm: MacsecAlgorithmEnum = Field(alias="macsecFallbackAlgorithm", description="MACsec fallback algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC) - macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec report timer", default=5) + macsec: bool = Field( + description=( + "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring MACsec on a fabric link if " + "MACsec is enabled on the link." + ), + default=False + ) + macsec_cipher_suite: MacsecCipherSuiteEnum = Field( + alias="macsecCipherSuite", + description="Configure Cipher Suite", + default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 + ) + macsec_key_string: str = Field( + alias="macsecKeyString", + description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", + default="" + ) + macsec_algorithm: MacsecAlgorithmEnum = Field( + alias="macsecAlgorithm", + description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", + default=MacsecAlgorithmEnum.AES_128_CMAC + ) + macsec_fallback_key_string: str = Field( + alias="macsecFallbackKeyString", + description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", + default="" + ) + macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( + alias="macsecFallbackAlgorithm", + description="MACsec Fallback Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", + default=MacsecAlgorithmEnum.AES_128_CMAC + ) + macsec_report_timer: int = Field( + alias="macsecReportTimer", + description="MACsec Operational Status periodic report timer in minutes", + default=5 + ) # VRF Lite MACsec - vrf_lite_macsec: bool = Field(alias="vrfLiteMacsec", description="Enable VRF lite MACsec", default=False) + vrf_lite_macsec: bool = Field( + alias="vrfLiteMacsec", + description=( + "Enable MACsec on DCI links. DCI MACsec fabric parameters are used for configuring MACsec on a DCI link if " + "'Use Link MACsec Setting' is disabled on the link." + ), + default=False + ) vrf_lite_macsec_cipher_suite: MacsecCipherSuiteEnum = Field( - alias="vrfLiteMacsecCipherSuite", description="VRF lite MACsec cipher suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 + alias="vrfLiteMacsecCipherSuite", + description="DCI MACsec Cipher Suite", + default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 + ) + vrf_lite_macsec_key_string: str = Field( + alias="vrfLiteMacsecKeyString", + description="DCI MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", + default="" ) - vrf_lite_macsec_key_string: str = Field(alias="vrfLiteMacsecKeyString", description="VRF lite MACsec key string", default="") vrf_lite_macsec_algorithm: MacsecAlgorithmEnum = Field( - alias="vrfLiteMacsecAlgorithm", description="VRF lite MACsec algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC + alias="vrfLiteMacsecAlgorithm", + description="DCI MACsec Primary Cryptographic Algorithm", + default=MacsecAlgorithmEnum.AES_128_CMAC ) vrf_lite_macsec_fallback_key_string: str = Field( - alias="vrfLiteMacsecFallbackKeyString", description="VRF lite MACsec fallback key string", default="" + alias="vrfLiteMacsecFallbackKeyString", + description=( + "DCI MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String. " + "This parameter is used when DCI link has QKD disabled." + ), + default="" ) vrf_lite_macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( - alias="vrfLiteMacsecFallbackAlgorithm", description="VRF lite MACsec fallback algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC + alias="vrfLiteMacsecFallbackAlgorithm", + description="AES_128_CMAC or AES_256_CMAC. This parameter is used when DCI link has QKD disabled.", + default=MacsecAlgorithmEnum.AES_128_CMAC ) # Quantum Key Distribution / Trustpoint - quantum_key_distribution: bool = Field(alias="quantumKeyDistribution", description="Enable quantum key distribution", default=False) + quantum_key_distribution: bool = Field( + alias="quantumKeyDistribution", + description=( + "Enable Data Center Interconnect Media Access Control Security " + "with Quantum Key Distribution config" + ), + default=False + ) quantum_key_distribution_profile_name: str = Field( - alias="quantumKeyDistributionProfileName", description="Quantum key distribution profile name", default="" + alias="quantumKeyDistributionProfileName", description="Name of crypto profile (Max Size 63)", default="" ) key_management_entity_server_ip: str = Field( - alias="keyManagementEntityServerIp", description="Key management entity server IP", default="" + alias="keyManagementEntityServerIp", description="Key Management Entity server ipv4 address", default="" ) key_management_entity_server_port: int = Field( - alias="keyManagementEntityServerPort", description="Key management entity server port", default=0 + alias="keyManagementEntityServerPort", description="Key Management Entity server port number", default=0 + ) + trustpoint_label: str = Field( + alias="trustpointLabel", + description="Tls authentication type trustpoint label", + default="" ) - trustpoint_label: str = Field(alias="trustpointLabel", description="Trustpoint label", default="") skip_certificate_verification: bool = Field( - alias="skipCertificateVerification", description="Skip certificate verification", default=False + alias="skipCertificateVerification", description="Skip verification of incoming certificate", default=False ) # BGP / Routing Enhancements auto_bgp_neighbor_description: bool = Field( - alias="autoBgpNeighborDescription", description="Auto BGP neighbor description", default=True + alias="autoBgpNeighborDescription", description="Generate BGP EVPN Neighbor Description", default=True + ) + ibgp_peer_template: str = Field( + alias="ibgpPeerTemplate", + description=( + "Specifies the iBGP Peer-Template config used for Route Reflectors and spines with border " + "or border gateway role. This field should begin with ' template peer' or " + "' template peer-session'. This must have 2 " + "leading spaces. Note ! All configs should strictly match show run output, with respect to case and " + "newlines. Any mismatches will yield unexpected diffs during deploy." + ), + default="" + ) + leaf_ibgp_peer_template: str = Field( + alias="leafIbgpPeerTemplate", + description=( + "Specifies the config used for leaf, border or border gateway. If this field is empty, the peer template " + "defined in iBGP Peer-Template Config is used on all BGP enabled devices (RRs, leafs, border or border " + "gateway roles). This field should begin with ' template peer' or ' template peer-session'. This must " + "have 2 leading spaces. Note ! All configs should strictly match 'show run' output, with respect to case " + "and newlines. Any mismatches will yield unexpected diffs during deploy." + ), + default="" + ) + link_state_routing_tag: str = Field( + alias="linkStateRoutingTag", + description="Underlay routing protocol process tag", + default="UNDERLAY" ) - ibgp_peer_template: str = Field(alias="ibgpPeerTemplate", description="iBGP peer template", default="") - leaf_ibgp_peer_template: str = Field(alias="leafIbgpPeerTemplate", description="Leaf iBGP peer template", default="") - link_state_routing_tag: str = Field(alias="linkStateRoutingTag", description="Link state routing tag", default="UNDERLAY") static_underlay_ip_allocation: bool = Field( - alias="staticUnderlayIpAllocation", description="Static underlay IP allocation", default=False + alias="staticUnderlayIpAllocation", + description="Checking this will disable Dynamic Underlay IP Address Allocations", + default=False + ) + router_id_range: str = Field( + alias="routerIdRange", + description="BGP Router ID Range in IPv4 subnet format used for IPv6 Underlay.", + default="10.2.0.0/23" ) - router_id_range: str = Field(alias="routerIdRange", description="Router ID range", default="10.2.0.0/23") # Security Group Tags (SGT) - security_group_tag: bool = Field(alias="securityGroupTag", description="Enable security group tag", default=False) - security_group_tag_prefix: str = Field(alias="securityGroupTagPrefix", description="SGT prefix", default="SG_") + security_group_tag: bool = Field( + alias="securityGroupTag", + description="Security group can be enabled only with cli overlay mode", + default=False + ) + security_group_tag_prefix: str = Field( + alias="securityGroupTagPrefix", + description="Prefix to be used when a new security group is created", + default="SG_" + ) security_group_tag_mac_segmentation: bool = Field( - alias="securityGroupTagMacSegmentation", description="Enable SGT MAC segmentation", default=False + alias="securityGroupTagMacSegmentation", + description="Enable MAC based segmentation for security groups", + default=False ) security_group_tag_id_range: str = Field( - alias="securityGroupTagIdRange", description="SGT ID range", default="10000-14000" + alias="securityGroupTagIdRange", + description="Security group tag (SGT) identifier range (minimum: 16, maximum: 65535)", + default="10000-14000" ) security_group_tag_preprovision: bool = Field( - alias="securityGroupTagPreprovision", description="Enable SGT preprovision", default=False + alias="securityGroupTagPreprovision", + description="Generate security groups configuration for non-enforced VRFs", + default=False + ) + security_group_status: SecurityGroupStatusEnum = Field( + alias="securityGroupStatus", + description="Security group status", + default=SecurityGroupStatusEnum.DISABLED ) - security_group_status: SecurityGroupStatusEnum = Field(alias="securityGroupStatus", description="Security group status", default=SecurityGroupStatusEnum.DISABLED) # Queuing / QoS - default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable default queuing policy", default=False) + default_queuing_policy: bool = Field( + alias="defaultQueuingPolicy", + description="Enable Default Queuing Policies", + default=False + ) default_queuing_policy_cloudscale: str = Field( - alias="defaultQueuingPolicyCloudscale", description="Default queuing policy cloudscale", default="queuing_policy_default_8q_cloudscale" + alias="defaultQueuingPolicyCloudscale", + description="Queuing Policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric", + default="queuing_policy_default_8q_cloudscale" ) default_queuing_policy_r_series: str = Field( - alias="defaultQueuingPolicyRSeries", description="Default queuing policy R-Series", default="queuing_policy_default_r_series" + alias="defaultQueuingPolicyRSeries", + description="Queueing policy for all Nexus R-series switches", + default="queuing_policy_default_r_series" ) default_queuing_policy_other: str = Field( - alias="defaultQueuingPolicyOther", description="Default queuing policy other", default="queuing_policy_default_other" - ) - aiml_qos: bool = Field(alias="aimlQos", description="Enable AI/ML QoS", default=False) - aiml_qos_policy: AimlQosPolicyEnum = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default=AimlQosPolicyEnum.V_400G) - roce_v2: str = Field(alias="roceV2", description="RoCEv2 DSCP value", default="26") - cnp: str = Field(description="CNP value", default="48") - wred_min: int = Field(alias="wredMin", description="WRED minimum threshold", default=950) - wred_max: int = Field(alias="wredMax", description="WRED maximum threshold", default=3000) - wred_drop_probability: int = Field(alias="wredDropProbability", description="WRED drop probability", default=7) - wred_weight: int = Field(alias="wredWeight", description="WRED weight", default=0) - bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Bandwidth remaining percentage", default=50) - dlb: bool = Field(description="Enable dynamic load balancing", default=False) - dlb_mode: DlbModeEnum = Field(alias="dlbMode", description="DLB mode", default=DlbModeEnum.FLOWLET) - dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default=DlbMixedModeDefaultEnum.ECMP) - flowlet_aging: int = Field(alias="flowletAging", description="Flowlet aging interval", default=1) - flowlet_dscp: str = Field(alias="flowletDscp", description="Flowlet DSCP value", default="") - per_packet_dscp: str = Field(alias="perPacketDscp", description="Per-packet DSCP value", default="") - ai_load_sharing: bool = Field(alias="aiLoadSharing", description="Enable AI load sharing", default=False) + alias="defaultQueuingPolicyOther", + description="Queuing Policy for all other switches in the fabric", + default="queuing_policy_default_other" + ) + aiml_qos: bool = Field( + alias="aimlQos", + description=( + "Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) switch fabric for " + "AI network workloads" + ), + default=False + ) + aiml_qos_policy: AimlQosPolicyEnum = Field( + alias="aimlQosPolicy", + description=( + "Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. User-defined allows for " + "custom configuration." + ), + default=AimlQosPolicyEnum.V_400G + ) + roce_v2: str = Field( + alias="roceV2", + description=( + "DSCP for RDMA traffic: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="26" + ) + cnp: str = Field( + description=( + "DSCP value for Congestion Notification: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="48" + ) + wred_min: int = Field(alias="wredMin", description="WRED minimum threshold (in kbytes)", default=950) + wred_max: int = Field(alias="wredMax", description="WRED maximum threshold (in kbytes)", default=3000) + wred_drop_probability: int = Field(alias="wredDropProbability", description="Drop probability %", default=7) + wred_weight: int = Field( + alias="wredWeight", + description="Influences how quickly WRED reacts to queue depth changes", + default=0 + ) + bandwidth_remaining: int = Field( + alias="bandwidthRemaining", + description="Percentage of remaining bandwidth allocated to AI traffic queues", + default=50 + ) + dlb: bool = Field( + description=( + "Enables fabric-level Dynamic Load Balancing (DLB) configuration. Note: Inter-Switch-Links (ISL) will be " + "configured as DLB Interfaces" + ), + default=False + ) + dlb_mode: DlbModeEnum = Field( + alias="dlbMode", + description=( + "Select system-wide flowlet, per-packet (packet spraying) or policy driven mixed mode. Note: Mixed mode is " + "supported on Silicon One (S1) platform only." + ), + default=DlbModeEnum.FLOWLET + ) + dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field( + alias="dlbMixedModeDefault", + description="Default load balancing mode for policy driven mixed mode DLB", + default=DlbMixedModeDefaultEnum.ECMP + ) + flowlet_aging: int = Field( + alias="flowletAging", + description=( + "Flowlet aging timer in microseconds. Valid range depends on platform: Cloud Scale (CS)=1-2000000 (default " + "500), Silicon One (S1)=1-1024 (default 256)" + ), + default=1 + ) + flowlet_dscp: str = Field( + alias="flowletDscp", + description=( + "DSCP values for flowlet load balancing: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="" + ) + per_packet_dscp: str = Field( + alias="perPacketDscp", + description=( + "DSCP values for per-packet load balancing: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="" + ) + ai_load_sharing: bool = Field( + alias="aiLoadSharing", + description="Enable IP load sharing using source and destination address for AI workloads", + default=False + ) priority_flow_control_watch_interval: int = Field( - alias="priorityFlowControlWatchInterval", description="Priority flow control watch interval", default=101 + alias="priorityFlowControlWatchInterval", + description="Acceptable values from 101 to 1000 (milliseconds). Leave blank for system default (100ms).", + default=101 ) # PTP - ptp: bool = Field(description="Enable PTP", default=False) - ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) - ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) - ptp_vlan_id: int = Field(alias="ptpVlanId", description="PTP VLAN ID", default=2) + ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) + ptp_loopback_id: int = Field( + alias="ptpLoopbackId", + description="Precision Time Protocol Source Loopback Id", + default=0 + ) + ptp_domain_id: int = Field( + alias="ptpDomainId", + description="Multiple Independent PTP Clocking Subdomains on a Single Network", + default=0 + ) + ptp_vlan_id: int = Field( + alias="ptpVlanId", + description="Precision Time Protocol (PTP) Source VLAN ID. SVI used for ptp source on ToRs", + default=2 + ) # STP - stp_root_option: StpRootOptionEnum = Field(alias="stpRootOption", description="STP root option", default=StpRootOptionEnum.UNMANAGED) - stp_vlan_range: str = Field(alias="stpVlanRange", description="STP VLAN range", default="1-3967") - mst_instance_range: str = Field(alias="mstInstanceRange", description="MST instance range", default="0") - stp_bridge_priority: int = Field(alias="stpBridgePriority", description="STP bridge priority", default=0) + stp_root_option: StpRootOptionEnum = Field( + alias="stpRootOption", + description=( + "Which protocol to use for configuring root bridge? rpvst+: Rapid Per-VLAN Spanning Tree, mst: Multiple " + "Spanning Tree, unmanaged (default): STP Root not managed by ND" + ), + default=StpRootOptionEnum.UNMANAGED + ) + stp_vlan_range: str = Field( + alias="stpVlanRange", + description="Spanning tree Vlan range (minimum: 1, maximum: 4094)", + default="1-3967" + ) + mst_instance_range: str = Field( + alias="mstInstanceRange", + description="Minimum Spanning Tree instance range (minimum: 0, maximum: 4094)", + default="0" + ) + stp_bridge_priority: int = Field( + alias="stpBridgePriority", + description="Bridge priority for the spanning tree in increments of 4096", + default=0 + ) # MPLS Handoff - mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS handoff", default=False) - mpls_loopback_identifier: int = Field(alias="mplsLoopbackIdentifier", description="MPLS loopback identifier", default=101) - mpls_isis_area_number: str = Field(alias="mplsIsisAreaNumber", description="MPLS IS-IS area number", default="0001") - mpls_loopback_ip_range: str = Field(alias="mplsLoopbackIpRange", description="MPLS loopback IP range", default="10.101.0.0/25") + mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS Handoff", default=False) + mpls_loopback_identifier: int = Field( + alias="mplsLoopbackIdentifier", + description="Used for VXLAN to MPLS SR/LDP Handoff", + default=101 + ) + mpls_isis_area_number: str = Field( + alias="mplsIsisAreaNumber", + description=( + "NET in form of XX.<4-hex-digit Custom Area Number>.XXXX.XXXX.XXXX.00, default Area Number is 0001, used " + "only if routing protocol on DCI MPLS link is is-is" + ), + default="0001" + ) + mpls_loopback_ip_range: str = Field( + alias="mplsLoopbackIpRange", + description="Used for VXLAN to MPLS SR/LDP Handoff", + default="10.101.0.0/25" + ) # Private VLAN - private_vlan: bool = Field(alias="privateVlan", description="Enable private VLAN", default=False) + private_vlan: bool = Field( + alias="privateVlan", + description="Enable PVLAN on switches except spines and super spines", + default=False + ) default_private_vlan_secondary_network_template: str = Field( alias="defaultPrivateVlanSecondaryNetworkTemplate", - description="Default private VLAN secondary network template", + description="Default PVLAN secondary network template", default="Pvlan_Secondary_Network" ) allow_vlan_on_leaf_tor_pairing: AllowVlanOnLeafTorPairingEnum = Field( - alias="allowVlanOnLeafTorPairing", description="Allow VLAN on leaf/TOR pairing", default=AllowVlanOnLeafTorPairingEnum.NONE + alias="allowVlanOnLeafTorPairing", + description="Set trunk allowed vlan to 'none' or 'all' for leaf-tor pairing port-channels", + default=AllowVlanOnLeafTorPairingEnum.NONE ) # Leaf / TOR - leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Enable leaf/TOR ID range", default=False) + leaf_tor_id_range: bool = Field( + alias="leafTorIdRange", + description="Use specific vPC/Port-channel ID range for leaf-tor pairings", + default=False + ) leaf_tor_vpc_port_channel_id_range: str = Field( - alias="leafTorVpcPortChannelIdRange", description="Leaf/TOR vPC port-channel ID range", default="1-499" + alias="leafTorVpcPortChannelIdRange", + description=( + "Specify vPC/Port-channel ID range (minimum: 1, maximum: 4096), this range is used for auto-allocating " + "vPC/Port-Channel IDs for leaf-tor pairings" + ), + default="1-499" ) # Resource ID Ranges l3_vni_no_vlan_default_option: bool = Field( - alias="l3VniNoVlanDefaultOption", description="L3 VNI no-VLAN default option", default=False + alias="l3VniNoVlanDefaultOption", + description=( + "L3 VNI configuration without VLAN configuration. This value is propagated on vrf creation as the default " + "value of 'Enable L3VNI w/o VLAN' in vrf" + ), + default=False ) ip_service_level_agreement_id_range: str = Field( - alias="ipServiceLevelAgreementIdRange", description="IP SLA ID range", default="10000-19999" + alias="ipServiceLevelAgreementIdRange", + description=( + "Service Level Agreement (SLA) ID Range " + "(minimum: 1, maximum: 655214748364735). Per switch SLA ID Range" + ), + default="10000-19999" ) object_tracking_number_range: str = Field( - alias="objectTrackingNumberRange", description="Object tracking number range", default="100-299" + alias="objectTrackingNumberRange", + description="Tracked Object ID Range (minimum: 1, maximum: 512) Per switch tracked object ID Range", + default="100-299" ) service_network_vlan_range: str = Field( - alias="serviceNetworkVlanRange", description="Service network VLAN range", default="3000-3199" + alias="serviceNetworkVlanRange", + description=( + "Service Network VLAN Range (minimum: 2, maximum: 4094). " + "Per Switch Overlay Service Network VLAN Range" + ), + default="3000-3199" ) route_map_sequence_number_range: str = Field( - alias="routeMapSequenceNumberRange", description="Route map sequence number range", default="1-65534" + alias="routeMapSequenceNumberRange", + description="Route Map Sequence Number Range (minimum: 1, maximum: 65534)", + default="1-65534" ) # DNS / NTP / Syslog Collections @@ -1030,61 +1723,172 @@ class VxlanIbgpManagementModel(NDNestedModel): dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection") dns_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="dnsVrfCollection") syslog_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerCollection") - syslog_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerVrfCollection") - syslog_severity_collection: List[int] = Field(default_factory=lambda: [7], alias="syslogSeverityCollection", description="Syslog severity levels (0-7)") + syslog_server_vrf_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="syslogServerVrfCollection" + ) + syslog_severity_collection: List[int] = Field( + default_factory=lambda: [7], + alias="syslogSeverityCollection", + description="List of Syslog severity values, one per Syslog server" + ) # Extra Config / Pre-Interface Config / AAA / Banner - banner: str = Field(description="Fabric banner text", default="") - extra_config_leaf: str = Field(alias="extraConfigLeaf", description="Extra leaf config", default="") - extra_config_spine: str = Field(alias="extraConfigSpine", description="Extra spine config", default="") - extra_config_tor: str = Field(alias="extraConfigTor", description="Extra TOR config", default="") + banner: str = Field( + description=( + "Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) followed by message " + "ending with delimiter" + ), + default="" + ) + extra_config_leaf: str = Field( + alias="extraConfigLeaf", + description=( + "Additional CLIs as captured from the show running configuration, added after interface configurations for " + "all switches with a VTEP unless they have some spine role" + ), + default="" + ) + extra_config_spine: str = Field( + alias="extraConfigSpine", + description=( + "Additional CLIs as captured from the show running configuration, added after interface configurations for " + "all switches with some spine role" + ), + default="" + ) + extra_config_tor: str = Field( + alias="extraConfigTor", + description=( + "Additional CLIs as captured from the show running configuration, added after interface configurations for " + "all ToRs" + ), + default="" + ) extra_config_intra_fabric_links: str = Field( - alias="extraConfigIntraFabricLinks", description="Extra intra-fabric links config", default="" + alias="extraConfigIntraFabricLinks", description="Additional CLIs for all Intra-Fabric links", default="" + ) + extra_config_aaa: str = Field(alias="extraConfigAaa", description="AAA Configurations", default="") + aaa: bool = Field(description="Include AAA configs from Manageability tab during device bootup", default=False) + pre_interface_config_leaf: str = Field( + alias="preInterfaceConfigLeaf", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all switches with a VTEP unless they have some spine role" + ), + default="" + ) + pre_interface_config_spine: str = Field( + alias="preInterfaceConfigSpine", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all switches with some spine role" + ), + default="" + ) + pre_interface_config_tor: str = Field( + alias="preInterfaceConfigTor", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all ToRs" + ), + default="" ) - extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") - aaa: bool = Field(description="Enable AAA", default=False) - pre_interface_config_leaf: str = Field(alias="preInterfaceConfigLeaf", description="Pre-interface leaf config", default="") - pre_interface_config_spine: str = Field(alias="preInterfaceConfigSpine", description="Pre-interface spine config", default="") - pre_interface_config_tor: str = Field(alias="preInterfaceConfigTor", description="Pre-interface TOR config", default="") # System / Compliance / OAM / Misc anycast_border_gateway_advertise_physical_ip: bool = Field( - alias="anycastBorderGatewayAdvertisePhysicalIp", description="Anycast border gateway advertise physical IP", default=False + alias="anycastBorderGatewayAdvertisePhysicalIp", + description="To advertise Anycast Border Gateway PIP as VTEP. Effective on MSD fabric 'Recalculate Config'", + default=False + ) + greenfield_debug_flag: GreenfieldDebugFlagEnum = Field( + alias="greenfieldDebugFlag", + description="Allow switch configuration to be cleared without a reload when preserveConfig is set to false", + default=GreenfieldDebugFlagEnum.DISABLE ) - greenfield_debug_flag: GreenfieldDebugFlagEnum = Field(alias="greenfieldDebugFlag", description="Greenfield debug flag", default=GreenfieldDebugFlagEnum.DISABLE) interface_statistics_load_interval: int = Field( - alias="interfaceStatisticsLoadInterval", description="Interface statistics load interval", default=10 + alias="interfaceStatisticsLoadInterval", + description="Interface Statistics Load Interval. Time in seconds", + default=10 + ) + nve_hold_down_timer: int = Field( + alias="nveHoldDownTimer", + description="NVE Source Inteface HoldDown Time in seconds", + default=180 + ) + next_generation_oam: bool = Field( + alias="nextGenerationOAM", + description=( + "Enable the Next Generation (NG) OAM feature for all switches in the fabric to aid in trouble-shooting " + "VXLAN EVPN fabrics" + ), + default=True ) - nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE hold-down timer", default=180) - next_generation_oam: bool = Field(alias="nextGenerationOAM", description="Enable next-generation OAM", default=True) ngoam_south_bound_loop_detect: bool = Field( - alias="ngoamSouthBoundLoopDetect", description="Enable NGOAM south bound loop detect", default=False + alias="ngoamSouthBoundLoopDetect", + description="Enable the Next Generation (NG) OAM southbound loop detection", + default=False ) ngoam_south_bound_loop_detect_probe_interval: int = Field( - alias="ngoamSouthBoundLoopDetectProbeInterval", description="NGOAM south bound loop detect probe interval", default=300 + alias="ngoamSouthBoundLoopDetectProbeInterval", + description="Set Next Generation (NG) OAM southbound loop detection probe interval in seconds.", + default=300 ) ngoam_south_bound_loop_detect_recovery_interval: int = Field( - alias="ngoamSouthBoundLoopDetectRecoveryInterval", description="NGOAM south bound loop detect recovery interval", default=600 + alias="ngoamSouthBoundLoopDetectRecoveryInterval", + description="Set the Next Generation (NG) OAM southbound loop detection recovery interval in seconds", + default=600 ) strict_config_compliance_mode: bool = Field( - alias="strictConfigComplianceMode", description="Enable strict config compliance mode", default=False + alias="strictConfigComplianceMode", + description=( + "Enable bi-directional compliance checks to flag additional configs in the running config that are not in " + "the intent/expected config" + ), + default=False + ) + advanced_ssh_option: bool = Field( + alias="advancedSshOption", + description="Enable AAA IP Authorization. Enable only, when IP Authorization is enabled in the AAA Server", + default=False + ) + copp_policy: CoppPolicyEnum = Field( + alias="coppPolicy", + description="Fabric wide CoPP policy. Customized CoPP policy should be provided when 'manual' is selected.", + default=CoppPolicyEnum.STRICT + ) + power_redundancy_mode: PowerRedundancyModeEnum = Field( + alias="powerRedundancyMode", + description="Default Power Supply Mode for NX-OS Switches", + default=PowerRedundancyModeEnum.REDUNDANT ) - advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) - copp_policy: CoppPolicyEnum = Field(alias="coppPolicy", description="CoPP policy", default=CoppPolicyEnum.STRICT) - power_redundancy_mode: PowerRedundancyModeEnum = Field(alias="powerRedundancyMode", description="Power redundancy mode", default=PowerRedundancyModeEnum.REDUNDANT) host_interface_admin_state: bool = Field( - alias="hostInterfaceAdminState", description="Host interface admin state", default=True + alias="hostInterfaceAdminState", description="Unshut Host Interfaces by Default", default=True + ) + heartbeat_interval: int = Field( + alias="heartbeatInterval", + description="XConnect heartbeat interval for periodic link status checks", + default=190 + ) + policy_based_routing: bool = Field( + alias="policyBasedRouting", + description="Enable feature pbr, sla sender, epbr, or enable feature pbr, based on the L4-L7 Services use case", + default=False ) - heartbeat_interval: int = Field(alias="heartbeatInterval", description="Heartbeat interval", default=190) - policy_based_routing: bool = Field(alias="policyBasedRouting", description="Enable policy-based routing", default=False) brownfield_network_name_format: str = Field( - alias="brownfieldNetworkNameFormat", description="Brownfield network name format", default="Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + alias="brownfieldNetworkNameFormat", + description="Generated network name should be less than 64 characters", + default="Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" ) brownfield_skip_overlay_network_attachments: bool = Field( - alias="brownfieldSkipOverlayNetworkAttachments", description="Skip brownfield overlay network attachments", default=False + alias="brownfieldSkipOverlayNetworkAttachments", + description="Skip Overlay Network Interface Attachments for Brownfield and Host Port Resync cases", + default=False ) allow_smart_switch_onboarding: bool = Field( - alias="allowSmartSwitchOnboarding", description="Allow smart switch onboarding", default=False + alias="allowSmartSwitchOnboarding", + description="Enable onboarding of smart switches to Hypershield for firewall service", + default=False ) # Hypershield / Connectivity @@ -1208,17 +2012,44 @@ class FabricIbgpModel(NDBaseModel): location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations - license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) - alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) - telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) - telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") - telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") - telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") + license_tier: LicenseTierEnum = Field( + alias="licenseTier", + description="License tier", + default=LicenseTierEnum.PREMIER + ) + alert_suspend: AlertSuspendEnum = Field( + alias="alertSuspend", + description="Alert suspension state", + default=AlertSuspendEnum.DISABLED + ) + telemetry_collection: bool = Field( + alias="telemetryCollection", + description="Enable telemetry collection", + default=False + ) + telemetry_collection_type: str = Field( + alias="telemetryCollectionType", + description="Telemetry collection type", + default="outOfBand" + ) + telemetry_streaming_protocol: str = Field( + alias="telemetryStreamingProtocol", + description="Telemetry streaming protocol", + default="ipv4" + ) + telemetry_source_interface: str = Field( + alias="telemetrySourceInterface", + description="Telemetry source interface", + default="" + ) telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") # Core Management Configuration - management: Optional[VxlanIbgpManagementModel] = Field(description="iBGP VXLAN management configuration", default=None) + management: Optional[VxlanIbgpManagementModel] = Field( + description="iBGP VXLAN management configuration", + default=None + ) # Optional Advanced Settings telemetry_settings: Optional[TelemetrySettingsModel] = Field( diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 9d857fc6..8f834e4f 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -60,7 +60,7 @@ - The license tier for the fabric. type: str default: premier - choices: [ essentials, premier ] + choices: [ essentials, advantage, premier ] alert_suspend: description: - The alert suspension state for the fabric. @@ -111,19 +111,19 @@ bgp_asn: description: - The BGP Autonomous System Number for the fabric. - - Must be a numeric value between 1 and 4294967295. + - Accepts a plain integer (1-4294967295) or dotted notation (1-65535.0-65535). type: str required: true site_id: description: - - The site identifier for the fabric. - - Must be a numeric value between 1 and 65535. + - The site identifier for the fabric (for EVPN Multi-Site support). + - Must be a numeric value between 1 and 281474976710655. - Defaults to the value of O(config.management.bgp_asn) if not provided. type: str default: "" target_subnet_mask: description: - - The target subnet mask for intra-fabric links. + - The target subnet mask for intra-fabric links (24-31). type: int default: 30 anycast_gateway_mac: @@ -149,9 +149,11 @@ default: false underlay_multicast_group_address_limit: description: - - The underlay multicast group address limit (1-255). + - The underlay multicast group address limit. + - The maximum supported value is 128 for NX-OS version 10.2(1) or earlier and 512 for versions above 10.2(1). type: int default: 128 + choices: [ 128, 512 ] tenant_routed_multicast: description: - Enable tenant routed multicast. @@ -159,9 +161,10 @@ default: false rendezvous_point_count: description: - - The number of rendezvous points (1-4). + - The number of spines acting as Rendezvous-Points (RPs). type: int default: 2 + choices: [ 2, 4 ] rendezvous_point_loopback_id: description: - The rendezvous point loopback interface ID (0-1023). @@ -186,9 +189,10 @@ default: "0.0.0.0" fabric_interface_type: description: - - The fabric interface type. + - The fabric interface type. Numbered (Point-to-Point) or unnumbered. type: str default: p2p + choices: [ p2p, unNumbered ] bgp_loopback_id: description: - The BGP loopback interface ID (0-1023). @@ -201,9 +205,10 @@ default: 1 route_reflector_count: description: - - The number of BGP route reflectors (1-4). + - The number of spines acting as BGP route reflectors. type: int default: 2 + choices: [ 2, 4 ] bgp_loopback_ip_range: description: - The BGP loopback IP address pool. @@ -226,7 +231,7 @@ default: "10.4.0.0/16" router_id_range: description: - - The router ID IP address pool. + - The BGP router ID range in IPv4 subnet format. Used for IPv6 underlay. type: str default: "10.2.0.0/23" l2_vni_range: @@ -251,14 +256,9 @@ default: "2000-2299" sub_interface_dot1q_range: description: - - The sub-interface 802.1q range. + - The sub-interface 802.1q range (minimum 2, maximum 4093). type: str default: "2-511" - service_network_vlan_range: - description: - - The service network VLAN range. - type: str - default: "3000-3199" l3_vni_no_vlan_default_option: description: - Enable L3 VNI no-VLAN default option. @@ -293,7 +293,8 @@ description: - The vPC peer keep-alive option. type: str - default: loopback + default: management + choices: [ loopback, management ] vpc_auto_recovery_timer: description: - The vPC auto recovery timer in seconds (240-3600). @@ -421,9 +422,10 @@ default: 10 greenfield_debug_flag: description: - - The greenfield debug flag. + - Allow switch configuration to be cleared without a reload when preserveConfig is set to false. type: str - default: enable + default: disable + choices: [ enable, disable ] nxapi: description: - Enable NX-API (HTTPS). @@ -436,9 +438,9 @@ default: 443 nxapi_http: description: - - Enable NX-API HTTP. + - Enable NX-API over HTTP. type: bool - default: true + default: false nxapi_http_port: description: - The NX-API HTTP port (1-65535). @@ -451,9 +453,10 @@ default: false bgp_authentication_key_type: description: - - The BGP authentication key type. + - "BGP key encryption type: 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7." type: str default: 3des + choices: [ 3des, type6, type7 ] bgp_authentication_key: description: - The BGP authentication key. @@ -529,6 +532,7 @@ - The IS-IS level. type: str default: level-2 + choices: [ level-1, level-2 ] isis_area_number: description: - The IS-IS area number. @@ -579,6 +583,7 @@ - The MACsec cipher suite. type: str default: GCM-AES-XPN-256 + choices: [ GCM-AES-128, GCM-AES-256, GCM-AES-XPN-128, GCM-AES-XPN-256 ] macsec_key_string: description: - The MACsec primary key string. @@ -586,9 +591,10 @@ default: "" macsec_algorithm: description: - - The MACsec algorithm. + - The MACsec primary cryptographic algorithm. type: str default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] macsec_fallback_key_string: description: - The MACsec fallback key string. @@ -596,9 +602,10 @@ default: "" macsec_fallback_algorithm: description: - - The MACsec fallback algorithm. + - The MACsec fallback cryptographic algorithm. type: str default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] macsec_report_timer: description: - The MACsec report timer. @@ -606,9 +613,39 @@ default: 5 vrf_lite_macsec: description: - - Enable MACsec on VRF lite links. + - Enable MACsec on DCI links. type: bool default: false + vrf_lite_macsec_cipher_suite: + description: + - The DCI MACsec cipher suite. + type: str + default: GCM-AES-XPN-256 + choices: [ GCM-AES-128, GCM-AES-256, GCM-AES-XPN-128, GCM-AES-XPN-256 ] + vrf_lite_macsec_key_string: + description: + - The DCI MACsec primary key string (Cisco Type 7 Encrypted Octet String). + type: str + default: "" + vrf_lite_macsec_algorithm: + description: + - The DCI MACsec primary cryptographic algorithm. + type: str + default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] + vrf_lite_macsec_fallback_key_string: + description: + - The DCI MACsec fallback key string (Cisco Type 7 Encrypted Octet String). + - This parameter is used when DCI link has QKD disabled. + type: str + default: "" + vrf_lite_macsec_fallback_algorithm: + description: + - The DCI MACsec fallback cryptographic algorithm. + - This parameter is used when DCI link has QKD disabled. + type: str + default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] quantum_key_distribution: description: - Enable quantum key distribution. @@ -631,14 +668,22 @@ default: 0 trustpoint_label: description: - - The trustpoint label. + - The trustpoint label for TLS authentication. type: str default: "" + skip_certificate_verification: + description: + - Skip verification of incoming certificate. + type: bool + default: false vrf_lite_auto_config: description: - - The VRF lite auto-configuration mode. + - "VRF Lite Inter-Fabric Connection deployment options. If C(back2BackAndToExternal) is selected, + VRF Lite IFCs are auto created between border devices of two Easy Fabrics, and between + border devices in Easy Fabric and edge routers in External Fabric." type: str default: manual + choices: [ manual, back2BackAndToExternal ] vrf_lite_subnet_range: description: - The VRF lite subnet IP address pool. @@ -649,16 +694,6 @@ - The VRF lite subnet target mask. type: int default: 30 - vrf_lite_ipv6_subnet_range: - description: - - The VRF lite IPv6 subnet range. - type: str - default: "fd00::a33:0/112" - vrf_lite_ipv6_subnet_target_mask: - description: - - The VRF lite IPv6 subnet target mask (112-128). - type: int - default: 126 auto_unique_vrf_lite_ip_prefix: description: - Enable auto unique VRF lite IP prefix. @@ -679,6 +714,11 @@ - Enable auto symmetric default VRF. type: bool default: false + default_vrf_redistribution_bgp_route_map: + description: + - Route Map used to redistribute BGP routes to IGP in default VRF in auto created VRF Lite IFC links. + type: str + default: extcon-rmap-filter per_vrf_loopback_auto_provision: description: - Enable per-VRF loopback auto-provisioning. @@ -699,6 +739,27 @@ - The per-VRF loopback IPv6 address pool. type: str default: "fd00::a05:0/112" + per_vrf_unique_loopback_auto_provision: + description: + - Auto provision a unique IPv4 loopback on a VTEP on VRF attachment. + - This option and per VRF per VTEP loopback auto-provisioning are mutually exclusive. + type: bool + default: false + per_vrf_unique_loopback_ip_range: + description: + - Prefix pool to assign unique IPv4 addresses to loopbacks on VTEPs on a per VRF basis. + type: str + default: "10.6.0.0/22" + per_vrf_unique_loopback_auto_provision_v6: + description: + - Auto provision a unique IPv6 loopback on a VTEP on VRF attachment. + type: bool + default: false + per_vrf_unique_loopback_ipv6_range: + description: + - Prefix pool to assign unique IPv6 addresses to loopbacks on VTEPs on a per VRF basis. + type: str + default: "fd00::a06:0/112" underlay_ipv6: description: - Enable IPv6 underlay. @@ -744,6 +805,63 @@ - The IPv6 anycast rendezvous point IP address pool. type: str default: "fd00::254:254:0/118" + mvpn_vrf_route_import_id: + description: + - Enable MVPN VRI ID generation for Tenant Routed Multicast with IPv4 underlay. + type: bool + default: true + mvpn_vrf_route_import_id_range: + description: + - MVPN VRI ID range (minimum 1, maximum 65535) for vPC. + - Applicable when TRM is enabled with IPv6 underlay, or mvpn_vrf_route_import_id is enabled with IPv4 underlay. + type: str + default: "" + vrf_route_import_id_reallocation: + description: + - One time VRI ID re-allocation based on MVPN VRI ID Range. + type: bool + default: false + l3vni_multicast_group: + description: + - Default underlay multicast group IPv4 address assigned for every overlay VRF. + type: str + default: "239.1.1.0" + l3_vni_ipv6_multicast_group: + description: + - Default underlay multicast group IPv6 address assigned for every overlay VRF. + type: str + default: "ff1e::" + rendezvous_point_mode: + description: + - Multicast rendezvous point mode. For IPv6 underlay, use C(asm) only. + type: str + default: asm + choices: [ asm, bidir ] + phantom_rendezvous_point_loopback_id1: + description: + - Underlay phantom RP loopback primary ID for PIM Bi-dir deployments. + type: int + default: 2 + phantom_rendezvous_point_loopback_id2: + description: + - Underlay phantom RP loopback secondary ID for PIM Bi-dir deployments. + type: int + default: 3 + phantom_rendezvous_point_loopback_id3: + description: + - Underlay phantom RP loopback tertiary ID for PIM Bi-dir deployments. + type: int + default: 4 + phantom_rendezvous_point_loopback_id4: + description: + - Underlay phantom RP loopback quaternary ID for PIM Bi-dir deployments. + type: int + default: 5 + anycast_loopback_id: + description: + - Underlay Anycast Loopback ID. Used for vPC Peering in VXLANv6 Fabrics. + type: int + default: 10 auto_bgp_neighbor_description: description: - Enable automatic BGP neighbor description. @@ -798,32 +916,119 @@ description: - The security group status. type: str - default: enabled + default: disabled + choices: [ enabled, enabledStrict, enabledLoose, enablePending, enablePendingStrict, enablePendingLoose, disablePending, disabled ] default_queuing_policy: description: - - Enable default queuing policy. + - Enable default queuing policies. type: bool default: false + default_queuing_policy_cloudscale: + description: + - Queuing policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric. + type: str + default: queuing_policy_default_8q_cloudscale + default_queuing_policy_r_series: + description: + - Queuing policy for all Nexus R-series switches. + type: str + default: queuing_policy_default_r_series + default_queuing_policy_other: + description: + - Queuing policy for all other switches in the fabric. + type: str + default: queuing_policy_default_other aiml_qos: description: - - Enable AI/ML QoS. + - Enable AI/ML QoS. Configures QoS and queuing policies specific to N9K Cloud Scale and Silicon One switch fabric + for AI network workloads. type: bool default: false aiml_qos_policy: description: - - The AI/ML QoS policy. + - Queuing policy based on predominant fabric link speed. type: str default: 400G + choices: [ 800G, 400G, 100G, 25G, User-defined ] + roce_v2: + description: + - DSCP for RDMA traffic. Numeric (0-63) with ranges/comma, or named values. + type: str + default: "26" + cnp: + description: + - DSCP value for Congestion Notification. Numeric (0-63) with ranges/comma, or named values. + type: str + default: "48" + wred_min: + description: + - WRED minimum threshold (in kbytes). + type: int + default: 950 + wred_max: + description: + - WRED maximum threshold (in kbytes). + type: int + default: 3000 + wred_drop_probability: + description: + - WRED drop probability percentage. + type: int + default: 7 + wred_weight: + description: + - Influences how quickly WRED reacts to queue depth changes. + type: int + default: 0 + bandwidth_remaining: + description: + - Percentage of remaining bandwidth allocated to AI traffic queues. + type: int + default: 50 dlb: description: - - Enable dynamic load balancing. + - Enable fabric-level Dynamic Load Balancing (DLB). Inter-Switch-Links will be configured as DLB interfaces. type: bool default: false dlb_mode: description: - - The DLB mode. + - "Select system-wide DLB mode: flowlet, per-packet (packet spraying), or policy driven mixed mode. + Mixed mode is supported on Silicon One (S1) platform only." type: str default: flowlet + choices: [ flowlet, per-packet, policy-driven-flowlet, policy-driven-per-packet, policy-driven-mixed-mode ] + dlb_mixed_mode_default: + description: + - Default load balancing mode for policy driven mixed mode DLB. + type: str + default: ecmp + choices: [ ecmp, flowlet, per-packet ] + flowlet_aging: + description: + - "Flowlet aging timer in microseconds. Valid range depends on platform: Cloud Scale (CS)=1-2000000, + Silicon One (S1)=1-1024." + type: int + default: 1 + flowlet_dscp: + description: + - DSCP values for flowlet load balancing. Numeric (0-63) with ranges/comma, or named values. + type: str + default: "" + per_packet_dscp: + description: + - DSCP values for per-packet load balancing. Numeric (0-63) with ranges/comma, or named values. + type: str + default: "" + ai_load_sharing: + description: + - Enable IP load sharing using source and destination address for AI workloads. + type: bool + default: false + priority_flow_control_watch_interval: + description: + - PFC watch interval in milliseconds (101-1000). Leave blank for system default (100ms). + type: int + default: 101 ptp: description: - Enable Precision Time Protocol (PTP). @@ -836,24 +1041,31 @@ default: 0 ptp_domain_id: description: - - The PTP domain ID. + - The PTP domain ID for multiple independent PTP clocking subdomains on a single network. type: int default: 0 + ptp_vlan_id: + description: + - Precision Time Protocol (PTP) source VLAN ID. SVI used for PTP source on ToRs. + type: int + default: 2 stp_root_option: description: - - The STP root option. + - "Which protocol to use for configuring root bridge: rpvst+ (Rapid Per-VLAN Spanning Tree), + mst (Multiple Spanning Tree), or unmanaged (STP Root not managed by ND)." type: str - default: mst + default: unmanaged + choices: [ rpvst+, mst, unmanaged ] stp_vlan_range: description: - - The STP VLAN range. + - The STP VLAN range (minimum 1, maximum 4094). type: str - default: "" + default: "1-3967" mst_instance_range: description: - - The MST instance range. + - The MST instance range (minimum 0, maximum 4094). type: str - default: "0-3,5,7-9" + default: "0" stp_bridge_priority: description: - The STP bridge priority. @@ -866,9 +1078,14 @@ default: false mpls_loopback_identifier: description: - - The MPLS loopback identifier. + - The MPLS loopback identifier used for VXLAN to MPLS SR/LDP Handoff. type: int default: 101 + mpls_isis_area_number: + description: + - IS-IS area number for DCI MPLS link. Used only if routing protocol on DCI MPLS link is IS-IS. + type: str + default: "0001" mpls_loopback_ip_range: description: - The MPLS loopback IP address pool. @@ -876,9 +1093,30 @@ default: "10.101.0.0/25" private_vlan: description: - - Enable private VLAN support. + - Enable PVLAN on switches except spines and super spines. + type: bool + default: false + default_private_vlan_secondary_network_template: + description: + - Default PVLAN secondary network template. + type: str + default: Pvlan_Secondary_Network + allow_vlan_on_leaf_tor_pairing: + description: + - "Set trunk allowed VLAN to 'none' or 'all' for leaf-TOR pairing port-channels." + type: str + default: none + choices: [ none, all ] + leaf_tor_id_range: + description: + - Use specific vPC/Port-channel ID range for leaf-TOR pairings. type: bool default: false + leaf_tor_vpc_port_channel_id_range: + description: + - Specify vPC/Port-channel ID range (minimum 1, maximum 4096) for leaf-TOR pairings. + type: str + default: "1-499" ip_service_level_agreement_id_range: description: - The IP SLA ID range. @@ -891,9 +1129,14 @@ default: "100-299" route_map_sequence_number_range: description: - - The route map sequence number range. + - The route map sequence number range (minimum 1, maximum 65534). type: str default: "1-65534" + service_network_vlan_range: + description: + - Per Switch Overlay Service Network VLAN Range (minimum 2, maximum 4094). + type: str + default: "3000-3199" day0_bootstrap: description: - Enable day-0 bootstrap (POAP). @@ -906,9 +1149,10 @@ default: false dhcp_protocol_version: description: - - The DHCP protocol version for bootstrap. + - The IP protocol version for local DHCP server. type: str default: dhcpv4 + choices: [ dhcpv4, dhcpv6 ] dhcp_start_address: description: - The DHCP start address for bootstrap. @@ -934,6 +1178,160 @@ - The management IPv6 prefix length for bootstrap. type: int default: 64 + extra_config_nxos_bootstrap: + description: + - Additional CLIs required during device bootup/login (e.g. AAA/Radius). + type: str + default: "" + un_numbered_bootstrap_loopback_id: + description: + - Bootstrap Seed Switch Loopback Interface ID. + type: int + default: 253 + un_numbered_dhcp_start_address: + description: + - Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + type: str + default: "" + un_numbered_dhcp_end_address: + description: + - Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + type: str + default: "" + inband_management: + description: + - Manage switches with only inband connectivity. + type: bool + default: false + inband_dhcp_servers: + description: + - List of external DHCP server IP addresses (Max 3). + type: list + elements: str + seed_switch_core_interfaces: + description: + - Seed switch fabric interfaces. Core-facing interface list on seed switch. + type: list + elements: str + spine_switch_core_interfaces: + description: + - Spine switch fabric interfaces. Core-facing interface list on all spines. + type: list + elements: str + bootstrap_subnet_collection: + description: + - List of IPv4 or IPv6 subnets to be used for bootstrap. + type: list + elements: dict + suboptions: + start_ip: + description: + - Starting IP address of the bootstrap range. + type: str + required: true + end_ip: + description: + - Ending IP address of the bootstrap range. + type: str + required: true + default_gateway: + description: + - Default gateway for bootstrap subnet. + type: str + required: true + subnet_prefix: + description: + - Subnet prefix length (8-30). + type: int + required: true + netflow_settings: + description: + - Settings associated with netflow. + type: dict + suboptions: + netflow: + description: + - Enable netflow collection. + type: bool + default: false + netflow_exporter_collection: + description: + - List of netflow exporters. + type: list + elements: dict + suboptions: + exporter_name: + description: + - Name of the netflow exporter. + type: str + required: true + exporter_ip: + description: + - IP address of the netflow collector. + type: str + required: true + vrf: + description: + - VRF name for the exporter. + type: str + default: management + source_interface_name: + description: + - Source interface name. + type: str + required: true + udp_port: + description: + - UDP port for netflow export (1-65535). + type: int + required: true + netflow_record_collection: + description: + - List of netflow records. + type: list + elements: dict + suboptions: + record_name: + description: + - Name of the netflow record. + type: str + required: true + record_template: + description: + - Template type for the record. + type: str + required: true + layer2_record: + description: + - Enable layer 2 record fields. + type: bool + default: false + netflow_monitor_collection: + description: + - List of netflow monitors. + type: list + elements: dict + suboptions: + monitor_name: + description: + - Name of the netflow monitor. + type: str + required: true + record_name: + description: + - Associated record name. + type: str + required: true + exporter1_name: + description: + - Primary exporter name. + type: str + required: true + exporter2_name: + description: + - Secondary exporter name. + type: str + default: "" real_time_backup: description: - Enable real-time backup. @@ -956,24 +1354,47 @@ default: 180 next_generation_oam: description: - - Enable next-generation OAM. + - Enable the Next Generation (NG) OAM feature for all switches in the fabric. type: bool default: true + ngoam_south_bound_loop_detect: + description: + - Enable the Next Generation (NG) OAM southbound loop detection. + type: bool + default: false + ngoam_south_bound_loop_detect_probe_interval: + description: + - Set NG OAM southbound loop detection probe interval in seconds. + type: int + default: 300 + ngoam_south_bound_loop_detect_recovery_interval: + description: + - Set NG OAM southbound loop detection recovery interval in seconds. + type: int + default: 600 strict_config_compliance_mode: description: - - Enable strict configuration compliance mode. + - Enable bi-directional compliance checks to flag additional configs in the running config + that are not in the intent/expected config. + type: bool + default: false + advanced_ssh_option: + description: + - Enable AAA IP Authorization. Enable only when IP Authorization is enabled in the AAA Server. type: bool default: false copp_policy: description: - - The CoPP policy. + - The fabric wide CoPP policy. Customized CoPP policy should be provided when C(manual) is selected. type: str - default: dense + default: strict + choices: [ dense, lenient, moderate, strict, manual ] power_redundancy_mode: description: - - The power redundancy mode. + - Default power supply mode for NX-OS switches. type: str default: redundant + choices: [ redundant, combined, inputSrcRedundant ] host_interface_admin_state: description: - Enable host interface admin state. @@ -1001,9 +1422,25 @@ default: false allow_smart_switch_onboarding: description: - - Allow smart switch onboarding. + - Enable onboarding of smart switches to Hypershield for firewall service. type: bool default: false + connectivity_domain_name: + description: + - Domain name to connect to Hypershield. + type: str + hypershield_connectivity_proxy_server: + description: + - IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication. + type: str + hypershield_connectivity_proxy_server_port: + description: + - Proxy port number for communication with Hypershield. + type: int + hypershield_connectivity_source_intf: + description: + - Loopback interface on smart switch for communication with Hypershield. + type: str aaa: description: - Enable AAA. @@ -1034,6 +1471,22 @@ - Extra freeform AAA configuration. type: str default: "" + pre_interface_config_leaf: + description: + - Additional CLIs added before interface configurations for all switches with a VTEP + unless they have some spine role. + type: str + default: "" + pre_interface_config_spine: + description: + - Additional CLIs added before interface configurations for all switches with some spine role. + type: str + default: "" + pre_interface_config_tor: + description: + - Additional CLIs added before interface configurations for all ToRs. + type: str + default: "" banner: description: - The fabric banner text displayed on switch login. @@ -1044,11 +1497,21 @@ - The list of NTP server IP addresses. type: list elements: str + ntp_server_vrf_collection: + description: + - The list of VRFs for NTP servers. + type: list + elements: str dns_collection: description: - The list of DNS server IP addresses. type: list elements: str + dns_vrf_collection: + description: + - The list of VRFs for DNS servers. + type: list + elements: str syslog_server_collection: description: - The list of syslog server IP addresses. From 7f39af2366b17f7bc4f4bf6778f7e8ae3df97548 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Fri, 27 Mar 2026 18:25:42 -0400 Subject: [PATCH 04/27] Update pydantic model for ebgp --- .../manage_fabric/manage_fabric_ebgp.py | 1028 +++++++++++++---- 1 file changed, 828 insertions(+), 200 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index 8894941c..e30fe28c 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -33,6 +33,19 @@ VpcPeerKeepAliveOptionEnum, BgpAsModeEnum, FirstHopRedundancyProtocolEnum, + AimlQosPolicyEnum, + AllowVlanOnLeafTorPairingEnum, + BgpAuthenticationKeyTypeEnum, + DhcpProtocolVersionEnum, + DlbMixedModeDefaultEnum, + DlbModeEnum, + MacsecAlgorithmEnum, + MacsecCipherSuiteEnum, + PowerRedundancyModeEnum, + RendezvousPointCountEnum, + RendezvousPointModeEnum, + UnderlayMulticastGroupAddressLimitEnum, + VrfLiteAutoConfigEnum, ) # Re-use shared nested models from the iBGP module from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import ( @@ -111,7 +124,7 @@ class VxlanEbgpManagementModel(NDNestedModel): ) # Fabric Type (required for discriminated union) - type: Literal[FabricTypeEnum.VXLAN_EBGP] = Field(description="Fabric management type", default=FabricTypeEnum.VXLAN_EBGP) + type: Literal[FabricTypeEnum.VXLAN_EBGP] = Field(description="Type of the fabric", default=FabricTypeEnum.VXLAN_EBGP) # Core eBGP Configuration bgp_asn: Optional[str] = Field( @@ -119,28 +132,41 @@ class VxlanEbgpManagementModel(NDNestedModel): description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]. Optional when bgpAsnAutoAllocation is True.", default=None ) - site_id: Optional[str] = Field(alias="siteId", description="Site identifier for the fabric. Defaults to Fabric ASN.", default="") + site_id: Optional[str] = Field( + alias="siteId", + description="For EVPN Multi-Site Support. Defaults to Fabric ASN", + default="" + ) bgp_as_mode: BgpAsModeEnum = Field( alias="bgpAsMode", - description="BGP AS mode: multiAS assigns unique AS per leaf tier, sameTierAS assigns same AS within a tier", + description=( + "Multi-AS Unique ASN per Leaf/Border/Border Gateway (Borders and border gateways are " + "allowed to share ASN). Same-Tier-AS Leafs share one ASN, Borders/border gateways share one ASN" + ), default=BgpAsModeEnum.MULTI_AS ) bgp_asn_auto_allocation: bool = Field( alias="bgpAsnAutoAllocation", - description="Enable automatic BGP ASN allocation from bgpAsnRange", + description=( + "Automatically allocate and track BGP ASN for leafs, borders and border gateways " + "in Multi-AS mode" + ), default=True ) bgp_asn_range: Optional[str] = Field( alias="bgpAsnRange", - description="BGP ASN range for automatic allocation (e.g., '65000-65535')", + description=( + "BGP ASN range for auto-allocation " + "(minimum: 1 or 1.0, maximum: 4294967295 or 65535.65535)" + ), default=None ) bgp_allow_as_in_num: int = Field( alias="bgpAllowAsInNum", - description="Number of times BGP allows AS-path that contains local AS", + description="Number of occurrences of ASN allowed in the BGP AS-path", default=1 ) - bgp_max_path: int = Field(alias="bgpMaxPath", description="Maximum number of BGP equal-cost paths", default=4) + bgp_max_path: int = Field(alias="bgpMaxPath", description="BGP Maximum Paths", default=4) bgp_underlay_failure_protect: bool = Field( alias="bgpUnderlayFailureProtect", description="Enable BGP underlay failure protection", @@ -148,39 +174,53 @@ class VxlanEbgpManagementModel(NDNestedModel): ) auto_configure_ebgp_evpn_peering: bool = Field( alias="autoConfigureEbgpEvpnPeering", - description="Automatically configure eBGP EVPN peering between spine and leaf", + description=( + "Automatically configure eBGP EVPN overlay peering between leaf and spine switches" + ), default=True ) allow_leaf_same_as: bool = Field( alias="allowLeafSameAs", - description="Allow leaf switches to have the same BGP AS number", + description="Leafs can have same BGP ASN even when AS mode is Multi-AS", default=False ) assign_ipv4_to_loopback0: bool = Field( alias="assignIpv4ToLoopback0", - description="Assign IPv4 address to loopback0 interface", + description=( + "In an IPv6 routed fabric or VXLAN EVPN fabric with IPv6 underlay, assign IPv4 address " + "used for BGP Router ID to the routing loopback interface" + ), default=True ) - evpn: bool = Field(description="Enable EVPN control plane", default=True) - route_map_tag: int = Field(alias="routeMapTag", description="Route map tag for redistribution", default=12345) + evpn: bool = Field( + description=( + "Enable BGP EVPN as the control plane and VXLAN as the data plane for this fabric" + ), + default=True + ) + route_map_tag: int = Field( + alias="routeMapTag", + description="Tag for Route Map FABRIC-RMAP-REDIST-SUBNET. (Min:0, Max:4294967295)", + default=12345 + ) disable_route_map_tag: bool = Field( alias="disableRouteMapTag", - description="Disable route map tag usage", + description="No match tag for Route Map FABRIC-RMAP-REDIST-SUBNET", default=False ) leaf_bgp_as: Optional[str] = Field( alias="leafBgpAs", - description="BGP AS number for leaf switches (used with sameTierAS mode)", + description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None ) border_bgp_as: Optional[str] = Field( alias="borderBgpAs", - description="BGP AS number for border switches", + description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None ) super_spine_bgp_as: Optional[str] = Field( alias="superSpineBgpAs", - description="BGP AS number for super-spine switches", + description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None ) @@ -188,251 +228,513 @@ class VxlanEbgpManagementModel(NDNestedModel): name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") # Network Addressing - bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="BGP loopback interface ID", ge=0, le=1023, default=0) - bgp_loopback_ip_range: str = Field(alias="bgpLoopbackIpRange", description="BGP loopback IP range", default="10.2.0.0/22") - bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="BGP loopback IPv6 range", default="fd00::a02:0/119") - nve_loopback_id: int = Field(alias="nveLoopbackId", description="NVE loopback interface ID", ge=0, le=1023, default=1) - nve_loopback_ip_range: str = Field(alias="nveLoopbackIpRange", description="NVE loopback IP range", default="10.3.0.0/22") - nve_loopback_ipv6_range: str = Field(alias="nveLoopbackIpv6Range", description="NVE loopback IPv6 range", default="fd00::a03:0/118") - anycast_loopback_id: int = Field(alias="anycastLoopbackId", description="Anycast loopback ID", default=10) + bgp_loopback_id: int = Field( + alias="bgpLoopbackId", + description="Underlay Routing Loopback Id", + ge=0, le=1023, default=0 + ) + bgp_loopback_ip_range: str = Field( + alias="bgpLoopbackIpRange", + description="Typically Loopback0 IP Address Range", + default="10.2.0.0/22" + ) + bgp_loopback_ipv6_range: str = Field( + alias="bgpLoopbackIpv6Range", + description="Typically Loopback0 IPv6 Address Range", + default="fd00::a02:0/119" + ) + nve_loopback_id: int = Field( + alias="nveLoopbackId", + description=( + "Underlay VTEP loopback Id associated with the Network Virtualization Edge (nve) interface" + ), + ge=0, le=1023, default=1 + ) + nve_loopback_ip_range: str = Field( + alias="nveLoopbackIpRange", + description="Typically Loopback1 IP Address Range", + default="10.3.0.0/22" + ) + nve_loopback_ipv6_range: str = Field( + alias="nveLoopbackIpv6Range", + description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", + default="fd00::a03:0/118" + ) + anycast_loopback_id: int = Field( + alias="anycastLoopbackId", + description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", + default=10 + ) anycast_rendezvous_point_ip_range: str = Field( alias="anycastRendezvousPointIpRange", - description="Anycast RP IP range", + description="Anycast or Phantom RP IP Address Range", default="10.254.254.0/24" ) ipv6_anycast_rendezvous_point_ip_range: str = Field( alias="ipv6AnycastRendezvousPointIpRange", - description="IPv6 anycast RP IP range", + description="Anycast RP IPv6 Address Range", default="fd00::254:254:0/118" ) intra_fabric_subnet_range: str = Field( alias="intraFabricSubnetRange", - description="Intra-fabric subnet range", + description="Address range to assign numbered and peer link SVI IPs", default="10.4.0.0/16" ) # VLAN and VNI Ranges - l2_vni_range: str = Field(alias="l2VniRange", description="Layer 2 VNI range", default="30000-49000") - l3_vni_range: str = Field(alias="l3VniRange", description="Layer 3 VNI range", default="50000-59000") - network_vlan_range: str = Field(alias="networkVlanRange", description="Network VLAN range", default="2300-2999") - vrf_vlan_range: str = Field(alias="vrfVlanRange", description="VRF VLAN range", default="2000-2299") + l2_vni_range: str = Field( + alias="l2VniRange", + description="Overlay network identifier range (minimum: 1, maximum: 16777214)", + default="30000-49000" + ) + l3_vni_range: str = Field( + alias="l3VniRange", + description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", + default="50000-59000" + ) + network_vlan_range: str = Field( + alias="networkVlanRange", + description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", + default="2300-2999" + ) + vrf_vlan_range: str = Field( + alias="vrfVlanRange", + description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", + default="2000-2299" + ) # Overlay Configuration - overlay_mode: OverlayModeEnum = Field(alias="overlayMode", description="Overlay configuration mode", default=OverlayModeEnum.CLI) + overlay_mode: OverlayModeEnum = Field( + alias="overlayMode", + description="Overlay Mode. VRF/Network configuration using config-profile or CLI", + default=OverlayModeEnum.CLI + ) replication_mode: ReplicationModeEnum = Field( alias="replicationMode", - description="Multicast replication mode", + description="Replication Mode for BUM Traffic", default=ReplicationModeEnum.MULTICAST ) - multicast_group_subnet: str = Field(alias="multicastGroupSubnet", description="Multicast group subnet", default="239.1.1.0/25") + multicast_group_subnet: str = Field( + alias="multicastGroupSubnet", + description=( + "Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool " + "is used for BUM traffic for each overlay network." + ), + default="239.1.1.0/25" + ) auto_generate_multicast_group_address: bool = Field( alias="autoGenerateMulticastGroupAddress", - description="Auto-generate multicast group addresses", + description=( + "Generate a new multicast group address from the multicast pool using a round-robin approach" + ), default=False ) - underlay_multicast_group_address_limit: int = Field( + underlay_multicast_group_address_limit: UnderlayMulticastGroupAddressLimitEnum = Field( alias="underlayMulticastGroupAddressLimit", - description="Underlay multicast group address limit", - ge=1, - le=255, - default=128 + description=( + "The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " + "and 512 for versions above 10.2(1)" + ), + default=UnderlayMulticastGroupAddressLimitEnum.V_128 + ) + tenant_routed_multicast: bool = Field( + alias="tenantRoutedMulticast", + description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", + default=False ) - tenant_routed_multicast: bool = Field(alias="tenantRoutedMulticast", description="Enable tenant routed multicast", default=False) tenant_routed_multicast_ipv6: bool = Field( alias="tenantRoutedMulticastIpv6", - description="Enable tenant routed multicast IPv6", + description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", default=False ) first_hop_redundancy_protocol: FirstHopRedundancyProtocolEnum = Field( alias="firstHopRedundancyProtocol", - description="First-hop redundancy protocol for tenant networks", + description="First Hop Redundancy Protocol HSRP or VRRP", default=FirstHopRedundancyProtocolEnum.HSRP ) # Multicast / Rendezvous Point - rendezvous_point_count: int = Field( + rendezvous_point_count: RendezvousPointCountEnum = Field( alias="rendezvousPointCount", - description="Number of spines acting as Rendezvous-Points", + description="Number of spines acting as Rendezvous-Points (RPs)", + default=RendezvousPointCountEnum.TWO + ) + rendezvous_point_loopback_id: int = Field( + alias="rendezvousPointLoopbackId", + description="Rendezvous point loopback Id", + default=254 + ) + rendezvous_point_mode: RendezvousPointModeEnum = Field( + alias="rendezvousPointMode", + description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", + default=RendezvousPointModeEnum.ASM + ) + phantom_rendezvous_point_loopback_id1: int = Field( + alias="phantomRendezvousPointLoopbackId1", + description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", default=2 ) - rendezvous_point_loopback_id: int = Field(alias="rendezvousPointLoopbackId", description="RP loopback ID", default=254) - rendezvous_point_mode: str = Field(alias="rendezvousPointMode", description="Multicast RP mode", default="asm") - phantom_rendezvous_point_loopback_id1: int = Field(alias="phantomRendezvousPointLoopbackId1", description="Phantom RP loopback ID 1", default=2) - phantom_rendezvous_point_loopback_id2: int = Field(alias="phantomRendezvousPointLoopbackId2", description="Phantom RP loopback ID 2", default=3) - phantom_rendezvous_point_loopback_id3: int = Field(alias="phantomRendezvousPointLoopbackId3", description="Phantom RP loopback ID 3", default=4) - phantom_rendezvous_point_loopback_id4: int = Field(alias="phantomRendezvousPointLoopbackId4", description="Phantom RP loopback ID 4", default=5) - l3vni_multicast_group: str = Field(alias="l3vniMulticastGroup", description="Default L3 VNI multicast group IPv4 address", default="239.1.1.0") - l3_vni_ipv6_multicast_group: str = Field(alias="l3VniIpv6MulticastGroup", description="Default L3 VNI multicast group IPv6 address", default="ff1e::") - ipv6_multicast_group_subnet: str = Field(alias="ipv6MulticastGroupSubnet", description="IPv6 multicast group subnet", default="ff1e::/121") - mvpn_vrf_route_import_id: bool = Field(alias="mvpnVrfRouteImportId", description="Enable MVPN VRF route import ID", default=True) + phantom_rendezvous_point_loopback_id2: int = Field( + alias="phantomRendezvousPointLoopbackId2", + description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", + default=3 + ) + phantom_rendezvous_point_loopback_id3: int = Field( + alias="phantomRendezvousPointLoopbackId3", + description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", + default=4 + ) + phantom_rendezvous_point_loopback_id4: int = Field( + alias="phantomRendezvousPointLoopbackId4", + description=( + "Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments" + ), + default=5 + ) + l3vni_multicast_group: str = Field( + alias="l3vniMulticastGroup", + description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", + default="239.1.1.0" + ) + l3_vni_ipv6_multicast_group: str = Field( + alias="l3VniIpv6MulticastGroup", + description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", + default="ff1e::" + ) + ipv6_multicast_group_subnet: str = Field( + alias="ipv6MulticastGroupSubnet", + description="IPv6 Multicast address with prefix 112 to 128", + default="ff1e::/121" + ) + mvpn_vrf_route_import_id: bool = Field( + alias="mvpnVrfRouteImportId", + description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", + default=True + ) mvpn_vrf_route_import_id_range: Optional[str] = Field( alias="mvpnVrfRouteImportIdRange", - description="MVPN VRF route import ID range", + description=( + "MVPN VRI ID (minimum: 1, maximum: 65535) for vPC, applicable when TRM enabled " + "with IPv6 underlay, or mvpnVrfRouteImportId enabled with IPv4 underlay" + ), default=None ) vrf_route_import_id_reallocation: bool = Field( alias="vrfRouteImportIdReallocation", - description="Enable VRF route import ID reallocation", + description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", default=False ) # Advanced Features anycast_gateway_mac: str = Field( alias="anycastGatewayMac", - description="Anycast gateway MAC address", + description="Shared anycast gateway MAC address for all VTEPs", default="2020.0000.00aa" ) - target_subnet_mask: int = Field(alias="targetSubnetMask", description="Target subnet mask", ge=24, le=31, default=30) - fabric_mtu: int = Field(alias="fabricMtu", description="Fabric MTU size", ge=1500, le=9216, default=9216) - l2_host_interface_mtu: int = Field(alias="l2HostInterfaceMtu", description="L2 host interface MTU", ge=1500, le=9216, default=9216) + target_subnet_mask: int = Field( + alias="targetSubnetMask", + description="Mask for underlay subnet IP range", + ge=24, le=31, default=30 + ) + fabric_mtu: int = Field( + alias="fabricMtu", + description="Intra Fabric Interface MTU. Must be an even number", + ge=1500, le=9216, default=9216 + ) + l2_host_interface_mtu: int = Field( + alias="l2HostInterfaceMtu", + description="Layer 2 host interface MTU. Must be an even number", + ge=1500, le=9216, default=9216 + ) l3_vni_no_vlan_default_option: bool = Field( alias="l3VniNoVlanDefaultOption", - description="L3 VNI configuration without VLAN", + description=( + "L3 VNI configuration without VLAN configuration. This value is propagated on vrf " + "creation as the default value of 'Enable L3VNI w/o VLAN' in vrf" + ), + default=False + ) + underlay_ipv6: bool = Field( + alias="underlayIpv6", + description="If not enabled, IPv4 underlay is used", default=False ) - underlay_ipv6: bool = Field(alias="underlayIpv6", description="Enable IPv6 underlay", default=False) static_underlay_ip_allocation: bool = Field( alias="staticUnderlayIpAllocation", - description="Disable dynamic underlay IP address allocation", + description="Checking this will disable Dynamic Underlay IP Address Allocations", default=False ) anycast_border_gateway_advertise_physical_ip: bool = Field( alias="anycastBorderGatewayAdvertisePhysicalIp", - description="Advertise Anycast Border Gateway PIP as VTEP", + description=( + "To advertise Anycast Border Gateway PIP as VTEP. " + "Effective on MSD fabric 'Recalculate Config'" + ), default=False ) # VPC Configuration - vpc_domain_id_range: str = Field(alias="vpcDomainIdRange", description="vPC domain ID range", default="1-1000") - vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="vPC peer link VLAN", default="3600") + vpc_domain_id_range: str = Field( + alias="vpcDomainIdRange", + description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", + default="1-1000" + ) + vpc_peer_link_vlan: str = Field( + alias="vpcPeerLinkVlan", + description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", + default="3600" + ) vpc_peer_link_enable_native_vlan: bool = Field( alias="vpcPeerLinkEnableNativeVlan", - description="Enable native VLAN on vPC peer link", + description="Enable VpcPeer Link for Native Vlan", default=False ) vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( alias="vpcPeerKeepAliveOption", - description="vPC peer keep-alive option", + description="Use vPC Peer Keep Alive with Loopback or Management", default=VpcPeerKeepAliveOptionEnum.MANAGEMENT ) vpc_auto_recovery_timer: int = Field( alias="vpcAutoRecoveryTimer", - description="vPC auto recovery timer", + description="vPC auto recovery timer (in seconds)", ge=240, le=3600, default=360 ) vpc_delay_restore_timer: int = Field( alias="vpcDelayRestoreTimer", - description="vPC delay restore timer", + description="vPC delay restore timer (in seconds)", ge=1, le=3600, default=150 ) - vpc_peer_link_port_channel_id: str = Field(alias="vpcPeerLinkPortChannelId", description="vPC peer link port-channel ID", default="500") + vpc_peer_link_port_channel_id: str = Field( + alias="vpcPeerLinkPortChannelId", + description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", + default="500" + ) vpc_ipv6_neighbor_discovery_sync: bool = Field( alias="vpcIpv6NeighborDiscoverySync", - description="Enable vPC IPv6 ND sync", + description="Enable IPv6 ND synchronization between vPC peers", + default=True + ) + vpc_layer3_peer_router: bool = Field( + alias="vpcLayer3PeerRouter", + description="Enable Layer-3 Peer-Router on all Leaf switches", default=True ) - vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable vPC layer-3 peer router", default=True) - vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC TOR delay restore timer", default=30) - fabric_vpc_domain_id: bool = Field(alias="fabricVpcDomainId", description="Enable fabric vPC domain ID", default=False) - shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="Shared vPC domain ID", default=1) - fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Enable fabric vPC QoS", default=False) + vpc_tor_delay_restore_timer: int = Field( + alias="vpcTorDelayRestoreTimer", + description="vPC delay restore timer for ToR switches (in seconds)", + default=30 + ) + fabric_vpc_domain_id: bool = Field( + alias="fabricVpcDomainId", + description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", + default=False + ) + shared_vpc_domain_id: int = Field( + alias="sharedVpcDomainId", + description="vPC Domain Id to be used on all vPC pairs", + default=1 + ) + fabric_vpc_qos: bool = Field( + alias="fabricVpcQos", + description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", + default=False + ) fabric_vpc_qos_policy_name: str = Field( alias="fabricVpcQosPolicyName", - description="Fabric vPC QoS policy name", + description="Qos Policy name should be same on all spines", default="spine_qos_for_fabric_vpc_peering" ) - enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable vPC peer-switch feature on ToR switches", default=False) + enable_peer_switch: bool = Field( + alias="enablePeerSwitch", + description="Enable the vPC peer-switch feature on ToR switches", + default=False + ) # Per-VRF Loopback per_vrf_loopback_auto_provision: bool = Field( alias="perVrfLoopbackAutoProvision", - description="Auto provision IPv4 loopback on VRF attachment", + description=( + "Auto provision an IPv4 loopback on a VTEP on VRF attachment. Note: Enabling this option " + "auto-provisions loopback on existing VRF attachments and also when Edit, QuickAttach, or " + "Multiattach actions are performed. Provisioned loopbacks cannot be deleted until VRFs " + "are unattached." + ), default=False ) per_vrf_loopback_ip_range: str = Field( alias="perVrfLoopbackIpRange", - description="Per-VRF loopback IPv4 prefix pool", + description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", default="10.5.0.0/22" ) per_vrf_loopback_auto_provision_ipv6: bool = Field( alias="perVrfLoopbackAutoProvisionIpv6", - description="Auto provision IPv6 loopback on VRF attachment", + description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", default=False ) per_vrf_loopback_ipv6_range: str = Field( alias="perVrfLoopbackIpv6Range", - description="Per-VRF loopback IPv6 prefix pool", + description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", default="fd00::a05:0/112" ) # Templates - vrf_template: str = Field(alias="vrfTemplate", description="VRF template", default="Default_VRF_Universal") - network_template: str = Field(alias="networkTemplate", description="Network template", default="Default_Network_Universal") + vrf_template: str = Field( + alias="vrfTemplate", + description="Default overlay VRF template for leafs", + default="Default_VRF_Universal" + ) + network_template: str = Field( + alias="networkTemplate", + description="Default overlay network template for leafs", + default="Default_Network_Universal" + ) vrf_extension_template: str = Field( alias="vrfExtensionTemplate", - description="VRF extension template", + description="Default overlay VRF template for borders", default="Default_VRF_Extension_Universal" ) network_extension_template: str = Field( alias="networkExtensionTemplate", - description="Network extension template", + description="Default overlay network template for borders", default="Default_Network_Extension_Universal" ) # Optional Advanced Settings - performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) + performance_monitoring: bool = Field( + alias="performanceMonitoring", + description=( + "If enabled, switch metrics are collected through periodic SNMP polling. " + "Alternative to real-time telemetry" + ), + default=False + ) tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable tenant DHCP", default=True) - advertise_physical_ip: bool = Field(alias="advertisePhysicalIp", description="Advertise physical IP as VTEP", default=False) + advertise_physical_ip: bool = Field( + alias="advertisePhysicalIp", + description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", + default=False + ) advertise_physical_ip_on_border: bool = Field( alias="advertisePhysicalIpOnBorder", - description="Advertise physical IP on border switches only", + description=( + "Enable advertise-pip on vPC borders and border gateways only. " + "Applicable only when vPC advertise-pip is not enabled" + ), default=True ) # Protocol Settings — BGP - bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enable BGP authentication", default=False) - bgp_authentication_key_type: str = Field( + bgp_authentication: bool = Field( + alias="bgpAuthentication", + description="Enables or disables the BGP authentication", + default=False + ) + bgp_authentication_key_type: BgpAuthenticationKeyTypeEnum = Field( alias="bgpAuthenticationKeyType", - description="BGP authentication key type", - default="3des" + description="BGP key encryption type: 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7", + default=BgpAuthenticationKeyTypeEnum.THREE_DES + ) + bgp_authentication_key: str = Field( + alias="bgpAuthenticationKey", + description="Encrypted BGP authentication key based on type", + default="" ) - bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="BGP authentication key", default="") # Protocol Settings — BFD - bfd: bool = Field(description="Enable BFD", default=False) - bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD for iBGP", default=False) - bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD authentication", default=False) - bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD authentication key ID", default=100) - bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="BFD authentication key", default="") + bfd: bool = Field(description="Enable BFD. Valid for IPv4 Underlay only", default=False) + bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD For iBGP", default=False) + bfd_authentication: bool = Field( + alias="bfdAuthentication", + description="Enable BFD Authentication. Valid for P2P Interfaces only", + default=False + ) + bfd_authentication_key_id: int = Field( + alias="bfdAuthenticationKeyId", + description="BFD Authentication Key ID", + default=100 + ) + bfd_authentication_key: str = Field( + alias="bfdAuthenticationKey", + description="Encrypted SHA1 secret value", + default="" + ) # Protocol Settings — PIM - pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Enable PIM hello authentication", default=False) - pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="PIM hello authentication key", default="") + pim_hello_authentication: bool = Field( + alias="pimHelloAuthentication", + description="Valid for IPv4 Underlay only", + default=False + ) + pim_hello_authentication_key: str = Field( + alias="pimHelloAuthenticationKey", + description="3DES Encrypted", + default="" + ) # Management Settings - nxapi: bool = Field(description="Enable NX-API", default=False) - nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) - nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) - nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) + nxapi: bool = Field(description="Enable NX-API over HTTPS", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API over HTTP", default=False) + nxapi_https_port: int = Field( + alias="nxapiHttpsPort", + description="HTTPS port for NX-API", + ge=1, le=65535, default=443 + ) + nxapi_http_port: int = Field( + alias="nxapiHttpPort", + description="HTTP port for NX-API", + ge=1, le=65535, default=80 + ) # Bootstrap / Day-0 / DHCP - day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) + day0_bootstrap: bool = Field( + alias="day0Bootstrap", + description="Automatic IP Assignment For POAP", + default=False + ) bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( alias="bootstrapSubnetCollection", - description="Bootstrap subnet collection", + description="List of IPv4 or IPv6 subnets to be used for bootstrap", default_factory=list ) - local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) - dhcp_protocol_version: str = Field(alias="dhcpProtocolVersion", description="DHCP protocol version", default="dhcpv4") - dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") - dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") - management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") - management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) - management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) + local_dhcp_server: bool = Field( + alias="localDhcpServer", + description="Automatic IP Assignment For POAP From Local DHCP Server", + default=False + ) + dhcp_protocol_version: DhcpProtocolVersionEnum = Field( + alias="dhcpProtocolVersion", + description="IP protocol version for Local DHCP Server", + default=DhcpProtocolVersionEnum.DHCPV4 + ) + dhcp_start_address: str = Field( + alias="dhcpStartAddress", + description="DHCP Scope Start Address For Switch POAP", + default="" + ) + dhcp_end_address: str = Field( + alias="dhcpEndAddress", + description="DHCP Scope End Address For Switch POAP", + default="" + ) + management_gateway: str = Field( + alias="managementGateway", + description="Default Gateway For Management VRF On The Switch", + default="" + ) + management_ipv4_prefix: int = Field( + alias="managementIpv4Prefix", + description="Switch Mgmt IP Subnet Prefix if ipv4", + default=24 + ) + management_ipv6_prefix: int = Field( + alias="managementIpv6Prefix", + description="Switch Management IP Subnet Prefix if ipv6", + default=64 + ) # Netflow Settings netflow_settings: NetflowSettingsModel = Field( @@ -442,181 +744,507 @@ class VxlanEbgpManagementModel(NDNestedModel): ) # Backup / Restore - real_time_backup: Optional[bool] = Field(alias="realTimeBackup", description="Enable real-time backup", default=None) - scheduled_backup: Optional[bool] = Field(alias="scheduledBackup", description="Enable scheduled backup", default=None) - scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + real_time_backup: Optional[bool] = Field( + alias="realTimeBackup", + description=( + "Backup hourly only if there is any config deployment since last backup" + ), + default=None + ) + scheduled_backup: Optional[bool] = Field( + alias="scheduledBackup", + description="Enable backup at the specified time daily", + default=None + ) + scheduled_backup_time: str = Field( + alias="scheduledBackupTime", + description=( + "Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)" + ), + default="" + ) # VRF Lite / Sub-Interface - sub_interface_dot1q_range: str = Field(alias="subInterfaceDot1qRange", description="Sub-interface 802.1q range", default="2-511") - vrf_lite_auto_config: str = Field(alias="vrfLiteAutoConfig", description="VRF lite auto-config mode", default="manual") - vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="VRF lite subnet range", default="10.33.0.0/16") - vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF lite subnet target mask", default=30) + sub_interface_dot1q_range: str = Field( + alias="subInterfaceDot1qRange", + description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", + default="2-511" + ) + vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field( + alias="vrfLiteAutoConfig", + description=( + "VRF Lite Inter-Fabric Connection Deployment Options. If 'back2BackAndToExternal' is " + "selected, VRF Lite IFCs are auto created between border devices of two Easy Fabrics, " + "and between border devices in Easy Fabric and edge routers in External Fabric. " + "The IP address is taken from the 'VRF Lite Subnet IP Range' pool." + ), + default=VrfLiteAutoConfigEnum.MANUAL + ) + vrf_lite_subnet_range: str = Field( + alias="vrfLiteSubnetRange", + description="Address range to assign P2P Interfabric Connections", + default="10.33.0.0/16" + ) + vrf_lite_subnet_target_mask: int = Field( + alias="vrfLiteSubnetTargetMask", + description="VRF Lite Subnet Mask", + default=30 + ) auto_unique_vrf_lite_ip_prefix: bool = Field( alias="autoUniqueVrfLiteIpPrefix", - description="Auto unique VRF lite IP prefix", + description=( + "When enabled, IP prefix allocated to the VRF LITE IFC is not reused on VRF extension " + "over VRF LITE IFC. Instead, unique IP Subnet is allocated for each VRF extension " + "over VRF LITE IFC." + ), default=False ) # Leaf / TOR - leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Enable leaf/TOR ID range", default=False) + leaf_tor_id_range: bool = Field( + alias="leafTorIdRange", + description="Use specific vPC/Port-channel ID range for leaf-tor pairings", + default=False + ) leaf_tor_vpc_port_channel_id_range: str = Field( alias="leafTorVpcPortChannelIdRange", - description="Leaf/TOR vPC port-channel ID range", + description=( + "Specify vPC/Port-channel ID range (minimum: 1, maximum: 4096), this range is used " + "for auto-allocating vPC/Port-Channel IDs for leaf-tor pairings" + ), default="1-499" ) - allow_vlan_on_leaf_tor_pairing: str = Field( + allow_vlan_on_leaf_tor_pairing: AllowVlanOnLeafTorPairingEnum = Field( alias="allowVlanOnLeafTorPairing", - description="Set trunk allowed VLAN on leaf-TOR pairing port-channels", - default="none" + description="Set trunk allowed vlan to 'none' or 'all' for leaf-tor pairing port-channels", + default=AllowVlanOnLeafTorPairingEnum.NONE ) # DNS / NTP / Syslog Collections - ntp_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerCollection") - ntp_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="ntpServerVrfCollection") - dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection") - dns_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="dnsVrfCollection") - syslog_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerCollection") - syslog_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerVrfCollection") - syslog_severity_collection: List[int] = Field(default_factory=lambda: [7], alias="syslogSeverityCollection") + ntp_server_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="ntpServerCollection", + description="List of NTP server IPv4/IPv6 addresses and/or hostnames" + ) + ntp_server_vrf_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="ntpServerVrfCollection", + description=( + "NTP Server VRFs. One VRF for all NTP servers or a list of VRFs, one per NTP server" + ) + ) + dns_collection: List[str] = Field( + default_factory=lambda: ["5.192.28.174"], + alias="dnsCollection", + description="List of IPv4 and IPv6 DNS addresses" + ) + dns_vrf_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="dnsVrfCollection", + description=( + "DNS Server VRFs. One VRF for all DNS servers or a list of VRFs, one per DNS server" + ) + ) + syslog_server_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="syslogServerCollection", + description="List of Syslog server IPv4/IPv6 addresses and/or hostnames" + ) + syslog_server_vrf_collection: List[str] = Field( + default_factory=lambda: ["string"], + alias="syslogServerVrfCollection", + description=( + "Syslog Server VRFs. One VRF for all Syslog servers or a list of VRFs, " + "one per Syslog server" + ) + ) + syslog_severity_collection: List[int] = Field( + default_factory=lambda: [7], + alias="syslogSeverityCollection", + description="List of Syslog severity values, one per Syslog server" + ) # Extra Config / Pre-Interface Config / AAA / Banner - banner: str = Field(description="Fabric banner text", default="") - extra_config_leaf: str = Field(alias="extraConfigLeaf", description="Extra leaf config", default="") - extra_config_spine: str = Field(alias="extraConfigSpine", description="Extra spine config", default="") - extra_config_tor: str = Field(alias="extraConfigTor", description="Extra TOR config", default="") + banner: str = Field( + description=( + "Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) " + "followed by message ending with delimiter" + ), + default="" + ) + extra_config_leaf: str = Field( + alias="extraConfigLeaf", + description=( + "Additional CLIs as captured from the show running configuration, added after interface " + "configurations for all switches with a VTEP unless they have some spine role" + ), + default="" + ) + extra_config_spine: str = Field( + alias="extraConfigSpine", + description=( + "Additional CLIs as captured from the show running configuration, added after interface " + "configurations for all switches with some spine role" + ), + default="" + ) + extra_config_tor: str = Field( + alias="extraConfigTor", + description=( + "Additional CLIs as captured from the show running configuration, added after interface " + "configurations for all ToRs" + ), + default="" + ) extra_config_intra_fabric_links: str = Field( alias="extraConfigIntraFabricLinks", - description="Extra intra-fabric links config", + description="Additional CLIs for all Intra-Fabric links", + default="" + ) + extra_config_aaa: str = Field( + alias="extraConfigAaa", + description="AAA Configurations", + default="" + ) + extra_config_nxos_bootstrap: str = Field( + alias="extraConfigNxosBootstrap", + description="Additional CLIs required during device bootup/login e.g. AAA/Radius", + default="" + ) + aaa: bool = Field( + description="Include AAA configs from Manageability tab during device bootup", + default=False + ) + pre_interface_config_leaf: str = Field( + alias="preInterfaceConfigLeaf", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all switches with a VTEP unless they have some spine role" + ), + default="" + ) + pre_interface_config_spine: str = Field( + alias="preInterfaceConfigSpine", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all switches with some spine role" + ), + default="" + ) + pre_interface_config_tor: str = Field( + alias="preInterfaceConfigTor", + description=( + "Additional CLIs as captured from the show running configuration, added before interface " + "configurations for all ToRs" + ), default="" ) - extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") - extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") - aaa: bool = Field(description="Enable AAA", default=False) - pre_interface_config_leaf: str = Field(alias="preInterfaceConfigLeaf", description="Pre-interface leaf config", default="") - pre_interface_config_spine: str = Field(alias="preInterfaceConfigSpine", description="Pre-interface spine config", default="") - pre_interface_config_tor: str = Field(alias="preInterfaceConfigTor", description="Pre-interface TOR config", default="") # System / Compliance / OAM / Misc greenfield_debug_flag: GreenfieldDebugFlagEnum = Field( alias="greenfieldDebugFlag", - description="Greenfield debug flag", + description=( + "Allow switch configuration to be cleared without a reload when " + "preserveConfig is set to false" + ), default=GreenfieldDebugFlagEnum.DISABLE ) interface_statistics_load_interval: int = Field( alias="interfaceStatisticsLoadInterval", - description="Interface statistics load interval in seconds", + description="Interface Statistics Load Interval. Time in seconds", default=10 ) - nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE source interface hold-down timer in seconds", default=180) - next_generation_oam: bool = Field(alias="nextGenerationOAM", description="Enable next-generation OAM", default=True) + nve_hold_down_timer: int = Field( + alias="nveHoldDownTimer", + description="NVE Source Inteface HoldDown Time in seconds", + default=180 + ) + next_generation_oam: bool = Field( + alias="nextGenerationOAM", + description=( + "Enable the Next Generation (NG) OAM feature for all switches in the fabric " + "to aid in trouble-shooting VXLAN EVPN fabrics" + ), + default=True + ) ngoam_south_bound_loop_detect: bool = Field( alias="ngoamSouthBoundLoopDetect", - description="Enable NGOAM south bound loop detection", + description="Enable the Next Generation (NG) OAM southbound loop detection", default=False ) ngoam_south_bound_loop_detect_probe_interval: int = Field( alias="ngoamSouthBoundLoopDetectProbeInterval", - description="NGOAM south bound loop detect probe interval in seconds", + description=( + "Set Next Generation (NG) OAM southbound loop detection probe interval in seconds." + ), default=300 ) ngoam_south_bound_loop_detect_recovery_interval: int = Field( alias="ngoamSouthBoundLoopDetectRecoveryInterval", - description="NGOAM south bound loop detect recovery interval in seconds", + description=( + "Set the Next Generation (NG) OAM southbound loop detection recovery interval in seconds" + ), default=600 ) strict_config_compliance_mode: bool = Field( alias="strictConfigComplianceMode", - description="Enable strict config compliance mode", + description=( + "Enable bi-directional compliance checks to flag additional configs in the running config " + "that are not in the intent/expected config" + ), + default=False + ) + advanced_ssh_option: bool = Field( + alias="advancedSshOption", + description=( + "Enable AAA IP Authorization. Enable only, when IP Authorization is enabled " + "in the AAA Server" + ), default=False ) - advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) - copp_policy: CoppPolicyEnum = Field(alias="coppPolicy", description="CoPP policy", default=CoppPolicyEnum.STRICT) - power_redundancy_mode: str = Field(alias="powerRedundancyMode", description="Power redundancy mode", default="redundant") - heartbeat_interval: int = Field(alias="heartbeatInterval", description="XConnect heartbeat interval", default=190) - snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) - cdp: bool = Field(description="Enable CDP", default=False) + copp_policy: CoppPolicyEnum = Field( + alias="coppPolicy", + description=( + "Fabric wide CoPP policy. Customized CoPP policy should be provided " + "when 'manual' is selected." + ), + default=CoppPolicyEnum.STRICT + ) + power_redundancy_mode: PowerRedundancyModeEnum = Field( + alias="powerRedundancyMode", + description="Default Power Supply Mode for NX-OS Switches", + default=PowerRedundancyModeEnum.REDUNDANT + ) + heartbeat_interval: int = Field( + alias="heartbeatInterval", + description="XConnect heartbeat interval for periodic link status checks", + default=190 + ) + snmp_trap: bool = Field( + alias="snmpTrap", + description="Configure ND as a receiver for SNMP traps", + default=True + ) + cdp: bool = Field(description="Enable CDP on management interface", default=False) real_time_interface_statistics_collection: bool = Field( alias="realTimeInterfaceStatisticsCollection", - description="Enable real-time interface statistics collection", + description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", default=False ) - tcam_allocation: bool = Field(alias="tcamAllocation", description="Enable TCAM allocation", default=True) + tcam_allocation: bool = Field( + alias="tcamAllocation", + description=( + "TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled" + ), + default=True + ) allow_smart_switch_onboarding: bool = Field( alias="allowSmartSwitchOnboarding", - description="Allow smart switch onboarding", + description="Enable onboarding of smart switches to Hypershield for firewall service", default=False ) # Queuing / QoS - default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable default queuing policy", default=False) + default_queuing_policy: bool = Field( + alias="defaultQueuingPolicy", + description="Enable Default Queuing Policies", + default=False + ) default_queuing_policy_cloudscale: str = Field( alias="defaultQueuingPolicyCloudscale", - description="Default queuing policy for cloudscale switches", + description=( + "Queuing Policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric" + ), default="queuing_policy_default_8q_cloudscale" ) default_queuing_policy_r_series: str = Field( alias="defaultQueuingPolicyRSeries", - description="Default queuing policy for R-Series switches", + description="Queueing policy for all Nexus R-series switches", default="queuing_policy_default_r_series" ) default_queuing_policy_other: str = Field( alias="defaultQueuingPolicyOther", - description="Default queuing policy for other switches", + description="Queuing Policy for all other switches in the fabric", default="queuing_policy_default_other" ) - aiml_qos: bool = Field(alias="aimlQos", description="Enable AI/ML QoS", default=False) - aiml_qos_policy: str = Field(alias="aimlQosPolicy", description="AI/ML QoS policy", default="400G") - roce_v2: str = Field(alias="roceV2", description="RoCEv2 DSCP value", default="26") - cnp: str = Field(description="CNP DSCP value", default="48") - wred_min: int = Field(alias="wredMin", description="WRED minimum threshold in kbytes", default=950) - wred_max: int = Field(alias="wredMax", description="WRED maximum threshold in kbytes", default=3000) - wred_drop_probability: int = Field(alias="wredDropProbability", description="WRED drop probability %", default=7) - wred_weight: int = Field(alias="wredWeight", description="WRED weight", default=0) - bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Bandwidth remaining % for AI traffic queues", default=50) - dlb: bool = Field(description="Enable dynamic load balancing", default=False) - dlb_mode: str = Field(alias="dlbMode", description="DLB mode", default="flowlet") - dlb_mixed_mode_default: str = Field(alias="dlbMixedModeDefault", description="DLB mixed mode default", default="ecmp") - flowlet_aging: Optional[int] = Field(alias="flowletAging", description="Flowlet aging timer in microseconds", default=None) - flowlet_dscp: str = Field(alias="flowletDscp", description="Flowlet DSCP value", default="") - per_packet_dscp: str = Field(alias="perPacketDscp", description="Per-packet DSCP value", default="") - ai_load_sharing: bool = Field(alias="aiLoadSharing", description="Enable AI load sharing", default=False) + aiml_qos: bool = Field( + alias="aimlQos", + description=( + "Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) " + "switch fabric for AI network workloads" + ), + default=False + ) + aiml_qos_policy: AimlQosPolicyEnum = Field( + alias="aimlQosPolicy", + description=( + "Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. " + "User-defined allows for custom configuration." + ), + default=AimlQosPolicyEnum.V_400G + ) + roce_v2: str = Field( + alias="roceV2", + description=( + "DSCP for RDMA traffic: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," + "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="26" + ) + cnp: str = Field( + description=( + "DSCP value for Congestion Notification: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," + "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="48" + ) + wred_min: int = Field(alias="wredMin", description="WRED minimum threshold (in kbytes)", default=950) + wred_max: int = Field(alias="wredMax", description="WRED maximum threshold (in kbytes)", default=3000) + wred_drop_probability: int = Field(alias="wredDropProbability", description="Drop probability %", default=7) + wred_weight: int = Field( + alias="wredWeight", + description="Influences how quickly WRED reacts to queue depth changes", + default=0 + ) + bandwidth_remaining: int = Field( + alias="bandwidthRemaining", + description="Percentage of remaining bandwidth allocated to AI traffic queues", + default=50 + ) + dlb: bool = Field( + description=( + "Enables fabric-level Dynamic Load Balancing (DLB) configuration. " + "Note: Inter-Switch-Links (ISL) will be configured as DLB Interfaces" + ), + default=False + ) + dlb_mode: DlbModeEnum = Field( + alias="dlbMode", + description=( + "Select system-wide flowlet, per-packet (packet spraying) or policy driven mixed mode. " + "Note: Mixed mode is supported on Silicon One (S1) platform only." + ), + default=DlbModeEnum.FLOWLET + ) + dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field( + alias="dlbMixedModeDefault", + description="Default load balancing mode for policy driven mixed mode DLB", + default=DlbMixedModeDefaultEnum.ECMP + ) + flowlet_aging: Optional[int] = Field( + alias="flowletAging", + description=( + "Flowlet aging timer in microseconds. Valid range depends on platform: " + "Cloud Scale (CS)=1-2000000 (default 500), Silicon One (S1)=1-1024 (default 256)" + ), + default=None + ) + flowlet_dscp: str = Field( + alias="flowletDscp", + description=( + "DSCP values for flowlet load balancing: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," + "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="" + ) + per_packet_dscp: str = Field( + alias="perPacketDscp", + description=( + "DSCP values for per-packet load balancing: numeric (0-63) with ranges/comma, named values " + "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," + "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" + ), + default="" + ) + ai_load_sharing: bool = Field( + alias="aiLoadSharing", + description=( + "Enable IP load sharing using source and destination address for AI workloads" + ), + default=False + ) priority_flow_control_watch_interval: Optional[int] = Field( alias="priorityFlowControlWatchInterval", - description="Priority flow control watch interval in milliseconds", + description=( + "Acceptable values from 101 to 1000 (milliseconds). " + "Leave blank for system default (100ms)." + ), default=None ) # PTP - ptp: bool = Field(description="Enable PTP", default=False) - ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) - ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) + ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) + ptp_loopback_id: int = Field( + alias="ptpLoopbackId", + description="Precision Time Protocol Source Loopback Id", + default=0 + ) + ptp_domain_id: int = Field( + alias="ptpDomainId", + description="Multiple Independent PTP Clocking Subdomains on a Single Network", + default=0 + ) # Private VLAN - private_vlan: bool = Field(alias="privateVlan", description="Enable private VLAN", default=False) + private_vlan: bool = Field( + alias="privateVlan", + description="Enable PVLAN on switches except spines and super spines", + default=False + ) default_private_vlan_secondary_network_template: str = Field( alias="defaultPrivateVlanSecondaryNetworkTemplate", - description="Default private VLAN secondary network template", + description="Default PVLAN secondary network template", default="Pvlan_Secondary_Network" ) # MACsec - macsec: bool = Field(description="Enable MACsec", default=False) - macsec_cipher_suite: str = Field( + macsec: bool = Field( + description=( + "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring " + "MACsec on a fabric link if MACsec is enabled on the link." + ), + default=False + ) + macsec_cipher_suite: MacsecCipherSuiteEnum = Field( alias="macsecCipherSuite", - description="MACsec cipher suite", - default="GCM-AES-XPN-256" + description="Configure Cipher Suite", + default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 ) - macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec primary key string", default="") - macsec_algorithm: str = Field(alias="macsecAlgorithm", description="MACsec primary cryptographic algorithm", default="AES_128_CMAC") - macsec_fallback_key_string: str = Field(alias="macsecFallbackKeyString", description="MACsec fallback key string", default="") - macsec_fallback_algorithm: str = Field( + macsec_key_string: str = Field( + alias="macsecKeyString", + description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", + default="" + ) + macsec_algorithm: MacsecAlgorithmEnum = Field( + alias="macsecAlgorithm", + description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", + default=MacsecAlgorithmEnum.AES_128_CMAC + ) + macsec_fallback_key_string: str = Field( + alias="macsecFallbackKeyString", + description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", + default="" + ) + macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( alias="macsecFallbackAlgorithm", - description="MACsec fallback cryptographic algorithm", - default="AES_128_CMAC" + description="MACsec Fallback Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", + default=MacsecAlgorithmEnum.AES_128_CMAC + ) + macsec_report_timer: int = Field( + alias="macsecReportTimer", + description="MACsec Operational Status periodic report timer in minutes", + default=5 ) - macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec report timer in minutes", default=5) # Hypershield / Connectivity + enable_dpu_pinning: bool = Field( + alias="enableDpuPinning", + description="Enable pinning of VRFs and networks to specific DPUs on smart switches", + default=False + ) connectivity_domain_name: Optional[str] = Field( alias="connectivityDomainName", description="Domain name to connect to Hypershield", From a0f5b7aa08fd94602faf58e642fc1d96081ee268 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 13:04:52 -0400 Subject: [PATCH 05/27] Update ebgp module doc headers --- plugins/modules/nd_manage_fabric_ebgp.py | 1151 ++++++++++++++++------ 1 file changed, 828 insertions(+), 323 deletions(-) diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index 04a4ab72..1be23ff7 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -60,7 +60,7 @@ - The license tier for the fabric. type: str default: premier - choices: [ essentials, premier ] + choices: [ essentials, advantage, premier ] alert_suspend: description: - The alert suspension state for the fabric. @@ -127,14 +127,14 @@ bgp_as_mode: description: - The BGP AS mode for the fabric. - - C(multiAS) assigns a unique AS number to each leaf tier. - - C(sameTierAS) assigns the same AS number within a tier. + - C(multiAS) assigns a unique AS number per leaf/border/border gateway (borders and border gateways may share ASN). + - C(sameTierAS) assigns the same AS number within a tier (leafs share one ASN, borders/border gateways share one ASN). type: str default: multiAS choices: [ multiAS, sameTierAS ] bgp_allow_as_in_num: description: - - The number of times BGP allows an AS-path containing the local AS number. + - The number of occurrences of the local AS number allowed in the BGP AS-path. type: int default: 1 bgp_max_path: @@ -149,208 +149,268 @@ default: false auto_configure_ebgp_evpn_peering: description: - - Automatically configure eBGP EVPN peering between spine and leaf switches. + - Automatically configure eBGP EVPN overlay peering between leaf and spine switches. type: bool default: true allow_leaf_same_as: description: - - Allow leaf switches to share the same BGP AS number. + - Allow leaf switches to have the same BGP ASN even when AS mode is Multi-AS. type: bool default: false assign_ipv4_to_loopback0: description: - - Assign an IPv4 address to the loopback0 interface. + - In an IPv6 routed fabric or VXLAN EVPN fabric with IPv6 underlay, assign IPv4 address + used for BGP Router ID to the routing loopback interface. type: bool default: true evpn: description: - - Enable the EVPN control plane. + - Enable BGP EVPN as the control plane and VXLAN as the data plane for this fabric. type: bool default: true route_map_tag: description: - - The route map tag used for redistribution. + - Tag for Route Map FABRIC-RMAP-REDIST-SUBNET. (Min 0, Max 4294967295). type: int default: 12345 disable_route_map_tag: description: - - Disable route map tag usage. + - Disable match tag for Route Map FABRIC-RMAP-REDIST-SUBNET. type: bool default: false leaf_bgp_as: description: - - The BGP AS number for leaf switches (used with C(sameTierAS) mode). + - The BGP AS number for leaf switches. + - Autonomous system number 1-4294967295 or dotted notation 1-65535.0-65535. type: str border_bgp_as: description: - The BGP AS number for border switches. + - Autonomous system number 1-4294967295 or dotted notation 1-65535.0-65535. type: str super_spine_bgp_as: description: - The BGP AS number for super-spine switches. + - Autonomous system number 1-4294967295 or dotted notation 1-65535.0-65535. type: str site_id: description: - - The site identifier for the fabric. + - The site identifier for EVPN Multi-Site support. - Defaults to the value of O(config.management.bgp_asn) if not provided. type: str default: "" - target_subnet_mask: + bgp_loopback_id: description: - - The target subnet mask for intra-fabric links (24-31). + - The underlay routing loopback interface ID (0-1023). type: int - default: 30 - anycast_gateway_mac: + default: 0 + bgp_loopback_ip_range: description: - - The anycast gateway MAC address in xxxx.xxxx.xxxx format. + - Typically Loopback0 IP address range. type: str - default: 2020.0000.00aa + default: "10.2.0.0/22" + bgp_loopback_ipv6_range: + description: + - Typically Loopback0 IPv6 address range. + type: str + default: "fd00::a02:0/119" + nve_loopback_id: + description: + - The underlay VTEP loopback ID associated with the NVE interface (0-1023). + type: int + default: 1 + nve_loopback_ip_range: + description: + - Typically Loopback1 IP address range. + type: str + default: "10.3.0.0/22" + nve_loopback_ipv6_range: + description: + - Typically Loopback1 and Anycast Loopback IPv6 address range. + type: str + default: "fd00::a03:0/118" + anycast_loopback_id: + description: + - Underlay anycast loopback ID. Used for vPC peering in VXLANv6 fabrics. + type: int + default: 10 + anycast_rendezvous_point_ip_range: + description: + - Anycast or Phantom RP IP address range. + type: str + default: "10.254.254.0/24" + ipv6_anycast_rendezvous_point_ip_range: + description: + - Anycast RP IPv6 address range. + type: str + default: "fd00::254:254:0/118" + intra_fabric_subnet_range: + description: + - Address range to assign numbered and peer link SVI IPs. + type: str + default: "10.4.0.0/16" + l2_vni_range: + description: + - Overlay network identifier range (minimum 1, maximum 16777214). + type: str + default: "30000-49000" + l3_vni_range: + description: + - Overlay VRF identifier range (minimum 1, maximum 16777214). + type: str + default: "50000-59000" + network_vlan_range: + description: + - Per switch overlay network VLAN range (minimum 2, maximum 4094). + type: str + default: "2300-2999" + vrf_vlan_range: + description: + - Per switch overlay VRF VLAN range (minimum 2, maximum 4094). + type: str + default: "2000-2299" + overlay_mode: + description: + - Overlay mode. VRF/Network configuration using config-profile or CLI. + type: str + default: cli + choices: [ cli, config-profile ] replication_mode: description: - - The multicast replication mode. + - Replication mode for BUM traffic. type: str default: multicast choices: [ multicast, ingress ] multicast_group_subnet: description: - - The multicast group subnet. + - Multicast pool prefix between 8 to 30. A multicast group IPv4 from this pool + is used for BUM traffic for each overlay network. type: str default: "239.1.1.0/25" auto_generate_multicast_group_address: description: - - Automatically generate multicast group addresses. + - Generate a new multicast group address from the multicast pool using a round-robin approach. type: bool default: false underlay_multicast_group_address_limit: description: - - The underlay multicast group address limit (1-255). + - The maximum supported value is 128 for NX-OS version 10.2(1) or earlier + and 512 for versions above 10.2(1). type: int default: 128 + choices: [ 128, 512 ] tenant_routed_multicast: description: - - Enable tenant routed multicast. + - Enable overlay IPv4 multicast support in VXLAN fabrics. type: bool default: false tenant_routed_multicast_ipv6: description: - - Enable tenant routed multicast for IPv6. + - Enable overlay IPv6 multicast support in VXLAN fabrics. type: bool default: false first_hop_redundancy_protocol: description: - - The first-hop redundancy protocol for tenant networks. + - First hop redundancy protocol, HSRP or VRRP. type: str default: hsrp choices: [ hsrp, vrrp ] rendezvous_point_count: description: - - The number of rendezvous points (1-4). + - Number of spines acting as Rendezvous-Points (RPs). type: int default: 2 + choices: [ 2, 4 ] rendezvous_point_loopback_id: description: - - The rendezvous point loopback interface ID (0-1023). + - The rendezvous point loopback interface ID. type: int default: 254 - overlay_mode: + rendezvous_point_mode: description: - - The overlay configuration mode. + - Multicast rendezvous point mode. For IPv6 underlay, use C(asm) only. type: str - default: cli - choices: [ cli, config-profile ] - bgp_loopback_id: + default: asm + choices: [ asm, bidir ] + phantom_rendezvous_point_loopback_id1: description: - - The BGP loopback interface ID (0-1023). + - Underlay phantom rendezvous point loopback primary ID for PIM Bi-dir deployments. type: int - default: 0 - nve_loopback_id: + default: 2 + phantom_rendezvous_point_loopback_id2: description: - - The NVE loopback interface ID (0-1023). + - Underlay phantom rendezvous point loopback secondary ID for PIM Bi-dir deployments. type: int - default: 1 - anycast_loopback_id: + default: 3 + phantom_rendezvous_point_loopback_id3: description: - - The anycast loopback interface ID. + - Underlay phantom rendezvous point loopback tertiary ID for PIM Bi-dir deployments. type: int - default: 10 - bgp_loopback_ip_range: - description: - - The BGP loopback IP address pool. - type: str - default: "10.2.0.0/22" - bgp_loopback_ipv6_range: - description: - - The BGP loopback IPv6 address pool. - type: str - default: "fd00::a02:0/119" - nve_loopback_ip_range: - description: - - The NVE loopback IP address pool. - type: str - default: "10.3.0.0/22" - nve_loopback_ipv6_range: + default: 4 + phantom_rendezvous_point_loopback_id4: description: - - The NVE loopback IPv6 address pool. - type: str - default: "fd00::a03:0/118" - anycast_rendezvous_point_ip_range: + - Underlay phantom rendezvous point loopback quaternary ID for PIM Bi-dir deployments. + type: int + default: 5 + l3vni_multicast_group: description: - - The anycast rendezvous point IP address pool. + - Default underlay multicast group IPv4 address assigned for every overlay VRF. type: str - default: "10.254.254.0/24" - ipv6_anycast_rendezvous_point_ip_range: + default: "239.1.1.0" + l3_vni_ipv6_multicast_group: description: - - The IPv6 anycast rendezvous point IP address pool. + - Default underlay multicast group IPv6 address assigned for every overlay VRF. type: str - default: "fd00::254:254:0/118" - intra_fabric_subnet_range: + default: "ff1e::" + ipv6_multicast_group_subnet: description: - - The intra-fabric subnet IP address pool. + - IPv6 multicast address with prefix 112 to 128. type: str - default: "10.4.0.0/16" - l2_vni_range: + default: "ff1e::/121" + mvpn_vrf_route_import_id: description: - - The Layer 2 VNI range. - type: str - default: "30000-49000" - l3_vni_range: + - Enable MVPN VRI ID generation for tenant routed multicast with IPv4 underlay. + type: bool + default: true + mvpn_vrf_route_import_id_range: description: - - The Layer 3 VNI range. + - MVPN VRI ID range (minimum 1, maximum 65535) for vPC, applicable when TRM is enabled + with IPv6 underlay, or O(config.management.mvpn_vrf_route_import_id) is enabled with IPv4 underlay. type: str - default: "50000-59000" - network_vlan_range: + vrf_route_import_id_reallocation: description: - - The network VLAN range. - type: str - default: "2300-2999" - vrf_vlan_range: + - One time VRI ID re-allocation based on MVPN VRI ID Range. + type: bool + default: false + target_subnet_mask: description: - - The VRF VLAN range. - type: str - default: "2000-2299" - sub_interface_dot1q_range: + - Mask for underlay subnet IP range (24-31). + type: int + default: 30 + anycast_gateway_mac: description: - - The sub-interface 802.1q range. + - Shared anycast gateway MAC address for all VTEPs in xxxx.xxxx.xxxx format. type: str - default: "2-511" - l3_vni_no_vlan_default_option: - description: - - Enable L3 VNI no-VLAN default option. - type: bool - default: false + default: 2020.0000.00aa fabric_mtu: description: - - The fabric MTU size (1500-9216). + - Intra fabric interface MTU. Must be an even number (1500-9216). type: int default: 9216 l2_host_interface_mtu: description: - - The L2 host interface MTU size (1500-9216). + - Layer 2 host interface MTU. Must be an even number (1500-9216). type: int default: 9216 + l3_vni_no_vlan_default_option: + description: + - L3 VNI configuration without VLAN configuration. This value is propagated on VRF + creation as the default value of Enable L3VNI w/o VLAN in VRF. + type: bool + default: false underlay_ipv6: description: - - Enable IPv6 underlay. + - Enable IPv6 underlay. If not enabled, IPv4 underlay is used. type: bool default: false static_underlay_ip_allocation: @@ -358,125 +418,164 @@ - Disable dynamic underlay IP address allocation. type: bool default: false + anycast_border_gateway_advertise_physical_ip: + description: + - Advertise Anycast Border Gateway PIP as VTEP. + Effective on MSD fabric Recalculate Config. + type: bool + default: false + sub_interface_dot1q_range: + description: + - Per aggregation dot1q range for VRF-Lite connectivity (minimum 2, maximum 4093). + type: str + default: "2-511" + vrf_lite_auto_config: + description: + - VRF Lite Inter-Fabric Connection Deployment Options. + - If C(back2BackAndToExternal) is selected, VRF Lite IFCs are auto created between + border devices of two Easy Fabrics, and between border devices in Easy Fabric and + edge routers in External Fabric. + type: str + default: manual + choices: [ manual, back2BackAndToExternal ] + vrf_lite_subnet_range: + description: + - Address range to assign P2P interfabric connections. + type: str + default: "10.33.0.0/16" + vrf_lite_subnet_target_mask: + description: + - VRF Lite subnet mask. + type: int + default: 30 + auto_unique_vrf_lite_ip_prefix: + description: + - When enabled, IP prefix allocated to the VRF LITE IFC is not reused on VRF extension + over VRF LITE IFC. Instead, a unique IP subnet is allocated for each VRF extension. + type: bool + default: false vpc_domain_id_range: description: - - The vPC domain ID range. + - vPC domain ID range (minimum 1, maximum 1000) to use for new pairings. type: str default: "1-1000" vpc_peer_link_vlan: description: - - The vPC peer link VLAN ID. + - VLAN range (minimum 2, maximum 4094) for vPC Peer Link SVI. type: str default: "3600" vpc_peer_link_enable_native_vlan: description: - - Enable native VLAN on the vPC peer link. + - Enable vPC peer link for native VLAN. type: bool default: false vpc_peer_keep_alive_option: description: - - The vPC peer keep-alive option. + - Use vPC peer keep alive with loopback or management. type: str default: management choices: [ loopback, management ] vpc_auto_recovery_timer: description: - - The vPC auto recovery timer in seconds (240-3600). + - vPC auto recovery timer in seconds (240-3600). type: int default: 360 vpc_delay_restore_timer: description: - - The vPC delay restore timer in seconds (1-3600). + - vPC delay restore timer in seconds (1-3600). type: int default: 150 vpc_peer_link_port_channel_id: description: - - The vPC peer link port-channel ID. + - vPC peer link port channel ID (minimum 1, maximum 4096). type: str default: "500" vpc_ipv6_neighbor_discovery_sync: description: - - Enable vPC IPv6 neighbor discovery synchronization. + - Enable IPv6 ND synchronization between vPC peers. type: bool default: true vpc_layer3_peer_router: description: - - Enable vPC layer-3 peer router. + - Enable layer-3 peer-router on all leaf switches. type: bool default: true vpc_tor_delay_restore_timer: description: - - The vPC TOR delay restore timer. + - vPC delay restore timer for ToR switches in seconds. type: int default: 30 fabric_vpc_domain_id: description: - - Enable fabric vPC domain ID. + - Enable the same vPC domain ID for all vPC pairs. Not recommended. type: bool default: false shared_vpc_domain_id: description: - - The shared vPC domain ID. + - vPC domain ID to be used on all vPC pairs. type: int default: 1 fabric_vpc_qos: description: - - Enable fabric vPC QoS. + - QoS on spines for guaranteed delivery of vPC Fabric Peering communication. type: bool default: false fabric_vpc_qos_policy_name: description: - - The fabric vPC QoS policy name. + - QoS policy name. Should be the same on all spines. type: str default: spine_qos_for_fabric_vpc_peering enable_peer_switch: description: - - Enable peer switch. + - Enable the vPC peer-switch feature on ToR switches. type: bool default: false per_vrf_loopback_auto_provision: description: - - Enable per-VRF loopback auto-provisioning. + - Auto provision an IPv4 loopback on a VTEP on VRF attachment. + - Enabling this option auto-provisions loopback on existing VRF attachments and also + when Edit, QuickAttach, or Multiattach actions are performed. type: bool default: false per_vrf_loopback_ip_range: description: - - The per-VRF loopback IP address pool. + - Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis. type: str default: "10.5.0.0/22" per_vrf_loopback_auto_provision_ipv6: description: - - Enable per-VRF loopback auto-provisioning for IPv6. + - Auto provision an IPv6 loopback on a VTEP on VRF attachment. type: bool default: false per_vrf_loopback_ipv6_range: description: - - The per-VRF loopback IPv6 address pool. + - Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis. type: str default: "fd00::a05:0/112" vrf_template: description: - - The VRF template name. + - Default overlay VRF template for leafs. type: str default: Default_VRF_Universal network_template: description: - - The network template name. + - Default overlay network template for leafs. type: str default: Default_Network_Universal vrf_extension_template: description: - - The VRF extension template name. + - Default overlay VRF template for borders. type: str default: Default_VRF_Extension_Universal network_extension_template: description: - - The network extension template name. + - Default overlay network template for borders. type: str default: Default_Network_Extension_Universal performance_monitoring: description: - - Enable performance monitoring. + - If enabled, switch metrics are collected through periodic SNMP polling. + Alternative to real-time telemetry. type: bool default: false tenant_dhcp: @@ -486,368 +585,774 @@ default: true advertise_physical_ip: description: - - Advertise physical IP address for NVE loopback. + - For primary VTEP IP advertisement as next-hop of prefix routes. type: bool default: false advertise_physical_ip_on_border: description: - - Advertise physical IP address on border switches. + - Enable advertise-pip on vPC borders and border gateways only. + Applicable only when vPC advertise-pip is not enabled. type: bool default: true - anycast_border_gateway_advertise_physical_ip: + bgp_authentication: description: - - Enable anycast border gateway to advertise physical IP. + - Enable BGP authentication. type: bool default: false - snmp_trap: + bgp_authentication_key_type: description: - - Enable SNMP traps. - type: bool - default: true - cdp: + - BGP key encryption type. 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7. + type: str + default: 3des + choices: [ 3des, type6, type7 ] + bgp_authentication_key: + description: + - Encrypted BGP authentication key based on type. + type: str + default: "" + bfd: description: - - Enable CDP. + - Enable BFD. Valid for IPv4 underlay only. type: bool default: false - tcam_allocation: + bfd_ibgp: description: - - Enable TCAM allocation. + - Enable BFD for iBGP. type: bool - default: true - real_time_interface_statistics_collection: + default: false + bfd_authentication: description: - - Enable real-time interface statistics collection. + - Enable BFD authentication. Valid for P2P interfaces only. type: bool default: false - interface_statistics_load_interval: + bfd_authentication_key_id: description: - - The interface statistics load interval in seconds. + - BFD authentication key ID. type: int - default: 10 - greenfield_debug_flag: + default: 100 + bfd_authentication_key: description: - - The greenfield debug flag. + - Encrypted SHA1 secret value. type: str - default: disable - choices: [ enable, disable ] - nxapi: + default: "" + pim_hello_authentication: description: - - Enable NX-API (HTTPS). + - Enable PIM hello authentication. Valid for IPv4 underlay only. type: bool default: false - nxapi_https_port: + pim_hello_authentication_key: description: - - The NX-API HTTPS port (1-65535). - type: int - default: 443 + - PIM hello authentication key. 3DES encrypted. + type: str + default: "" + nxapi: + description: + - Enable NX-API over HTTPS. + type: bool + default: false nxapi_http: description: - - Enable NX-API HTTP. + - Enable NX-API over HTTP. type: bool default: false + nxapi_https_port: + description: + - HTTPS port for NX-API (1-65535). + type: int + default: 443 nxapi_http_port: description: - - The NX-API HTTP port (1-65535). + - HTTP port for NX-API (1-65535). type: int default: 80 - bgp_authentication: + day0_bootstrap: description: - - Enable BGP authentication. + - Automatic IP assignment for POAP. type: bool default: false - bgp_authentication_key_type: + bootstrap_subnet_collection: + description: + - List of IPv4 or IPv6 subnets to be used for bootstrap. + type: list + elements: dict + suboptions: + start_ip: + description: + - Starting IP address of the bootstrap range. + type: str + required: true + end_ip: + description: + - Ending IP address of the bootstrap range. + type: str + required: true + default_gateway: + description: + - Default gateway for the bootstrap subnet. + type: str + required: true + subnet_prefix: + description: + - Subnet prefix length (8-30). + type: int + required: true + local_dhcp_server: description: - - The BGP authentication key type. + - Automatic IP assignment for POAP from local DHCP server. + type: bool + default: false + dhcp_protocol_version: + description: + - IP protocol version for local DHCP server. type: str - default: 3des - bgp_authentication_key: + default: dhcpv4 + choices: [ dhcpv4, dhcpv6 ] + dhcp_start_address: description: - - The BGP authentication key. + - DHCP scope start address for switch POAP. type: str default: "" - bfd: + dhcp_end_address: description: - - Enable BFD globally. - type: bool - default: false - bfd_ibgp: + - DHCP scope end address for switch POAP. + type: str + default: "" + management_gateway: description: - - Enable BFD for iBGP sessions. - type: bool - default: false - bfd_authentication: + - Default gateway for management VRF on the switch. + type: str + default: "" + management_ipv4_prefix: description: - - Enable BFD authentication. - type: bool - default: false - bfd_authentication_key_id: + - Switch management IP subnet prefix for IPv4. + type: int + default: 24 + management_ipv6_prefix: description: - - The BFD authentication key ID. + - Switch management IP subnet prefix for IPv6. type: int - default: 100 - bfd_authentication_key: + default: 64 + netflow_settings: + description: + - Netflow configuration settings. + type: dict + suboptions: + netflow: + description: + - Enable netflow collection. + type: bool + default: false + netflow_exporter_collection: + description: + - List of netflow exporters. + type: list + elements: dict + suboptions: + exporter_name: + description: + - Name of the netflow exporter. + type: str + required: true + exporter_ip: + description: + - IP address of the netflow collector. + type: str + required: true + vrf: + description: + - VRF name for the exporter. + type: str + default: management + source_interface_name: + description: + - Source interface name. + type: str + required: true + udp_port: + description: + - UDP port for netflow export (1-65535). + type: int + required: true + netflow_record_collection: + description: + - List of netflow records. + type: list + elements: dict + suboptions: + record_name: + description: + - Name of the netflow record. + type: str + required: true + record_template: + description: + - Template type for the record. + type: str + required: true + layer2_record: + description: + - Enable layer 2 record fields. + type: bool + default: false + netflow_monitor_collection: + description: + - List of netflow monitors. + type: list + elements: dict + suboptions: + monitor_name: + description: + - Name of the netflow monitor. + type: str + required: true + record_name: + description: + - Associated record name. + type: str + required: true + exporter1_name: + description: + - Primary exporter name. + type: str + required: true + exporter2_name: + description: + - Secondary exporter name. + type: str + default: "" + real_time_backup: + description: + - Backup hourly only if there is any config deployment since last backup. + type: bool + scheduled_backup: description: - - The BFD authentication key. + - Enable backup at the specified time daily. + type: bool + scheduled_backup_time: + description: + - Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59). type: str default: "" - pim_hello_authentication: + leaf_tor_id_range: description: - - Enable PIM hello authentication. + - Use specific vPC/Port-channel ID range for leaf-tor pairings. type: bool default: false - pim_hello_authentication_key: + leaf_tor_vpc_port_channel_id_range: + description: + - vPC/Port-channel ID range (minimum 1, maximum 4096), used for auto-allocating + vPC/Port-Channel IDs for leaf-tor pairings. + type: str + default: "1-499" + allow_vlan_on_leaf_tor_pairing: + description: + - Set trunk allowed VLAN to none or all for leaf-tor pairing port-channels. + type: str + default: none + choices: [ none, all ] + ntp_server_collection: + description: + - List of NTP server IPv4/IPv6 addresses and/or hostnames. + type: list + elements: str + ntp_server_vrf_collection: + description: + - NTP Server VRFs. One VRF for all NTP servers or a list of VRFs, one per NTP server. + type: list + elements: str + dns_collection: + description: + - List of IPv4 and IPv6 DNS addresses. + type: list + elements: str + dns_vrf_collection: + description: + - DNS Server VRFs. One VRF for all DNS servers or a list of VRFs, one per DNS server. + type: list + elements: str + syslog_server_collection: description: - - The PIM hello authentication key. + - List of syslog server IPv4/IPv6 addresses and/or hostnames. + type: list + elements: str + syslog_server_vrf_collection: + description: + - Syslog Server VRFs. One VRF for all syslog servers or a list of VRFs, one per syslog server. + type: list + elements: str + syslog_severity_collection: + description: + - List of syslog severity values, one per syslog server. + type: list + elements: int + banner: + description: + - Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) + followed by message ending with delimiter. type: str default: "" - macsec: + extra_config_leaf: description: - - Enable MACsec on intra-fabric links. - type: bool - default: false - macsec_cipher_suite: + - Additional CLIs added after interface configurations for all switches with a VTEP + unless they have some spine role. + type: str + default: "" + extra_config_spine: description: - - The MACsec cipher suite. + - Additional CLIs added after interface configurations for all switches with some spine role. type: str - default: GCM-AES-XPN-256 - macsec_key_string: + default: "" + extra_config_tor: description: - - The MACsec primary key string. + - Additional CLIs added after interface configurations for all ToRs. type: str default: "" - macsec_algorithm: + extra_config_intra_fabric_links: description: - - The MACsec algorithm. + - Additional CLIs for all intra-fabric links. type: str - default: AES_128_CMAC - macsec_fallback_key_string: + default: "" + extra_config_aaa: description: - - The MACsec fallback key string. + - AAA configurations. type: str default: "" - macsec_fallback_algorithm: + extra_config_nxos_bootstrap: description: - - The MACsec fallback algorithm. + - Additional CLIs required during device bootup/login e.g. AAA/Radius. type: str - default: AES_128_CMAC - macsec_report_timer: + default: "" + aaa: description: - - The MACsec report timer in minutes. - type: int - default: 5 - vrf_lite_auto_config: + - Include AAA configs from Manageability tab during device bootup. + type: bool + default: false + pre_interface_config_leaf: description: - - The VRF lite auto-configuration mode. + - Additional CLIs added before interface configurations for all switches with a VTEP + unless they have some spine role. type: str - default: manual - vrf_lite_subnet_range: + default: "" + pre_interface_config_spine: description: - - The VRF lite subnet IP address pool. + - Additional CLIs added before interface configurations for all switches with some spine role. type: str - default: "10.33.0.0/16" - vrf_lite_subnet_target_mask: + default: "" + pre_interface_config_tor: + description: + - Additional CLIs added before interface configurations for all ToRs. + type: str + default: "" + greenfield_debug_flag: + description: + - Allow switch configuration to be cleared without a reload when preserveConfig is set to false. + type: str + default: disable + choices: [ enable, disable ] + interface_statistics_load_interval: description: - - The VRF lite subnet target mask. + - Interface statistics load interval in seconds. type: int - default: 30 - auto_unique_vrf_lite_ip_prefix: + default: 10 + nve_hold_down_timer: + description: + - NVE source interface hold-down time in seconds. + type: int + default: 180 + next_generation_oam: description: - - Enable auto unique VRF lite IP prefix. + - Enable the Next Generation (NG) OAM feature for all switches in the fabric + to aid in troubleshooting VXLAN EVPN fabrics. type: bool - default: false - default_queuing_policy: + default: true + ngoam_south_bound_loop_detect: description: - - Enable default queuing policy. + - Enable the Next Generation (NG) OAM southbound loop detection. type: bool default: false - aiml_qos: + ngoam_south_bound_loop_detect_probe_interval: + description: + - Next Generation (NG) OAM southbound loop detection probe interval in seconds. + type: int + default: 300 + ngoam_south_bound_loop_detect_recovery_interval: description: - - Enable AI/ML QoS. + - Next Generation (NG) OAM southbound loop detection recovery interval in seconds. + type: int + default: 600 + strict_config_compliance_mode: + description: + - Enable bi-directional compliance checks to flag additional configs in the running + config that are not in the intent/expected config. type: bool default: false - aiml_qos_policy: + advanced_ssh_option: description: - - The AI/ML QoS policy. - type: str - default: 400G - dlb: - description: - - Enable dynamic load balancing. + - Enable AAA IP Authorization. Enable only when IP Authorization is enabled + in the AAA Server. type: bool default: false - dlb_mode: + copp_policy: description: - - The DLB mode. + - Fabric wide CoPP policy. Customized CoPP policy should be provided when C(manual) is selected. type: str - default: flowlet - ptp: + default: strict + choices: [ dense, lenient, moderate, strict, manual ] + power_redundancy_mode: description: - - Enable Precision Time Protocol (PTP). - type: bool - default: false - ptp_loopback_id: + - Default power supply mode for NX-OS switches. + type: str + default: redundant + choices: [ redundant, combined, inputSrcRedundant ] + heartbeat_interval: description: - - The PTP loopback ID. + - XConnect heartbeat interval for periodic link status checks. type: int - default: 0 - ptp_domain_id: + default: 190 + snmp_trap: description: - - The PTP domain ID. - type: int - default: 0 - private_vlan: + - Configure ND as a receiver for SNMP traps. + type: bool + default: true + cdp: description: - - Enable private VLAN support. + - Enable CDP on management interface. type: bool default: false - day0_bootstrap: + real_time_interface_statistics_collection: description: - - Enable day-0 bootstrap (POAP). + - Enable real time interface statistics collection. Valid for NX-OS only. type: bool default: false - local_dhcp_server: + tcam_allocation: description: - - Enable local DHCP server for bootstrap. + - TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when enabled. + type: bool + default: true + allow_smart_switch_onboarding: + description: + - Enable onboarding of smart switches to Hypershield for firewall service. type: bool default: false - dhcp_protocol_version: + default_queuing_policy: + description: + - Enable default queuing policies. + type: bool + default: false + default_queuing_policy_cloudscale: description: - - The DHCP protocol version for bootstrap. + - Queuing policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric. type: str - default: dhcpv4 - dhcp_start_address: + default: queuing_policy_default_8q_cloudscale + default_queuing_policy_r_series: description: - - The DHCP start address for bootstrap. + - Queueing policy for all Nexus R-series switches. type: str - default: "" - dhcp_end_address: + default: queuing_policy_default_r_series + default_queuing_policy_other: description: - - The DHCP end address for bootstrap. + - Queuing policy for all other switches in the fabric. type: str - default: "" - management_gateway: + default: queuing_policy_default_other + aiml_qos: + description: + - Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) and + Silicon One (S1) switch fabric for AI network workloads. + type: bool + default: false + aiml_qos_policy: description: - - The management gateway for bootstrap. + - Queuing policy based on predominant fabric link speed. + C(User-defined) allows for custom configuration. type: str - default: "" - management_ipv4_prefix: + default: 400G + choices: [ 800G, 400G, 100G, 25G, User-defined ] + roce_v2: description: - - The management IPv4 prefix length for bootstrap. + - DSCP for RDMA traffic. Numeric (0-63) with ranges/comma, or named values + (af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, + cs1, cs2, cs3, cs4, cs5, cs6, cs7, default, ef). + type: str + default: "26" + cnp: + description: + - DSCP value for Congestion Notification. Numeric (0-63) with ranges/comma, or named values + (af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, + cs1, cs2, cs3, cs4, cs5, cs6, cs7, default, ef). + type: str + default: "48" + wred_min: + description: + - WRED minimum threshold in kbytes. type: int - default: 24 - management_ipv6_prefix: + default: 950 + wred_max: description: - - The management IPv6 prefix length for bootstrap. + - WRED maximum threshold in kbytes. type: int - default: 64 - real_time_backup: + default: 3000 + wred_drop_probability: description: - - Enable real-time backup. - type: bool - scheduled_backup: + - WRED drop probability percentage. + type: int + default: 7 + wred_weight: + description: + - Influences how quickly WRED reacts to queue depth changes. + type: int + default: 0 + bandwidth_remaining: + description: + - Percentage of remaining bandwidth allocated to AI traffic queues. + type: int + default: 50 + dlb: description: - - Enable scheduled backup. + - Enables fabric-level Dynamic Load Balancing (DLB) configuration. + Inter-Switch-Links (ISL) will be configured as DLB interfaces. type: bool - scheduled_backup_time: + default: false + dlb_mode: description: - - The scheduled backup time. + - Select system-wide flowlet, per-packet (packet spraying) or policy driven mixed mode. + Mixed mode is supported on Silicon One (S1) platform only. type: str - default: "" - nve_hold_down_timer: + default: flowlet + choices: [ flowlet, per-packet, policy-driven-flowlet, policy-driven-per-packet, policy-driven-mixed-mode ] + dlb_mixed_mode_default: description: - - The NVE hold-down timer in seconds. + - Default load balancing mode for policy driven mixed mode DLB. + type: str + default: ecmp + choices: [ ecmp, flowlet, per-packet ] + flowlet_aging: + description: + - Flowlet aging timer in microseconds. Valid range depends on platform. + Cloud Scale (CS) 1-2000000 (default 500), Silicon One (S1) 1-1024 (default 256). type: int - default: 180 - next_generation_oam: + flowlet_dscp: description: - - Enable next-generation OAM. - type: bool - default: true - strict_config_compliance_mode: + - DSCP values for flowlet load balancing. Numeric (0-63) with ranges/comma, or named values + (af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, + cs1, cs2, cs3, cs4, cs5, cs6, cs7, default, ef). + type: str + default: "" + per_packet_dscp: + description: + - DSCP values for per-packet load balancing. Numeric (0-63) with ranges/comma, or named values + (af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, + cs1, cs2, cs3, cs4, cs5, cs6, cs7, default, ef). + type: str + default: "" + ai_load_sharing: description: - - Enable strict configuration compliance mode. + - Enable IP load sharing using source and destination address for AI workloads. type: bool default: false - copp_policy: + priority_flow_control_watch_interval: description: - - The CoPP policy. - type: str - default: strict - power_redundancy_mode: + - Acceptable values from 101 to 1000 (milliseconds). + Leave blank for system default (100ms). + type: int + ptp: description: - - The power redundancy mode. - type: str - default: redundant - heartbeat_interval: + - Enable Precision Time Protocol (PTP). + type: bool + default: false + ptp_loopback_id: description: - - The heartbeat interval. + - Precision Time Protocol source loopback ID. type: int - default: 190 - allow_smart_switch_onboarding: + default: 0 + ptp_domain_id: + description: + - Multiple independent PTP clocking subdomains on a single network. + type: int + default: 0 + private_vlan: description: - - Allow smart switch onboarding. + - Enable PVLAN on switches except spines and super spines. type: bool default: false - aaa: + default_private_vlan_secondary_network_template: + description: + - Default PVLAN secondary network template. + type: str + default: Pvlan_Secondary_Network + macsec: description: - - Enable AAA. + - Enable MACsec in the fabric. MACsec fabric parameters are used for configuring + MACsec on a fabric link if MACsec is enabled on the link. type: bool default: false - extra_config_leaf: + macsec_cipher_suite: description: - - Extra freeform configuration applied to leaf switches. + - Configure MACsec cipher suite. type: str - default: "" - extra_config_spine: + default: GCM-AES-XPN-256 + choices: [ GCM-AES-128, GCM-AES-256, GCM-AES-XPN-128, GCM-AES-XPN-256 ] + macsec_key_string: description: - - Extra freeform configuration applied to spine switches. + - MACsec primary key string. Cisco Type 7 encrypted octet string. type: str default: "" - extra_config_tor: + macsec_algorithm: description: - - Extra freeform configuration applied to TOR switches. + - MACsec primary cryptographic algorithm. AES_128_CMAC or AES_256_CMAC. type: str - default: "" - extra_config_intra_fabric_links: + default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] + macsec_fallback_key_string: description: - - Extra freeform configuration applied to intra-fabric links. + - MACsec fallback key string. Cisco Type 7 encrypted octet string. type: str default: "" - extra_config_aaa: + macsec_fallback_algorithm: description: - - Extra freeform AAA configuration. + - MACsec fallback cryptographic algorithm. AES_128_CMAC or AES_256_CMAC. type: str - default: "" - banner: + default: AES_128_CMAC + choices: [ AES_128_CMAC, AES_256_CMAC ] + macsec_report_timer: + description: + - MACsec operational status periodic report timer in minutes. + type: int + default: 5 + enable_dpu_pinning: description: - - The fabric banner text displayed on switch login. + - Enable pinning of VRFs and networks to specific DPUs on smart switches. + type: bool + default: false + connectivity_domain_name: + description: + - Domain name to connect to Hypershield. type: str - default: "" - ntp_server_collection: + hypershield_connectivity_proxy_server: description: - - The list of NTP server IP addresses. - type: list - elements: str - dns_collection: + - IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication. + type: str + hypershield_connectivity_proxy_server_port: description: - - The list of DNS server IP addresses. - type: list - elements: str - syslog_server_collection: + - Proxy port number for communication with Hypershield. + type: int + hypershield_connectivity_source_intf: description: - - The list of syslog server IP addresses. + - Loopback interface on smart switch for communication with Hypershield. + type: str + telemetry_settings: + description: + - Telemetry configuration settings. + type: dict + suboptions: + flow_collection: + description: + - Flow collection settings. + type: dict + suboptions: + traffic_analytics: + description: + - Traffic analytics state. + type: str + default: enabled + traffic_analytics_scope: + description: + - Traffic analytics scope. + type: str + default: intraFabric + operating_mode: + description: + - Operating mode. + type: str + default: flowTelemetry + udp_categorization: + description: + - UDP categorization. + type: str + default: enabled + microburst: + description: + - Microburst detection settings. + type: dict + suboptions: + microburst: + description: + - Enable microburst detection. + type: bool + default: false + sensitivity: + description: + - Microburst sensitivity level. + type: str + default: low + analysis_settings: + description: + - Telemetry analysis settings. + type: dict + suboptions: + is_enabled: + description: + - Enable telemetry analysis. + type: bool + default: false + nas: + description: + - NAS telemetry configuration. + type: dict + suboptions: + server: + description: + - NAS server address. + type: str + default: "" + export_settings: + description: + - NAS export settings. + type: dict + suboptions: + export_type: + description: + - Export type. + type: str + default: full + export_format: + description: + - Export format. + type: str + default: json + energy_management: + description: + - Energy management settings. + type: dict + suboptions: + cost: + description: + - Energy cost per unit. + type: float + default: 1.2 + external_streaming_settings: + description: + - External streaming settings. + type: dict + suboptions: + email: + description: + - Email streaming configuration. type: list - elements: str - syslog_server_vrf_collection: + elements: dict + message_bus: description: - - The list of VRFs for syslog servers. + - Message bus configuration. type: list - elements: str - syslog_severity_collection: + elements: dict + syslog: + description: + - Syslog streaming configuration. + type: dict + webhooks: description: - - The list of syslog severity levels (0-7). + - Webhook configuration. type: list - elements: int + elements: dict state: description: - The desired state of the fabric resources on the Cisco Nexus Dashboard. From 20ca050e3d6c04edbdcef05e5961a92c3326b62a Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 16:33:31 -0400 Subject: [PATCH 06/27] Update enums and pydantic model descriptions for external fabrics --- .../models/manage_fabric/enums.py | 22 ++ .../manage_fabric/manage_fabric_external.py | 337 ++++++++++++++---- 2 files changed, 290 insertions(+), 69 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/enums.py b/plugins/module_utils/models/manage_fabric/enums.py index 2386db54..af88742e 100644 --- a/plugins/module_utils/models/manage_fabric/enums.py +++ b/plugins/module_utils/models/manage_fabric/enums.py @@ -384,6 +384,28 @@ class UnderlayMulticastGroupAddressLimitEnum(int, Enum): V_512 = 512 +class TelemetryCollectionTypeEnum(str, Enum): + """ + # Summary + + Enumeration for telemetry collection method options. + """ + + IN_BAND = "inBand" + OUT_OF_BAND = "outOfBand" + + +class TelemetryStreamingProtocolEnum(str, Enum): + """ + # Summary + + Enumeration for telemetry streaming protocol options. + """ + + IPV4 = "ipv4" + IPV6 = "ipv6" + + class VrfLiteAutoConfigEnum(str, Enum): """ # Summary diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index 9210a8a7..0cfe666e 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -30,6 +30,8 @@ CoppPolicyEnum, DhcpProtocolVersionEnum, PowerRedundancyModeEnum, + TelemetryCollectionTypeEnum, + TelemetryStreamingProtocolEnum, ) @@ -481,169 +483,332 @@ class ExternalConnectivityManagementModel(NDNestedModel): ) # Core Configuration - bgp_asn: str = Field(alias="bgpAsn", description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]") + bgp_asn: str = Field( + alias="bgpAsn", + description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", + ) # Name under management section is optional for backward compatibility name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") # AAA - aaa: bool = Field(description="Enable AAA", default=False) + aaa: bool = Field( + description="Include AAA configs from Advanced tab during device bootup", + default=False, + ) # SSH - advanced_ssh_option: bool = Field(alias="advancedSshOption", description="Enable advanced SSH option", default=False) + advanced_ssh_option: bool = Field( + alias="advancedSshOption", + description="Enable only, when IP Authorization is enabled in the AAA Server", + default=False, + ) # Loopback allow_same_loopback_ip_on_switches: bool = Field( alias="allowSameLoopbackIpOnSwitches", - description="Allow same loopback IP on switches", - default=False + description=( + "Allow the same loopback IP address to be configured on multiple" + " switches (e.g. RP loopback IP)" + ), + default=False, ) # Smart Switch allow_smart_switch_onboarding: bool = Field( alias="allowSmartSwitchOnboarding", - description="Allow smart switch onboarding", - default=False + description=( + "Enable onboarding of smart switches to Hypershield" + " for firewall service" + ), + default=False, ) # Bootstrap Subnet Collection bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( alias="bootstrapSubnetCollection", - description="Bootstrap subnet collection", - default_factory=list + description="List of IPv4 or IPv6 subnets to be used for bootstrap", + default_factory=list, ) # CDP - cdp: bool = Field(description="Enable CDP", default=False) + cdp: bool = Field(description="Enable CDP on management interface", default=False) # CoPP Policy copp_policy: CoppPolicyEnum = Field( alias="coppPolicy", - description="CoPP policy", - default=CoppPolicyEnum.MANUAL + description=( + "Fabric wide CoPP policy. Customized CoPP policy should be" + " provided when 'manual' is selected." + ), + default=CoppPolicyEnum.MANUAL, ) # BGP Configuration create_bgp_config: bool = Field( alias="createBgpConfig", - description="Create BGP configuration", - default=True + description="Generate BGP configuration for core and edge routers", + default=True, ) # Bootstrap Settings - day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Enable day-0 bootstrap", default=False) - day0_plug_and_play: bool = Field(alias="day0PlugAndPlay", description="Enable day-0 plug and play", default=False) + day0_bootstrap: bool = Field( + alias="day0Bootstrap", + description="Support day 0 touchless switch bringup", + default=False, + ) + day0_plug_and_play: bool = Field( + alias="day0PlugAndPlay", + description="Enable Plug n Play for Catalyst 9000 switches", + default=False, + ) # DHCP - dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP end address", default="") + dhcp_end_address: str = Field( + alias="dhcpEndAddress", + description="DHCP Scope End Address For Switch POAP", + default="", + ) dhcp_protocol_version: DhcpProtocolVersionEnum = Field( alias="dhcpProtocolVersion", - description="DHCP protocol version", - default=DhcpProtocolVersionEnum.DHCPV4 + description="IP protocol version for Local DHCP Server", + default=DhcpProtocolVersionEnum.DHCPV4, + ) + dhcp_start_address: str = Field( + alias="dhcpStartAddress", + description="DHCP Scope Start Address For Switch POAP", + default="", ) - dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP start address", default="") # DNS - dns_collection: List[str] = Field(alias="dnsCollection", description="DNS server collection", default_factory=list) - dns_vrf_collection: List[str] = Field(alias="dnsVrfCollection", description="DNS VRF collection", default_factory=list) + dns_collection: List[str] = Field( + alias="dnsCollection", + description="List of IPv4 and IPv6 DNS addresses", + default_factory=list, + ) + dns_vrf_collection: List[str] = Field( + alias="dnsVrfCollection", + description=( + "DNS Server VRFs. One VRF for all DNS servers or a list of VRFs," + " one per DNS server" + ), + default_factory=list, + ) # Domain - domain_name: str = Field(alias="domainName", description="Domain name", default="") + domain_name: str = Field( + alias="domainName", + description="Domain name for DHCP server PnP block", + default="", + ) # DPU Pinning - enable_dpu_pinning: bool = Field(alias="enableDpuPinning", description="Enable DPU pinning", default=False) + enable_dpu_pinning: bool = Field( + alias="enableDpuPinning", + description=( + "Enable pinning of VRFs and networks to specific DPUs" + " on smart switches" + ), + default=False, + ) # Extra Config - extra_config_aaa: str = Field(alias="extraConfigAaa", description="Extra AAA config", default="") - extra_config_fabric: str = Field(alias="extraConfigFabric", description="Extra fabric config", default="") - extra_config_nxos_bootstrap: str = Field(alias="extraConfigNxosBootstrap", description="Extra NX-OS bootstrap config", default="") - extra_config_xe_bootstrap: str = Field(alias="extraConfigXeBootstrap", description="Extra XE bootstrap config", default="") + extra_config_aaa: str = Field( + alias="extraConfigAaa", + description="Additional CLIs for AAA Configuration", + default="", + ) + extra_config_fabric: str = Field( + alias="extraConfigFabric", + description="Additional CLIs for all switches", + default="", + ) + extra_config_nxos_bootstrap: str = Field( + alias="extraConfigNxosBootstrap", + description=( + "Additional CLIs required during device bootup/login" + " e.g. AAA/Radius (NX-OS)" + ), + default="", + ) + extra_config_xe_bootstrap: str = Field( + alias="extraConfigXeBootstrap", + description=( + "Additional CLIs required during device bootup/login" + " e.g. AAA/Radius (IOS-XE)" + ), + default="", + ) # Inband Management - inband_day0_bootstrap: bool = Field(alias="inbandDay0Bootstrap", description="Enable inband day-0 bootstrap", default=False) - inband_management: bool = Field(alias="inbandManagement", description="Enable in-band management", default=False) + inband_day0_bootstrap: bool = Field( + alias="inbandDay0Bootstrap", + description="Support day 0 touchless switch bringup via inband management", + default=False, + ) + inband_management: bool = Field( + alias="inbandManagement", + description=( + "Import switches with reachability over the switch" + " front-panel ports" + ), + default=False, + ) # Interface Statistics interface_statistics_load_interval: int = Field( alias="interfaceStatisticsLoadInterval", - description="Interface statistics load interval", - default=10 + description="Interface Statistics Load Interval Time in seconds", + default=10, ) # Local DHCP Server - local_dhcp_server: bool = Field(alias="localDhcpServer", description="Enable local DHCP server", default=False) + local_dhcp_server: bool = Field( + alias="localDhcpServer", + description="Automatic IP Assignment For POAP from Local DHCP Server", + default=False, + ) # Management - management_gateway: str = Field(alias="managementGateway", description="Management gateway", default="") - management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Management IPv4 prefix length", default=24) - management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Management IPv6 prefix length", default=64) + management_gateway: str = Field( + alias="managementGateway", + description="Default Gateway For Management VRF On The Switch", + default="", + ) + management_ipv4_prefix: int = Field( + alias="managementIpv4Prefix", + description="Switch Mgmt IP Subnet Prefix if ipv4", + default=24, + ) + management_ipv6_prefix: int = Field( + alias="managementIpv6Prefix", + description="Switch Management IP Subnet Prefix if ipv6", + default=64, + ) # Monitored Mode - monitored_mode: bool = Field(alias="monitoredMode", description="Enable monitored mode", default=False) + monitored_mode: bool = Field( + alias="monitoredMode", + description=( + "If enabled, fabric is only monitored." + " No configuration will be deployed" + ), + default=False, + ) # MPLS Handoff - mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS handoff", default=False) + mpls_handoff: bool = Field( + alias="mplsHandoff", + description="Enable MPLS Handoff", + default=False, + ) mpls_loopback_identifier: Optional[int] = Field( alias="mplsLoopbackIdentifier", - description="MPLS loopback identifier", - default=None + description="Underlay MPLS Loopback Identifier", + default=None, ) mpls_loopback_ip_range: str = Field( alias="mplsLoopbackIpRange", - description="MPLS loopback IP range", - default="10.102.0.0/25" + description="MPLS Loopback IP Address Range", + default="10.102.0.0/25", ) # Netflow Settings netflow_settings: NetflowSettingsModel = Field( alias="netflowSettings", - description="Netflow configuration", - default_factory=NetflowSettingsModel + description="Settings associated with netflow", + default_factory=NetflowSettingsModel, ) # NX-API Settings - nxapi: bool = Field(description="Enable NX-API", default=False) - nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API HTTP", default=False) - nxapi_http_port: int = Field(alias="nxapiHttpPort", description="NX-API HTTP port", ge=1, le=65535, default=80) - nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="NX-API HTTPS port", ge=1, le=65535, default=443) + nxapi: bool = Field(description="Enable NX-API over HTTPS", default=False) + nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API over HTTP", default=False) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="HTTP port for NX-API", ge=1, le=65535, default=80) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="HTTPS port for NX-API", ge=1, le=65535, default=443) # Performance Monitoring - performance_monitoring: bool = Field(alias="performanceMonitoring", description="Enable performance monitoring", default=False) + performance_monitoring: bool = Field( + alias="performanceMonitoring", + description=( + "If enabled, switch metrics are collected through periodic SNMP" + " polling. Alternative to real-time telemetry" + ), + default=False, + ) # Power Redundancy power_redundancy_mode: PowerRedundancyModeEnum = Field( alias="powerRedundancyMode", - description="Power redundancy mode", - default=PowerRedundancyModeEnum.REDUNDANT + description="Default Power Supply Mode for NX-OS Switches", + default=PowerRedundancyModeEnum.REDUNDANT, ) # PTP - ptp: bool = Field(description="Enable PTP", default=False) - ptp_domain_id: int = Field(alias="ptpDomainId", description="PTP domain ID", default=0) - ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="PTP loopback ID", default=0) + ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) + ptp_domain_id: int = Field( + alias="ptpDomainId", + description=( + "Multiple Independent PTP Clocking Subdomains" + " on a Single Network" + ), + default=0, + ) + ptp_loopback_id: int = Field( + alias="ptpLoopbackId", + description="Precision Time Protocol Source Loopback Id", + default=0, + ) # Backup / Restore - real_time_backup: Optional[bool] = Field(alias="realTimeBackup", description="Enable real-time backup", default=None) + real_time_backup: Optional[bool] = Field( + alias="realTimeBackup", + description=( + "Hourly Fabric Backup only if there is any config deployment" + " since last backup" + ), + default=None, + ) # Interface Statistics Collection real_time_interface_statistics_collection: bool = Field( alias="realTimeInterfaceStatisticsCollection", - description="Enable real-time interface statistics", - default=False + description=( + "Enable Real Time Interface Statistics Collection." + " Valid for NX-OS only" + ), + default=False, ) # Scheduled Backup - scheduled_backup: Optional[bool] = Field(alias="scheduledBackup", description="Enable scheduled backup", default=None) - scheduled_backup_time: str = Field(alias="scheduledBackupTime", description="Scheduled backup time", default="") + scheduled_backup: Optional[bool] = Field( + alias="scheduledBackup", + description="Enable backup at the specified time daily", + default=None, + ) + scheduled_backup_time: str = Field( + alias="scheduledBackupTime", + description=( + "Time (UTC) in 24 hour format to take a daily backup" + " if enabled (00:00 to 23:59)" + ), + default="", + ) # SNMP - snmp_trap: bool = Field(alias="snmpTrap", description="Enable SNMP traps", default=True) + snmp_trap: bool = Field( + alias="snmpTrap", + description="Configure Nexus Dashboard as a receiver for SNMP traps", + default=True, + ) # Sub-Interface sub_interface_dot1q_range: str = Field( alias="subInterfaceDot1qRange", - description="Sub-interface 802.1q range", - default="2-511" + description=( + "Per aggregation dot1q range for VRF-Lite connectivity" + " (minimum: 2, maximum: 4093)" + ), + default="2-511", ) # Hypershield / Connectivity @@ -725,14 +890,48 @@ class FabricExternalConnectivityModel(NDBaseModel): location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations - license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) - alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) + license_tier: LicenseTierEnum = Field( + alias="licenseTier", + description="License Tier value of a fabric.", + default=LicenseTierEnum.PREMIER, + ) + alert_suspend: AlertSuspendEnum = Field( + alias="alertSuspend", + description="Alert Suspend state configured on the fabric", + default=AlertSuspendEnum.DISABLED, + ) telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) - telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") - telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") - telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") - telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") - security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") + telemetry_collection_type: TelemetryCollectionTypeEnum = Field( + alias="telemetryCollectionType", + description="Telemetry collection method.", + default=TelemetryCollectionTypeEnum.OUT_OF_BAND, + ) + telemetry_streaming_protocol: TelemetryStreamingProtocolEnum = Field( + alias="telemetryStreamingProtocol", + description="Telemetry Streaming Protocol.", + default=TelemetryStreamingProtocolEnum.IPV4, + ) + telemetry_source_interface: str = Field( + alias="telemetrySourceInterface", + description=( + "Telemetry Source Interface (VLAN id or Loopback id) only valid" + " if Telemetry Collection is set to inBand" + ), + default="", + ) + telemetry_source_vrf: str = Field( + alias="telemetrySourceVrf", + description=( + "VRF over which telemetry is streamed, valid only if telemetry" + " collection is set to inband" + ), + default="", + ) + security_domain: str = Field( + alias="securityDomain", + description="Security Domain associated with the fabric", + default="all", + ) # Core Management Configuration management: Optional[ExternalConnectivityManagementModel] = Field( From c5a681ad8b573e18f00721ef4c7f71bd0dd1791e Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 16:55:39 -0400 Subject: [PATCH 07/27] Update ebgp module doc strings --- plugins/modules/nd_manage_fabric_external.py | 360 ++++++++++++++++--- 1 file changed, 305 insertions(+), 55 deletions(-) diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index a2ab33df..9bc3b2ef 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -57,13 +57,13 @@ required: true license_tier: description: - - The license tier for the fabric. + - License Tier value of a fabric. type: str default: premier - choices: [ essentials, premier ] + choices: [ essentials, advantage, premier ] alert_suspend: description: - - The alert suspension state for the fabric. + - Alert Suspend state configured on the fabric. type: str default: disabled choices: [ enabled, disabled ] @@ -74,27 +74,29 @@ default: false telemetry_collection_type: description: - - The telemetry collection type. + - Telemetry collection method. type: str default: outOfBand + choices: [ inBand, outOfBand ] telemetry_streaming_protocol: description: - - The telemetry streaming protocol. + - Telemetry Streaming Protocol. type: str default: ipv4 + choices: [ ipv4, ipv6 ] telemetry_source_interface: description: - - The telemetry source interface. + - Telemetry Source Interface (VLAN id or Loopback id) only valid if Telemetry Collection is set to inBand. type: str default: "" telemetry_source_vrf: description: - - The telemetry source VRF. + - VRF over which telemetry is streamed, valid only if telemetry collection is set to inband. type: str default: "" security_domain: description: - - The security domain associated with the fabric. + - Security Domain associated with the fabric. type: str default: all management: @@ -110,194 +112,311 @@ choices: [ externalConnectivity ] bgp_asn: description: - - The BGP Autonomous System Number for the fabric. - - Must be a numeric value between 1 and 4294967295 or dotted notation 1-65535.0-65535. + - Autonomous system number 1-4294967295 | 1-65535[.0-65535]. type: str required: true aaa: description: - - Enable AAA. + - Include AAA configs from Advanced tab during device bootup. type: bool default: false advanced_ssh_option: description: - - Enable advanced SSH option. + - Enable only, when IP Authorization is enabled in the AAA Server. type: bool default: false allow_same_loopback_ip_on_switches: description: - - Allow same loopback IP on switches. + - Allow the same loopback IP address to be configured on multiple switches (e.g. RP loopback IP). type: bool default: false allow_smart_switch_onboarding: description: - - Allow smart switch onboarding. + - Enable onboarding of smart switches to Hypershield for firewall service. type: bool default: false + bootstrap_subnet_collection: + description: + - List of IPv4 or IPv6 subnets to be used for bootstrap. + type: list + elements: dict + suboptions: + start_ip: + description: + - Starting IP address of the bootstrap range. + type: str + required: true + end_ip: + description: + - Ending IP address of the bootstrap range. + type: str + required: true + default_gateway: + description: + - Default gateway for bootstrap subnet. + type: str + required: true + subnet_prefix: + description: + - Subnet prefix length (8-30). + type: int + required: true cdp: description: - - Enable CDP. + - Enable CDP on management interface. type: bool default: false copp_policy: description: - - The CoPP policy. + - Fabric wide CoPP policy. + - Customized CoPP policy should be provided when C(manual) is selected. type: str default: manual choices: [ dense, lenient, moderate, strict, manual ] create_bgp_config: description: - - Create BGP configuration. + - Generate BGP configuration for core and edge routers. type: bool default: true day0_bootstrap: description: - - Enable day-0 bootstrap (POAP). + - Support day 0 touchless switch bringup. type: bool default: false day0_plug_and_play: description: - - Enable day-0 plug and play. + - Enable Plug n Play for Catalyst 9000 switches. type: bool default: false dhcp_end_address: description: - - The DHCP end address for bootstrap. + - DHCP Scope End Address For Switch POAP. type: str default: "" dhcp_protocol_version: description: - - The DHCP protocol version for bootstrap. + - IP protocol version for Local DHCP Server. type: str default: dhcpv4 choices: [ dhcpv4, dhcpv6 ] dhcp_start_address: description: - - The DHCP start address for bootstrap. + - DHCP Scope Start Address For Switch POAP. type: str default: "" dns_collection: description: - - The list of DNS server IP addresses. + - List of IPv4 and IPv6 DNS addresses. type: list elements: str dns_vrf_collection: description: - - The list of VRFs for DNS servers. + - DNS Server VRFs. + - One VRF for all DNS servers or a list of VRFs, one per DNS server. type: list elements: str domain_name: description: - - The domain name. + - Domain name for DHCP server PnP block. type: str default: "" enable_dpu_pinning: description: - - Enable DPU pinning. + - Enable pinning of VRFs and networks to specific DPUs on smart switches. type: bool default: false extra_config_aaa: description: - - Extra freeform AAA configuration. + - Additional CLIs for AAA Configuration. type: str default: "" extra_config_fabric: description: - - Extra freeform fabric configuration. + - Additional CLIs for all switches. type: str default: "" extra_config_nxos_bootstrap: description: - - Extra NX-OS bootstrap configuration. + - Additional CLIs required during device bootup/login e.g. AAA/Radius (NX-OS). type: str default: "" extra_config_xe_bootstrap: description: - - Extra XE bootstrap configuration. + - Additional CLIs required during device bootup/login e.g. AAA/Radius (IOS-XE). type: str default: "" inband_day0_bootstrap: description: - - Enable inband day-0 bootstrap. + - Support day 0 touchless switch bringup via inband management. type: bool default: false inband_management: description: - - Enable in-band management. + - Import switches with reachability over the switch front-panel ports. type: bool default: false interface_statistics_load_interval: description: - - The interface statistics load interval in seconds. + - Interface Statistics Load Interval Time in seconds. type: int default: 10 local_dhcp_server: description: - - Enable local DHCP server for bootstrap. + - Automatic IP Assignment For POAP from Local DHCP Server. type: bool default: false management_gateway: description: - - The management gateway for bootstrap. + - Default Gateway For Management VRF On The Switch. type: str default: "" management_ipv4_prefix: description: - - The management IPv4 prefix length for bootstrap. + - Switch Mgmt IP Subnet Prefix if ipv4. type: int default: 24 management_ipv6_prefix: description: - - The management IPv6 prefix length for bootstrap. + - Switch Management IP Subnet Prefix if ipv6. type: int default: 64 monitored_mode: description: - - Enable monitored mode. + - If enabled, fabric is only monitored. + - No configuration will be deployed. type: bool default: false mpls_handoff: description: - - Enable MPLS handoff. + - Enable MPLS Handoff. type: bool default: false mpls_loopback_identifier: description: - - The MPLS loopback identifier. + - Underlay MPLS Loopback Identifier. type: int mpls_loopback_ip_range: description: - - The MPLS loopback IP address pool. + - MPLS Loopback IP Address Range. type: str default: "10.102.0.0/25" + netflow_settings: + description: + - Settings associated with netflow. + type: dict + suboptions: + netflow: + description: + - Enable netflow collection. + type: bool + default: false + netflow_exporter_collection: + description: + - List of netflow exporters. + type: list + elements: dict + suboptions: + exporter_name: + description: + - Name of the netflow exporter. + type: str + required: true + exporter_ip: + description: + - IP address of the netflow collector. + type: str + required: true + vrf: + description: + - VRF name for the exporter. + type: str + default: management + source_interface_name: + description: + - Source interface name. + type: str + required: true + udp_port: + description: + - UDP port for netflow export (1-65535). + type: int + required: true + netflow_record_collection: + description: + - List of netflow records. + type: list + elements: dict + suboptions: + record_name: + description: + - Name of the netflow record. + type: str + required: true + record_template: + description: + - Template type for the record. + type: str + required: true + layer2_record: + description: + - Enable layer 2 record fields. + type: bool + default: false + netflow_monitor_collection: + description: + - List of netflow monitors. + type: list + elements: dict + suboptions: + monitor_name: + description: + - Name of the netflow monitor. + type: str + required: true + record_name: + description: + - Associated record name. + type: str + required: true + exporter1_name: + description: + - Primary exporter name. + type: str + required: true + exporter2_name: + description: + - Secondary exporter name. + type: str + default: "" nxapi: description: - - Enable NX-API (HTTPS). + - Enable NX-API over HTTPS. type: bool default: false nxapi_http: description: - - Enable NX-API HTTP. + - Enable NX-API over HTTP. type: bool default: false nxapi_http_port: description: - - The NX-API HTTP port (1-65535). + - HTTP port for NX-API (1-65535). type: int default: 80 nxapi_https_port: description: - - The NX-API HTTPS port (1-65535). + - HTTPS port for NX-API (1-65535). type: int default: 443 performance_monitoring: description: - - Enable performance monitoring. + - If enabled, switch metrics are collected through periodic SNMP polling. + - Alternative to real-time telemetry. type: bool default: false power_redundancy_mode: description: - - The power redundancy mode. + - Default Power Supply Mode for NX-OS Switches. type: str default: redundant choices: [ redundant, combined, inputSrcRedundant ] @@ -308,42 +427,173 @@ default: false ptp_domain_id: description: - - The PTP domain ID. + - Multiple Independent PTP Clocking Subdomains on a Single Network. type: int default: 0 ptp_loopback_id: description: - - The PTP loopback ID. + - Precision Time Protocol Source Loopback Id. type: int default: 0 real_time_backup: description: - - Enable real-time backup. + - Hourly Fabric Backup only if there is any config deployment since last backup. type: bool real_time_interface_statistics_collection: description: - - Enable real-time interface statistics collection. + - Enable Real Time Interface Statistics Collection. + - Valid for NX-OS only. type: bool default: false scheduled_backup: description: - - Enable scheduled backup. + - Enable backup at the specified time daily. type: bool scheduled_backup_time: description: - - The scheduled backup time. + - Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59). type: str default: "" snmp_trap: description: - - Enable SNMP traps. + - Configure Nexus Dashboard as a receiver for SNMP traps. type: bool default: true sub_interface_dot1q_range: description: - - The sub-interface 802.1q range. + - Per aggregation dot1q range for VRF-Lite connectivity (minimum 2, maximum 4093). type: str default: "2-511" + connectivity_domain_name: + description: + - Domain name to connect to Hypershield. + type: str + hypershield_connectivity_proxy_server: + description: + - IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication. + type: str + hypershield_connectivity_proxy_server_port: + description: + - Proxy port number for communication with Hypershield. + type: int + hypershield_connectivity_source_intf: + description: + - Loopback interface on smart switch for communication with Hypershield. + type: str + telemetry_settings: + description: + - Telemetry configuration for the fabric. + type: dict + suboptions: + flow_collection: + description: + - Flow collection settings. + type: dict + suboptions: + traffic_analytics: + description: + - Traffic analytics state. + type: str + default: enabled + traffic_analytics_scope: + description: + - Traffic analytics scope. + type: str + default: intraFabric + operating_mode: + description: + - Operating mode. + type: str + default: flowTelemetry + udp_categorization: + description: + - UDP categorization. + type: str + default: enabled + microburst: + description: + - Microburst detection settings. + type: dict + suboptions: + microburst: + description: + - Enable microburst detection. + type: bool + default: false + sensitivity: + description: + - Microburst sensitivity level. + type: str + default: low + analysis_settings: + description: + - Analysis settings. + type: dict + suboptions: + is_enabled: + description: + - Enable telemetry analysis. + type: bool + default: false + nas: + description: + - NAS telemetry configuration. + type: dict + suboptions: + server: + description: + - NAS server address. + type: str + default: "" + export_settings: + description: + - NAS export settings. + type: dict + suboptions: + export_type: + description: + - Export type. + type: str + default: full + export_format: + description: + - Export format. + type: str + default: json + energy_management: + description: + - Energy management settings. + type: dict + suboptions: + cost: + description: + - Energy cost per unit. + type: float + default: 1.2 + external_streaming_settings: + description: + - External streaming settings for the fabric. + type: dict + suboptions: + email: + description: + - Email streaming configuration. + type: list + elements: dict + message_bus: + description: + - Message bus configuration. + type: list + elements: dict + syslog: + description: + - Syslog streaming configuration. + type: dict + webhooks: + description: + - Webhook configuration. + type: list + elements: dict state: description: - The desired state of the fabric resources on the Cisco Nexus Dashboard. From cf5e04e224aee8560c8e27f8c5db0fde480f420b Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 17:30:28 -0400 Subject: [PATCH 08/27] Fix ansible sanity tests failures --- .../endpoints/v1/manage/manage_fabrics.py | 7 +++--- .../models/manage_fabric/enums.py | 1 + .../manage_fabric/manage_fabric_ebgp.py | 20 ++++++---------- .../manage_fabric/manage_fabric_external.py | 11 +++++---- .../manage_fabric/manage_fabric_ibgp.py | 23 +++++++++++-------- .../orchestrators/manage_fabric_ebgp.py | 2 +- .../orchestrators/manage_fabric_ibgp.py | 1 - plugins/modules/nd_manage_fabric_ebgp.py | 1 + plugins/modules/nd_manage_fabric_external.py | 1 + plugins/modules/nd_manage_fabric_ibgp.py | 1 + 10 files changed, 36 insertions(+), 32 deletions(-) diff --git a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py index 5cb08213..1b32a1b3 100644 --- a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py +++ b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py @@ -31,16 +31,16 @@ # pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=inFinal, valid-name +# pylint: enable=invalid-name -from typing import ClassVar, Literal, Optional, Final +from typing import ClassVar, Literal, Optional from ansible_collections.cisco.nd.plugins.module_utils.enums import HttpVerbEnum from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.base_path import BasePath from ansible_collections.cisco.nd.plugins.module_utils.endpoints.mixins import FabricNameMixin from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel from ansible_collections.cisco.nd.plugins.module_utils.endpoints.query_params import EndpointQueryParams -from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import BaseModel, ConfigDict, Field +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import Field from ansible_collections.cisco.nd.plugins.module_utils.types import IdentifierKey @@ -127,6 +127,7 @@ def path(self) -> str: return f"{base_path}?{query_string}" return base_path + class EpManageFabricsGet(_EpManageFabricsBase): """ # Summary diff --git a/plugins/module_utils/models/manage_fabric/enums.py b/plugins/module_utils/models/manage_fabric/enums.py index af88742e..d23a2656 100644 --- a/plugins/module_utils/models/manage_fabric/enums.py +++ b/plugins/module_utils/models/manage_fabric/enums.py @@ -22,6 +22,7 @@ from enum import Enum + class FabricTypeEnum(str, Enum): """ # Summary diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index e30fe28c..32f0e66d 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -16,7 +16,6 @@ from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( - BaseModel, ConfigDict, Field, field_validator, @@ -50,17 +49,8 @@ # Re-use shared nested models from the iBGP module from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import ( LocationModel, - NetflowExporterModel, - NetflowRecordModel, - NetflowMonitorModel, NetflowSettingsModel, BootstrapSubnetModel, - TelemetryFlowCollectionModel, - TelemetryMicroburstModel, - TelemetryAnalysisSettingsModel, - TelemetryEnergyManagementModel, - TelemetryNasExportSettingsModel, - TelemetryNasModel, TelemetrySettingsModel, ExternalStreamingSettingsModel, ) @@ -97,7 +87,12 @@ # Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and # dotted four-byte ASN notation (1-65535).(0-65535) _BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" + r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" + r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" + r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" + r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" + r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" ) @@ -1442,7 +1437,7 @@ def get_argument_spec(cls) -> Dict: state={ "type": "str", "default": "merged", - "choices": ["merged", "replaced", "deleted", "overridden", "query"], + "choices": ["merged", "replaced", "deleted", "overridden"], }, config={"required": False, "type": "list", "elements": "dict"}, ) @@ -1452,7 +1447,6 @@ def get_argument_spec(cls) -> Dict: __all__ = [ "VxlanEbgpManagementModel", "FabricEbgpModel", - "FabricEbgpDeleteModel", "FabricTypeEnum", "AlertSuspendEnum", "LicenseTierEnum", diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index 0cfe666e..f35946de 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -11,13 +11,11 @@ # pylint: enable=invalid-name import re -from enum import Enum from typing import List, Dict, Any, Optional, ClassVar, Literal from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( - BaseModel, ConfigDict, Field, field_validator, @@ -74,7 +72,12 @@ # Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and # dotted four-byte ASN notation (1-65535).(0-65535) _BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" + r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" + r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" + r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" + r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" + r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" ) @@ -1001,7 +1004,7 @@ def get_argument_spec(cls) -> Dict: state={ "type": "str", "default": "merged", - "choices": ["merged", "replaced", "deleted", "overridden", "query"], + "choices": ["merged", "replaced", "deleted", "overridden"], }, config={"required": False, "type": "list", "elements": "dict"}, ) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index 4275dbd9..20908b0c 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -12,13 +12,11 @@ import re # from datetime import datetime -from enum import Enum from typing import List, Dict, Any, Optional, ClassVar, Literal from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( - BaseModel, ConfigDict, Field, field_validator, @@ -96,7 +94,12 @@ # Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and # dotted four-byte ASN notation (1-65535).(0-65535) _BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" + r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" + r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" + r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" + r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" + r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" ) @@ -524,8 +527,9 @@ class VxlanIbgpManagementModel(NDNestedModel): # leaf_count: Optional[int] = Field(alias="leafCount", description="Number of leaf switches", ge=1, le=128, default=1) # spine_count: Optional[int] = Field(alias="spineCount", description="Number of spine switches", ge=1, le=32, default=1) # vrf_lite_ipv6_subnet_range: Optional[str] = Field(alias="vrfLiteIpv6SubnetRange", description="VRF Lite IPv6 subnet range", default="fd00::a33:0/112") - # vrf_lite_ipv6_subnet_target_mask: Optional[int] = Field(alias="vrfLiteIpv6SubnetTargetMask", description="VRF Lite IPv6 subnet target mask", ge=112, le=128, default=126) - + # vrf_lite_ipv6_subnet_target_mask: Optional[int] = Field( + # alias="vrfLiteIpv6SubnetTargetMask", + # description="VRF Lite IPv6 subnet target mask", ge=112, le=128, default=126) # Network Addressing bgp_loopback_ip_range: str = Field( @@ -2124,10 +2128,10 @@ def get_argument_spec(cls) -> Dict: state={ "type": "str", "default": "merged", - "choices": ["merged", "replaced", "deleted", "overridden", "query"], + "choices": ["merged", "replaced", "deleted", "overridden"], }, config={"required": False, "type": "list", "elements": "dict"}, - ) + ) # Export all models for external use @@ -2145,12 +2149,11 @@ def get_argument_spec(cls) -> Dict: "TelemetrySettingsModel", "ExternalStreamingSettingsModel", "VxlanIbgpManagementModel", - "FabricModel", - "FabricDeleteModel", + "FabricIbgpModel", "FabricTypeEnum", "AlertSuspendEnum", "LicenseTierEnum", "ReplicationModeEnum", "OverlayModeEnum", "LinkStateRoutingProtocolEnum" -] \ No newline at end of file +] diff --git a/plugins/module_utils/orchestrators/manage_fabric_ebgp.py b/plugins/module_utils/orchestrators/manage_fabric_ebgp.py index 45df1acd..2171189a 100644 --- a/plugins/module_utils/orchestrators/manage_fabric_ebgp.py +++ b/plugins/module_utils/orchestrators/manage_fabric_ebgp.py @@ -11,7 +11,6 @@ from typing import Type from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.base import NDBaseOrchestrator from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel -from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import FabricIbgpModel from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ebgp import FabricEbgpModel from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.types import ResponseType @@ -23,6 +22,7 @@ EpManageFabricsDelete, ) + class ManageEbgpFabricOrchestrator(NDBaseOrchestrator): model_class: Type[NDBaseModel] = FabricEbgpModel diff --git a/plugins/module_utils/orchestrators/manage_fabric_ibgp.py b/plugins/module_utils/orchestrators/manage_fabric_ibgp.py index e2082b57..9fb5da78 100644 --- a/plugins/module_utils/orchestrators/manage_fabric_ibgp.py +++ b/plugins/module_utils/orchestrators/manage_fabric_ibgp.py @@ -12,7 +12,6 @@ from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.base import NDBaseOrchestrator from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import FabricIbgpModel -from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ebgp import FabricEbgpModel from ansible_collections.cisco.nd.plugins.module_utils.endpoints.base import NDEndpointBaseModel from ansible_collections.cisco.nd.plugins.module_utils.orchestrators.types import ResponseType from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.manage_fabrics import ( diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index 1be23ff7..e3012be9 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -1680,5 +1680,6 @@ def main(): except Exception as e: module.fail_json(msg=f"Module execution failed: {str(e)}") + if __name__ == "__main__": main() diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index 9bc3b2ef..459c1e9d 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -770,5 +770,6 @@ def main(): except Exception as e: module.fail_json(msg=f"Module execution failed: {str(e)}") + if __name__ == "__main__": main() diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 8f834e4f..e743e47d 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -1852,5 +1852,6 @@ def main(): except Exception as e: module.fail_json(msg=f"Module execution failed: {str(e)}") + if __name__ == "__main__": main() From 9db1d09bc62893248b8ce991d63818534318721a Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 19:38:11 -0400 Subject: [PATCH 09/27] Black formatting --- .../endpoints/v1/manage/manage_fabrics.py | 52 +- .../manage_fabric/manage_fabric_ebgp.py | 967 +++---------- .../manage_fabric/manage_fabric_external.py | 308 +--- .../manage_fabric/manage_fabric_ibgp.py | 1285 ++++------------- 4 files changed, 588 insertions(+), 2024 deletions(-) diff --git a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py index 1b32a1b3..0642a6ea 100644 --- a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py +++ b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py @@ -89,9 +89,7 @@ class _EpManageFabricsBase(FabricNameMixin, NDEndpointBaseModel): _require_fabric_name: ClassVar[bool] = True _path_suffix: ClassVar[Optional[str]] = None - endpoint_params: EndpointQueryParams = Field( - default_factory=EndpointQueryParams, description="Endpoint-specific query parameters" - ) + endpoint_params: EndpointQueryParams = Field(default_factory=EndpointQueryParams, description="Endpoint-specific query parameters") def set_identifiers(self, identifier: IdentifierKey = None): self.fabric_name = identifier @@ -113,9 +111,7 @@ def path(self) -> str: - `ValueError` if `fabric_name` is required but not set """ if self._require_fabric_name and self.fabric_name is None: - raise ValueError( - f"{type(self).__name__}.path: fabric_name must be set before accessing path." - ) + raise ValueError(f"{type(self).__name__}.path: fabric_name must be set before accessing path.") segments = ["fabrics"] if self.fabric_name is not None: segments.append(self.fabric_name) @@ -173,13 +169,9 @@ class EpManageFabricsGet(_EpManageFabricsBase): ``` """ - class_name: Literal["EpApiV1ManageFabricsGet"] = Field( - default="EpApiV1ManageFabricsGet", description="Class name for backward compatibility" - ) + class_name: Literal["EpApiV1ManageFabricsGet"] = Field(default="EpApiV1ManageFabricsGet", description="Class name for backward compatibility") - endpoint_params: FabricsEndpointParams = Field( - default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsEndpointParams = Field(default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: @@ -289,13 +281,9 @@ class EpManageFabricsListGet(_EpManageFabricsBase): _require_fabric_name: ClassVar[bool] = False - class_name: Literal["EpApiV1ManageFabricsListGet"] = Field( - default="EpApiV1ManageFabricsListGet", description="Class name for backward compatibility" - ) + class_name: Literal["EpApiV1ManageFabricsListGet"] = Field(default="EpApiV1ManageFabricsListGet", description="Class name for backward compatibility") - endpoint_params: FabricsListEndpointParams = Field( - default_factory=FabricsListEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsListEndpointParams = Field(default_factory=FabricsListEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: @@ -355,13 +343,9 @@ class EpManageFabricsPost(_EpManageFabricsBase): _require_fabric_name: ClassVar[bool] = False - class_name: Literal["EpApiV1ManageFabricsPost"] = Field( - default="EpApiV1ManageFabricsPost", description="Class name for backward compatibility" - ) + class_name: Literal["EpApiV1ManageFabricsPost"] = Field(default="EpApiV1ManageFabricsPost", description="Class name for backward compatibility") - endpoint_params: FabricsEndpointParams = Field( - default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsEndpointParams = Field(default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: @@ -413,13 +397,9 @@ class EpManageFabricsPut(_EpManageFabricsBase): ``` """ - class_name: Literal["EpApiV1ManageFabricsPut"] = Field( - default="EpApiV1ManageFabricsPut", description="Class name for backward compatibility" - ) + class_name: Literal["EpApiV1ManageFabricsPut"] = Field(default="EpApiV1ManageFabricsPut", description="Class name for backward compatibility") - endpoint_params: FabricsEndpointParams = Field( - default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsEndpointParams = Field(default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: @@ -461,13 +441,9 @@ class EpManageFabricsDelete(_EpManageFabricsBase): ``` """ - class_name: Literal["EpApiV1ManageFabricsDelete"] = Field( - default="EpApiV1ManageFabricsDelete", description="Class name for backward compatibility" - ) + class_name: Literal["EpApiV1ManageFabricsDelete"] = Field(default="EpApiV1ManageFabricsDelete", description="Class name for backward compatibility") - endpoint_params: FabricsEndpointParams = Field( - default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsEndpointParams = Field(default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: @@ -516,9 +492,7 @@ class EpManageFabricsSummaryGet(_EpManageFabricsBase): _path_suffix: ClassVar[Optional[str]] = "summary" - endpoint_params: FabricsEndpointParams = Field( - default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters" - ) + endpoint_params: FabricsEndpointParams = Field(default_factory=FabricsEndpointParams, description="Endpoint-specific query parameters") @property def verb(self) -> HttpVerbEnum: diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index 32f0e66d..9370bc41 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -46,6 +46,7 @@ UnderlayMulticastGroupAddressLimitEnum, VrfLiteAutoConfigEnum, ) + # Re-use shared nested models from the iBGP module from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import ( LocationModel, @@ -55,7 +56,6 @@ ExternalStreamingSettingsModel, ) - """ # Comprehensive Pydantic models for eBGP VXLAN fabric management via Nexus Dashboard @@ -111,300 +111,155 @@ class VxlanEbgpManagementModel(NDNestedModel): - `TypeError` - If required string fields are not provided """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") # Fabric Type (required for discriminated union) type: Literal[FabricTypeEnum.VXLAN_EBGP] = Field(description="Type of the fabric", default=FabricTypeEnum.VXLAN_EBGP) # Core eBGP Configuration bgp_asn: Optional[str] = Field( - alias="bgpAsn", - description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]. Optional when bgpAsnAutoAllocation is True.", - default=None - ) - site_id: Optional[str] = Field( - alias="siteId", - description="For EVPN Multi-Site Support. Defaults to Fabric ASN", - default="" + alias="bgpAsn", description="BGP Autonomous System Number 1-4294967295 | 1-65535[.0-65535]. Optional when bgpAsnAutoAllocation is True.", default=None ) + site_id: Optional[str] = Field(alias="siteId", description="For EVPN Multi-Site Support. Defaults to Fabric ASN", default="") bgp_as_mode: BgpAsModeEnum = Field( alias="bgpAsMode", description=( "Multi-AS Unique ASN per Leaf/Border/Border Gateway (Borders and border gateways are " "allowed to share ASN). Same-Tier-AS Leafs share one ASN, Borders/border gateways share one ASN" ), - default=BgpAsModeEnum.MULTI_AS + default=BgpAsModeEnum.MULTI_AS, ) bgp_asn_auto_allocation: bool = Field( alias="bgpAsnAutoAllocation", - description=( - "Automatically allocate and track BGP ASN for leafs, borders and border gateways " - "in Multi-AS mode" - ), - default=True + description=("Automatically allocate and track BGP ASN for leafs, borders and border gateways " "in Multi-AS mode"), + default=True, ) bgp_asn_range: Optional[str] = Field( - alias="bgpAsnRange", - description=( - "BGP ASN range for auto-allocation " - "(minimum: 1 or 1.0, maximum: 4294967295 or 65535.65535)" - ), - default=None - ) - bgp_allow_as_in_num: int = Field( - alias="bgpAllowAsInNum", - description="Number of occurrences of ASN allowed in the BGP AS-path", - default=1 + alias="bgpAsnRange", description=("BGP ASN range for auto-allocation " "(minimum: 1 or 1.0, maximum: 4294967295 or 65535.65535)"), default=None ) + bgp_allow_as_in_num: int = Field(alias="bgpAllowAsInNum", description="Number of occurrences of ASN allowed in the BGP AS-path", default=1) bgp_max_path: int = Field(alias="bgpMaxPath", description="BGP Maximum Paths", default=4) - bgp_underlay_failure_protect: bool = Field( - alias="bgpUnderlayFailureProtect", - description="Enable BGP underlay failure protection", - default=False - ) + bgp_underlay_failure_protect: bool = Field(alias="bgpUnderlayFailureProtect", description="Enable BGP underlay failure protection", default=False) auto_configure_ebgp_evpn_peering: bool = Field( - alias="autoConfigureEbgpEvpnPeering", - description=( - "Automatically configure eBGP EVPN overlay peering between leaf and spine switches" - ), - default=True - ) - allow_leaf_same_as: bool = Field( - alias="allowLeafSameAs", - description="Leafs can have same BGP ASN even when AS mode is Multi-AS", - default=False + alias="autoConfigureEbgpEvpnPeering", description=("Automatically configure eBGP EVPN overlay peering between leaf and spine switches"), default=True ) + allow_leaf_same_as: bool = Field(alias="allowLeafSameAs", description="Leafs can have same BGP ASN even when AS mode is Multi-AS", default=False) assign_ipv4_to_loopback0: bool = Field( alias="assignIpv4ToLoopback0", description=( - "In an IPv6 routed fabric or VXLAN EVPN fabric with IPv6 underlay, assign IPv4 address " - "used for BGP Router ID to the routing loopback interface" + "In an IPv6 routed fabric or VXLAN EVPN fabric with IPv6 underlay, assign IPv4 address " "used for BGP Router ID to the routing loopback interface" ), - default=True - ) - evpn: bool = Field( - description=( - "Enable BGP EVPN as the control plane and VXLAN as the data plane for this fabric" - ), - default=True - ) - route_map_tag: int = Field( - alias="routeMapTag", - description="Tag for Route Map FABRIC-RMAP-REDIST-SUBNET. (Min:0, Max:4294967295)", - default=12345 - ) - disable_route_map_tag: bool = Field( - alias="disableRouteMapTag", - description="No match tag for Route Map FABRIC-RMAP-REDIST-SUBNET", - default=False - ) - leaf_bgp_as: Optional[str] = Field( - alias="leafBgpAs", - description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", - default=None - ) - border_bgp_as: Optional[str] = Field( - alias="borderBgpAs", - description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", - default=None - ) - super_spine_bgp_as: Optional[str] = Field( - alias="superSpineBgpAs", - description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", - default=None + default=True, ) + evpn: bool = Field(description=("Enable BGP EVPN as the control plane and VXLAN as the data plane for this fabric"), default=True) + route_map_tag: int = Field(alias="routeMapTag", description="Tag for Route Map FABRIC-RMAP-REDIST-SUBNET. (Min:0, Max:4294967295)", default=12345) + disable_route_map_tag: bool = Field(alias="disableRouteMapTag", description="No match tag for Route Map FABRIC-RMAP-REDIST-SUBNET", default=False) + leaf_bgp_as: Optional[str] = Field(alias="leafBgpAs", description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None) + border_bgp_as: Optional[str] = Field(alias="borderBgpAs", description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None) + super_spine_bgp_as: Optional[str] = Field(alias="superSpineBgpAs", description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]", default=None) # Propagated from FabricEbgpModel name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") # Network Addressing - bgp_loopback_id: int = Field( - alias="bgpLoopbackId", - description="Underlay Routing Loopback Id", - ge=0, le=1023, default=0 - ) - bgp_loopback_ip_range: str = Field( - alias="bgpLoopbackIpRange", - description="Typically Loopback0 IP Address Range", - default="10.2.0.0/22" - ) - bgp_loopback_ipv6_range: str = Field( - alias="bgpLoopbackIpv6Range", - description="Typically Loopback0 IPv6 Address Range", - default="fd00::a02:0/119" - ) + bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="Underlay Routing Loopback Id", ge=0, le=1023, default=0) + bgp_loopback_ip_range: str = Field(alias="bgpLoopbackIpRange", description="Typically Loopback0 IP Address Range", default="10.2.0.0/22") + bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="Typically Loopback0 IPv6 Address Range", default="fd00::a02:0/119") nve_loopback_id: int = Field( alias="nveLoopbackId", - description=( - "Underlay VTEP loopback Id associated with the Network Virtualization Edge (nve) interface" - ), - ge=0, le=1023, default=1 - ) - nve_loopback_ip_range: str = Field( - alias="nveLoopbackIpRange", - description="Typically Loopback1 IP Address Range", - default="10.3.0.0/22" + description=("Underlay VTEP loopback Id associated with the Network Virtualization Edge (nve) interface"), + ge=0, + le=1023, + default=1, ) + nve_loopback_ip_range: str = Field(alias="nveLoopbackIpRange", description="Typically Loopback1 IP Address Range", default="10.3.0.0/22") nve_loopback_ipv6_range: str = Field( - alias="nveLoopbackIpv6Range", - description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", - default="fd00::a03:0/118" + alias="nveLoopbackIpv6Range", description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", default="fd00::a03:0/118" ) anycast_loopback_id: int = Field( - alias="anycastLoopbackId", - description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", - default=10 + alias="anycastLoopbackId", description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", default=10 ) anycast_rendezvous_point_ip_range: str = Field( - alias="anycastRendezvousPointIpRange", - description="Anycast or Phantom RP IP Address Range", - default="10.254.254.0/24" + alias="anycastRendezvousPointIpRange", description="Anycast or Phantom RP IP Address Range", default="10.254.254.0/24" ) ipv6_anycast_rendezvous_point_ip_range: str = Field( - alias="ipv6AnycastRendezvousPointIpRange", - description="Anycast RP IPv6 Address Range", - default="fd00::254:254:0/118" + alias="ipv6AnycastRendezvousPointIpRange", description="Anycast RP IPv6 Address Range", default="fd00::254:254:0/118" ) intra_fabric_subnet_range: str = Field( - alias="intraFabricSubnetRange", - description="Address range to assign numbered and peer link SVI IPs", - default="10.4.0.0/16" + alias="intraFabricSubnetRange", description="Address range to assign numbered and peer link SVI IPs", default="10.4.0.0/16" ) # VLAN and VNI Ranges - l2_vni_range: str = Field( - alias="l2VniRange", - description="Overlay network identifier range (minimum: 1, maximum: 16777214)", - default="30000-49000" - ) - l3_vni_range: str = Field( - alias="l3VniRange", - description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", - default="50000-59000" - ) + l2_vni_range: str = Field(alias="l2VniRange", description="Overlay network identifier range (minimum: 1, maximum: 16777214)", default="30000-49000") + l3_vni_range: str = Field(alias="l3VniRange", description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", default="50000-59000") network_vlan_range: str = Field( - alias="networkVlanRange", - description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", - default="2300-2999" - ) - vrf_vlan_range: str = Field( - alias="vrfVlanRange", - description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", - default="2000-2299" + alias="networkVlanRange", description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", default="2300-2999" ) + vrf_vlan_range: str = Field(alias="vrfVlanRange", description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", default="2000-2299") # Overlay Configuration overlay_mode: OverlayModeEnum = Field( - alias="overlayMode", - description="Overlay Mode. VRF/Network configuration using config-profile or CLI", - default=OverlayModeEnum.CLI + alias="overlayMode", description="Overlay Mode. VRF/Network configuration using config-profile or CLI", default=OverlayModeEnum.CLI ) replication_mode: ReplicationModeEnum = Field( - alias="replicationMode", - description="Replication Mode for BUM Traffic", - default=ReplicationModeEnum.MULTICAST + alias="replicationMode", description="Replication Mode for BUM Traffic", default=ReplicationModeEnum.MULTICAST ) multicast_group_subnet: str = Field( alias="multicastGroupSubnet", - description=( - "Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool " - "is used for BUM traffic for each overlay network." - ), - default="239.1.1.0/25" + description=("Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool " "is used for BUM traffic for each overlay network."), + default="239.1.1.0/25", ) auto_generate_multicast_group_address: bool = Field( alias="autoGenerateMulticastGroupAddress", - description=( - "Generate a new multicast group address from the multicast pool using a round-robin approach" - ), - default=False + description=("Generate a new multicast group address from the multicast pool using a round-robin approach"), + default=False, ) underlay_multicast_group_address_limit: UnderlayMulticastGroupAddressLimitEnum = Field( alias="underlayMulticastGroupAddressLimit", - description=( - "The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " - "and 512 for versions above 10.2(1)" - ), - default=UnderlayMulticastGroupAddressLimitEnum.V_128 - ) - tenant_routed_multicast: bool = Field( - alias="tenantRoutedMulticast", - description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", - default=False + description=("The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " "and 512 for versions above 10.2(1)"), + default=UnderlayMulticastGroupAddressLimitEnum.V_128, ) + tenant_routed_multicast: bool = Field(alias="tenantRoutedMulticast", description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", default=False) tenant_routed_multicast_ipv6: bool = Field( - alias="tenantRoutedMulticastIpv6", - description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", - default=False + alias="tenantRoutedMulticastIpv6", description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", default=False ) first_hop_redundancy_protocol: FirstHopRedundancyProtocolEnum = Field( - alias="firstHopRedundancyProtocol", - description="First Hop Redundancy Protocol HSRP or VRRP", - default=FirstHopRedundancyProtocolEnum.HSRP + alias="firstHopRedundancyProtocol", description="First Hop Redundancy Protocol HSRP or VRRP", default=FirstHopRedundancyProtocolEnum.HSRP ) # Multicast / Rendezvous Point rendezvous_point_count: RendezvousPointCountEnum = Field( - alias="rendezvousPointCount", - description="Number of spines acting as Rendezvous-Points (RPs)", - default=RendezvousPointCountEnum.TWO - ) - rendezvous_point_loopback_id: int = Field( - alias="rendezvousPointLoopbackId", - description="Rendezvous point loopback Id", - default=254 + alias="rendezvousPointCount", description="Number of spines acting as Rendezvous-Points (RPs)", default=RendezvousPointCountEnum.TWO ) + rendezvous_point_loopback_id: int = Field(alias="rendezvousPointLoopbackId", description="Rendezvous point loopback Id", default=254) rendezvous_point_mode: RendezvousPointModeEnum = Field( - alias="rendezvousPointMode", - description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", - default=RendezvousPointModeEnum.ASM + alias="rendezvousPointMode", description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", default=RendezvousPointModeEnum.ASM ) phantom_rendezvous_point_loopback_id1: int = Field( - alias="phantomRendezvousPointLoopbackId1", - description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", - default=2 + alias="phantomRendezvousPointLoopbackId1", description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", default=2 ) phantom_rendezvous_point_loopback_id2: int = Field( - alias="phantomRendezvousPointLoopbackId2", - description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", - default=3 + alias="phantomRendezvousPointLoopbackId2", description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", default=3 ) phantom_rendezvous_point_loopback_id3: int = Field( - alias="phantomRendezvousPointLoopbackId3", - description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", - default=4 + alias="phantomRendezvousPointLoopbackId3", description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", default=4 ) phantom_rendezvous_point_loopback_id4: int = Field( alias="phantomRendezvousPointLoopbackId4", - description=( - "Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments" - ), - default=5 + description=("Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments"), + default=5, ) l3vni_multicast_group: str = Field( - alias="l3vniMulticastGroup", - description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", - default="239.1.1.0" + alias="l3vniMulticastGroup", description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", default="239.1.1.0" ) l3_vni_ipv6_multicast_group: str = Field( - alias="l3VniIpv6MulticastGroup", - description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", - default="ff1e::" + alias="l3VniIpv6MulticastGroup", description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", default="ff1e::" ) ipv6_multicast_group_subnet: str = Field( - alias="ipv6MulticastGroupSubnet", - description="IPv6 Multicast address with prefix 112 to 128", - default="ff1e::/121" + alias="ipv6MulticastGroupSubnet", description="IPv6 Multicast address with prefix 112 to 128", default="ff1e::/121" ) mvpn_vrf_route_import_id: bool = Field( - alias="mvpnVrfRouteImportId", - description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", - default=True + alias="mvpnVrfRouteImportId", description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", default=True ) mvpn_vrf_route_import_id_range: Optional[str] = Field( alias="mvpnVrfRouteImportIdRange", @@ -412,34 +267,18 @@ class VxlanEbgpManagementModel(NDNestedModel): "MVPN VRI ID (minimum: 1, maximum: 65535) for vPC, applicable when TRM enabled " "with IPv6 underlay, or mvpnVrfRouteImportId enabled with IPv4 underlay" ), - default=None + default=None, ) vrf_route_import_id_reallocation: bool = Field( - alias="vrfRouteImportIdReallocation", - description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", - default=False + alias="vrfRouteImportIdReallocation", description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", default=False ) # Advanced Features - anycast_gateway_mac: str = Field( - alias="anycastGatewayMac", - description="Shared anycast gateway MAC address for all VTEPs", - default="2020.0000.00aa" - ) - target_subnet_mask: int = Field( - alias="targetSubnetMask", - description="Mask for underlay subnet IP range", - ge=24, le=31, default=30 - ) - fabric_mtu: int = Field( - alias="fabricMtu", - description="Intra Fabric Interface MTU. Must be an even number", - ge=1500, le=9216, default=9216 - ) + anycast_gateway_mac: str = Field(alias="anycastGatewayMac", description="Shared anycast gateway MAC address for all VTEPs", default="2020.0000.00aa") + target_subnet_mask: int = Field(alias="targetSubnetMask", description="Mask for underlay subnet IP range", ge=24, le=31, default=30) + fabric_mtu: int = Field(alias="fabricMtu", description="Intra Fabric Interface MTU. Must be an even number", ge=1500, le=9216, default=9216) l2_host_interface_mtu: int = Field( - alias="l2HostInterfaceMtu", - description="Layer 2 host interface MTU. Must be an even number", - ge=1500, le=9216, default=9216 + alias="l2HostInterfaceMtu", description="Layer 2 host interface MTU. Must be an even number", ge=1500, le=9216, default=9216 ) l3_vni_no_vlan_default_option: bool = Field( alias="l3VniNoVlanDefaultOption", @@ -447,107 +286,46 @@ class VxlanEbgpManagementModel(NDNestedModel): "L3 VNI configuration without VLAN configuration. This value is propagated on vrf " "creation as the default value of 'Enable L3VNI w/o VLAN' in vrf" ), - default=False - ) - underlay_ipv6: bool = Field( - alias="underlayIpv6", - description="If not enabled, IPv4 underlay is used", - default=False + default=False, ) + underlay_ipv6: bool = Field(alias="underlayIpv6", description="If not enabled, IPv4 underlay is used", default=False) static_underlay_ip_allocation: bool = Field( - alias="staticUnderlayIpAllocation", - description="Checking this will disable Dynamic Underlay IP Address Allocations", - default=False + alias="staticUnderlayIpAllocation", description="Checking this will disable Dynamic Underlay IP Address Allocations", default=False ) anycast_border_gateway_advertise_physical_ip: bool = Field( alias="anycastBorderGatewayAdvertisePhysicalIp", - description=( - "To advertise Anycast Border Gateway PIP as VTEP. " - "Effective on MSD fabric 'Recalculate Config'" - ), - default=False + description=("To advertise Anycast Border Gateway PIP as VTEP. " "Effective on MSD fabric 'Recalculate Config'"), + default=False, ) # VPC Configuration vpc_domain_id_range: str = Field( - alias="vpcDomainIdRange", - description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", - default="1-1000" - ) - vpc_peer_link_vlan: str = Field( - alias="vpcPeerLinkVlan", - description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", - default="3600" - ) - vpc_peer_link_enable_native_vlan: bool = Field( - alias="vpcPeerLinkEnableNativeVlan", - description="Enable VpcPeer Link for Native Vlan", - default=False + alias="vpcDomainIdRange", description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", default="1-1000" ) + vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", default="3600") + vpc_peer_link_enable_native_vlan: bool = Field(alias="vpcPeerLinkEnableNativeVlan", description="Enable VpcPeer Link for Native Vlan", default=False) vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( - alias="vpcPeerKeepAliveOption", - description="Use vPC Peer Keep Alive with Loopback or Management", - default=VpcPeerKeepAliveOptionEnum.MANAGEMENT - ) - vpc_auto_recovery_timer: int = Field( - alias="vpcAutoRecoveryTimer", - description="vPC auto recovery timer (in seconds)", - ge=240, - le=3600, - default=360 - ) - vpc_delay_restore_timer: int = Field( - alias="vpcDelayRestoreTimer", - description="vPC delay restore timer (in seconds)", - ge=1, - le=3600, - default=150 + alias="vpcPeerKeepAliveOption", description="Use vPC Peer Keep Alive with Loopback or Management", default=VpcPeerKeepAliveOptionEnum.MANAGEMENT ) + vpc_auto_recovery_timer: int = Field(alias="vpcAutoRecoveryTimer", description="vPC auto recovery timer (in seconds)", ge=240, le=3600, default=360) + vpc_delay_restore_timer: int = Field(alias="vpcDelayRestoreTimer", description="vPC delay restore timer (in seconds)", ge=1, le=3600, default=150) vpc_peer_link_port_channel_id: str = Field( - alias="vpcPeerLinkPortChannelId", - description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", - default="500" + alias="vpcPeerLinkPortChannelId", description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", default="500" ) vpc_ipv6_neighbor_discovery_sync: bool = Field( - alias="vpcIpv6NeighborDiscoverySync", - description="Enable IPv6 ND synchronization between vPC peers", - default=True - ) - vpc_layer3_peer_router: bool = Field( - alias="vpcLayer3PeerRouter", - description="Enable Layer-3 Peer-Router on all Leaf switches", - default=True - ) - vpc_tor_delay_restore_timer: int = Field( - alias="vpcTorDelayRestoreTimer", - description="vPC delay restore timer for ToR switches (in seconds)", - default=30 + alias="vpcIpv6NeighborDiscoverySync", description="Enable IPv6 ND synchronization between vPC peers", default=True ) + vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable Layer-3 Peer-Router on all Leaf switches", default=True) + vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC delay restore timer for ToR switches (in seconds)", default=30) fabric_vpc_domain_id: bool = Field( - alias="fabricVpcDomainId", - description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", - default=False - ) - shared_vpc_domain_id: int = Field( - alias="sharedVpcDomainId", - description="vPC Domain Id to be used on all vPC pairs", - default=1 - ) - fabric_vpc_qos: bool = Field( - alias="fabricVpcQos", - description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", - default=False + alias="fabricVpcDomainId", description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", default=False ) + shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="vPC Domain Id to be used on all vPC pairs", default=1) + fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", default=False) fabric_vpc_qos_policy_name: str = Field( - alias="fabricVpcQosPolicyName", - description="Qos Policy name should be same on all spines", - default="spine_qos_for_fabric_vpc_peering" - ) - enable_peer_switch: bool = Field( - alias="enablePeerSwitch", - description="Enable the vPC peer-switch feature on ToR switches", - default=False + alias="fabricVpcQosPolicyName", description="Qos Policy name should be same on all spines", default="spine_qos_for_fabric_vpc_peering" ) + enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable the vPC peer-switch feature on ToR switches", default=False) # Per-VRF Loopback per_vrf_loopback_auto_provision: bool = Field( @@ -558,212 +336,100 @@ class VxlanEbgpManagementModel(NDNestedModel): "Multiattach actions are performed. Provisioned loopbacks cannot be deleted until VRFs " "are unattached." ), - default=False + default=False, ) per_vrf_loopback_ip_range: str = Field( - alias="perVrfLoopbackIpRange", - description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", - default="10.5.0.0/22" + alias="perVrfLoopbackIpRange", description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", default="10.5.0.0/22" ) per_vrf_loopback_auto_provision_ipv6: bool = Field( - alias="perVrfLoopbackAutoProvisionIpv6", - description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", - default=False + alias="perVrfLoopbackAutoProvisionIpv6", description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", default=False ) per_vrf_loopback_ipv6_range: str = Field( - alias="perVrfLoopbackIpv6Range", - description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", - default="fd00::a05:0/112" + alias="perVrfLoopbackIpv6Range", description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", default="fd00::a05:0/112" ) # Templates - vrf_template: str = Field( - alias="vrfTemplate", - description="Default overlay VRF template for leafs", - default="Default_VRF_Universal" - ) - network_template: str = Field( - alias="networkTemplate", - description="Default overlay network template for leafs", - default="Default_Network_Universal" - ) + vrf_template: str = Field(alias="vrfTemplate", description="Default overlay VRF template for leafs", default="Default_VRF_Universal") + network_template: str = Field(alias="networkTemplate", description="Default overlay network template for leafs", default="Default_Network_Universal") vrf_extension_template: str = Field( - alias="vrfExtensionTemplate", - description="Default overlay VRF template for borders", - default="Default_VRF_Extension_Universal" + alias="vrfExtensionTemplate", description="Default overlay VRF template for borders", default="Default_VRF_Extension_Universal" ) network_extension_template: str = Field( - alias="networkExtensionTemplate", - description="Default overlay network template for borders", - default="Default_Network_Extension_Universal" + alias="networkExtensionTemplate", description="Default overlay network template for borders", default="Default_Network_Extension_Universal" ) # Optional Advanced Settings performance_monitoring: bool = Field( alias="performanceMonitoring", - description=( - "If enabled, switch metrics are collected through periodic SNMP polling. " - "Alternative to real-time telemetry" - ), - default=False + description=("If enabled, switch metrics are collected through periodic SNMP polling. " "Alternative to real-time telemetry"), + default=False, ) tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable tenant DHCP", default=True) advertise_physical_ip: bool = Field( - alias="advertisePhysicalIp", - description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", - default=False + alias="advertisePhysicalIp", description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", default=False ) advertise_physical_ip_on_border: bool = Field( alias="advertisePhysicalIpOnBorder", - description=( - "Enable advertise-pip on vPC borders and border gateways only. " - "Applicable only when vPC advertise-pip is not enabled" - ), - default=True + description=("Enable advertise-pip on vPC borders and border gateways only. " "Applicable only when vPC advertise-pip is not enabled"), + default=True, ) # Protocol Settings — BGP - bgp_authentication: bool = Field( - alias="bgpAuthentication", - description="Enables or disables the BGP authentication", - default=False - ) + bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enables or disables the BGP authentication", default=False) bgp_authentication_key_type: BgpAuthenticationKeyTypeEnum = Field( alias="bgpAuthenticationKeyType", description="BGP key encryption type: 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7", - default=BgpAuthenticationKeyTypeEnum.THREE_DES - ) - bgp_authentication_key: str = Field( - alias="bgpAuthenticationKey", - description="Encrypted BGP authentication key based on type", - default="" + default=BgpAuthenticationKeyTypeEnum.THREE_DES, ) + bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="Encrypted BGP authentication key based on type", default="") # Protocol Settings — BFD bfd: bool = Field(description="Enable BFD. Valid for IPv4 Underlay only", default=False) bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD For iBGP", default=False) - bfd_authentication: bool = Field( - alias="bfdAuthentication", - description="Enable BFD Authentication. Valid for P2P Interfaces only", - default=False - ) - bfd_authentication_key_id: int = Field( - alias="bfdAuthenticationKeyId", - description="BFD Authentication Key ID", - default=100 - ) - bfd_authentication_key: str = Field( - alias="bfdAuthenticationKey", - description="Encrypted SHA1 secret value", - default="" - ) + bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD Authentication. Valid for P2P Interfaces only", default=False) + bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD Authentication Key ID", default=100) + bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="Encrypted SHA1 secret value", default="") # Protocol Settings — PIM - pim_hello_authentication: bool = Field( - alias="pimHelloAuthentication", - description="Valid for IPv4 Underlay only", - default=False - ) - pim_hello_authentication_key: str = Field( - alias="pimHelloAuthenticationKey", - description="3DES Encrypted", - default="" - ) + pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Valid for IPv4 Underlay only", default=False) + pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="3DES Encrypted", default="") # Management Settings nxapi: bool = Field(description="Enable NX-API over HTTPS", default=False) nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API over HTTP", default=False) - nxapi_https_port: int = Field( - alias="nxapiHttpsPort", - description="HTTPS port for NX-API", - ge=1, le=65535, default=443 - ) - nxapi_http_port: int = Field( - alias="nxapiHttpPort", - description="HTTP port for NX-API", - ge=1, le=65535, default=80 - ) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="HTTPS port for NX-API", ge=1, le=65535, default=443) + nxapi_http_port: int = Field(alias="nxapiHttpPort", description="HTTP port for NX-API", ge=1, le=65535, default=80) # Bootstrap / Day-0 / DHCP - day0_bootstrap: bool = Field( - alias="day0Bootstrap", - description="Automatic IP Assignment For POAP", - default=False - ) + day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Automatic IP Assignment For POAP", default=False) bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( - alias="bootstrapSubnetCollection", - description="List of IPv4 or IPv6 subnets to be used for bootstrap", - default_factory=list - ) - local_dhcp_server: bool = Field( - alias="localDhcpServer", - description="Automatic IP Assignment For POAP From Local DHCP Server", - default=False + alias="bootstrapSubnetCollection", description="List of IPv4 or IPv6 subnets to be used for bootstrap", default_factory=list ) + local_dhcp_server: bool = Field(alias="localDhcpServer", description="Automatic IP Assignment For POAP From Local DHCP Server", default=False) dhcp_protocol_version: DhcpProtocolVersionEnum = Field( - alias="dhcpProtocolVersion", - description="IP protocol version for Local DHCP Server", - default=DhcpProtocolVersionEnum.DHCPV4 - ) - dhcp_start_address: str = Field( - alias="dhcpStartAddress", - description="DHCP Scope Start Address For Switch POAP", - default="" - ) - dhcp_end_address: str = Field( - alias="dhcpEndAddress", - description="DHCP Scope End Address For Switch POAP", - default="" - ) - management_gateway: str = Field( - alias="managementGateway", - description="Default Gateway For Management VRF On The Switch", - default="" - ) - management_ipv4_prefix: int = Field( - alias="managementIpv4Prefix", - description="Switch Mgmt IP Subnet Prefix if ipv4", - default=24 - ) - management_ipv6_prefix: int = Field( - alias="managementIpv6Prefix", - description="Switch Management IP Subnet Prefix if ipv6", - default=64 + alias="dhcpProtocolVersion", description="IP protocol version for Local DHCP Server", default=DhcpProtocolVersionEnum.DHCPV4 ) + dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP Scope Start Address For Switch POAP", default="") + dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP Scope End Address For Switch POAP", default="") + management_gateway: str = Field(alias="managementGateway", description="Default Gateway For Management VRF On The Switch", default="") + management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Switch Mgmt IP Subnet Prefix if ipv4", default=24) + management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Switch Management IP Subnet Prefix if ipv6", default=64) # Netflow Settings - netflow_settings: NetflowSettingsModel = Field( - alias="netflowSettings", - description="Netflow configuration", - default_factory=NetflowSettingsModel - ) + netflow_settings: NetflowSettingsModel = Field(alias="netflowSettings", description="Netflow configuration", default_factory=NetflowSettingsModel) # Backup / Restore real_time_backup: Optional[bool] = Field( - alias="realTimeBackup", - description=( - "Backup hourly only if there is any config deployment since last backup" - ), - default=None - ) - scheduled_backup: Optional[bool] = Field( - alias="scheduledBackup", - description="Enable backup at the specified time daily", - default=None + alias="realTimeBackup", description=("Backup hourly only if there is any config deployment since last backup"), default=None ) + scheduled_backup: Optional[bool] = Field(alias="scheduledBackup", description="Enable backup at the specified time daily", default=None) scheduled_backup_time: str = Field( - alias="scheduledBackupTime", - description=( - "Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)" - ), - default="" + alias="scheduledBackupTime", description=("Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)"), default="" ) # VRF Lite / Sub-Interface sub_interface_dot1q_range: str = Field( - alias="subInterfaceDot1qRange", - description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", - default="2-511" + alias="subInterfaceDot1qRange", description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", default="2-511" ) vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field( alias="vrfLiteAutoConfig", @@ -773,18 +439,10 @@ class VxlanEbgpManagementModel(NDNestedModel): "and between border devices in Easy Fabric and edge routers in External Fabric. " "The IP address is taken from the 'VRF Lite Subnet IP Range' pool." ), - default=VrfLiteAutoConfigEnum.MANUAL - ) - vrf_lite_subnet_range: str = Field( - alias="vrfLiteSubnetRange", - description="Address range to assign P2P Interfabric Connections", - default="10.33.0.0/16" - ) - vrf_lite_subnet_target_mask: int = Field( - alias="vrfLiteSubnetTargetMask", - description="VRF Lite Subnet Mask", - default=30 + default=VrfLiteAutoConfigEnum.MANUAL, ) + vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="Address range to assign P2P Interfabric Connections", default="10.33.0.0/16") + vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF Lite Subnet Mask", default=30) auto_unique_vrf_lite_ip_prefix: bool = Field( alias="autoUniqueVrfLiteIpPrefix", description=( @@ -792,80 +450,56 @@ class VxlanEbgpManagementModel(NDNestedModel): "over VRF LITE IFC. Instead, unique IP Subnet is allocated for each VRF extension " "over VRF LITE IFC." ), - default=False + default=False, ) # Leaf / TOR - leaf_tor_id_range: bool = Field( - alias="leafTorIdRange", - description="Use specific vPC/Port-channel ID range for leaf-tor pairings", - default=False - ) + leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Use specific vPC/Port-channel ID range for leaf-tor pairings", default=False) leaf_tor_vpc_port_channel_id_range: str = Field( alias="leafTorVpcPortChannelIdRange", description=( "Specify vPC/Port-channel ID range (minimum: 1, maximum: 4096), this range is used " "for auto-allocating vPC/Port-Channel IDs for leaf-tor pairings" ), - default="1-499" + default="1-499", ) allow_vlan_on_leaf_tor_pairing: AllowVlanOnLeafTorPairingEnum = Field( alias="allowVlanOnLeafTorPairing", description="Set trunk allowed vlan to 'none' or 'all' for leaf-tor pairing port-channels", - default=AllowVlanOnLeafTorPairingEnum.NONE + default=AllowVlanOnLeafTorPairingEnum.NONE, ) # DNS / NTP / Syslog Collections ntp_server_collection: List[str] = Field( - default_factory=lambda: ["string"], - alias="ntpServerCollection", - description="List of NTP server IPv4/IPv6 addresses and/or hostnames" + default_factory=lambda: ["string"], alias="ntpServerCollection", description="List of NTP server IPv4/IPv6 addresses and/or hostnames" ) ntp_server_vrf_collection: List[str] = Field( default_factory=lambda: ["string"], alias="ntpServerVrfCollection", - description=( - "NTP Server VRFs. One VRF for all NTP servers or a list of VRFs, one per NTP server" - ) - ) - dns_collection: List[str] = Field( - default_factory=lambda: ["5.192.28.174"], - alias="dnsCollection", - description="List of IPv4 and IPv6 DNS addresses" + description=("NTP Server VRFs. One VRF for all NTP servers or a list of VRFs, one per NTP server"), ) + dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection", description="List of IPv4 and IPv6 DNS addresses") dns_vrf_collection: List[str] = Field( default_factory=lambda: ["string"], alias="dnsVrfCollection", - description=( - "DNS Server VRFs. One VRF for all DNS servers or a list of VRFs, one per DNS server" - ) + description=("DNS Server VRFs. One VRF for all DNS servers or a list of VRFs, one per DNS server"), ) syslog_server_collection: List[str] = Field( - default_factory=lambda: ["string"], - alias="syslogServerCollection", - description="List of Syslog server IPv4/IPv6 addresses and/or hostnames" + default_factory=lambda: ["string"], alias="syslogServerCollection", description="List of Syslog server IPv4/IPv6 addresses and/or hostnames" ) syslog_server_vrf_collection: List[str] = Field( default_factory=lambda: ["string"], alias="syslogServerVrfCollection", - description=( - "Syslog Server VRFs. One VRF for all Syslog servers or a list of VRFs, " - "one per Syslog server" - ) + description=("Syslog Server VRFs. One VRF for all Syslog servers or a list of VRFs, " "one per Syslog server"), ) syslog_severity_collection: List[int] = Field( - default_factory=lambda: [7], - alias="syslogSeverityCollection", - description="List of Syslog severity values, one per Syslog server" + default_factory=lambda: [7], alias="syslogSeverityCollection", description="List of Syslog severity values, one per Syslog server" ) # Extra Config / Pre-Interface Config / AAA / Banner banner: str = Field( - description=( - "Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) " - "followed by message ending with delimiter" - ), - default="" + description=("Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) " "followed by message ending with delimiter"), + default="", ) extra_config_leaf: str = Field( alias="extraConfigLeaf", @@ -873,210 +507,128 @@ class VxlanEbgpManagementModel(NDNestedModel): "Additional CLIs as captured from the show running configuration, added after interface " "configurations for all switches with a VTEP unless they have some spine role" ), - default="" + default="", ) extra_config_spine: str = Field( alias="extraConfigSpine", description=( - "Additional CLIs as captured from the show running configuration, added after interface " - "configurations for all switches with some spine role" + "Additional CLIs as captured from the show running configuration, added after interface " "configurations for all switches with some spine role" ), - default="" + default="", ) extra_config_tor: str = Field( alias="extraConfigTor", - description=( - "Additional CLIs as captured from the show running configuration, added after interface " - "configurations for all ToRs" - ), - default="" - ) - extra_config_intra_fabric_links: str = Field( - alias="extraConfigIntraFabricLinks", - description="Additional CLIs for all Intra-Fabric links", - default="" - ) - extra_config_aaa: str = Field( - alias="extraConfigAaa", - description="AAA Configurations", - default="" + description=("Additional CLIs as captured from the show running configuration, added after interface " "configurations for all ToRs"), + default="", ) + extra_config_intra_fabric_links: str = Field(alias="extraConfigIntraFabricLinks", description="Additional CLIs for all Intra-Fabric links", default="") + extra_config_aaa: str = Field(alias="extraConfigAaa", description="AAA Configurations", default="") extra_config_nxos_bootstrap: str = Field( - alias="extraConfigNxosBootstrap", - description="Additional CLIs required during device bootup/login e.g. AAA/Radius", - default="" - ) - aaa: bool = Field( - description="Include AAA configs from Manageability tab during device bootup", - default=False + alias="extraConfigNxosBootstrap", description="Additional CLIs required during device bootup/login e.g. AAA/Radius", default="" ) + aaa: bool = Field(description="Include AAA configs from Manageability tab during device bootup", default=False) pre_interface_config_leaf: str = Field( alias="preInterfaceConfigLeaf", description=( "Additional CLIs as captured from the show running configuration, added before interface " "configurations for all switches with a VTEP unless they have some spine role" ), - default="" + default="", ) pre_interface_config_spine: str = Field( alias="preInterfaceConfigSpine", description=( - "Additional CLIs as captured from the show running configuration, added before interface " - "configurations for all switches with some spine role" + "Additional CLIs as captured from the show running configuration, added before interface " "configurations for all switches with some spine role" ), - default="" + default="", ) pre_interface_config_tor: str = Field( alias="preInterfaceConfigTor", - description=( - "Additional CLIs as captured from the show running configuration, added before interface " - "configurations for all ToRs" - ), - default="" + description=("Additional CLIs as captured from the show running configuration, added before interface " "configurations for all ToRs"), + default="", ) # System / Compliance / OAM / Misc greenfield_debug_flag: GreenfieldDebugFlagEnum = Field( alias="greenfieldDebugFlag", - description=( - "Allow switch configuration to be cleared without a reload when " - "preserveConfig is set to false" - ), - default=GreenfieldDebugFlagEnum.DISABLE + description=("Allow switch configuration to be cleared without a reload when " "preserveConfig is set to false"), + default=GreenfieldDebugFlagEnum.DISABLE, ) interface_statistics_load_interval: int = Field( - alias="interfaceStatisticsLoadInterval", - description="Interface Statistics Load Interval. Time in seconds", - default=10 - ) - nve_hold_down_timer: int = Field( - alias="nveHoldDownTimer", - description="NVE Source Inteface HoldDown Time in seconds", - default=180 + alias="interfaceStatisticsLoadInterval", description="Interface Statistics Load Interval. Time in seconds", default=10 ) + nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE Source Inteface HoldDown Time in seconds", default=180) next_generation_oam: bool = Field( alias="nextGenerationOAM", - description=( - "Enable the Next Generation (NG) OAM feature for all switches in the fabric " - "to aid in trouble-shooting VXLAN EVPN fabrics" - ), - default=True + description=("Enable the Next Generation (NG) OAM feature for all switches in the fabric " "to aid in trouble-shooting VXLAN EVPN fabrics"), + default=True, ) ngoam_south_bound_loop_detect: bool = Field( - alias="ngoamSouthBoundLoopDetect", - description="Enable the Next Generation (NG) OAM southbound loop detection", - default=False + alias="ngoamSouthBoundLoopDetect", description="Enable the Next Generation (NG) OAM southbound loop detection", default=False ) ngoam_south_bound_loop_detect_probe_interval: int = Field( alias="ngoamSouthBoundLoopDetectProbeInterval", - description=( - "Set Next Generation (NG) OAM southbound loop detection probe interval in seconds." - ), - default=300 + description=("Set Next Generation (NG) OAM southbound loop detection probe interval in seconds."), + default=300, ) ngoam_south_bound_loop_detect_recovery_interval: int = Field( alias="ngoamSouthBoundLoopDetectRecoveryInterval", - description=( - "Set the Next Generation (NG) OAM southbound loop detection recovery interval in seconds" - ), - default=600 + description=("Set the Next Generation (NG) OAM southbound loop detection recovery interval in seconds"), + default=600, ) strict_config_compliance_mode: bool = Field( alias="strictConfigComplianceMode", - description=( - "Enable bi-directional compliance checks to flag additional configs in the running config " - "that are not in the intent/expected config" - ), - default=False + description=("Enable bi-directional compliance checks to flag additional configs in the running config " "that are not in the intent/expected config"), + default=False, ) advanced_ssh_option: bool = Field( alias="advancedSshOption", - description=( - "Enable AAA IP Authorization. Enable only, when IP Authorization is enabled " - "in the AAA Server" - ), - default=False + description=("Enable AAA IP Authorization. Enable only, when IP Authorization is enabled " "in the AAA Server"), + default=False, ) copp_policy: CoppPolicyEnum = Field( alias="coppPolicy", - description=( - "Fabric wide CoPP policy. Customized CoPP policy should be provided " - "when 'manual' is selected." - ), - default=CoppPolicyEnum.STRICT + description=("Fabric wide CoPP policy. Customized CoPP policy should be provided " "when 'manual' is selected."), + default=CoppPolicyEnum.STRICT, ) power_redundancy_mode: PowerRedundancyModeEnum = Field( - alias="powerRedundancyMode", - description="Default Power Supply Mode for NX-OS Switches", - default=PowerRedundancyModeEnum.REDUNDANT - ) - heartbeat_interval: int = Field( - alias="heartbeatInterval", - description="XConnect heartbeat interval for periodic link status checks", - default=190 - ) - snmp_trap: bool = Field( - alias="snmpTrap", - description="Configure ND as a receiver for SNMP traps", - default=True + alias="powerRedundancyMode", description="Default Power Supply Mode for NX-OS Switches", default=PowerRedundancyModeEnum.REDUNDANT ) + heartbeat_interval: int = Field(alias="heartbeatInterval", description="XConnect heartbeat interval for periodic link status checks", default=190) + snmp_trap: bool = Field(alias="snmpTrap", description="Configure ND as a receiver for SNMP traps", default=True) cdp: bool = Field(description="Enable CDP on management interface", default=False) real_time_interface_statistics_collection: bool = Field( - alias="realTimeInterfaceStatisticsCollection", - description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", - default=False + alias="realTimeInterfaceStatisticsCollection", description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", default=False ) tcam_allocation: bool = Field( - alias="tcamAllocation", - description=( - "TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled" - ), - default=True + alias="tcamAllocation", description=("TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled"), default=True ) allow_smart_switch_onboarding: bool = Field( - alias="allowSmartSwitchOnboarding", - description="Enable onboarding of smart switches to Hypershield for firewall service", - default=False + alias="allowSmartSwitchOnboarding", description="Enable onboarding of smart switches to Hypershield for firewall service", default=False ) # Queuing / QoS - default_queuing_policy: bool = Field( - alias="defaultQueuingPolicy", - description="Enable Default Queuing Policies", - default=False - ) + default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable Default Queuing Policies", default=False) default_queuing_policy_cloudscale: str = Field( alias="defaultQueuingPolicyCloudscale", - description=( - "Queuing Policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric" - ), - default="queuing_policy_default_8q_cloudscale" + description=("Queuing Policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric"), + default="queuing_policy_default_8q_cloudscale", ) default_queuing_policy_r_series: str = Field( - alias="defaultQueuingPolicyRSeries", - description="Queueing policy for all Nexus R-series switches", - default="queuing_policy_default_r_series" + alias="defaultQueuingPolicyRSeries", description="Queueing policy for all Nexus R-series switches", default="queuing_policy_default_r_series" ) default_queuing_policy_other: str = Field( - alias="defaultQueuingPolicyOther", - description="Queuing Policy for all other switches in the fabric", - default="queuing_policy_default_other" + alias="defaultQueuingPolicyOther", description="Queuing Policy for all other switches in the fabric", default="queuing_policy_default_other" ) aiml_qos: bool = Field( alias="aimlQos", - description=( - "Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) " - "switch fabric for AI network workloads" - ), - default=False + description=("Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) " "switch fabric for AI network workloads"), + default=False, ) aiml_qos_policy: AimlQosPolicyEnum = Field( alias="aimlQosPolicy", - description=( - "Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. " - "User-defined allows for custom configuration." - ), - default=AimlQosPolicyEnum.V_400G + description=("Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. " "User-defined allows for custom configuration."), + default=AimlQosPolicyEnum.V_400G, ) roce_v2: str = Field( alias="roceV2", @@ -1085,7 +637,7 @@ class VxlanEbgpManagementModel(NDNestedModel): "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="26" + default="26", ) cnp: str = Field( description=( @@ -1093,27 +645,18 @@ class VxlanEbgpManagementModel(NDNestedModel): "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="48" + default="48", ) wred_min: int = Field(alias="wredMin", description="WRED minimum threshold (in kbytes)", default=950) wred_max: int = Field(alias="wredMax", description="WRED maximum threshold (in kbytes)", default=3000) wred_drop_probability: int = Field(alias="wredDropProbability", description="Drop probability %", default=7) - wred_weight: int = Field( - alias="wredWeight", - description="Influences how quickly WRED reacts to queue depth changes", - default=0 - ) - bandwidth_remaining: int = Field( - alias="bandwidthRemaining", - description="Percentage of remaining bandwidth allocated to AI traffic queues", - default=50 - ) + wred_weight: int = Field(alias="wredWeight", description="Influences how quickly WRED reacts to queue depth changes", default=0) + bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Percentage of remaining bandwidth allocated to AI traffic queues", default=50) dlb: bool = Field( description=( - "Enables fabric-level Dynamic Load Balancing (DLB) configuration. " - "Note: Inter-Switch-Links (ISL) will be configured as DLB Interfaces" + "Enables fabric-level Dynamic Load Balancing (DLB) configuration. " "Note: Inter-Switch-Links (ISL) will be configured as DLB Interfaces" ), - default=False + default=False, ) dlb_mode: DlbModeEnum = Field( alias="dlbMode", @@ -1121,12 +664,10 @@ class VxlanEbgpManagementModel(NDNestedModel): "Select system-wide flowlet, per-packet (packet spraying) or policy driven mixed mode. " "Note: Mixed mode is supported on Silicon One (S1) platform only." ), - default=DlbModeEnum.FLOWLET + default=DlbModeEnum.FLOWLET, ) dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field( - alias="dlbMixedModeDefault", - description="Default load balancing mode for policy driven mixed mode DLB", - default=DlbMixedModeDefaultEnum.ECMP + alias="dlbMixedModeDefault", description="Default load balancing mode for policy driven mixed mode DLB", default=DlbMixedModeDefaultEnum.ECMP ) flowlet_aging: Optional[int] = Field( alias="flowletAging", @@ -1134,7 +675,7 @@ class VxlanEbgpManagementModel(NDNestedModel): "Flowlet aging timer in microseconds. Valid range depends on platform: " "Cloud Scale (CS)=1-2000000 (default 500), Silicon One (S1)=1-1024 (default 256)" ), - default=None + default=None, ) flowlet_dscp: str = Field( alias="flowletDscp", @@ -1143,7 +684,7 @@ class VxlanEbgpManagementModel(NDNestedModel): "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="" + default="", ) per_packet_dscp: str = Field( alias="perPacketDscp", @@ -1152,113 +693,67 @@ class VxlanEbgpManagementModel(NDNestedModel): "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43," "cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="" + default="", ) ai_load_sharing: bool = Field( - alias="aiLoadSharing", - description=( - "Enable IP load sharing using source and destination address for AI workloads" - ), - default=False + alias="aiLoadSharing", description=("Enable IP load sharing using source and destination address for AI workloads"), default=False ) priority_flow_control_watch_interval: Optional[int] = Field( alias="priorityFlowControlWatchInterval", - description=( - "Acceptable values from 101 to 1000 (milliseconds). " - "Leave blank for system default (100ms)." - ), - default=None + description=("Acceptable values from 101 to 1000 (milliseconds). " "Leave blank for system default (100ms)."), + default=None, ) # PTP ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) - ptp_loopback_id: int = Field( - alias="ptpLoopbackId", - description="Precision Time Protocol Source Loopback Id", - default=0 - ) - ptp_domain_id: int = Field( - alias="ptpDomainId", - description="Multiple Independent PTP Clocking Subdomains on a Single Network", - default=0 - ) + ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="Precision Time Protocol Source Loopback Id", default=0) + ptp_domain_id: int = Field(alias="ptpDomainId", description="Multiple Independent PTP Clocking Subdomains on a Single Network", default=0) # Private VLAN - private_vlan: bool = Field( - alias="privateVlan", - description="Enable PVLAN on switches except spines and super spines", - default=False - ) + private_vlan: bool = Field(alias="privateVlan", description="Enable PVLAN on switches except spines and super spines", default=False) default_private_vlan_secondary_network_template: str = Field( - alias="defaultPrivateVlanSecondaryNetworkTemplate", - description="Default PVLAN secondary network template", - default="Pvlan_Secondary_Network" + alias="defaultPrivateVlanSecondaryNetworkTemplate", description="Default PVLAN secondary network template", default="Pvlan_Secondary_Network" ) # MACsec macsec: bool = Field( description=( - "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring " - "MACsec on a fabric link if MACsec is enabled on the link." + "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring " "MACsec on a fabric link if MACsec is enabled on the link." ), - default=False + default=False, ) macsec_cipher_suite: MacsecCipherSuiteEnum = Field( - alias="macsecCipherSuite", - description="Configure Cipher Suite", - default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 - ) - macsec_key_string: str = Field( - alias="macsecKeyString", - description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", - default="" + alias="macsecCipherSuite", description="Configure Cipher Suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 ) + macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", default="") macsec_algorithm: MacsecAlgorithmEnum = Field( - alias="macsecAlgorithm", - description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", - default=MacsecAlgorithmEnum.AES_128_CMAC + alias="macsecAlgorithm", description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", default=MacsecAlgorithmEnum.AES_128_CMAC ) macsec_fallback_key_string: str = Field( - alias="macsecFallbackKeyString", - description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", - default="" + alias="macsecFallbackKeyString", description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", default="" ) macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( alias="macsecFallbackAlgorithm", description="MACsec Fallback Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", - default=MacsecAlgorithmEnum.AES_128_CMAC - ) - macsec_report_timer: int = Field( - alias="macsecReportTimer", - description="MACsec Operational Status periodic report timer in minutes", - default=5 + default=MacsecAlgorithmEnum.AES_128_CMAC, ) + macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec Operational Status periodic report timer in minutes", default=5) # Hypershield / Connectivity enable_dpu_pinning: bool = Field( - alias="enableDpuPinning", - description="Enable pinning of VRFs and networks to specific DPUs on smart switches", - default=False - ) - connectivity_domain_name: Optional[str] = Field( - alias="connectivityDomainName", - description="Domain name to connect to Hypershield", - default=None + alias="enableDpuPinning", description="Enable pinning of VRFs and networks to specific DPUs on smart switches", default=False ) + connectivity_domain_name: Optional[str] = Field(alias="connectivityDomainName", description="Domain name to connect to Hypershield", default=None) hypershield_connectivity_proxy_server: Optional[str] = Field( alias="hypershieldConnectivityProxyServer", description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", - default=None + default=None, ) hypershield_connectivity_proxy_server_port: Optional[int] = Field( - alias="hypershieldConnectivityProxyServerPort", - description="Proxy port number for communication with Hypershield", - default=None + alias="hypershieldConnectivityProxyServerPort", description="Proxy port number for communication with Hypershield", default=None ) hypershield_connectivity_source_intf: Optional[str] = Field( - alias="hypershieldConnectivitySourceIntf", - description="Loopback interface on smart switch for communication with Hypershield", - default=None + alias="hypershieldConnectivitySourceIntf", description="Loopback interface on smart switch for communication with Hypershield", default=None ) @field_validator("bgp_asn") @@ -1276,10 +771,7 @@ def validate_bgp_asn(cls, value: Optional[str]) -> Optional[str]: if value is None: return value if not _BGP_ASN_RE.match(value): - raise ValueError( - f"Invalid BGP ASN '{value}'. " - "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." - ) + raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value @field_validator("site_id") @@ -1315,7 +807,7 @@ def validate_mac_address(cls, value: str) -> str: - `ValueError` - If MAC address format is invalid """ - mac_pattern = re.compile(r'^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$') + mac_pattern = re.compile(r"^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$") if not mac_pattern.match(value): raise ValueError(f"Invalid MAC address format, expected xxxx.xxxx.xxxx, got: {value}") return value.lower() @@ -1333,12 +825,7 @@ class FabricEbgpModel(NDBaseModel): - `TypeError` - If field types don't match expected types """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") identifiers: ClassVar[Optional[List[str]]] = ["name"] identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" @@ -1362,15 +849,9 @@ class FabricEbgpModel(NDBaseModel): management: Optional[VxlanEbgpManagementModel] = Field(description="eBGP VXLAN management configuration", default=None) # Optional Advanced Settings - telemetry_settings: Optional[TelemetrySettingsModel] = Field( - alias="telemetrySettings", - description="Telemetry configuration", - default=None - ) + telemetry_settings: Optional[TelemetrySettingsModel] = Field(alias="telemetrySettings", description="Telemetry configuration", default=None) external_streaming_settings: ExternalStreamingSettingsModel = Field( - alias="externalStreamingSettings", - description="External streaming settings", - default_factory=ExternalStreamingSettingsModel + alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) @field_validator("name") @@ -1385,12 +866,12 @@ def validate_fabric_name(cls, value: str) -> str: - `ValueError` - If name contains invalid characters or format """ - if not re.match(r'^[a-zA-Z0-9_-]+$', value): + if not re.match(r"^[a-zA-Z0-9_-]+$", value): raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") return value - @model_validator(mode='after') - def validate_fabric_consistency(self) -> 'FabricEbgpModel': + @model_validator(mode="after") + def validate_fabric_consistency(self) -> "FabricEbgpModel": """ # Summary diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index f35946de..16b54265 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -32,7 +32,6 @@ TelemetryStreamingProtocolEnum, ) - """ # Comprehensive Pydantic models for External Connectivity fabric management via Nexus Dashboard @@ -92,23 +91,10 @@ class LocationModel(NDNestedModel): - `ValueError` - If latitude or longitude are outside valid ranges """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - latitude: float = Field( - description="Latitude coordinate (-90 to 90)", - ge=-90.0, - le=90.0 - ) - longitude: float = Field( - description="Longitude coordinate (-180 to 180)", - ge=-180.0, - le=180.0 - ) + latitude: float = Field(description="Latitude coordinate (-90 to 90)", ge=-90.0, le=90.0) + longitude: float = Field(description="Longitude coordinate (-180 to 180)", ge=-180.0, le=180.0) class NetflowExporterModel(NDNestedModel): @@ -122,12 +108,7 @@ class NetflowExporterModel(NDNestedModel): - `ValueError` - If UDP port is outside valid range or IP address is invalid """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") @@ -147,12 +128,7 @@ class NetflowRecordModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") record_name: str = Field(alias="recordName", description="Name of the netflow record") record_template: str = Field(alias="recordTemplate", description="Template type for the record") @@ -170,12 +146,7 @@ class NetflowMonitorModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") record_name: str = Field(alias="recordName", description="Associated record name") @@ -194,28 +165,15 @@ class NetflowSettingsModel(NDNestedModel): - `ValueError` - If netflow lists are inconsistent with netflow enabled state """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") netflow: bool = Field(description="Enable netflow collection", default=False) netflow_exporter_collection: List[NetflowExporterModel] = Field( - alias="netflowExporterCollection", - description="List of netflow exporters", - default_factory=list - ) - netflow_record_collection: List[NetflowRecordModel] = Field( - alias="netflowRecordCollection", - description="List of netflow records", - default_factory=list + alias="netflowExporterCollection", description="List of netflow exporters", default_factory=list ) + netflow_record_collection: List[NetflowRecordModel] = Field(alias="netflowRecordCollection", description="List of netflow records", default_factory=list) netflow_monitor_collection: List[NetflowMonitorModel] = Field( - alias="netflowMonitorCollection", - description="List of netflow monitors", - default_factory=list + alias="netflowMonitorCollection", description="List of netflow monitors", default_factory=list ) @@ -230,12 +188,7 @@ class BootstrapSubnetModel(NDNestedModel): - `ValueError` - If IP addresses or subnet prefix are invalid """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") @@ -254,19 +207,10 @@ class TelemetryFlowCollectionModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") - traffic_analytics_scope: str = Field( - alias="trafficAnalyticsScope", - description="Traffic analytics scope", - default="intraFabric" - ) + traffic_analytics_scope: str = Field(alias="trafficAnalyticsScope", description="Traffic analytics scope", default="intraFabric") operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") @@ -282,12 +226,7 @@ class TelemetryMicroburstModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") microburst: bool = Field(description="Enable microburst detection", default=False) sensitivity: str = Field(description="Microburst sensitivity level", default="low") @@ -304,12 +243,7 @@ class TelemetryAnalysisSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) @@ -325,12 +259,7 @@ class TelemetryEnergyManagementModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") cost: float = Field(description="Energy cost per unit", default=1.2) @@ -346,12 +275,7 @@ class TelemetryNasExportSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") export_type: str = Field(alias="exportType", description="Export type", default="full") export_format: str = Field(alias="exportFormat", description="Export format", default="json") @@ -368,18 +292,11 @@ class TelemetryNasModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") server: str = Field(description="NAS server address", default="") export_settings: TelemetryNasExportSettingsModel = Field( - alias="exportSettings", - description="NAS export settings", - default_factory=TelemetryNasExportSettingsModel + alias="exportSettings", description="NAS export settings", default_factory=TelemetryNasExportSettingsModel ) @@ -394,35 +311,18 @@ class TelemetrySettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") flow_collection: TelemetryFlowCollectionModel = Field( - alias="flowCollection", - description="Flow collection settings", - default_factory=TelemetryFlowCollectionModel - ) - microburst: TelemetryMicroburstModel = Field( - description="Microburst detection settings", - default_factory=TelemetryMicroburstModel + alias="flowCollection", description="Flow collection settings", default_factory=TelemetryFlowCollectionModel ) + microburst: TelemetryMicroburstModel = Field(description="Microburst detection settings", default_factory=TelemetryMicroburstModel) analysis_settings: TelemetryAnalysisSettingsModel = Field( - alias="analysisSettings", - description="Analysis settings", - default_factory=TelemetryAnalysisSettingsModel - ) - nas: TelemetryNasModel = Field( - description="NAS telemetry configuration", - default_factory=TelemetryNasModel + alias="analysisSettings", description="Analysis settings", default_factory=TelemetryAnalysisSettingsModel ) + nas: TelemetryNasModel = Field(description="NAS telemetry configuration", default_factory=TelemetryNasModel) energy_management: TelemetryEnergyManagementModel = Field( - alias="energyManagement", - description="Energy management settings", - default_factory=TelemetryEnergyManagementModel + alias="energyManagement", description="Energy management settings", default_factory=TelemetryEnergyManagementModel ) @@ -437,22 +337,12 @@ class ExternalStreamingSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) syslog: Dict[str, Any] = Field( - description="Syslog streaming configuration", - default_factory=lambda: { - "collectionSettings": {"anomalies": []}, - "facility": "", - "servers": [] - } + description="Syslog streaming configuration", default_factory=lambda: {"collectionSettings": {"anomalies": []}, "facility": "", "servers": []} ) webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) @@ -472,18 +362,10 @@ class ExternalConnectivityManagementModel(NDNestedModel): - `TypeError` - If required string fields are not provided """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") # Fabric Type (required for discriminated union) - type: Literal[FabricTypeEnum.EXTERNAL_CONNECTIVITY] = Field( - description="Fabric management type", - default=FabricTypeEnum.EXTERNAL_CONNECTIVITY - ) + type: Literal[FabricTypeEnum.EXTERNAL_CONNECTIVITY] = Field(description="Fabric management type", default=FabricTypeEnum.EXTERNAL_CONNECTIVITY) # Core Configuration bgp_asn: str = Field( @@ -510,20 +392,14 @@ class ExternalConnectivityManagementModel(NDNestedModel): # Loopback allow_same_loopback_ip_on_switches: bool = Field( alias="allowSameLoopbackIpOnSwitches", - description=( - "Allow the same loopback IP address to be configured on multiple" - " switches (e.g. RP loopback IP)" - ), + description=("Allow the same loopback IP address to be configured on multiple" " switches (e.g. RP loopback IP)"), default=False, ) # Smart Switch allow_smart_switch_onboarding: bool = Field( alias="allowSmartSwitchOnboarding", - description=( - "Enable onboarding of smart switches to Hypershield" - " for firewall service" - ), + description=("Enable onboarding of smart switches to Hypershield" " for firewall service"), default=False, ) @@ -540,10 +416,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): # CoPP Policy copp_policy: CoppPolicyEnum = Field( alias="coppPolicy", - description=( - "Fabric wide CoPP policy. Customized CoPP policy should be" - " provided when 'manual' is selected." - ), + description=("Fabric wide CoPP policy. Customized CoPP policy should be" " provided when 'manual' is selected."), default=CoppPolicyEnum.MANUAL, ) @@ -591,10 +464,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): ) dns_vrf_collection: List[str] = Field( alias="dnsVrfCollection", - description=( - "DNS Server VRFs. One VRF for all DNS servers or a list of VRFs," - " one per DNS server" - ), + description=("DNS Server VRFs. One VRF for all DNS servers or a list of VRFs," " one per DNS server"), default_factory=list, ) @@ -608,10 +478,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): # DPU Pinning enable_dpu_pinning: bool = Field( alias="enableDpuPinning", - description=( - "Enable pinning of VRFs and networks to specific DPUs" - " on smart switches" - ), + description=("Enable pinning of VRFs and networks to specific DPUs" " on smart switches"), default=False, ) @@ -628,18 +495,12 @@ class ExternalConnectivityManagementModel(NDNestedModel): ) extra_config_nxos_bootstrap: str = Field( alias="extraConfigNxosBootstrap", - description=( - "Additional CLIs required during device bootup/login" - " e.g. AAA/Radius (NX-OS)" - ), + description=("Additional CLIs required during device bootup/login" " e.g. AAA/Radius (NX-OS)"), default="", ) extra_config_xe_bootstrap: str = Field( alias="extraConfigXeBootstrap", - description=( - "Additional CLIs required during device bootup/login" - " e.g. AAA/Radius (IOS-XE)" - ), + description=("Additional CLIs required during device bootup/login" " e.g. AAA/Radius (IOS-XE)"), default="", ) @@ -651,10 +512,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): ) inband_management: bool = Field( alias="inbandManagement", - description=( - "Import switches with reachability over the switch" - " front-panel ports" - ), + description=("Import switches with reachability over the switch" " front-panel ports"), default=False, ) @@ -692,10 +550,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): # Monitored Mode monitored_mode: bool = Field( alias="monitoredMode", - description=( - "If enabled, fabric is only monitored." - " No configuration will be deployed" - ), + description=("If enabled, fabric is only monitored." " No configuration will be deployed"), default=False, ) @@ -732,10 +587,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): # Performance Monitoring performance_monitoring: bool = Field( alias="performanceMonitoring", - description=( - "If enabled, switch metrics are collected through periodic SNMP" - " polling. Alternative to real-time telemetry" - ), + description=("If enabled, switch metrics are collected through periodic SNMP" " polling. Alternative to real-time telemetry"), default=False, ) @@ -750,10 +602,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) ptp_domain_id: int = Field( alias="ptpDomainId", - description=( - "Multiple Independent PTP Clocking Subdomains" - " on a Single Network" - ), + description=("Multiple Independent PTP Clocking Subdomains" " on a Single Network"), default=0, ) ptp_loopback_id: int = Field( @@ -765,20 +614,14 @@ class ExternalConnectivityManagementModel(NDNestedModel): # Backup / Restore real_time_backup: Optional[bool] = Field( alias="realTimeBackup", - description=( - "Hourly Fabric Backup only if there is any config deployment" - " since last backup" - ), + description=("Hourly Fabric Backup only if there is any config deployment" " since last backup"), default=None, ) # Interface Statistics Collection real_time_interface_statistics_collection: bool = Field( alias="realTimeInterfaceStatisticsCollection", - description=( - "Enable Real Time Interface Statistics Collection." - " Valid for NX-OS only" - ), + description=("Enable Real Time Interface Statistics Collection." " Valid for NX-OS only"), default=False, ) @@ -790,10 +633,7 @@ class ExternalConnectivityManagementModel(NDNestedModel): ) scheduled_backup_time: str = Field( alias="scheduledBackupTime", - description=( - "Time (UTC) in 24 hour format to take a daily backup" - " if enabled (00:00 to 23:59)" - ), + description=("Time (UTC) in 24 hour format to take a daily backup" " if enabled (00:00 to 23:59)"), default="", ) @@ -807,33 +647,22 @@ class ExternalConnectivityManagementModel(NDNestedModel): # Sub-Interface sub_interface_dot1q_range: str = Field( alias="subInterfaceDot1qRange", - description=( - "Per aggregation dot1q range for VRF-Lite connectivity" - " (minimum: 2, maximum: 4093)" - ), + description=("Per aggregation dot1q range for VRF-Lite connectivity" " (minimum: 2, maximum: 4093)"), default="2-511", ) # Hypershield / Connectivity - connectivity_domain_name: Optional[str] = Field( - alias="connectivityDomainName", - description="Domain name to connect to Hypershield", - default=None - ) + connectivity_domain_name: Optional[str] = Field(alias="connectivityDomainName", description="Domain name to connect to Hypershield", default=None) hypershield_connectivity_proxy_server: Optional[str] = Field( alias="hypershieldConnectivityProxyServer", description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", - default=None + default=None, ) hypershield_connectivity_proxy_server_port: Optional[int] = Field( - alias="hypershieldConnectivityProxyServerPort", - description="Proxy port number for communication with Hypershield", - default=None + alias="hypershieldConnectivityProxyServerPort", description="Proxy port number for communication with Hypershield", default=None ) hypershield_connectivity_source_intf: Optional[str] = Field( - alias="hypershieldConnectivitySourceIntf", - description="Loopback interface on smart switch for communication with Hypershield", - default=None + alias="hypershieldConnectivitySourceIntf", description="Loopback interface on smart switch for communication with Hypershield", default=None ) @field_validator("bgp_asn") @@ -855,10 +684,7 @@ def validate_bgp_asn(cls, value: str) -> str: - `ValueError` - If the value does not match the expected ASN format """ if not _BGP_ASN_RE.match(value): - raise ValueError( - f"Invalid BGP ASN '{value}'. " - "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." - ) + raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value @@ -878,10 +704,7 @@ class FabricExternalConnectivityModel(NDBaseModel): """ model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" # Allow extra fields from API responses + str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow" # Allow extra fields from API responses ) identifiers: ClassVar[Optional[List[str]]] = ["name"] @@ -916,18 +739,12 @@ class FabricExternalConnectivityModel(NDBaseModel): ) telemetry_source_interface: str = Field( alias="telemetrySourceInterface", - description=( - "Telemetry Source Interface (VLAN id or Loopback id) only valid" - " if Telemetry Collection is set to inBand" - ), + description=("Telemetry Source Interface (VLAN id or Loopback id) only valid" " if Telemetry Collection is set to inBand"), default="", ) telemetry_source_vrf: str = Field( alias="telemetrySourceVrf", - description=( - "VRF over which telemetry is streamed, valid only if telemetry" - " collection is set to inband" - ), + description=("VRF over which telemetry is streamed, valid only if telemetry" " collection is set to inband"), default="", ) security_domain: str = Field( @@ -937,21 +754,12 @@ class FabricExternalConnectivityModel(NDBaseModel): ) # Core Management Configuration - management: Optional[ExternalConnectivityManagementModel] = Field( - description="External Connectivity management configuration", - default=None - ) + management: Optional[ExternalConnectivityManagementModel] = Field(description="External Connectivity management configuration", default=None) # Optional Advanced Settings - telemetry_settings: Optional[TelemetrySettingsModel] = Field( - alias="telemetrySettings", - description="Telemetry configuration", - default=None - ) + telemetry_settings: Optional[TelemetrySettingsModel] = Field(alias="telemetrySettings", description="Telemetry configuration", default=None) external_streaming_settings: ExternalStreamingSettingsModel = Field( - alias="externalStreamingSettings", - description="External streaming settings", - default_factory=ExternalStreamingSettingsModel + alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) @field_validator("name") @@ -966,13 +774,13 @@ def validate_fabric_name(cls, value: str) -> str: - `ValueError` - If name contains invalid characters or format """ - if not re.match(r'^[a-zA-Z0-9_-]+$', value): + if not re.match(r"^[a-zA-Z0-9_-]+$", value): raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") return value - @model_validator(mode='after') - def validate_fabric_consistency(self) -> 'FabricExternalConnectivityModel': + @model_validator(mode="after") + def validate_fabric_consistency(self) -> "FabricExternalConnectivityModel": """ # Summary diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index 20908b0c..ab32e87e 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -11,6 +11,7 @@ # pylint: enable=invalid-name import re + # from datetime import datetime from typing import List, Dict, Any, Optional, ClassVar, Literal @@ -52,7 +53,6 @@ VrfLiteAutoConfigEnum, ) - """ # Comprehensive Pydantic models for iBGP VXLAN fabric management via Nexus Dashboard @@ -114,23 +114,10 @@ class LocationModel(NDNestedModel): - `ValueError` - If latitude or longitude are outside valid ranges """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - latitude: float = Field( - description="Latitude coordinate (-90 to 90)", - ge=-90.0, - le=90.0 - ) - longitude: float = Field( - description="Longitude coordinate (-180 to 180)", - ge=-180.0, - le=180.0 - ) + latitude: float = Field(description="Latitude coordinate (-90 to 90)", ge=-90.0, le=90.0) + longitude: float = Field(description="Longitude coordinate (-180 to 180)", ge=-180.0, le=180.0) class NetflowExporterModel(NDNestedModel): @@ -144,12 +131,7 @@ class NetflowExporterModel(NDNestedModel): - `ValueError` - If UDP port is outside valid range or IP address is invalid """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") @@ -169,12 +151,7 @@ class NetflowRecordModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") record_name: str = Field(alias="recordName", description="Name of the netflow record") record_template: str = Field(alias="recordTemplate", description="Template type for the record") @@ -192,12 +169,7 @@ class NetflowMonitorModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") record_name: str = Field(alias="recordName", description="Associated record name") @@ -216,28 +188,15 @@ class NetflowSettingsModel(NDNestedModel): - `ValueError` - If netflow lists are inconsistent with netflow enabled state """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") netflow: bool = Field(description="Enable netflow collection", default=False) netflow_exporter_collection: List[NetflowExporterModel] = Field( - alias="netflowExporterCollection", - description="List of netflow exporters", - default_factory=list - ) - netflow_record_collection: List[NetflowRecordModel] = Field( - alias="netflowRecordCollection", - description="List of netflow records", - default_factory=list + alias="netflowExporterCollection", description="List of netflow exporters", default_factory=list ) + netflow_record_collection: List[NetflowRecordModel] = Field(alias="netflowRecordCollection", description="List of netflow records", default_factory=list) netflow_monitor_collection: List[NetflowMonitorModel] = Field( - alias="netflowMonitorCollection", - description="List of netflow monitors", - default_factory=list + alias="netflowMonitorCollection", description="List of netflow monitors", default_factory=list ) @@ -252,12 +211,7 @@ class BootstrapSubnetModel(NDNestedModel): - `ValueError` - If IP addresses or subnet prefix are invalid """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") @@ -276,19 +230,10 @@ class TelemetryFlowCollectionModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") - traffic_analytics_scope: str = Field( - alias="trafficAnalyticsScope", - description="Traffic analytics scope", - default="intraFabric" - ) + traffic_analytics_scope: str = Field(alias="trafficAnalyticsScope", description="Traffic analytics scope", default="intraFabric") operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") @@ -304,12 +249,7 @@ class TelemetryMicroburstModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") microburst: bool = Field(description="Enable microburst detection", default=False) sensitivity: str = Field(description="Microburst sensitivity level", default="low") @@ -326,12 +266,7 @@ class TelemetryAnalysisSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) @@ -347,12 +282,7 @@ class TelemetryEnergyManagementModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") cost: float = Field(description="Energy cost per unit", default=1.2) @@ -368,12 +298,7 @@ class TelemetryNasExportSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") export_type: str = Field(alias="exportType", description="Export type", default="full") export_format: str = Field(alias="exportFormat", description="Export format", default="json") @@ -390,18 +315,11 @@ class TelemetryNasModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") server: str = Field(description="NAS server address", default="") export_settings: TelemetryNasExportSettingsModel = Field( - alias="exportSettings", - description="NAS export settings", - default_factory=TelemetryNasExportSettingsModel + alias="exportSettings", description="NAS export settings", default_factory=TelemetryNasExportSettingsModel ) @@ -416,35 +334,18 @@ class TelemetrySettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") flow_collection: TelemetryFlowCollectionModel = Field( - alias="flowCollection", - description="Flow collection settings", - default_factory=TelemetryFlowCollectionModel - ) - microburst: TelemetryMicroburstModel = Field( - description="Microburst detection settings", - default_factory=TelemetryMicroburstModel + alias="flowCollection", description="Flow collection settings", default_factory=TelemetryFlowCollectionModel ) + microburst: TelemetryMicroburstModel = Field(description="Microburst detection settings", default_factory=TelemetryMicroburstModel) analysis_settings: TelemetryAnalysisSettingsModel = Field( - alias="analysisSettings", - description="Analysis settings", - default_factory=TelemetryAnalysisSettingsModel - ) - nas: TelemetryNasModel = Field( - description="NAS telemetry configuration", - default_factory=TelemetryNasModel + alias="analysisSettings", description="Analysis settings", default_factory=TelemetryAnalysisSettingsModel ) + nas: TelemetryNasModel = Field(description="NAS telemetry configuration", default_factory=TelemetryNasModel) energy_management: TelemetryEnergyManagementModel = Field( - alias="energyManagement", - description="Energy management settings", - default_factory=TelemetryEnergyManagementModel + alias="energyManagement", description="Energy management settings", default_factory=TelemetryEnergyManagementModel ) @@ -459,26 +360,12 @@ class ExternalStreamingSettingsModel(NDNestedModel): None """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) - message_bus: List[Dict[str, Any]] = Field( - alias="messageBus", - description="Message bus configuration", - default_factory=list - ) + message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) syslog: Dict[str, Any] = Field( - description="Syslog streaming configuration", - default_factory=lambda: { - "collectionSettings": {"anomalies": []}, - "facility": "", - "servers": [] - } + description="Syslog streaming configuration", default_factory=lambda: {"collectionSettings": {"anomalies": []}, "facility": "", "servers": []} ) webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) @@ -498,26 +385,14 @@ class VxlanIbgpManagementModel(NDNestedModel): - `TypeError` - If required string fields are not provided """ - model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" - ) + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") # Fabric Type (required for discriminated union) - type: Literal[FabricTypeEnum.VXLAN_IBGP] = Field( - description="Type of the fabric", - default=FabricTypeEnum.VXLAN_IBGP - ) + type: Literal[FabricTypeEnum.VXLAN_IBGP] = Field(description="Type of the fabric", default=FabricTypeEnum.VXLAN_IBGP) # Core iBGP Configuration bgp_asn: str = Field(alias="bgpAsn", description="Autonomous system number 1-4294967295 | 1-65535[.0-65535]") - site_id: Optional[str] = Field( - alias="siteId", - description="For EVPN Multi-Site Support. Defaults to Fabric ASN", - default="" - ) + site_id: Optional[str] = Field(alias="siteId", description="For EVPN Multi-Site Support. Defaults to Fabric ASN", default="") # Name under management section is optional for backward compatibility, but if provided must be non-empty string name: Optional[str] = Field(description="Fabric name", min_length=1, max_length=64, default="") @@ -532,241 +407,121 @@ class VxlanIbgpManagementModel(NDNestedModel): # description="VRF Lite IPv6 subnet target mask", ge=112, le=128, default=126) # Network Addressing - bgp_loopback_ip_range: str = Field( - alias="bgpLoopbackIpRange", - description="Typically Loopback0 IP Address Range", - default="10.2.0.0/22" - ) - nve_loopback_ip_range: str = Field( - alias="nveLoopbackIpRange", - description="Typically Loopback1 IP Address Range", - default="10.3.0.0/22" - ) + bgp_loopback_ip_range: str = Field(alias="bgpLoopbackIpRange", description="Typically Loopback0 IP Address Range", default="10.2.0.0/22") + nve_loopback_ip_range: str = Field(alias="nveLoopbackIpRange", description="Typically Loopback1 IP Address Range", default="10.3.0.0/22") anycast_rendezvous_point_ip_range: str = Field( - alias="anycastRendezvousPointIpRange", - description="Anycast or Phantom RP IP Address Range", - default="10.254.254.0/24" + alias="anycastRendezvousPointIpRange", description="Anycast or Phantom RP IP Address Range", default="10.254.254.0/24" ) intra_fabric_subnet_range: str = Field( - alias="intraFabricSubnetRange", - description="Address range to assign numbered and peer link SVI IPs", - default="10.4.0.0/16" + alias="intraFabricSubnetRange", description="Address range to assign numbered and peer link SVI IPs", default="10.4.0.0/16" ) # VLAN and VNI Ranges - l2_vni_range: str = Field( - alias="l2VniRange", - description="Overlay network identifier range (minimum: 1, maximum: 16777214)", - default="30000-49000" - ) - l3_vni_range: str = Field( - alias="l3VniRange", - description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", - default="50000-59000" - ) + l2_vni_range: str = Field(alias="l2VniRange", description="Overlay network identifier range (minimum: 1, maximum: 16777214)", default="30000-49000") + l3_vni_range: str = Field(alias="l3VniRange", description="Overlay VRF identifier range (minimum: 1, maximum: 16777214)", default="50000-59000") network_vlan_range: str = Field( - alias="networkVlanRange", - description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", - default="2300-2999" - ) - vrf_vlan_range: str = Field( - alias="vrfVlanRange", - description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", - default="2000-2299" + alias="networkVlanRange", description="Per Switch Overlay Network VLAN Range (minimum: 2, maximum: 4094)", default="2300-2999" ) + vrf_vlan_range: str = Field(alias="vrfVlanRange", description="Per Switch Overlay VRF VLAN Range (minimum: 2, maximum: 4094)", default="2000-2299") # Overlay Configuration overlay_mode: OverlayModeEnum = Field( - alias="overlayMode", - description="Overlay Mode. VRF/Network configuration using config-profile or CLI", - default=OverlayModeEnum.CLI + alias="overlayMode", description="Overlay Mode. VRF/Network configuration using config-profile or CLI", default=OverlayModeEnum.CLI ) replication_mode: ReplicationModeEnum = Field( - alias="replicationMode", - description="Replication Mode for BUM Traffic", - default=ReplicationModeEnum.MULTICAST + alias="replicationMode", description="Replication Mode for BUM Traffic", default=ReplicationModeEnum.MULTICAST ) multicast_group_subnet: str = Field( alias="multicastGroupSubnet", - description=( - "Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool is used for BUM traffic for " - "each overlay network." - ), - default="239.1.1.0/25" + description=("Multicast pool prefix between 8 to 30. A multicast group ipv4 from this pool is used for BUM traffic for " "each overlay network."), + default="239.1.1.0/25", ) auto_generate_multicast_group_address: bool = Field( alias="autoGenerateMulticastGroupAddress", description="Generate a new multicast group address from the multicast pool using a round-robin approach", - default=False + default=False, ) underlay_multicast_group_address_limit: UnderlayMulticastGroupAddressLimitEnum = Field( alias="underlayMulticastGroupAddressLimit", - description=( - "The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " - "and 512 for versions above 10.2(1)" - ), - default=UnderlayMulticastGroupAddressLimitEnum.V_128 - ) - tenant_routed_multicast: bool = Field( - alias="tenantRoutedMulticast", - description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", - default=False + description=("The maximum supported value is 128 for NX-OS version 10.2(1) or earlier " "and 512 for versions above 10.2(1)"), + default=UnderlayMulticastGroupAddressLimitEnum.V_128, ) + tenant_routed_multicast: bool = Field(alias="tenantRoutedMulticast", description="For Overlay ipv4 Multicast Support In VXLAN Fabrics", default=False) # Underlay Configuration link_state_routing_protocol: LinkStateRoutingProtocolEnum = Field( - alias="linkStateRoutingProtocol", - description="Underlay Routing Protocol. Used for Spine-Leaf Connectivity", - default=LinkStateRoutingProtocolEnum.OSPF + alias="linkStateRoutingProtocol", description="Underlay Routing Protocol. Used for Spine-Leaf Connectivity", default=LinkStateRoutingProtocolEnum.OSPF ) ospf_area_id: str = Field(alias="ospfAreaId", description="OSPF Area Id in IP address format", default="0.0.0.0") fabric_interface_type: FabricInterfaceTypeEnum = Field( - alias="fabricInterfaceType", - description="Numbered(Point-to-Point) or unNumbered", - default=FabricInterfaceTypeEnum.P2P + alias="fabricInterfaceType", description="Numbered(Point-to-Point) or unNumbered", default=FabricInterfaceTypeEnum.P2P ) # Advanced Features - target_subnet_mask: int = Field( - alias="targetSubnetMask", - description="Mask for underlay subnet IP range", - ge=24, - le=31, - default=30 - ) - anycast_gateway_mac: str = Field( - alias="anycastGatewayMac", - description="Shared anycast gateway MAC address for all VTEPs", - default="2020.0000.00aa" - ) - fabric_mtu: int = Field( - alias="fabricMtu", - description="Intra Fabric Interface MTU. Must be an even number", - ge=1500, - le=9216, - default=9216 - ) + target_subnet_mask: int = Field(alias="targetSubnetMask", description="Mask for underlay subnet IP range", ge=24, le=31, default=30) + anycast_gateway_mac: str = Field(alias="anycastGatewayMac", description="Shared anycast gateway MAC address for all VTEPs", default="2020.0000.00aa") + fabric_mtu: int = Field(alias="fabricMtu", description="Intra Fabric Interface MTU. Must be an even number", ge=1500, le=9216, default=9216) l2_host_interface_mtu: int = Field( - alias="l2HostInterfaceMtu", - description="Layer 2 host interface MTU. Must be an even number", - ge=1500, - le=9216, - default=9216 + alias="l2HostInterfaceMtu", description="Layer 2 host interface MTU. Must be an even number", ge=1500, le=9216, default=9216 ) # VPC Configuration vpc_domain_id_range: str = Field( - alias="vpcDomainIdRange", - description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", - default="1-1000" - ) - vpc_peer_link_vlan: str = Field( - alias="vpcPeerLinkVlan", - description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", - default="3600" - ) - vpc_peer_link_enable_native_vlan: bool = Field( - alias="vpcPeerLinkEnableNativeVlan", - description="Enable VpcPeer Link for Native Vlan", - default=False + alias="vpcDomainIdRange", description="vPC Domain id range (minimum: 1, maximum: 1000) to use for new pairings", default="1-1000" ) + vpc_peer_link_vlan: str = Field(alias="vpcPeerLinkVlan", description="VLAN range (minimum: 2, maximum: 4094) for vPC Peer Link SVI", default="3600") + vpc_peer_link_enable_native_vlan: bool = Field(alias="vpcPeerLinkEnableNativeVlan", description="Enable VpcPeer Link for Native Vlan", default=False) vpc_peer_keep_alive_option: VpcPeerKeepAliveOptionEnum = Field( - alias="vpcPeerKeepAliveOption", - description="Use vPC Peer Keep Alive with Loopback or Management", - default=VpcPeerKeepAliveOptionEnum.MANAGEMENT - ) - vpc_auto_recovery_timer: int = Field( - alias="vpcAutoRecoveryTimer", - description="vPC auto recovery timer (in seconds)", - ge=240, - le=3600, - default=360 - ) - vpc_delay_restore_timer: int = Field( - alias="vpcDelayRestoreTimer", - description="vPC delay restore timer (in seconds)", - ge=1, - le=3600, - default=150 + alias="vpcPeerKeepAliveOption", description="Use vPC Peer Keep Alive with Loopback or Management", default=VpcPeerKeepAliveOptionEnum.MANAGEMENT ) + vpc_auto_recovery_timer: int = Field(alias="vpcAutoRecoveryTimer", description="vPC auto recovery timer (in seconds)", ge=240, le=3600, default=360) + vpc_delay_restore_timer: int = Field(alias="vpcDelayRestoreTimer", description="vPC delay restore timer (in seconds)", ge=1, le=3600, default=150) # Loopback Configuration - bgp_loopback_id: int = Field( - alias="bgpLoopbackId", - description="Underlay Routing Loopback Id", - ge=0, - le=1023, - default=0 - ) + bgp_loopback_id: int = Field(alias="bgpLoopbackId", description="Underlay Routing Loopback Id", ge=0, le=1023, default=0) nve_loopback_id: int = Field( alias="nveLoopbackId", description="Underlay VTEP loopback Id associated with the Network Virtualization Edge (nve) interface", ge=0, le=1023, - default=1 + default=1, ) route_reflector_count: RouteReflectorCountEnum = Field( - alias="routeReflectorCount", - description="Number of spines acting as Route-Reflectors", - default=RouteReflectorCountEnum.TWO + alias="routeReflectorCount", description="Number of spines acting as Route-Reflectors", default=RouteReflectorCountEnum.TWO ) # Templates - vrf_template: str = Field( - alias="vrfTemplate", - description="Default overlay VRF template for leafs", - default="Default_VRF_Universal" - ) - network_template: str = Field( - alias="networkTemplate", - description="Default overlay network template for leafs", - default="Default_Network_Universal" - ) + vrf_template: str = Field(alias="vrfTemplate", description="Default overlay VRF template for leafs", default="Default_VRF_Universal") + network_template: str = Field(alias="networkTemplate", description="Default overlay network template for leafs", default="Default_Network_Universal") vrf_extension_template: str = Field( - alias="vrfExtensionTemplate", - description="Default overlay VRF template for borders", - default="Default_VRF_Extension_Universal" + alias="vrfExtensionTemplate", description="Default overlay VRF template for borders", default="Default_VRF_Extension_Universal" ) network_extension_template: str = Field( - alias="networkExtensionTemplate", - description="Default overlay network template for borders", - default="Default_Network_Extension_Universal" + alias="networkExtensionTemplate", description="Default overlay network template for borders", default="Default_Network_Extension_Universal" ) # Optional Advanced Settings performance_monitoring: bool = Field( alias="performanceMonitoring", - description=( - "If enabled, switch metrics are collected through periodic SNMP polling. " - "Alternative to real-time telemetry" - ), - default=False + description=("If enabled, switch metrics are collected through periodic SNMP polling. " "Alternative to real-time telemetry"), + default=False, ) tenant_dhcp: bool = Field(alias="tenantDhcp", description="Enable Tenant DHCP", default=True) advertise_physical_ip: bool = Field( - alias="advertisePhysicalIp", - description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", - default=False + alias="advertisePhysicalIp", description="For Primary VTEP IP Advertisement As Next-Hop Of Prefix Routes", default=False ) advertise_physical_ip_on_border: bool = Field( alias="advertisePhysicalIpOnBorder", - description=( - "Enable advertise-pip on vPC borders and border gateways only. Applicable only when vPC advertise-pip is " - "not enabled" - ), - default=True + description=("Enable advertise-pip on vPC borders and border gateways only. Applicable only when vPC advertise-pip is " "not enabled"), + default=True, ) # Protocol Settings - bgp_authentication: bool = Field( - alias="bgpAuthentication", - description="Enables or disables the BGP authentication", - default=False - ) + bgp_authentication: bool = Field(alias="bgpAuthentication", description="Enables or disables the BGP authentication", default=False) bgp_authentication_key_type: BgpAuthenticationKeyTypeEnum = Field( alias="bgpAuthenticationKeyType", description="BGP key encryption type: 3 - 3DES, 6 - Cisco type 6, 7 - Cisco type 7", - default=BgpAuthenticationKeyTypeEnum.THREE_DES + default=BgpAuthenticationKeyTypeEnum.THREE_DES, ) bfd: bool = Field(description="Enable BFD. Valid for IPv4 Underlay only", default=False) bfd_ibgp: bool = Field(alias="bfdIbgp", description="Enable BFD For iBGP", default=False) @@ -774,145 +529,67 @@ class VxlanIbgpManagementModel(NDNestedModel): # Management Settings nxapi: bool = Field(description="Enable NX-API over HTTPS", default=False) nxapi_http: bool = Field(alias="nxapiHttp", description="Enable NX-API over HTTP", default=False) - nxapi_https_port: int = Field( - alias="nxapiHttpsPort", - description="HTTPS port for NX-API", - ge=1, - le=65535, - default=443 - ) + nxapi_https_port: int = Field(alias="nxapiHttpsPort", description="HTTPS port for NX-API", ge=1, le=65535, default=443) nxapi_http_port: int = Field(alias="nxapiHttpPort", description="HTTP port for NX-API", ge=1, le=65535, default=80) # Bootstrap Settings day0_bootstrap: bool = Field(alias="day0Bootstrap", description="Automatic IP Assignment For POAP", default=False) bootstrap_subnet_collection: List[BootstrapSubnetModel] = Field( - alias="bootstrapSubnetCollection", - description="List of IPv4 or IPv6 subnets to be used for bootstrap", - default_factory=list + alias="bootstrapSubnetCollection", description="List of IPv4 or IPv6 subnets to be used for bootstrap", default_factory=list ) # Netflow Settings netflow_settings: NetflowSettingsModel = Field( - alias="netflowSettings", - description="Settings associated with netflow", - default_factory=NetflowSettingsModel + alias="netflowSettings", description="Settings associated with netflow", default_factory=NetflowSettingsModel ) # Multicast Settings rendezvous_point_count: RendezvousPointCountEnum = Field( - alias="rendezvousPointCount", - description="Number of spines acting as Rendezvous-Points (RPs)", - default=RendezvousPointCountEnum.TWO - ) - rendezvous_point_loopback_id: int = Field( - alias="rendezvousPointLoopbackId", - description="Rendezvous point loopback Id", - ge=0, - le=1023, - default=254 + alias="rendezvousPointCount", description="Number of spines acting as Rendezvous-Points (RPs)", default=RendezvousPointCountEnum.TWO ) + rendezvous_point_loopback_id: int = Field(alias="rendezvousPointLoopbackId", description="Rendezvous point loopback Id", ge=0, le=1023, default=254) # System Settings snmp_trap: bool = Field(alias="snmpTrap", description="Configure ND as a receiver for SNMP traps", default=True) cdp: bool = Field(description="Enable CDP on management interface", default=False) real_time_interface_statistics_collection: bool = Field( - alias="realTimeInterfaceStatisticsCollection", - description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", - default=False + alias="realTimeInterfaceStatisticsCollection", description="Enable Real Time Interface Statistics Collection. Valid for NX-OS only", default=False ) tcam_allocation: bool = Field( - alias="tcamAllocation", - description="TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled", - default=True + alias="tcamAllocation", description="TCAM commands are automatically generated for VxLAN and vPC Fabric Peering when Enabled", default=True ) # VPC Extended Configuration vpc_peer_link_port_channel_id: str = Field( - alias="vpcPeerLinkPortChannelId", - description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", - default="500" + alias="vpcPeerLinkPortChannelId", description="vPC Peer Link Port Channel ID (minimum: 1, maximum: 4096)", default="500" ) vpc_ipv6_neighbor_discovery_sync: bool = Field( - alias="vpcIpv6NeighborDiscoverySync", - description="Enable IPv6 ND synchronization between vPC peers", - default=True - ) - vpc_layer3_peer_router: bool = Field( - alias="vpcLayer3PeerRouter", - description="Enable Layer-3 Peer-Router on all Leaf switches", - default=True - ) - vpc_tor_delay_restore_timer: int = Field( - alias="vpcTorDelayRestoreTimer", - description="vPC delay restore timer for ToR switches (in seconds)", - default=30 + alias="vpcIpv6NeighborDiscoverySync", description="Enable IPv6 ND synchronization between vPC peers", default=True ) + vpc_layer3_peer_router: bool = Field(alias="vpcLayer3PeerRouter", description="Enable Layer-3 Peer-Router on all Leaf switches", default=True) + vpc_tor_delay_restore_timer: int = Field(alias="vpcTorDelayRestoreTimer", description="vPC delay restore timer for ToR switches (in seconds)", default=30) fabric_vpc_domain_id: bool = Field( - alias="fabricVpcDomainId", - description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", - default=False - ) - shared_vpc_domain_id: int = Field( - alias="sharedVpcDomainId", - description="vPC Domain Id to be used on all vPC pairs", - default=1 - ) - fabric_vpc_qos: bool = Field( - alias="fabricVpcQos", - description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", - default=False + alias="fabricVpcDomainId", description="Enable the same vPC Domain Id for all vPC Pairs. Not Recommended.", default=False ) + shared_vpc_domain_id: int = Field(alias="sharedVpcDomainId", description="vPC Domain Id to be used on all vPC pairs", default=1) + fabric_vpc_qos: bool = Field(alias="fabricVpcQos", description="Qos on spines for guaranteed delivery of vPC Fabric Peering communication", default=False) fabric_vpc_qos_policy_name: str = Field( - alias="fabricVpcQosPolicyName", - description="Qos Policy name should be same on all spines", - default="spine_qos_for_fabric_vpc_peering" - ) - enable_peer_switch: bool = Field( - alias="enablePeerSwitch", - description="Enable the vPC peer-switch feature on ToR switches", - default=False + alias="fabricVpcQosPolicyName", description="Qos Policy name should be same on all spines", default="spine_qos_for_fabric_vpc_peering" ) + enable_peer_switch: bool = Field(alias="enablePeerSwitch", description="Enable the vPC peer-switch feature on ToR switches", default=False) # Bootstrap / Day-0 / DHCP - local_dhcp_server: bool = Field( - alias="localDhcpServer", - description="Automatic IP Assignment For POAP From Local DHCP Server", - default=False - ) + local_dhcp_server: bool = Field(alias="localDhcpServer", description="Automatic IP Assignment For POAP From Local DHCP Server", default=False) dhcp_protocol_version: DhcpProtocolVersionEnum = Field( - alias="dhcpProtocolVersion", - description="IP protocol version for Local DHCP Server", - default=DhcpProtocolVersionEnum.DHCPV4 - ) - dhcp_start_address: str = Field( - alias="dhcpStartAddress", - description="DHCP Scope Start Address For Switch POAP", - default="" - ) - dhcp_end_address: str = Field( - alias="dhcpEndAddress", - description="DHCP Scope End Address For Switch POAP", - default="" - ) - management_gateway: str = Field( - alias="managementGateway", - description="Default Gateway For Management VRF On The Switch", - default="" - ) - management_ipv4_prefix: int = Field( - alias="managementIpv4Prefix", - description="Switch Mgmt IP Subnet Prefix if ipv4", - default=24 - ) - management_ipv6_prefix: int = Field( - alias="managementIpv6Prefix", - description="Switch Management IP Subnet Prefix if ipv6", - default=64 + alias="dhcpProtocolVersion", description="IP protocol version for Local DHCP Server", default=DhcpProtocolVersionEnum.DHCPV4 ) + dhcp_start_address: str = Field(alias="dhcpStartAddress", description="DHCP Scope Start Address For Switch POAP", default="") + dhcp_end_address: str = Field(alias="dhcpEndAddress", description="DHCP Scope End Address For Switch POAP", default="") + management_gateway: str = Field(alias="managementGateway", description="Default Gateway For Management VRF On The Switch", default="") + management_ipv4_prefix: int = Field(alias="managementIpv4Prefix", description="Switch Mgmt IP Subnet Prefix if ipv4", default=24) + management_ipv6_prefix: int = Field(alias="managementIpv6Prefix", description="Switch Management IP Subnet Prefix if ipv6", default=64) extra_config_nxos_bootstrap: str = Field( - alias="extraConfigNxosBootstrap", - description="Additional CLIs required during device bootup/login e.g. AAA/Radius", - default="" + alias="extraConfigNxosBootstrap", description="Additional CLIs required during device bootup/login e.g. AAA/Radius", default="" ) un_numbered_bootstrap_loopback_id: int = Field( alias="unNumberedBootstrapLoopbackId", description="Bootstrap Seed Switch Loopback Interface ID", default=253 @@ -920,103 +597,51 @@ class VxlanIbgpManagementModel(NDNestedModel): un_numbered_dhcp_start_address: str = Field( alias="unNumberedDhcpStartAddress", description="Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool", - default="" + default="", ) un_numbered_dhcp_end_address: str = Field( - alias="unNumberedDhcpEndAddress", - description="Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool", - default="" - ) - inband_management: bool = Field( - alias="inbandManagement", - description="Manage switches with only Inband connectivity", - default=False - ) - inband_dhcp_servers: List[str] = Field( - alias="inbandDhcpServers", - description="List of external DHCP server IP addresses (Max 3)", - default_factory=list + alias="unNumberedDhcpEndAddress", description="Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool", default="" ) + inband_management: bool = Field(alias="inbandManagement", description="Manage switches with only Inband connectivity", default=False) + inband_dhcp_servers: List[str] = Field(alias="inbandDhcpServers", description="List of external DHCP server IP addresses (Max 3)", default_factory=list) seed_switch_core_interfaces: List[str] = Field( - alias="seedSwitchCoreInterfaces", - description="Seed switch fabric interfaces. Core-facing interface list on seed switch", - default_factory=list + alias="seedSwitchCoreInterfaces", description="Seed switch fabric interfaces. Core-facing interface list on seed switch", default_factory=list ) spine_switch_core_interfaces: List[str] = Field( - alias="spineSwitchCoreInterfaces", - description="Spine switch fabric interfaces. Core-facing interface list on all spines", - default_factory=list + alias="spineSwitchCoreInterfaces", description="Spine switch fabric interfaces. Core-facing interface list on all spines", default_factory=list ) # Backup / Restore - real_time_backup: bool = Field( - alias="realTimeBackup", - description="Backup hourly only if there is any config deployment since last backup", - default=False - ) - scheduled_backup: bool = Field( - alias="scheduledBackup", - description="Enable backup at the specified time daily", - default=False - ) + real_time_backup: bool = Field(alias="realTimeBackup", description="Backup hourly only if there is any config deployment since last backup", default=False) + scheduled_backup: bool = Field(alias="scheduledBackup", description="Enable backup at the specified time daily", default=False) scheduled_backup_time: str = Field( - alias="scheduledBackupTime", - description="Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)", - default="" + alias="scheduledBackupTime", description="Time (UTC) in 24 hour format to take a daily backup if enabled (00:00 to 23:59)", default="" ) # IPv6 / Dual-Stack - underlay_ipv6: bool = Field( - alias="underlayIpv6", - description="If not enabled, IPv4 underlay is used", - default=False - ) + underlay_ipv6: bool = Field(alias="underlayIpv6", description="If not enabled, IPv4 underlay is used", default=False) ipv6_multicast_group_subnet: str = Field( - alias="ipv6MulticastGroupSubnet", - description="IPv6 Multicast address with prefix 112 to 128", - default="ff1e::/121" + alias="ipv6MulticastGroupSubnet", description="IPv6 Multicast address with prefix 112 to 128", default="ff1e::/121" ) tenant_routed_multicast_ipv6: bool = Field( - alias="tenantRoutedMulticastIpv6", - description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", - default=False - ) - ipv6_link_local: bool = Field( - alias="ipv6LinkLocal", - description="If not enabled, Spine-Leaf interfaces will use global IPv6 addresses", - default=True - ) - ipv6_subnet_target_mask: int = Field( - alias="ipv6SubnetTargetMask", - description="Mask for Underlay Subnet IPv6 Range", - default=126 + alias="tenantRoutedMulticastIpv6", description="For Overlay IPv6 Multicast Support In VXLAN Fabrics", default=False ) + ipv6_link_local: bool = Field(alias="ipv6LinkLocal", description="If not enabled, Spine-Leaf interfaces will use global IPv6 addresses", default=True) + ipv6_subnet_target_mask: int = Field(alias="ipv6SubnetTargetMask", description="Mask for Underlay Subnet IPv6 Range", default=126) ipv6_subnet_range: str = Field( - alias="ipv6SubnetRange", - description="Underlay Subnet ipv6 range to assign Numbered and Peer Link SVI IPs", - default="fd00::a04:0/112" - ) - bgp_loopback_ipv6_range: str = Field( - alias="bgpLoopbackIpv6Range", - description="Typically Loopback0 IPv6 Address Range", - default="fd00::a02:0/119" + alias="ipv6SubnetRange", description="Underlay Subnet ipv6 range to assign Numbered and Peer Link SVI IPs", default="fd00::a04:0/112" ) + bgp_loopback_ipv6_range: str = Field(alias="bgpLoopbackIpv6Range", description="Typically Loopback0 IPv6 Address Range", default="fd00::a02:0/119") nve_loopback_ipv6_range: str = Field( - alias="nveLoopbackIpv6Range", - description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", - default="fd00::a03:0/118" + alias="nveLoopbackIpv6Range", description="Typically Loopback1 and Anycast Loopback IPv6 Address Range", default="fd00::a03:0/118" ) ipv6_anycast_rendezvous_point_ip_range: str = Field( - alias="ipv6AnycastRendezvousPointIpRange", - description="Anycast RP IPv6 Address Range", - default="fd00::254:254:0/118" + alias="ipv6AnycastRendezvousPointIpRange", description="Anycast RP IPv6 Address Range", default="fd00::254:254:0/118" ) # Multicast / Rendezvous Point Extended mvpn_vrf_route_import_id: bool = Field( - alias="mvpnVrfRouteImportId", - description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", - default=True + alias="mvpnVrfRouteImportId", description="Enable MVPN VRI ID Generation For Tenant Routed Multicast With IPv4 Underlay", default=True ) mvpn_vrf_route_import_id_range: str = Field( alias="mvpnVrfRouteImportIdRange", @@ -1024,59 +649,39 @@ class VxlanIbgpManagementModel(NDNestedModel): "MVPN VRI ID (minimum: 1, maximum: 65535) for vPC, applicable when TRM enabled with IPv6 underlay, or " "mvpnVrfRouteImportId enabled with IPv4 underlay" ), - default="" + default="", ) vrf_route_import_id_reallocation: bool = Field( - alias="vrfRouteImportIdReallocation", - description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", - default=False + alias="vrfRouteImportIdReallocation", description="One time VRI ID re-allocation based on 'MVPN VRI ID Range'", default=False ) l3vni_multicast_group: str = Field( - alias="l3vniMulticastGroup", - description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", - default="239.1.1.0" + alias="l3vniMulticastGroup", description="Default Underlay Multicast group IPv4 address assigned for every overlay VRF", default="239.1.1.0" ) l3_vni_ipv6_multicast_group: str = Field( - alias="l3VniIpv6MulticastGroup", - description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", - default="ff1e::" + alias="l3VniIpv6MulticastGroup", description="Default Underlay Multicast group IP6 address assigned for every overlay VRF", default="ff1e::" ) rendezvous_point_mode: RendezvousPointModeEnum = Field( - alias="rendezvousPointMode", - description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", - default=RendezvousPointModeEnum.ASM + alias="rendezvousPointMode", description="Multicast rendezvous point Mode. For ipv6 underlay, please use asm only", default=RendezvousPointModeEnum.ASM ) phantom_rendezvous_point_loopback_id1: int = Field( - alias="phantomRendezvousPointLoopbackId1", - description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", - default=2 + alias="phantomRendezvousPointLoopbackId1", description="Underlay phantom rendezvous point loopback primary Id for PIM Bi-dir deployments", default=2 ) phantom_rendezvous_point_loopback_id2: int = Field( - alias="phantomRendezvousPointLoopbackId2", - description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", - default=3 + alias="phantomRendezvousPointLoopbackId2", description="Underlay phantom rendezvous point loopback secondary Id for PIM Bi-dir deployments", default=3 ) phantom_rendezvous_point_loopback_id3: int = Field( - alias="phantomRendezvousPointLoopbackId3", - description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", - default=4 + alias="phantomRendezvousPointLoopbackId3", description="Underlay phantom rendezvous point loopback tertiary Id for PIM Bi-dir deployments", default=4 ) phantom_rendezvous_point_loopback_id4: int = Field( - alias="phantomRendezvousPointLoopbackId4", - description="Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments", - default=5 + alias="phantomRendezvousPointLoopbackId4", description="Underlay phantom rendezvous point loopback quaternary Id for PIM Bi-dir deployments", default=5 ) anycast_loopback_id: int = Field( - alias="anycastLoopbackId", - description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", - default=10 + alias="anycastLoopbackId", description="Underlay Anycast Loopback Id. Used for vPC Peering in VXLANv6 Fabrics", default=10 ) # VRF Lite / Sub-Interface sub_interface_dot1q_range: str = Field( - alias="subInterfaceDot1qRange", - description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", - default="2-511" + alias="subInterfaceDot1qRange", description="Per aggregation dot1q range for VRF-Lite connectivity (minimum: 2, maximum: 4093)", default="2-511" ) vrf_lite_auto_config: VrfLiteAutoConfigEnum = Field( alias="vrfLiteAutoConfig", @@ -1086,25 +691,17 @@ class VxlanIbgpManagementModel(NDNestedModel): "Fabric and edge routers in External Fabric. The IP address is taken from the 'VRF Lite Subnet IP Range' " "pool." ), - default=VrfLiteAutoConfigEnum.MANUAL - ) - vrf_lite_subnet_range: str = Field( - alias="vrfLiteSubnetRange", - description="Address range to assign P2P Interfabric Connections", - default="10.33.0.0/16" - ) - vrf_lite_subnet_target_mask: int = Field( - alias="vrfLiteSubnetTargetMask", - description="VRF Lite Subnet Mask", - default=30 + default=VrfLiteAutoConfigEnum.MANUAL, ) + vrf_lite_subnet_range: str = Field(alias="vrfLiteSubnetRange", description="Address range to assign P2P Interfabric Connections", default="10.33.0.0/16") + vrf_lite_subnet_target_mask: int = Field(alias="vrfLiteSubnetTargetMask", description="VRF Lite Subnet Mask", default=30) auto_unique_vrf_lite_ip_prefix: bool = Field( alias="autoUniqueVrfLiteIpPrefix", description=( "When enabled, IP prefix allocated to the VRF LITE IFC is not reused on VRF extension over VRF LITE IFC. " "Instead, unique IP Subnet is allocated for each VRF extension over VRF LITE IFC." ), - default=False + default=False, ) auto_symmetric_vrf_lite: bool = Field( alias="autoSymmetricVrfLite", @@ -1113,7 +710,7 @@ class VxlanIbgpManagementModel(NDNestedModel): "neighbor devices. If set, auto created VRF Lite IFC links will have " "'Auto Deploy for Peer' enabled." ), - default=False + default=False, ) auto_vrf_lite_default_vrf: bool = Field( alias="autoVrfLiteDefaultVrf", @@ -1121,7 +718,7 @@ class VxlanIbgpManagementModel(NDNestedModel): "For ipv4 underlay, whether to auto generate BGP peering in Default VRF for VRF Lite IFC auto deployment " "option. If set, will auto create VRF Lite Inter-Fabric links with 'Auto Deploy Default VRF' knob enabled" ), - default=False + default=False, ) auto_symmetric_default_vrf: bool = Field( alias="autoSymmetricDefaultVrf", @@ -1129,15 +726,12 @@ class VxlanIbgpManagementModel(NDNestedModel): "Whether to auto generate Default VRF interface and BGP peering configuration on managed neighbor devices. " "If set, auto created VRF Lite IFC links will have 'Auto Deploy Default VRF for Peer' enabled." ), - default=False + default=False, ) default_vrf_redistribution_bgp_route_map: str = Field( alias="defaultVrfRedistributionBgpRouteMap", - description=( - "Route Map used to redistribute BGP routes to IGP in default vrf " - "in auto created VRF Lite IFC links" - ), - default="extcon-rmap-filter" + description=("Route Map used to redistribute BGP routes to IGP in default vrf " "in auto created VRF Lite IFC links"), + default="extcon-rmap-filter", ) # Per-VRF Loopback @@ -1148,22 +742,16 @@ class VxlanIbgpManagementModel(NDNestedModel): "loopback on existing VRF attachments and also when Edit, QuickAttach, or Multiattach actions are " "performed. Provisioned loopbacks cannot be deleted until VRFs are unattached." ), - default=False + default=False, ) per_vrf_loopback_ip_range: str = Field( - alias="perVrfLoopbackIpRange", - description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", - default="10.5.0.0/22" + alias="perVrfLoopbackIpRange", description="Prefix pool to assign IPv4 addresses to loopbacks on VTEPs on a per VRF basis", default="10.5.0.0/22" ) per_vrf_loopback_auto_provision_ipv6: bool = Field( - alias="perVrfLoopbackAutoProvisionIpv6", - description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", - default=False + alias="perVrfLoopbackAutoProvisionIpv6", description="Auto provision an IPv6 loopback on a VTEP on VRF attachment.", default=False ) per_vrf_loopback_ipv6_range: str = Field( - alias="perVrfLoopbackIpv6Range", - description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", - default="fd00::a05:0/112" + alias="perVrfLoopbackIpv6Range", description="Prefix pool to assign IPv6 addresses to loopbacks on VTEPs on a per VRF basis", default="fd00::a05:0/112" ) per_vrf_unique_loopback_auto_provision: bool = Field( alias="perVrfUniqueLoopbackAutoProvision", @@ -1173,79 +761,41 @@ class VxlanIbgpManagementModel(NDNestedModel): "auto-provisioning are mutually exclusive. Provisioned unique loopbacks will be released upon VRF " "unattachment or per request." ), - default=False + default=False, ) per_vrf_unique_loopback_ip_range: str = Field( alias="perVrfUniqueLoopbackIpRange", description="Prefix pool to assign unique IPv4 addresses to loopbacks on VTEPs on a per VRF basis", - default="10.6.0.0/22" + default="10.6.0.0/22", ) per_vrf_unique_loopback_auto_provision_v6: bool = Field( - alias="perVrfUniqueLoopbackAutoProvisionV6", - description="Auto provision a unique IPV6 loopback on a VTEP on VRF attachment.", - default=False + alias="perVrfUniqueLoopbackAutoProvisionV6", description="Auto provision a unique IPV6 loopback on a VTEP on VRF attachment.", default=False ) per_vrf_unique_loopback_ipv6_range: str = Field( alias="perVrfUniqueLoopbackIpv6Range", description="Prefix pool to assign unique IPv6 addresses to loopbacks on VTEPs on a per VRF basis", - default="fd00::a06:0/112" + default="fd00::a06:0/112", ) # Authentication — BGP Extended - bgp_authentication_key: str = Field( - alias="bgpAuthenticationKey", - description="Encrypted BGP authentication key based on type", - default="" - ) + bgp_authentication_key: str = Field(alias="bgpAuthenticationKey", description="Encrypted BGP authentication key based on type", default="") # Authentication — PIM - pim_hello_authentication: bool = Field( - alias="pimHelloAuthentication", - description="Valid for IPv4 Underlay only", - default=False - ) - pim_hello_authentication_key: str = Field( - alias="pimHelloAuthenticationKey", - description="3DES Encrypted", - default="" - ) + pim_hello_authentication: bool = Field(alias="pimHelloAuthentication", description="Valid for IPv4 Underlay only", default=False) + pim_hello_authentication_key: str = Field(alias="pimHelloAuthenticationKey", description="3DES Encrypted", default="") # Authentication — BFD - bfd_authentication: bool = Field( - alias="bfdAuthentication", - description="Enable BFD Authentication. Valid for P2P Interfaces only", - default=False - ) - bfd_authentication_key_id: int = Field( - alias="bfdAuthenticationKeyId", - description="BFD Authentication Key ID", - default=100 - ) - bfd_authentication_key: str = Field( - alias="bfdAuthenticationKey", - description="Encrypted SHA1 secret value", - default="" - ) + bfd_authentication: bool = Field(alias="bfdAuthentication", description="Enable BFD Authentication. Valid for P2P Interfaces only", default=False) + bfd_authentication_key_id: int = Field(alias="bfdAuthenticationKeyId", description="BFD Authentication Key ID", default=100) + bfd_authentication_key: str = Field(alias="bfdAuthenticationKey", description="Encrypted SHA1 secret value", default="") bfd_ospf: bool = Field(alias="bfdOspf", description="Enable BFD For OSPF", default=False) bfd_isis: bool = Field(alias="bfdIsis", description="Enable BFD For ISIS", default=False) bfd_pim: bool = Field(alias="bfdPim", description="Enable BFD For PIM", default=False) # Authentication — OSPF - ospf_authentication: bool = Field( - alias="ospfAuthentication", - description="Enable OSPF Authentication", - default=False - ) - ospf_authentication_key_id: int = Field( - alias="ospfAuthenticationKeyId", - description="(Min:0, Max:255)", - default=127 - ) - ospf_authentication_key: str = Field( - alias="ospfAuthenticationKey", - description="OSPF Authentication Key. 3DES Encrypted", - default="" - ) + ospf_authentication: bool = Field(alias="ospfAuthentication", description="Enable OSPF Authentication", default=False) + ospf_authentication_key_id: int = Field(alias="ospfAuthenticationKeyId", description="(Min:0, Max:255)", default=127) + ospf_authentication_key: str = Field(alias="ospfAuthenticationKey", description="OSPF Authentication Key. 3DES Encrypted", default="") # IS-IS isis_level: IsisLevelEnum = Field(alias="isisLevel", description="IS-IS Level", default=IsisLevelEnum.LEVEL_2) @@ -1257,78 +807,45 @@ class VxlanIbgpManagementModel(NDNestedModel): "settings and is different from the " "current area number, these NETs will be updated by Recalculate and Deploy." ), - default="0001" + default="0001", ) isis_point_to_point: bool = Field( - alias="isisPointToPoint", - description="This will enable network point-to-point on fabric interfaces which are numbered", - default=True - ) - isis_authentication: bool = Field( - alias="isisAuthentication", - description="Enable IS-IS Authentication", - default=False - ) - isis_authentication_keychain_name: str = Field( - alias="isisAuthenticationKeychainName", description="IS-IS Authentication Keychain Name", default="" - ) - isis_authentication_keychain_key_id: int = Field( - alias="isisAuthenticationKeychainKeyId", description="IS-IS Authentication Key ID", default=127 - ) - isis_authentication_key: str = Field( - alias="isisAuthenticationKey", - description="IS-IS Authentication Key. Cisco Type 7 Encrypted", - default="" + alias="isisPointToPoint", description="This will enable network point-to-point on fabric interfaces which are numbered", default=True ) + isis_authentication: bool = Field(alias="isisAuthentication", description="Enable IS-IS Authentication", default=False) + isis_authentication_keychain_name: str = Field(alias="isisAuthenticationKeychainName", description="IS-IS Authentication Keychain Name", default="") + isis_authentication_keychain_key_id: int = Field(alias="isisAuthenticationKeychainKeyId", description="IS-IS Authentication Key ID", default=127) + isis_authentication_key: str = Field(alias="isisAuthenticationKey", description="IS-IS Authentication Key. Cisco Type 7 Encrypted", default="") isis_overload: bool = Field( - alias="isisOverload", - description="Set IS-IS Overload Bit. When enabled, set the overload bit for an elapsed time after a reload", - default=True + alias="isisOverload", description="Set IS-IS Overload Bit. When enabled, set the overload bit for an elapsed time after a reload", default=True ) isis_overload_elapse_time: int = Field( - alias="isisOverloadElapseTime", - description="IS-IS Overload Bit Elapsed Time. Clear the overload bit after an elapsed time in seconds", - default=60 + alias="isisOverloadElapseTime", description="IS-IS Overload Bit Elapsed Time. Clear the overload bit after an elapsed time in seconds", default=60 ) # MACsec macsec: bool = Field( description=( - "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring MACsec on a fabric link if " - "MACsec is enabled on the link." + "Enable MACsec in the fabric. MACsec fabric parameters are used for configuring MACsec on a fabric link if " "MACsec is enabled on the link." ), - default=False + default=False, ) macsec_cipher_suite: MacsecCipherSuiteEnum = Field( - alias="macsecCipherSuite", - description="Configure Cipher Suite", - default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 - ) - macsec_key_string: str = Field( - alias="macsecKeyString", - description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", - default="" + alias="macsecCipherSuite", description="Configure Cipher Suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 ) + macsec_key_string: str = Field(alias="macsecKeyString", description="MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", default="") macsec_algorithm: MacsecAlgorithmEnum = Field( - alias="macsecAlgorithm", - description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", - default=MacsecAlgorithmEnum.AES_128_CMAC + alias="macsecAlgorithm", description="MACsec Primary Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", default=MacsecAlgorithmEnum.AES_128_CMAC ) macsec_fallback_key_string: str = Field( - alias="macsecFallbackKeyString", - description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", - default="" + alias="macsecFallbackKeyString", description="MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String", default="" ) macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( alias="macsecFallbackAlgorithm", description="MACsec Fallback Cryptographic Algorithm. AES_128_CMAC or AES_256_CMAC", - default=MacsecAlgorithmEnum.AES_128_CMAC - ) - macsec_report_timer: int = Field( - alias="macsecReportTimer", - description="MACsec Operational Status periodic report timer in minutes", - default=5 + default=MacsecAlgorithmEnum.AES_128_CMAC, ) + macsec_report_timer: int = Field(alias="macsecReportTimer", description="MACsec Operational Status periodic report timer in minutes", default=5) # VRF Lite MACsec vrf_lite_macsec: bool = Field( @@ -1337,68 +854,44 @@ class VxlanIbgpManagementModel(NDNestedModel): "Enable MACsec on DCI links. DCI MACsec fabric parameters are used for configuring MACsec on a DCI link if " "'Use Link MACsec Setting' is disabled on the link." ), - default=False + default=False, ) vrf_lite_macsec_cipher_suite: MacsecCipherSuiteEnum = Field( - alias="vrfLiteMacsecCipherSuite", - description="DCI MACsec Cipher Suite", - default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 + alias="vrfLiteMacsecCipherSuite", description="DCI MACsec Cipher Suite", default=MacsecCipherSuiteEnum.GCM_AES_XPN_256 ) vrf_lite_macsec_key_string: str = Field( - alias="vrfLiteMacsecKeyString", - description="DCI MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", - default="" + alias="vrfLiteMacsecKeyString", description="DCI MACsec Primary Key String. Cisco Type 7 Encrypted Octet String", default="" ) vrf_lite_macsec_algorithm: MacsecAlgorithmEnum = Field( - alias="vrfLiteMacsecAlgorithm", - description="DCI MACsec Primary Cryptographic Algorithm", - default=MacsecAlgorithmEnum.AES_128_CMAC + alias="vrfLiteMacsecAlgorithm", description="DCI MACsec Primary Cryptographic Algorithm", default=MacsecAlgorithmEnum.AES_128_CMAC ) vrf_lite_macsec_fallback_key_string: str = Field( alias="vrfLiteMacsecFallbackKeyString", - description=( - "DCI MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String. " - "This parameter is used when DCI link has QKD disabled." - ), - default="" + description=("DCI MACsec Fallback Key String. Cisco Type 7 Encrypted Octet String. " "This parameter is used when DCI link has QKD disabled."), + default="", ) vrf_lite_macsec_fallback_algorithm: MacsecAlgorithmEnum = Field( alias="vrfLiteMacsecFallbackAlgorithm", description="AES_128_CMAC or AES_256_CMAC. This parameter is used when DCI link has QKD disabled.", - default=MacsecAlgorithmEnum.AES_128_CMAC + default=MacsecAlgorithmEnum.AES_128_CMAC, ) # Quantum Key Distribution / Trustpoint quantum_key_distribution: bool = Field( alias="quantumKeyDistribution", - description=( - "Enable Data Center Interconnect Media Access Control Security " - "with Quantum Key Distribution config" - ), - default=False + description=("Enable Data Center Interconnect Media Access Control Security " "with Quantum Key Distribution config"), + default=False, ) quantum_key_distribution_profile_name: str = Field( alias="quantumKeyDistributionProfileName", description="Name of crypto profile (Max Size 63)", default="" ) - key_management_entity_server_ip: str = Field( - alias="keyManagementEntityServerIp", description="Key Management Entity server ipv4 address", default="" - ) - key_management_entity_server_port: int = Field( - alias="keyManagementEntityServerPort", description="Key Management Entity server port number", default=0 - ) - trustpoint_label: str = Field( - alias="trustpointLabel", - description="Tls authentication type trustpoint label", - default="" - ) - skip_certificate_verification: bool = Field( - alias="skipCertificateVerification", description="Skip verification of incoming certificate", default=False - ) + key_management_entity_server_ip: str = Field(alias="keyManagementEntityServerIp", description="Key Management Entity server ipv4 address", default="") + key_management_entity_server_port: int = Field(alias="keyManagementEntityServerPort", description="Key Management Entity server port number", default=0) + trustpoint_label: str = Field(alias="trustpointLabel", description="Tls authentication type trustpoint label", default="") + skip_certificate_verification: bool = Field(alias="skipCertificateVerification", description="Skip verification of incoming certificate", default=False) # BGP / Routing Enhancements - auto_bgp_neighbor_description: bool = Field( - alias="autoBgpNeighborDescription", description="Generate BGP EVPN Neighbor Description", default=True - ) + auto_bgp_neighbor_description: bool = Field(alias="autoBgpNeighborDescription", description="Generate BGP EVPN Neighbor Description", default=True) ibgp_peer_template: str = Field( alias="ibgpPeerTemplate", description=( @@ -1408,7 +901,7 @@ class VxlanIbgpManagementModel(NDNestedModel): "leading spaces. Note ! All configs should strictly match show run output, with respect to case and " "newlines. Any mismatches will yield unexpected diffs during deploy." ), - default="" + default="", ) leaf_ibgp_peer_template: str = Field( alias="leafIbgpPeerTemplate", @@ -1419,92 +912,52 @@ class VxlanIbgpManagementModel(NDNestedModel): "have 2 leading spaces. Note ! All configs should strictly match 'show run' output, with respect to case " "and newlines. Any mismatches will yield unexpected diffs during deploy." ), - default="" - ) - link_state_routing_tag: str = Field( - alias="linkStateRoutingTag", - description="Underlay routing protocol process tag", - default="UNDERLAY" + default="", ) + link_state_routing_tag: str = Field(alias="linkStateRoutingTag", description="Underlay routing protocol process tag", default="UNDERLAY") static_underlay_ip_allocation: bool = Field( - alias="staticUnderlayIpAllocation", - description="Checking this will disable Dynamic Underlay IP Address Allocations", - default=False - ) - router_id_range: str = Field( - alias="routerIdRange", - description="BGP Router ID Range in IPv4 subnet format used for IPv6 Underlay.", - default="10.2.0.0/23" + alias="staticUnderlayIpAllocation", description="Checking this will disable Dynamic Underlay IP Address Allocations", default=False ) + router_id_range: str = Field(alias="routerIdRange", description="BGP Router ID Range in IPv4 subnet format used for IPv6 Underlay.", default="10.2.0.0/23") # Security Group Tags (SGT) - security_group_tag: bool = Field( - alias="securityGroupTag", - description="Security group can be enabled only with cli overlay mode", - default=False - ) - security_group_tag_prefix: str = Field( - alias="securityGroupTagPrefix", - description="Prefix to be used when a new security group is created", - default="SG_" - ) + security_group_tag: bool = Field(alias="securityGroupTag", description="Security group can be enabled only with cli overlay mode", default=False) + security_group_tag_prefix: str = Field(alias="securityGroupTagPrefix", description="Prefix to be used when a new security group is created", default="SG_") security_group_tag_mac_segmentation: bool = Field( - alias="securityGroupTagMacSegmentation", - description="Enable MAC based segmentation for security groups", - default=False + alias="securityGroupTagMacSegmentation", description="Enable MAC based segmentation for security groups", default=False ) security_group_tag_id_range: str = Field( - alias="securityGroupTagIdRange", - description="Security group tag (SGT) identifier range (minimum: 16, maximum: 65535)", - default="10000-14000" + alias="securityGroupTagIdRange", description="Security group tag (SGT) identifier range (minimum: 16, maximum: 65535)", default="10000-14000" ) security_group_tag_preprovision: bool = Field( - alias="securityGroupTagPreprovision", - description="Generate security groups configuration for non-enforced VRFs", - default=False + alias="securityGroupTagPreprovision", description="Generate security groups configuration for non-enforced VRFs", default=False ) security_group_status: SecurityGroupStatusEnum = Field( - alias="securityGroupStatus", - description="Security group status", - default=SecurityGroupStatusEnum.DISABLED + alias="securityGroupStatus", description="Security group status", default=SecurityGroupStatusEnum.DISABLED ) # Queuing / QoS - default_queuing_policy: bool = Field( - alias="defaultQueuingPolicy", - description="Enable Default Queuing Policies", - default=False - ) + default_queuing_policy: bool = Field(alias="defaultQueuingPolicy", description="Enable Default Queuing Policies", default=False) default_queuing_policy_cloudscale: str = Field( alias="defaultQueuingPolicyCloudscale", description="Queuing Policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric", - default="queuing_policy_default_8q_cloudscale" + default="queuing_policy_default_8q_cloudscale", ) default_queuing_policy_r_series: str = Field( - alias="defaultQueuingPolicyRSeries", - description="Queueing policy for all Nexus R-series switches", - default="queuing_policy_default_r_series" + alias="defaultQueuingPolicyRSeries", description="Queueing policy for all Nexus R-series switches", default="queuing_policy_default_r_series" ) default_queuing_policy_other: str = Field( - alias="defaultQueuingPolicyOther", - description="Queuing Policy for all other switches in the fabric", - default="queuing_policy_default_other" + alias="defaultQueuingPolicyOther", description="Queuing Policy for all other switches in the fabric", default="queuing_policy_default_other" ) aiml_qos: bool = Field( alias="aimlQos", - description=( - "Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) switch fabric for " - "AI network workloads" - ), - default=False + description=("Configures QoS and Queuing Policies specific to N9K Cloud Scale (CS) & Silicon One (S1) switch fabric for " "AI network workloads"), + default=False, ) aiml_qos_policy: AimlQosPolicyEnum = Field( alias="aimlQosPolicy", - description=( - "Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. User-defined allows for " - "custom configuration." - ), - default=AimlQosPolicyEnum.V_400G + description=("Queuing Policy based on predominant fabric link speed: 800G / 400G / 100G / 25G. User-defined allows for " "custom configuration."), + default=AimlQosPolicyEnum.V_400G, ) roce_v2: str = Field( alias="roceV2", @@ -1512,34 +965,25 @@ class VxlanIbgpManagementModel(NDNestedModel): "DSCP for RDMA traffic: numeric (0-63) with ranges/comma, named values " "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="26" + default="26", ) cnp: str = Field( description=( "DSCP value for Congestion Notification: numeric (0-63) with ranges/comma, named values " "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="48" + default="48", ) wred_min: int = Field(alias="wredMin", description="WRED minimum threshold (in kbytes)", default=950) wred_max: int = Field(alias="wredMax", description="WRED maximum threshold (in kbytes)", default=3000) wred_drop_probability: int = Field(alias="wredDropProbability", description="Drop probability %", default=7) - wred_weight: int = Field( - alias="wredWeight", - description="Influences how quickly WRED reacts to queue depth changes", - default=0 - ) - bandwidth_remaining: int = Field( - alias="bandwidthRemaining", - description="Percentage of remaining bandwidth allocated to AI traffic queues", - default=50 - ) + wred_weight: int = Field(alias="wredWeight", description="Influences how quickly WRED reacts to queue depth changes", default=0) + bandwidth_remaining: int = Field(alias="bandwidthRemaining", description="Percentage of remaining bandwidth allocated to AI traffic queues", default=50) dlb: bool = Field( description=( - "Enables fabric-level Dynamic Load Balancing (DLB) configuration. Note: Inter-Switch-Links (ISL) will be " - "configured as DLB Interfaces" + "Enables fabric-level Dynamic Load Balancing (DLB) configuration. Note: Inter-Switch-Links (ISL) will be " "configured as DLB Interfaces" ), - default=False + default=False, ) dlb_mode: DlbModeEnum = Field( alias="dlbMode", @@ -1547,12 +991,10 @@ class VxlanIbgpManagementModel(NDNestedModel): "Select system-wide flowlet, per-packet (packet spraying) or policy driven mixed mode. Note: Mixed mode is " "supported on Silicon One (S1) platform only." ), - default=DlbModeEnum.FLOWLET + default=DlbModeEnum.FLOWLET, ) dlb_mixed_mode_default: DlbMixedModeDefaultEnum = Field( - alias="dlbMixedModeDefault", - description="Default load balancing mode for policy driven mixed mode DLB", - default=DlbMixedModeDefaultEnum.ECMP + alias="dlbMixedModeDefault", description="Default load balancing mode for policy driven mixed mode DLB", default=DlbMixedModeDefaultEnum.ECMP ) flowlet_aging: int = Field( alias="flowletAging", @@ -1560,7 +1002,7 @@ class VxlanIbgpManagementModel(NDNestedModel): "Flowlet aging timer in microseconds. Valid range depends on platform: Cloud Scale (CS)=1-2000000 (default " "500), Silicon One (S1)=1-1024 (default 256)" ), - default=1 + default=1, ) flowlet_dscp: str = Field( alias="flowletDscp", @@ -1568,7 +1010,7 @@ class VxlanIbgpManagementModel(NDNestedModel): "DSCP values for flowlet load balancing: numeric (0-63) with ranges/comma, named values " "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="" + default="", ) per_packet_dscp: str = Field( alias="perPacketDscp", @@ -1576,36 +1018,22 @@ class VxlanIbgpManagementModel(NDNestedModel): "DSCP values for per-packet load balancing: numeric (0-63) with ranges/comma, named values " "(af11,af12,af13,af21,af22,af23,af31,af32,af33,af41,af42,af43,cs1,cs2,cs3,cs4,cs5,cs6,cs7,default,ef)" ), - default="" + default="", ) ai_load_sharing: bool = Field( - alias="aiLoadSharing", - description="Enable IP load sharing using source and destination address for AI workloads", - default=False + alias="aiLoadSharing", description="Enable IP load sharing using source and destination address for AI workloads", default=False ) priority_flow_control_watch_interval: int = Field( alias="priorityFlowControlWatchInterval", description="Acceptable values from 101 to 1000 (milliseconds). Leave blank for system default (100ms).", - default=101 + default=101, ) # PTP ptp: bool = Field(description="Enable Precision Time Protocol (PTP)", default=False) - ptp_loopback_id: int = Field( - alias="ptpLoopbackId", - description="Precision Time Protocol Source Loopback Id", - default=0 - ) - ptp_domain_id: int = Field( - alias="ptpDomainId", - description="Multiple Independent PTP Clocking Subdomains on a Single Network", - default=0 - ) - ptp_vlan_id: int = Field( - alias="ptpVlanId", - description="Precision Time Protocol (PTP) Source VLAN ID. SVI used for ptp source on ToRs", - default=2 - ) + ptp_loopback_id: int = Field(alias="ptpLoopbackId", description="Precision Time Protocol Source Loopback Id", default=0) + ptp_domain_id: int = Field(alias="ptpDomainId", description="Multiple Independent PTP Clocking Subdomains on a Single Network", default=0) + ptp_vlan_id: int = Field(alias="ptpVlanId", description="Precision Time Protocol (PTP) Source VLAN ID. SVI used for ptp source on ToRs", default=2) # STP stp_root_option: StpRootOptionEnum = Field( @@ -1614,75 +1042,45 @@ class VxlanIbgpManagementModel(NDNestedModel): "Which protocol to use for configuring root bridge? rpvst+: Rapid Per-VLAN Spanning Tree, mst: Multiple " "Spanning Tree, unmanaged (default): STP Root not managed by ND" ), - default=StpRootOptionEnum.UNMANAGED - ) - stp_vlan_range: str = Field( - alias="stpVlanRange", - description="Spanning tree Vlan range (minimum: 1, maximum: 4094)", - default="1-3967" - ) - mst_instance_range: str = Field( - alias="mstInstanceRange", - description="Minimum Spanning Tree instance range (minimum: 0, maximum: 4094)", - default="0" - ) - stp_bridge_priority: int = Field( - alias="stpBridgePriority", - description="Bridge priority for the spanning tree in increments of 4096", - default=0 + default=StpRootOptionEnum.UNMANAGED, ) + stp_vlan_range: str = Field(alias="stpVlanRange", description="Spanning tree Vlan range (minimum: 1, maximum: 4094)", default="1-3967") + mst_instance_range: str = Field(alias="mstInstanceRange", description="Minimum Spanning Tree instance range (minimum: 0, maximum: 4094)", default="0") + stp_bridge_priority: int = Field(alias="stpBridgePriority", description="Bridge priority for the spanning tree in increments of 4096", default=0) # MPLS Handoff mpls_handoff: bool = Field(alias="mplsHandoff", description="Enable MPLS Handoff", default=False) - mpls_loopback_identifier: int = Field( - alias="mplsLoopbackIdentifier", - description="Used for VXLAN to MPLS SR/LDP Handoff", - default=101 - ) + mpls_loopback_identifier: int = Field(alias="mplsLoopbackIdentifier", description="Used for VXLAN to MPLS SR/LDP Handoff", default=101) mpls_isis_area_number: str = Field( alias="mplsIsisAreaNumber", description=( "NET in form of XX.<4-hex-digit Custom Area Number>.XXXX.XXXX.XXXX.00, default Area Number is 0001, used " "only if routing protocol on DCI MPLS link is is-is" ), - default="0001" - ) - mpls_loopback_ip_range: str = Field( - alias="mplsLoopbackIpRange", - description="Used for VXLAN to MPLS SR/LDP Handoff", - default="10.101.0.0/25" + default="0001", ) + mpls_loopback_ip_range: str = Field(alias="mplsLoopbackIpRange", description="Used for VXLAN to MPLS SR/LDP Handoff", default="10.101.0.0/25") # Private VLAN - private_vlan: bool = Field( - alias="privateVlan", - description="Enable PVLAN on switches except spines and super spines", - default=False - ) + private_vlan: bool = Field(alias="privateVlan", description="Enable PVLAN on switches except spines and super spines", default=False) default_private_vlan_secondary_network_template: str = Field( - alias="defaultPrivateVlanSecondaryNetworkTemplate", - description="Default PVLAN secondary network template", - default="Pvlan_Secondary_Network" + alias="defaultPrivateVlanSecondaryNetworkTemplate", description="Default PVLAN secondary network template", default="Pvlan_Secondary_Network" ) allow_vlan_on_leaf_tor_pairing: AllowVlanOnLeafTorPairingEnum = Field( alias="allowVlanOnLeafTorPairing", description="Set trunk allowed vlan to 'none' or 'all' for leaf-tor pairing port-channels", - default=AllowVlanOnLeafTorPairingEnum.NONE + default=AllowVlanOnLeafTorPairingEnum.NONE, ) # Leaf / TOR - leaf_tor_id_range: bool = Field( - alias="leafTorIdRange", - description="Use specific vPC/Port-channel ID range for leaf-tor pairings", - default=False - ) + leaf_tor_id_range: bool = Field(alias="leafTorIdRange", description="Use specific vPC/Port-channel ID range for leaf-tor pairings", default=False) leaf_tor_vpc_port_channel_id_range: str = Field( alias="leafTorVpcPortChannelIdRange", description=( "Specify vPC/Port-channel ID range (minimum: 1, maximum: 4096), this range is used for auto-allocating " "vPC/Port-Channel IDs for leaf-tor pairings" ), - default="1-499" + default="1-499", ) # Resource ID Ranges @@ -1692,33 +1090,25 @@ class VxlanIbgpManagementModel(NDNestedModel): "L3 VNI configuration without VLAN configuration. This value is propagated on vrf creation as the default " "value of 'Enable L3VNI w/o VLAN' in vrf" ), - default=False + default=False, ) ip_service_level_agreement_id_range: str = Field( alias="ipServiceLevelAgreementIdRange", - description=( - "Service Level Agreement (SLA) ID Range " - "(minimum: 1, maximum: 655214748364735). Per switch SLA ID Range" - ), - default="10000-19999" + description=("Service Level Agreement (SLA) ID Range " "(minimum: 1, maximum: 655214748364735). Per switch SLA ID Range"), + default="10000-19999", ) object_tracking_number_range: str = Field( alias="objectTrackingNumberRange", description="Tracked Object ID Range (minimum: 1, maximum: 512) Per switch tracked object ID Range", - default="100-299" + default="100-299", ) service_network_vlan_range: str = Field( alias="serviceNetworkVlanRange", - description=( - "Service Network VLAN Range (minimum: 2, maximum: 4094). " - "Per Switch Overlay Service Network VLAN Range" - ), - default="3000-3199" + description=("Service Network VLAN Range (minimum: 2, maximum: 4094). " "Per Switch Overlay Service Network VLAN Range"), + default="3000-3199", ) route_map_sequence_number_range: str = Field( - alias="routeMapSequenceNumberRange", - description="Route Map Sequence Number Range (minimum: 1, maximum: 65534)", - default="1-65534" + alias="routeMapSequenceNumberRange", description="Route Map Sequence Number Range (minimum: 1, maximum: 65534)", default="1-65534" ) # DNS / NTP / Syslog Collections @@ -1727,23 +1117,15 @@ class VxlanIbgpManagementModel(NDNestedModel): dns_collection: List[str] = Field(default_factory=lambda: ["5.192.28.174"], alias="dnsCollection") dns_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="dnsVrfCollection") syslog_server_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerCollection") - syslog_server_vrf_collection: List[str] = Field( - default_factory=lambda: ["string"], - alias="syslogServerVrfCollection" - ) + syslog_server_vrf_collection: List[str] = Field(default_factory=lambda: ["string"], alias="syslogServerVrfCollection") syslog_severity_collection: List[int] = Field( - default_factory=lambda: [7], - alias="syslogSeverityCollection", - description="List of Syslog severity values, one per Syslog server" + default_factory=lambda: [7], alias="syslogSeverityCollection", description="List of Syslog severity values, one per Syslog server" ) # Extra Config / Pre-Interface Config / AAA / Banner banner: str = Field( - description=( - "Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) followed by message " - "ending with delimiter" - ), - default="" + description=("Message of the Day (motd) banner. Delimiter char (very first char is delimiter char) followed by message " "ending with delimiter"), + default="", ) extra_config_leaf: str = Field( alias="extraConfigLeaf", @@ -1751,27 +1133,21 @@ class VxlanIbgpManagementModel(NDNestedModel): "Additional CLIs as captured from the show running configuration, added after interface configurations for " "all switches with a VTEP unless they have some spine role" ), - default="" + default="", ) extra_config_spine: str = Field( alias="extraConfigSpine", description=( - "Additional CLIs as captured from the show running configuration, added after interface configurations for " - "all switches with some spine role" + "Additional CLIs as captured from the show running configuration, added after interface configurations for " "all switches with some spine role" ), - default="" + default="", ) extra_config_tor: str = Field( alias="extraConfigTor", - description=( - "Additional CLIs as captured from the show running configuration, added after interface configurations for " - "all ToRs" - ), - default="" - ) - extra_config_intra_fabric_links: str = Field( - alias="extraConfigIntraFabricLinks", description="Additional CLIs for all Intra-Fabric links", default="" + description=("Additional CLIs as captured from the show running configuration, added after interface configurations for " "all ToRs"), + default="", ) + extra_config_intra_fabric_links: str = Field(alias="extraConfigIntraFabricLinks", description="Additional CLIs for all Intra-Fabric links", default="") extra_config_aaa: str = Field(alias="extraConfigAaa", description="AAA Configurations", default="") aaa: bool = Field(description="Include AAA configs from Manageability tab during device bootup", default=False) pre_interface_config_leaf: str = Field( @@ -1780,139 +1156,103 @@ class VxlanIbgpManagementModel(NDNestedModel): "Additional CLIs as captured from the show running configuration, added before interface " "configurations for all switches with a VTEP unless they have some spine role" ), - default="" + default="", ) pre_interface_config_spine: str = Field( alias="preInterfaceConfigSpine", description=( - "Additional CLIs as captured from the show running configuration, added before interface " - "configurations for all switches with some spine role" + "Additional CLIs as captured from the show running configuration, added before interface " "configurations for all switches with some spine role" ), - default="" + default="", ) pre_interface_config_tor: str = Field( alias="preInterfaceConfigTor", - description=( - "Additional CLIs as captured from the show running configuration, added before interface " - "configurations for all ToRs" - ), - default="" + description=("Additional CLIs as captured from the show running configuration, added before interface " "configurations for all ToRs"), + default="", ) # System / Compliance / OAM / Misc anycast_border_gateway_advertise_physical_ip: bool = Field( alias="anycastBorderGatewayAdvertisePhysicalIp", description="To advertise Anycast Border Gateway PIP as VTEP. Effective on MSD fabric 'Recalculate Config'", - default=False + default=False, ) greenfield_debug_flag: GreenfieldDebugFlagEnum = Field( alias="greenfieldDebugFlag", description="Allow switch configuration to be cleared without a reload when preserveConfig is set to false", - default=GreenfieldDebugFlagEnum.DISABLE + default=GreenfieldDebugFlagEnum.DISABLE, ) interface_statistics_load_interval: int = Field( - alias="interfaceStatisticsLoadInterval", - description="Interface Statistics Load Interval. Time in seconds", - default=10 - ) - nve_hold_down_timer: int = Field( - alias="nveHoldDownTimer", - description="NVE Source Inteface HoldDown Time in seconds", - default=180 + alias="interfaceStatisticsLoadInterval", description="Interface Statistics Load Interval. Time in seconds", default=10 ) + nve_hold_down_timer: int = Field(alias="nveHoldDownTimer", description="NVE Source Inteface HoldDown Time in seconds", default=180) next_generation_oam: bool = Field( alias="nextGenerationOAM", - description=( - "Enable the Next Generation (NG) OAM feature for all switches in the fabric to aid in trouble-shooting " - "VXLAN EVPN fabrics" - ), - default=True + description=("Enable the Next Generation (NG) OAM feature for all switches in the fabric to aid in trouble-shooting " "VXLAN EVPN fabrics"), + default=True, ) ngoam_south_bound_loop_detect: bool = Field( - alias="ngoamSouthBoundLoopDetect", - description="Enable the Next Generation (NG) OAM southbound loop detection", - default=False + alias="ngoamSouthBoundLoopDetect", description="Enable the Next Generation (NG) OAM southbound loop detection", default=False ) ngoam_south_bound_loop_detect_probe_interval: int = Field( alias="ngoamSouthBoundLoopDetectProbeInterval", description="Set Next Generation (NG) OAM southbound loop detection probe interval in seconds.", - default=300 + default=300, ) ngoam_south_bound_loop_detect_recovery_interval: int = Field( alias="ngoamSouthBoundLoopDetectRecoveryInterval", description="Set the Next Generation (NG) OAM southbound loop detection recovery interval in seconds", - default=600 + default=600, ) strict_config_compliance_mode: bool = Field( alias="strictConfigComplianceMode", - description=( - "Enable bi-directional compliance checks to flag additional configs in the running config that are not in " - "the intent/expected config" - ), - default=False + description=("Enable bi-directional compliance checks to flag additional configs in the running config that are not in " "the intent/expected config"), + default=False, ) advanced_ssh_option: bool = Field( - alias="advancedSshOption", - description="Enable AAA IP Authorization. Enable only, when IP Authorization is enabled in the AAA Server", - default=False + alias="advancedSshOption", description="Enable AAA IP Authorization. Enable only, when IP Authorization is enabled in the AAA Server", default=False ) copp_policy: CoppPolicyEnum = Field( alias="coppPolicy", description="Fabric wide CoPP policy. Customized CoPP policy should be provided when 'manual' is selected.", - default=CoppPolicyEnum.STRICT + default=CoppPolicyEnum.STRICT, ) power_redundancy_mode: PowerRedundancyModeEnum = Field( - alias="powerRedundancyMode", - description="Default Power Supply Mode for NX-OS Switches", - default=PowerRedundancyModeEnum.REDUNDANT - ) - host_interface_admin_state: bool = Field( - alias="hostInterfaceAdminState", description="Unshut Host Interfaces by Default", default=True - ) - heartbeat_interval: int = Field( - alias="heartbeatInterval", - description="XConnect heartbeat interval for periodic link status checks", - default=190 + alias="powerRedundancyMode", description="Default Power Supply Mode for NX-OS Switches", default=PowerRedundancyModeEnum.REDUNDANT ) + host_interface_admin_state: bool = Field(alias="hostInterfaceAdminState", description="Unshut Host Interfaces by Default", default=True) + heartbeat_interval: int = Field(alias="heartbeatInterval", description="XConnect heartbeat interval for periodic link status checks", default=190) policy_based_routing: bool = Field( alias="policyBasedRouting", description="Enable feature pbr, sla sender, epbr, or enable feature pbr, based on the L4-L7 Services use case", - default=False + default=False, ) brownfield_network_name_format: str = Field( alias="brownfieldNetworkNameFormat", description="Generated network name should be less than 64 characters", - default="Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + default="Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$", ) brownfield_skip_overlay_network_attachments: bool = Field( alias="brownfieldSkipOverlayNetworkAttachments", description="Skip Overlay Network Interface Attachments for Brownfield and Host Port Resync cases", - default=False + default=False, ) allow_smart_switch_onboarding: bool = Field( - alias="allowSmartSwitchOnboarding", - description="Enable onboarding of smart switches to Hypershield for firewall service", - default=False + alias="allowSmartSwitchOnboarding", description="Enable onboarding of smart switches to Hypershield for firewall service", default=False ) # Hypershield / Connectivity - connectivity_domain_name: Optional[str] = Field( - alias="connectivityDomainName", description="Domain name to connect to Hypershield", default=None - ) + connectivity_domain_name: Optional[str] = Field(alias="connectivityDomainName", description="Domain name to connect to Hypershield", default=None) hypershield_connectivity_proxy_server: Optional[str] = Field( alias="hypershieldConnectivityProxyServer", description="IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication", - default=None + default=None, ) hypershield_connectivity_proxy_server_port: Optional[int] = Field( - alias="hypershieldConnectivityProxyServerPort", - description="Proxy port number for communication with Hypershield", - default=None + alias="hypershieldConnectivityProxyServerPort", description="Proxy port number for communication with Hypershield", default=None ) hypershield_connectivity_source_intf: Optional[str] = Field( - alias="hypershieldConnectivitySourceIntf", - description="Loopback interface on smart switch for communication with Hypershield", - default=None + alias="hypershieldConnectivitySourceIntf", description="Loopback interface on smart switch for communication with Hypershield", default=None ) @field_validator("bgp_asn") @@ -1934,10 +1274,7 @@ def validate_bgp_asn(cls, value: str) -> str: - `ValueError` - If the value does not match the expected ASN format """ if not _BGP_ASN_RE.match(value): - raise ValueError( - f"Invalid BGP ASN '{value}'. " - "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535)." - ) + raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value @field_validator("site_id") @@ -1978,7 +1315,7 @@ def validate_mac_address(cls, value: str) -> str: - `ValueError` - If MAC address format is invalid """ - mac_pattern = re.compile(r'^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$') + mac_pattern = re.compile(r"^([0-9a-fA-F]{4}\.){2}[0-9a-fA-F]{4}$") if not mac_pattern.match(value): raise ValueError(f"Invalid MAC address format, expected xxxx.xxxx.xxxx, got: {value}") @@ -2001,10 +1338,7 @@ class FabricIbgpModel(NDBaseModel): """ model_config = ConfigDict( - str_strip_whitespace=True, - validate_assignment=True, - populate_by_name=True, - extra="allow" # Allow extra fields from API responses + str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow" # Allow extra fields from API responses ) identifiers: ClassVar[Optional[List[str]]] = ["name"] @@ -2016,55 +1350,22 @@ class FabricIbgpModel(NDBaseModel): location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations - license_tier: LicenseTierEnum = Field( - alias="licenseTier", - description="License tier", - default=LicenseTierEnum.PREMIER - ) - alert_suspend: AlertSuspendEnum = Field( - alias="alertSuspend", - description="Alert suspension state", - default=AlertSuspendEnum.DISABLED - ) - telemetry_collection: bool = Field( - alias="telemetryCollection", - description="Enable telemetry collection", - default=False - ) - telemetry_collection_type: str = Field( - alias="telemetryCollectionType", - description="Telemetry collection type", - default="outOfBand" - ) - telemetry_streaming_protocol: str = Field( - alias="telemetryStreamingProtocol", - description="Telemetry streaming protocol", - default="ipv4" - ) - telemetry_source_interface: str = Field( - alias="telemetrySourceInterface", - description="Telemetry source interface", - default="" - ) + license_tier: LicenseTierEnum = Field(alias="licenseTier", description="License tier", default=LicenseTierEnum.PREMIER) + alert_suspend: AlertSuspendEnum = Field(alias="alertSuspend", description="Alert suspension state", default=AlertSuspendEnum.DISABLED) + telemetry_collection: bool = Field(alias="telemetryCollection", description="Enable telemetry collection", default=False) + telemetry_collection_type: str = Field(alias="telemetryCollectionType", description="Telemetry collection type", default="outOfBand") + telemetry_streaming_protocol: str = Field(alias="telemetryStreamingProtocol", description="Telemetry streaming protocol", default="ipv4") + telemetry_source_interface: str = Field(alias="telemetrySourceInterface", description="Telemetry source interface", default="") telemetry_source_vrf: str = Field(alias="telemetrySourceVrf", description="Telemetry source VRF", default="") security_domain: str = Field(alias="securityDomain", description="Security domain", default="all") # Core Management Configuration - management: Optional[VxlanIbgpManagementModel] = Field( - description="iBGP VXLAN management configuration", - default=None - ) + management: Optional[VxlanIbgpManagementModel] = Field(description="iBGP VXLAN management configuration", default=None) # Optional Advanced Settings - telemetry_settings: Optional[TelemetrySettingsModel] = Field( - alias="telemetrySettings", - description="Telemetry configuration", - default=None - ) + telemetry_settings: Optional[TelemetrySettingsModel] = Field(alias="telemetrySettings", description="Telemetry configuration", default=None) external_streaming_settings: ExternalStreamingSettingsModel = Field( - alias="externalStreamingSettings", - description="External streaming settings", - default_factory=ExternalStreamingSettingsModel + alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) @field_validator("name") @@ -2079,13 +1380,13 @@ def validate_fabric_name(cls, value: str) -> str: - `ValueError` - If name contains invalid characters or format """ - if not re.match(r'^[a-zA-Z0-9_-]+$', value): + if not re.match(r"^[a-zA-Z0-9_-]+$", value): raise ValueError(f"Fabric name can only contain letters, numbers, underscores, and hyphens, got: {value}") return value - @model_validator(mode='after') - def validate_fabric_consistency(self) -> 'FabricModel': + @model_validator(mode="after") + def validate_fabric_consistency(self) -> "FabricModel": """ # Summary @@ -2155,5 +1456,5 @@ def get_argument_spec(cls) -> Dict: "LicenseTierEnum", "ReplicationModeEnum", "OverlayModeEnum", - "LinkStateRoutingProtocolEnum" + "LinkStateRoutingProtocolEnum", ] From 9d9fc960596b51909fcd0aa4de0edd5db3a79b66 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 20:40:10 -0400 Subject: [PATCH 10/27] Move common models into common location for import --- .../manage_fabric/manage_fabric_common.py | 342 ++++++++++++++++++ .../manage_fabric/manage_fabric_ebgp.py | 18 +- .../manage_fabric/manage_fabric_external.py | 306 +--------------- .../manage_fabric/manage_fabric_ibgp.py | 297 +-------------- 4 files changed, 380 insertions(+), 583 deletions(-) create mode 100644 plugins/module_utils/models/manage_fabric/manage_fabric_common.py diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py new file mode 100644 index 00000000..efb4c270 --- /dev/null +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py @@ -0,0 +1,342 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +# Summary + +Common Pydantic models shared across fabric types (iBGP, eBGP, External Connectivity). + +## Models + +- `LocationModel` - Geographic location coordinates +- `NetflowExporterModel` - Netflow exporter configuration +- `NetflowRecordModel` - Netflow record configuration +- `NetflowMonitorModel` - Netflow monitor configuration +- `NetflowSettingsModel` - Complete netflow settings +- `BootstrapSubnetModel` - Bootstrap subnet configuration +- `TelemetryFlowCollectionModel` - Telemetry flow collection settings +- `TelemetryMicroburstModel` - Microburst detection configuration +- `TelemetryAnalysisSettingsModel` - Telemetry analysis configuration +- `TelemetryEnergyManagementModel` - Energy management telemetry +- `TelemetryNasExportSettingsModel` - NAS export settings +- `TelemetryNasModel` - NAS telemetry configuration +- `TelemetrySettingsModel` - Complete telemetry configuration +- `ExternalStreamingSettingsModel` - External streaming configuration +""" + +from __future__ import absolute_import, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +import re +from typing import List, Dict, Any + +from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel +from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( + ConfigDict, + Field, +) + + +# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and +# dotted four-byte ASN notation (1-65535).(0-65535) +BGP_ASN_RE = re.compile( + r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" + r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" + r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" + r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" + r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" + r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" +) + + +class LocationModel(NDNestedModel): + """ + # Summary + + Geographic location coordinates for the fabric. + + ## Raises + + - `ValueError` - If latitude or longitude are outside valid ranges + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + latitude: float = Field(description="Latitude coordinate (-90 to 90)", ge=-90.0, le=90.0) + longitude: float = Field(description="Longitude coordinate (-180 to 180)", ge=-180.0, le=180.0) + + +class NetflowExporterModel(NDNestedModel): + """ + # Summary + + Netflow exporter configuration for telemetry. + + ## Raises + + - `ValueError` - If UDP port is outside valid range or IP address is invalid + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") + exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") + vrf: str = Field(description="VRF name for the exporter", default="management") + source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") + udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) + + +class NetflowRecordModel(NDNestedModel): + """ + # Summary + + Netflow record configuration defining flow record templates. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + record_name: str = Field(alias="recordName", description="Name of the netflow record") + record_template: str = Field(alias="recordTemplate", description="Template type for the record") + layer2_record: bool = Field(alias="layer2Record", description="Enable layer 2 record fields", default=False) + + +class NetflowMonitorModel(NDNestedModel): + """ + # Summary + + Netflow monitor configuration linking records to exporters. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") + record_name: str = Field(alias="recordName", description="Associated record name") + exporter1_name: str = Field(alias="exporter1Name", description="Primary exporter name") + exporter2_name: str = Field(alias="exporter2Name", description="Secondary exporter name", default="") + + +class NetflowSettingsModel(NDNestedModel): + """ + # Summary + + Complete netflow configuration including exporters, records, and monitors. + + ## Raises + + - `ValueError` - If netflow lists are inconsistent with netflow enabled state + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + netflow: bool = Field(description="Enable netflow collection", default=False) + netflow_exporter_collection: List[NetflowExporterModel] = Field( + alias="netflowExporterCollection", description="List of netflow exporters", default_factory=list + ) + netflow_record_collection: List[NetflowRecordModel] = Field(alias="netflowRecordCollection", description="List of netflow records", default_factory=list) + netflow_monitor_collection: List[NetflowMonitorModel] = Field( + alias="netflowMonitorCollection", description="List of netflow monitors", default_factory=list + ) + + +class BootstrapSubnetModel(NDNestedModel): + """ + # Summary + + Bootstrap subnet configuration for fabric initialization. + + ## Raises + + - `ValueError` - If IP addresses or subnet prefix are invalid + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") + end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") + default_gateway: str = Field(alias="defaultGateway", description="Default gateway for bootstrap subnet") + subnet_prefix: int = Field(alias="subnetPrefix", description="Subnet prefix length", ge=8, le=30) + + +class TelemetryFlowCollectionModel(NDNestedModel): + """ + # Summary + + Telemetry flow collection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") + traffic_analytics_scope: str = Field(alias="trafficAnalyticsScope", description="Traffic analytics scope", default="intraFabric") + operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") + udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") + + +class TelemetryMicroburstModel(NDNestedModel): + """ + # Summary + + Microburst detection configuration. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + microburst: bool = Field(description="Enable microburst detection", default=False) + sensitivity: str = Field(description="Microburst sensitivity level", default="low") + + +class TelemetryAnalysisSettingsModel(NDNestedModel): + """ + # Summary + + Telemetry analysis configuration. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) + + +class TelemetryEnergyManagementModel(NDNestedModel): + """ + # Summary + + Energy management telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + cost: float = Field(description="Energy cost per unit", default=1.2) + + +class TelemetryNasExportSettingsModel(NDNestedModel): + """ + # Summary + + NAS export settings for telemetry. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + export_type: str = Field(alias="exportType", description="Export type", default="full") + export_format: str = Field(alias="exportFormat", description="Export format", default="json") + + +class TelemetryNasModel(NDNestedModel): + """ + # Summary + + NAS (Network Attached Storage) telemetry configuration. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + server: str = Field(description="NAS server address", default="") + export_settings: TelemetryNasExportSettingsModel = Field( + alias="exportSettings", description="NAS export settings", default_factory=TelemetryNasExportSettingsModel + ) + + +class TelemetrySettingsModel(NDNestedModel): + """ + # Summary + + Complete telemetry configuration for the fabric. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + flow_collection: TelemetryFlowCollectionModel = Field( + alias="flowCollection", description="Flow collection settings", default_factory=TelemetryFlowCollectionModel + ) + microburst: TelemetryMicroburstModel = Field(description="Microburst detection settings", default_factory=TelemetryMicroburstModel) + analysis_settings: TelemetryAnalysisSettingsModel = Field( + alias="analysisSettings", description="Analysis settings", default_factory=TelemetryAnalysisSettingsModel + ) + nas: TelemetryNasModel = Field(description="NAS telemetry configuration", default_factory=TelemetryNasModel) + energy_management: TelemetryEnergyManagementModel = Field( + alias="energyManagement", description="Energy management settings", default_factory=TelemetryEnergyManagementModel + ) + + +class ExternalStreamingSettingsModel(NDNestedModel): + """ + # Summary + + External streaming configuration for events and data export. + + ## Raises + + None + """ + + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") + + email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) + message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) + syslog: Dict[str, Any] = Field( + description="Syslog streaming configuration", default_factory=lambda: {"collectionSettings": {"anomalies": []}, "facility": "", "servers": []} + ) + webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) + + +# Export all models for external use +__all__ = [ + "LocationModel", + "NetflowExporterModel", + "NetflowRecordModel", + "NetflowMonitorModel", + "NetflowSettingsModel", + "BootstrapSubnetModel", + "TelemetryFlowCollectionModel", + "TelemetryMicroburstModel", + "TelemetryAnalysisSettingsModel", + "TelemetryEnergyManagementModel", + "TelemetryNasExportSettingsModel", + "TelemetryNasModel", + "TelemetrySettingsModel", + "ExternalStreamingSettingsModel", + "BGP_ASN_RE", +] diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index 9370bc41..b134e86c 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -47,8 +47,9 @@ VrfLiteAutoConfigEnum, ) -# Re-use shared nested models from the iBGP module -from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_ibgp import ( +# Re-use shared nested models from the common module +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_common import ( + BGP_ASN_RE, LocationModel, NetflowSettingsModel, BootstrapSubnetModel, @@ -84,17 +85,6 @@ ``` """ -# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and -# dotted four-byte ASN notation (1-65535).(0-65535) -_BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" - r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" - r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" - r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" - r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" - r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" -) - class VxlanEbgpManagementModel(NDNestedModel): """ @@ -770,7 +760,7 @@ def validate_bgp_asn(cls, value: Optional[str]) -> Optional[str]: """ if value is None: return value - if not _BGP_ASN_RE.match(value): + if not BGP_ASN_RE.match(value): raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index 16b54265..a05a5a79 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -11,7 +11,7 @@ # pylint: enable=invalid-name import re -from typing import List, Dict, Any, Optional, ClassVar, Literal +from typing import List, Dict, Optional, ClassVar, Literal from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel @@ -31,6 +31,21 @@ TelemetryCollectionTypeEnum, TelemetryStreamingProtocolEnum, ) +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_common import ( + BGP_ASN_RE, + LocationModel, + NetflowExporterModel, + NetflowRecordModel, + NetflowMonitorModel, + NetflowSettingsModel, + BootstrapSubnetModel, + TelemetryFlowCollectionModel, + TelemetryMicroburstModel, + TelemetryAnalysisSettingsModel, + TelemetryEnergyManagementModel, + TelemetrySettingsModel, + ExternalStreamingSettingsModel, +) """ # Comprehensive Pydantic models for External Connectivity fabric management via Nexus Dashboard @@ -40,15 +55,6 @@ ## Models Overview -- `LocationModel` - Geographic location coordinates -- `NetflowExporterModel` - Netflow exporter configuration -- `NetflowRecordModel` - Netflow record configuration -- `NetflowMonitorModel` - Netflow monitor configuration -- `NetflowSettingsModel` - Complete netflow settings -- `BootstrapSubnetModel` - Bootstrap subnet configuration -- `TelemetryFlowCollectionModel` - Telemetry flow collection settings -- `TelemetrySettingsModel` - Complete telemetry configuration -- `ExternalStreamingSettingsModel` - External streaming configuration - `ExternalConnectivityManagementModel` - External Connectivity specific management settings - `FabricExternalConnectivityModel` - Complete fabric creation model @@ -68,284 +74,6 @@ ``` """ -# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and -# dotted four-byte ASN notation (1-65535).(0-65535) -_BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" - r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" - r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" - r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" - r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" - r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" -) - - -class LocationModel(NDNestedModel): - """ - # Summary - - Geographic location coordinates for the fabric. - - ## Raises - - - `ValueError` - If latitude or longitude are outside valid ranges - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - latitude: float = Field(description="Latitude coordinate (-90 to 90)", ge=-90.0, le=90.0) - longitude: float = Field(description="Longitude coordinate (-180 to 180)", ge=-180.0, le=180.0) - - -class NetflowExporterModel(NDNestedModel): - """ - # Summary - - Netflow exporter configuration for telemetry. - - ## Raises - - - `ValueError` - If UDP port is outside valid range or IP address is invalid - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") - exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") - vrf: str = Field(description="VRF name for the exporter", default="management") - source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") - udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) - - -class NetflowRecordModel(NDNestedModel): - """ - # Summary - - Netflow record configuration defining flow record templates. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - record_name: str = Field(alias="recordName", description="Name of the netflow record") - record_template: str = Field(alias="recordTemplate", description="Template type for the record") - layer2_record: bool = Field(alias="layer2Record", description="Enable layer 2 record fields", default=False) - - -class NetflowMonitorModel(NDNestedModel): - """ - # Summary - - Netflow monitor configuration linking records to exporters. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") - record_name: str = Field(alias="recordName", description="Associated record name") - exporter1_name: str = Field(alias="exporter1Name", description="Primary exporter name") - exporter2_name: str = Field(alias="exporter2Name", description="Secondary exporter name", default="") - - -class NetflowSettingsModel(NDNestedModel): - """ - # Summary - - Complete netflow configuration including exporters, records, and monitors. - - ## Raises - - - `ValueError` - If netflow lists are inconsistent with netflow enabled state - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - netflow: bool = Field(description="Enable netflow collection", default=False) - netflow_exporter_collection: List[NetflowExporterModel] = Field( - alias="netflowExporterCollection", description="List of netflow exporters", default_factory=list - ) - netflow_record_collection: List[NetflowRecordModel] = Field(alias="netflowRecordCollection", description="List of netflow records", default_factory=list) - netflow_monitor_collection: List[NetflowMonitorModel] = Field( - alias="netflowMonitorCollection", description="List of netflow monitors", default_factory=list - ) - - -class BootstrapSubnetModel(NDNestedModel): - """ - # Summary - - Bootstrap subnet configuration for fabric initialization. - - ## Raises - - - `ValueError` - If IP addresses or subnet prefix are invalid - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") - end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") - default_gateway: str = Field(alias="defaultGateway", description="Default gateway for bootstrap subnet") - subnet_prefix: int = Field(alias="subnetPrefix", description="Subnet prefix length", ge=8, le=30) - - -class TelemetryFlowCollectionModel(NDNestedModel): - """ - # Summary - - Telemetry flow collection configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") - traffic_analytics_scope: str = Field(alias="trafficAnalyticsScope", description="Traffic analytics scope", default="intraFabric") - operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") - udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") - - -class TelemetryMicroburstModel(NDNestedModel): - """ - # Summary - - Microburst detection configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - microburst: bool = Field(description="Enable microburst detection", default=False) - sensitivity: str = Field(description="Microburst sensitivity level", default="low") - - -class TelemetryAnalysisSettingsModel(NDNestedModel): - """ - # Summary - - Telemetry analysis configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) - - -class TelemetryEnergyManagementModel(NDNestedModel): - """ - # Summary - - Energy management telemetry configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - cost: float = Field(description="Energy cost per unit", default=1.2) - - -class TelemetryNasExportSettingsModel(NDNestedModel): - """ - # Summary - - NAS export settings for telemetry. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - export_type: str = Field(alias="exportType", description="Export type", default="full") - export_format: str = Field(alias="exportFormat", description="Export format", default="json") - - -class TelemetryNasModel(NDNestedModel): - """ - # Summary - - NAS (Network Attached Storage) telemetry configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - server: str = Field(description="NAS server address", default="") - export_settings: TelemetryNasExportSettingsModel = Field( - alias="exportSettings", description="NAS export settings", default_factory=TelemetryNasExportSettingsModel - ) - - -class TelemetrySettingsModel(NDNestedModel): - """ - # Summary - - Complete telemetry configuration for the fabric. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - flow_collection: TelemetryFlowCollectionModel = Field( - alias="flowCollection", description="Flow collection settings", default_factory=TelemetryFlowCollectionModel - ) - microburst: TelemetryMicroburstModel = Field(description="Microburst detection settings", default_factory=TelemetryMicroburstModel) - analysis_settings: TelemetryAnalysisSettingsModel = Field( - alias="analysisSettings", description="Analysis settings", default_factory=TelemetryAnalysisSettingsModel - ) - nas: TelemetryNasModel = Field(description="NAS telemetry configuration", default_factory=TelemetryNasModel) - energy_management: TelemetryEnergyManagementModel = Field( - alias="energyManagement", description="Energy management settings", default_factory=TelemetryEnergyManagementModel - ) - - -class ExternalStreamingSettingsModel(NDNestedModel): - """ - # Summary - - External streaming configuration for events and data export. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) - message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) - syslog: Dict[str, Any] = Field( - description="Syslog streaming configuration", default_factory=lambda: {"collectionSettings": {"anomalies": []}, "facility": "", "servers": []} - ) - webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) - class ExternalConnectivityManagementModel(NDNestedModel): """ @@ -683,7 +411,7 @@ def validate_bgp_asn(cls, value: str) -> str: - `ValueError` - If the value does not match the expected ASN format """ - if not _BGP_ASN_RE.match(value): + if not BGP_ASN_RE.match(value): raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index ab32e87e..a4b32b9e 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -13,7 +13,7 @@ import re # from datetime import datetime -from typing import List, Dict, Any, Optional, ClassVar, Literal +from typing import List, Dict, Optional, ClassVar, Literal from ansible_collections.cisco.nd.plugins.module_utils.models.base import NDBaseModel from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel @@ -52,6 +52,21 @@ UnderlayMulticastGroupAddressLimitEnum, VrfLiteAutoConfigEnum, ) +from ansible_collections.cisco.nd.plugins.module_utils.models.manage_fabric.manage_fabric_common import ( + BGP_ASN_RE, + LocationModel, + NetflowExporterModel, + NetflowRecordModel, + NetflowMonitorModel, + NetflowSettingsModel, + BootstrapSubnetModel, + TelemetryFlowCollectionModel, + TelemetryMicroburstModel, + TelemetryAnalysisSettingsModel, + TelemetryEnergyManagementModel, + TelemetrySettingsModel, + ExternalStreamingSettingsModel, +) """ # Comprehensive Pydantic models for iBGP VXLAN fabric management via Nexus Dashboard @@ -91,284 +106,6 @@ ``` """ -# Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and -# dotted four-byte ASN notation (1-65535).(0-65535) -_BGP_ASN_RE = re.compile( - r"^(([1-9]{1}[0-9]{0,8}|[1-3]{1}[0-9]{1,9}|[4]{1}([0-1]{1}[0-9]{8}" - r"|[2]{1}([0-8]{1}[0-9]{7}|[9]{1}([0-3]{1}[0-9]{6}|[4]{1}([0-8]{1}[0-9]{5}" - r"|[9]{1}([0-5]{1}[0-9]{4}|[6]{1}([0-6]{1}[0-9]{3}|[7]{1}([0-1]{1}[0-9]{2}" - r"|[2]{1}([0-8]{1}[0-9]{1}|[9]{1}[0-5]{1})))))))))" - r"|([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])" - r"(\.([1-5]\d{4}|[1-9]\d{0,3}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]|0))?)$" -) - - -class LocationModel(NDNestedModel): - """ - # Summary - - Geographic location coordinates for the fabric. - - ## Raises - - - `ValueError` - If latitude or longitude are outside valid ranges - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - latitude: float = Field(description="Latitude coordinate (-90 to 90)", ge=-90.0, le=90.0) - longitude: float = Field(description="Longitude coordinate (-180 to 180)", ge=-180.0, le=180.0) - - -class NetflowExporterModel(NDNestedModel): - """ - # Summary - - Netflow exporter configuration for telemetry. - - ## Raises - - - `ValueError` - If UDP port is outside valid range or IP address is invalid - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - exporter_name: str = Field(alias="exporterName", description="Name of the netflow exporter") - exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") - vrf: str = Field(description="VRF name for the exporter", default="management") - source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") - udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) - - -class NetflowRecordModel(NDNestedModel): - """ - # Summary - - Netflow record configuration defining flow record templates. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - record_name: str = Field(alias="recordName", description="Name of the netflow record") - record_template: str = Field(alias="recordTemplate", description="Template type for the record") - layer2_record: bool = Field(alias="layer2Record", description="Enable layer 2 record fields", default=False) - - -class NetflowMonitorModel(NDNestedModel): - """ - # Summary - - Netflow monitor configuration linking records to exporters. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - monitor_name: str = Field(alias="monitorName", description="Name of the netflow monitor") - record_name: str = Field(alias="recordName", description="Associated record name") - exporter1_name: str = Field(alias="exporter1Name", description="Primary exporter name") - exporter2_name: str = Field(alias="exporter2Name", description="Secondary exporter name", default="") - - -class NetflowSettingsModel(NDNestedModel): - """ - # Summary - - Complete netflow configuration including exporters, records, and monitors. - - ## Raises - - - `ValueError` - If netflow lists are inconsistent with netflow enabled state - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - netflow: bool = Field(description="Enable netflow collection", default=False) - netflow_exporter_collection: List[NetflowExporterModel] = Field( - alias="netflowExporterCollection", description="List of netflow exporters", default_factory=list - ) - netflow_record_collection: List[NetflowRecordModel] = Field(alias="netflowRecordCollection", description="List of netflow records", default_factory=list) - netflow_monitor_collection: List[NetflowMonitorModel] = Field( - alias="netflowMonitorCollection", description="List of netflow monitors", default_factory=list - ) - - -class BootstrapSubnetModel(NDNestedModel): - """ - # Summary - - Bootstrap subnet configuration for fabric initialization. - - ## Raises - - - `ValueError` - If IP addresses or subnet prefix are invalid - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - start_ip: str = Field(alias="startIp", description="Starting IP address of the bootstrap range") - end_ip: str = Field(alias="endIp", description="Ending IP address of the bootstrap range") - default_gateway: str = Field(alias="defaultGateway", description="Default gateway for bootstrap subnet") - subnet_prefix: int = Field(alias="subnetPrefix", description="Subnet prefix length", ge=8, le=30) - - -class TelemetryFlowCollectionModel(NDNestedModel): - """ - # Summary - - Telemetry flow collection configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - traffic_analytics: str = Field(alias="trafficAnalytics", description="Traffic analytics state", default="enabled") - traffic_analytics_scope: str = Field(alias="trafficAnalyticsScope", description="Traffic analytics scope", default="intraFabric") - operating_mode: str = Field(alias="operatingMode", description="Operating mode", default="flowTelemetry") - udp_categorization: str = Field(alias="udpCategorization", description="UDP categorization", default="enabled") - - -class TelemetryMicroburstModel(NDNestedModel): - """ - # Summary - - Microburst detection configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - microburst: bool = Field(description="Enable microburst detection", default=False) - sensitivity: str = Field(description="Microburst sensitivity level", default="low") - - -class TelemetryAnalysisSettingsModel(NDNestedModel): - """ - # Summary - - Telemetry analysis configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - is_enabled: bool = Field(alias="isEnabled", description="Enable telemetry analysis", default=False) - - -class TelemetryEnergyManagementModel(NDNestedModel): - """ - # Summary - - Energy management telemetry configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - cost: float = Field(description="Energy cost per unit", default=1.2) - - -class TelemetryNasExportSettingsModel(NDNestedModel): - """ - # Summary - - NAS export settings for telemetry. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - export_type: str = Field(alias="exportType", description="Export type", default="full") - export_format: str = Field(alias="exportFormat", description="Export format", default="json") - - -class TelemetryNasModel(NDNestedModel): - """ - # Summary - - NAS (Network Attached Storage) telemetry configuration. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - server: str = Field(description="NAS server address", default="") - export_settings: TelemetryNasExportSettingsModel = Field( - alias="exportSettings", description="NAS export settings", default_factory=TelemetryNasExportSettingsModel - ) - - -class TelemetrySettingsModel(NDNestedModel): - """ - # Summary - - Complete telemetry configuration for the fabric. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - flow_collection: TelemetryFlowCollectionModel = Field( - alias="flowCollection", description="Flow collection settings", default_factory=TelemetryFlowCollectionModel - ) - microburst: TelemetryMicroburstModel = Field(description="Microburst detection settings", default_factory=TelemetryMicroburstModel) - analysis_settings: TelemetryAnalysisSettingsModel = Field( - alias="analysisSettings", description="Analysis settings", default_factory=TelemetryAnalysisSettingsModel - ) - nas: TelemetryNasModel = Field(description="NAS telemetry configuration", default_factory=TelemetryNasModel) - energy_management: TelemetryEnergyManagementModel = Field( - alias="energyManagement", description="Energy management settings", default_factory=TelemetryEnergyManagementModel - ) - - -class ExternalStreamingSettingsModel(NDNestedModel): - """ - # Summary - - External streaming configuration for events and data export. - - ## Raises - - None - """ - - model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - - email: List[Dict[str, Any]] = Field(description="Email streaming configuration", default_factory=list) - message_bus: List[Dict[str, Any]] = Field(alias="messageBus", description="Message bus configuration", default_factory=list) - syslog: Dict[str, Any] = Field( - description="Syslog streaming configuration", default_factory=lambda: {"collectionSettings": {"anomalies": []}, "facility": "", "servers": []} - ) - webhooks: List[Dict[str, Any]] = Field(description="Webhook configuration", default_factory=list) - class VxlanIbgpManagementModel(NDNestedModel): """ @@ -1273,7 +1010,7 @@ def validate_bgp_asn(cls, value: str) -> str: - `ValueError` - If the value does not match the expected ASN format """ - if not _BGP_ASN_RE.match(value): + if not BGP_ASN_RE.match(value): raise ValueError(f"Invalid BGP ASN '{value}'. " "Expected a plain integer (1-4294967295) or dotted notation (1-65535.0-65535).") return value From 11861614b21a5c1e2f40ae37bdb872c799e634fc Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 20:43:53 -0400 Subject: [PATCH 11/27] Fix black formatting issue --- .../module_utils/models/manage_fabric/manage_fabric_common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py index efb4c270..26c71f41 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py @@ -42,7 +42,6 @@ Field, ) - # Regex from OpenAPI schema: bgpAsn accepts plain integers (1-4294967295) and # dotted four-byte ASN notation (1-65535).(0-65535) BGP_ASN_RE = re.compile( From 2ec2ee81bbcb4514f8e8bc99340522a64fc8721f Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 21:05:26 -0400 Subject: [PATCH 12/27] Add unit tests for fabric endpoints --- .../test_endpoints_api_v1_manage_fabrics.py | 736 ++++++++++++++++++ 1 file changed, 736 insertions(+) create mode 100644 tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py diff --git a/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py b/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py new file mode 100644 index 00000000..b9430eb8 --- /dev/null +++ b/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py @@ -0,0 +1,736 @@ +# Copyright: (c) 2026, Mike Wiebe (@mwiebe) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +Unit tests for manage_fabrics.py + +Tests the ND Manage Fabrics endpoint classes +""" + +from __future__ import absolute_import, annotations, division, print_function + +# pylint: disable=invalid-name +__metaclass__ = type +# pylint: enable=invalid-name + +import pytest # pylint: disable=unused-import +from ansible_collections.cisco.nd.plugins.module_utils.endpoints.v1.manage.manage_fabrics import ( + EpManageFabricsDelete, + EpManageFabricsGet, + EpManageFabricsListGet, + EpManageFabricsPost, + EpManageFabricsPut, + EpManageFabricsSummaryGet, +) +from ansible_collections.cisco.nd.plugins.module_utils.enums import HttpVerbEnum +from ansible_collections.cisco.nd.tests.unit.module_utils.common_utils import ( + does_not_raise, +) + +# ============================================================================= +# Test: EpManageFabricsGet +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00010(): + """ + # Summary + + Verify EpManageFabricsGet basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is GET + + ## Classes and Methods + + - EpManageFabricsGet.__init__() + - EpManageFabricsGet.verb + - EpManageFabricsGet.class_name + """ + with does_not_raise(): + instance = EpManageFabricsGet() + assert instance.class_name == "EpApiV1ManageFabricsGet" + assert instance.verb == HttpVerbEnum.GET + + +def test_endpoints_api_v1_manage_fabrics_00020(): + """ + # Summary + + Verify EpManageFabricsGet path with fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics/my-fabric" when fabric_name is set + + ## Classes and Methods + + - EpManageFabricsGet.path + - EpManageFabricsGet.fabric_name + """ + with does_not_raise(): + instance = EpManageFabricsGet() + instance.fabric_name = "my-fabric" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric" + + +def test_endpoints_api_v1_manage_fabrics_00030(): + """ + # Summary + + Verify EpManageFabricsGet path without fabric_name raises ValueError + + ## Test + + - Accessing path without setting fabric_name raises ValueError + + ## Classes and Methods + + - EpManageFabricsGet.path + """ + with pytest.raises(ValueError): + instance = EpManageFabricsGet() + _ = instance.path + + +def test_endpoints_api_v1_manage_fabrics_00040(): + """ + # Summary + + Verify EpManageFabricsGet path with fabric_name and cluster_name query param + + ## Test + + - path includes clusterName query parameter when set + + ## Classes and Methods + + - EpManageFabricsGet.path + - EpManageFabricsGet.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsGet() + instance.fabric_name = "my-fabric" + instance.endpoint_params.cluster_name = "cluster1" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric?clusterName=cluster1" + + +# ============================================================================= +# Test: EpManageFabricsListGet +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00100(): + """ + # Summary + + Verify EpManageFabricsListGet basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is GET + + ## Classes and Methods + + - EpManageFabricsListGet.__init__() + - EpManageFabricsListGet.verb + - EpManageFabricsListGet.class_name + """ + with does_not_raise(): + instance = EpManageFabricsListGet() + assert instance.class_name == "EpApiV1ManageFabricsListGet" + assert instance.verb == HttpVerbEnum.GET + + +def test_endpoints_api_v1_manage_fabrics_00110(): + """ + # Summary + + Verify EpManageFabricsListGet path without fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics" when fabric_name is not set + (no error since _require_fabric_name is False) + + ## Classes and Methods + + - EpManageFabricsListGet.path + """ + with does_not_raise(): + instance = EpManageFabricsListGet() + result = instance.path + assert result == "/api/v1/manage/fabrics" + + +def test_endpoints_api_v1_manage_fabrics_00120(): + """ + # Summary + + Verify EpManageFabricsListGet path with category and max query params + + ## Test + + - path includes category and max query parameters when set + + ## Classes and Methods + + - EpManageFabricsListGet.path + - EpManageFabricsListGet.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsListGet() + instance.endpoint_params.category = "fabric" + instance.endpoint_params.max = 10 + result = instance.path + assert "category=fabric" in result + assert "max=10" in result + assert result.startswith("/api/v1/manage/fabrics?") + + +def test_endpoints_api_v1_manage_fabrics_00130(): + """ + # Summary + + Verify EpManageFabricsListGet path with all query params + + ## Test + + - path includes all query parameters when set + (cluster_name, category, filter, max, offset, sort) + + ## Classes and Methods + + - EpManageFabricsListGet.path + - EpManageFabricsListGet.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsListGet() + instance.endpoint_params.cluster_name = "cluster1" + instance.endpoint_params.category = "fabric" + instance.endpoint_params.filter = "name:test" + instance.endpoint_params.max = 25 + instance.endpoint_params.offset = 5 + instance.endpoint_params.sort = "name:desc" + result = instance.path + assert "clusterName=cluster1" in result + assert "category=fabric" in result + assert "max=25" in result + assert "offset=5" in result + assert "sort=name%3Adesc" in result or "sort=name:desc" in result + assert result.startswith("/api/v1/manage/fabrics?") + + +def test_endpoints_api_v1_manage_fabrics_00140(): + """ + # Summary + + Verify EpManageFabricsListGet set_identifiers with None + + ## Test + + - set_identifiers(None) leaves fabric_name as None and path still works + + ## Classes and Methods + + - EpManageFabricsListGet.set_identifiers + - EpManageFabricsListGet.path + """ + with does_not_raise(): + instance = EpManageFabricsListGet() + instance.set_identifiers(None) + result = instance.path + assert instance.fabric_name is None + assert result == "/api/v1/manage/fabrics" + + +def test_endpoints_api_v1_manage_fabrics_00150(): + """ + # Summary + + Verify Pydantic validation rejects max < 1 + + ## Test + + - Setting max to 0 raises ValueError (ge=1 constraint) + + ## Classes and Methods + + - FabricsListEndpointParams.max + """ + with pytest.raises(ValueError): + instance = EpManageFabricsListGet() + instance.endpoint_params = type(instance.endpoint_params)(max=0) + + +# ============================================================================= +# Test: EpManageFabricsPost +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00200(): + """ + # Summary + + Verify EpManageFabricsPost basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is POST + + ## Classes and Methods + + - EpManageFabricsPost.__init__() + - EpManageFabricsPost.verb + - EpManageFabricsPost.class_name + """ + with does_not_raise(): + instance = EpManageFabricsPost() + assert instance.class_name == "EpApiV1ManageFabricsPost" + assert instance.verb == HttpVerbEnum.POST + + +def test_endpoints_api_v1_manage_fabrics_00210(): + """ + # Summary + + Verify EpManageFabricsPost path without fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics" when fabric_name is not set + (no error since _require_fabric_name is False) + + ## Classes and Methods + + - EpManageFabricsPost.path + """ + with does_not_raise(): + instance = EpManageFabricsPost() + result = instance.path + assert result == "/api/v1/manage/fabrics" + + +def test_endpoints_api_v1_manage_fabrics_00220(): + """ + # Summary + + Verify EpManageFabricsPost path with cluster_name query param + + ## Test + + - path includes clusterName query parameter when set + + ## Classes and Methods + + - EpManageFabricsPost.path + - EpManageFabricsPost.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsPost() + instance.endpoint_params.cluster_name = "cluster1" + result = instance.path + assert result == "/api/v1/manage/fabrics?clusterName=cluster1" + + +def test_endpoints_api_v1_manage_fabrics_00230(): + """ + # Summary + + Verify EpManageFabricsPost set_identifiers sets fabric_name + + ## Test + + - set_identifiers sets fabric_name (POST doesn't require it but allows it) + + ## Classes and Methods + + - EpManageFabricsPost.set_identifiers + """ + with does_not_raise(): + instance = EpManageFabricsPost() + instance.set_identifiers("test-fabric") + assert instance.fabric_name == "test-fabric" + + +# ============================================================================= +# Test: EpManageFabricsPut +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00300(): + """ + # Summary + + Verify EpManageFabricsPut basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is PUT + + ## Classes and Methods + + - EpManageFabricsPut.__init__() + - EpManageFabricsPut.verb + - EpManageFabricsPut.class_name + """ + with does_not_raise(): + instance = EpManageFabricsPut() + assert instance.class_name == "EpApiV1ManageFabricsPut" + assert instance.verb == HttpVerbEnum.PUT + + +def test_endpoints_api_v1_manage_fabrics_00310(): + """ + # Summary + + Verify EpManageFabricsPut path with fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics/my-fabric" when fabric_name is set + + ## Classes and Methods + + - EpManageFabricsPut.path + - EpManageFabricsPut.fabric_name + """ + with does_not_raise(): + instance = EpManageFabricsPut() + instance.fabric_name = "my-fabric" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric" + + +def test_endpoints_api_v1_manage_fabrics_00320(): + """ + # Summary + + Verify EpManageFabricsPut path without fabric_name raises ValueError + + ## Test + + - Accessing path without setting fabric_name raises ValueError + + ## Classes and Methods + + - EpManageFabricsPut.path + """ + with pytest.raises(ValueError): + instance = EpManageFabricsPut() + _ = instance.path + + +def test_endpoints_api_v1_manage_fabrics_00340(): + """ + # Summary + + Verify EpManageFabricsPut path with fabric_name and cluster_name query param + + ## Test + + - path includes clusterName query parameter when set + + ## Classes and Methods + + - EpManageFabricsPut.path + - EpManageFabricsPut.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsPut() + instance.fabric_name = "my-fabric" + instance.endpoint_params.cluster_name = "cluster1" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric?clusterName=cluster1" + + +# ============================================================================= +# Test: EpManageFabricsDelete +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00400(): + """ + # Summary + + Verify EpManageFabricsDelete basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is DELETE + + ## Classes and Methods + + - EpManageFabricsDelete.__init__() + - EpManageFabricsDelete.verb + - EpManageFabricsDelete.class_name + """ + with does_not_raise(): + instance = EpManageFabricsDelete() + assert instance.class_name == "EpApiV1ManageFabricsDelete" + assert instance.verb == HttpVerbEnum.DELETE + + +def test_endpoints_api_v1_manage_fabrics_00410(): + """ + # Summary + + Verify EpManageFabricsDelete path with fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics/my-fabric" when fabric_name is set + + ## Classes and Methods + + - EpManageFabricsDelete.path + - EpManageFabricsDelete.fabric_name + """ + with does_not_raise(): + instance = EpManageFabricsDelete() + instance.fabric_name = "my-fabric" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric" + + +def test_endpoints_api_v1_manage_fabrics_00420(): + """ + # Summary + + Verify EpManageFabricsDelete path without fabric_name raises ValueError + + ## Test + + - Accessing path without setting fabric_name raises ValueError + + ## Classes and Methods + + - EpManageFabricsDelete.path + """ + with pytest.raises(ValueError): + instance = EpManageFabricsDelete() + _ = instance.path + + +def test_endpoints_api_v1_manage_fabrics_00430(): + """ + # Summary + + Verify EpManageFabricsDelete path with fabric_name and cluster_name query param + + ## Test + + - path includes clusterName query parameter when set + + ## Classes and Methods + + - EpManageFabricsDelete.path + - EpManageFabricsDelete.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsDelete() + instance.fabric_name = "my-fabric" + instance.endpoint_params.cluster_name = "cluster1" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric?clusterName=cluster1" + + +# ============================================================================= +# Test: EpManageFabricsSummaryGet +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00500(): + """ + # Summary + + Verify EpManageFabricsSummaryGet basic instantiation + + ## Test + + - Instance can be created + - class_name is set correctly + - verb is GET + + ## Classes and Methods + + - EpManageFabricsSummaryGet.__init__() + - EpManageFabricsSummaryGet.verb + - EpManageFabricsSummaryGet.class_name + """ + with does_not_raise(): + instance = EpManageFabricsSummaryGet() + assert instance.class_name == "EpApiV1ManageFabricsSummaryGet" + assert instance.verb == HttpVerbEnum.GET + + +def test_endpoints_api_v1_manage_fabrics_00510(): + """ + # Summary + + Verify EpManageFabricsSummaryGet path with fabric_name + + ## Test + + - path returns "/api/v1/manage/fabrics/my-fabric/summary" when fabric_name is set + + ## Classes and Methods + + - EpManageFabricsSummaryGet.path + - EpManageFabricsSummaryGet.fabric_name + """ + with does_not_raise(): + instance = EpManageFabricsSummaryGet() + instance.fabric_name = "my-fabric" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric/summary" + + +def test_endpoints_api_v1_manage_fabrics_00520(): + """ + # Summary + + Verify EpManageFabricsSummaryGet path without fabric_name raises ValueError + + ## Test + + - Accessing path without setting fabric_name raises ValueError + + ## Classes and Methods + + - EpManageFabricsSummaryGet.path + """ + with pytest.raises(ValueError): + instance = EpManageFabricsSummaryGet() + _ = instance.path + + +def test_endpoints_api_v1_manage_fabrics_00530(): + """ + # Summary + + Verify EpManageFabricsSummaryGet path with fabric_name and cluster_name query param + + ## Test + + - path includes clusterName query parameter when set + + ## Classes and Methods + + - EpManageFabricsSummaryGet.path + - EpManageFabricsSummaryGet.endpoint_params + """ + with does_not_raise(): + instance = EpManageFabricsSummaryGet() + instance.fabric_name = "my-fabric" + instance.endpoint_params.cluster_name = "cluster1" + result = instance.path + assert result == "/api/v1/manage/fabrics/my-fabric/summary?clusterName=cluster1" + + +# ============================================================================= +# Test: All HTTP methods on same endpoint +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00600(): + """ + # Summary + + Verify all HTTP verbs produce correct paths and verbs for the same fabric_name + + ## Test + + - GET, POST, PUT, DELETE all return correct paths for same fabric_name + - Each endpoint returns the correct HTTP verb + + ## Classes and Methods + + - EpManageFabricsGet + - EpManageFabricsPost + - EpManageFabricsPut + - EpManageFabricsDelete + """ + fabric_name = "test-fabric" + + with does_not_raise(): + get_ep = EpManageFabricsGet() + get_ep.fabric_name = fabric_name + + post_ep = EpManageFabricsPost() + # POST is collection-level, but fabric_name can still be set + post_ep.fabric_name = fabric_name + + put_ep = EpManageFabricsPut() + put_ep.fabric_name = fabric_name + + delete_ep = EpManageFabricsDelete() + delete_ep.fabric_name = fabric_name + + expected_path = "/api/v1/manage/fabrics/test-fabric" + assert get_ep.path == expected_path + assert post_ep.path == expected_path + assert put_ep.path == expected_path + assert delete_ep.path == expected_path + + assert get_ep.verb == HttpVerbEnum.GET + assert post_ep.verb == HttpVerbEnum.POST + assert put_ep.verb == HttpVerbEnum.PUT + assert delete_ep.verb == HttpVerbEnum.DELETE + + +# ============================================================================= +# Test: Pydantic validation +# ============================================================================= + + +def test_endpoints_api_v1_manage_fabrics_00610(): + """ + # Summary + + Verify Pydantic validation rejects empty string for fabric_name + + ## Test + + - Empty string is rejected for fabric_name (min_length=1) + + ## Classes and Methods + + - EpManageFabricsGet.__init__() + """ + with pytest.raises(ValueError): + instance = EpManageFabricsGet() + instance.fabric_name = "" + + +def test_endpoints_api_v1_manage_fabrics_00620(): + """ + # Summary + + Verify Pydantic validation rejects fabric_name exceeding max_length + + ## Test + + - fabric_name longer than 64 characters is rejected (max_length=64) + + ## Classes and Methods + + - EpManageFabricsGet.__init__() + """ + with pytest.raises(ValueError): + instance = EpManageFabricsGet() + instance.fabric_name = "a" * 65 From 645eb03a3b935f85b597c1e005fac5a8cc544a81 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Sat, 28 Mar 2026 21:15:55 -0400 Subject: [PATCH 13/27] Fix ansible sanity test failures --- .../endpoints/test_endpoints_api_v1_manage_fabrics.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py b/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py index b9430eb8..cb1b17d4 100644 --- a/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py +++ b/tests/unit/module_utils/endpoints/test_endpoints_api_v1_manage_fabrics.py @@ -95,7 +95,7 @@ def test_endpoints_api_v1_manage_fabrics_00030(): """ with pytest.raises(ValueError): instance = EpManageFabricsGet() - _ = instance.path + result = instance.path # noqa: F841 def test_endpoints_api_v1_manage_fabrics_00040(): @@ -430,7 +430,7 @@ def test_endpoints_api_v1_manage_fabrics_00320(): """ with pytest.raises(ValueError): instance = EpManageFabricsPut() - _ = instance.path + result = instance.path # noqa: F841 def test_endpoints_api_v1_manage_fabrics_00340(): @@ -523,7 +523,7 @@ def test_endpoints_api_v1_manage_fabrics_00420(): """ with pytest.raises(ValueError): instance = EpManageFabricsDelete() - _ = instance.path + result = instance.path # noqa: F841 def test_endpoints_api_v1_manage_fabrics_00430(): @@ -616,7 +616,7 @@ def test_endpoints_api_v1_manage_fabrics_00520(): """ with pytest.raises(ValueError): instance = EpManageFabricsSummaryGet() - _ = instance.path + result = instance.path # noqa: F841 def test_endpoints_api_v1_manage_fabrics_00530(): From 7e1e32372ee7b23e8108423cdb192869d2da0ac8 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 31 Mar 2026 14:15:40 -0400 Subject: [PATCH 14/27] Test cleanup --- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 15 --------------- .../targets/nd_manage_fabric/vars/main.yaml | 9 ++++++++- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index b4385dc2..c77c3efe 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -58,11 +58,6 @@ success_msg: "Merged state is idempotent - no changes on second run" tags: [test_merged, test_merged_idempotent] -# - name: "PAUSE: Review TEST 1b results before continuing" -# ansible.builtin.pause: -# prompt: "TEST 1b complete. Review results and press Enter to continue, or Ctrl+C then A to abort" -# tags: [test_merged, test_merged_update] - - name: "TEST 1c: Update fabric using state merged (modify existing)" cisco.nd.nd_manage_fabric_ibgp: state: merged @@ -133,8 +128,6 @@ auto_unique_vrf_lite_ip_prefix: false per_vrf_loopback_auto_provision: true per_vrf_loopback_ip_range: "10.5.0.0/22" - # per_vrf_loopback_auto_provision_ipv6: false - # per_vrf_loopback_ipv6_range: "fd00::a05:0/112" banner: "" day0_bootstrap: false local_dhcp_server: false @@ -194,9 +187,6 @@ tags: [test_merged, test_merged_validation] delegate_to: localhost -# - debug: msg="{{ merged_fabric_query }}" -# - meta: end_play - - name: "VALIDATION 1: Parse fabric configuration response" set_fact: merged_fabric_config: "{{ merged_fabric_query.json }}" @@ -249,11 +239,6 @@ ======================================== tags: [test_merged, test_merged_validation] -# - name: "PAUSE: Review TEST 1c results before continuing" -# ansible.builtin.pause: -# prompt: "TEST 1c complete. Review results and press Enter to continue, or Ctrl+C then A to abort" -# tags: [test_merged, test_merged_update] - ############################################################################# # TEST 2: STATE REPLACED - Create and manage fabric using replaced state ############################################################################# diff --git a/tests/integration/targets/nd_manage_fabric/vars/main.yaml b/tests/integration/targets/nd_manage_fabric/vars/main.yaml index d15748d5..b10777a5 100644 --- a/tests/integration/targets/nd_manage_fabric/vars/main.yaml +++ b/tests/integration/targets/nd_manage_fabric/vars/main.yaml @@ -26,6 +26,9 @@ common_fabric_config: type: vxlanIbgp bgp_asn: "65001.55" site_id: "65001" + fabric_interface_type: p2p + link_state_routing_protocol: ospf + route_reflector_count: 4 target_subnet_mask: 30 anycast_gateway_mac: "2020.0000.00aa" performance_monitoring: false @@ -34,7 +37,9 @@ common_fabric_config: auto_generate_multicast_group_address: false underlay_multicast_group_address_limit: 128 tenant_routed_multicast: false + tenant_routed_multicast_ipv6: false rendezvous_point_count: 2 + rendezvous_point_mode: asm rendezvous_point_loopback_id: 254 vpc_peer_link_vlan: "3600" vpc_peer_link_enable_native_vlan: false @@ -42,6 +47,7 @@ common_fabric_config: vpc_auto_recovery_timer: 360 vpc_delay_restore_timer: 150 vpc_peer_link_port_channel_id: "500" + vpc_ipv6_neighbor_discovery_sync: true advertise_physical_ip: false vpc_domain_id_range: "1-1000" bgp_loopback_id: 0 @@ -79,7 +85,8 @@ common_fabric_config: auto_unique_vrf_lite_ip_prefix: false per_vrf_loopback_auto_provision: true per_vrf_loopback_ip_range: "10.5.0.0/22" - banner: "" + banner: | + @ADVISORY This is a test fabric deployed by Ansible for validation purposes. Do not make changes to this fabric outside of Ansible or use it for production traffic. ADVISORY@ day0_bootstrap: false local_dhcp_server: false dhcp_protocol_version: dhcpv4 From 771380351011fa468fd2c8634ca5b466c2ebc3b1 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 31 Mar 2026 15:20:24 -0400 Subject: [PATCH 15/27] Add ibgp testing params --- .../nd_manage_fabric/tasks/fabric_ebgp.yaml | 6 +- .../tasks/fabric_external.yaml | 6 +- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 16 +- .../targets/nd_manage_fabric/vars/main.yaml | 152 +++++++++++++++--- 4 files changed, 141 insertions(+), 39 deletions(-) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml index 1d22adb2..eb20baf9 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml @@ -28,7 +28,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - "{{ {'name': ebgp_test_fabric_merged} | combine(common_ebgp_fabric_config) }}" + - "{{ {'name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" register: ebgp_merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +45,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - "{{ {'name': ebgp_test_fabric_merged} | combine(common_ebgp_fabric_config) }}" + - "{{ {'name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" register: ebgp_merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -888,7 +888,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - "{{ {'name': ebgp_test_fabric_deleted} | combine(common_ebgp_fabric_config) }}" + - "{{ {'name': ebgp_test_fabric_deleted} | combine(fabric_config_ebgp) }}" register: ebgp_comparison_fabric_creation tags: [test_comparison] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml index 17b292f1..be7fd482 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml @@ -28,7 +28,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - "{{ {'name': ext_test_fabric_merged} | combine(common_external_fabric_config) }}" + - "{{ {'name': ext_test_fabric_merged} | combine(fabric_config_external) }}" register: ext_merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +45,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - "{{ {'name': ext_test_fabric_merged} | combine(common_external_fabric_config) }}" + - "{{ {'name': ext_test_fabric_merged} | combine(fabric_config_external) }}" register: ext_merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -481,7 +481,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - "{{ {'name': ext_test_fabric_deleted} | combine(common_external_fabric_config) }}" + - "{{ {'name': ext_test_fabric_deleted} | combine(fabric_config_external) }}" register: ext_comparison_fabric_creation tags: [test_comparison] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index c77c3efe..ec559d26 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -28,7 +28,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - "{{ {'name': test_fabric_merged} | combine(common_fabric_config) }}" + - "{{ {'name': test_fabric_merged} | combine(fabric_config_ibgp) }}" register: merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +45,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - "{{ {'name': test_fabric_merged} | combine(common_fabric_config) }}" + - "{{ {'name': test_fabric_merged} | combine(fabric_config_ibgp) }}" register: merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -430,11 +430,6 @@ success_msg: "Replaced state is idempotent - no changes on second run" tags: [test_replaced, test_replaced_idempotent] -# - name: "PAUSE: Review TEST 2b results before continuing" -# ansible.builtin.pause: -# prompt: "TEST 2b complete. Review results and press Enter to continue, or Ctrl+C then A to abort" -# tags: [test_replaced, test_replaced_idempotent] - - name: "TEST 2c: Update fabric using state replaced (complete replacement)" cisco.nd.nd_manage_fabric_ibgp: state: replaced @@ -831,11 +826,6 @@ ======================================== tags: [test_replaced, test_replaced_validation] -# - name: "PAUSE: Review TEST 2c results before continuing" -# ansible.builtin.pause: -# prompt: "TEST 2c complete. Review results and press Enter to continue, or Ctrl+C then A to abort" -# tags: [test_replaced, test_replaced_idempotent] - ############################################################################# # TEST 3: Demonstrate difference between merged and replaced states ############################################################################# @@ -843,7 +833,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - "{{ {'name': test_fabric_deleted} | combine(common_fabric_config) }}" + - "{{ {'name': test_fabric_deleted} | combine(fabric_config_ibgp) }}" register: comparison_fabric_creation tags: [test_comparison] diff --git a/tests/integration/targets/nd_manage_fabric/vars/main.yaml b/tests/integration/targets/nd_manage_fabric/vars/main.yaml index b10777a5..893b17bb 100644 --- a/tests/integration/targets/nd_manage_fabric/vars/main.yaml +++ b/tests/integration/targets/nd_manage_fabric/vars/main.yaml @@ -13,7 +13,8 @@ ext_test_fabric_replaced: "ext_test_fabric_replaced" ext_test_fabric_deleted: "ext_test_fabric_deleted" # Common fabric configuration for all tests -common_fabric_config: +# common_fabric_config: +fabric_config_ibgp: category: fabric location: latitude: 37.7749 @@ -26,12 +27,20 @@ common_fabric_config: type: vxlanIbgp bgp_asn: "65001.55" site_id: "65001" + overlay_mode: cli + underlay_ipv6: false fabric_interface_type: p2p link_state_routing_protocol: ospf + ospf_area_id: "0.0.0.0" route_reflector_count: 4 target_subnet_mask: 30 anycast_gateway_mac: "2020.0000.00aa" + fabric_mtu: 9216 + l2_host_interface_mtu: 9216 performance_monitoring: false + static_underlay_ip_allocation: false + + # Replication / Multicast replication_mode: multicast multicast_group_subnet: "239.1.1.0/25" auto_generate_multicast_group_address: false @@ -41,6 +50,9 @@ common_fabric_config: rendezvous_point_count: 2 rendezvous_point_mode: asm rendezvous_point_loopback_id: 254 + pim_hello_authentication: false + + # vPC vpc_peer_link_vlan: "3600" vpc_peer_link_enable_native_vlan: false vpc_peer_keep_alive_option: loopback @@ -48,45 +60,143 @@ common_fabric_config: vpc_delay_restore_timer: 150 vpc_peer_link_port_channel_id: "500" vpc_ipv6_neighbor_discovery_sync: true + vpc_layer3_peer_router: true + vpc_tor_delay_restore_timer: 30 + fabric_vpc_domain_id: false + fabric_vpc_qos: false + enable_peer_switch: false + + # PIP / Advertising advertise_physical_ip: false + advertise_physical_ip_on_border: true + anycast_border_gateway_advertise_physical_ip: false + + # Domain / Loopback IDs vpc_domain_id_range: "1-1000" bgp_loopback_id: 0 nve_loopback_id: 1 + + # Templates vrf_template: Default_VRF_Universal network_template: Default_Network_Universal vrf_extension_template: Default_VRF_Extension_Universal network_extension_template: Default_Network_Extension_Universal + + # Protocol Authentication + bgp_authentication: false + ospf_authentication: false + bfd: false + macsec: false + vrf_lite_macsec: false + + # BGP / Routing Enhancements + auto_bgp_neighbor_description: true + ibgp_peer_template: "" + leaf_ibgp_peer_template: "" + link_state_routing_tag: "UNDERLAY" + + # Resource ID Ranges l3_vni_no_vlan_default_option: false - fabric_mtu: 9216 - l2_host_interface_mtu: 9216 - tenant_dhcp: true - nxapi: true - nxapi_https_port: 443 - nxapi_http: false - nxapi_http_port: 80 - snmp_trap: true - anycast_border_gateway_advertise_physical_ip: false - greenfield_debug_flag: enable - tcam_allocation: true - real_time_interface_statistics_collection: false - interface_statistics_load_interval: 10 - bgp_loopback_ip_range: "10.2.0.0/22" - nve_loopback_ip_range: "10.3.0.0/22" - anycast_rendezvous_point_ip_range: "10.254.254.0/24" - intra_fabric_subnet_range: "10.4.0.0/16" l2_vni_range: "30000-49000" l3_vni_range: "50000-59000" network_vlan_range: "2300-2999" vrf_vlan_range: "2000-2299" sub_interface_dot1q_range: "2-511" + ip_service_level_agreement_id_range: "10000-19999" + object_tracking_number_range: "100-299" + service_network_vlan_range: "3000-3199" + route_map_sequence_number_range: "1-65534" + + # IP Ranges + bgp_loopback_ip_range: "10.2.0.0/22" + nve_loopback_ip_range: "10.3.0.0/22" + anycast_rendezvous_point_ip_range: "10.254.254.0/24" + intra_fabric_subnet_range: "10.4.0.0/16" + + # VRF Lite / DCI vrf_lite_auto_config: manual vrf_lite_subnet_range: "10.33.0.0/16" vrf_lite_subnet_target_mask: 30 auto_unique_vrf_lite_ip_prefix: false + + # Per-VRF Loopback per_vrf_loopback_auto_provision: true per_vrf_loopback_ip_range: "10.5.0.0/22" + per_vrf_loopback_auto_provision_ipv6: false + + # Management / System + tenant_dhcp: true + nxapi: true + nxapi_https_port: 443 + nxapi_http: false + nxapi_http_port: 80 + snmp_trap: true + cdp: false + real_time_interface_statistics_collection: false + interface_statistics_load_interval: 10 + tcam_allocation: true + inband_management: false + + # Security + security_group_tag: false + private_vlan: false + + # QoS / Queuing + default_queuing_policy: false + aiml_qos: false + + # DLB / AI + dlb: false + ai_load_sharing: false + + # PTP / STP / MPLS + ptp: false + stp_root_option: unmanaged + mpls_handoff: false + + # Leaf / TOR + allow_vlan_on_leaf_tor_pairing: none + leaf_tor_id_range: false + + # OAM / Compliance + nve_hold_down_timer: 180 + next_generation_oam: true + strict_config_compliance_mode: false + greenfield_debug_flag: enable + + # System Policies + advanced_ssh_option: false + copp_policy: strict + power_redundancy_mode: redundant + host_interface_admin_state: true + policy_based_routing: false + + # Freeform Config + extra_config_leaf: "" + extra_config_spine: "" + extra_config_tor: "" + extra_config_intra_fabric_links: "" + extra_config_aaa: "" + pre_interface_config_leaf: "" + pre_interface_config_spine: "" + pre_interface_config_tor: "" + + # Banner banner: | @ADVISORY This is a test fabric deployed by Ansible for validation purposes. Do not make changes to this fabric outside of Ansible or use it for production traffic. ADVISORY@ + + # Backup + real_time_backup: false + scheduled_backup: false + + # Brownfield + brownfield_network_name_format: "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + brownfield_skip_overlay_network_attachments: false + + # Hypershield + allow_smart_switch_onboarding: false + + # Bootstrap / DHCP day0_bootstrap: false local_dhcp_server: false dhcp_protocol_version: dhcpv4 @@ -96,7 +206,8 @@ common_fabric_config: management_ipv4_prefix: 24 # Common External Connectivity fabric configuration for all External tests -common_external_fabric_config: +# common_external_fabric_config: +fabric_config_external: category: fabric location: latitude: 37.7749 @@ -135,7 +246,8 @@ common_external_fabric_config: management_ipv4_prefix: 24 # Common eBGP fabric configuration for all eBGP tests -common_ebgp_fabric_config: +# common_ebgp_fabric_config: +fabric_config_ebgp: category: fabric location: latitude: 37.7749 From 69a2d61940b553dd6b2eac4b17cea4de6cefffa0 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 31 Mar 2026 16:46:48 -0400 Subject: [PATCH 16/27] Fix for merged state and tests --- plugins/module_utils/models/base.py | 60 +++++++++++++++++-- plugins/module_utils/nd_config_collection.py | 10 +++- plugins/module_utils/nd_state_machine.py | 6 +- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 16 ++++- 4 files changed, 83 insertions(+), 9 deletions(-) diff --git a/plugins/module_utils/models/base.py b/plugins/module_utils/models/base.py index a62a12b1..bf0072c4 100644 --- a/plugins/module_utils/models/base.py +++ b/plugins/module_utils/models/base.py @@ -196,16 +196,64 @@ def to_diff_dict(self, **kwargs) -> Dict[str, Any]: **kwargs, ) - def get_diff(self, other: "NDBaseModel") -> bool: - """Diff comparison.""" + def _to_set_fields_diff_dict(self) -> Dict[str, Any]: + """Build diff dict containing only explicitly set fields, recursively. + + Used for merge-state diff comparison where only user-provided fields + should be compared against existing configuration. Fields that received + their value from model defaults are excluded. + """ + full_dump = self.to_diff_dict() + return self._filter_set_fields(full_dump) + + def _filter_set_fields(self, full_dump: Dict[str, Any]) -> Dict[str, Any]: + """Filter a serialized dict to only include explicitly set fields.""" + result = {} + exclude = self.exclude_from_diff or set() + + for field_name in self.model_fields_set: + if field_name in exclude: + continue + + value = getattr(self, field_name) + if value is None: + continue + + field_info = self.__class__.model_fields.get(field_name) + alias = field_info.alias if field_info and field_info.alias else field_name + + if alias not in full_dump: + continue + + if isinstance(value, NDBaseModel): + result[alias] = value._to_set_fields_diff_dict() + else: + result[alias] = full_dump[alias] + + return result + + def get_diff(self, other: "NDBaseModel", only_set_fields: bool = False) -> bool: + """Diff comparison. + + Args: + other: The model to compare against. + only_set_fields: When True, only compare fields explicitly set in + ``other`` (via model_fields_set). This prevents default values + from triggering false diffs during merge operations. + """ self_data = self.to_diff_dict() - other_data = other.to_diff_dict() + if only_set_fields: + other_data = other._to_set_fields_diff_dict() + else: + other_data = other.to_diff_dict() return issubset(other_data, self_data) def merge(self, other: "NDBaseModel") -> "NDBaseModel": """ - Merge another model's non-None values into this instance. + Merge another model's explicitly set, non-None values into this instance. Recursively merges nested NDBaseModel fields. + Only fields present in ``other.model_fields_set`` are applied so that + Pydantic default values do not overwrite existing configuration. Returns self for chaining. """ @@ -216,6 +264,10 @@ def merge(self, other: "NDBaseModel") -> "NDBaseModel": if value is None: continue + # Only merge fields that were explicitly provided, not defaults + if field_name not in other.model_fields_set: + continue + current = getattr(self, field_name) if isinstance(current, NDBaseModel) and isinstance(value, NDBaseModel): current.merge(value) diff --git a/plugins/module_utils/nd_config_collection.py b/plugins/module_utils/nd_config_collection.py index 832cc132..3f17e593 100644 --- a/plugins/module_utils/nd_config_collection.py +++ b/plugins/module_utils/nd_config_collection.py @@ -119,9 +119,15 @@ def delete(self, key: IdentifierKey) -> bool: # Diff Operations - def get_diff_config(self, new_item: NDBaseModel) -> Literal["new", "no_diff", "changed"]: + def get_diff_config(self, new_item: NDBaseModel, only_set_fields: bool = False) -> Literal["new", "no_diff", "changed"]: """ Compare single item against collection. + + Args: + new_item: The proposed configuration item. + only_set_fields: When True, only compare fields explicitly set in + ``new_item``. Useful for merge operations where unspecified + fields should not trigger a diff. """ try: key = self._extract_key(new_item) @@ -133,7 +139,7 @@ def get_diff_config(self, new_item: NDBaseModel) -> Literal["new", "no_diff", "c if existing is None: return "new" - is_subset = existing.get_diff(new_item) + is_subset = existing.get_diff(new_item, only_set_fields=only_set_fields) return "no_diff" if is_subset else "changed" diff --git a/plugins/module_utils/nd_state_machine.py b/plugins/module_utils/nd_state_machine.py index fb812c33..b98627fd 100644 --- a/plugins/module_utils/nd_state_machine.py +++ b/plugins/module_utils/nd_state_machine.py @@ -77,7 +77,11 @@ def _manage_create_update_state(self) -> None: identifier = proposed_item.get_identifier_value() try: # Determine diff status - diff_status = self.existing.get_diff_config(proposed_item) + # For merged state, only compare fields explicitly provided by + # the user so that Pydantic default values do not trigger false + # diffs or overwrite existing configuration. + only_set = self.state == "merged" + diff_status = self.existing.get_diff_config(proposed_item, only_set_fields=only_set) # No changes needed if diff_status == "no_diff": diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index ec559d26..37f0bc9f 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -128,7 +128,6 @@ auto_unique_vrf_lite_ip_prefix: false per_vrf_loopback_auto_provision: true per_vrf_loopback_ip_range: "10.5.0.0/22" - banner: "" day0_bootstrap: false local_dhcp_server: false dhcp_protocol_version: dhcpv4 @@ -224,6 +223,18 @@ success_msg: "✓ Performance Monitoring correctly enabled" tags: [test_merged, test_merged_validation] +- name: "VALIDATION 1: Verify Banner was preserved after merge (not reset to empty)" + assert: + that: + - merged_fabric_config.management.banner is defined + - merged_fabric_config.management.banner | length > 0 + - "'ADVISORY' in merged_fabric_config.management.banner" + fail_msg: >- + Banner validation failed. The banner should be preserved after a merge + that does not specify it. Expected banner containing 'ADVISORY', Actual: '{{ merged_fabric_config.management.banner | default("") }}' + success_msg: "✓ Banner correctly preserved after merge: {{ merged_fabric_config.management.banner }}" + tags: [test_merged, test_merged_validation] + - name: "VALIDATION 1: Display successful validation summary for test_fabric_merged" debug: msg: | @@ -234,8 +245,9 @@ ✓ Site ID: {{ merged_fabric_config.management.siteId }} ✓ Anycast Gateway MAC: {{ merged_fabric_config.management.anycastGatewayMac }} ✓ Performance Monitoring: {{ merged_fabric_config.management.performanceMonitoring }} + ✓ Banner preserved: {{ merged_fabric_config.management.banner }} - All 4 expected changes validated successfully! + All 5 expected validations passed successfully! ======================================== tags: [test_merged, test_merged_validation] From 7e0e51844698d84e81ea6e8a63c5833678a147d5 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 31 Mar 2026 17:12:39 -0400 Subject: [PATCH 17/27] Add more properties in ibgp merged test --- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 287 ++++++++++++++++-- 1 file changed, 257 insertions(+), 30 deletions(-) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index 37f0bc9f..6d837a64 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -191,64 +191,291 @@ merged_fabric_config: "{{ merged_fabric_query.json }}" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Verify BGP ASN was updated to 65002" +# +# Category 1: Properties CHANGED by TEST 1c merge +# +- name: "VALIDATION 1a: Verify changed properties after merge" assert: that: - merged_fabric_config.management.bgpAsn == "65002" - fail_msg: "BGP ASN validation failed. Expected: 65002, Actual: {{ merged_fabric_config.management.bgpAsn }}" - success_msg: "✓ BGP ASN correctly updated to 65002" + - merged_fabric_config.management.siteId == "65002" + - merged_fabric_config.management.anycastGatewayMac == "2020.0000.00bb" + - merged_fabric_config.management.performanceMonitoring == true + fail_msg: >- + Changed properties validation failed. + bgpAsn: {{ merged_fabric_config.management.bgpAsn }} (expected 65002), + siteId: {{ merged_fabric_config.management.siteId }} (expected 65002), + anycastGatewayMac: {{ merged_fabric_config.management.anycastGatewayMac }} (expected 2020.0000.00bb), + performanceMonitoring: {{ merged_fabric_config.management.performanceMonitoring }} (expected true) + success_msg: "✓ All 4 changed properties updated correctly (bgpAsn, siteId, anycastGatewayMac, performanceMonitoring)" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Verify Site ID was updated to 65002" +# +# Category 2: Properties re-specified in TEST 1c with same values +# +- name: "VALIDATION 1b: Verify re-specified management properties (same values)" assert: that: - - merged_fabric_config.management.siteId == "65002" - fail_msg: "Site ID validation failed. Expected: 65002, Actual: {{ merged_fabric_config.management.siteId }}" - success_msg: "✓ Site ID correctly updated to 65002" + # Core + - merged_fabric_config.management.targetSubnetMask == 30 + - merged_fabric_config.management.fabricMtu == 9216 + - merged_fabric_config.management.l2HostInterfaceMtu == 9216 + - merged_fabric_config.management.l3VniNoVlanDefaultOption == false + # Multicast / Replication + - merged_fabric_config.management.replicationMode == "multicast" + - merged_fabric_config.management.multicastGroupSubnet == "239.1.1.0/25" + - merged_fabric_config.management.autoGenerateMulticastGroupAddress == false + - merged_fabric_config.management.underlayMulticastGroupAddressLimit == 128 + - merged_fabric_config.management.tenantRoutedMulticast == false + - merged_fabric_config.management.rendezvousPointCount == 2 + - merged_fabric_config.management.rendezvousPointLoopbackId == 254 + # vPC + - merged_fabric_config.management.vpcPeerLinkVlan == "3600" + - merged_fabric_config.management.vpcPeerLinkEnableNativeVlan == false + - merged_fabric_config.management.vpcPeerKeepAliveOption == "loopback" + - merged_fabric_config.management.vpcAutoRecoveryTimer == 360 + - merged_fabric_config.management.vpcDelayRestoreTimer == 150 + - merged_fabric_config.management.vpcPeerLinkPortChannelId == "500" + # Loopback / Domain IDs + - merged_fabric_config.management.vpcDomainIdRange == "1-1000" + - merged_fabric_config.management.bgpLoopbackId == 0 + - merged_fabric_config.management.nveLoopbackId == 1 + # Templates + - merged_fabric_config.management.vrfTemplate == "Default_VRF_Universal" + - merged_fabric_config.management.networkTemplate == "Default_Network_Universal" + - merged_fabric_config.management.vrfExtensionTemplate == "Default_VRF_Extension_Universal" + - merged_fabric_config.management.networkExtensionTemplate == "Default_Network_Extension_Universal" + fail_msg: "Re-specified management properties validation failed" + success_msg: "✓ All 24 re-specified management properties match expected values" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Verify Anycast Gateway MAC was updated to 2020.0000.00bb" +- name: "VALIDATION 1b: Verify re-specified IP ranges and VNI ranges" assert: that: - - merged_fabric_config.management.anycastGatewayMac == "2020.0000.00bb" - fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00bb, Actual: {{ merged_fabric_config.management.anycastGatewayMac }}" - success_msg: "✓ Anycast Gateway MAC correctly updated to 2020.0000.00bb" + # IP Ranges + - merged_fabric_config.management.bgpLoopbackIpRange == "10.2.0.0/22" + - merged_fabric_config.management.nveLoopbackIpRange == "10.3.0.0/22" + - merged_fabric_config.management.anycastRendezvousPointIpRange == "10.254.254.0/24" + - merged_fabric_config.management.intraFabricSubnetRange == "10.4.0.0/16" + # VNI / VLAN Ranges + - merged_fabric_config.management.l2VniRange == "30000-49000" + - merged_fabric_config.management.l3VniRange == "50000-59000" + - merged_fabric_config.management.networkVlanRange == "2300-2999" + - merged_fabric_config.management.vrfVlanRange == "2000-2299" + - merged_fabric_config.management.subInterfaceDot1qRange == "2-511" + # VRF Lite / DCI + - merged_fabric_config.management.vrfLiteAutoConfig == "manual" + - merged_fabric_config.management.vrfLiteSubnetRange == "10.33.0.0/16" + - merged_fabric_config.management.vrfLiteSubnetTargetMask == 30 + - merged_fabric_config.management.autoUniqueVrfLiteIpPrefix == false + # Per-VRF Loopback + - merged_fabric_config.management.perVrfLoopbackAutoProvision == true + - merged_fabric_config.management.perVrfLoopbackIpRange == "10.5.0.0/22" + fail_msg: "Re-specified IP/VNI ranges validation failed" + success_msg: "✓ All 15 re-specified IP and VNI range properties match expected values" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Verify Performance Monitoring was enabled" +- name: "VALIDATION 1b: Verify re-specified system and NX-API settings" assert: that: - - merged_fabric_config.management.performanceMonitoring == true - fail_msg: "Performance Monitoring validation failed. Expected: true, Actual: {{ merged_fabric_config.management.performanceMonitoring }}" - success_msg: "✓ Performance Monitoring correctly enabled" + - merged_fabric_config.management.tenantDhcp == true + - merged_fabric_config.management.nxapi == true + - merged_fabric_config.management.nxapiHttpsPort == 443 + - merged_fabric_config.management.nxapiHttp == false + - merged_fabric_config.management.nxapiHttpPort == 80 + - merged_fabric_config.management.snmpTrap == true + - merged_fabric_config.management.advertisePhysicalIp == false + - merged_fabric_config.management.anycastBorderGatewayAdvertisePhysicalIp == false + - merged_fabric_config.management.greenfieldDebugFlag == "enable" + - merged_fabric_config.management.tcamAllocation == true + - merged_fabric_config.management.realTimeInterfaceStatisticsCollection == false + # Bootstrap / DHCP + - merged_fabric_config.management.day0Bootstrap == false + - merged_fabric_config.management.localDhcpServer == false + fail_msg: "Re-specified system/NX-API settings validation failed" + success_msg: "✓ All 13 re-specified system and NX-API properties match expected values" + tags: [test_merged, test_merged_validation] + +# +# Category 3: Properties NOT in TEST 1c - MUST be preserved from original create +# These are the critical assertions for validating the merge fix. +# Prior to the fix, these would be reset to Pydantic model defaults. +# +- name: "VALIDATION 1c: Verify preserved underlay/overlay config (not in merge task)" + assert: + that: + - merged_fabric_config.management.overlayMode == "cli" + - merged_fabric_config.management.underlayIpv6 == false + - merged_fabric_config.management.fabricInterfaceType == "p2p" + - merged_fabric_config.management.linkStateRoutingProtocol == "ospf" + - merged_fabric_config.management.ospfAreaId == "0.0.0.0" + - merged_fabric_config.management.routeReflectorCount == 4 + - merged_fabric_config.management.staticUnderlayIpAllocation == false + fail_msg: >- + Preserved underlay/overlay config validation failed. These fields were not + in the merge task and should retain their original values from creation. + success_msg: "✓ All 7 preserved underlay/overlay properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved multicast/RP settings (not in merge task)" + assert: + that: + - merged_fabric_config.management.tenantRoutedMulticastIpv6 == false + - merged_fabric_config.management.rendezvousPointMode == "asm" + - merged_fabric_config.management.pimHelloAuthentication == false + fail_msg: >- + Preserved multicast/RP settings validation failed. These fields were not + in the merge task and should retain their original values. + success_msg: "✓ All 3 preserved multicast/RP properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved vPC extended settings (not in merge task)" + assert: + that: + - merged_fabric_config.management.vpcIpv6NeighborDiscoverySync == true + - merged_fabric_config.management.vpcLayer3PeerRouter == true + - merged_fabric_config.management.vpcTorDelayRestoreTimer == 30 + - merged_fabric_config.management.fabricVpcDomainId == false + - merged_fabric_config.management.fabricVpcQos == false + - merged_fabric_config.management.enablePeerSwitch == false + fail_msg: >- + Preserved vPC extended settings validation failed. These fields were not + in the merge task and should retain their original values. + success_msg: "✓ All 6 preserved vPC extended properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved advertising and protocol auth (not in merge task)" + assert: + that: + - merged_fabric_config.management.advertisePhysicalIpOnBorder == true + - merged_fabric_config.management.bgpAuthentication == false + - merged_fabric_config.management.ospfAuthentication == false + - merged_fabric_config.management.bfd == false + - merged_fabric_config.management.macsec == false + - merged_fabric_config.management.vrfLiteMacsec == false + fail_msg: >- + Preserved advertising/protocol auth validation failed. These fields were + not in the merge task and should retain their original values. + success_msg: "✓ All 6 preserved advertising and protocol authentication properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved BGP/routing enhancements (not in merge task)" + assert: + that: + - merged_fabric_config.management.autoBgpNeighborDescription == true + - merged_fabric_config.management.linkStateRoutingTag == "UNDERLAY" + fail_msg: >- + Preserved BGP/routing enhancements validation failed. + success_msg: "✓ All 2 preserved BGP/routing enhancement properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved resource ID ranges (not in merge task)" + assert: + that: + - merged_fabric_config.management.ipServiceLevelAgreementIdRange == "10000-19999" + - merged_fabric_config.management.objectTrackingNumberRange == "100-299" + - merged_fabric_config.management.serviceNetworkVlanRange == "3000-3199" + - merged_fabric_config.management.routeMapSequenceNumberRange == "1-65534" + - merged_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 == false + fail_msg: >- + Preserved resource ID ranges validation failed. These fields were not + in the merge task and should retain their original values. + success_msg: "✓ All 5 preserved resource ID range properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved system policies (not in merge task)" + assert: + that: + - merged_fabric_config.management.cdp == false + - merged_fabric_config.management.inbandManagement == false + - merged_fabric_config.management.securityGroupTag == false + - merged_fabric_config.management.privateVlan == false + - merged_fabric_config.management.defaultQueuingPolicy == false + - merged_fabric_config.management.aimlQos == false + - merged_fabric_config.management.dlb == false + - merged_fabric_config.management.aiLoadSharing == false + - merged_fabric_config.management.ptp == false + - merged_fabric_config.management.stpRootOption == "unmanaged" + - merged_fabric_config.management.mplsHandoff == false + fail_msg: >- + Preserved system policies validation failed. These fields were not + in the merge task and should retain their original values. + success_msg: "✓ All 11 preserved system policy properties retained correctly" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Verify Banner was preserved after merge (not reset to empty)" +- name: "VALIDATION 1c: Verify preserved OAM/compliance/system settings (not in merge task)" + assert: + that: + - merged_fabric_config.management.allowVlanOnLeafTorPairing == "none" + - merged_fabric_config.management.leafTorIdRange == false + - merged_fabric_config.management.nveHoldDownTimer == 180 + - merged_fabric_config.management.strictConfigComplianceMode == false + - merged_fabric_config.management.advancedSshOption == false + - merged_fabric_config.management.coppPolicy == "strict" + - merged_fabric_config.management.powerRedundancyMode == "redundant" + - merged_fabric_config.management.hostInterfaceAdminState == true + - merged_fabric_config.management.policyBasedRouting == false + fail_msg: >- + Preserved OAM/compliance/system settings validation failed. + success_msg: "✓ All 9 preserved OAM/compliance/system properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1c: Verify preserved banner (not in merge task - critical merge fix test)" assert: that: - merged_fabric_config.management.banner is defined - merged_fabric_config.management.banner | length > 0 - "'ADVISORY' in merged_fabric_config.management.banner" fail_msg: >- - Banner validation failed. The banner should be preserved after a merge - that does not specify it. Expected banner containing 'ADVISORY', Actual: '{{ merged_fabric_config.management.banner | default("") }}' + CRITICAL: Banner was reset to empty by the merge operation! + The banner field was not specified in the merge task and must be + preserved from the original create. Expected banner containing + 'ADVISORY', Actual: '{{ merged_fabric_config.management.banner | default("") }}' success_msg: "✓ Banner correctly preserved after merge: {{ merged_fabric_config.management.banner }}" tags: [test_merged, test_merged_validation] -- name: "VALIDATION 1: Display successful validation summary for test_fabric_merged" +- name: "VALIDATION 1c: Verify preserved backup and brownfield settings (not in merge task)" + assert: + that: + - merged_fabric_config.management.realTimeBackup == false + - merged_fabric_config.management.scheduledBackup == false + - merged_fabric_config.management.brownfieldNetworkNameFormat == "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + - merged_fabric_config.management.brownfieldSkipOverlayNetworkAttachments == false + - merged_fabric_config.management.allowSmartSwitchOnboarding == false + fail_msg: >- + Preserved backup/brownfield settings validation failed. + success_msg: "✓ All 5 preserved backup and brownfield properties retained correctly" + tags: [test_merged, test_merged_validation] + +- name: "VALIDATION 1: Display comprehensive validation summary" debug: msg: | - ======================================== - VALIDATION SUMMARY for test_fabric_merged: - ======================================== - ✓ BGP ASN: {{ merged_fabric_config.management.bgpAsn }} - ✓ Site ID: {{ merged_fabric_config.management.siteId }} - ✓ Anycast Gateway MAC: {{ merged_fabric_config.management.anycastGatewayMac }} - ✓ Performance Monitoring: {{ merged_fabric_config.management.performanceMonitoring }} - ✓ Banner preserved: {{ merged_fabric_config.management.banner }} - - All 5 expected validations passed successfully! - ======================================== + ============================================================ + COMPREHENSIVE VALIDATION SUMMARY for test_fabric_merged + ============================================================ + Category 1 - Changed properties (4 fields): + ✓ bgpAsn: {{ merged_fabric_config.management.bgpAsn }} + ✓ siteId: {{ merged_fabric_config.management.siteId }} + ✓ anycastGatewayMac: {{ merged_fabric_config.management.anycastGatewayMac }} + ✓ performanceMonitoring: {{ merged_fabric_config.management.performanceMonitoring }} + + Category 2 - Re-specified properties (52 fields): + ✓ Management, IP ranges, VNI ranges, system, NX-API, bootstrap + + Category 3 - Preserved properties NOT in merge task (54 fields): + ✓ Underlay/overlay config (7 fields) + ✓ Multicast/RP settings (3 fields) + ✓ vPC extended settings (6 fields) + ✓ Advertising & protocol auth (6 fields) + ✓ BGP/routing enhancements (2 fields) + ✓ Resource ID ranges (5 fields) + ✓ System policies (11 fields) + ✓ OAM/compliance/system (9 fields) + ✓ Banner: {{ merged_fabric_config.management.banner }} + ✓ Backup & brownfield (5 fields) + + Total: 110 properties validated across all categories! + ============================================================ tags: [test_merged, test_merged_validation] ############################################################################# From 173f17c246647ba63ccab238c297f51164ded49d Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 31 Mar 2026 17:55:17 -0400 Subject: [PATCH 18/27] Add more properties in ibgp replaced test --- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 481 +++++++++--------- 1 file changed, 236 insertions(+), 245 deletions(-) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index 6d837a64..aebea06a 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -744,325 +744,316 @@ tags: [test_replaced, test_replaced_validation] # Network Range Validations -- name: "VALIDATION 2: Verify L3 VNI Range was standardized to 50000-59000" - assert: - that: - - replaced_fabric_config.management.l3VniRange == "50000-59000" - fail_msg: "L3 VNI Range validation failed. Expected: 50000-59000, Actual: {{ replaced_fabric_config.management.l3VniRange }}" - success_msg: "✓ L3 VNI Range correctly standardized to 50000-59000" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify L2 VNI Range was standardized to 30000-49000" - assert: - that: - - replaced_fabric_config.management.l2VniRange == "30000-49000" - fail_msg: "L2 VNI Range validation failed. Expected: 30000-49000, Actual: {{ replaced_fabric_config.management.l2VniRange }}" - success_msg: "✓ L2 VNI Range correctly standardized to 30000-49000" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify BGP Loopback IP Range was standardized to 10.2.0.0/22" - assert: - that: - - replaced_fabric_config.management.bgpLoopbackIpRange == "10.2.0.0/22" - fail_msg: "BGP Loopback IP Range validation failed. Expected: 10.2.0.0/22, Actual: {{ replaced_fabric_config.management.bgpLoopbackIpRange }}" - success_msg: "✓ BGP Loopback IP Range correctly standardized to 10.2.0.0/22" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify NVE Loopback IP Range was standardized to 10.3.0.0/22" - assert: - that: - - replaced_fabric_config.management.nveLoopbackIpRange == "10.3.0.0/22" - fail_msg: "NVE Loopback IP Range validation failed. Expected: 10.3.0.0/22, Actual: {{ replaced_fabric_config.management.nveLoopbackIpRange }}" - success_msg: "✓ NVE Loopback IP Range correctly standardized to 10.3.0.0/22" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify Intra-Fabric Subnet Range was standardized to 10.4.0.0/16" - assert: - that: - - replaced_fabric_config.management.intraFabricSubnetRange == "10.4.0.0/16" - fail_msg: "Intra-Fabric Subnet Range validation failed. Expected: 10.4.0.0/16, Actual: {{ replaced_fabric_config.management.intraFabricSubnetRange }}" - success_msg: "✓ Intra-Fabric Subnet Range correctly standardized to 10.4.0.0/16" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify VRF Lite Subnet Range was standardized to 10.33.0.0/16" - assert: - that: - - replaced_fabric_config.management.vrfLiteSubnetRange == "10.33.0.0/16" - fail_msg: "VRF Lite Subnet Range validation failed. Expected: 10.33.0.0/16, Actual: {{ replaced_fabric_config.management.vrfLiteSubnetRange }}" - success_msg: "✓ VRF Lite Subnet Range correctly standardized to 10.33.0.0/16" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify Anycast RP IP Range was standardized to 10.254.254.0/24" - assert: - that: - - replaced_fabric_config.management.anycastRendezvousPointIpRange == "10.254.254.0/24" - fail_msg: "Anycast RP IP Range validation failed. Expected: 10.254.254.0/24, Actual: {{ replaced_fabric_config.management.anycastRendezvousPointIpRange }}" - success_msg: "✓ Anycast RP IP Range correctly standardized to 10.254.254.0/24" - tags: [test_replaced, test_replaced_validation] - -# VLAN Range Validations -- name: "VALIDATION 2: Verify Network VLAN Range was standardized to 2300-2999" - assert: - that: - - replaced_fabric_config.management.networkVlanRange == "2300-2999" - fail_msg: "Network VLAN Range validation failed. Expected: 2300-2999, Actual: {{ replaced_fabric_config.management.networkVlanRange }}" - success_msg: "✓ Network VLAN Range correctly standardized to 2300-2999" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify VRF VLAN Range was standardized to 2000-2299" - assert: - that: - - replaced_fabric_config.management.vrfVlanRange == "2000-2299" - fail_msg: "VRF VLAN Range validation failed. Expected: 2000-2299, Actual: {{ replaced_fabric_config.management.vrfVlanRange }}" - success_msg: "✓ VRF VLAN Range correctly standardized to 2000-2299" - tags: [test_replaced, test_replaced_validation] - -# MTU Validations -- name: "VALIDATION 2: Verify Fabric MTU was increased to 9216" - assert: - that: - - replaced_fabric_config.management.fabricMtu == 9216 - fail_msg: "Fabric MTU validation failed. Expected: 9216, Actual: {{ replaced_fabric_config.management.fabricMtu }}" - success_msg: "✓ Fabric MTU correctly increased to 9216" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify L2 Host Interface MTU was increased to 9216" - assert: - that: - - replaced_fabric_config.management.l2HostInterfaceMtu == 9216 - fail_msg: "L2 Host Interface MTU validation failed. Expected: 9216, Actual: {{ replaced_fabric_config.management.l2HostInterfaceMtu }}" - success_msg: "✓ L2 Host Interface MTU correctly increased to 9216" - tags: [test_replaced, test_replaced_validation] - -# Gateway and Multicast Validations -- name: "VALIDATION 2: Verify Anycast Gateway MAC was standardized to 2020.0000.00aa" - assert: - that: - - replaced_fabric_config.management.anycastGatewayMac == "2020.0000.00aa" - fail_msg: "Anycast Gateway MAC validation failed. Expected: 2020.0000.00aa, Actual: {{ replaced_fabric_config.management.anycastGatewayMac }}" - success_msg: "✓ Anycast Gateway MAC correctly standardized to 2020.0000.00aa" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify Multicast Group Subnet was standardized to 239.1.1.0/25" - assert: - that: - - replaced_fabric_config.management.multicastGroupSubnet == "239.1.1.0/25" - fail_msg: "Multicast Group Subnet validation failed. Expected: 239.1.1.0/25, Actual: {{ replaced_fabric_config.management.multicastGroupSubnet }}" - success_msg: "✓ Multicast Group Subnet correctly standardized to 239.1.1.0/25" - tags: [test_replaced, test_replaced_validation] - -# VPC Configuration Validations -- name: "VALIDATION 2: Verify VPC Auto Recovery Timer was standardized to 360" - assert: - that: - - replaced_fabric_config.management.vpcAutoRecoveryTimer == 360 - fail_msg: "VPC Auto Recovery Timer validation failed. Expected: 360, Actual: {{ replaced_fabric_config.management.vpcAutoRecoveryTimer }}" - success_msg: "✓ VPC Auto Recovery Timer correctly standardized to 360" - tags: [test_replaced, test_replaced_validation] - -- name: "VALIDATION 2: Verify VPC Delay Restore Timer was standardized to 150" +# +# Category 1: Properties explicitly specified in TEST 2c replace task +# +- name: "VALIDATION 2a: Verify explicitly specified properties in replace" assert: that: - - replaced_fabric_config.management.vpcDelayRestoreTimer == 150 - fail_msg: "VPC Delay Restore Timer validation failed. Expected: 150, Actual: {{ replaced_fabric_config.management.vpcDelayRestoreTimer }}" - success_msg: "✓ VPC Delay Restore Timer correctly standardized to 150" + - replaced_fabric_config.management.bgpAsn == "65004" + - replaced_fabric_config.management.siteId == "65004" + - replaced_fabric_config.management.banner == "^ Updated via replaced state ^" + fail_msg: >- + Explicitly specified properties validation failed. + bgpAsn: {{ replaced_fabric_config.management.bgpAsn }} (expected 65004), + siteId: {{ replaced_fabric_config.management.siteId }} (expected 65004), + banner: '{{ replaced_fabric_config.management.banner }}' (expected '^ Updated via replaced state ^') + success_msg: "✓ All 3 explicitly specified properties set correctly (bgpAsn, siteId, banner)" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify VPC Peer Link Port Channel ID was standardized to 500" +# +# Category 2: Properties NOT specified in TEST 2c - MUST revert to Pydantic defaults +# This is the critical replaced behavior: complete replacement means unspecified +# properties get their model default values, NOT the values from TEST 2a. +# +- name: "VALIDATION 2b: Verify core/overlay defaults after replace (was changed in 2a)" assert: that: - - replaced_fabric_config.management.vpcPeerLinkPortChannelId == "500" - fail_msg: "VPC Peer Link Port Channel ID validation failed. Expected: 500, Actual: {{ replaced_fabric_config.management.vpcPeerLinkPortChannelId }}" - success_msg: "✓ VPC Peer Link Port Channel ID correctly standardized to 500" + # These were set to non-default values in TEST 2a and must now revert + - replaced_fabric_config.management.anycastGatewayMac == "2020.0000.00aa" # was 2020.0000.00dd + - replaced_fabric_config.management.performanceMonitoring == false # was true + - replaced_fabric_config.management.targetSubnetMask == 30 + - replaced_fabric_config.management.fabricMtu == 9216 # was 9000 + - replaced_fabric_config.management.l2HostInterfaceMtu == 9216 # was 9000 + - replaced_fabric_config.management.overlayMode == "cli" + - replaced_fabric_config.management.underlayIpv6 == false + - replaced_fabric_config.management.fabricInterfaceType == "p2p" + - replaced_fabric_config.management.linkStateRoutingProtocol == "ospf" + - replaced_fabric_config.management.ospfAreaId == "0.0.0.0" + - replaced_fabric_config.management.staticUnderlayIpAllocation == false + fail_msg: >- + Core/overlay defaults validation failed after replace. Properties not + specified in the replace task must revert to Pydantic model defaults. + success_msg: "✓ All 11 core/overlay properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify VPC Peer Link VLAN was standardized to 3600" +- name: "VALIDATION 2b: Verify multicast/replication defaults after replace" assert: that: - - replaced_fabric_config.management.vpcPeerLinkVlan == "3600" - fail_msg: "VPC Peer Link VLAN validation failed. Expected: 3600, Actual: {{ replaced_fabric_config.management.vpcPeerLinkVlan }}" - success_msg: "✓ VPC Peer Link VLAN correctly standardized to 3600" + - replaced_fabric_config.management.replicationMode == "multicast" + - replaced_fabric_config.management.multicastGroupSubnet == "239.1.1.0/25" # was 239.1.3.0/25 + - replaced_fabric_config.management.autoGenerateMulticastGroupAddress == false + - replaced_fabric_config.management.underlayMulticastGroupAddressLimit == 128 + - replaced_fabric_config.management.tenantRoutedMulticast == false + - replaced_fabric_config.management.tenantRoutedMulticastIpv6 == false + - replaced_fabric_config.management.rendezvousPointCount == 2 # was 4 + - replaced_fabric_config.management.rendezvousPointLoopbackId == 254 # was 253 + - replaced_fabric_config.management.rendezvousPointMode == "asm" + - replaced_fabric_config.management.pimHelloAuthentication == false + fail_msg: >- + Multicast/replication defaults validation failed after replace. + success_msg: "✓ All 10 multicast/replication properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify VPC Domain ID Range was standardized to 1-1000" +- name: "VALIDATION 2b: Verify vPC defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.vpcDomainIdRange == "1-1000" - fail_msg: "VPC Domain ID Range validation failed. Expected: 1-1000, Actual: {{ replaced_fabric_config.management.vpcDomainIdRange }}" - success_msg: "✓ VPC Domain ID Range correctly standardized to 1-1000" + - replaced_fabric_config.management.vpcPeerLinkVlan == "3600" # was 3700 + - replaced_fabric_config.management.vpcPeerLinkEnableNativeVlan == false + - replaced_fabric_config.management.vpcPeerKeepAliveOption == "management" # default is management, 2a used loopback + - replaced_fabric_config.management.vpcAutoRecoveryTimer == 360 # was 300 + - replaced_fabric_config.management.vpcDelayRestoreTimer == 150 # was 120 + - replaced_fabric_config.management.vpcPeerLinkPortChannelId == "500" # was 600 + - replaced_fabric_config.management.vpcDomainIdRange == "1-1000" # was 1-800 + - replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync == true # was false in 2a + - replaced_fabric_config.management.vpcLayer3PeerRouter == true + - replaced_fabric_config.management.vpcTorDelayRestoreTimer == 30 + - replaced_fabric_config.management.fabricVpcDomainId == false + - replaced_fabric_config.management.fabricVpcQos == false + - replaced_fabric_config.management.enablePeerSwitch == false + fail_msg: >- + vPC defaults validation failed after replace. + success_msg: "✓ All 13 vPC properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify VPC IPv6 Neighbor Discovery Sync was enabled" +- name: "VALIDATION 2b: Verify loopback/template/routing defaults after replace" assert: that: - - replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync == true - fail_msg: "VPC IPv6 Neighbor Discovery Sync validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }}" - success_msg: "✓ VPC IPv6 Neighbor Discovery Sync correctly enabled" + - replaced_fabric_config.management.bgpLoopbackId == 0 + - replaced_fabric_config.management.nveLoopbackId == 1 + - replaced_fabric_config.management.routeReflectorCount == 2 + - replaced_fabric_config.management.vrfTemplate == "Default_VRF_Universal" + - replaced_fabric_config.management.networkTemplate == "Default_Network_Universal" + - replaced_fabric_config.management.vrfExtensionTemplate == "Default_VRF_Extension_Universal" + - replaced_fabric_config.management.networkExtensionTemplate == "Default_Network_Extension_Universal" + - replaced_fabric_config.management.autoBgpNeighborDescription == true + - replaced_fabric_config.management.linkStateRoutingTag == "UNDERLAY" + fail_msg: >- + Loopback/template/routing defaults validation failed after replace. + success_msg: "✓ All 9 loopback/template/routing properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -# Multicast Settings Validations -- name: "VALIDATION 2: Verify Rendezvous Point Count was standardized to 2" +- name: "VALIDATION 2b: Verify IP range defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.rendezvousPointCount == 2 - fail_msg: "Rendezvous Point Count validation failed. Expected: 2, Actual: {{ replaced_fabric_config.management.rendezvousPointCount }}" - success_msg: "✓ Rendezvous Point Count correctly standardized to 2" + - replaced_fabric_config.management.bgpLoopbackIpRange == "10.2.0.0/22" # was 10.22.0.0/22 + - replaced_fabric_config.management.nveLoopbackIpRange == "10.3.0.0/22" # was 10.23.0.0/22 + - replaced_fabric_config.management.anycastRendezvousPointIpRange == "10.254.254.0/24" # was 10.254.252.0/24 + - replaced_fabric_config.management.intraFabricSubnetRange == "10.4.0.0/16" # was 10.24.0.0/16 + fail_msg: >- + IP range defaults validation failed after replace. + success_msg: "✓ All 4 IP range properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Rendezvous Point Loopback ID was standardized to 254" +- name: "VALIDATION 2b: Verify VNI/VLAN range defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.rendezvousPointLoopbackId == 254 - fail_msg: "Rendezvous Point Loopback ID validation failed. Expected: 254, Actual: {{ replaced_fabric_config.management.rendezvousPointLoopbackId }}" - success_msg: "✓ Rendezvous Point Loopback ID correctly standardized to 254" + - replaced_fabric_config.management.l2VniRange == "30000-49000" # was 40000-59000 + - replaced_fabric_config.management.l3VniRange == "50000-59000" # was 60000-69000 + - replaced_fabric_config.management.networkVlanRange == "2300-2999" # was 2400-3099 + - replaced_fabric_config.management.vrfVlanRange == "2000-2299" # was 2100-2399 + - replaced_fabric_config.management.subInterfaceDot1qRange == "2-511" + - replaced_fabric_config.management.l3VniNoVlanDefaultOption == false + fail_msg: >- + VNI/VLAN range defaults validation failed after replace. + success_msg: "✓ All 6 VNI/VLAN range properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -# Feature Flag Validations -- name: "VALIDATION 2: Verify TCAM Allocation was enabled" +- name: "VALIDATION 2b: Verify VRF Lite defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.tcamAllocation == true - fail_msg: "TCAM Allocation validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.tcamAllocation }}" - success_msg: "✓ TCAM Allocation correctly enabled" + - replaced_fabric_config.management.vrfLiteAutoConfig == "manual" + - replaced_fabric_config.management.vrfLiteSubnetRange == "10.33.0.0/16" # was 10.53.0.0/16 + - replaced_fabric_config.management.vrfLiteSubnetTargetMask == 30 + - replaced_fabric_config.management.autoUniqueVrfLiteIpPrefix == false + fail_msg: >- + VRF Lite defaults validation failed after replace. + success_msg: "✓ All 4 VRF Lite properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Real Time Interface Statistics Collection was disabled" +- name: "VALIDATION 2b: Verify per-VRF loopback defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.realTimeInterfaceStatisticsCollection == false - fail_msg: "Real Time Interface Statistics Collection validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }}" - success_msg: "✓ Real Time Interface Statistics Collection correctly disabled" + - replaced_fabric_config.management.perVrfLoopbackAutoProvision == false # was true in 2a + - replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 == false # was true in 2a + fail_msg: >- + Per-VRF loopback defaults validation failed after replace. + success_msg: "✓ All 2 per-VRF loopback properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Performance Monitoring was disabled" +- name: "VALIDATION 2b: Verify NX-API/system defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.performanceMonitoring == false - fail_msg: "Performance Monitoring validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.performanceMonitoring }}" - success_msg: "✓ Performance Monitoring correctly disabled" + - replaced_fabric_config.management.nxapi == false # default is false + - replaced_fabric_config.management.nxapiHttp == true # NDFC API default is true (overrides Pydantic default of false) + - replaced_fabric_config.management.nxapiHttpsPort == 443 + - replaced_fabric_config.management.nxapiHttpPort == 80 + - replaced_fabric_config.management.tenantDhcp == true + - replaced_fabric_config.management.snmpTrap == true # was false in 2a + - replaced_fabric_config.management.cdp == false + - replaced_fabric_config.management.tcamAllocation == true # was false in 2a + - replaced_fabric_config.management.realTimeInterfaceStatisticsCollection == false # was true in 2a + fail_msg: >- + NX-API/system defaults validation failed after replace. + success_msg: "✓ All 9 NX-API/system properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Tenant DHCP was enabled" +- name: "VALIDATION 2b: Verify advertising/pip defaults after replace (were changed in 2a)" assert: that: - - replaced_fabric_config.management.tenantDhcp == true - fail_msg: "Tenant DHCP validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.tenantDhcp }}" - success_msg: "✓ Tenant DHCP correctly enabled" + - replaced_fabric_config.management.advertisePhysicalIp == false # was true in 2a + - replaced_fabric_config.management.advertisePhysicalIpOnBorder == true + - replaced_fabric_config.management.anycastBorderGatewayAdvertisePhysicalIp == false # was true in 2a + fail_msg: >- + Advertising/PIP defaults validation failed after replace. + success_msg: "✓ All 3 advertising/PIP properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify SNMP Trap was enabled" +- name: "VALIDATION 2b: Verify greenfield/debug defaults after replace" assert: that: - - replaced_fabric_config.management.snmpTrap == true - fail_msg: "SNMP Trap validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.snmpTrap }}" - success_msg: "✓ SNMP Trap correctly enabled" + - replaced_fabric_config.management.greenfieldDebugFlag == "disable" # default is disable + fail_msg: >- + Greenfield debug flag validation failed after replace. + success_msg: "✓ Greenfield debug flag correctly set to default" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Greenfield Debug Flag was disabled" +- name: "VALIDATION 2b: Verify protocol auth defaults after replace" assert: that: - - replaced_fabric_config.management.greenfieldDebugFlag == "disable" - fail_msg: "Greenfield Debug Flag validation failed. Expected: disable, Actual: {{ replaced_fabric_config.management.greenfieldDebugFlag }}" - success_msg: "✓ Greenfield Debug Flag correctly disabled" + - replaced_fabric_config.management.bgpAuthentication == false + - replaced_fabric_config.management.ospfAuthentication == false + - replaced_fabric_config.management.bfd == false + - replaced_fabric_config.management.macsec == false + - replaced_fabric_config.management.vrfLiteMacsec == false + fail_msg: >- + Protocol authentication defaults validation failed after replace. + success_msg: "✓ All 5 protocol authentication properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify NXAPI HTTP was enabled" +- name: "VALIDATION 2b: Verify system policy defaults after replace" assert: that: - - replaced_fabric_config.management.nxapiHttp == true - fail_msg: "NXAPI HTTP validation failed. Expected: true, Actual: {{ replaced_fabric_config.management.nxapiHttp }}" - success_msg: "✓ NXAPI HTTP correctly enabled" + - replaced_fabric_config.management.securityGroupTag == false + - replaced_fabric_config.management.privateVlan == false + - replaced_fabric_config.management.defaultQueuingPolicy == false + - replaced_fabric_config.management.aimlQos == false + - replaced_fabric_config.management.dlb == false + - replaced_fabric_config.management.aiLoadSharing == false + - replaced_fabric_config.management.ptp == false + - replaced_fabric_config.management.stpRootOption == "unmanaged" + - replaced_fabric_config.management.mplsHandoff == false + - replaced_fabric_config.management.allowVlanOnLeafTorPairing == "none" + - replaced_fabric_config.management.leafTorIdRange == false + fail_msg: >- + System policy defaults validation failed after replace. + success_msg: "✓ All 11 system policy properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify NXAPI was disabled" +- name: "VALIDATION 2b: Verify OAM/compliance/advanced defaults after replace" assert: that: - - replaced_fabric_config.management.nxapi == false - fail_msg: "NXAPI validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.nxapi }}" - success_msg: "✓ NXAPI correctly disabled" + - replaced_fabric_config.management.nveHoldDownTimer == 180 + - replaced_fabric_config.management.nextGenerationOAM == true + - replaced_fabric_config.management.strictConfigComplianceMode == false + - replaced_fabric_config.management.advancedSshOption == false + - replaced_fabric_config.management.coppPolicy == "strict" + - replaced_fabric_config.management.powerRedundancyMode == "redundant" + - replaced_fabric_config.management.hostInterfaceAdminState == true + - replaced_fabric_config.management.policyBasedRouting == false + - replaced_fabric_config.management.inbandManagement == false + fail_msg: >- + OAM/compliance/advanced defaults validation failed after replace. + success_msg: "✓ All 9 OAM/compliance/advanced properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision was disabled" +- name: "VALIDATION 2b: Verify resource ID range defaults after replace" assert: that: - - replaced_fabric_config.management.perVrfLoopbackAutoProvision == false - fail_msg: "Per VRF Loopback Auto Provision validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvision }}" - success_msg: "✓ Per VRF Loopback Auto Provision correctly disabled" + - replaced_fabric_config.management.ipServiceLevelAgreementIdRange == "10000-19999" + - replaced_fabric_config.management.objectTrackingNumberRange == "100-299" + - replaced_fabric_config.management.serviceNetworkVlanRange == "3000-3199" + - replaced_fabric_config.management.routeMapSequenceNumberRange == "1-65534" + fail_msg: >- + Resource ID range defaults validation failed after replace. + success_msg: "✓ All 4 resource ID range properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Verify Per VRF Loopback Auto Provision IPv6 was disabled" +- name: "VALIDATION 2b: Verify backup/brownfield defaults after replace" assert: that: - - replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 == false - fail_msg: "Per VRF Loopback Auto Provision IPv6 validation failed. Expected: false, Actual: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }}" - success_msg: "✓ Per VRF Loopback Auto Provision IPv6 correctly disabled" + - replaced_fabric_config.management.realTimeBackup == false + - replaced_fabric_config.management.scheduledBackup == false + - replaced_fabric_config.management.brownfieldNetworkNameFormat == "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + - replaced_fabric_config.management.brownfieldSkipOverlayNetworkAttachments == false + - replaced_fabric_config.management.allowSmartSwitchOnboarding == false + fail_msg: >- + Backup/brownfield defaults validation failed after replace. + success_msg: "✓ All 5 backup/brownfield properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -# Verify banner was preserved -- name: "VALIDATION 2: Verify Banner was preserved" +- name: "VALIDATION 2b: Verify bootstrap/DHCP defaults after replace" assert: that: - - replaced_fabric_config.management.banner == "^ Updated via replaced state ^" - fail_msg: "Banner validation failed. Expected: '^ Updated via replaced state ^', Actual: {{ replaced_fabric_config.management.banner }}" - success_msg: "✓ Banner correctly preserved: '{{ replaced_fabric_config.management.banner }}'" + - replaced_fabric_config.management.day0Bootstrap == false + - replaced_fabric_config.management.localDhcpServer == false + fail_msg: >- + Bootstrap/DHCP defaults validation failed after replace. + success_msg: "✓ All 2 bootstrap/DHCP properties correctly reverted to defaults" tags: [test_replaced, test_replaced_validation] -- name: "VALIDATION 2: Display successful validation summary for test_fabric_replaced" +- name: "VALIDATION 2: Display comprehensive validation summary for replaced" debug: msg: | - ======================================== - VALIDATION SUMMARY for test_fabric_replaced: - ======================================== - Network Ranges: - ✓ L3 VNI Range: {{ replaced_fabric_config.management.l3VniRange }} - ✓ L2 VNI Range: {{ replaced_fabric_config.management.l2VniRange }} - ✓ BGP Loopback IP Range: {{ replaced_fabric_config.management.bgpLoopbackIpRange }} - ✓ NVE Loopback IP Range: {{ replaced_fabric_config.management.nveLoopbackIpRange }} - ✓ Intra-Fabric Subnet Range: {{ replaced_fabric_config.management.intraFabricSubnetRange }} - ✓ VRF Lite Subnet Range: {{ replaced_fabric_config.management.vrfLiteSubnetRange }} - ✓ Anycast RP IP Range: {{ replaced_fabric_config.management.anycastRendezvousPointIpRange }} - - VLAN Ranges: - ✓ Network VLAN Range: {{ replaced_fabric_config.management.networkVlanRange }} - ✓ VRF VLAN Range: {{ replaced_fabric_config.management.vrfVlanRange }} - - MTU Settings: - ✓ Fabric MTU: {{ replaced_fabric_config.management.fabricMtu }} - ✓ L2 Host Interface MTU: {{ replaced_fabric_config.management.l2HostInterfaceMtu }} - - VPC Configuration: - ✓ VPC Auto Recovery Timer: {{ replaced_fabric_config.management.vpcAutoRecoveryTimer }} - ✓ VPC Delay Restore Timer: {{ replaced_fabric_config.management.vpcDelayRestoreTimer }} - ✓ VPC Peer Link Port Channel ID: {{ replaced_fabric_config.management.vpcPeerLinkPortChannelId }} - ✓ VPC Peer Link VLAN: {{ replaced_fabric_config.management.vpcPeerLinkVlan }} - ✓ VPC Domain ID Range: {{ replaced_fabric_config.management.vpcDomainIdRange }} - ✓ VPC IPv6 Neighbor Discovery Sync: {{ replaced_fabric_config.management.vpcIpv6NeighborDiscoverySync }} - - Gateway & Multicast: - ✓ Anycast Gateway MAC: {{ replaced_fabric_config.management.anycastGatewayMac }} - ✓ Multicast Group Subnet: {{ replaced_fabric_config.management.multicastGroupSubnet }} - ✓ Rendezvous Point Count: {{ replaced_fabric_config.management.rendezvousPointCount }} - ✓ Rendezvous Point Loopback ID: {{ replaced_fabric_config.management.rendezvousPointLoopbackId }} - - Feature Flags: - ✓ TCAM Allocation: {{ replaced_fabric_config.management.tcamAllocation }} - ✓ Real Time Interface Statistics Collection: {{ replaced_fabric_config.management.realTimeInterfaceStatisticsCollection }} - ✓ Performance Monitoring: {{ replaced_fabric_config.management.performanceMonitoring }} - ✓ Tenant DHCP: {{ replaced_fabric_config.management.tenantDhcp }} - ✓ SNMP Trap: {{ replaced_fabric_config.management.snmpTrap }} - ✓ Greenfield Debug Flag: {{ replaced_fabric_config.management.greenfieldDebugFlag }} - ✓ NXAPI HTTP: {{ replaced_fabric_config.management.nxapiHttp }} - ✓ NXAPI: {{ replaced_fabric_config.management.nxapi }} - - Auto-Provisioning: - ✓ Per VRF Loopback Auto Provision: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvision }} - ✓ Per VRF Loopback Auto Provision IPv6: {{ replaced_fabric_config.management.perVrfLoopbackAutoProvisionIpv6 }} - - Preserved Settings: - ✓ Banner: "{{ replaced_fabric_config.management.banner }}" - - All 30+ expected changes validated successfully! - ======================================== + ============================================================ + COMPREHENSIVE VALIDATION SUMMARY for test_fabric_replaced + After TEST 2c: Replace with only bgpAsn, siteId, and banner + ============================================================ + Category 1 - Explicitly specified (3 fields): + ✓ bgpAsn: {{ replaced_fabric_config.management.bgpAsn }} + ✓ siteId: {{ replaced_fabric_config.management.siteId }} + ✓ banner: "{{ replaced_fabric_config.management.banner }}" + + Category 2 - Reverted to Pydantic defaults (108 fields): + ✓ Core/overlay config (11 fields) + ✓ Multicast/replication (10 fields) + ✓ vPC settings (13 fields) + ✓ Loopback/template/routing (9 fields) + ✓ IP ranges (4 fields) + ✓ VNI/VLAN ranges (6 fields) + ✓ VRF Lite (4 fields) + ✓ Per-VRF loopback (2 fields) + ✓ NX-API/system (9 fields) + ✓ Advertising/PIP (3 fields) + ✓ Greenfield debug (1 field) + ✓ Protocol auth (5 fields) + ✓ System policies (11 fields) + ✓ OAM/compliance/advanced (9 fields) + ✓ Resource ID ranges (4 fields) + ✓ Backup/brownfield (5 fields) + ✓ Bootstrap/DHCP (2 fields) + + Total: 111 properties validated! + Key replaced behavior verified: + - Properties from TEST 2a that were NOT in TEST 2c are reset to defaults + - anycastGatewayMac: 2020.0000.00dd → 2020.0000.00aa (default) + - multicastGroupSubnet: 239.1.3.0/25 → 239.1.1.0/25 (default) + - fabricMtu: 9000 → 9216 (default) + - vpcAutoRecoveryTimer: 300 → 360 (default) + - advertisePhysicalIp: true → false (default) + - nxapiHttp: true → false (default) + - perVrfLoopbackAutoProvision: true → false (default) + ============================================================ tags: [test_replaced, test_replaced_validation] ############################################################################# From 9289ec9b1323ffdecb5d19ced4e5c00bd80825e0 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Wed, 1 Apr 2026 11:29:24 -0400 Subject: [PATCH 19/27] Refactor merged fix --- plugins/module_utils/models/base.py | 50 +++----------------- plugins/module_utils/nd_config_collection.py | 6 +-- plugins/module_utils/nd_state_machine.py | 4 +- 3 files changed, 11 insertions(+), 49 deletions(-) diff --git a/plugins/module_utils/models/base.py b/plugins/module_utils/models/base.py index bf0072c4..57689c3a 100644 --- a/plugins/module_utils/models/base.py +++ b/plugins/module_utils/models/base.py @@ -196,56 +196,18 @@ def to_diff_dict(self, **kwargs) -> Dict[str, Any]: **kwargs, ) - def _to_set_fields_diff_dict(self) -> Dict[str, Any]: - """Build diff dict containing only explicitly set fields, recursively. - - Used for merge-state diff comparison where only user-provided fields - should be compared against existing configuration. Fields that received - their value from model defaults are excluded. - """ - full_dump = self.to_diff_dict() - return self._filter_set_fields(full_dump) - - def _filter_set_fields(self, full_dump: Dict[str, Any]) -> Dict[str, Any]: - """Filter a serialized dict to only include explicitly set fields.""" - result = {} - exclude = self.exclude_from_diff or set() - - for field_name in self.model_fields_set: - if field_name in exclude: - continue - - value = getattr(self, field_name) - if value is None: - continue - - field_info = self.__class__.model_fields.get(field_name) - alias = field_info.alias if field_info and field_info.alias else field_name - - if alias not in full_dump: - continue - - if isinstance(value, NDBaseModel): - result[alias] = value._to_set_fields_diff_dict() - else: - result[alias] = full_dump[alias] - - return result - - def get_diff(self, other: "NDBaseModel", only_set_fields: bool = False) -> bool: + def get_diff(self, other: "NDBaseModel", exclude_unset: bool = False) -> bool: """Diff comparison. Args: other: The model to compare against. - only_set_fields: When True, only compare fields explicitly set in - ``other`` (via model_fields_set). This prevents default values - from triggering false diffs during merge operations. + exclude_unset: When True, only compare fields explicitly set in + ``other`` (via Pydantic's ``exclude_unset``). This prevents + default values from triggering false diffs during merge + operations. """ self_data = self.to_diff_dict() - if only_set_fields: - other_data = other._to_set_fields_diff_dict() - else: - other_data = other.to_diff_dict() + other_data = other.to_diff_dict(exclude_unset=exclude_unset) return issubset(other_data, self_data) def merge(self, other: "NDBaseModel") -> "NDBaseModel": diff --git a/plugins/module_utils/nd_config_collection.py b/plugins/module_utils/nd_config_collection.py index 3f17e593..4e3541cd 100644 --- a/plugins/module_utils/nd_config_collection.py +++ b/plugins/module_utils/nd_config_collection.py @@ -119,13 +119,13 @@ def delete(self, key: IdentifierKey) -> bool: # Diff Operations - def get_diff_config(self, new_item: NDBaseModel, only_set_fields: bool = False) -> Literal["new", "no_diff", "changed"]: + def get_diff_config(self, new_item: NDBaseModel, exclude_unset: bool = False) -> Literal["new", "no_diff", "changed"]: """ Compare single item against collection. Args: new_item: The proposed configuration item. - only_set_fields: When True, only compare fields explicitly set in + exclude_unset: When True, only compare fields explicitly set in ``new_item``. Useful for merge operations where unspecified fields should not trigger a diff. """ @@ -139,7 +139,7 @@ def get_diff_config(self, new_item: NDBaseModel, only_set_fields: bool = False) if existing is None: return "new" - is_subset = existing.get_diff(new_item, only_set_fields=only_set_fields) + is_subset = existing.get_diff(new_item, exclude_unset=exclude_unset) return "no_diff" if is_subset else "changed" diff --git a/plugins/module_utils/nd_state_machine.py b/plugins/module_utils/nd_state_machine.py index b98627fd..109c7ca3 100644 --- a/plugins/module_utils/nd_state_machine.py +++ b/plugins/module_utils/nd_state_machine.py @@ -80,8 +80,8 @@ def _manage_create_update_state(self) -> None: # For merged state, only compare fields explicitly provided by # the user so that Pydantic default values do not trigger false # diffs or overwrite existing configuration. - only_set = self.state == "merged" - diff_status = self.existing.get_diff_config(proposed_item, only_set_fields=only_set) + exclude_unset = self.state == "merged" + diff_status = self.existing.get_diff_config(proposed_item, exclude_unset=exclude_unset) # No changes needed if diff_status == "no_diff": From ff1a2d2c9c8dce37715e0a6d6472d2d35237d27b Mon Sep 17 00:00:00 2001 From: mwiebe Date: Wed, 1 Apr 2026 15:44:38 -0400 Subject: [PATCH 20/27] Change name property to fabric_name --- .../manage_fabric/manage_fabric_ebgp.py | 8 +- .../manage_fabric/manage_fabric_external.py | 8 +- .../manage_fabric/manage_fabric_ibgp.py | 8 +- plugins/modules/nd_manage_fabric_ebgp.py | 26 ++-- plugins/modules/nd_manage_fabric_external.py | 18 +-- plugins/modules/nd_manage_fabric_ibgp.py | 24 ++-- .../nd_manage_fabric/tasks/fabric_ebgp.yaml | 42 +++---- .../tasks/fabric_external.yaml | 42 +++---- .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 113 ++++-------------- 9 files changed, 114 insertions(+), 175 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index b134e86c..649fb031 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -817,12 +817,12 @@ class FabricEbgpModel(NDBaseModel): model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow") - identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifiers: ClassVar[Optional[List[str]]] = ["fabric_name"] identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" # Basic Fabric Properties category: Literal["fabric"] = Field(description="Resource category", default="fabric") - name: str = Field(description="Fabric name", min_length=1, max_length=64) + fabric_name: str = Field(alias="name", description="Fabric name", min_length=1, max_length=64) location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations @@ -844,7 +844,7 @@ class FabricEbgpModel(NDBaseModel): alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) - @field_validator("name") + @field_validator("fabric_name") @classmethod def validate_fabric_name(cls, value: str) -> str: """ @@ -876,7 +876,7 @@ def validate_fabric_consistency(self) -> "FabricEbgpModel": # Propagate fabric name to management model if self.management is not None: - self.management.name = self.name + self.management.name = self.fabric_name # Propagate BGP ASN to site_id if both are set and site_id is empty if self.management is not None and self.management.site_id == "" and self.management.bgp_asn is not None: diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index a05a5a79..01b5d025 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -435,12 +435,12 @@ class FabricExternalConnectivityModel(NDBaseModel): str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow" # Allow extra fields from API responses ) - identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifiers: ClassVar[Optional[List[str]]] = ["fabric_name"] identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" # Basic Fabric Properties category: Literal["fabric"] = Field(description="Resource category", default="fabric") - name: str = Field(description="Fabric name", min_length=1, max_length=64) + fabric_name: str = Field(alias="name", description="Fabric name", min_length=1, max_length=64) location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations @@ -490,7 +490,7 @@ class FabricExternalConnectivityModel(NDBaseModel): alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) - @field_validator("name") + @field_validator("fabric_name") @classmethod def validate_fabric_name(cls, value: str) -> str: """ @@ -524,7 +524,7 @@ def validate_fabric_consistency(self) -> "FabricExternalConnectivityModel": # Propagate fabric name to management model if self.management is not None: - self.management.name = self.name + self.management.name = self.fabric_name # Validate telemetry consistency if self.telemetry_collection and self.telemetry_settings is None: diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index a4b32b9e..36beebb5 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -1078,12 +1078,12 @@ class FabricIbgpModel(NDBaseModel): str_strip_whitespace=True, validate_assignment=True, populate_by_name=True, extra="allow" # Allow extra fields from API responses ) - identifiers: ClassVar[Optional[List[str]]] = ["name"] + identifiers: ClassVar[Optional[List[str]]] = ["fabric_name"] identifier_strategy: ClassVar[Optional[Literal["single", "composite", "hierarchical", "singleton"]]] = "single" # Basic Fabric Properties category: Literal["fabric"] = Field(description="Resource category", default="fabric") - name: str = Field(description="Fabric name", min_length=1, max_length=64) + fabric_name: str = Field(alias="name", description="Fabric name", min_length=1, max_length=64) location: Optional[LocationModel] = Field(description="Geographic location of the fabric", default=None) # License and Operations @@ -1105,7 +1105,7 @@ class FabricIbgpModel(NDBaseModel): alias="externalStreamingSettings", description="External streaming settings", default_factory=ExternalStreamingSettingsModel ) - @field_validator("name") + @field_validator("fabric_name") @classmethod def validate_fabric_name(cls, value: str) -> str: """ @@ -1139,7 +1139,7 @@ def validate_fabric_consistency(self) -> "FabricModel": # Propagate fabric name to management model if self.management is not None: - self.management.name = self.name + self.management.name = self.fabric_name # Propagate BGP ASN to Site ID management model if not set if self.management is not None and self.management.site_id == "": diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index e3012be9..7c293236 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -28,11 +28,11 @@ type: list elements: dict suboptions: - name: + fabric_name: description: - The name of the fabric. - Only letters, numbers, underscores, and hyphens are allowed. - - The O(config.name) must be defined when creating, updating or deleting a fabric. + - The O(config.fabric_name) must be defined when creating, updating or deleting a fabric. type: str required: true category: @@ -1384,7 +1384,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: my_ebgp_fabric + - fabric_name: my_ebgp_fabric category: fabric location: latitude: 37.7749 @@ -1465,7 +1465,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: my_ebgp_fabric_static + - fabric_name: my_ebgp_fabric_static category: fabric management: type: vxlanEbgp @@ -1491,7 +1491,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: my_ebgp_fabric + - fabric_name: my_ebgp_fabric category: fabric management: bgp_asn_range: "65100-65199" @@ -1503,7 +1503,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: my_ebgp_fabric + - fabric_name: my_ebgp_fabric category: fabric location: latitude: 37.7749 @@ -1556,7 +1556,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: my_ebgp_fabric + - fabric_name: my_ebgp_fabric category: fabric management: type: vxlanEbgp @@ -1570,7 +1570,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: overridden config: - - name: fabric_east + - fabric_name: fabric_east category: fabric location: latitude: 40.7128 @@ -1597,7 +1597,7 @@ l3_vni_range: "50000-59000" network_vlan_range: "2300-2999" vrf_vlan_range: "2000-2299" - - name: fabric_west + - fabric_name: fabric_west category: fabric location: latitude: 34.0522 @@ -1630,16 +1630,16 @@ cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: my_ebgp_fabric + - fabric_name: my_ebgp_fabric register: result - name: Delete multiple eBGP fabrics in a single task cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: fabric_east - - name: fabric_west - - name: fabric_old + - fabric_name: fabric_east + - fabric_name: fabric_west + - fabric_name: fabric_old register: result """ diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index 459c1e9d..f59dcc84 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -28,11 +28,11 @@ type: list elements: dict suboptions: - name: + fabric_name: description: - The name of the fabric. - Only letters, numbers, underscores, and hyphens are allowed. - - The O(config.name) must be defined when creating, updating or deleting a fabric. + - The O(config.fabric_name) must be defined when creating, updating or deleting a fabric. type: str required: true category: @@ -622,7 +622,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - name: my_ext_fabric + - fabric_name: my_ext_fabric category: fabric location: latitude: 37.7749 @@ -659,7 +659,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - name: my_ext_fabric + - fabric_name: my_ext_fabric category: fabric management: bgp_asn: "65002" @@ -671,7 +671,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: my_ext_fabric + - fabric_name: my_ext_fabric category: fabric location: latitude: 37.7749 @@ -710,7 +710,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: my_ext_fabric + - fabric_name: my_ext_fabric category: fabric management: type: externalConnectivity @@ -721,15 +721,15 @@ cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: my_ext_fabric + - fabric_name: my_ext_fabric register: result - name: Delete multiple fabrics in a single task cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: ext_fabric_east - - name: ext_fabric_west + - fabric_name: ext_fabric_east + - fabric_name: ext_fabric_west register: result """ diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index e743e47d..99c0c942 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -28,11 +28,11 @@ type: list elements: dict suboptions: - name: + fabric_name: description: - The name of the fabric. - Only letters, numbers, underscores, and hyphens are allowed. - - The O(config.name) must be defined when creating, updating or deleting a fabric. + - The O(config.fabric_name) must be defined when creating, updating or deleting a fabric. type: str required: true category: @@ -1556,7 +1556,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - name: my_fabric + - fabric_name: my_fabric category: fabric location: latitude: 37.7749 @@ -1636,7 +1636,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - name: my_fabric + - fabric_name: my_fabric category: fabric management: bgp_asn: "65002" @@ -1649,7 +1649,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - name: my_fabric + - fabric_name: my_fabric category: fabric location: latitude: 37.7749 @@ -1733,7 +1733,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - name: my_fabric + - fabric_name: my_fabric category: fabric management: type: vxlanIbgp @@ -1746,7 +1746,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: overridden config: - - name: fabric_east + - fabric_name: fabric_east category: fabric location: latitude: 40.7128 @@ -1771,7 +1771,7 @@ l3_vni_range: "50000-59000" network_vlan_range: "2300-2999" vrf_vlan_range: "2000-2299" - - name: fabric_west + - fabric_name: fabric_west category: fabric location: latitude: 34.0522 @@ -1802,16 +1802,16 @@ cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: my_fabric + - fabric_name: my_fabric register: result - name: Delete multiple fabrics in a single task cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: fabric_east - - name: fabric_west - - name: fabric_old + - fabric_name: fabric_east + - fabric_name: fabric_west + - fabric_name: fabric_old register: result """ diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml index eb20baf9..27c8d9ec 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml @@ -16,9 +16,9 @@ cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: "{{ ebgp_test_fabric_merged }}" - - name: "{{ ebgp_test_fabric_replaced }}" - - name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "{{ ebgp_test_fabric_merged }}" + - fabric_name: "{{ ebgp_test_fabric_replaced }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" tags: always ############################################################################# @@ -28,7 +28,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - "{{ {'name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" + - "{{ {'fabric_name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" register: ebgp_merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +45,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - "{{ {'name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" + - "{{ {'fabric_name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" register: ebgp_merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -62,7 +62,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: "{{ ebgp_test_fabric_merged }}" + - fabric_name: "{{ ebgp_test_fabric_merged }}" category: fabric location: latitude: 37.7749 @@ -259,7 +259,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: "{{ ebgp_test_fabric_replaced }}" + - fabric_name: "{{ ebgp_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -359,7 +359,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: "{{ ebgp_test_fabric_replaced }}" + - fabric_name: "{{ ebgp_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -459,7 +459,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: "{{ ebgp_test_fabric_replaced }}" + - fabric_name: "{{ ebgp_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -888,7 +888,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - "{{ {'name': ebgp_test_fabric_deleted} | combine(fabric_config_ebgp) }}" + - "{{ {'fabric_name': ebgp_test_fabric_deleted} | combine(fabric_config_ebgp) }}" register: ebgp_comparison_fabric_creation tags: [test_comparison] @@ -896,7 +896,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" category: fabric management: bgp_asn: "65004" # Different from default ASN @@ -919,7 +919,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: replaced config: - - name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" category: fabric management: type: vxlanEbgp @@ -946,7 +946,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" register: ebgp_deleted_result_1 tags: [test_deleted, test_deleted_delete] @@ -963,7 +963,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" register: ebgp_deleted_result_2 tags: [test_deleted, test_deleted_idempotent] @@ -983,7 +983,7 @@ cisco.nd.nd_manage_fabric_ebgp: state: merged config: - - name: "multi_ebgp_fabric_1" + - fabric_name: "multi_ebgp_fabric_1" category: fabric location: latitude: 37.7749 @@ -1062,7 +1062,7 @@ dhcp_end_address: "" management_gateway: "" management_ipv4_prefix: 24 - - name: "multi_ebgp_fabric_2" + - fabric_name: "multi_ebgp_fabric_2" category: fabric location: latitude: 37.7749 @@ -1160,11 +1160,11 @@ cisco.nd.nd_manage_fabric_ebgp: state: deleted config: - - name: "{{ ebgp_test_fabric_merged }}" - - name: "{{ ebgp_test_fabric_replaced }}" - - name: "{{ ebgp_test_fabric_deleted }}" - - name: "multi_ebgp_fabric_1" - - name: "multi_ebgp_fabric_2" + - fabric_name: "{{ ebgp_test_fabric_merged }}" + - fabric_name: "{{ ebgp_test_fabric_replaced }}" + - fabric_name: "{{ ebgp_test_fabric_deleted }}" + - fabric_name: "multi_ebgp_fabric_1" + - fabric_name: "multi_ebgp_fabric_2" ignore_errors: true tags: [cleanup, always] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml index be7fd482..192f8570 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml @@ -16,9 +16,9 @@ cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: "{{ ext_test_fabric_merged }}" - - name: "{{ ext_test_fabric_replaced }}" - - name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "{{ ext_test_fabric_merged }}" + - fabric_name: "{{ ext_test_fabric_replaced }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" tags: always ############################################################################# @@ -28,7 +28,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - "{{ {'name': ext_test_fabric_merged} | combine(fabric_config_external) }}" + - "{{ {'fabric_name': ext_test_fabric_merged} | combine(fabric_config_external) }}" register: ext_merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +45,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - "{{ {'name': ext_test_fabric_merged} | combine(fabric_config_external) }}" + - "{{ {'fabric_name': ext_test_fabric_merged} | combine(fabric_config_external) }}" register: ext_merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -62,7 +62,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - name: "{{ ext_test_fabric_merged }}" + - fabric_name: "{{ ext_test_fabric_merged }}" category: fabric location: latitude: 37.7749 @@ -203,7 +203,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: "{{ ext_test_fabric_replaced }}" + - fabric_name: "{{ ext_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -259,7 +259,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: "{{ ext_test_fabric_replaced }}" + - fabric_name: "{{ ext_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -315,7 +315,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: "{{ ext_test_fabric_replaced }}" + - fabric_name: "{{ ext_test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -481,7 +481,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - "{{ {'name': ext_test_fabric_deleted} | combine(fabric_config_external) }}" + - "{{ {'fabric_name': ext_test_fabric_deleted} | combine(fabric_config_external) }}" register: ext_comparison_fabric_creation tags: [test_comparison] @@ -489,7 +489,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" category: fabric management: bgp_asn: "65099" # Only updating ASN @@ -510,7 +510,7 @@ cisco.nd.nd_manage_fabric_external: state: replaced config: - - name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" category: fabric management: type: externalConnectivity @@ -534,7 +534,7 @@ cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" register: ext_deleted_result_1 tags: [test_deleted, test_deleted_delete] @@ -551,7 +551,7 @@ cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" register: ext_deleted_result_2 tags: [test_deleted, test_deleted_idempotent] @@ -571,7 +571,7 @@ cisco.nd.nd_manage_fabric_external: state: merged config: - - name: "ext_multi_fabric_1" + - fabric_name: "ext_multi_fabric_1" category: fabric location: latitude: 37.7749 @@ -602,7 +602,7 @@ dhcp_end_address: "" management_gateway: "" management_ipv4_prefix: 24 - - name: "ext_multi_fabric_2" + - fabric_name: "ext_multi_fabric_2" category: fabric location: latitude: 37.7749 @@ -652,11 +652,11 @@ cisco.nd.nd_manage_fabric_external: state: deleted config: - - name: "{{ ext_test_fabric_merged }}" - - name: "{{ ext_test_fabric_replaced }}" - - name: "{{ ext_test_fabric_deleted }}" - - name: "ext_multi_fabric_1" - - name: "ext_multi_fabric_2" + - fabric_name: "{{ ext_test_fabric_merged }}" + - fabric_name: "{{ ext_test_fabric_replaced }}" + - fabric_name: "{{ ext_test_fabric_deleted }}" + - fabric_name: "ext_multi_fabric_1" + - fabric_name: "ext_multi_fabric_2" ignore_errors: true tags: [cleanup, always] diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index aebea06a..1a3b4a43 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -16,9 +16,8 @@ cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: "{{ test_fabric_merged }}" - - name: "{{ test_fabric_replaced }}" - - name: "{{ test_fabric_deleted }}" + - fabric_name: "{{ test_fabric_merged }}" + - fabric_name: "{{ test_fabric_replaced }}" tags: always ############################################################################# @@ -28,7 +27,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - "{{ {'name': test_fabric_merged} | combine(fabric_config_ibgp) }}" + - "{{ {'fabric_name': test_fabric_merged} | combine(fabric_config_ibgp) }}" register: merged_result_1 tags: [test_merged, test_merged_create] @@ -45,7 +44,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - "{{ {'name': test_fabric_merged} | combine(fabric_config_ibgp) }}" + - "{{ {'fabric_name': test_fabric_merged} | combine(fabric_config_ibgp) }}" register: merged_result_2 tags: [test_merged, test_merged_idempotent] @@ -62,7 +61,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - name: "{{ test_fabric_merged }}" + - fabric_name: "{{ test_fabric_merged }}" category: fabric location: latitude: 37.7749 @@ -485,7 +484,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - name: "{{ test_fabric_replaced }}" + - fabric_name: "{{ test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -579,7 +578,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - name: "{{ test_fabric_replaced }}" + - fabric_name: "{{ test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -673,7 +672,7 @@ cisco.nd.nd_manage_fabric_ibgp: state: replaced config: - - name: "{{ test_fabric_replaced }}" + - fabric_name: "{{ test_fabric_replaced }}" category: fabric location: latitude: 37.7749 @@ -1057,71 +1056,17 @@ tags: [test_replaced, test_replaced_validation] ############################################################################# -# TEST 3: Demonstrate difference between merged and replaced states +# TEST 3: STATE DELETED - Delete fabrics (uses test_fabric_replaced from TEST 2) ############################################################################# -- name: "TEST 3: Create fabric for merged vs replaced comparison" - cisco.nd.nd_manage_fabric_ibgp: - state: replaced - config: - - "{{ {'name': test_fabric_deleted} | combine(fabric_config_ibgp) }}" - register: comparison_fabric_creation - tags: [test_comparison] - -- name: "TEST 3a: Partial update using merged state (should merge changes)" - cisco.nd.nd_manage_fabric_ibgp: - state: merged - config: - - name: "{{ test_fabric_deleted }}" - category: fabric - management: - bgp_asn: "65099" # Only updating ASN - fabric_mtu: 8000 # Only updating MTU - register: merged_partial_result - tags: [test_comparison, test_merged_partial] - -- name: "TEST 3a: Verify merged state preserves existing configuration" - assert: - that: - - merged_partial_result is changed - - merged_partial_result is not failed - fail_msg: "Partial update with merged state failed" - success_msg: "Merged state successfully performed partial update" - tags: [test_comparison, test_merged_partial] - -- name: "TEST 3b: Partial update using replaced state (should replace entire config)" - cisco.nd.nd_manage_fabric_ibgp: - state: replaced - config: - - name: "{{ test_fabric_deleted }}" - category: fabric - management: - type: vxlanIbgp - bgp_asn: "65100" # Only specifying minimal config for replaced - target_subnet_mask: 30 - register: replaced_partial_result - tags: [test_comparison, test_replaced_partial] - -- name: "TEST 3b: Verify replaced state performs complete replacement" - assert: - that: - - replaced_partial_result is changed - - replaced_partial_result is not failed - fail_msg: "Partial replacement with replaced state failed" - success_msg: "Replaced state successfully performed complete replacement" - tags: [test_comparison, test_replaced_partial] - -############################################################################# -# TEST 4: STATE DELETED - Delete fabrics -############################################################################# -- name: "TEST 4a: Delete fabric using state deleted" +- name: "TEST 3a: Delete fabric using state deleted" cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: "{{ test_fabric_deleted }}" + - fabric_name: "{{ test_fabric_replaced }}" register: deleted_result_1 tags: [test_deleted, test_deleted_delete] -- name: "TEST 4a: Verify fabric was deleted" +- name: "TEST 3a: Verify fabric was deleted" assert: that: - deleted_result_1 is changed @@ -1130,15 +1075,15 @@ success_msg: "Fabric successfully deleted with state deleted" tags: [test_deleted, test_deleted_delete] -- name: "TEST 4b: Delete fabric using state deleted (second run - idempotency test)" +- name: "TEST 3b: Delete fabric using state deleted (second run - idempotency test)" cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: "{{ test_fabric_deleted }}" + - fabric_name: "{{ test_fabric_replaced }}" register: deleted_result_2 tags: [test_deleted, test_deleted_idempotent] -- name: "TEST 4b: Verify deleted state is idempotent" +- name: "TEST 3b: Verify deleted state is idempotent" assert: that: - deleted_result_2 is not changed @@ -1148,13 +1093,13 @@ tags: [test_deleted, test_deleted_idempotent] ############################################################################# -# TEST 5: Multiple fabric operations in single task +# TEST 4: Multiple fabric operations in single task ############################################################################# -- name: "TEST 5: Multiple fabric operations in single task" +- name: "TEST 4: Multiple fabric operations in single task" cisco.nd.nd_manage_fabric_ibgp: state: merged config: - - name: "multi_fabric_1" + - fabric_name: "multi_fabric_1" category: fabric location: latitude: 37.7749 @@ -1232,7 +1177,7 @@ management_gateway: "" management_ipv4_prefix: 24 # management_ipv6_prefix: 64 - - name: "multi_fabric_2" + - fabric_name: "multi_fabric_2" category: fabric location: latitude: 37.7749 @@ -1313,7 +1258,7 @@ register: multi_fabric_result tags: [test_multi, test_multi_create] -- name: "TEST 5: Verify multiple fabrics were created" +- name: "TEST 4: Verify multiple fabrics were created" assert: that: - multi_fabric_result is changed @@ -1329,11 +1274,10 @@ cisco.nd.nd_manage_fabric_ibgp: state: deleted config: - - name: "{{ test_fabric_merged }}" - - name: "{{ test_fabric_replaced }}" - - name: "{{ test_fabric_deleted }}" - - name: "multi_fabric_1" - - name: "multi_fabric_2" + - fabric_name: "{{ test_fabric_merged }}" + - fabric_name: "{{ test_fabric_replaced }}" + - fabric_name: "multi_fabric_1" + - fabric_name: "multi_fabric_2" ignore_errors: true tags: [cleanup, always] @@ -1356,15 +1300,11 @@ - Idempotency: {{ 'PASSED' if replaced_result_2 is not changed else 'FAILED' }} - Replace fabric: {{ 'PASSED' if replaced_result_3 is changed else 'FAILED' }} - ✓ TEST 3: MERGED vs REPLACED Comparison - - Merged partial: {{ 'PASSED' if merged_partial_result is changed else 'FAILED' }} - - Replaced partial: {{ 'PASSED' if replaced_partial_result is changed else 'FAILED' }} - - ✓ TEST 4: STATE DELETED + ✓ TEST 3: STATE DELETED - Delete fabric: {{ 'PASSED' if deleted_result_1 is changed else 'FAILED' }} - Idempotency: {{ 'PASSED' if deleted_result_2 is not changed else 'FAILED' }} - ✓ TEST 5: MULTIPLE FABRICS + ✓ TEST 4: MULTIPLE FABRICS - Multi-create: {{ 'PASSED' if multi_fabric_result is changed else 'FAILED' }} All tests validate: @@ -1372,6 +1312,5 @@ - State replaced: Creates and completely replaces fabric configuration - State deleted: Removes fabrics - Idempotency: All operations are idempotent when run multiple times - - Difference: Merged preserves existing config, replaced overwrites completely ======================================== tags: [summary, always] \ No newline at end of file From 580bcf46686149af73915ec8c099f57cad869232 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Wed, 1 Apr 2026 16:01:53 -0400 Subject: [PATCH 21/27] Add nd_info into integration tests --- .../nd_manage_fabric/tasks/fabric_ebgp.yaml | 19 +++++++++++++++++++ .../tasks/fabric_external.yaml | 19 +++++++++++++++++++ .../nd_manage_fabric/tasks/fabric_ibgp.yaml | 16 ++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml index 27c8d9ec..80671b5d 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ebgp.yaml @@ -9,11 +9,17 @@ msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined +- name: Set vars + ansible.builtin.set_fact: + nd_info: &nd_info + output_level: '{{ api_key_output_level | default("debug") }}' + ############################################################################# # CLEANUP - Ensure clean state before tests ############################################################################# - name: Clean up any existing test fabrics before starting tests cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ ebgp_test_fabric_merged }}" @@ -26,6 +32,7 @@ ############################################################################# - name: "TEST 1a: Create eBGP fabric using state merged (first run)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: merged config: - "{{ {'fabric_name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" @@ -43,6 +50,7 @@ - name: "TEST 1b: Create eBGP fabric using state merged (second run - idempotency test)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: merged config: - "{{ {'fabric_name': ebgp_test_fabric_merged} | combine(fabric_config_ebgp) }}" @@ -60,6 +68,7 @@ - name: "TEST 1c: Update eBGP fabric using state merged (modify existing)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: merged config: - fabric_name: "{{ ebgp_test_fabric_merged }}" @@ -257,6 +266,7 @@ ############################################################################# - name: "TEST 2a: Create eBGP fabric using state replaced (first run)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ ebgp_test_fabric_replaced }}" @@ -357,6 +367,7 @@ - name: "TEST 2b: Create eBGP fabric using state replaced (second run - idempotency test)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ ebgp_test_fabric_replaced }}" @@ -457,6 +468,7 @@ - name: "TEST 2c: Update eBGP fabric using state replaced (complete replacement with minimal config)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ ebgp_test_fabric_replaced }}" @@ -886,6 +898,7 @@ ############################################################################# - name: "TEST 3: Create eBGP fabric for merged vs replaced comparison" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: replaced config: - "{{ {'fabric_name': ebgp_test_fabric_deleted} | combine(fabric_config_ebgp) }}" @@ -894,6 +907,7 @@ - name: "TEST 3a: Partial update using merged state (should merge changes)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: merged config: - fabric_name: "{{ ebgp_test_fabric_deleted }}" @@ -917,6 +931,7 @@ - name: "TEST 3b: Partial update using replaced state (should replace entire config)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ ebgp_test_fabric_deleted }}" @@ -944,6 +959,7 @@ ############################################################################# - name: "TEST 4a: Delete eBGP fabric using state deleted" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ ebgp_test_fabric_deleted }}" @@ -961,6 +977,7 @@ - name: "TEST 4b: Delete eBGP fabric using state deleted (second run - idempotency test)" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ ebgp_test_fabric_deleted }}" @@ -981,6 +998,7 @@ ############################################################################# - name: "TEST 5: Multiple eBGP fabric operations in single task" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: merged config: - fabric_name: "multi_ebgp_fabric_1" @@ -1158,6 +1176,7 @@ ############################################################################# - name: "CLEANUP: Delete all test eBGP fabrics" cisco.nd.nd_manage_fabric_ebgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ ebgp_test_fabric_merged }}" diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml index 192f8570..e5841a81 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_external.yaml @@ -9,11 +9,17 @@ msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined +- name: Set vars + ansible.builtin.set_fact: + nd_info: &nd_info + output_level: '{{ api_key_output_level | default("debug") }}' + ############################################################################# # CLEANUP - Ensure clean state before tests ############################################################################# - name: Clean up any existing test fabrics before starting tests cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: deleted config: - fabric_name: "{{ ext_test_fabric_merged }}" @@ -26,6 +32,7 @@ ############################################################################# - name: "TEST 1a: Create fabric using state merged (first run)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: merged config: - "{{ {'fabric_name': ext_test_fabric_merged} | combine(fabric_config_external) }}" @@ -43,6 +50,7 @@ - name: "TEST 1b: Create fabric using state merged (second run - idempotency test)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: merged config: - "{{ {'fabric_name': ext_test_fabric_merged} | combine(fabric_config_external) }}" @@ -60,6 +68,7 @@ - name: "TEST 1c: Update fabric using state merged (modify existing)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: merged config: - fabric_name: "{{ ext_test_fabric_merged }}" @@ -201,6 +210,7 @@ ############################################################################# - name: "TEST 2a: Create fabric using state replaced (first run)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: replaced config: - fabric_name: "{{ ext_test_fabric_replaced }}" @@ -257,6 +267,7 @@ - name: "TEST 2b: Create fabric using state replaced (second run - idempotency test)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: replaced config: - fabric_name: "{{ ext_test_fabric_replaced }}" @@ -313,6 +324,7 @@ - name: "TEST 2c: Update fabric using state replaced (complete replacement with minimal config)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: replaced config: - fabric_name: "{{ ext_test_fabric_replaced }}" @@ -479,6 +491,7 @@ ############################################################################# - name: "TEST 3: Create fabric for merged vs replaced comparison" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: replaced config: - "{{ {'fabric_name': ext_test_fabric_deleted} | combine(fabric_config_external) }}" @@ -487,6 +500,7 @@ - name: "TEST 3a: Partial update using merged state (should merge changes)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: merged config: - fabric_name: "{{ ext_test_fabric_deleted }}" @@ -508,6 +522,7 @@ - name: "TEST 3b: Partial update using replaced state (should replace entire config)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: replaced config: - fabric_name: "{{ ext_test_fabric_deleted }}" @@ -532,6 +547,7 @@ ############################################################################# - name: "TEST 4a: Delete fabric using state deleted" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: deleted config: - fabric_name: "{{ ext_test_fabric_deleted }}" @@ -549,6 +565,7 @@ - name: "TEST 4b: Delete fabric using state deleted (second run - idempotency test)" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: deleted config: - fabric_name: "{{ ext_test_fabric_deleted }}" @@ -569,6 +586,7 @@ ############################################################################# - name: "TEST 5: Multiple fabric operations in single task" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: merged config: - fabric_name: "ext_multi_fabric_1" @@ -650,6 +668,7 @@ ############################################################################# - name: "CLEANUP: Delete all test fabrics" cisco.nd.nd_manage_fabric_external: + <<: *nd_info state: deleted config: - fabric_name: "{{ ext_test_fabric_merged }}" diff --git a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml index 1a3b4a43..733cd35a 100644 --- a/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml +++ b/tests/integration/targets/nd_manage_fabric/tasks/fabric_ibgp.yaml @@ -9,11 +9,17 @@ msg: 'Please define the following variables: ansible_host, ansible_user and ansible_password.' when: ansible_host is not defined or ansible_user is not defined or ansible_password is not defined +- name: Set vars + ansible.builtin.set_fact: + nd_info: &nd_info + output_level: '{{ api_key_output_level | default("debug") }}' + ############################################################################# # CLEANUP - Ensure clean state before tests ############################################################################# - name: Clean up any existing test fabrics before starting tests cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ test_fabric_merged }}" @@ -25,6 +31,7 @@ ############################################################################# - name: "TEST 1a: Create fabric using state merged (first run)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: merged config: - "{{ {'fabric_name': test_fabric_merged} | combine(fabric_config_ibgp) }}" @@ -42,6 +49,7 @@ - name: "TEST 1b: Create fabric using state merged (second run - idempotency test)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: merged config: - "{{ {'fabric_name': test_fabric_merged} | combine(fabric_config_ibgp) }}" @@ -59,6 +67,7 @@ - name: "TEST 1c: Update fabric using state merged (modify existing)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: merged config: - fabric_name: "{{ test_fabric_merged }}" @@ -482,6 +491,7 @@ ############################################################################# - name: "TEST 2a: Create fabric using state replaced (first run)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ test_fabric_replaced }}" @@ -576,6 +586,7 @@ - name: "TEST 2b: Create fabric using state replaced (second run - idempotency test)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ test_fabric_replaced }}" @@ -670,6 +681,7 @@ - name: "TEST 2c: Update fabric using state replaced (complete replacement)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: replaced config: - fabric_name: "{{ test_fabric_replaced }}" @@ -1060,6 +1072,7 @@ ############################################################################# - name: "TEST 3a: Delete fabric using state deleted" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ test_fabric_replaced }}" @@ -1077,6 +1090,7 @@ - name: "TEST 3b: Delete fabric using state deleted (second run - idempotency test)" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ test_fabric_replaced }}" @@ -1097,6 +1111,7 @@ ############################################################################# - name: "TEST 4: Multiple fabric operations in single task" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: merged config: - fabric_name: "multi_fabric_1" @@ -1272,6 +1287,7 @@ ############################################################################# - name: "CLEANUP: Delete all test fabrics" cisco.nd.nd_manage_fabric_ibgp: + <<: *nd_info state: deleted config: - fabric_name: "{{ test_fabric_merged }}" From c135bd4b31a3df7b8e0743c8c0f9ffcdb272efb5 Mon Sep 17 00:00:00 2001 From: Matt Tarkington Date: Thu, 2 Apr 2026 14:36:15 -0400 Subject: [PATCH 22/27] remove underscore between un & numbered --- .../module_utils/models/manage_fabric/manage_fabric_ibgp.py | 6 +++--- plugins/modules/nd_manage_fabric_ibgp.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index 36beebb5..6c05c962 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -328,15 +328,15 @@ class VxlanIbgpManagementModel(NDNestedModel): extra_config_nxos_bootstrap: str = Field( alias="extraConfigNxosBootstrap", description="Additional CLIs required during device bootup/login e.g. AAA/Radius", default="" ) - un_numbered_bootstrap_loopback_id: int = Field( + unnumbered_bootstrap_loopback_id: int = Field( alias="unNumberedBootstrapLoopbackId", description="Bootstrap Seed Switch Loopback Interface ID", default=253 ) - un_numbered_dhcp_start_address: str = Field( + unnumbered_dhcp_start_address: str = Field( alias="unNumberedDhcpStartAddress", description="Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool", default="", ) - un_numbered_dhcp_end_address: str = Field( + unnumbered_dhcp_end_address: str = Field( alias="unNumberedDhcpEndAddress", description="Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool", default="" ) inband_management: bool = Field(alias="inbandManagement", description="Manage switches with only Inband connectivity", default=False) diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 99c0c942..3df5a361 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -1183,17 +1183,17 @@ - Additional CLIs required during device bootup/login (e.g. AAA/Radius). type: str default: "" - un_numbered_bootstrap_loopback_id: + unnumbered_bootstrap_loopback_id: description: - Bootstrap Seed Switch Loopback Interface ID. type: int default: 253 - un_numbered_dhcp_start_address: + unnumbered_dhcp_start_address: description: - Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool. type: str default: "" - un_numbered_dhcp_end_address: + unnumbered_dhcp_end_address: description: - Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool. type: str From 4c4905b605cf699d211178d6cdda68e5b1f09e56 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Mon, 6 Apr 2026 16:23:01 -0400 Subject: [PATCH 23/27] Address review comments --- plugins/module_utils/endpoints/v1/manage/manage_fabrics.py | 6 +----- plugins/module_utils/models/manage_fabric/enums.py | 4 +--- .../models/manage_fabric/manage_fabric_common.py | 2 -- .../module_utils/models/manage_fabric/manage_fabric_ebgp.py | 2 -- .../models/manage_fabric/manage_fabric_external.py | 2 -- .../module_utils/models/manage_fabric/manage_fabric_ibgp.py | 2 -- 6 files changed, 2 insertions(+), 16 deletions(-) diff --git a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py index 0642a6ea..8a9b1c2b 100644 --- a/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py +++ b/plugins/module_utils/endpoints/v1/manage/manage_fabrics.py @@ -25,13 +25,9 @@ (GET /api/v1/manage/fabrics/{fabric_name}/summary) """ -from __future__ import absolute_import, annotations, division, print_function +from __future__ import annotations -# from plugins.module_utils.endpoints.base import NDBaseEndpoint - -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name from typing import ClassVar, Literal, Optional diff --git a/plugins/module_utils/models/manage_fabric/enums.py b/plugins/module_utils/models/manage_fabric/enums.py index d23a2656..8bb17076 100644 --- a/plugins/module_utils/models/manage_fabric/enums.py +++ b/plugins/module_utils/models/manage_fabric/enums.py @@ -14,11 +14,9 @@ - OperationType: Enum for operation types used by Results to determine if changes have occurred. """ -from __future__ import absolute_import, annotations, division, print_function +from __future__ import annotations -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name from enum import Enum diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py index 26c71f41..08ca1c5a 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py @@ -29,9 +29,7 @@ from __future__ import absolute_import, division, print_function -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name import re from typing import List, Dict, Any diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py index 649fb031..77aef7f4 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ebgp.py @@ -6,9 +6,7 @@ from __future__ import absolute_import, division, print_function -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name import re from typing import List, Dict, Any, Optional, ClassVar, Literal diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py index 01b5d025..893c908a 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_external.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_external.py @@ -6,9 +6,7 @@ from __future__ import absolute_import, division, print_function -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name import re from typing import List, Dict, Optional, ClassVar, Literal diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py index 6c05c962..c2ecb713 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_ibgp.py @@ -6,9 +6,7 @@ from __future__ import absolute_import, division, print_function -# pylint: disable=invalid-name __metaclass__ = type -# pylint: enable=invalid-name import re From aba16bb169848c309d1f68ebdcf0043870d9957d Mon Sep 17 00:00:00 2001 From: mwiebe Date: Mon, 6 Apr 2026 18:14:18 -0400 Subject: [PATCH 24/27] Fix list behavior bug and update module docs --- plugins/module_utils/nd.py | 13 ++++++++++++- plugins/module_utils/utils.py | 13 ++++++++++++- plugins/modules/nd_manage_fabric_ebgp.py | 3 +++ plugins/modules/nd_manage_fabric_external.py | 3 +++ plugins/modules/nd_manage_fabric_ibgp.py | 3 +++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/plugins/module_utils/nd.py b/plugins/module_utils/nd.py index 50a5eeb2..f8f14e5d 100644 --- a/plugins/module_utils/nd.py +++ b/plugins/module_utils/nd.py @@ -75,7 +75,18 @@ def issubset(subset, superset): if not isinstance(subset, dict): if isinstance(subset, list): - return all(item in superset for item in subset) + if len(subset) != len(superset): + return False + + remaining = list(superset) + for item in subset: + for index, candidate in enumerate(remaining): + if issubset(item, candidate) and issubset(candidate, item): + del remaining[index] + break + else: + return False + return True return subset == superset for key, value in subset.items(): diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index 7d05e4af..d4c3e59b 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -36,7 +36,18 @@ def issubset(subset: Any, superset: Any) -> bool: if not isinstance(subset, dict): if isinstance(subset, list): - return all(item in superset for item in subset) + if len(subset) != len(superset): + return False + + remaining = list(superset) + for item in subset: + for index, candidate in enumerate(remaining): + if issubset(item, candidate) and issubset(candidate, item): + del remaining[index] + break + else: + return False + return True return subset == superset for key, value in subset.items(): diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index 7c293236..f2a8a463 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -673,6 +673,9 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. + - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. type: list elements: dict suboptions: diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index f59dcc84..a3196a55 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -138,6 +138,9 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. + - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. type: list elements: dict suboptions: diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 3df5a361..82c0e8b6 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -1221,6 +1221,9 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. + - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. type: list elements: dict suboptions: From 83090cc588f519a7061b0edc7718cad6d0321c52 Mon Sep 17 00:00:00 2001 From: mwiebe Date: Mon, 6 Apr 2026 18:20:44 -0400 Subject: [PATCH 25/27] Make ansible sanity happy --- plugins/modules/nd_manage_fabric_ebgp.py | 9 ++++++--- plugins/modules/nd_manage_fabric_external.py | 9 ++++++--- plugins/modules/nd_manage_fabric_ibgp.py | 9 ++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index f2a8a463..29088940 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -673,9 +673,12 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. - - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. - - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. - - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. + - When O(state=merged), omitting this option preserves the existing collection. + - When O(state=merged), providing this option replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. + - Under O(state=merged), removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. + - When O(state=replaced), omitting this option resets the collection to its default empty value. type: list elements: dict suboptions: diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index a3196a55..0a12f63d 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -138,9 +138,12 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. - - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. - - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. - - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. + - When O(state=merged), omitting this option preserves the existing collection. + - When O(state=merged), providing this option replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. + - Under O(state=merged), removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. + - When O(state=replaced), omitting this option resets the collection to its default empty value. type: list elements: dict suboptions: diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 82c0e8b6..ba62bef2 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -1221,9 +1221,12 @@ bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. - - When O(state=merged), omitting this option preserves the existing collection, but providing it replaces the entire collection with the supplied list. - - Under O(state=merged), entries in this list are not merged item-by-item. Removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. - - When O(state=replaced), this option is also treated as the exact desired collection. If omitted, the collection is reset to its default empty value. + - When O(state=merged), omitting this option preserves the existing collection. + - When O(state=merged), providing this option replaces the entire collection with the supplied list. + - Under O(state=merged), entries in this list are not merged item-by-item. + - Under O(state=merged), removing one entry from the playbook removes it from the fabric, and setting an empty list clears the collection. + - When O(state=replaced), this option is also treated as the exact desired collection. + - When O(state=replaced), omitting this option resets the collection to its default empty value. type: list elements: dict suboptions: From 699f9d507f1cceace3571ac0a1bdc369c31938eb Mon Sep 17 00:00:00 2001 From: mwiebe Date: Mon, 6 Apr 2026 18:52:31 -0400 Subject: [PATCH 26/27] Make netflow_exporter udp_port optional --- .../module_utils/models/manage_fabric/manage_fabric_common.py | 4 ++-- plugins/modules/nd_manage_fabric_ebgp.py | 1 - plugins/modules/nd_manage_fabric_external.py | 1 - plugins/modules/nd_manage_fabric_ibgp.py | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py index 08ca1c5a..9cb3af47 100644 --- a/plugins/module_utils/models/manage_fabric/manage_fabric_common.py +++ b/plugins/module_utils/models/manage_fabric/manage_fabric_common.py @@ -32,7 +32,7 @@ __metaclass__ = type import re -from typing import List, Dict, Any +from typing import List, Dict, Any, Optional from ansible_collections.cisco.nd.plugins.module_utils.models.nested import NDNestedModel from ansible_collections.cisco.nd.plugins.module_utils.common.pydantic_compat import ( @@ -86,7 +86,7 @@ class NetflowExporterModel(NDNestedModel): exporter_ip: str = Field(alias="exporterIp", description="IP address of the netflow collector") vrf: str = Field(description="VRF name for the exporter", default="management") source_interface_name: str = Field(alias="sourceInterfaceName", description="Source interface name") - udp_port: int = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535) + udp_port: Optional[int] = Field(alias="udpPort", description="UDP port for netflow export", ge=1, le=65535, default=None) class NetflowRecordModel(NDNestedModel): diff --git a/plugins/modules/nd_manage_fabric_ebgp.py b/plugins/modules/nd_manage_fabric_ebgp.py index 29088940..dc6affaf 100644 --- a/plugins/modules/nd_manage_fabric_ebgp.py +++ b/plugins/modules/nd_manage_fabric_ebgp.py @@ -778,7 +778,6 @@ description: - UDP port for netflow export (1-65535). type: int - required: true netflow_record_collection: description: - List of netflow records. diff --git a/plugins/modules/nd_manage_fabric_external.py b/plugins/modules/nd_manage_fabric_external.py index 0a12f63d..0bed6cc3 100644 --- a/plugins/modules/nd_manage_fabric_external.py +++ b/plugins/modules/nd_manage_fabric_external.py @@ -346,7 +346,6 @@ description: - UDP port for netflow export (1-65535). type: int - required: true netflow_record_collection: description: - List of netflow records. diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index ba62bef2..428e9f06 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -1290,7 +1290,6 @@ description: - UDP port for netflow export (1-65535). type: int - required: true netflow_record_collection: description: - List of netflow records. From 8824c9d27f184e6ee54c8504dc1588061350952e Mon Sep 17 00:00:00 2001 From: mwiebe Date: Tue, 7 Apr 2026 12:32:05 -0400 Subject: [PATCH 27/27] Organize ibgp module doc header --- plugins/modules/nd_manage_fabric_ibgp.py | 1350 +++++++++++----------- 1 file changed, 688 insertions(+), 662 deletions(-) diff --git a/plugins/modules/nd_manage_fabric_ibgp.py b/plugins/modules/nd_manage_fabric_ibgp.py index 428e9f06..61ac1f0d 100644 --- a/plugins/modules/nd_manage_fabric_ibgp.py +++ b/plugins/modules/nd_manage_fabric_ibgp.py @@ -100,8 +100,10 @@ management: description: - The iBGP VXLAN management configuration for the fabric. + - Properties are grouped by template section for readability in the module documentation source. type: dict suboptions: + # General type: description: - The fabric management type. Must be C(vxlanIbgp) for iBGP VXLAN fabrics. @@ -114,23 +116,56 @@ - Accepts a plain integer (1-4294967295) or dotted notation (1-65535.0-65535). type: str required: true - site_id: + underlay_ipv6: description: - - The site identifier for the fabric (for EVPN Multi-Site support). - - Must be a numeric value between 1 and 281474976710655. - - Defaults to the value of O(config.management.bgp_asn) if not provided. + - Enable IPv6 underlay. + type: bool + default: false + fabric_interface_type: + description: + - The fabric interface type. Numbered (Point-to-Point) or unnumbered. type: str - default: "" + default: p2p + choices: [ p2p, unNumbered ] + link_state_routing_protocol: + description: + - The underlay link-state routing protocol. + type: str + default: ospf + choices: [ ospf, isis ] target_subnet_mask: description: - The target subnet mask for intra-fabric links (24-31). type: int default: 30 + ipv6_link_local: + description: + - Enable IPv6 link-local addressing. + type: bool + default: true + ipv6_subnet_target_mask: + description: + - The IPv6 subnet target mask. + type: int + default: 126 + route_reflector_count: + description: + - The number of spines acting as BGP route reflectors. + type: int + default: 2 + choices: [ 2, 4 ] anycast_gateway_mac: description: - The anycast gateway MAC address in xxxx.xxxx.xxxx format. type: str default: 2020.0000.00aa + performance_monitoring: + description: + - Enable performance monitoring. + type: bool + default: false + + # Replication replication_mode: description: - The multicast replication mode. @@ -142,6 +177,11 @@ - The multicast group subnet. type: str default: "239.1.1.0/25" + ipv6_multicast_group_subnet: + description: + - The IPv6 multicast group subnet. + type: str + default: "ff1e::/121" auto_generate_multicast_group_address: description: - Automatically generate multicast group addresses. @@ -159,121 +199,86 @@ - Enable tenant routed multicast. type: bool default: false + tenant_routed_multicast_ipv6: + description: + - Enable tenant routed multicast for IPv6. + type: bool + default: false rendezvous_point_count: description: - The number of spines acting as Rendezvous-Points (RPs). type: int default: 2 choices: [ 2, 4 ] + rendezvous_point_mode: + description: + - Multicast rendezvous point mode. For IPv6 underlay, use C(asm) only. + type: str + default: asm + choices: [ asm, bidir ] rendezvous_point_loopback_id: description: - The rendezvous point loopback interface ID (0-1023). type: int default: 254 - overlay_mode: - description: - - The overlay configuration mode. - type: str - default: cli - choices: [ cli, config-profile ] - link_state_routing_protocol: - description: - - The underlay link-state routing protocol. - type: str - default: ospf - choices: [ ospf, isis ] - ospf_area_id: - description: - - The OSPF area ID. - type: str - default: "0.0.0.0" - fabric_interface_type: - description: - - The fabric interface type. Numbered (Point-to-Point) or unnumbered. - type: str - default: p2p - choices: [ p2p, unNumbered ] - bgp_loopback_id: + phantom_rendezvous_point_loopback_id1: description: - - The BGP loopback interface ID (0-1023). + - Underlay phantom RP loopback primary ID for PIM Bi-dir deployments. type: int - default: 0 - nve_loopback_id: + default: 2 + phantom_rendezvous_point_loopback_id2: description: - - The NVE loopback interface ID (0-1023). + - Underlay phantom RP loopback secondary ID for PIM Bi-dir deployments. type: int - default: 1 - route_reflector_count: + default: 3 + phantom_rendezvous_point_loopback_id3: description: - - The number of spines acting as BGP route reflectors. + - Underlay phantom RP loopback tertiary ID for PIM Bi-dir deployments. type: int - default: 2 - choices: [ 2, 4 ] - bgp_loopback_ip_range: - description: - - The BGP loopback IP address pool. - type: str - default: "10.2.0.0/22" - nve_loopback_ip_range: + default: 4 + phantom_rendezvous_point_loopback_id4: description: - - The NVE loopback IP address pool. - type: str - default: "10.3.0.0/22" + - Underlay phantom RP loopback quaternary ID for PIM Bi-dir deployments. + type: int + default: 5 anycast_rendezvous_point_ip_range: description: - The anycast rendezvous point IP address pool. type: str default: "10.254.254.0/24" - intra_fabric_subnet_range: - description: - - The intra-fabric subnet IP address pool. - type: str - default: "10.4.0.0/16" - router_id_range: - description: - - The BGP router ID range in IPv4 subnet format. Used for IPv6 underlay. - type: str - default: "10.2.0.0/23" - l2_vni_range: + ipv6_anycast_rendezvous_point_ip_range: description: - - The Layer 2 VNI range. + - The IPv6 anycast rendezvous point IP address pool. type: str - default: "30000-49000" - l3_vni_range: + default: "fd00::254:254:0/118" + l3vni_multicast_group: description: - - The Layer 3 VNI range. + - Default underlay multicast group IPv4 address assigned for every overlay VRF. type: str - default: "50000-59000" - network_vlan_range: + default: "239.1.1.0" + l3_vni_ipv6_multicast_group: description: - - The network VLAN range. + - Default underlay multicast group IPv6 address assigned for every overlay VRF. type: str - default: "2300-2999" - vrf_vlan_range: + default: "ff1e::" + mvpn_vrf_route_import_id: description: - - The VRF VLAN range. - type: str - default: "2000-2299" - sub_interface_dot1q_range: + - Enable MVPN VRI ID generation for Tenant Routed Multicast with IPv4 underlay. + type: bool + default: true + mvpn_vrf_route_import_id_range: description: - - The sub-interface 802.1q range (minimum 2, maximum 4093). + - MVPN VRI ID range (minimum 1, maximum 65535) for vPC. + - Applicable when TRM is enabled with IPv6 underlay, or mvpn_vrf_route_import_id is enabled with IPv4 underlay. type: str - default: "2-511" - l3_vni_no_vlan_default_option: + default: "" + vrf_route_import_id_reallocation: description: - - Enable L3 VNI no-VLAN default option. + - One time VRI ID re-allocation based on MVPN VRI ID Range. type: bool default: false - fabric_mtu: - description: - - The fabric MTU size (1500-9216). - type: int - default: 9216 - l2_host_interface_mtu: - description: - - The L2 host interface MTU size (1500-9216). - type: int - default: 9216 + + # vPC vpc_domain_id_range: description: - The vPC domain ID range. @@ -350,36 +355,6 @@ - Enable peer switch. type: bool default: false - vrf_template: - description: - - The VRF template name. - type: str - default: Default_VRF_Universal - network_template: - description: - - The network template name. - type: str - default: Default_Network_Universal - vrf_extension_template: - description: - - The VRF extension template name. - type: str - default: Default_VRF_Extension_Universal - network_extension_template: - description: - - The network extension template name. - type: str - default: Default_Network_Extension_Universal - performance_monitoring: - description: - - Enable performance monitoring. - type: bool - default: false - tenant_dhcp: - description: - - Enable tenant DHCP. - type: bool - default: true advertise_physical_ip: description: - Advertise physical IP address for NVE loopback. @@ -395,57 +370,64 @@ - Enable anycast border gateway to advertise physical IP. type: bool default: false - snmp_trap: - description: - - Enable SNMP traps. - type: bool - default: true - cdp: - description: - - Enable CDP. - type: bool - default: false - tcam_allocation: + allow_vlan_on_leaf_tor_pairing: description: - - Enable TCAM allocation. - type: bool - default: true - real_time_interface_statistics_collection: + - "Set trunk allowed VLAN to 'none' or 'all' for leaf-TOR pairing port-channels." + type: str + default: none + choices: [ none, all ] + leaf_tor_id_range: description: - - Enable real-time interface statistics collection. + - Use specific vPC/Port-channel ID range for leaf-TOR pairings. type: bool default: false - interface_statistics_load_interval: + leaf_tor_vpc_port_channel_id_range: description: - - The interface statistics load interval in seconds. - type: int - default: 10 - greenfield_debug_flag: + - Specify vPC/Port-channel ID range (minimum 1, maximum 4096) for leaf-TOR pairings. + type: str + default: "1-499" + + # Protocols + ospf_area_id: description: - - Allow switch configuration to be cleared without a reload when preserveConfig is set to false. + - The OSPF area ID. type: str - default: disable - choices: [ enable, disable ] - nxapi: + default: "0.0.0.0" + bgp_loopback_id: description: - - Enable NX-API (HTTPS). - type: bool - default: false - nxapi_https_port: + - The BGP loopback interface ID (0-1023). + type: int + default: 0 + nve_loopback_id: description: - - The NX-API HTTPS port (1-65535). + - The NVE loopback interface ID (0-1023). type: int - default: 443 - nxapi_http: + default: 1 + anycast_loopback_id: description: - - Enable NX-API over HTTP. + - Underlay Anycast Loopback ID. Used for vPC Peering in VXLANv6 Fabrics. + type: int + default: 10 + auto_bgp_neighbor_description: + description: + - Enable automatic BGP neighbor description. type: bool - default: false - nxapi_http_port: + default: true + ibgp_peer_template: description: - - The NX-API HTTP port (1-65535). - type: int - default: 80 + - The iBGP peer template name. + type: str + default: "" + leaf_ibgp_peer_template: + description: + - The leaf iBGP peer template name. + type: str + default: "" + link_state_routing_tag: + description: + - The link state routing tag. + type: str + default: UNDERLAY bgp_authentication: description: - Enable BGP authentication. @@ -573,6 +555,39 @@ - The IS-IS overload elapse time in seconds. type: int default: 60 + + # Security + security_group_tag: + description: + - Enable Security Group Tag (SGT) support. + type: bool + default: false + security_group_tag_prefix: + description: + - The SGT prefix. + type: str + default: SG_ + security_group_tag_mac_segmentation: + description: + - Enable SGT MAC segmentation. + type: bool + default: false + security_group_tag_id_range: + description: + - The SGT ID range. + type: str + default: "10000-14000" + security_group_tag_preprovision: + description: + - Enable SGT pre-provisioning. + type: bool + default: false + security_group_status: + description: + - The security group status. + type: str + default: disabled + choices: [ enabled, enabledStrict, enabledLoose, enablePending, enablePendingStrict, enablePendingLoose, disablePending, disabled ] macsec: description: - Enable MACsec on intra-fabric links. @@ -676,301 +691,165 @@ - Skip verification of incoming certificate. type: bool default: false - vrf_lite_auto_config: + + # Advanced + site_id: description: - - "VRF Lite Inter-Fabric Connection deployment options. If C(back2BackAndToExternal) is selected, - VRF Lite IFCs are auto created between border devices of two Easy Fabrics, and between - border devices in Easy Fabric and edge routers in External Fabric." + - The site identifier for the fabric (for EVPN Multi-Site support). + - Must be a numeric value between 1 and 281474976710655. + - Defaults to the value of O(config.management.bgp_asn) if not provided. type: str - default: manual - choices: [ manual, back2BackAndToExternal ] - vrf_lite_subnet_range: + default: "" + overlay_mode: description: - - The VRF lite subnet IP address pool. + - The overlay configuration mode. type: str - default: "10.33.0.0/16" - vrf_lite_subnet_target_mask: + default: cli + choices: [ cli, config-profile ] + vrf_template: description: - - The VRF lite subnet target mask. - type: int - default: 30 - auto_unique_vrf_lite_ip_prefix: + - The VRF template name. + type: str + default: Default_VRF_Universal + network_template: description: - - Enable auto unique VRF lite IP prefix. + - The network template name. + type: str + default: Default_Network_Universal + vrf_extension_template: + description: + - The VRF extension template name. + type: str + default: Default_VRF_Extension_Universal + network_extension_template: + description: + - The network extension template name. + type: str + default: Default_Network_Extension_Universal + l3_vni_no_vlan_default_option: + description: + - Enable L3 VNI no-VLAN default option. type: bool default: false - auto_symmetric_vrf_lite: + fabric_mtu: description: - - Enable auto symmetric VRF lite. + - The fabric MTU size (1500-9216). + type: int + default: 9216 + l2_host_interface_mtu: + description: + - The L2 host interface MTU size (1500-9216). + type: int + default: 9216 + tenant_dhcp: + description: + - Enable tenant DHCP. type: bool - default: false - auto_vrf_lite_default_vrf: + default: true + snmp_trap: description: - - Enable auto VRF lite for the default VRF. + - Enable SNMP traps. type: bool - default: false - auto_symmetric_default_vrf: + default: true + cdp: description: - - Enable auto symmetric default VRF. + - Enable CDP. type: bool default: false - default_vrf_redistribution_bgp_route_map: + tcam_allocation: description: - - Route Map used to redistribute BGP routes to IGP in default VRF in auto created VRF Lite IFC links. - type: str - default: extcon-rmap-filter - per_vrf_loopback_auto_provision: + - Enable TCAM allocation. + type: bool + default: true + real_time_interface_statistics_collection: description: - - Enable per-VRF loopback auto-provisioning. + - Enable real-time interface statistics collection. type: bool default: false - per_vrf_loopback_ip_range: + interface_statistics_load_interval: description: - - The per-VRF loopback IP address pool. + - The interface statistics load interval in seconds. + type: int + default: 10 + greenfield_debug_flag: + description: + - Allow switch configuration to be cleared without a reload when preserveConfig is set to false. type: str - default: "10.5.0.0/22" - per_vrf_loopback_auto_provision_ipv6: + default: disable + choices: [ enable, disable ] + nxapi: description: - - Enable per-VRF loopback auto-provisioning for IPv6. + - Enable NX-API (HTTPS). type: bool default: false - per_vrf_loopback_ipv6_range: + nxapi_https_port: description: - - The per-VRF loopback IPv6 address pool. - type: str - default: "fd00::a05:0/112" - per_vrf_unique_loopback_auto_provision: + - The NX-API HTTPS port (1-65535). + type: int + default: 443 + nxapi_http: description: - - Auto provision a unique IPv4 loopback on a VTEP on VRF attachment. - - This option and per VRF per VTEP loopback auto-provisioning are mutually exclusive. + - Enable NX-API over HTTP. type: bool default: false - per_vrf_unique_loopback_ip_range: + nxapi_http_port: description: - - Prefix pool to assign unique IPv4 addresses to loopbacks on VTEPs on a per VRF basis. - type: str - default: "10.6.0.0/22" - per_vrf_unique_loopback_auto_provision_v6: + - The NX-API HTTP port (1-65535). + type: int + default: 80 + default_queuing_policy: description: - - Auto provision a unique IPv6 loopback on a VTEP on VRF attachment. + - Enable default queuing policies. type: bool default: false - per_vrf_unique_loopback_ipv6_range: + default_queuing_policy_cloudscale: description: - - Prefix pool to assign unique IPv6 addresses to loopbacks on VTEPs on a per VRF basis. + - Queuing policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric. type: str - default: "fd00::a06:0/112" - underlay_ipv6: + default: queuing_policy_default_8q_cloudscale + default_queuing_policy_r_series: description: - - Enable IPv6 underlay. - type: bool - default: false - ipv6_multicast_group_subnet: + - Queuing policy for all Nexus R-series switches. + type: str + default: queuing_policy_default_r_series + default_queuing_policy_other: description: - - The IPv6 multicast group subnet. + - Queuing policy for all other switches in the fabric. type: str - default: "ff1e::/121" - tenant_routed_multicast_ipv6: + default: queuing_policy_default_other + aiml_qos: description: - - Enable tenant routed multicast for IPv6. + - Enable AI/ML QoS. Configures QoS and queuing policies specific to N9K Cloud Scale and Silicon One switch fabric + for AI network workloads. type: bool default: false - ipv6_link_local: - description: - - Enable IPv6 link-local addressing. - type: bool - default: true - ipv6_subnet_target_mask: - description: - - The IPv6 subnet target mask. - type: int - default: 126 - ipv6_subnet_range: + aiml_qos_policy: description: - - The IPv6 subnet range. + - Queuing policy based on predominant fabric link speed. type: str - default: "fd00::a04:0/112" - bgp_loopback_ipv6_range: + default: 400G + choices: [ 800G, 400G, 100G, 25G, User-defined ] + roce_v2: description: - - The BGP loopback IPv6 address pool. + - DSCP for RDMA traffic. Numeric (0-63) with ranges/comma, or named values. type: str - default: "fd00::a02:0/119" - nve_loopback_ipv6_range: + default: "26" + cnp: description: - - The NVE loopback IPv6 address pool. + - DSCP value for Congestion Notification. Numeric (0-63) with ranges/comma, or named values. type: str - default: "fd00::a03:0/118" - ipv6_anycast_rendezvous_point_ip_range: + default: "48" + wred_min: description: - - The IPv6 anycast rendezvous point IP address pool. - type: str - default: "fd00::254:254:0/118" - mvpn_vrf_route_import_id: + - WRED minimum threshold (in kbytes). + type: int + default: 950 + wred_max: description: - - Enable MVPN VRI ID generation for Tenant Routed Multicast with IPv4 underlay. - type: bool - default: true - mvpn_vrf_route_import_id_range: - description: - - MVPN VRI ID range (minimum 1, maximum 65535) for vPC. - - Applicable when TRM is enabled with IPv6 underlay, or mvpn_vrf_route_import_id is enabled with IPv4 underlay. - type: str - default: "" - vrf_route_import_id_reallocation: - description: - - One time VRI ID re-allocation based on MVPN VRI ID Range. - type: bool - default: false - l3vni_multicast_group: - description: - - Default underlay multicast group IPv4 address assigned for every overlay VRF. - type: str - default: "239.1.1.0" - l3_vni_ipv6_multicast_group: - description: - - Default underlay multicast group IPv6 address assigned for every overlay VRF. - type: str - default: "ff1e::" - rendezvous_point_mode: - description: - - Multicast rendezvous point mode. For IPv6 underlay, use C(asm) only. - type: str - default: asm - choices: [ asm, bidir ] - phantom_rendezvous_point_loopback_id1: - description: - - Underlay phantom RP loopback primary ID for PIM Bi-dir deployments. - type: int - default: 2 - phantom_rendezvous_point_loopback_id2: - description: - - Underlay phantom RP loopback secondary ID for PIM Bi-dir deployments. - type: int - default: 3 - phantom_rendezvous_point_loopback_id3: - description: - - Underlay phantom RP loopback tertiary ID for PIM Bi-dir deployments. - type: int - default: 4 - phantom_rendezvous_point_loopback_id4: - description: - - Underlay phantom RP loopback quaternary ID for PIM Bi-dir deployments. - type: int - default: 5 - anycast_loopback_id: - description: - - Underlay Anycast Loopback ID. Used for vPC Peering in VXLANv6 Fabrics. - type: int - default: 10 - auto_bgp_neighbor_description: - description: - - Enable automatic BGP neighbor description. - type: bool - default: true - ibgp_peer_template: - description: - - The iBGP peer template name. - type: str - default: "" - leaf_ibgp_peer_template: - description: - - The leaf iBGP peer template name. - type: str - default: "" - link_state_routing_tag: - description: - - The link state routing tag. - type: str - default: UNDERLAY - static_underlay_ip_allocation: - description: - - Enable static underlay IP allocation. - type: bool - default: false - security_group_tag: - description: - - Enable Security Group Tag (SGT) support. - type: bool - default: false - security_group_tag_prefix: - description: - - The SGT prefix. - type: str - default: SG_ - security_group_tag_mac_segmentation: - description: - - Enable SGT MAC segmentation. - type: bool - default: false - security_group_tag_id_range: - description: - - The SGT ID range. - type: str - default: "10000-14000" - security_group_tag_preprovision: - description: - - Enable SGT pre-provisioning. - type: bool - default: false - security_group_status: - description: - - The security group status. - type: str - default: disabled - choices: [ enabled, enabledStrict, enabledLoose, enablePending, enablePendingStrict, enablePendingLoose, disablePending, disabled ] - default_queuing_policy: - description: - - Enable default queuing policies. - type: bool - default: false - default_queuing_policy_cloudscale: - description: - - Queuing policy for all 92xx, -EX, -FX, -FX2, -FX3, -GX series switches in the fabric. - type: str - default: queuing_policy_default_8q_cloudscale - default_queuing_policy_r_series: - description: - - Queuing policy for all Nexus R-series switches. - type: str - default: queuing_policy_default_r_series - default_queuing_policy_other: - description: - - Queuing policy for all other switches in the fabric. - type: str - default: queuing_policy_default_other - aiml_qos: - description: - - Enable AI/ML QoS. Configures QoS and queuing policies specific to N9K Cloud Scale and Silicon One switch fabric - for AI network workloads. - type: bool - default: false - aiml_qos_policy: - description: - - Queuing policy based on predominant fabric link speed. - type: str - default: 400G - choices: [ 800G, 400G, 100G, 25G, User-defined ] - roce_v2: - description: - - DSCP for RDMA traffic. Numeric (0-63) with ranges/comma, or named values. - type: str - default: "26" - cnp: - description: - - DSCP value for Congestion Notification. Numeric (0-63) with ranges/comma, or named values. - type: str - default: "48" - wred_min: - description: - - WRED minimum threshold (in kbytes). - type: int - default: 950 - wred_max: - description: - - WRED maximum threshold (in kbytes). - type: int - default: 3000 - wred_drop_probability: + - WRED maximum threshold (in kbytes). + type: int + default: 3000 + wred_drop_probability: description: - WRED drop probability percentage. type: int @@ -1101,123 +980,411 @@ - Default PVLAN secondary network template. type: str default: Pvlan_Secondary_Network - allow_vlan_on_leaf_tor_pairing: + nve_hold_down_timer: description: - - "Set trunk allowed VLAN to 'none' or 'all' for leaf-TOR pairing port-channels." - type: str - default: none - choices: [ none, all ] - leaf_tor_id_range: + - The NVE hold-down timer in seconds. + type: int + default: 180 + next_generation_oam: description: - - Use specific vPC/Port-channel ID range for leaf-TOR pairings. + - Enable the Next Generation (NG) OAM feature for all switches in the fabric. + type: bool + default: true + ngoam_south_bound_loop_detect: + description: + - Enable the Next Generation (NG) OAM southbound loop detection. type: bool default: false - leaf_tor_vpc_port_channel_id_range: + ngoam_south_bound_loop_detect_probe_interval: description: - - Specify vPC/Port-channel ID range (minimum 1, maximum 4096) for leaf-TOR pairings. - type: str - default: "1-499" - ip_service_level_agreement_id_range: + - Set NG OAM southbound loop detection probe interval in seconds. + type: int + default: 300 + ngoam_south_bound_loop_detect_recovery_interval: description: - - The IP SLA ID range. - type: str - default: "10000-19999" - object_tracking_number_range: + - Set NG OAM southbound loop detection recovery interval in seconds. + type: int + default: 600 + strict_config_compliance_mode: description: - - The object tracking number range. - type: str - default: "100-299" - route_map_sequence_number_range: + - Enable bi-directional compliance checks to flag additional configs in the running config + that are not in the intent/expected config. + type: bool + default: false + advanced_ssh_option: description: - - The route map sequence number range (minimum 1, maximum 65534). + - Enable AAA IP Authorization. Enable only when IP Authorization is enabled in the AAA Server. + type: bool + default: false + copp_policy: + description: + - The fabric wide CoPP policy. Customized CoPP policy should be provided when C(manual) is selected. type: str - default: "1-65534" - service_network_vlan_range: + default: strict + choices: [ dense, lenient, moderate, strict, manual ] + power_redundancy_mode: description: - - Per Switch Overlay Service Network VLAN Range (minimum 2, maximum 4094). + - Default power supply mode for NX-OS switches. type: str - default: "3000-3199" - day0_bootstrap: + default: redundant + choices: [ redundant, combined, inputSrcRedundant ] + host_interface_admin_state: description: - - Enable day-0 bootstrap (POAP). + - Enable host interface admin state. type: bool - default: false - local_dhcp_server: + default: true + heartbeat_interval: description: - - Enable local DHCP server for bootstrap. + - The heartbeat interval. + type: int + default: 190 + policy_based_routing: + description: + - Enable policy-based routing. type: bool default: false - dhcp_protocol_version: + brownfield_network_name_format: description: - - The IP protocol version for local DHCP server. + - The brownfield network name format. type: str - default: dhcpv4 - choices: [ dhcpv4, dhcpv6 ] - dhcp_start_address: + default: "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" + brownfield_skip_overlay_network_attachments: description: - - The DHCP start address for bootstrap. + - Skip brownfield overlay network attachments. + type: bool + default: false + + # Freeform + extra_config_leaf: + description: + - Extra freeform configuration applied to leaf switches. type: str default: "" - dhcp_end_address: + extra_config_spine: description: - - The DHCP end address for bootstrap. + - Extra freeform configuration applied to spine switches. type: str default: "" - management_gateway: + extra_config_tor: description: - - The management gateway for bootstrap. + - Extra freeform configuration applied to TOR switches. type: str default: "" - management_ipv4_prefix: + extra_config_intra_fabric_links: description: - - The management IPv4 prefix length for bootstrap. - type: int - default: 24 - management_ipv6_prefix: + - Extra freeform configuration applied to intra-fabric links. + type: str + default: "" + pre_interface_config_leaf: description: - - The management IPv6 prefix length for bootstrap. - type: int - default: 64 - extra_config_nxos_bootstrap: + - Additional CLIs added before interface configurations for all switches with a VTEP + unless they have some spine role. + type: str + default: "" + pre_interface_config_spine: description: - - Additional CLIs required during device bootup/login (e.g. AAA/Radius). + - Additional CLIs added before interface configurations for all switches with some spine role. type: str default: "" - unnumbered_bootstrap_loopback_id: + pre_interface_config_tor: description: - - Bootstrap Seed Switch Loopback Interface ID. + - Additional CLIs added before interface configurations for all ToRs. + type: str + default: "" + + # Resources + static_underlay_ip_allocation: + description: + - Enable static underlay IP allocation. + type: bool + default: false + bgp_loopback_ip_range: + description: + - The BGP loopback IP address pool. + type: str + default: "10.2.0.0/22" + nve_loopback_ip_range: + description: + - The NVE loopback IP address pool. + type: str + default: "10.3.0.0/22" + bgp_loopback_ipv6_range: + description: + - The BGP loopback IPv6 address pool. + type: str + default: "fd00::a02:0/119" + nve_loopback_ipv6_range: + description: + - The NVE loopback IPv6 address pool. + type: str + default: "fd00::a03:0/118" + intra_fabric_subnet_range: + description: + - The intra-fabric subnet IP address pool. + type: str + default: "10.4.0.0/16" + ipv6_subnet_range: + description: + - The IPv6 subnet range. + type: str + default: "fd00::a04:0/112" + router_id_range: + description: + - The BGP router ID range in IPv4 subnet format. Used for IPv6 underlay. + type: str + default: "10.2.0.0/23" + l2_vni_range: + description: + - The Layer 2 VNI range. + type: str + default: "30000-49000" + l3_vni_range: + description: + - The Layer 3 VNI range. + type: str + default: "50000-59000" + network_vlan_range: + description: + - The network VLAN range. + type: str + default: "2300-2999" + vrf_vlan_range: + description: + - The VRF VLAN range. + type: str + default: "2000-2299" + sub_interface_dot1q_range: + description: + - The sub-interface 802.1q range (minimum 2, maximum 4093). + type: str + default: "2-511" + vrf_lite_auto_config: + description: + - "VRF Lite Inter-Fabric Connection deployment options. If C(back2BackAndToExternal) is selected, + VRF Lite IFCs are auto created between border devices of two Easy Fabrics, and between + border devices in Easy Fabric and edge routers in External Fabric." + type: str + default: manual + choices: [ manual, back2BackAndToExternal ] + vrf_lite_subnet_range: + description: + - The VRF lite subnet IP address pool. + type: str + default: "10.33.0.0/16" + vrf_lite_subnet_target_mask: + description: + - The VRF lite subnet target mask. type: int - default: 253 - unnumbered_dhcp_start_address: + default: 30 + auto_unique_vrf_lite_ip_prefix: description: - - Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + - Enable auto unique VRF lite IP prefix. + type: bool + default: false + auto_symmetric_vrf_lite: + description: + - Enable auto symmetric VRF lite. + type: bool + default: false + auto_vrf_lite_default_vrf: + description: + - Enable auto VRF lite for the default VRF. + type: bool + default: false + auto_symmetric_default_vrf: + description: + - Enable auto symmetric default VRF. + type: bool + default: false + default_vrf_redistribution_bgp_route_map: + description: + - Route Map used to redistribute BGP routes to IGP in default VRF in auto created VRF Lite IFC links. type: str - default: "" - unnumbered_dhcp_end_address: + default: extcon-rmap-filter + per_vrf_loopback_auto_provision: description: - - Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + - Enable per-VRF loopback auto-provisioning. + type: bool + default: false + per_vrf_loopback_ip_range: + description: + - The per-VRF loopback IP address pool. type: str - default: "" + default: "10.5.0.0/22" + per_vrf_loopback_auto_provision_ipv6: + description: + - Enable per-VRF loopback auto-provisioning for IPv6. + type: bool + default: false + per_vrf_loopback_ipv6_range: + description: + - The per-VRF loopback IPv6 address pool. + type: str + default: "fd00::a05:0/112" + per_vrf_unique_loopback_auto_provision: + description: + - Auto provision a unique IPv4 loopback on a VTEP on VRF attachment. + - This option and per VRF per VTEP loopback auto-provisioning are mutually exclusive. + type: bool + default: false + per_vrf_unique_loopback_ip_range: + description: + - Prefix pool to assign unique IPv4 addresses to loopbacks on VTEPs on a per VRF basis. + type: str + default: "10.6.0.0/22" + per_vrf_unique_loopback_auto_provision_v6: + description: + - Auto provision a unique IPv6 loopback on a VTEP on VRF attachment. + type: bool + default: false + per_vrf_unique_loopback_ipv6_range: + description: + - Prefix pool to assign unique IPv6 addresses to loopbacks on VTEPs on a per VRF basis. + type: str + default: "fd00::a06:0/112" + ip_service_level_agreement_id_range: + description: + - The IP SLA ID range. + type: str + default: "10000-19999" + object_tracking_number_range: + description: + - The object tracking number range. + type: str + default: "100-299" + route_map_sequence_number_range: + description: + - The route map sequence number range (minimum 1, maximum 65534). + type: str + default: "1-65534" + service_network_vlan_range: + description: + - Per Switch Overlay Service Network VLAN Range (minimum 2, maximum 4094). + type: str + default: "3000-3199" + + # Manageability inband_management: description: - Manage switches with only inband connectivity. type: bool default: false - inband_dhcp_servers: + aaa: description: - - List of external DHCP server IP addresses (Max 3). + - Enable AAA. + type: bool + default: false + extra_config_aaa: + description: + - Extra freeform AAA configuration. + type: str + default: "" + banner: + description: + - The fabric banner text displayed on switch login. + type: str + default: "" + ntp_server_collection: + description: + - The list of NTP server IP addresses. type: list elements: str - seed_switch_core_interfaces: + ntp_server_vrf_collection: description: - - Seed switch fabric interfaces. Core-facing interface list on seed switch. + - The list of VRFs for NTP servers. type: list elements: str - spine_switch_core_interfaces: + dns_collection: + description: + - The list of DNS server IP addresses. + type: list + elements: str + dns_vrf_collection: + description: + - The list of VRFs for DNS servers. + type: list + elements: str + syslog_server_collection: + description: + - The list of syslog server IP addresses. + type: list + elements: str + syslog_server_vrf_collection: + description: + - The list of VRFs for syslog servers. + type: list + elements: str + syslog_severity_collection: + description: + - The list of syslog severity levels (0-7). + type: list + elements: int + + # Hypershield + allow_smart_switch_onboarding: + description: + - Enable onboarding of smart switches to Hypershield for firewall service. + type: bool + default: false + connectivity_domain_name: + description: + - Domain name to connect to Hypershield. + type: str + hypershield_connectivity_proxy_server: + description: + - IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication. + type: str + hypershield_connectivity_proxy_server_port: + description: + - Proxy port number for communication with Hypershield. + type: int + hypershield_connectivity_source_intf: + description: + - Loopback interface on smart switch for communication with Hypershield. + type: str + + # Bootstrap + day0_bootstrap: + description: + - Enable day-0 bootstrap (POAP). + type: bool + default: false + local_dhcp_server: + description: + - Enable local DHCP server for bootstrap. + type: bool + default: false + dhcp_protocol_version: + description: + - The IP protocol version for local DHCP server. + type: str + default: dhcpv4 + choices: [ dhcpv4, dhcpv6 ] + dhcp_start_address: + description: + - The DHCP start address for bootstrap. + type: str + default: "" + dhcp_end_address: + description: + - The DHCP end address for bootstrap. + type: str + default: "" + management_gateway: + description: + - The management gateway for bootstrap. + type: str + default: "" + management_ipv4_prefix: + description: + - The management IPv4 prefix length for bootstrap. + type: int + default: 24 + management_ipv6_prefix: description: - - Spine switch fabric interfaces. Core-facing interface list on all spines. - type: list - elements: str + - The management IPv6 prefix length for bootstrap. + type: int + default: 64 bootstrap_subnet_collection: description: - List of IPv4 or IPv6 subnets to be used for bootstrap. @@ -1250,6 +1417,60 @@ - Subnet prefix length (8-30). type: int required: true + seed_switch_core_interfaces: + description: + - Seed switch fabric interfaces. Core-facing interface list on seed switch. + type: list + elements: str + spine_switch_core_interfaces: + description: + - Spine switch fabric interfaces. Core-facing interface list on all spines. + type: list + elements: str + inband_dhcp_servers: + description: + - List of external DHCP server IP addresses (Max 3). + type: list + elements: str + extra_config_nxos_bootstrap: + description: + - Additional CLIs required during device bootup/login (e.g. AAA/Radius). + type: str + default: "" + unnumbered_bootstrap_loopback_id: + description: + - Bootstrap Seed Switch Loopback Interface ID. + type: int + default: 253 + unnumbered_dhcp_start_address: + description: + - Switch Loopback DHCP Scope Start Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + type: str + default: "" + unnumbered_dhcp_end_address: + description: + - Switch Loopback DHCP Scope End Address. Must be a subset of IGP/BGP Loopback Prefix Pool. + type: str + default: "" + + # Configuration Backup + real_time_backup: + description: + - Enable real-time backup. + type: bool + default: false + scheduled_backup: + description: + - Enable scheduled backup. + type: bool + default: false + scheduled_backup_time: + description: + - The scheduled backup time. + type: str + default: "" + + # Flow Monitor netflow_settings: description: - Settings associated with netflow. @@ -1337,201 +1558,6 @@ - Secondary exporter name. type: str default: "" - real_time_backup: - description: - - Enable real-time backup. - type: bool - default: false - scheduled_backup: - description: - - Enable scheduled backup. - type: bool - default: false - scheduled_backup_time: - description: - - The scheduled backup time. - type: str - default: "" - nve_hold_down_timer: - description: - - The NVE hold-down timer in seconds. - type: int - default: 180 - next_generation_oam: - description: - - Enable the Next Generation (NG) OAM feature for all switches in the fabric. - type: bool - default: true - ngoam_south_bound_loop_detect: - description: - - Enable the Next Generation (NG) OAM southbound loop detection. - type: bool - default: false - ngoam_south_bound_loop_detect_probe_interval: - description: - - Set NG OAM southbound loop detection probe interval in seconds. - type: int - default: 300 - ngoam_south_bound_loop_detect_recovery_interval: - description: - - Set NG OAM southbound loop detection recovery interval in seconds. - type: int - default: 600 - strict_config_compliance_mode: - description: - - Enable bi-directional compliance checks to flag additional configs in the running config - that are not in the intent/expected config. - type: bool - default: false - advanced_ssh_option: - description: - - Enable AAA IP Authorization. Enable only when IP Authorization is enabled in the AAA Server. - type: bool - default: false - copp_policy: - description: - - The fabric wide CoPP policy. Customized CoPP policy should be provided when C(manual) is selected. - type: str - default: strict - choices: [ dense, lenient, moderate, strict, manual ] - power_redundancy_mode: - description: - - Default power supply mode for NX-OS switches. - type: str - default: redundant - choices: [ redundant, combined, inputSrcRedundant ] - host_interface_admin_state: - description: - - Enable host interface admin state. - type: bool - default: true - heartbeat_interval: - description: - - The heartbeat interval. - type: int - default: 190 - policy_based_routing: - description: - - Enable policy-based routing. - type: bool - default: false - brownfield_network_name_format: - description: - - The brownfield network name format. - type: str - default: "Auto_Net_VNI$$VNI$$_VLAN$$VLAN_ID$$" - brownfield_skip_overlay_network_attachments: - description: - - Skip brownfield overlay network attachments. - type: bool - default: false - allow_smart_switch_onboarding: - description: - - Enable onboarding of smart switches to Hypershield for firewall service. - type: bool - default: false - connectivity_domain_name: - description: - - Domain name to connect to Hypershield. - type: str - hypershield_connectivity_proxy_server: - description: - - IPv4 address, IPv6 address, or DNS name of the proxy server for Hypershield communication. - type: str - hypershield_connectivity_proxy_server_port: - description: - - Proxy port number for communication with Hypershield. - type: int - hypershield_connectivity_source_intf: - description: - - Loopback interface on smart switch for communication with Hypershield. - type: str - aaa: - description: - - Enable AAA. - type: bool - default: false - extra_config_leaf: - description: - - Extra freeform configuration applied to leaf switches. - type: str - default: "" - extra_config_spine: - description: - - Extra freeform configuration applied to spine switches. - type: str - default: "" - extra_config_tor: - description: - - Extra freeform configuration applied to TOR switches. - type: str - default: "" - extra_config_intra_fabric_links: - description: - - Extra freeform configuration applied to intra-fabric links. - type: str - default: "" - extra_config_aaa: - description: - - Extra freeform AAA configuration. - type: str - default: "" - pre_interface_config_leaf: - description: - - Additional CLIs added before interface configurations for all switches with a VTEP - unless they have some spine role. - type: str - default: "" - pre_interface_config_spine: - description: - - Additional CLIs added before interface configurations for all switches with some spine role. - type: str - default: "" - pre_interface_config_tor: - description: - - Additional CLIs added before interface configurations for all ToRs. - type: str - default: "" - banner: - description: - - The fabric banner text displayed on switch login. - type: str - default: "" - ntp_server_collection: - description: - - The list of NTP server IP addresses. - type: list - elements: str - ntp_server_vrf_collection: - description: - - The list of VRFs for NTP servers. - type: list - elements: str - dns_collection: - description: - - The list of DNS server IP addresses. - type: list - elements: str - dns_vrf_collection: - description: - - The list of VRFs for DNS servers. - type: list - elements: str - syslog_server_collection: - description: - - The list of syslog server IP addresses. - type: list - elements: str - syslog_server_vrf_collection: - description: - - The list of VRFs for syslog servers. - type: list - elements: str - syslog_severity_collection: - description: - - The list of syslog severity levels (0-7). - type: list - elements: int state: description: - The desired state of the fabric resources on the Cisco Nexus Dashboard.